Using the Dynamic Prerequisite Function
Having completed the three rules in our nifty prerequisite-generator function, let’s invoke it to create specific rules. One additional line in our enclosing makefile is all that is necessary:
$(eval $(call gen_file_if_different,grpA,type_X,prereqfile1,GENPROGRAM))
As mentioned in another article, $(eval ...)
is required to treat a multiple line chunk of text (such as that
returned from our new function) as rule-defining input to Make.
The function can be called many times, as long as the two argument values “grpA
” and “type_X
”
are unique for each call. These two strings form a unique prefix for
the pseudo-target name inside the the function definition. That
constructed psuedo-target name is then used as a prerequisite of the
downstream target during pass one. We’ll examine the downstream target
rules shortly. Each of the two values can be any string that is
meaningful to your build environment.
The third argument, prereqfile1
, is the point of all
this Make coding. This is the file that is a proper prerequisite to
another Make target. In our desired use model, we want to enable
recreating it (differently) “on-the-fly”, using Make command line
variable settings. Even so, the file should not be obsoleted if it
would be generated identically to the present version. This saves us
from a potentially expensive, unnecessary downstream target
(re-)construction.
The function call’s final argument, GENPROGRAM
, is the
name of a variable (not it’s value!). That variable holds the shell
command string used for creating the intermediate target file inside the
function definition. The contents of GENPROGRAM
are evaluated only when the dependency check is performed for the test version of “prereqfile1
“, which happens after all the makefiles are parsed. That means GENPROGRAM
may be set anywhere in the containing makefile. With the function
definition shown above, the command string must be self-contained. No
other variable expansion will be performed when executing $(GENPROGRAM)
inside the called function rules. Therefore, any command line switches
or argument values required by the generator command must be known when
GENPROGRAM
is assigned a value.
As a trivial example, GENPROGRAM
might be defined like this:
GENPROGRAM := echo "This is the life!"
The value can be anything that could be passed as a valid command string on the shell invocation command line (using the ‘-c
‘
switch). You can use Unix pipes and control constructs, so long as
they are represented in a single line (a semicolon separates individual
commands). Although still contrived, the following example shows a
3-command pipe:
GENPROGRAM := grep udp /etc/services | awk '{print $1, $2}' | sed -e 's^/.*^^'
Now let’s put it all together…