Reputation: 2422
I have a two-level heirarchy: a code directory with a dozen or so sub-projects.
I want to be able to issue a gmake <target>
in the parent directory, for any target, and have it recurse into all subdirectories with containing a Makefile
and repeat the gmake there. Question 1 is: how?
I could manually manage the list of targets and list of subdirectories but I'm sure gmake can do this automatically, and I cannot think of why I would ever want a target or subdirectory excluded.
I used to use a shell loop to loop through subdirectories, but I'd guess there's a gmake way to do this.
The following is as far as I've gotten but it only works for the default target, not my wish. Also, it doesn't even go down into both directories I have, only one.
DIRS := ${dir ${wildcard */Makefile}}
.PHONY: $(DIRS)
$(DIRS):
echo $(DIRS)
cd $@; $(MAKE)
This produces the output:
> gmake
echo HelloWorld/ doc/
HelloWorld/ doc/
cd HelloWorld/; make
make[1]: Entering directory '/t/TimeLike/dev/TL3/code/HelloWorld'
g++ -fPIC -march=native -I. -I/l/demo/include -MT main.o -MMD -MP -MF .deps/main.d -c main.cpp
rm -rf ../../dist/Fedora31-64/HelloWorld
gcc -fPIC -march=native main.o -L. -L/l/demo/include -o ../../dist/Fedora31-64/HelloWorld
make[1]: Leaving directory '/t/TimeLike/dev/TL3/code/HelloWorld'
So question 2: why no Entering directory '/t/TimeLike/dev/TL3/code/doc'
? I suppose I don't need to know as the actual solution probably involves a very different approach but still curious.
Upvotes: 0
Views: 71
Reputation: 2422
Many thanks to MadScientist for pointing me in the right direction.
Here is the entire top-level makefile:
SUBDIRS = ${dir ${wildcard */Makefile}}
all $(MAKECMDGOALS):: $(SUBDIRS)
.PHONY: $(SUBDIRS)
$(SUBDIRS)::
$(MAKE) --no-print-directory -C $@ $(MAKECMDGOALS)
@echo -----------
@echo
clean veryclean::
rm -rf *~
The wildcard text function in gmake matches the name of all Makefile's in subdirectories, and dir strips the filename off leaving the directory.
Whatever the user specified to make (all, clean, etc.) is stored by gmake in $MAKECMDGOALS
. I say: whatever the user just typed, or, if they typed nothing, by default, all
depends on all those targets.
Then we tell gmake the targets aren't real files (or directories)(which they by definition are), but phony. They only exist for the purpose of firing off rules.
And in each of those subdirectories that had a Makefile
, we recurse. The -C
option basically does a cd
to that directory first. The --no-print-directory
option prevents some messages that annoy me about entering and leaving directories. Instead of those I prefer the separator and some blank space.
Finally, for a few other targets I also do some extra cleanup. The fact that there are multiple lines giving rules for these targets necessitates the double-colon here and on the $(MAKECMDGOALS)
line.
Upvotes: 0
Reputation: 100946
Let's expand the variable:
.PHONY: HelloWorld/ doc/
HelloWorld/ doc/:
echo HelloWorld/ doc/
cd $@; $(MAKE)
How does make treat a rule with multiple targets? This is identical in every way to this makefile:
.PHONY: HelloWorld/ doc/
HelloWorld/:
echo HelloWorld/ doc/
cd $@; $(MAKE)
doc/:
echo HelloWorld/ doc/
cd $@; $(MAKE)
So you can easily see why you always see both directories output regardless of what is being built.
And, since make always only builds the first explicit target in the makefile, you can see why it only builds HelloWorld/
and not doc/
. Generally you would add an initial target that depended on everything you wanted to be built when no target is provided:
.PHONY: all $(DIR)
all: $(DIRS)
$(DIR):
cd $@ && $(MAKE)
Upvotes: 1