How to combine makefiles? - makefile

I encountered a software that had three makefiles, one in the root directory and two in immediate subdirectories. Experimenting a bit it turned out that there is a good order of them:
$ make -C wordn all
$ make -C buses all
$ make all
from the root directory. What is an elegant way to rewrite the main makefile so that this sequence happens?

In the target all of the main Makefile you can add:
all:
$(MAKE) -C wordn all
$(MAKE) -C buses all
...
This will execute the submake before doing the all commands.
You can also create a dependency of the main all target:
all: suball
...
suball:
$(MAKE) -C wordn all
$(MAKE) -C buses all
.PHONY: suball
The suball target must be a PHONY target as it is a virtual target

Related

How to make a recursive make evaluate sub-directory pre-requisites?

Appreciating that the title is not quite on "target", how can I make it so that when I call make at the top level, it will recursively call the makefiles in the sub-directories?
Having been intrigued by the Kconfig pattern, to learn it, I've applied it to a mark down to pdf generator.
The recursive Makefile resides in ./scripts/Makefile.boilerplate and is defined:
HEADER=$(wildcard section-header.md)
.PHONY:all clean $(md-sub-y)
all clean: $(md-sub-y)
all: $(TARGET)
clean:
# $(RM) $(TARGET)
$(TARGET): $(HEADER) $(md-y) | $(md-sub-y)
# cat /dev/null $^ > $#
$(md-sub-y):
# $(MAKE) -C $(#D) TOPDIR=$(TOPDIR) $(MAKECMDGOALS)
I'm likely using the order-only prerequisite for the $(TARGET) target inappropriately, but it solved a minor problem.
In each directory there is a unique KConfig file (not shown), which lists CONFIG_{OPTION} macros that evaluate to either y or n. Then each directory contains a Makefile that has the form:
include Kconfig
md-$(CONFIG_INTRODUCTION)+= Introduction.md
md-$(CONFIG_FW_UPDATE)+= FW-update.md
md-sub-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_EXAMPLES)+= Examples.md
md-$(CONFIG_APPENDIX_I)+= Appendix-I.md
md-$(CONFIG_APPENDIX_II)+= Appendix-II.md
md-$(CONFIG_APPENDIX_III)+= Appendix-III.md
include ${TOPDIR}/scripts/Makefile.boilerplate
And finally, the very top level makefile is (abbreviated):
.PHONY: all clean pdf embedded_html
all clean test: JsonAPI/JsonAPI.md
all: pdf embedded_html
pdf: $(MARKDOWN_FILES:.md=.pdf)
embedded_html: $(MARKDOWN_FILES:.md=.html)
MAKEFLAGS += --no-print-directory
clean:
# $(RM) *.pdf *.html
JsonAPI/JsonAPI.md:
# $(MAKE) -C $(#D) TOPDIR=${CURDIR} $(MAKECMDGOALS)
%.html:%.md
# pandoc -s --toc -c /var/www/css/bootstrap.css $< -f markdown -t html -s -o $#
%.pdf:%.md
# pandoc --read=markdown --table-of-contents --toc-depth=3 --preserve-tabs --standalone --template=template.latex $(PANDOC_ENGINE)=pdflatex --listings -V geometry:margin=1in --highlight-style=pygments -H listing-setup.tex -r markdown+simple_tables+table_captions+yaml_metadata_block $< -o $#
If I call make on an unbuilt directory tree, it works fine. But there are a few problems I'm not sure how to address:
How can I ensure that if an updated .md deeply nested in the directory tree will cause the top level PDF file to be updated? Or, How can I force the makefile's in the sub-directories to be called?
The clean target at the top level is problematic, in that it doesn't recurse through the sub-directories. What do I need to do to remedy that?
Is there a better way to include the Makefile.boilerplate makefile, without having to define the TOPDIR on the $(MAKE) command line as I've done?
For 1, and 2, I'm guessing that an empty target dependency (FORCE:) will be required. And for 3, I've tried using $(CURDIR) but it was always evaluating to the directory the Makefile resided in, not the parent directory where the original make command was invoked.
Changing the md-sub-$(CONFIG_EEEE) macro definition to be just the directory was the key, and to make those targets have an empty rule.
Essentially, the per directory Makefile from above becomes:
include Kconfig
md-$(CONFIG_INTRODUCTION)+= Introduction.md
md-$(CONFIG_FW_UPDATE)+= FW-update.md
md-sub-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_CHAPTERS)+= Chapters
md-$(CONFIG_EXAMPLES)+= Examples.md
md-$(CONFIG_APPENDIX_I)+= Appendix-I.md
md-$(CONFIG_APPENDIX_II)+= Appendix-II.md
md-$(CONFIG_APPENDIX_III)+= Appendix-III.md
include ${TOPDIR}/scripts/Makefile.boilerplate
and the default Makefile.boilerplate changes the $(md-sub-y) target too:
$(md-sub-y): FORCE
# $(MAKE) -C $# TOPDIR=$(TOPDIR) $(MAKECMDGOALS)
FORCE:
And the top level makefile no longer needs $(#D) on the command line for the JsonAPI recipe, just $#.

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.

Calling other makefile from make

I have seen this question and am somehow still unable to get make to properly call another Makefile. Here is my Makefile:
base:
cd Base && $(MAKE)
Base is a sub-directory of my current directory where there is a different Makefile used to compile the files in said folder. However, when I call make, I simply get nothing to be done for base, whereas when I do cd Base && make from the command-line, I get expected behavior (i.e., compilation occurs). In case it is something wrong with the Makefile in Base, here is a copy of that as well:
CC=g++
CFLAGS=-O3 -Wall -pedantic -Werror -c
LINK=g++
LFLAGS=-O3
SRC=main.cpp
OBJ=..\lib\main.o
EXE=..\bin\test.exe
all: $(EXE)
$(EXE): $(OBJ) $(SRC)
$(LINK) $(LFLAGS) $(OBJ) -o $(EXE)
..\lib\main.o: main.cpp
$(CC) $(CFLAGS) $< -o $#
I'm not sure what $(MAKE) amounts to (I'm assuming just 'make'), but I have my makefiles setup in a cascading manner where one calls another in order to cover all subdirectories.
Each makefile has some common targets like 'all' and 'clean'.
Then my makefiles which are just delegating to subdirs look something like this:
.PHONY : all
all:
cd SubDir1 && make $#
cd SubDir2 && make $#
...
.PHONY : clean
clean:
cd SubDir1 && make $#
cd SubDir2 && make $#
...
$# is an automatic variable which will be substituted for the target ('all' or 'clean' in the example above).
This allows you to run 'make', 'make clean', 'make all', or even 'make clean all' from the top-level dir and it will recurse through the subdirs specified in the makefiles for the appropriate target. So if you run 'make clean' at the top, all directories in the makefile's list will be called with 'make clean'.
You can still use this technique even if you don't have common targets. Simply change the automatic variable $# with the appropriate target for each directory's makefile.

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)

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

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 ...

Resources