Tab's in Makefile - makefile

I have Makefile that contains tabified line echo $(foo) and untabified line ifneq (,$(findstring i, $(MAKEFLAGS))):
bar =
foo = $(bar)
all:
echo $(foo)
ifneq (,$(findstring i, $(MAKEFLAGS)))
echo "i was passed to MAKEFLAGS"
endif
If I untabify echo $(foo) I got error:
Makefile:5: *** missing separator. Stop.
Why some lines should be tabified while other ones - not?

Because, that's the syntax makefiles use. Why do you have to indent the body of methods in Python? Because that's the syntax.
Makefiles consist of two different "languages" in the same file. Lines that are not indented by TAB are makefile format. Lines that are indented by TAB are shell scripts. Make uses TAB to differentiate the two.
See the GNU make manual for more info.

Why some lines should be tabified while other ones - not?
Every line of each recipe must begin with a tab. Every line that is not part of a recipe should not begin with a tab. The crux of the issue is that in the example makefile, your ifneq and endif directives are not part of a recipe. They are processed by make, during makefile parsing, not passed to a shell when the recipe is run. In some ways, this is the same kind of distinction as between C preprocessing directives and the surrounding source code.

Related

Makefile start and end of target

Hello I have some debug target in my makefile.
How I can control what code will be inside this target?
I don't want to something after echo will be in target test, but now it is.
.PHONY: test
test:
#echo ${TYPE}
ifndef SOME
SOME=some123
endif
$ make test
typevalue
SOME=some123
I think I understand what you're asking.
A makefile rule lasts from the introduction of the target, up until the next line which is not part of a recipe. Blank lines, comment lines, and preprocessor lines like ifeq do not count as ending a recipe.
Other lines that begin with TAB characters are part of the recipe. Lines that don't begin with a TAB will end the recipe.
So, in your makefile above simply ensure that the assignment SOME=some123 does not start with a TAB character.

Why doesn't Make allow indented "if" statements?

It's a constant thorn in my side when trying to read a makefile with nested logic, that Make does not allow indented if statements. Why is this, and is there a good way to work around this limitation, and still have readable makefiles?
Update: I now realise that this question is based on a false premise, but I believe that leaving it here may be valuable to anyone who makes the same mistake that I did.
Thanks to the help of others, I now realize that my question is posed on a false premise. Makefiles absolutely do allow for indented if statements, or rather indented conditionals to be more precise. What they don't allow for - at least out of the box - are tabbed conditionals. This is because, by default, Make interprets tabs as especially meaningful characters. Almost any line beginning with a tab character is interpreted to be part of a recipe. Therefore, any line that is not intended to be part of a recipe - such as conditionals - should not begin with a tab.
As far as answering the part of my question that asked why they chose to use the tab character in this way, I haven't found an answer. Perhaps the designers intended for conditionals to be used sparingly.
As for workarounds, here I will attempt to describe a couple.
The first solution is a terrible pain if you don't have an editor that shows whitespace characters, but if you do, the simplest thing to do might be to just add some spaces to indent your non recipe code. This is a rather hackish workaround though and probably ill advised.
Another solution (courtesy of #Stefan Becker) is to set the special variable, .RECIPEPREFIX to a character other than tab. Here's an example of what I tried:
.RECIPEPREFIX := >
# Now, all recipes will begin with the > character rather than a tab.
things = something another_thing something_else nothing
nothing = true
something: another_thing something_else
# See how each line of a recipe now begins with >.
# You can see I also added a tab after the >.
# These tabs doesn't mean anything to Make; it's just for readability.
> $(info Making $#.)
> #touch $#
another_thing:
> $(info Making $#.)
# See also how lines like comments can be tabbed,
# but no longer add anything meaningful to recipes.
> #touch $#
something_else:
> $(info Making $#.)
> #touch $#
# And just to prove the situation with conditionals is resolved...
# See how the #touch command begins with the new RECIPEPREFIX
# but the conditionals don't.
ifeq ($(nothing),true)
> $(info Also making nothing, because nothing is true.)
> #touch nothing
endif
.PHONY: everything_clean
everything_clean:
> $(info Cleaning up everything.)
> rm -f $(things)
One thing worth remembering is that recipe lines must begin with the new RECIPEPREFIX. That is to say, that something like this won't work:
something: another_thing something_else
# Remember that the RECIPEPREFIX must come first.
# Indenting your recipe lines first and then using the RECIPEPRIFX will not work.
>$(info Making $#.)
>#touch $#
I don't know why you are under the impression that indented conditionals aren't supported. They do seem to work fine when I use them in the following example:
.PHONY: all
all:
CONFIGS :=
ifeq ($(CONFIG1),1)
$(info CONFIG1 selected)
CONFIGS += config1
all: config1
config1:
#echo $#
ifeq ($(CONFIG2),1)
$(info CONFIG2 selected)
CONFIGS += config2
all: config2
config2:
#echo $#
else
$(info CONFIG2 not selected)
endif
else
$(info CONFIG1 NOT selected)
endif
all:
#echo "all: $(CONFIGS)"
NOTE: the TABS in my example will probably not survive copy & paste. So you'll have to re-enter them for the recipes.
Test run:
$ make
CONFIG1 NOT selected
all:
$ make CONFIG1=1
CONFIG1 selected
CONFIG2 not selected
config1
all: config1
$ make CONFIG1=1 CONFIG2=1
CONFIG1 selected
CONFIG2 selected
config1
config2
all: config1 config2
But...
There is one case where indentation can lead to problems. To quote the GNU make manual:
A recipe is an action that make carries out. A recipe may have more than one command, either on the same line or each on its own line. Please note: you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary.
As GNU make takes all TAB indented lines after a rule to be part of the recipe for the rule the following will fail for make CONFIG1=1:
.PHONY: all
all:
CONFIGS :=
config1:
# TAB in the following line
#echo $#
# the following lines are indented with TABs
ifeq ($(CONFIG1),1)
CONFIGS += config1
test1:
#echo $#
endif
ifeq ($(CONFIG1),1)
all: config1
endif
all:
# TAB in the following line
#echo "all: $(CONFIGS)"
$ make CONFIG1=1
config1
ifeq (1,1)
/bin/sh: -c: line 0: syntax error near unexpected token `1,1'
/bin/sh: -c: line 0: `ifeq (1,1)'
make: *** [Makefile:9: config1] Error 1
Solution
organize the makefile to have conditionals first, then rules, i.e. no TAB indentation after rules anymore except for recipes, or
always make sure to use SPACEs for conditional, variable assignment and rule lines.
set .RECIPEPREFIX to a non-whitespace character, e.g. > and use that to indicate recipe lines.1
Unless you have an editor which shows the difference between TABs and SPACEs, alternative 2 will probably drive you insane. I would suggest alternative 1 instead...
The following works for make CONFIG2=1:
.PHONY: all
all:
CONFIGS :=
config2:
# TAB in the following line
#echo $#
# the following lines are indented with SPACES
ifeq ($(CONFIG2),1)
CONFIGS += config2
test2:
# 2 TABs in the following line
#echo $#
endif
ifeq ($(CONFIG2),1)
all: config2
endif
all:
# TAB in the following line
#echo "all: $(CONFIGS)"
$ make CONFIG2=1
config2
all: config2
1 you might be tempted to set .RECIPEPREFIX to SPACE like this:
_empty :=
_space := $(_empty) $(_empty)
.RECIPEPREFIX := $(_space)
and then switch your editor to use only SPACEs. But that makes things worse, i.e. now make can't distinguish between normal and recipe indentation. If you try this with the above example you will note that it now fails for any invocation that enables one of the indented rules.

Makefile ifeq not behaving

I have a very simple makefile as follows
FRUIT = apple orange
all : $(FRUIT)
$(FRUIT) :
ifeq ($(#),apple)
#echo "APPLE!"
else
#echo "ORANGE!"
endif
When I execute
make all
I see
ORANGE!
ORANGE!
I have checked my tabbing and I believe my spacing on ifeq is correct. What have I missed?
The ifeq is evaluated when the Makefile is first parsed, not when the individual recipe is executed. At that point, $(#) is always the empty string.
You can perform the same logic in shell script in the recipe itelf.
$(FRUIT):
case $# in apple) echo "APPLE!";; *) echo "ORANGE!";; asac
Conditional directives are expanded as soon as make reads them, long before the recipe is executed. $#expands to the empty string when make parses that line so you always end up with the else part of the conditional.
Just use something like the following instead:
FRUIT = apple orange
all: $(FRUIT)
$(FRUIT):
#echo "$#!"
If you need separate recipes then write them
apple:
#echo "APPLE!"
orange:
#echo "ORANGE!"

When to use space or tab in Makefile?

I am creating a makefile that uses the condition if and ifneq.
I noticed that if I am using if, the next lines should be indented by spaces.
if [-d "$$d" ]; then
<space><space><space> echo "file found";
fi;
But if I am using the ifneq command, the next lines had to be indented by tabs.
ifneq ($(strip $(USE_FILE)),NO)
<tab>echo "file not to be used"
endif
Space and tabs should not matter at all. But how come in makefile, there is a difference with space and tab?
You have to understand that a makefile is really written in two completely different "languages", in one file.
Recipes (the commands that run compilers, echo, etc.) are written in shell script syntax.
The rest of the makefile that is not in a recipe is written in makefile syntax.
In order for make to tell the difference between a recipe and things that are not a recipe, it uses TAB characters. So, lines that begin with TAB are assumed to be part of a recipe (so they are shell scripts and passed to the shell for parsing), and lines that do not begin with TAB cannot be part of a recipe (so they cannot be shell scripts: they must be make syntax).
In your examples, if [ -d ... is shell syntax. If it appears in a makefile it must be part of a recipe, and so must be preceded by a TAB; if make tries to interpret this as makefile syntax it will be an error. ifneq is makefile syntax: if the shell tries to interpret this as a shell script it will be a syntax error, so it cannot be part of a recipe and must NOT be preceded by a TAB.
All other uses of indentation are optional and irrelevant (for example in your first example above you say "the next line should be indented by spaces"; that's just a convention and the script will work exactly the same way whether or not you indent it at all).
Now, there are some details which get tricky: backslash-escaped newlines, rule contexts, etc. but if you stick with the rule that all recipe lines are indented with a TAB and no non-recipe lines are indented with a TAB, you'll be OK.

How to print out a variable in makefile

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"

Resources