In Makefiles, how to re-enable --print-directory in a sub-directory when --no-print-directory has been set? - makefile

First, let me say that I am aware of the cons of using recursive Makefiles. So if you are here just to tell me don't use it, please don't.
Imagine this directory structure:
rootdir
`-- subdir
|-- a
|-- b
`-- c
Let's say the Makefile on rootdir reads like this:
.PHONY: all
all:
# build some stuff
$(MAKE) -C subdir
and the one in subdir reads like this:
.PHONY: all
all:
# nothing here except redirecting make to each of the subdirectories
$(MAKE) -C a
$(MAKE) -C b
$(MAKE) -C c
and another Makefile in each of a, b and c folders building something.
Since the Makefile in subdir serves no purpose except redirecting make, I want make not to print: Entering directory rootdir/subdir and Leaving directory rootdir/subdir to clean up the output a bit.
On the other hand, since there are commands being executed in the subfolders a, b and c, I do want make to print these outputs. Here's what I thought would work:
rootdir's Makefile:
.PHONY: all
all:
# build some stuff
$(MAKE) --no-print-directory -C subdir
subdir's Makefile:
.PHONY: all
all:
# nothing here except redirecting make to each of the subdirectories
$(MAKE) --print-directory -C a
$(MAKE) --print-directory -C b
$(MAKE) --print-directory -C c
The problem is, once the --no-print-directory is given to make when calling make for subdir, --print-directory doesn't enable it again when calling make for a, b or c.
So my question is, how can I re-enable printing directories when a parent make has disabled it?

Make command line flags get communicated to sub-makes via MAKEFLAGS variable. You may like to replace --no-print-directory (if any) from MAKEFLAGS with w manually before invoking the sub-makes:
${MAKE} MAKEFLAGS="$(subst --no-print-directory,w,${MAKEFLAGS})" -C ...

Related

How to call a bunch of subdirectories Makefile from a top level Makefile with different targets

I have a top level Makefile that define a list of subdirectories containing sub-Makefiles.
Each sub-Makefile define the same targets: all, install, clean, test, verify, style...
How do I make the top level Makefile call all my subdirectories Makefile with the same target ?
It works for the default target, like this:
SUBDIRS = dir1 dir2 dir3
all: subdirs
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
.PHONY: subdirs $(SUBDIRS)
But how do I extend that to the other non default targets ?
If you use GNU make you could use the MAKECMDGOALS variable and the filter function:
SUBDIRS := dir1 dir2 dir3
TARGETS := all install clean test verify style
$(TARGETS): subdirs
#echo making top $#
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(filter $(TARGETS),$(MAKECMDGOALS))
.PHONY: subdirs $(TARGETS) $(SUBDIRS)
%::
#echo making top $#
Demo:
$ make --no-print-directory all install foobar
make -C dir1 all install
make -C dir2 all install
make -C dir3 all install
making top all
making top install
making top foobar
Note that filter is needed to avoid passing top-only goals to the sub-makes.
If, for any reason, this MAKECMDGOALS based solution does not work for you, here are some other, but a bit more complex, solutions.
Target-specific variables
Target-specific variables propagate to prerequisites. So you can define a pattern-specific variable (SUBGOAL) for each of your top-level goals and use it in the subdir's recipe:
SUBDIRS := dir1 dir2 dir3
all: SUBGOAL := all
all: subdirs
#echo making top $#
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(SUBGOAL)
.PHONY: subdirs $(SUBDIRS)
If you have many such top-level goals and use GNU make you can factorize a bit with the foreach and eval GNU make functions:
SUBDIRS := dir1 dir2 dir3
TARGETS := all install clean test verify style
$(foreach t,$(TARGETS),$(eval $(t): GOAL := $(t)))
$(TARGETS): subdirs
#echo making top $#
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(GOAL)
.PHONY: subdirs $(SUBDIRS) $(TARGETS)
One important drawback is that as you use the same phony subdirectory target (dir1) for several top phony targets (all, install...) you cannot invoke make with several top goals:
$ make --no-print-directory all install
make -C dir1 all
make -C dir2 all
make -C dir3 all
making top all
making top install
will only make all in the subdirectories. If this is a problem you can use the next solution.
One phony target (all-dir1) per top-subdirectory combination
Let's do this programmatically with a 2-variables macro and a foreach-eval-call combination (GNU make only):
SUBDIRS := dir1 dir2 dir3
TARGETS := all install clean test verify style
$(TARGETS): subdirs
#echo making top $#
TARGETS_X_SUBDIRS :=
# $(1): top target
# $(2): subdirectory target
define TARGETS_X_SUBDIRS_RULES
$(1): $(1)-$(2)
$(1)-$(2):
$(MAKE) -C $(2) $(1)
TARGETS_X_SUBDIRS += $(1)-$(2)
endef
$(foreach t,$(TARGETS),$(foreach s,$(SUBDIRS),$(eval $(call TARGETS_X_SUBDIRS_RULES,$(t),$(s)))))
.PHONY: $(TARGETS) $(TARGETS_X_SUBDIRS)
Demo:
$ make --no-print-directory all install
make -C dir1 all
make -C dir2 all
make -C dir3 all
making top all
make -C dir1 install
make -C dir2 install
make -C dir3 install
making top install
Renaud's answer is more complete, but I also found this technique that worked fine for my use case:
all:
%:
make -C dir1 $#
make -C dir2 $#
make -C dir3 $#
In this case, I don't have the subdirs list in a variable, but they are spelled out in the commands for the generic target %:. It requires to add an extra make -C in front of every subdirectory, and a # in the back, but otherwise pretty maintainable and easy to read.
But it probably would not work if there was any other targets specific to the top-level Makefile.

How to use make's --jobs with subdirs?

Build time of my C++ project is very big. It consists of a bunch of subprojects (libraries), structured in file system folders. I want to speed up it with parallel build with --jobs (-j) parameter of make. What is the correct way of doing it? Documentation says that there are some tricks of doing parallel build with subdirs.
Makefile in root directory looks like:
...
all:
$(MAKE) -C DirA
$(MAKE) -C DirB
...
Makefile in DirA:
all:
$(MAKE) -C SubDirA
$(MAKE) -C SubDirB
$(MAKE) -C SubDirC
In DirB:
all:
$(MAKE) -C SubDirD
$(MAKE) -C SubDirE
$(MAKE) -C SubDirF
And so on. Makefiles in leaf folders are quite simple, contain only build instruction without any tricks.
You can just add -j to make using these makefiles as-is and you'll get some parallelism, but you won't get maximum parallelism. The problem is that make only parallelizes targets: clearly it won't work (in general) to run multiple commands in the same target in parallel!
So, in the top level make will run $(MAKE) -C DirA then $(MAKE) -C DirB, serially. When make builds DirA, it will first run $(MAKE) -C SubDirA, then $(MAKE) -C SubDirB, etc. serially. Then finally when make gets into SubDirA, it will build the targets there in parallel. This is fine, and maybe what you need to do if the order of building directories is important, except that there will be times when make could start working on SubDirB targets but won't, until all targets in SubDirA are complete.
A better way to handle subdirectories is to use make rules:
SUBDIRS := DirA DirB
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
.PHONY: $(SUBDIRS)
and ditto for the subdirectories. Note you must include the .PHONY.
Since each directory is a separate target, now make can invoke them in parallel if you use -j.
Oh, and if some of the contents of some subdirectories depends on others, you can declare that dependency explicitly in the makefile so make know about it:
SUBDIRS := SubDirA SubDirB SubDirC SubDirD
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
.PHONY: $(SUBDIRS)
SubDirA: SubDirD
etc.

Makefile Automatic Target Generation

I have the following directory tree
moving_files/
Makefile
source/
a
b
c
target/
With my Makefile I want to cp every file in source/ to target/.
The catch: I want to be able to move other files to source/ afterwards without having to edit the Makefile. For this purpose I have written this:
FILES = $(filter-out Makefile, $(wildcard source/*) )
all: $(subst source,target,$(FILES))
$(subst source,target,$(FILES)): $(FILES)
cat $< >| $#
And it works fine.
However, when I execute touch source/d afterwards and make once again, in addition to d, a, b and c get cated, as well. What do I have to do in order to change this behavior.
$(subst source,target,$(FILES)): $(FILES)
expands to
target/a target/b target/c: source/a source/b source/c
which means that each single target depends on all files in source, probably not what you intended. Either a static or an implicit rule can fix this, static rules are generally better as they are more specific:
$(subst source,target,$(FILES)): target/%: source/%

How to tell make to watch dependencies of a sub-make target?

I was investigating on the same question here, but I was not very clear of what I was asking, even for myself. Sorry for those who spent time answering my unclear question.
So let's try again with a more realistic example. We consider this structure:
.
├── Makefile
└── src/
├── bar
├── foo
└── Makefile
Where the main Makefile is:
all: src/foobar
src/foobar:
make -C $(dir $#)
And the sub-makefile is:
foobar: foo bar
join $^ > $#
If I run make for the first time (from ./) everything works as expected, foobar is produced.
$ make
make -C src/
make[1]: Entering directory '/project/src'
join foo bar > foobar
make[1]: Leaving directory '/project/src'
$ make
make: Nothing to be done for 'all'.
However if I touch any of the foobar dependencies. The parent Makefile will not regenerate the target. Here, I perfectly understand the behavior of Make, but I want to tell it to be aware of foobar' dependencies.
$ touch src/foo
$ make
make: Nothing to be done for 'all'.
My current solution which is not very neat is to ask for the dependencies. So the src/Makefile become:
src=foo bar
foobar: $(src)
#echo "Joining"
join $^ > $#
files: $(src)
#echo $^
And the ./Makefile:
all: src/foobar
src=$(addprefix src/,$(shell make --no-print-directory -C src files | tr '\n' ' '))
src/foobar: $(src)
make -C $(dir $#)
I must also say that this particular example could be simplified using a single Makefile only. My real example is quite more complex. The src/Makefile generate an executable while the parent Makefile do lots of other things (packing in a specific format, generate the documentation, build other sub-makefiles and so on). Thus, I want to keep these tasks well separated and I need to different Makefiles.
In the main Makefile create a dependency for the child target or directory that is always in need of building, and let the child Make then do the real work.
There is a good example here: http://owen.sj.ca.us/~rk/howto/slides/make/slides/makerecurs.html.
To translate for your case, change your main Makefile to be:
all: src/foobar
src/foobar: force
$(MAKE) $(MFLAGS) -C src
force:
true
I also added $(MFLAGS) which will pass same flags from parent to child make.

Run make in each subdirectory

I have a directory (root_dir), that contains a number of sub-directories (subdir1, subdir2, ...).
I want to run the make in each directory in root_dir, using a Makefile placed in it.
(Obviously supposed that each of subdir... has inside its own Makefile).
So there are essentially two questions:
How to get a list of directories in Makefile (automatically)?
How to run make for each of the directories inside a make file?
As I know in order to run make in a specific directory I need to do the following:
$(MAKE) -C subdir
There are various problems with doing the sub-make inside a for loop in a single recipe. The best way to do multiple subdirectories is like this:
SUBDIRS := $(wildcard */.)
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
.PHONY: all $(SUBDIRS)
(Just to point out this is GNU make specific; you didn't mention any restrictions on the version of make you're using).
ETA Here's a version which supports multiple top-level targets.
TOPTARGETS := all clean
SUBDIRS := $(wildcard */.)
$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)
.PHONY: $(TOPTARGETS) $(SUBDIRS)
Try this :
SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
done
This may help you link
Edit : you can also do :
The simplest way is to do:
CODE_DIR = code
.PHONY: project_code
project_code:
$(MAKE) -C $(CODE_DIR)
The .PHONY rule means that project_code is not a file that needs to be
built, and the -C flag indicates a change in directory (equivalent to
running cd code before calling make). You can use the same approach
for calling other targets in the code Makefile.
For example:
clean:
$(MAKE) -C $(CODE_DIR) clean
Source
This is another approach to MadScientist's answer. .PHONY is a GNU-specific feature that can be used to force make into recursing into each subdirectory. However, some non-GNU versions of make do not support .PHONY, so an alternative is a force target.
4.7 Rules without Recipes or Prerequisites
If a rule has no prerequisites or recipe, and the target of the rule
is a nonexistent file, then make imagines this target to have been
updated whenever its rule is run. This implies that all targets
depending on this one will always have their recipe run.
An example will illustrate this:
clean: FORCE
rm $(objects)
FORCE:
Here the target ‘FORCE’ satisfies the special conditions, so the
target clean that depends on it is forced to run its recipe. There is
nothing special about the name ‘FORCE’, but that is one name commonly
used this way.
As you can see, using ‘FORCE’ this way has the same results as using
‘.PHONY: clean’.
Using ‘.PHONY’ is more explicit and more efficient. However, other
versions of make do not support ‘.PHONY’; thus ‘FORCE’ appears in many
makefiles. See Phony Targets.
The following is a minimal example that recurses make into each subdirectory, each of which presumably contains a Makefile. If you simply run make, only the first subdirectory, which is non-deterministic, is processed. You may also run make subdir1 subdir2 ....
# Register all subdirectories in the project's root directory.
SUBDIRS := $(wildcard */.)
# Recurse `make` into each subdirectory.
$(SUBDIRS): FORCE
$(MAKE) -C $#
# A target without prerequisites and a recipe, and there is no file named `FORCE`.
# `make` will always run this and any other target that depends on it.
FORCE:
Here is another example with top-level phony targets: all and clean. Note that the all and clean targets, passed from command-line via $(MAKECMDGOALS), are handled by each subdirectory's all and clean targets respectively.
# Register all subdirectories in the project's root directory.
SUBDIRS := $(wildcard */.)
# Top-level phony targets.
all clean: $(SUBDIRS) FORCE
# Similar to:
# .PHONY: all clean
# all clean: $(SUBDIRS)
# GNU's .PHONY target is more efficient in that it explicitly declares non-files.
# Recurse `make` into each subdirectory
# Pass along targets specified at command-line (if any).
$(SUBDIRS): FORCE
$(MAKE) -C $# $(MAKECMDGOALS)
# Force targets.
FORCE:
You can also define a function in the Makefile (also you of course need an additional makefile in each subdirectory). This is shell-dependent, but can be useful:
define FOREACH
for DIR in packages/*; do \
$(MAKE) -C $$DIR $(1); \
done
endef
.PHONY: build
build:
$(call FOREACH,build)
.PHONY: clean
clean:
$(call FOREACH,clean)
.PHONY: test
test:
$(call FOREACH,test)
Only a small icing on the cake after MadScientist's answer in order to make all the individual targets in the sub-directories available from the top level (you will need to have the SUBDIRS variable defined in order to use the following snippet – you can use MadScientist's answer for that):
# Make all the individual targets in the sub-directories available from the top
# level; as in, for instance, `make foo/my_program` or `make bar/clean`
$(foreach __dir__,$(SUBDIRS),$(__dir__)/%):
#$(MAKE) -C '$(#D)' '$(#F)'
With the code above you can run, for instance,
make foo/my_program
or
make bar/clean
Furthermore, by pasting the code above you can even use an individual target from a sub-directory as a prerequisite for a target in the top level. For example:
my_target: my_subdirectory/my_prerequisite
'my_subdirectory/my_prerequisite' > 'my_target'
…With the example above, launching make my_target from the top level will first build the my_subdirectory/my_prerequisite program, then the latter will be run for building the my_target file.
Since I was not aware of the MAKECMDGOALS variable and overlooked that MadScientist has its own implementation of multiple top-level targets, I wrote an alternative implementation. Maybe someone find it useful.
SUBDIRS := $(wildcard */.)
define submake
for d in $(SUBDIRS); \
do \
$(MAKE) $(1) --directory=$$d; \
done
endef
all:
$(call submake,$#)
install:
$(call submake,$#)
.PHONY: all install $(SUBDIRS)
There is a library called prorab for GNU make which supports inclusion of standalone makefiles in subdirectories.
Some info on github: https://github.com/cppfw/prorab/blob/master/wiki/HomePage.adoc
Basically, with prorab invoking all makefiles in subdirectories looks like this:
include prorab.mk
$(eval $(prorab-build-subdirs))
In reference to https://stackoverflow.com/posts/17845120/revisions
This is what I learned from that post.
Top Level Makefile
# set the default goal.
# I want the default to really just dump contents of dirs
# as a stub. For instance, I don't want it to
# push code or
.DEFAULT_GOAL := deploy
TOPTARGETS := all clean
SUBDIRS := docs src
$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
echo "make arg is" $(MAKECMDGOALS)
$(MAKE) -C $# $(MAKECMDGOALS)
SUBCLEAN = $(addsuffix .clean,$(SUBDIRS))
clean: $(SUBCLEAN)
$(SUBCLEAN): %.clean:
$(MAKE) -C $* clean
deploy:
echo do deploy stub
The src/ and docs/ common to this Makefile directory, all have a corresponding Makefile.
Here is an example of the docs setup:
# set the default goal.
.DEFAULT_GOAL := list_docs
list_docs:
ls -l
clean:
echo "docs: make clean"
-rm "*.backup"
I did this a little different than any of the answers because I didn't want to have to define each possible make target
SUBDIRS := $(patsubst %/,%,$(wildcard */))
.PHONY: all $(MAKECMDGOALS) $(SUBDIRS)
$(MAKECMDGOALS) all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)

Resources