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.
In a makefile I'm trying to compare the target name with a string, and depending on this set a variable with a string or another.
This example illustrates what I'm trying to do:
ifeq ($#,"Target_A")
THE_PATH="Path_a"
THE_TARGET=$#
else
THE_PATH="Path_b"
THE_TARGET=$#
endif
Target_A:
#echo $(THE_PATH)
#echo $(THE_TARGET)
Target_B:
#echo $(THE_PATH)
#echo $(THE_TARGET)
This is the output when I call make passing Target_A and when I call it passing Target_B:
$ make Target_A
Path_b
Target_A
$ make Target_B
Path_b
Target_B
The fact that I always get "Path_b" indicates the ifeq always evaluates to false, but you can see that $# contained the right string.
Why doesn't this work?
You probably want target-specific variables:
Target_A: THE_PATH="Path_a"
Target_A:
#echo $(THE_PATH)
Since contents of a (regular) variable are expanded each time it's used, THE_TARGET=$# can be made global.
Target-specific variables are only accesible in a target they belong to, and its dependencies.
Normally this is enough, but if you need to have global variables, you can use the same code you have in the question, with the condition changed to this:
ifneq ($(filter Target_A,$(MAKECMDGOALS)),)
$# (which you tried to use) only works inside of a recipe, and expands to a target name that the recipe builds.
$(MAKECMDGOALS) is a global variable that contains all targets specified (as command-line parameters) when invoking make.
This option will only work if the target you're looking for was specified as a command-line parameter.
Here is a Makefile example that uses double slashes in paths to targets:
out/file.txt:
#mkdir -p $(dir $#)
#echo aaa > $#
out//file.txt:
#mkdir -p $(dir $#)
#echo bbb > $#
make interprets this as two different targets. If you run make out/file.txt, the first rule will be executed. If you run make out//file.txt, the second rule will be executed.
Also, if you run make out///file.txt and file.txt does not exist, you'll get the error:
make: *** No rule to make target `out///file.txt'
However if the file exists, it just says:
make: Nothing to be done for `out///file.txt'
Also make builds targets successfully if you run make .///out/file.txt or even make .////././././././////.///.////out/file.txt
So, is there any defined behavior how make works with paths that are literally different, but point to the same file in the filesystem?
I got from this answer, that operating system itself doesn't differ such paths. But for make they are different.
The problem originates from the such usage:
my_target: $(SOME_DIR)/some_file
If SOME_DIR already has trailing slash, the code above doesn't work. It expands to something like this: some_dir//some_file and the rule for the specific case with double slashes doesn't exist.
How can such problems be avoided? Is there any path canonization means in make?
Here is direct workaround for my problem - creating a macro that trims trailing slashes:
trslashes = $(if $(filter %/,$(1)),$(call trslashes,$(patsubst %/,%,$(1))),$(1))
This macro must be used in every place where double slashes can cause a problem:
my_target: $(call trslashes,$(SOME_DIR))/some_file
If $(SOME_DIR) is empty the file from root directory will be used:
/some_file. If empty variable should mean current directory then another macro should be used:
trslashes_cur = $(if $(1),$(call trslashes,$(1)),.)
Then $(call trslashes,$(SOME_DIR))/some_file will expand to ./somefile.
I'm having some issues after try to run some small Makefile with make 3.82.
error:
[me#localhost make]$ make
Makefile:3: *** empty variable name. Stop.
This works with make 3.81, but not with the new one. I know there are some backward compatibilities with the old version.
I have two Makefiles, a base one and main one.
This is my Makebase
define TestFile
ifeq ($$(shell test $(1) $(2) || echo 1),1)
$$(error $(2) mmm, not found)
endif
endef
define CheckIt
$(eval $(call TestFile,-d,$(1)))
endef
define CheckDir
p := $(foreach d,$1,$(call CheckIt,$d))
endef
define SomeCheck
$(call CheckDir,$(1))
endef
This is my Makefile
include Makebase
$(call SomeCheck, ~/test/make)
As I said, it works fine in make 3.81.
Any help will be appreciated.
Thanks
BR
So, I have no idea what this was intended to do in GNU make 3.81. As Etan points out, when I run your makefile with GNU make 3.81 I get this error:
make: *** No rule to make target `=', needed by `p'. Stop.
That's because a call function cannot expand to a variable assignment, so make interprets the p := as if it were p: = (that is, a target p with a prerequisite of =). I don't see how this is actually what you want. If you don't see this error all I can assume is that somewhere in your makefile, someone has declared a recipe with target = (ugh!!)
In GNU make 3.82 I see the empty variable name message. The reason for this is that GNU make 3.82 introduced parser enhancements which caused some backwards-incompatibility. The NEWS file gives this warning:
As a result of parser enhancements, three backward-compatibility issues
exist: first, a prerequisite containing an "=" cannot be escaped with a
backslash any longer. You must create a variable containing an "=" and
use that variable in the prerequisite.
An unnoticed side-effect of this is that an equals sign with no value before it in the prerequisites list is now considered a target-specific variable where the variable name is empty, whereas before it was assumed to be a target since it didn't meet the requirements for a variable assignment. I am not sure this is a bug... in general I'm not a fan of "tricking" the parser with odd corner cases so I actually prefer the newer behavior.
This entire define is quite bogus:
define CheckDir
p := $(foreach d,$1,$(call CheckIt,$d))
endef
Why? Because the CheckIt user-defined function contains nothing but an eval statement. But eval statements are expanded and the results parsed by make, so they always expand to the empty string. Therefore, the entire foreach loop expands to the empty string. Therefore even if this were interpreted as you (apparently) intended by make, it would always simply expand to:
p :=
which doesn't seem very useful. If you change the above define to simply:
define CheckDir
$(foreach d,$1,$(call CheckIt,$d))
endef
then it will always work, and you won't see these weird problems.
I'm not going to comment on how bogus this makefile is in general... :)
In my makefile, I have a variable 'NDK_PROJECT_PATH', my question is how can I print it out when it compiles?
I read Make file echo displaying "$PATH" string and I tried:
#echo $(NDK_PROJECT_PATH)
#echo $(value NDK_PROJECT_PATH)
Both gives me
"build-local.mk:102: *** missing separator. Stop."
Any one knows why it is not working for me?
You can print out variables as the makefile is read (assuming GNU make as you have tagged this question appropriately) using this method (with a variable named "var"):
$(info $$var is [${var}])
You can add this construct to any recipe to see what make will pass to the shell:
.PHONY: all
all: ; $(info $$var is [${var}])echo Hello world
Now, what happens here is that make stores the entire recipe ($(info $$var is [${var}])echo Hello world) as a single recursively expanded variable. When make decides to run the recipe (for instance when you tell it to build all), it expands the variable, and then passes each resulting line separately to the shell.
So, in painful detail:
It expands $(info $$var is [${var}])echo Hello world
To do this it first expands $(info $$var is [${var}])
$$ becomes literal $
${var} becomes :-) (say)
The side effect is that $var is [:-)] appears on standard out
The expansion of the $(info...) though is empty
Make is left with echo Hello world
Make prints echo Hello world on stdout first to let you know what it's going to ask the shell to do
The shell prints Hello world on stdout.
As per the GNU Make manual and also pointed by 'bobbogo' in the below answer,
you can use info / warning / error to display text.
$(error text…)
$(warning text…)
$(info text…)
To print variables,
$(error VAR is $(VAR))
$(warning VAR is $(VAR))
$(info VAR is $(VAR))
'error' would stop the make execution, after showing the error string
from a "Mr. Make post"
https://www.cmcrossroads.com/article/printing-value-makefile-variable
Add the following rule to your Makefile:
print-% : ; #echo $* = $($*)
Then, if you want to find out the value of a makefile variable, just:
make print-VARIABLE
and it will return:
VARIABLE = the_value_of_the_variable
If you simply want some output, you want to use $(info) by itself. You can do that anywhere in a Makefile, and it will show when that line is evaluated:
$(info VAR="$(VAR)")
Will output VAR="<value of VAR>" whenever make processes that line. This behavior is very position dependent, so you must make sure that the $(info) expansion happens AFTER everything that could modify $(VAR) has already happened!
A more generic option is to create a special rule for printing the value of a variable. Generally speaking, rules are executed after variables are assigned, so this will show you the value that is actually being used. (Though, it is possible for a rule to change a variable.) Good formatting will help clarify what a variable is set to, and the $(flavor) function will tell you what kind of a variable something is. So in this rule:
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) #true
$* expands to the stem that the % pattern matched in the rule.
$($*) expands to the value of the variable whose name is given by by $*.
The [ and ] clearly delineate the variable expansion.
You could also use " and " or similar.
$(flavor $*) tells you what kind of variable it is. NOTE: $(flavor)
takes a variable name, and not its expansion.
So if you say make print-LDFLAGS, you get $(flavor LDFLAGS),
which is what you want.
$(info text) provides output.
Make prints text on its stdout as a side-effect of the expansion.
The expansion of $(info) though is empty.
You can think of it like #echo,
but importantly it doesn't use the shell,
so you don't have to worry about shell quoting rules.
#true is there just to provide a command for the rule.
Without that,
make will also output print-blah is up to date. I feel #true makes it more clear that it's meant to be a no-op.
Running it, you get
$ make print-LDFLAGS
LDFLAGS is a recursive variable set to [-L/Users/...]
All versions of make require that command lines be indented with a TAB (not space) as the first character in the line. If you showed us the entire rule instead of just the two lines in question we could give a clearer answer, but it should be something like:
myTarget: myDependencies
#echo hi
where the first character in the second line must be TAB.
#echo $(NDK_PROJECT_PATH) is the good way to do it.
I don't think the error comes from there.
Generally this error appears when you mistyped the intendation : I think you have spaces where you should have a tab.
No need to modify the Makefile.
$ cat printvars.mak
print-%:
#echo '$*=$($*)'
$ cd /to/Makefile/dir
$ make -f ~/printvars.mak -f Makefile print-VARIABLE
Run make -n; it shows you the value of the variable..
Makefile...
all:
#echo $(NDK_PROJECT_PATH)
Command:
export NDK_PROJECT_PATH=/opt/ndk/project
make -n
Output:
echo /opt/ndk/project
This makefile will generate the 'missing separator' error message:
all
#echo NDK_PROJECT_PATH=$(NDK_PROJECT_PATH)
done:
#echo "All done"
There's a tab before the #echo "All done" (though the done: rule and action are largely superfluous), but not before the #echo PATH=$(PATH).
The trouble is that the line starting all should either have a colon : or an equals = to indicate that it is a target line or a macro line, and it has neither, so the separator is missing.
The action that echoes the value of a variable must be associated with a target, possibly a dummy or PHONEY target. And that target line must have a colon on it. If you add a : after all in the example makefile and replace the leading blanks on the next line by a tab, it will work sanely.
You probably have an analogous problem near line 102 in the original makefile. If you showed 5 non-blank, non-comment lines before the echo operations that are failing, it would probably be possible to finish the diagnosis. However, since the question was asked in May 2013, it is unlikely that the broken makefile is still available now (August 2014), so this answer can't be validated formally. It can only be used to illustrate a plausible way in which the problem occurred.
The problem is that echo works only under an execution block. i.e. anything after "xx:"
So anything above the first execution block is just initialization so no execution command can used.
So create a execution blocl
If you don't want to modify the Makefile itself, you can use --eval to add a new target, and then execute the new target, e.g.
make --eval='print-tests:
#echo TESTS $(TESTS)
' print-tests
You can insert the required TAB character in the command line using CTRL-V, TAB
example Makefile from above:
all: do-something
TESTS=
TESTS+='a'
TESTS+='b'
TESTS+='c'
do-something:
#echo "doing something"
#echo "running tests $(TESTS)"
#exit 1
This can be done in a generic way and can be very useful when debugging a complex makefile. Following the same technique as described in another answer, you can insert the following into any makefile:
# if the first command line argument is "print"
ifeq ($(firstword $(MAKECMDGOALS)),print)
# take the rest of the arguments as variable names
VAR_NAMES := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
# turn them into do-nothing targets
$(eval $(VAR_NAMES):;#:))
# then print them
.PHONY: print
print:
#$(foreach var,$(VAR_NAMES),\
echo '$(var) = $($(var))';)
endif
Then you can just do "make print" to dump the value of any variable:
$ make print CXXFLAGS
CXXFLAGS = -g -Wall
You could create a vars rule in your make file, like this:
dispvar = echo $(1)=$($(1)) ; echo
.PHONY: vars
vars:
#$(call dispvar,SOMEVAR1)
#$(call dispvar,SOMEVAR2)
There are some more robust ways to dump all variables here: gnu make: list the values of all variables (or "macros") in a particular run.
if you use android make (mka) #echo $(NDK_PROJECT_PATH) will not work and gives you error *** missing separator. Stop."
use this answer if you are trying to print variables in android make
NDK_PROJECT_PATH := some_value
$(warning $(NDK_PROJECT_PATH))
that worked for me
I usually echo with an error if I wanted to see the variable value.(Only if you wanted to see the value. It will stop execution.)
#echo $(error NDK_PROJECT_PATH= $(NDK_PROJECT_PATH))
The following command does it for me on Windows:
Path | tr ; "\n"