sbunny
sbunny

Reputation: 439

Nested for loop not working in makefile

I am trying to use a nested for loop for searching and copying some files inside the recipe of one of the targets inside a makefile:

DIR = $(DIR_A) $(DIR_B)

install:
      for dirs in $(DIR); do \
        for file in $(shell find $(dirs) -type f -and -not -path "*/.svn*" | sed -e "s|$(dirs)||"); do \
        folder=$${file%/*}; \
          $(INSTALL) -d $(DEST_DIR)$$folder/log; \
          $(INSTALL) $(dirs)/$$file $(DEST_DIR)$$folder/log; \
        done \
      done

However $(dirs) variable always evaluates to empty inside the second for loop and the current working directory gets passed to "find" instead of first directory path from $(DIR).

Can someone please suggest if I am missing something ?

Thanks.

Upvotes: 0

Views: 974

Answers (2)

Beta
Beta

Reputation: 99094

You have made several errors, and you will find it almost impossible to solve them as l;ong as you insist on trying to solve them all at once.

Let's try this in stages. Suppose your DIR_A and DIR_B are north and south. On the command line, if you try this:

for dirs in north south; do echo $dirs; done

you will get the correct output:

north
south

If you try it as a makefile rule:

install:
    for dirs in $(DIR); do echo $$dirs; done

again, it works correctly.

If you try your makefile recipe:

install:
    for dirs in $(DIR); do \ for file in $(shell ls $$dirs); do \ echo "file is $$file"; \ done \ done

it fails, because Make expands the $(shell ...) command before passing the entire for command to the shell, when dirs has not yet been assigned a value. One way to construct a sensible shell command is to use backticks:

for dirs in north south; do for file in `ls $dirs`; do  echo "file is $file";  done  done

This works on the command line. A makefile rule built around it:

install:
    for dirs in $(DIR); do for file in `ls $$dirs`; do  echo "file is $$file";  done  done

also works.

That should be enough to allow you to rewrite your makefile.

Upvotes: 0

William Pursell
William Pursell

Reputation: 212228

The $(dirs) in the find command is being expanded by make to the make variable dirs which is unset and thus the empty string. To reference the shell variable, you need to escape the $:

 for file in $$(find $${dirs} -type f -and \
      -not -path "*/.svn*" | sed -e "s|$${dirs}||"); do

But don't do this. It is much cleaner to explicitly list the files you intend to install. If there are many, it is fine to write a script to generate the Makefile. What you are doing is a fragile mess.

Upvotes: 1

Related Questions