I'm seeing unexpected results for target-specfic variables in GNU make.
What I want is to set a target-specific variable that affects dependencies. I can use .SECONDEXPANSION to achieve that.
some-target: DEP := debug-dep
debug: some-target
.SECONDEXPANSION:
some-target: $$(DEP)
#echo $^
debug-dep:
make debug prints debug-dep.
Now I read that make defines target-specific variables for descendant rules:
When you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc.
But when I change my makefile to have the variable set on "parent" target:
debug: DEP := debug-dep
debug: some-target
.SECONDEXPANSION:
some-target: $$(DEP)
#echo $^
debug-dep:
and do make debug I get a blank line.
This seems to contradict the documented behavior of target-specific variables. Is there something that I am missing?
This is sort of similar to make: Using target specific variables in prerequisites, but what I am doing isn't working.
I believe the issue here is that target-specific variables are not set until the targets are being run (if you add #echo '$(DEP)' to your some-target body in that second case you will see that it is set) but that second expansion happens immediately after the initial read-in phase.
I was going to say that I'm actually surprised that this works in the first case at all (and speculate as to why) but then I pulled up the manual for a minute and while reading about .SECONDEXPANSION I found the following:
[T]he true power of this feature only becomes apparent when you discover that secondary expansions always take place within the scope of the automatic variables for that target. This means that you can use variables such as $#, $*, etc. during the second expansion and they will have their expected values, just as in the recipe. All you have to do is defer the expansion by escaping the $. Also, secondary expansion occurs for both explicit and implicit (pattern) rules.
Which explains your behaviour exactly. The expansion only sees variables set in the scope of the target and the prerequisite inheriting only happens at target evaluation time (since it depends on target prerequisites).
All variables are set in the first read-in phase. It is true no matter it is a global variable, or a target-specific variable (or a pattern-specific variable).
There is no such thing that is like the other answer says
target-specific variables are not set until the targets are being run
See the example Makefile below,
GA = ga
GB = $(GA)
GC := $(GA)
a: aTA = $(GA)
a: aTB := $(GA)
a: aTC = $(aTA)
a: b;
GA = ga2
b: bTA = $(aTA)
b: bTB := $(aTA)
b: bTC := $(bTA)
b:;
After the first read-in phase, all variables are set (therefore, aTB is ga, not ga2). They are as follows respectively,
GA is literal ga2
GB is literal $(GA)
GC is literal ga
aTA is literal $(GA)
aTB is literal ga
aTC is literal $(aTA)
bTA is literal $(aTA)
bTB is literal (empty)
bTC is literal (empty)
Although they are all set, they are not visible everywhere, they have scope. E.g.,
Global variables' scope is everywhere
Target-specific variables' scope is
the target's other target-specific variables
the recipe of the target
the recipes of the target's prerequisites
This is the only reason that we could see bTA is eventually expanded to ga2 if we use bTA in the target b's recipe.
the prerequisite-literal appearing in the target's rule when using .SECONDEXPANSION
This is why the 1st case in OP's question works.
The following is NOT the scope of a target-specific variable,
everywhere in the parent target
the target-specific variables of the target's prerequisites
This is why bTB and bTC are both empty.
the (sub) prerequisite-literal appearing in the rule of the target's prerequisites (whether using .SECONDEXPANSION or not)
This is why the 2nd case in OP's question doesn't work.
Related
I am designing a simple makefile that defines one target which takes an argument, and I would like to define a second target that invokes the first target in a loop, once per every variable defined in an array variable at the top of the Makefile.
my_loop_var = var1 var2
my_thing:
echo $MY_VAR
all:
invoke my_thing once for each value of my_loop_var
What is the right way to solve this?
Note: $MY_VAR will probably expand as Y_VAR because make will try to expand variable M, which is probably undefined, and concatenate Y_VAR to the result. Use $(VARNAME) to expand make variable VARNAME. It is only with single-character variable names that you can expand with $X.
There is not really a "right way". There are many ways. The most straightforward from your specifications (but the less natural) would be something like:
my_loop_var := var1 var2
my_thing:
echo "$(MY_VAR)"
.PHONY: all my_thing
all:
for v in $(my_loop_var); do $(MAKE) my_thing MY_VAR="$$v"; done
The recipe of all is a shell loop that re-invokes make with the my_thing goal and with each value in my_loop_var passed as the value of make variable MY_VAR.
Note the $$v to escape the first expansion that make always performs before passing the recipe to the shell. With just $v make would expand the recipe as ... MY_VAR="" ... and the result would not be what you expect.
Note also the use of the MAKE make variable instead of directly calling make (have a look at the documentation if you want to understand why it is better like this).
all and my_thing are declared as phony because they are not real file names that their recipes create, and make needs to know this kind of things.
But a much more make-ish way would be something like:
my_loop_var := var1 var2
my_thing_targets := $(addprefix my_thing_,$(my_loop_var))
.PHONY: all $(my_thing_targets)
all: $(my_thing_targets)
$(my_thing_targets): my_thing_%:
echo "$*"
Here we define as many my_thing_something targets as there are something values in my_loop_var. And explain make how to build such targets with a static pattern rule. In the recipe of a pattern rule the $* automatic variable expands as the part that matched the % pattern. So, this static pattern rule says that if we need to build my_thing_something, the recipe is echo "something".
We declare all these my_thing_something targets as prerequisites of all such that if you type make or make all, make will build all the my_thing_something targets.
This second solution is better for at least two reasons. First, make is invoked only once, which is better, at least for performance. Second, make can parallelize the build of the my_thing_something if you use the -j N option (to allow make to run up to N jobs in parallel). This also is better for performance.
But it is also a matter of style. Very frequently if you use shell loops in your recipes, especially to invoke make again, it is the sign that you did not really understand what make is intended for and how it works. The make language is not a scripting language (even if the recipes are written in shell language, which is a scripting language); make is designed to "loop" over all targets to build, without the need for explicit loops.
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?
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 $#
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
I want to define a variable differently depending on another variables value in a makefile. I thought using conditionals would solve the problem, like this in the makefile:
ifeq ($(BOOT_FLAG),installed)
BOOT_TEST=$(BOOT_FLAG)
else
BOOT_TEST=no
endif
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
I expected the output to be:
installed
installed
but I got this instead:
no
installed
apparently the ifeq does not expand the BOOT_FLAG to installed
but setting of the BOOT_DEFINE variable manages to expand it correctly.
I read in the manual that:
"make evaluates conditionals when it reads a makefile. Consequently, you cannot use automatic variables in the tests of conditionals because they are not defined until commands are run"
but the BOOT_FLAG is not an automatic variable. Also if I move the definition of BOOT_FLAG to before the ifeq, then it works as I want it. However, I want to keep the current order of the definitions (and I don't understand why make does an exception to the order independence of the definitions when using conditions)
The answer is right there in the statement you quoted:
make evaluates conditionals when it reads a makefile.
Since make has evaluated the conditional when it read that line in the makefile, and the variable has not been defined when it read that line, there's no way that variables set after the conditional can take effect.
Just because the documentation lists one consequence of this behavior (the one that most people get confused by) doesn't mean that this is the only consequence of this behavior.
However, I want to keep the current order of the definitions
You can't.
(and I don't understand why make does an exception to the order independence of the definitions when using conditions)
It would be virtually impossible, and even if it could be done the resulting behavior would be almost indecipherable except in the most trivial situations. If you don't believe me, try to write down an algorithm describing how that could work. Remember to consider things like simple variable assignments, nested conditionals, variables used in target and prerequisite lists, variables that are intentionally reset in different parts of makefiles, etc.
ETA You could do it, by putting the ifeq into a define variable then using eval later, after BOOT_FLAG is set, to expand it. Seems gross to me but...
This is because makefile is evaulating the ifeq as it parses the file.
So when it gets to the ifeq..., then BOOT_FLAG is yet not set, so BOOT_TEST = no
Then you set BOOT_FLAG.
Then once all the variables are parsed, makefile will go through and run your rule - so in this case BOOT_DEFINE is evaluated to $(BOOT_FLAG) final value of installed
Try this:
$(info start - BOOT_FLAG=$(BOOT_FLAG))
ifeq ($(BOOT_FLAG),installed)
BOOT_TEST=$(BOOT_FLAG)
else
BOOT_TEST=no
endif
$(info after if - BOOT_FLAG=$(BOOT_FLAG))
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
$(info after assignment - BOOT_FLAG=$(BOOT_FLAG))
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
You will see various values printed at different times during the makefile parsing. On the first pass it evaluates the variables (and if's) and then on the second pass it can do the target rules.
As others noted the problem is that ifeq is expanded and evaluated in-place.
If you want to postpone the evaluation until some late moment, you must keep the whole expression inside of a recursive variable. Then the conditional could be implemented by $(if ...) function, instead of ifeq (okay, $(eval ifeq...) should also be doable, but... well, gross).
Of course, this is quite an overhead for such simple case, but nonetheless it could be done like this:
BOOT_TEST=$(if $(subst _installed,,_$(BOOT_FLAG)),no,installed)
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
I maintain a project which provides makefile for Arduino
There is a master (common) makefile and the users create a small makefile where they set certain environmental variables and then include the master makefile.
A typical makefile looks likes this
# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile
BOARD_TAG = atmega328
ARDUINO_PORT = /dev/ttyACM*
include $(ARDMK_DIR)/arduino-mk/Arduino.mk
Recently, I was debugging a bug and then found out that if there is an additional space at the end of BOARD_TAG environmental variable, then it results in an error, which is not very intuitive for someone to fix.
My initial thought was to fix it by trimming the environmental variable inside the makefile. But on second thought I was not sure whether that is the correct way to fix this issue.
So my question is what is the recommended way to fix these kind of user input issues in makefile? Should I try to trim it or is there an alternate way to fix this?
To trim the variable "in place", just treat it as a simply expanded variable:
BOARD_TAG = atmega328
BOARD_TAG := $(strip $(BOARD_TAG))
If you use "=", it's a recursive expanded variable, so any expression on the right (e.g. $(strip ...)) will not be expanded until the variable itself is evaluated; if you try to put the variable itself on the right, Make warns you that that expression can't be evaluated because it goes into an infinite loop.
If you use ":=", it becomes a simply expanded variable; Make expands the right-hand side then and there, assigns the result to the variable and that's that.
Given this bit of Makefile:
# for pattern matching
$(OBJDIR) := build
# just to see if a level of indirection will work
my_dir = $(dir $(1))
$(OBJECTS) : $(OBJDIR)/% : $(HEADERS) $(SRCDIR)/% | % $(dir %) $(call my_dir,%)
#echo output-only = $|
This is a static pattern rule with order-only prerequisites.
Consider the target "build/utility/debug.js". The output of the above rule will be this:
output-only = utility/debug.js ./
The first component, "utility/debug.js", is properly copied from the stem (%).
The second component, "./", is the output of calling the dir function in the prerequisites list.
The third component, an empty string, is the output of calling my my_dir function in the prerequisites list.
If I change my_dir to this:
my_dir = $(1)
The output remains the same. If I change it to this:
my_dir = "foo"
Then make complains there is no rule to make "foo" (which is expected). It appears, then, that $(1) is not getting bound in the call to my_dir.
What's going on? Why can't I pass the stem to a function? I have a workaround that uses secondary expansion, but I want to know why I can't write my rule this way.
EDIT: I'm new to stackoverflow, forgive me if this is not the way things are done here.
I want $(1) because I am passing the stem as an argument to my_dir, as Alex pointed out.
I don't know why it was suggested I want "$". I don't believe $ by itself expands to anything in any context.
I know that automatic variables are only available in the recipe. I am not using an automatic variable in the prerequisites - I am using the stem:
Each target is matched against the target-pattern to extract a part of the target name, called the stem. This stem is substituted into each of the prereq-patterns to make the prerequisite names (one from each prereq-pattern). - the manual
The fact that the stem is available is demonstrated by the example: the stem expands to the correct value when used alone, but not when passed to a function.
As can be seen in this section of the GNU make manual, variable and function references in the list of prerequisites are immediately expanded, during the read in phase. This means, before any pattern matching is done, so the % has no special meaning yet; it is interpreted as a literal character in the two function references, $(dir %) and $(call my_dir,%), both having ./ as a result, which get merged in the reference to $| in the recipe.
I don't know of any other workaround than the one you already found, i.e. secondary expansion.
Note $1 is not a special variable that expands to anything interesting related to pattern rules (or static pattern rules). The $1 variable only has unique behavior within the context of a user-defined macro invoked by the $(call ...) function.
You wanted to use $*, not $1; $* is an automatic variable which expands to the stem of the target of the rule.
However, in all versions of make (including the POSIX standard definition of make), automatic variables (including $*, $<, $#, $^, etc.) are only available in the context of the recipe. They are not available in the context of the target or prerequisite lists. See the GNU make manual section on Automatic Variables for more details.
As you suggest, there is a GNU make-specific feature enabled by the .SECONDEXPANSION pseudo target which provides a way to avoid this limitation.