Makefile technique to rebuild on changes in build options
The make
(e.g. the GNU
implementation)
system is a very widespread of managing builds of compiled programs
and some other data processing (e.g. builds of documentation from
restructured text/markdown, sometimes builds of docker containers
etc).
In typical usage make
detects that a file input to the build
has changed (by observing its timestamp relative to the output) and
uses this to decide that a rebuild of this part of the system is
needed. But in this usage make
will not detect changes in the
way the output is built, e.g., a change in the optimisation flags to
the compiler, or change in the architecture, etc.
Here is a simple technique suitable for smaller projects that will detect changes in build flags (e.g., a change in optimisation level, change in CPU that is targeted etc). It can be simply extended to detect change in the compiler version and most other typical changes. For large projects I recommend a more fully featured build systems.
Update added 2024-07-19: If all you need is to rebuild
everything use the -B
make option, i.e., make -B
. This
will cause all targets to be-rebuilt regardless if they appear to be
needed to rebuilt or not.
Principle of operation
The idea is simple:
-
Construct a string containing the contents of all relevant variables; in case below all variables that end with FLAGS but can include the value of say CC F77 etc
-
Compute the hash of this string using external program
sha256sum
-
All objects (i.e. compilation outputs) to be build automatically have this has appended to their file (base) name
-
If flags change there the hash will change, and in turn, the dependencies will be rebuilt correctly
Here is an implementation which builds a very simple program:
CFLAGS= -O1
srcfiles:=t1.c
allflagsc:=$(foreach v,$(filter %FLAGS,$(.VARIABLES)),$($(v)))
flaghash:=$(strip $(shell echo $(allflagsc) | sha256sum | cut -d " " -f1 ) )
objfiles := $(foreach f,$(srcfiles), $(basename $f)-$(flaghash).o)
%-$(flaghash).o: %.c
gcc $(CFLAGS) -c $< -o $@
app: $(objfiles)
gcc -o app $^
Some notes on how this works:
-
The special variable
.VARIABLES
holds the names of all Makefile variables. We filter on this ($(filter %FLAGS,...)
) to select all variables which end with “FLAGS” and then construct a string of their values. -
This string is hashed using the external program sha256sum (
$(shell ...)
) -
The names of object files are appending the hash to the name of their source files
-
The implicit pattern
%-$(flaghash).o: %.c
separates out the hash part from the stem of the object name to be able to specify the source files as dependencies