8

I would like to execute a task in several directories but found no "makefile-like" solution up to now. I know this is an often asked question and I know how to solve it for sub-makfiles and so on, but I am looking for something simpler.

Instead of doing

copy: 
     cd module1 && mkdir foo
     cd module2 && mkdir foo
     cd module3 && mkdir foo

I would like to have something like

directories = module1 module2 module3

copy: $(directories)
     cd $< && mkdir foo

but that does not work, since the receipe is called only once with the first directory. I came up with this solution which works but is probably not in the style of Makefiles:

directories = module1 module2 module3

copy: 
     for d in $(directories); do cd $$d && mkdir foo && cd ..; done

How can I do this more nicely?

Robert
  • 872
  • 3
  • 10
  • 15

2 Answers2

7

There are lots of ways of doing this.

You can do what Andrew suggests without hard-coding a prefix:

directories = module1 module2 module2

%/foo: ; mkdir -p -- "$@"

copy: $(addsuffix /foo,$(directories))

which gives

$ make -n copy
mkdir -p -- "module1/foo"
mkdir -p -- "module2/foo"
mkdir -p -- "module3/foo"

You can also generate the copy target from the makefile:

directories = module1 module2 module2

define copy-target
  copy:: ; cd $1 && mkdir foo
endef

$(foreach dir,$(directories),$(eval $(call copy-target,$(dir))))

This gives:

$ make -n copy
cd module1 && mkdir foo
cd module2 && mkdir foo
cd module3 && mkdir foo

You could also generate the commands, not the target:

directories = module1 module2 module2

copy: ; $(foreach dir,$(directories),(cd $(dir) && mkdir foo) &&) :

which results in

$ make -n copy
(cd module1 && mkdir foo) && (cd module2 && mkdir foo) && (cd module3 && mkdir foo) && :
Idelic
  • 14,976
  • 5
  • 35
  • 40
  • Thanks, that works. I picked solution 2 (generate the `copy` target from the makefile) because it is not bound to the `mkdir` case. Unfortunately, it is not too much different from using a shell-for-loop, but that does not matter. – Robert Dec 06 '12 at 08:43
2

In Makefiles, whenever you want to create a file, make a rule with the name of the file as the target, and give the command to create the target file.

copy: module1/foo module2/foo module3/foo

module%/foo:
        mkdir $@

If you'd like to parameterize that a bit to avoid duplication, you could also write something like

MODULE_NUMBERS = 1 2 3

copy: ${MODULE_NUMBERS:%=module%/foo}

module%/foo:
        mkdir $@
andrewdotn
  • 32,721
  • 10
  • 101
  • 130
  • Thanks. Unfortunately, I can not apply your solution because I can not address the relevant directories using a wildcard ("module%" in your example) since I have many directories with the same prefix and want to use a variable ("directories" in my example) to select only some of them. BTW: I used "mkdir" just as an example command (actually, I don't want to create directories but upload files from each directory to a server), so the solution should work regardless of the task at hand. – Robert Nov 29 '12 at 17:23