1
votes

I have a complicated set of rules I need to write to generate a rather large number of "parameterised" output files and thought that, rather than expand them all out by hand, I could repeatedly "include" a template file with sets of rules and use (GNU)make's facility for allowing "simply expanded" variables to avoid the pain.

(In the past I've always been using the "recursively expanded" variable approach, so this is new to me)

As a trivial example of what I thought would work, I tried putting the following in a Makefile

Targ:=A
Param1:=Pa
Param2:=Qa
$(Targ):
    @echo expect A, get $(Targ), Target is $@. Params are $(Param1) and $(Param2) 

Targ:=B
Param1:=Pb
Param2:=Qb
$(Targ):
    @echo expect B, get $(Targ), Target is $@. Params are $(Param1) and $(Param2) 

Targ:=C
Param1:=Pc
Param2:=Qc
$(Targ):
    @echo expect C, get $(Targ), Target is $@. Params are $(Param1) and $(Param2) 

The eventual plan was to replace the rules with an include file containing dozens of different rules, each referencing the various "parameter" variables.

However, what I get is...

prompt> make A
expect A, get C, Target is A. Params are Pc and Qc


prompt> make B
expect B, get C, Target is B. Params are Pc and Qc

Essentially, unlike each rule's target, which is picking up the intended definition, the $(Targ), $(Param1), and $(Param2) in each rule's command is instead being run with the final definition.

Does anyone know how to prevent this, i.e. how do you force the command to use the definition at the time it is encountered in the Makefile?

2

2 Answers

2
votes

Simple vs recursive expansion makes no difference here; regardless of which you use you'll see the same behavior. A GNU make variable is global and obviously can have only one value.

You have to understand when variables are expanded. The documentation provides a detailed description of this. Targets and prerequisites are expanded when the makefile is read in, so the value of Targ as the makefile is being parsed is used.

Recipes are expanded when the recipe is to be invoked, which is not until after all makefiles are parsed and make starts to build targets. At that time of course the variable Targ has its last set value.

Without knowing what your makefile really does it's hard to suggest an alternative. One option is to use target-specific variables:

Targ := A
$(Targ): LocalTarg := $(Targ)
$(Targ):
        @echo expect A, get $(LocalTarg), Target is $@

Another option is to use constructed variable names:

Targ := A
Targ_$(Targ) := $(Targ)
$(Targ):
        @echo expect A, get $(Targ_$@), Target is $@
0
votes

Apologies for answering my own question, but I now realised it is possible to solve the issue I was having by running make recursively.

E.g. if the parameter variables for the rules are Targ, Param1 and Param2 then

#Set up "default" values for the parameters (As @madscientist points out, 
#these will safely be overridden by the defs on the @(make) commands below

Targ=XXXXXXXXXX
Param=XXXXXXXXXX
Param2=XXXXXXXXXX
Recursing=

#
# define N (==3) templated rule(s)
#
$(Targ)%a:
    @echo Run Combo_a $(Targ) $(Param1) $(Param2) $@

$(Targ)%b:
    @echo Run Combo_b $(Targ) $(Param2) $(Param1) reversed $@

$(Targ)%c:
    @echo Run Combo_c  $(Param1) $(Targ) $(Param2) mixed again $@

#
#Enumerate "M" (==2) sets of parameters, 
# (Except if we are already recursing because unrecognised targets may cause
# it to descend forever)
#
ifneq ($(Recursing), Yes)
Set1%:
    @$(MAKE) Targ=Set1 Param1=foo Param2=bar Recursing=Yes $@

Set2%:
    @$(MAKE) Targ=Set2 Param1=ray Param2=tracing Recursing=Yes $@
endif

This then allows N*M different combos for N+M typing cost. eg. (removing messages from make re recursion)

>make Set1.a
Run Combo_a Set1 foo bar Set1.a

>make Set2.c
Run Combo_c ray Set2 tracing mixed again Set2.c