pass variable from sub-make to main make - makefile

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

Related

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

Makefile do not reevaluate variable with every call [duplicate]

Can anybody give a clear explanation of how variable assignment really works in Makefiles.
What is the difference between :
VARIABLE = value
VARIABLE ?= value
VARIABLE := value
VARIABLE += value
I have read the section in GNU Make's manual, but it still doesn't make sense to me.
Lazy Set
VARIABLE = value
Normal setting of a variable, but any other variables mentioned with the value field are recursively expanded with their value at the point at which the variable is used, not the one it had when it was declared
Immediate Set
VARIABLE := value
Setting of a variable with simple expansion of the values inside - values within it are expanded at declaration time.
Lazy Set If Absent
VARIABLE ?= value
Setting of a variable only if it doesn't have a value. value is always evaluated when VARIABLE is accessed. It is equivalent to
ifeq ($(origin VARIABLE), undefined)
VARIABLE = value
endif
See the documentation for more details.
Append
VARIABLE += value
Appending the supplied value to the existing value (or setting to that value if the variable didn't exist)
Using = causes the variable to be assigned a value. If the variable already had a value, it is replaced. This value will be expanded when it is used. For example:
HELLO = world
HELLO_WORLD = $(HELLO) world!
# This echoes "world world!"
echo $(HELLO_WORLD)
HELLO = hello
# This echoes "hello world!"
echo $(HELLO_WORLD)
Using := is similar to using =. However, instead of the value being expanded when it is used, it is expanded during the assignment. For example:
HELLO = world
HELLO_WORLD := $(HELLO) world!
# This echoes "world world!"
echo $(HELLO_WORLD)
HELLO = hello
# Still echoes "world world!"
echo $(HELLO_WORLD)
HELLO_WORLD := $(HELLO) world!
# This echoes "hello world!"
echo $(HELLO_WORLD)
Using ?= assigns the variable a value iff the variable was not previously assigned. If the variable was previously assigned a blank value (VAR=), it is still considered set I think. Otherwise, functions exactly like =.
Using += is like using =, but instead of replacing the value, the value is appended to the current one, with a space in between. If the variable was previously set with :=, it is expanded I think. The resulting value is expanded when it is used I think. For example:
HELLO_WORLD = hello
HELLO_WORLD += world!
# This echoes "hello world!"
echo $(HELLO_WORLD)
If something like HELLO_WORLD = $(HELLO_WORLD) world! were used, recursion would result, which would most likely end the execution of your Makefile. If A := $(A) $(B) were used, the result would not be the exact same as using += because B is expanded with := whereas += would not cause B to be expanded.
I suggest you do some experiments using "make". Here is a simple demo, showing the difference between = and :=.
/* Filename: Makefile*/
x := foo
y := $(x) bar
x := later
a = foo
b = $(a) bar
a = later
test:
#echo x - $(x)
#echo y - $(y)
#echo a - $(a)
#echo b - $(b)
make test prints:
x - later
y - foo bar
a - later
b - later bar
Check more elaborate explanation here
When you use VARIABLE = value, if value is actually a reference to another variable, then the value is only determined when VARIABLE is used. This is best illustrated with an example:
VAL = foo
VARIABLE = $(VAL)
VAL = bar
# VARIABLE and VAL will both evaluate to "bar"
When you use VARIABLE := value, you get the value of value as it is now. For example:
VAL = foo
VARIABLE := $(VAL)
VAL = bar
# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"
Using VARIABLE ?= val means that you only set the value of VARIABLE if VARIABLE is not set already. If it's not set already, the setting of the value is deferred until VARIABLE is used (as in example 1).
VARIABLE += value just appends value to VARIABLE. The actual value of value is determined as it was when it was initially set, using either = or :=.
In the above answers, it is important to understand what is meant by "values are expanded at declaration/use time". Giving a value like *.c does not entail any expansion. It is only when this string is used by a command that it will maybe trigger some globbing. Similarly, a value like $(wildcard *.c) or $(shell ls *.c) does not entail any expansion and is completely evaluated at definition time even if we used := in the variable definition.
Try the following Makefile in directory where you have some C files:
VAR1 = *.c
VAR2 := *.c
VAR3 = $(wildcard *.c)
VAR4 := $(wildcard *.c)
VAR5 = $(shell ls *.c)
VAR6 := $(shell ls *.c)
all :
touch foo.c
#echo "now VAR1 = \"$(VAR1)\"" ; ls $(VAR1)
#echo "now VAR2 = \"$(VAR2)\"" ; ls $(VAR2)
#echo "now VAR3 = \"$(VAR3)\"" ; ls $(VAR3)
#echo "now VAR4 = \"$(VAR4)\"" ; ls $(VAR4)
#echo "now VAR5 = \"$(VAR5)\"" ; ls $(VAR5)
#echo "now VAR6 = \"$(VAR6)\"" ; ls $(VAR6)
rm -v foo.c
Running make will trigger a rule that creates an extra (empty) C file, called foo.c but none of the 6 variables has foo.c in its value.
The most upvoted answer can be improved.
Let me refer to GNU Make manual "Setting variables" and "Flavors", and add some comments.
Recursively expanded variables
The value you specify is installed verbatim; if it contains references to other variables, these references are expanded whenever this variable is substituted (in the course of expanding some other string). When this happens, it is called recursive expansion.
foo = $(bar)
The catch: foo will be expanded to the value of $(bar) each time foo is evaluated, possibly resulting in different values. Surely you cannot call it "lazy"! This can surprise you if executed on midnight:
# This variable is haunted!
WHEN = $(shell date -I)
something:
touch $(WHEN).flag
# If this is executed on 00:00:00:000, $(WHEN) will have a different value!
something-else-later: something
test -f $(WHEN).flag || echo "Boo!"
Simply expanded variable
VARIABLE := value
VARIABLE ::= value
Variables defined with ‘:=’ or ‘::=’ are simply expanded variables.
Simply expanded variables are defined by lines using ‘:=’ or ‘::=’ [...]. Both forms are equivalent in GNU make; however only the ‘::=’ form is described by the POSIX standard [...] 2012.
The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined.
Not much to add. It's evaluated immediately, including recursive expansion of, well, recursively expanded variables.
The catch: If VARIABLE refers to ANOTHER_VARIABLE:
VARIABLE := $(ANOTHER_VARIABLE)-yohoho
and ANOTHER_VARIABLE is not defined before this assignment, ANOTHER_VARIABLE will expand to an empty value.
Assign if not set
FOO ?= bar
is equivalent to
ifeq ($(origin FOO), undefined)
FOO = bar
endif
where $(origin FOO) equals to undefined only if the variable was not set at all.
The catch: if FOO was set to an empty string, either in makefiles, shell environment, or command line overrides, it will not be assigned bar.
Appending
VAR += bar
Appending:
When the variable in question has not been defined before, ‘+=’ acts just like normal ‘=’: it defines a recursively-expanded variable. However, when there is a previous definition, exactly what ‘+=’ does depends on what flavor of variable you defined originally.
So, this will print foo bar:
VAR = foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))
but this will print foo:
VAR := foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))
The catch is that += behaves differently depending on what type of variable VAR was assigned before.
Multiline values
The syntax to assign multiline value to a variable is:
define VAR_NAME :=
line
line
endef
or
define VAR_NAME =
line
line
endef
Assignment operator can be omitted, then it creates a recursively-expanded variable.
define VAR_NAME
line
line
endef
The last newline before endef is removed.
Bonus: the shell assignment operator ‘!=’
HASH != printf '\043'
is the same as
HASH := $(shell printf '\043')
Don't use it. $(shell) call is more readable, and the usage of both in a makefiles is highly discouraged. At least, $(shell) follows Joel's advice and makes wrong code look obviously wrong.

Make args: is there a variable that contains all of them?

In make, I can define a file as follows:
.PHONY: echo-foo
echo-foo:
echo ${foo}
Usage:
make echo-foo foo=foo
Now suppose I would like to do the following:
make echo-vars foo=foo bar=bar
And I start by writing:
echo-vars:
echo ${???}
Is there a variable automatically defined which will output:
// either
foo=foo bar=bar
// or
foo bar
In other words: is there a MAKEARGS or some variable along those lines defined as part of the make env?
There's a special variable just for that:
echo-vars:
echo ${MAKEOVERRIDES}
.VARIABLES is somewhat close.
This prints only command-line variables:
test:
echo "$(foreach VAR_NAME,$(.VARIABLES),\
$(if $(filter command line,$(origin $(VAR_NAME))),$(VAR_NAME)))"
$(filter command line,$(origin VAR_NAME)) equals to "command line" if it's the value of $(origin VAR_NAME), and is empty otherwise. $(filter <needle>,<haystack>) is a make's boolean test for "<needle> is in or equals to <haystack> and is not empty".
$(if <condition>,<value>) returns <value> if <condition> is nonempty, otherwise an empty string.
$(foreach VAR_NAME,SET,EXPRESSION) returns a joined result of EXPRESSION applied to each element of a (space-separated) SET, where VAR_NAME is substituted with each element of the SET.
Add $(strip) to get rid of excess spaces.

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.

Makefile: Setting env var for one build step

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'

Resources