Gnu make conditional on environment variable - makefile

I have the following in a Makefile:
ifndef MYVAR
$(error "MYVAR is not set")
else
$(warning "MYVAR is set to [$(MYVAR)]")
ifeq ($(MYVAR),"abc")
$(error "Value is known.")
else
$(error "Not known.")
endif
endif
I set MYVAR when calling make:
MYVAR=abc make
I would have expected to see "Value is known." but I get the following:
Makefile:4: "MYVAR is set to [abc]"
Makefile:8: *** "Not known.". Stop.
Could someone please enlighten me on what is wrong with my ifeq statement?

Question answered by #melpomene in the comments. As so often before, a quotation error.
ifeq ($(MYVAR),abc) or MYVAR='"abc"' make

Related

Ifdef conditional unexpected behavior

I have a Makefile with command like this:
#Makefile
hello:
echo 'hello'
echo $(TAG)
ifdef TAG
$(warning MYWARNING)
else
$(error MYERROR)
endif
I use it like:
# make TAG='1.0' hello
I expect that command performs echo 'hello', then echo $(TAG) and $(warning MYWARNING) but I get:
Makefile:17: MYWARNING
Makefile:19: *** MYERROR. Stop.
What is wrong?
Let's try some simpler cases(*).
hello:
echo hello
$(error MYERROR)
This produces:
Makefile:3: *** MYERROR. Stop.
Notice that the error blocks the echo, even thought it comes afterward.
Now let's try something silly:
hello:
ifdef TAG
The result is:
ifdef TAG
make: ifdef: No such file or directory
"ifdef TAG", interpreted as a shell command, makes no sense. And it is interpreted as a shell command because it's in a recipe and preceded by a TAB.
Now let's combine them:
hello:
ifdef TAG
$(error MYERROR)
The result is Makefile:3: *** MYERROR. Stop. So the error obscures the fact that the ifdef... was incorrect.
Do we want a shell conditional, or a Make conditional? If we want to Make to act on it (with error or warning), then it must be a Make conditional, so we must not preceded it with a TAB:
hello:
ifdef TAG
$(warning MYWARNING)
else
$(error MYERROR)
endif
This works as intended.
(*) as you ought to have tried before you posted.

Makefile variable value not available during ifeq

I have the following Makefile
SHELL=/bin/bash
.SHELLFLAGS=-O extglob -o errexit -o pipefail -o nounset -c
.PHONY: testing
define getFileContents
$(shell cat ./test.txt)
endef
TEST_STATIC=dummy
deploy:
$(eval TEST=$(getFileContents))
#echo "$(TEST)"
ifeq ($(TEST),dummy)
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
ifneq (,$(findstring dummy,$(TEST)))
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
ifeq ($(TEST_STATIC),dummy)
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
ifneq (,$(findstring dummy,$(TEST_STATIC)))
#echo "$(TEST) is FAILED"
else
#echo "$(TEST) is PASS"
endif
No matter what value I put in ./test.txt, I always go into PASS in both the ifeq & the findstring conditions but the variable's values show up properly in the echo statements. So the value is not available during the evaluation of ifeq
However, the if-else behaves properly for the TEST_STATIC variable.
Any help would be appreciated. Thanks.
ifeq is parsed while the makefile is read in. Even if it looks like it's part of a recipe, it isn't. You can tell that it isn't, because it isn't indented with a TAB character. Anything not indented by TAB, is part of the makefile not part of the recipe, and is parsed when the makefile is read in, not when the rule is run.
So by the time your rule is running and it gets to your eval, all the ifeq statements have long been expanded and dealt with.
In general, it's virtually never a good idea to use eval inside a recipe. It will almost never do what you're hoping for.
If you need to test some value inside a recipe then you have to write shell code to do it, not makefile code, and indent the shell code with a TAB so it's passed to the shell.

How ifeq from Makefile works?

I have a Makefile with content:
define SOME_FUNC
ifeq (n,y)
$(warning TRUE)
else
$(warning FALSE)
endif
endef
.PHONY: all
all:
$(eval $(call SOME_FUNC))
After executing "make" command I've got following output:
$ make
Makefile:10: TRUE
Makefile:10: FALSE
make: Nothing to be done for 'all'.
I cannot explain why it happens.
From the documentation:
It’s important to realize that the eval argument is expanded twice; first by the eval function, then the results of that expansion are expanded again when they are parsed as makefile syntax. This means you may need to provide extra levels of escaping for “$” characters when using eval.
You need to double the dollars to have those $(warning ...) functions evaluated on interpreting ifeq instead of expanding eval/call:
$ cat Makefile
define SOME_FUNC
ifeq (n,y)
$$(warning TRUE)
else
$$(warning FALSE)
endif
endef
.PHONY: all
all:
$(eval $(call SOME_FUNC))
$ make
Makefile:11: FALSE
make: Nothing to be done for 'all'.

If comparison in make file

I have a makefile. I need to check the exit status of a command and the performing comparison, if exit status is 0 then perform some display action. But i am getting the same message if its success or failure for both the scenario.
Please find the below code and Help me what is the right way to do this:-
FILES = test1.sh test2.sh
manoj: $(FILES)
ls $(FILES)
$(eval exitstatus="$(shell echo $$?)")
#echo $(exitstatus)
ifeq (0,$(exitstatus))
$(error something going wrong..........)
endif
clean: pwd
Getting same output as :
testmake.mk:4: *** something going wrong........... Stop.
for both ifeq ifeq (0,$(exitstatus)) and ifneq ifeq (0,$(exitstatus))
I want to perform some action if the condition is success otherwise nothing want to do.
In the recipe shell commands are used, not make directives.
And when target's recipe is executed that means all its prerequisites do exist:
manoj: $(FILES)
#echo "$(FILES) do exist at this point."

Makefile ifeq: when are they evaluated?

The following is a very simple makefile that does not seem to work properly.
TEST=ON
buildbegin:
ifeq ($(TEST),ON)
#echo TEST PASSED
else
#echo TEST FAILED
endif
No matter what I set the TEST variable to, my ifeq statement passes. I always see TEST PASSED. Anyone see what I am doing wrong here?
EDIT:
ok. my example was not exactly accurate. What I actually have is this:
SHELL = /bin/sh
DEFAULT_TARGS:= all all_debug
DEBUG_TARGS:= all_debug
ALL_TARGS:= $(DEFAULT_TARGS) $(DEBUG_TARGS)
.PHONY: $(ALL_TARGS)
.PHONY: buildbegin
$(ALL_TARGS): buildbegin
TEST=ON
$(DEBUG_TARGS): TEST=OFF
buildbegin:
#echo $(TEST)
ifeq ($(TEST),ON)
#echo PASSED
else
#echo FAILED
endif
Running either make all or make all_debug will result in "PASSED" being printed. If I echo $(TEST) before the condition, it looks as if my rules are changing the variable, but the ifeq only ever sees whatever the default value is.
make evaluates conditionals when it reads a makefile (as you know it uses 2 passes), see: Conditional Parts of Makefiles. You can simply check this by using warning (which is good thing to debug makefiles):
buildbegin:
#echo $(TEST)
$(warning now we reached ifeq TEST=$(TEST))
ifeq ($(TEST),ON)
#echo PASSED
else
#echo FAILED
endif
You should use shell commands instead and include them in rule:
buildbegin:
#if [ "$(TEST)" = "ON" ]; then echo "PASSED"; else echo "FAILED"; fi
Here's a cleaner (I think, anyway) way to do what you want:
all:
$(MAKE) TEST=ON buildbegin
all_debug:
$(MAKE) TEST=OFF buildbegin
buildbegin:
#echo $(TEST)
ifeq ($(TEST),ON)
#echo PASSED
else
#echo FAILED
endif
Parameterise the shell command using make variables. This avoids recursive make and even the shell (make will fork the command directly and not go via a shell if the command contains no shell metacharacters (<>"&; and the like)).
Something like:
result<ON> := PASSED
result<OFF> := FAILED
buildbegin:
#echo ${result-<${TEST}>}
The <...> is simply a convention to indicate some sort of indirection.
Although the question is old, I want to share a simpler way.
It is to use MAKECMDGOALSvariable that represents the list of targets.
In your case, a solution maybe:
buildbegin:
#echo $(MAKECMDGOALS)
ifeq ($(MAKECMDGOALS),all)
#echo PASSED
else ifeq ($(MAKECMDGOALS),debug_all)
#echo FAILED
endif
debug_all: buildbegin
all: buildbegin
Usage: make debug_all or make all

Resources