Makefile problem with conditional used in define used in commands part of a rule - makefile

I have a section of makefile that has this sort of structure:
bob:
ifdef DEBUG
#echo running
endif
#echo chug chug chug
ifdef DEBUG
#echo done
endif
bobit:
#echo "before"
#make bob
#echo "after"
I'm simplifying greatly here, all the echo's are actually non trivial blocks of commands and there is more conditional stuff, but this captures the essence of my problem.
For technical reasons I don't want to get into right now, I need to get rid of that submake, but because the echo's represent nontrivial amounts of code I don't want to just copy and past the body of bob in place of the submake.
Ideally what I'd like to do is something like this
define BOB_BODY
ifdef DEBUG
#echo running
endif
#echo chug chug chug
ifdef DEBUG
#echo done
endif
endef
bob:
$(BOB_BODY)
bobit:
#echo "before"
$(BOB_BODY)
#echo "after"
Unfortunately the conditionals seem to be shafting me, they produce "ifdef: Command not found" errors, I tried getting around this with various combinations of eval and call, but can't seem to figure out a way to get it to work.
How do I make this work? and is it even the right way to approach the problem?

The way I have fixed this is to use bash conditionals instead, which actually makes a certain amount of sense since we are playing with commands and not make rules.
So my ideal solution from above becomes something like
define BOB_BODY
#if [[ -n "$(DEBUG)" ]]; then \
echo running; \
fi;
#echo chug chug chug
#if [[ -n "$(DEBUG)" ]]; then \
echo done; \
fi
endef
bob:
$(BOB_BODY)
bobit:
#echo "before"
$(BOB_BODY)
#echo "after"

You can simply change the order of ifdef/define:
ifdef DEBUG
define BOB_BODY
#echo running
#echo chug chug chug
#echo done
endef
else
define BOB_BODY
#echo chug chug chug
endef
endif
UPDATE
define CHUG
#echo chug chug chug
endef
ifdef DEBUG
define BOB_BODY
#echo running
$(CHUG)
#echo done
endef
else
define BOB_BODY
$(CHUG)
endef
endif

Related

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 do I check values of variables from an included makefile?

../Makefile
BAR=sometext
Makefile
include ../Makefile
FOO=othertext
ifneq ($(BAR),)
FOOBAR=$(FOO) $(BAR)
else
FOOBAR=$(FOO)
endif
all:
#echo $(FOOBAR)
This will print "othertext" regardless of whether I have BAR defined in ../Makefile. Why is that? If i have BAR defined locally, it will print "othertext sometext" which is what I want. But I'm stuck using an include in my situation.
I've tried various scenarios using $(value $(BAR)) $(strip $(BAR)), etc but I can't seem to get this to work how I want.
You could do this other way around. In Makefile:
FOO=othertext
BAR=$(FOO)
include ../Makefile
all:
#echo $(BAR)
and ../Makefile
BAR+=sometext

Unexpected token in ifeq makefile

I'm picking up some old code that is out of my norm and running into some issues over something that is hopefully very simple to explain.
I'm working on a makefile that runs a number of SQL files then generates .done files to track the progress. After one specific SQL file, I need to be able to run a shell script to do some additional processing.
# this is the rule the describes how to execute a SQL script
%.done: %.sql
#echo "+==============================================================================+"
#echo "building $#"
#echo "...from $<"
#echo "building $<"
#$(PSQL_WRAPPER) -f $<
#ifeq ($(#),mysqlfile.done)
#echo "Executing Shell Script"
#../path/to/script/myscript.sh
endif
I added the ifeq portion, everything else was there before and works as expected.
Here's the output I'm getting. I'm really stuck and have been trying different little syntax tweaks, but there's clearly something else I'm just not understanding.
+==============================================================================+
building mysqlfile.done
...from ../..//path/to/file/mysqlfile.sql
building ../..//path/to/file/mysqlfile.sql
/bin/bash: -c: line 0: syntax error near unexpected token `mysqlfile.done,mysqlfile.done'
/bin/bash: -c: line 0: `ifeq (mysqlfile.done,mysqlfile.done)'
make: *** [mysqlfile.done] Error 1
Command exited with non-zero status 2
From the error message, it looks like I have two equal values. I'm really not sure what the unexpected token would be.
ifeq is a GNU Make directive,
for conditionally including or excluding a part of a parsed makefile. It is not a shell command.
You are using it in one of the lines of the %.done: %.sql recipe.
All lines of a recipe must be shell commands. Just use shell:
%.done: %.sql
#echo "+==============================================================================+"
#echo "building $#"
#echo "...from $<"
#echo "building $<"
#$(PSQL_WRAPPER) -f $<
#if [ "$#" = "mysqlfile.done" ]; then \
echo "Executing Shell Script"; \
../path/to/script/myscript.sh; \
fi
You cannot conditionally exclude the special-case processing from the recipe itself,
like:
%.done: %.sql
#echo "+==============================================================================+"
#echo "building $#"
#echo "...from $<"
#echo "building $<"
#$(PSQL_WRAPPER) -f $<
ifeq ($(#),mysqlfile.done)
#echo "Executing Shell Script"
#../path/to/script/myscript.sh
endif
because the target $(#) of the rule is only defined within the recipe.

basic makefile ifeq how to

I am just learning about Makefiles and am having trouble with ifeq.
Version = GNU Make 3.82
Here is my simple Makefile:
CHECK := 0
CHECK2 := 0
check :
#echo "Check=${CHECK}"
#echo "Check2=${CHECK2}"
ifeq (${CHECK2},${CHECK})
#echo "EQUAL"
else
#echo "NOT EQUAL"
endif
Here is the output:
Check=0
Check2=0
NOT EQUAL
Why am I not seeing "EQUAL"?
Thanks!
Makefiles (and make) are extremely white-spaces sensitive. You seem to have a lot of white spaces in definition of CHECK2 (but not for CHECK), so they are not equal.

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