Makefile: Setting env var for one build step - makefile

I'm using GNU make and Makefiles. Is it possible to set an environment variable for one build step?
Currently I'm doing something like this:
VAR:=valueA
step1:
echo $(VAR)
Then, "make step1" will print "valueA".
Is it possible to redefine the env var for one build step?
VAR:=valueA
step1:
echo $(VAR)
step2:
VAR:=valueB
echo $(VAR)
I want "make step1" to print "valueA", but "make step1" to print "valueB".

First, those are not environment variables, they're make variables. It's really important to understand and remember the difference.
Second, yes, you can do this with target-specific variables:
VAR := valueA
step1:
echo $(VAR)
step2: VAR := valueB
step2:
echo $(VAR)
If you really do want the value to be present in the child process's environment, you can use make's export keyword:
export VAR := valueA
step1:
echo $$VAR

To complement MadScientist's answer:
if you actually need to set an environment variable because a command you call expects it to be set, you need to do it within the shell commands that are your recipes:
VAR := value0
all: step1 step2
step1: VAR := value1
step2: VAR := value2
all step1 step2 step3:
ENVVAR=$(VAR) sh -c 'echo the nested sh for $# has ENVVAR = $$ENVVAR'

Related

pass variable from sub-make to main make

up make level could export variable X, and sub-make could use the variable X. is there a way do opposite? I want sub make pass variable to upper level make.
following code won't work.
Makefile
a:
make -f eval_X.mk
echo $(X)
eval_X.mk
b:
$(eval X=123)
Each make target commands run as a sub shell. So there is no existience of variable outside single command. Take a look at this if it helps:
VAR_X = 11
test:
$(eval VAR_X=123)
#echo ${VAR_X} // output : 123
test2:
#echo ${VAR_X} // output : 11
Also, you can use target dependencies to set the var:
a: $(eval X=123)
echo $(X) // output : 123

Makefile splitting variable

I am having an set of event as INPUT in makefile and I would like to assign them with EVENT_1, EVENT_2...so on so forth. And I would also like to check the number of events in INPUT variable, is there a way I can do this?
I have tried the following:
INPUT=g1 g2
all:
for eventid in INPUT; do \
echo evenit; \
i+=1; \
done
echo i
seems that i only valid in the loop and I could not check the eventid and number of event outside the loop.
After this, I would like to make use of eventid and number to have targets like "generate_EVENT1". Is it possible?
Check the amount of elements in INPUT:
COUNT := $(words $(INPUT))
Generate a rule and create a folder for each element in INPUT:
INPUT := g1 g2
.PHONY: all
all: $(addprefix run_dir/generate_, $(INPUT))
run_dir/generate_%:
mkdir -p $#
You can also omit the INPUT variable declaration and call make like so:
make INPUT="g1 g2"

how using eval in makfile command change macros value with bash variable

I have a bash function inside the makefile command and want to change macros value.
Is it possible?
C_DFLAGS :=
gui :
parse_flags () { echo $$1; for word in $$1; do if [ $${word::2} = -D ] ; then $(eval C_D_FLAGS+=$${word}); fi ; done ; } ; parse_flags "-D/test -D/TEST"
#echo "C_D_FLAGS :$(C_D_FLAGS)"
$(eval) will be interpreted before your actual bash function call. You cannot update make variables from bash - it's a downstream process.
However, the code you try to run is fairly simple to replace with a native syntax, i.e.:
$ cat Makefile
C_D_FLAGS :=
gui: C_D_FLAGS += -D/test -D/TEST
gui:
#echo "C_D_FLAGS: $(C_D_FLAGS)"
$ make gui
C_D_FLAGS: -D/test -D/TEST
If the flags are provided from elsewhere, they can also be filtered, i.e.:
$ cat Makefile
C_D_FLAGS :=
gui: C_D_FLAGS += $(filter -D%,$(EXTRA_FLAGS))
gui:
#echo "C_D_FLAGS: $(C_D_FLAGS)"
$ make gui
C_D_FLAGS:
$ make gui EXTRA_FLAGS="-Isomething -DFOO -m32"
C_D_FLAGS: -DFOO

How to expand variables after entering custom define that wraps foreach?

I have many very similar $(foreach..) loops in a makefile that work like in the target_old target below:
define NL
endef
extras=some/path/
vars=a b c
all: target_old target_new
target_old:
# foreach and some multiple evals inside and some multiple commands inside
$(foreach var, ${vars}, \
$(eval extra := ${extras}${var}) \
#echo var is ${var} and extras is ${extra}$(NL) \
)
# my try to wrap it in a function
define foreach_vars
$(foreach var, ${vars},
$(eval extra := ${extras}${var}) \
$1$(NL) \
)
endef
target_new:
#echo this is wrong:
$(call foreach_vars, \
#echo var is ${var} and extras is ${extra} \
)
I have many multiple such foreach loops with all the same evals inside the foreach. So I wanted to wrap the foreach loop with the evals inside my own function in foreach_vars. So I don't have to $(eval extra := ${extras}${var}) inside each foreach call. I created target_new target to test it. I would want the output from both targets to be the same, make target_old prints:
$ make target_old
var is a and extras is some/path/a
var is b and extras is some/path/b
var is c and extras is some/path/c
However target_new doesn't pick the ${var} from inside the loop, and ${var} just expands to nothing:
$ make target_new
this is wrong:
var is and extras is
var is and extras is
var is and extras is
I guess this is because the expansion happens before entering the $(call...). Is there any method I can use to "defer" the expansion of arguments inside the $(call...) call until inside foreach inside my function? Is is possible to write a custom foreach-like macro in make? Is there just other method used to implement such functionality?
Yes, your problem comes from the expansion(s) that do not happen when you would like and in the order you would like.
Your use of make is quite unusual because you are using make constructs (foreach, eval, call...) in recipes that are normally plain shell. I guess you have a very good reason but wouldn't it be much easier if you were separating the make world and the shell world? Like in the following, for instance:
extras := some/path/
vars := a b c
target_old:
#for var in $(vars); do \
extra=$(extras)$${var}; \
echo var is $${var} and extra is $${extra}; \
done
It uses make variables (vars, extras) and shell variables (extra, var). The recipe is plain shell. Note the $$ used to escape the first expansion by make such that the shell expansion ${xxx} is done by the shell. Note also the line continuations (\) that form a single line recipe, despite the look. As each line of a make recipe is executed by a separate shell, this is needed to pass shell variables between commands of the shell script.
If you wish, you can also wrap the shell for loop in a make recursively expanded variable:
for = for var in $(vars); do $(1); done
target_new:
#$(call for,extra=$(extras)$${var}; echo var is $${var} and extra is $${extra})
${var} gets immediately expanded, so it needs to be escaped as $${var}. This itself does not fix the issue, since now $1 contains a literal ${var}, which does NOT get expanded within foreach. I would make a simple subst though to get it fixed, e.g.:
$ cat Makefile
define NL
endef
extras=some/path/
vars=a b c
define foreach_vars
$(foreach var, ${vars},
$(eval extra := ${extras}${var}) \
$(subst $$(var),$(var), \
$(subst $$(extra),$(extra), \
$(1))) \
$(NL) \
)
endef
target_new:
$(call foreach_vars, \
#echo var is $$(var) and extras is $$(extra) \
)
Output:
$ make target_new
var is a and extras is some/path/a
var is b and extras is some/path/b
var is c and extras is some/path/c
When make comes to build target_new (like when you type make target_new for instance):
It expands the whole recipeImportant: The recipe is expanded before firing up the shell
For each line of the resulting expansion, it passes one at a time to a fresh invocation of the shell
It's worth showing the expansion make does in painful detail. We have as the recipe:
#echo this is wrong:
$(call foreach_vars, \
#echo var is ${var} and extras is ${extra} \
)
First off, ${var} becomes empty, as is ${extra}
make is left with $(call foreach_vars, #echo var is and extras is ). Now for the call:
1 is set to #echo var is and extras is
make expands $(foreach var, ${vars}, $(eval extra := ${extras}${var}) $1$(NL) )
${vars} is a b c
First iteration:
var is set to a
Make evals extra := some/path/a
The expansion of the eval is empty however, and we are left with $1$(NL) (modulo some whitespace), leaving #echo var is and extras is
Second iteration: ${extra} becomes some/path/b, and we are again left with #echo var is and extras is
Last iteration: ${extra} becomes some/path/c, and we are again left with #echo var is and extras is
The final recipe then:
#echo this is wrong:
#echo var is and extras is
#echo var is and extras is
#echo var is and extras is
which produces the output you described.
So how do we avoid the early expansion of parameters?
Once nice solution is to stick the command-line you want into a variable,
and pass the name of that variable instead.
define foreach_vars # 1: variable containing command-line
$(foreach var,${vars},
$(eval extra := ${extras}${var}) \
${$1}$(NL) \
)
endef
cmds<target_new> = #echo var is ${var} and extras is ${extra}
target_new:
#echo this is right:
$(call foreach_vars,cmds<$#>)
Why mangle the variable name with the name of the target? Lookup tables are nice, and you may find many targets ending up with the same recipe.
cmds<target_new> = #echo var is ${var} and extras is ${extra}
cmds<target_beta> = ${MAKE} ${var}-${extra}
cmds<target_release> = script ${var} | eat ${extra}
target_new target_beta target_release:
$(call foreach_vars,cmds<$#>)
etc.

How to declare a deferred variable that is computed only once for all?

I have a shell program that takes ages to complete. As written, executing make build takes 4 x 2 seconds to complete because $(value) is computed for each file.
A solution is to declare value a deferred variable by using := instead of =.
Unfortunately this is not a solution either because it slows down the execution of make clean and any other targets by 2 seconds because value is computed for nothing.
value = $(shell sleep 2 && echo 42)
in = a b c d
out = $(addsuffix .out,$(in))
build: $(out)
%.out: %
echo $(value) > $< || [ rm $# -a true ]
init:
touch $(in)
clean:
rm -vf $(out)
How can I set a variable what is assigned only if used, but only computed once ?
Said differently, I would like build to take 2 seconds to complete and clean to be immediate.
I am not interested to a solution that involves conditionals in order to bypass the assignment of value if the target is not build.
An alternative solution would be this. Unfortunately in this case I need to check whether or not the shelve file needs to be regenerated.
value = $(cat shelve)
shelve:
sleep 2 && echo 42 > $# || [ rm $# -a true ]
in = a b c d
out = $(addsuffix .out,$(in))
build: $(out)
%.out: %
echo $(value) > $< || [ rm $# -a true ]
init:
touch $(in)
clean:
rm -vf $(out)
Here's a trick you can play:
value = $(eval value := $(shell cat shelve))$(value)
How this works: value is first assigned using recursive assignment so the value on the RHS is not expanded.
The first time value is expanded the make parser will first run the $(eval ...) which starts up a "new parser" for makefiles. In that parser, the content value := $(cat shelve) is evaluated. Here, value is a simple variable assignment so the RHS is expanded immediately and the $(shell ...) is run and assigned to value.
Remember make doesn't really have a concept of variable scope, so this value is just the same global value variable that we are setting in the outer parser.
Then the eval completes and expands to the empty string, and make continues parsing things. Here it finds the value $(value) and expands that... value now has the result from the eval, not the eval text itself, so that's what will be expanded.
Maybe this will help:
value = $(eval value := $(shell cat shelve))$(value)
Here value contains the string $(eval value := $(shell cat shelve))$(value)
Now you expand it:
%.out: %
echo $(value) > $< ...
Make starts to expand this recipe. It gets to $(value) and sees it needs to expand the variable value: since it's recursive it expands the value:
$(eval value := $(shell cat shelve))$(value)
First it expands the eval, which parses this:
value := $(shell cat shelve)
That sets the value variable as a simply-expanded variable, so the RHS is expanded immediately. Say the results of cat shelve are "foo", so value is now set to foo (and it's marked simply expanded).
That's the end of the eval, so then make starts the next part which is $(value), so it looks up the variable value and discovers it's a simply-expanded variable with the value foo.
One solution would be to turn that value into a regular file target that gets updated only when its prerequisites change. If you insist on rebuilding that target for every build, mark it as phony.
When clean target does not depend on that file, then it won't be rebuilt when you invoke make clean.
In
%.out: %
echo $(value) > $< || [ rm $# -a true ]
echo $(value) > $< updates the prerequisite, whereas make expects it to update the target only. Updating a prerequisite must be done by a separate rule with that prerequisite being the target.
You can make the assignment depend on the target name in $(MAKECMDGOALS):
ifneq ($(MAKECMDGOALS),clean)
value := $(shell sleep 2 && echo 42)
endif
See also the docs for details.

Resources