How to use ifeq inside of a define in GNU Make? - makefile

I'm trying to do an ifeq inside of a define within a Makefile, but I seem to be running into some errors, and I'm wondering if I'm missing something. I have the following Makefile:
$(info ---- start ----)
ifeq ("X","Y")
$(info DOES not appear_1)
endif
define TESTDEF
ifeq ("X","Y")
$(info SHOULD not appear)
# $(error DEFINITELY SHOULD not error...)
endif
endef
$(eval $(call TESTDEF, 1,2,3))
I'm getting the following error:
---- start ----
SHOULD not appear
Makefile:14: *** DEFINITELY SHOULD not error.... Stop.
Is there some trick that I'm missing? Is it possible to do ifeq's inside define? (note: this happens on both my native GNU 3.81 make, and on my mips uclibc cross-compiler)

When you call this function, Make evaluates the definition, using whatever parameters you provide (irrelevant in this case). So if the definition includes something like $(info ...) or $(error ...), even in a comment, Make will evaluate it and you'll see the result (see documentation; I've tested it in GNUMake 3.81).
To get the behavior you want, add a couple of dollar signs:
define TESTDEF
ifeq ("X","Y")
$$(info SHALL not appear)
# $$(info DEFINITELY SHALL not error...)
endif
endef
$(eval $(call TESTDEF))

Related

Missing separator error in Makefile after calling a custom function

I'm writing a macro that creates an empty target file name from a given name. And also redirects the named target to that empty target name.
After running this function I'm expecting my Makefile to look like this:
a:./build/._a
./build/._a:
#echo building $#
However I constantly get this:
$ make a
a:./build/._a
Makefile:7: *** missing separator. Stop.
Here's my Makefile:
define empty_target
$(eval $2:=./build/._$1)
$(info $1:$($2))
$(eval $1:$($2))
endef
$(call empty_target,a,_A)
$(_A):
#echo building $#
This will do it:
define empty_target
$(eval $2:=./build/._$1)
$1:$($2)
endef
$(eval $(call empty_target,a,_A))
$(info _A=$(_A))
$(_A):
#echo building $#
And yes, there's a nested eval, which you need if you want the variable assignment to take effect inside of the eval call (otherwise $2 is expanded before it is set, and expands to blank).
As to your original code, I agree, it's somewhat confusing why it raises that error. I can reproduce, and if I add the line blah:blah2 in the middle of empty_target, it seems to get rid of the message. I'm guessing it is a bug in how make colapses whitespace/newlines between the $(eval) calls.

Makefile ifndef variable in target

Please believe me: I searched and tested a lot... but I don't get whats wrong here:
VERSION := 123
all:
ifndef VERSION
$(error VERSION not set)
else
$(info Start deploy $(VERSION))
endif
outputs VERSION not set
what I really wanted was to call make like VERSION=1.2.3 make but not even setting the variable in the Makefile worked
What am I missing?
For me it outputs Start deploy 123.
Note that ifndef and $(error) and $(info) are part of makefile syntax, not the recipe syntax belonging to all target. So, your code is equivalent to:
VERSION := 123
ifndef VERSION
$(error VERSION not set)
else
$(info Start deploy $(VERSION))
endif

ifndef include guard in Gnu Make breaks on nested conditional

I’m trying to implement include guards in Gnu Make. In this Makefile, the first inclusion is OK, while the second one fails with an error.
ifndef INCLUDED
INCLUDED = 1
$(info Including)
define macro
ifneq ($(1),)
define inner_macro
macro content...
endef
else
define inner_macro
endef
endif
endef
endif
The same effect can be simulated by explicitly giving INCLUDED = 1 before the inclusion, e.g. on command line.
Gnu Make 4.1 under Gentoo says Makefile:14: *** missing separator. Stop., while Gnu Make 3.81 under Debian Wheezy says Makefile:14: *** extraneous `endef'. Stop.. On the first inclusion, they both say:
Including
make: *** No targets. Stop.
If I try $(eval $(call macro,whatever)) after the first inclusion, it defines inner_macro as expected.
I used make INCLUDED=1 and make commands respectively to get the described behavior.
The same happens when I clear the environment and disable built-in rules and variables: env -i make -rR INCLUDE=1. When I use -p to dump the database, without INCLUDED=1, the macro is defined as it should be, but with INCLUDED=1, empty inner_macro is defined. This is consistent across both the versions of Make. This hints me that when the condition is false, Make parses the Makefile differently and thinks the else inside macro’s definition belongs to the ifndef. Other condition types behave all the same.
If I remove both the definitions of inner_macro, the problem goes away.
I read the manual pages info make conditional\ syntax and info make multi-line (formerly defining), but I found no caveat there and I still think I am doing nothing wrong.
Am I correct with my conclusions?
Is this a bug in Make, or am I invoking undefined behavior?
How should I implement include guards in Gnu Make?
That's a bug. Report it on Savannah.
There's something wrong with the tracking of nested define/endef inside a not-taken ifdef/ifndef condition. If you don't use nested define/endef then it works; for example (obviously you may not be able to do this in your environment):
ifndef INCLUDED
INCLUDED = 1
$(info Including)
define macro
ifneq ($(1),)
inner_macro = macro content...
else
inner_macro =
endif
endef
endif

Display the results of foreach / eval / call expansion

I'm trying to debug makefiles for a large project and I'm struggling define TEMPLATE/endef and foreach/eval/call constructs. In particular I think I'm having a hard time figuring out which variables I need to reference with $ and which I need to reference with $$.
I think would be easier for me to debug if I could see the actual results of the the eval/call expansion, before variable expansion.
For instance if we use the example in the eval documentation for gnu-make, we have the following makefile-fragment:
PROGRAMS = server client
server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol
client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol
...
define PROGRAM_template =
$(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
ALL_OBJS += $$($(1)_OBJS)
endef
$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
I think this foreach should effectively expand to the following, before variable expansion:
server: $(server_OBJS) $(server_LIBS:%=-l%)
ALL_OBJS += $(server_OBJS)
client: $(client_OBJS) $(client_LIBS:%=-l%)
ALL_OBJS += $(client_OBJS)
I figured out the above expansion by hand (corrections welcome), but I'm looking for a general method to display this expansion for more complex examples. Does such a method exist?
I have looked into make -d and make -pn options, and as far as I can tell, I don't think either of these will provide this particular output.
I'm using make-3.81.
Replace all your $(eval ...) calls with $(info ...). Actually if you're stuck with 3.81 you may have to use $(warning ...) and ignore the extra output, because I think $(info ...) might not have existed until version 3.82.
Anyway, writing:
$(foreach prog,$(PROGRAMS),$(info $(call PROGRAM_template,$(prog))))
(or warning) you'll see what make is parsing.
I've written a macro to help with seeing the results of $(eval ...):
# This version prints out rules for debugging
#c = $(eval $(call $(strip $1),$(strip $2),$(strip $3),$(strip $4),$(strip $5),$(strip $6),$(strip $7),$(strip $8),$(strip $9))) $(info $(call $(strip $1),$(strip $2),$(strip $3),$(strip $4),$(strip $5),$(strip $6),$(strip $7),$(strip $8),$(strip $9)))
# This version is the production version
c = $(eval $(call $(strip $1),$(strip $2),$(strip $3),$(strip $4),$(strip $5),$(strip $6),$(strip $7),$(strip $8),$(strip $9)))
Use it like this:
$(call c,function,arg1,arg2,...)
To see what is being generated by eval, simply use the debugging version. I needed this instead of simply replacing $(eval ...) by $(info ...) because functionality later in the makefile depended on the results of the $(eval ...)

Using ifeq and ifndef in GNU Make

I've written a fairly simple test Makefile where I define two targets, all & clean. I've got two different conditional statements. One checks for the existence of the $(MAKECMDGOALS) special variable and the other detects whether any of the command line targets matched those listed in a variable (NODEPS). The problem I'm having is that none of the branches within my conditionals get executed. Ultimately I want to use a conditional to decide whether the target I'm supplying should include some autogenerated dependency files but at the moment I'm struggling to get either expression to even evaluate. I'm running GNU make version 3.81 and I've tried it under Ubuntu and Mac OS X to no avail.
NODEPS := clean
INCLUDE = $(filter $(NODEPS),$(MAKECMDGOALS))
.PHONY : all clean
ifndef $(MAKECMDGOALS)
#echo "$$(MAKECMDGOALS) is not defined"
else
#echo "$(MAKECMDGOALS) is defined"
endif
ifneq (0, $(words $(INCLUDE)))
#echo "INCLUDE = $(INCLUDE) != 0"
else
#echo "INCLUDE = $(INCLUDE) == 0"
endif
all :
#echo "all : $(MAKECMDGOALS)"
clean :
#echo "clean : $(MAKECMDGOALS)"
I eventually managed to work out what was wrong. #eriktous was right, pointing out that I should be using $(info) rather than #echo. More subtly though, part of the problem was that I'd indented the #echos with a tab. It seems that tabs are mandatory for rules but not allowed in conditionals. The other mistake was I'd expanded the $(MAKECMDGOALS) variable in the test condition when it should have been written as just ifndef MAKECMDGOALS.
https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
A makefile is not a shell script. You can not "randomly" place executable statements anywhere you like and expect them to be executed.
There are various ways of communicating with the outside world from within a makefile: $(info ...), $(warning ...), $(error ...) and $(shell #echo ...) (some or all of these may be GNU make extensions).
Ps: you misspelled PHONY.

Resources