if statement for global variable in makefile - makefile

I have a makefile in which I am using some global variables like
REL=something..
DIR=something...
This makefile is used to build two products say P1 and P2, based on product for which it is build value of global variables is getting used which was not an issue because value of all the global variables for both the product was same, but now the value of variable REL is different for product 1 and Product 2, I was trying this but its not working:
!IF "$(PRODUCT)"=="P1"
REL=value1
!ELSEIF "$(PRODUCT)"=="P2"
REL=value2
!ENDIF

If I undertand your question correctly (a big if), then you can do it with target-specific variable values:
P1: REL=product1
P1: some_preq
do_something...
P2: REL=product2
P2: some_other_preq
do_something_else...

If you are using both make or automake, then you can do as #Beta suggested. In make, definition checks are done as follows:
ifeq (P1,$(PRODUCT))
REL:=value1
endif
ifeq (P2,$(PRODUCT))
REL:=value2
endif
The better way to do what you are trying to do is as #Beta suggested. Create PHONY targets if you have to.

Related

Makefile expanding variables inside conditionals depends on order of definition

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)

Make: Variable assignment

I am facing the issue while accessing a variable from other makefile which is included.
i have test.mak which has variable LIBS32 := $(TESTLIBS)/$(NEW_PLAT32)
i have included test.mak in other makefile and trying to assign that variable in one of the target.
extlib32: EXTLIBS = $(LIBS32)
extlib64: EXTLIBS = $(LIBS64)
The expected value of EXTLIBS should be '/home/testlib/extlibs/Linux' . But here when i print EXTLIBS the value which i am seeing is '/home/testlib/extlibs/'
Note:- When i jut print LIBS i can see the content as expected. But when i assigned to EXTLIBS and try to use it.. I can see word 'Linux' is missing.
Thanks!
You set EXTLIBS as a target-specific variable for target extlib32. Such variables are non-global and their value is only available in the target recipe and target's prerequisites, rather than globally (this is why $(info $(EXTLIBS)) doesn't print the expected value).
To print its value you need to print it from the recipe of target extlib32, e.g.:
extlib32:
#echo "EXTLIBS=${EXTLIBS}"
If extlib32 and extlib64 are .PHONY targets to build something then your original target-specific assignments should propagate to the dependencies and be available in their recipes. You just cannot print its value from the global makefile scope.
To have one makefile build both in 32 and 64-bit mode (as well as release and debug) you need to structure it differently and invoke make separately for each build mode. Example:
# User can overrided capitalized variables. E.g.
# make BUILD=release MODE=32 LDLIBS=-lrt
BUILD := debug
MODE := 64
build_dir := ${BUILD}/${MODE}
ldlibs.32 := my-32-bit-libs
ldlibs.64 := my-64-bit-libs
ldlibs := ${ldlibs.${MODE}} ${LDLIBS}
all :
#echo "build_dir=${build_dir}"
#echo "ldlibs=${ldlibs}"
Usage examples:
$ make
build_dir=debug/64
ldlibs=my-64-bit-libs
$ make BUILD=release MODE=32
build_dir=release/32
ldlibs=my-32-bit-libs
Another example for debug/release compiler options.

Makefile set global variable in target body

I want to set a Global variable through a recipe then reference that variable in another independent recipe
The below code is an example code that sets the variable within the recipe but the variable stays with the initial value if referenced outside the recipe
ACTIVE = a
switch:
ifeq ($(ACTIVE),b)
ACTIVE=$(shell echo 'a')
else
ACTIVE=$(shell echo 'b')
endif
print:
$(info acitve = $(ACTIVE))
I know there are ways to broadcast the value of a target-specific variable to dependent targets, but that's not what I want.
You can use $(eval ...) for this, although it's almost always a bad idea. I have to assume that your real situation is much more complicated because there are many better ways to accomplish what you've actually provided in the sample makefile.
switch:
$(eval ACTIVE=$(if $(filter-out a,$(ACTIVE)),a,b))

Target-specific variables in a makefile & prerequisites

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.

Overriding a makefile variable

I have a master makefile which has the default values for variables and then a child makefile which includes project specific settings. At the end of the child makefile, I include the master makefile.
I have been using the following code in the master makefile to set default values for a variable
ifndef CC
CC = avr-gcc
endif
And then recently I read that I can also do
CC ?= avr-gcc
So my question is, whether both are same and if yes which one is the recommended way of overriding variables.
The second is broadly understood, easier to read and causes less clutter.
The first way, using ifndef / endif is more for instances where you want to do more than just set a variable, like toggling many things depending on if DEBUG is set, or something else.
If you just want to set a variable if it's not already set, then var ?= value is definitely sufficient.

Resources