I'm writing a Makefile that does string matching at one place, the code is like:
if test ...; \
then \
shell scripts... \
fi
ifeq ($(DIST_TYPE),nightly)
shell scripts ...
endif
Here the first if is shell script, the second ifeq is GNU Make's conditional. However the following error generates:
ifeq (nightly,nightly)
/bin/sh: -c: line 0: syntax error near unexpected token `nightly,nightly'
/bin/sh: -c: line 0: `ifeq (nightly,nightly)'
What's happening here? It seems that Make is trying to call the shell.
I played around the code and found that the conditional statements should be written without indentation, and this solved my problem.
If there is no indentation, Make will treat it as a directive for itself; otherwise, it's regarded as a shell script.
Example code
Wrong:
target:
ifeq (foo, bar)
...
endif
Correct:
target:
ifeq (foo, bar)
...
endif
In addition, if the conditional statements is used in define functions, like:
define myFunc
ifeq (foo, bar)
...
endif
endef
In this case, Make will also treat it as a shell script.
This problem can be solved by using if-function instead:
define myFunc
$(if condition,then-part[,else-part])
endef
Related
I have the following code:
LOCAL_VERSION := $(shell some_binary -v | head -n 1)
REMOTE_VERSION := $(shell curl -s https://example.com/key)
all:
ifeq($(REMOTE_VERSION), $(LOCAL_VERSION))
#echo yes
endfi
But I am getting this:
user:tmp user$ make
ifeq(v0.11.1, v0.11.1)
/bin/sh: -c: line 0: syntax error near unexpected token `v0.11.1,'
/bin/sh: -c: line 0: `ifeq(v0.11.1, v0.11.1)'
make: *** [all] Error
I am on Mac OSX, but it's using GNU Make anyway.
ifeq should not be indented, e.g.
LOCAL_VERSION := $(shell some_binary -v | head -n 1)
REMOTE_VERSION := $(shell curl -s https://example.com/key)
all:
ifeq ($(REMOTE_VERSION), $(LOCAL_VERSION))
#echo yes
else
#echo NO
endif
The issue is not that ifeq is indented in the recipe, the problem is that it was indented using a tab. If you indent using space, the code runs as expected.
From Make manual:
5.1 Recipe Syntax
Each line in the recipe must start with a tab (or the first character in the value of the .RECIPEPREFIX variable; see Special Variables), except that the first recipe line may be attached to the target-and-prerequisites line with a semicolon in between. Any line in the makefile that begins with a tab and appears in a “rule context” (that is, after a rule has been started until another rule or variable definition) will be considered part of a recipe for that rule. Blank lines and lines of just comments may appear among the recipe lines; they are ignored.
I wanted to test some expressions of the ifeq kind that run a shell command that I read somewhere, so I wrote this tiny mymakefile (all lines being indented with a tab):
ifeq ($(shell echo test 2>/dev/null; echo $$?),0)
$(info I am inside)
endif
... and I tried to run it:
$ make -f mymakefile
make: *** No targets. Stop.
How could I test expressions like this inside their own makefile? Do I need to define a default target, or not? And how should the commands be formatted (indented with a tab, or space, or not indented at all?)
Well, I got somewhere - apparently, one must specify a target; but since I'm a make noob, I would love to see a more qualified answer.
I found this link https://www.gnu.org/software/make/manual/html_node/Conditional-Example.html that gave me a hint.. Anyways, this is mymakefile now:
.PHONY: default
default: mytarget;
ifeq ($(shell echo test 2>/dev/null; echo $$?),0)
$(info I am inside)
else
$(info I am outside)
endif
mytarget:
\t (TAB) echo A
So, the mytarget here is just a dummy, which simply does an echo A; running this prints:
$ make -f mymakefile
I am outside
echo A
A
If you don't want the echo A printed, suppress it with at sign: #echo A.
The echo A line has to be indented with a TAB - else error "mymakefile:11: *** missing separator. Stop.".
Strangely, if I indent the two $(info... lines with a TAB, then "I am outside" is printed last (?!), but when they are not indented (or indented with spaces), then it is printed first (as per the order in the file).
I'm a starter at this(make), and having some problems trying to grep for a text in a file (as part of the make process on Windows). The larger problem I'm trying to solve is to check whether all binary executables in a given directory have their respective dependencies satisfied. I use depends.exe (Dependency Walker) for the later part, whose output file I'm trying to grep, and possibly abort the build process if the dependency validation fails.
binary-dependency-validate:
for BINARYEXEC in $(shell $(PATH_TO_FIND_EXE) $(PRE_DEFINED_DIR) -name "*.exe"); do \
$(PATH_TO_DEPENDS_EXE) /c /pb /ot $(PRE_DEFINED_DIR)/$$BINARYEXEC-depends.txt $(PRE_DEFINED_DIR)/$$BINARYEXEC ; \
ifeq ($(shell $(PATH_TO_GREP_EXE) "Error: At least one required implicit or forwarded dependency was not found." $(PRE_DEFINED_DIR)/$$BINARYEXEC-depends.txt),); \
#echo "Dependency ok" ; \
endif ; \
done
I'm encountering the following error :
line 1: syntax error near unexpected token `,'
Any suggestions would greatly help. I looked at this post and tried aligning ifeq without indentation as well (that didn't help either.)
The problem is that you are mixing Make language with shell language.
A makefile contains rules, and a rule contains commands which are executed by a shell:
target:
first command
second command
The commands are in shell language, and each command must be preceded by a TAB.
There are conditionals in Make:
ifeq (foo, bar)
VAR = something
endif
(The indentation is just for the eye.)
There are also conditionals in the various scripting languages:
if [ -f log ]
then
echo "log exists."
fi
A Make conditional can enclose a command within a rule:
target:
ifeq (foo, bar)
first command
endif
second command
Make will evaluate this conditional before running the rule, and there must be no TAB before ifeq or endif, because Make must not interpret them as commands to be passed to the shell.
A command (in a rule) can contain a shell conditional:
target:
if [ -f log ]; \
then echo "log exists." ; \
fi
The indentation before if is a TAB. The other whitespace is for the eye.
Your makefile has a Make conditional in the middle of a shell command; Make can't evaluate the conditional before the command executes, and the shell can't understand Make syntax. You should use a shell conditional.
It seems like you have a stray comma on the ifeq line
binary-dependency-validate:
for BINARYEXEC in $(shell $(PATH_TO_FIND_EXE) $(PRE_DEFINED_DIR) -name "*.exe"); do \
$(PATH_TO_DEPENDS_EXE) /c /pb /ot $(PRE_DEFINED_DIR)/$$BINARYEXEC-depends.txt $(PRE_DEFINED_DIR)/$$BINARYEXEC ; \
ifeq ($(shell $(PATH_TO_GREP_EXE) "Error: At least one required implicit or forwarded dependency was not found." $(PRE_DEFINED_DIR)/$$BINARYEXEC-depends.txt)); \
#echo "Dependency ok" ; \
endif ; \
done
How could I abort a make/makefile execution based on a makefile's variable not being set/valued?
I came up with this, but works only if caller doesn't explicitly run a target (i.e. runs make only).
ifeq ($(MY_FLAG),)
abort: ## This MUST be the first target :( ugly
#echo Variable MY_FLAG not set && false
endif
all:
#echo MY_FLAG=$(MY_FLAG)
I think something like this would be a good idea, but didn't find anything in make's manual:
ifndef MY_FLAG
.ABORT
endif
TL;DR: Use the error function:
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
Note that the lines must not be indented. More precisely, no tabs must precede these lines.
Generic solution
In case you're going to test many variables, it's worth defining an auxiliary function for that:
# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
# 1. Variable name(s) to test.
# 2. (optional) Error message to print.
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))))
And here is how to use it:
$(call check_defined, MY_FLAG)
$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
LIB_INCLUDE_DIR \
LIB_SOURCE_DIR, \
library path)
This would output an error like this:
Makefile:17: *** Undefined OUT_DIR (build directory). Stop.
Notes:
The real check is done here:
$(if $(value $1),,$(error ...))
This reflects the behavior of the ifndef conditional, so that a variable defined to an empty value is also considered "undefined". But this is only true for simple variables and explicitly empty recursive variables:
# ifndef and check_defined consider these UNDEFINED:
explicitly_empty =
simple_empty := $(explicitly_empty)
# ifndef and check_defined consider it OK (defined):
recursive_empty = $(explicitly_empty)
As suggested by #VictorSergienko in the comments, a slightly different behavior may be desired:
$(if $(value $1) tests if the value is non-empty. It's sometimes OK if the variable is defined with an empty value. I'd use $(if $(filter undefined,$(origin $1)) ...
And:
Moreover, if it's a directory and it must exist when the check is run, I'd use $(if $(wildcard $1)). But would be another function.
Target-specific check
It is also possible to extend the solution so that one can require a variable only if a certain target is invoked.
$(call check_defined, ...) from inside the recipe
Just move the check into the recipe:
foo :
#:$(call check_defined, BAR, baz value)
The leading # sign turns off command echoing and : is the actual command, a shell no-op stub.
Showing target name
The check_defined function can be improved to also output the target name (provided through the $# variable):
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))$(if $(value #), \
required by target `$#')))
So that, now a failed check produces a nicely formatted output:
Makefile:7: *** Undefined BAR (baz value) required by target `foo'. Stop.
check-defined-MY_FLAG special target
Personally I would use the simple and straightforward solution above. However, for example, this answer suggests using a special target to perform the actual check. One could try to generalize that and define the target as an implicit pattern rule:
# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
# %: The name of the variable to test.
#
check-defined-% : __check_defined_FORCE
#:$(call check_defined, $*, target-specific)
# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :
Usage:
foo :|check-defined-BAR
Notice that the check-defined-BAR is listed as the order-only (|...) prerequisite.
Pros:
(arguably) a more clean syntax
Cons:
One can't specify a custom error message
Running make -t (see Instead of Executing Recipes) will pollute your root directory with lots of check-defined-... files. This is a sad drawback of the fact that pattern rules can't be declared .PHONY.
I believe, these limitations can be overcome using some eval magic and secondary expansion hacks, although I'm not sure it's worth it.
Use the shell function test:
foo:
test $(something)
Usage:
$ make foo
test
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x
You can use an IF to test:
check:
#[ "${var}" ] || ( echo ">> var is not set"; exit 1 )
Result:
$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1
For simplicity and brevity:
$ cat Makefile
check-%:
#: $(if $(value $*),,$(error $* is undefined))
bar:| check-foo
echo "foo is $$foo"
With outputs:
$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something
Use the shell error handling for unset variables (note the double $):
$ cat Makefile
foo:
echo "something is set to $${something:?}"
$ make foo
echo "something is set to ${something:?}"
/bin/sh: something: parameter null or not set
make: *** [foo] Error 127
$ make foo something=x
echo "something is set to ${something:?}"
something is set to x
If you need a custom error message, add it after the ?:
$ cat Makefile
hello:
echo "hello $${name:?please tell me who you are via \$$name}"
$ make hello
echo "hello ${name:?please tell me who you are via \$name}"
/bin/sh: name: please tell me who you are via $name
make: *** [hello] Error 127
$ make hello name=jesus
echo "hello ${name:?please tell me who you are via \$name}"
hello jesus
Another option:
MY_FLAG = $(error Please set this flag)
Attempting to use this variable anywhere will cause an error, unless it's overriden from the command line.
To accept environment variables as well, use ?=:
MY_FLAG ?= $(error Please set this flag)
is my first question here and first time I manage GNU Make so I want to explain my problem.perhaps you could help me to find a light at the end of this tunnel.
That thing Im trying to do is to check a word into my path and do something after check path
I've got that code on make:
WORD=GNUMAKE; \
FOUND=1; \
echo "$$FOUND"; \
PWD=$(PWD); \
ifeq ($(findstring $$WORD,$$PWD),) \
$(warning list contains "$$WORD") \
endif
but when I run $make I get this error, for me so strange and can't find a solution
could you please help me?
/bin/sh: syntax error at line 1: `ifeq' unexpected
make: *** [all] Error 2
Thank you
Gnu make treats lines joined with \ as a single line. ifeq et. al. need to be on their own line, rather like #ifdef in C (if that's any help to you).
You seem rather confused over what make does.
Make executes a makefile in three distinct phases:
It reads in the Makefile, building a graph in memory, saving macros/expanding macros as necessary.
It looks at what you asked it to make, and decides how to walk the graph.
It walks the graph, expanding the shell recipes before passing the manufactured string to the shell.
You can get make to do your bidding as it reads the makefile
WORD = GNUMAKE
FOUND = 1
$(warning ${FOUND})
ifneq ($(findstring ${WORD},${CURDIR}),)
$(warning list contains "${WORD}")
endif
Or you can get make to do this just as it is making the command to pass to the shell (i.e., before the shell is executed):
.PHONY: target
target:
$(if $(findstring ${WORD},${CURDIR}),$(warning list contains "${WORD}"))echo Shell command here
Or indeed get the shell to do it.
You are messing make commands with shell commands. ifeq is apparently belongs to make but got into shell somehow.
This will find occurance of GNUMAKE word in current path, i.e. it will be one of parent directories. Put this into Makefile and call make.
INPUT := $(shell pwd | tr -s "/" " ")
WORD=GNUMAKE
ifneq ($(filter $(WORD),$(INPUT)),)
$(warning list contains $(WORD))
endif
WORD = GNUMAKE
FOUND = 0
$(warning $$FOUND)
ifneq ($(findstring $$WORD,$(PWD)),)
$(warning list contains $$WORD)
endif
that is exactly what I have been set
hope it helps