Swiss Frank
Swiss Frank

Reputation: 2422

recursive gmake in any subdir with Makefile

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

Answers (2)

Swiss Frank
Swiss Frank

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

MadScientist
MadScientist

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

Related Questions