makefile: foreach "make -C" call
That's because you are calling a single make
, with lots of arguments including the subsequent make
commands. You need to add a shell command separator between the invocations of make. Something like this will work:
all:
$(foreach c,$(ALLMAKES),$(MAKE) -C $(c) && ) true
On second look, there are other issues with your makefile. You should not set a make variable named PATH
, because that will override the subshell's $PATH
variable. You can just use the make built-in variable $(CURDIR)
rather than running $(shell pwd)
. You don't actually need to prefix your directories with the path at all, because running $(MAKE) -C ...
does not change the shell's working directory, just make's, and when make exits it will be set back, so you can just use a relative path.
Also invoking sub-makes in a loop has some issues. First, you reduce the amount of parallelization you can get. Second, the -k
option can't be properly supported (without a lot of unpleasant effort). A better way to handle this issue is to take advantage of the fact that make already knows how to build lots of targets:
all: $(EFFECTS)
$(EFFECTS):
$(MAKE) -C $@
.PHONY: all $(EFFECTS)
If you have specific ordering issues between the different subdirectories, you can define them as well:
impl/thing2: impl/thing1
etc. This ensures maximum parallelization opportunities, while still preserving important ordering.
To add alternative rules, say for clean
, you can do something like this:
CLEAN_EFFECTS := $(addsuffix .clean,$(EFFECTS))
clean: $(CLEAN_EFFECTS)
$(CLEAN_EFFECTS):
$(MAKE) -C $(basename $@) clean
.PHONY: clean $(CLEAN_EFFECTS)
This is nice because you can also build a single subdirectory (and all its prerequisites) by running make impl/thing1
for example. Or clean them by running make impl/thing1.clean
If you have more of these you can also get fancy with pattern rules, etc. to avoid repeating this for every type of target. It gets more cumbersome.
Olivier Trahan
Updated on June 04, 2022Comments
-
Olivier Trahan almost 2 years
here is part of my makefile:
PATH := $(shell pwd) EDIR := impl EFFECTS := $(filter-out $(EDIR), $(shell find $(EDIR) -maxdepth 1 -type d)) ALLMAKES := $(patsubst %, $(PATH)/%, $(EFFECTS)) all: $(foreach c,$(ALLMAKES),$(MAKE) -C $(c))
So essentially, I want to call make for all directories in the "impl" directory without "impl" itself. I understand that make will remember the last directory it was at when last called with the -C argument which is why I give the absolute path everytime. What make echos seems to be what I want:
make -C <projectdir>/impl/thing1 make -C <projectdir>/impl/thing2 make -C <projectdir>/impl/thing3
The issue is that make doesn't accomplish the command and simply prints:
make: make: Command not found.
I can call "make -C <path>" for each of the directories individually outside of the makefile but it doesn't work in the foreach call. I have tried this instead but it doesn't work either:
$(foreach c,$(ALLMAKES),$(shell make -C $(c)))
Any ideas?
-
MadScientist about 11 yearsGenerally in my makefiles I don't like to run sub-makes in a shell loop like this, because (a) it reduces opportunities for parallelism with
-j
and (b) it is almost impossible to make it work properly with respect to the-k
option. But, it is a quick and dirty solution that answers the specific question and will work. I'll add an alternative which is "cleaner". -
Olivier Trahan about 11 yearsThank you! That last suggestion did the trick. I will take notice of your other suggestions for future makefiles (since I won't need $(CURDIR) if I use relative paths)
-
Olivier Trahan about 11 yearsI also want to add a clean rule for this... so call each of those "effects" the same way except by adding a clean at the end of the command like so: "make -C impl/thing1 clean" Is there a way to do this, since I can't duplicate the same target?
-
Olivier Trahan about 11 yearsFor now, I am using your first suggestion with the foreach call and making a separate all and clean rule.