Olivier Trahan
Olivier Trahan

Reputation: 95

makefile: foreach "make -C" call

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?

Upvotes: 2

Views: 11954

Answers (1)

MadScientist
MadScientist

Reputation: 100816

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.

Upvotes: 10

Related Questions