A project to collect Makefile macros and structure for using recursive Makefiles in order to prevent recursive make.
For example, a normal recurive make would consist of the following two makefiles:
a: subdir1/b touch $@ subdir1/b: $(MAKE) -C subdir1 b
b: c touch $@ c: touch $@
In this situation make knows that it needs to rebuild a
if b
has been
modified, and also how to build b
if it does not exist, but does not know
that b
depends on c
, so will not rebuild a
and b
once they exist if
c
has been updated.
There is extensive literature on this subject, most famously Miller, Peter, "Recursive make considered harmful," AUUGN Journal of AUUG Inc 19.1 (1998), http://aegis.sourceforge.net/auug97.pdf .
common.mk tries to solve these issues through recursive Makefile inclusion. The main insights leading to common.mk are the following:
-
All targets must be relative
$(CURDIR)
, the working directory. -
All targets may only have one path, that is, the path must be canonical.
-
Makefiles must be included only once to avoid overwriting variables and targets.
-
The phony targets
all
andclean
must be possible to define in each Makefile.
These insights are implemented through the following extensions of normal make (using Makefile macros and variables):
-
A special variable
$(D)
which is defined as the directory of the current Makefile, calculated in the beginning of the Makefile from$(MAKEFILE_LIST)
, and then rewritten to canonical form bycommon.mk
:D := $(dir $(lastword $(MAKEFILE_LIST)))
-
A Makefile
common.mk
which must be included directly after the computation of$(D)
relative the current Makefile:include $(D)/common.mk
-
A macro for including other Makefiles,
include_dep_makefile
guaranteeing that these are included only once and that$(D)
will be restored after each inclusion:$(call include_dep_makefile,$(D)/subdir1/Makefile)
-
A macro for computing the canonical path towards a dependency located in another Makefile,
get_dep
:$(call get_dep,$(D)/subdir1/b)
-
Two phony targets,
$(all)
and$(clean)
which replaceall
andclean
in each Makefile, but which can be called withmake all
andmake clean
.
This allows for the construction of recursively included Makefiles as in the following example:
D := $(dir $(lastword $(MAKEFILE_LIST))) include $(D)/common.mk $(all): $(D)/a $(call include_dep_makefile,$(D)/subdir1/Makefile) $(D)/a: $(call get_dep,$(D)/subdir1/b) $(c) touch $@ $(clean): rm -f $(D)/a
D := $(dir $(lastword $(MAKEFILE_LIST))) include $(D)/../common.mk $(all): $(D)/b $(c) $(D)/b: touch $@ c := $(D)/d $(c): touch $@ $(clean): rm -f $(D)/b $(c)
As can be seen the target a
depends on both b
and c
in subdir1. It has the
canonical path to both, for b
by calling $(call get_dep,$(D)/subdir1/b)
and
for c
by using a variable $(c)
exported by subdir1/Makefile
. Both methods
of depending on targets defined in other Makefiles work, one is more verbose and
the other requires attention to namespace pollution in case there are multiple
targets with similar names.
The $(all)
target is listed first, but this is pure symbolism as common.mk
will already set the first target as all
. If this is to be changed the
.DEFAULT_GOAL
variable should be used.