Build flavors handling with Make
The goal of this paper isn’t to explain everything about
Make, but to offer a way to handle build flavors. The technique presented here has been checked with GNU make, and depends on it for some parts.
What are flavors?
Flavors are a related set of variable values that can be chosen at compile time. For instance you could have a debug flavor with the following variables defined:
while the corresponding release flavor would use
The choice of tool chain would be another example. We’ll simplify the presentation considering that we want the user to be able to chose the value of the variable
VAR by specifying the value of the variable
FLAVOR on the command line with the value
A simple switch
This is the basic structure. We just use the possibility to build a variable from the value of another:
FLAVOR=name1 VAR_name1=value1 VAR_name2=value2 VAR=$(VAR_$(FLAVOR))
Defining a value for
FLAVOR in the
Makefile allows to give it a default which can be overritten in the command line.
The variables have often a common part. There are two ways of reducing the redundancy in the definition. The most efficient and drastic way is to define that common part in the definition of
VAR, perhaps with an indirection:
FLAVOR=name1 VAR_COMMON=common part VAR_name1=value1 VAR_name2=value2 VAR=$(COMMON_PART) $(VAR_$(FLAVOR))
An alternative is to use the variable defining the common part in the flavored definitions. This is more verbose but allows a flavor to avoid using the common part.
FLAVOR=name1 VAR_COMMON=common part VAR_name1=$(VAR_COMMON) value1 VAR_name2=$(VAR_COMMON) value2 VAR=$(VAR_$(FLAVOR))
Defining default for unknown flavors
origin function allows to know if a variable has been defined. Use of
filter then allow to switch.
FLAVOR=name1 VAR_DEFAULT=valued VAR_name1=value1 VAR_name2=value2 VAR=$($(if $(filter undefined,$(origin VAR_$(FLAVOR))),$(VAR_DEFAULT),$(VAR_$(FLAVOR))))
Automatic rebuild when the flavor changes
As flavor controls the way the build is make, one has to be careful to clean the build artefacts when changing flavor with the techniques presented until now if one desires to have an homogenous build. If one want to ensure the homogeneity of the build, a way is to define a file holding a timestamp for the last flavor build, ensuring that only one such file exist and to make all the actions which depend on the flavor depend on it. Redefining pattern rules can be used for that (but not suffix rules). For instance, one could do
FLAVOR=name1 VAR_name1=value1 VAR_name2=value2 VAR=$(VAR_$(FLAVOR)) FLAVOR_TIMESTAMP=.timestamp-for-flavor-$(FLAVOR) %.o: %.c $(FLAVOR_TIMESTAMP) $(COMPILE.c) $(OUTPUT_OPTION) $< $(FLAVOR_TIMESTAMP): -rm .timestamp-for-flavor-* touch $@
Once you have the mechanism in place for automatic rebuild, you can save the current rule and use it as a default:
FLAVOR:=$(shell [ -f .last-flavor ] && cat .last-flavor || echo name1) VAR_name1=value1 VAR_name2=value2 VAR=$(VAR_$(FLAVOR)) FLAVOR_TIMESTAMP=.timestamp-for-flavor-$(FLAVOR) %.o: %.c $(FLAVOR_TIMESTAMP) $(COMPILE.c) $(OUTPUT_OPTION) $< $(FLAVOR_TIMESTAMP): -rm .timestamp-for-flavor-* echo $(FLAVOR) > .last-flavor touch $@
Note the use of
:= to define
FLAVOR so that the shell is not reevaluated each time
FLAVOR is used.
An other way to handle flavors which can be used alone or combined with the one offered here is to use inclusion of makefile fragments which depend on the flavor desired. The simplest example would be to use something like:
This offer a different set of trade-offs which will not be examined here.