Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating through a list of directories in a Makefile

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?

like image 555
Robert Avatar asked Nov 29 '12 10:11

Robert


2 Answers

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) && :
like image 78
Idelic Avatar answered Nov 19 '22 00:11

Idelic


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 $@
like image 2
andrewdotn Avatar answered Nov 19 '22 01:11

andrewdotn