I would like to check multiple conditions in an if loop of GNU make file. Here's an example:
ifeq ($(TEST_FLAG),TRUE && ($(DEBUG_FLAG),FALSE))
true statement
else
false statement
endif
What's the right way to do it?
Although Hasturkun's solution will work, I think the idiomatic way to write this is:
ifeq ($(TEST_FLAG),TRUE)
ifeq ($(DEBUG_FLAG),FALSE)
# Stuff
endif
endif
You can use ifeq with a concatenation of your values, eg.
ifeq ($(TEST_FLAG)$(DEBUG_FLAG),TRUEFALSE)
do something
endif
It's also possible to use the Conditional functions, which are more likely to be useful in a loop (as ifeq will probably not do what you expect in a loop, it will be tested exactly once).
Related
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 have a somewhat complex makefile that I want to change and I don't know much about make.
BUILD_TYPE = SERVER
BAS_CSRC = a.c \
b.c \
c.c
What I want to do is conditionally add things to BAS_CSRC like so:
ifeq ($(BUILD_TYPE), SERVER)
USR_CSRC = $(BAS_CSRC) \
d.c \
e.c
endif
all_csrc = $(USR_CSRC) $(foreach var, $(COMMON_OBJECTS), $($(var)_csrc))
But when I compile the d.c and e.c are just ignored so the ifeq fails. Why? What about quoting?
Based on what you've typed here it should work (and I cut and pasted this into a test makefile which worked for me), which likely means there's something different about your real environment, than this example. Can you show us where you use the variable all_csrc? Maybe it's that that's the problem, not the assignment of the variable.
Make sure you don't have any trailing whitespace. Make sure you have matching case in your variables and values (make, like all UNIX tools, is case-sensitive). You can try adding $(info ...) statements to your makefile and it will print out what it's doing. Put one inside the ifeq to see if it fires, and after the endif to see what the value of USR_CSRC is.
Also, in general it's not a good idea to add whitespace into if statements or function calls like foreach. In the above situations it shouldn't matter but in general it's best avoided.
I have Makefile where flag is set for IPv6 compilation.
IPV6 = 1
ifeq ($(IPV6), 1)
Then ipv6 targets are include for compilations.
Requirement:
I want to enable this flag based on feature, and this feature is part of the CFLAG.
ifdef IPV6_FEATURE
IPV6=1
else
IPV6=0
endif
But since IPV6_FEATURE is not available in Makefile as it is a global include, it will always going to else.
Please let me know if any alternatives for this.
If I understand your question, in the cases where IPV6_FEATURE is defined, it gets included after the section where you are testing it. This does not work, since make evaluates conditionals when it reads the makefile.
The solution is to not use an ifdef to set IPV6, and use a conditional function instead.
IPV6 = $(if $(IPV6_FEATURE), 1, 0)
This will set IPV6=1 if IPV6_FEATURE is defined to some non-empty value.
You may also need to change how you are using $(IPV6), so that you don't have an ifeq conditional.
I am trying to check a file against a list before I try to compile it in a GNU makefile. Will the conditional ifneq below be evaluated every time the rule is invoked or just once? The condition seems to always evaluate the same way.
If not is the only way to do this to put the conditional in the shell command? I realize it may seem weird that the target list could be "not OK" ... the Make system could certainly be fixed to eliminate that weirdness but the pain will be greater.
Any suggestions?
Eli
OKSRC := realfile1.cpp realfile2.cpp
%.o: %.cpp
ifneq ($(findstring $<,$(OKSRC),),)
... do the compile
else
#skip the file
endif
Quoting from the Make documentation:
Conditional directives are parsed immediately. This means, for example, that automatic variables cannot be used in conditional directives, as automatic variables are not set until the recipe for that rule is invoked. If you need to use automatic variables in a conditional directive you must move the condition into the recipe and use shell conditional syntax instead.
ifneq is a conditional directive, and $< is an automatic variable. So in short, your above code will not work, so you would have to use the shell-based conditional.
But I would strongly suggest that you fix the root cause (i.e. the erroneous dependency generation), rather than trying to hack around it.
I think you can use if function to defer the expansion of automatic variables until the condition is evaluated.
OKSRC = realfile1.cpp realfile2.cpp
.cpp.o:
$(if $(findstring $<,$(OKSRC)),$(CC) $(CFLAGS) -c $<,#echo skip $<)
And the result shows:
$ make realfile1.o realfile2.o realfile3.o
cc -c realfile1.cpp
cc -c realfile2.cpp
skip realfile3.cpp
I'd like to enable a verbose compilation in my makefile, but I can't figure out how to make a conditional OR.
Let me explain: I want to be able to specify a verbose compilation either by setting V=1 or VERBOSE=1. I want to keep VERBOSE=1 available because we have some scripts that make use of it (and use other makefiles only aware of VERBOSE)
So the result must be that these two commands are the same:
make all VERBOSE=1 # pain to write
make all V=1
Now, my makefile looks like this today:
ifdef VERBOSE
[issue compilation commands with verbose mode]
endif
What I'd like to achieve is close to the preprocessor in C:
if defined(VERBOSE) || defined(V)
[issue compilation commands with verbose mode]
endif
Do you know how to do that?
I do like this:
ifneq "$(or $(LINUX_TARGET),$(OSX_TARGET))" ""
endif
Similar to the $(strip approach, but using the more intuitive $(or keyword
VERBOSE := $(or $(VERBOSE),$(V))
...then...
ifeq ($(VERBOSE),1)
#Conditional stuff
endif
I like Neil Butterworth's approach, but if you really want to do it in the style you describe, this will give you OR:
ifneq "$(strip $(VERBOSE) $(V))" ""
[be verbose]
endif
As far as I know, the conditional stuff in GNU make doesn't allow for ORs and ANDS. You could always do something like:
ifdef VERBOSE
DOVERBOSE = yes
endif
ifdef V
DOVERBOSE = yes
endif
ifeq( $DOVERBOSE, yes )
main verbose stuff here
endif
but I don't see why you need to introduce the (hardly self documenting) define of V in the first place.
Ok, really late to the party, but I came across this, and wanted to add another solution for others who were looking how to add logic to makefiles: basically, do the logic in a shell, and get the output that way.
ifneq ( $(shell ( [ $(VERBOSE) ] || [ $(V) ] ) && echo y ),)
it seems more convoluted, but if you have an if statement with many ands and ors, this offers a lot of flexibility, and would be easier to read than nested $(and .. $(or ...)) statements.