expand a variable upon evaluation not assignment - makefile

I am having trouble with some make variables, I have something like this:
SUFFIX := raw
FILENAME = name.$(SUFFIX)
...
...
#much later, in a different makefile far away:
SUFFIX := img
echo $(FILENAME)
But FILENAME has the original raw suffix value (name.raw), not the more recently assigned "img" value.
Am I missing something simple here? Using latest released gmake.

I cannot reproduce this error with a series of includes; there is something else involved. I suggest you try the following and tell us the results:
#echo filename: $(FILENAME)
#echo origin: $(origin FILENAME)
#echo flavor: $(flavor FILENAME)
EDIT:
All right, now we know that FILENAME is being defined simply (i.e. with :=) somewhere. So try to trace the include chain, and find out where that happens. Here's a trick: put these lines in any makefile you're interested in
ifeq ($(flavor FILENAME),simple)
$(warning FILENAME is simply defined)
endif

Related

gnumake pattern subsituation

Having some trouble with GNUMAKE $(subst) I cannot explain
My goal is to transform a "SRC_DIR" directory into an "OBJ_DIR"
In one case it works, the other it does not - I do not understand why.
I thought, that the two "HERE{1|2}" constructs below would be identical, it seems they are not.
I do not believe it is a "delayed execution" problem because I am using the := (evaluate now, immediately in all cases)
PROJ_ROOT :=$(HOME)/a
BUILD_DIR :=$(PROJ_ROOT)/build/debug
HERE1:=$(shell pwd)
HERE2:=`pwd`
OK1 := $(subst $(PROJ_ROOT),$(BUILD_DIR),$(HERE1))
BAD := $(subst $(PROJ_ROOT),$(BUILD_DIR),$(HERE2))
TEST=$(subst ee,EE,feet on the street)
all:
#echo PR=$(PROJ_ROOT)
#echo BR=$(BUILD_DIR)
#echo H1=$(HERE1)
#echo H2=$(HERE2)
#echo OK1 is $(OK1)
#echo BAD IS $(BAD)
#echo TEST=$(TEST)
The output is below, the OK1 is correct,
but BAD is wrong It should be the same ask OK1
PR=/home/foobar/a
BR=/home/foobar/a/build/debug
H1=/home/foobar/a/src/one/two/three
H2=/home/foobar/a/src/one/two/three
OK1 is /home/foobar/a/build/debug/src/one/two/three
BAD IS /home/foobar/a/src/one/two/three
TEST=fEEt on the strEEt
background: I have a large project (400+ source files) spread over about 10 directories, each directory has a src folder, ie: $(PROJECT_ROOT)/libfoo/src/foo.c, and $(PROJECT_ROOT)/libbar/src/bar.c - Unlike the "autoconfigure" method where SRCDIR != BUILD_DIR (you configure in the build dir, and create makefiles in the build dir, I am doing the opposite - I have a prebuilt makefile that should create/populate a build directory structure with object files.
To do that, I need to create various subdirectories under $(PROJECT_ROOT)/build" that MIRROR the source folders. For example the makefiles should create: $(PROJECT_ROOT)/build/debug/libfoo/src/foo.o, and $(PROJECT_ROOT)/build/debug/libbar/src/bar.o
thus I need to 'slice the 'lib{foo|bar}/src and paste this into/onto the root of the build directory using makefile tricks.
As mentioned in the comments:
HERE1:=$(shell pwd)
HERE2:=`pwd`
The first line runs the make shell function and sets the value of HERE1 to the result of running the pwd command, which is what you expected. The second line sets the value of HERE2 to the literal string `pwd`.
When you use subst on the string `pwd` obviously nothing happens.
Once again, people are led astray by premature addition of #. Never, ever, ever add # to your recipes until after your makefile works completely.
If you removed the # from the lines that used HERE2 then it would be immediately obvious why things worked differently. Make would print out the command it was going to run and you could see that HERE2 is not replaced by make but that it's the shell that is running the pwd command:
echo H2=`pwd`
H2=/home/foobar/a/src/one/two/three
and:
echo BAD IS `pwd`
BAD IS /home/foobar/a/src/one/two/three

Make: extract the target path segment that follows a known one

I am trying to optimize our build targets. In our current process we have separate targets for 32bit & 64bit build. Due to separate targets we have hundreds of targets in our build flow. Where we try to make it global i.e. one target for both 32-bit and 64-bit build. I'm trying to achieve this with the code below.
Target/Output directory for 32-bit build should be like:
/test/scratch/<client_name>/Linux/...
Target/Output directory for 64-bit build should be like:
/test/scratch/<client_name>/Linux-64bit/...
So based on above Target directory paths I am searching the string Linux using $(findstring) function and proceed with 32-bit command to run. Else it will run 64bit command as shown below.
RELEASE_FILES := $(TARGET_DIR)/build/test/csimtime.c
$(RELEASE_FILES): $(TGTDIRFILES)/%: %
ifneq (Linux,$(findstring $(OS),$#))
$(test_lib_32)
else
$(test_lib_64)
endif
$(TARGET_DIR) variable is passed as parameter to make command
make TARGET_DIR=$(TGT32) all32
For 64-bit we will pass TARGET_DIR=$(TGT64) instead.
Note: test_lib_32/64 above are macro definition in other make file which we are including in current make file.
It works fine as expected, But I am not sure whether this is the best way? And I notice one problem here, Generally TGT32/TGT64 variable which we are passing has values either:
/test/scratch/<client_name>/Linux/ (for 32bit)
or
/test/scratch/<client_name>/Linux-64bit/... (for 64bit)
If someone creates a client with client_name 'Linux' string it wont work. It will always go to if block and try to run 32-bit command even when we run 64-bit build. How can I handle this?
I am looking for getting the string after 'client_name' in above path.. Could please help?
If your target directory name is always something like:
/test/scratch/<client_name>/Linux/...
and there is no / character and no spaces in <client-name>, you can base your test on the exact position of Linux (or Linux-64bit) in the path:
TYPE := $(word 4,$(subst /, ,$(TARGET_DIR)))
ifeq ($(TYPE),Linux)
<something>
else ifeq ($(TYPE),Linux-64bit)
<something-else>
else
$(error Unknown type: $(TYPE))
endif
EDIT if the position is not constant but you know what <client-name> is, and there is no / character and no spaces in <client-name>, you can extract the name of the directory that follows <client-name> in $(TARGET_DIR) like this:
TAIL := $(subst /, ,$(TARGET_DIR))
$(foreach w,$(TAIL),$(eval TAIL := $(wordlist 2,$(words $(TAIL)),$(TAIL)))$(if $(patsubst $(CLIENT_NAME),,$(w)),,$(eval TYPE := $(word 1,$(TAIL)))))
all:
#printf 'TYPE = $(TYPE)\n'
Demo:
$ make TARGET_DIR=/a/b/test-client/Linux/c/d CLIENT_NAME=test-client
TYPE = Linux
$ make TARGET_DIR=/a/b/c/d/test-client/Linux-64bit/e/f/g CLIENT_NAME=test-client
TYPE = Linux-64bit
Explanation:
TAIL := $(subst /, ,$(TARGET_DIR)) replaces the / characters in $(TARGET_DIR) by one space, such that it becomes a list of words (one per directory in the path), and assigns the result to variable TAIL.
$(foreach w,$(TAIL),...) evaluates ... with variable w set successively to each word of $(TAIL).
$(eval TAIL := $(wordlist 2,$(words $(TAIL)),$(TAIL))) re-assigns variable TAIL by removing the leading word.
$(if $(patsubst $(CLIENT_NAME),,$(w)),,...) evaluates ... if $(w) equals $(CLIENT_NAME).
$(eval TYPE := $(word 1,$(TAIL))) assigns the first word of $(TAIL) to variable TYPE.
There may be simpler ways to do the same...
If I understand your question correctly then you get problems with the following $(TARGET_DIR) value:
/.../Linux/.../Linux-64bit
The problem is that you are testing on the value of $#:
/.../Linux/.../Linux-64bit/some/other/file
As you can see that makes it impossible to decide which Linux in the path should be used for the decision.
I would suggest to try this instead:
# if $(TARGET_DIR) ends with /Linux -> 32-bit build
$(RELEASE_FILES): $(TGTDIRFILES)/%: %
ifneq ($(filter %/Linux,$(TARGET_DIR)),)
$(test_lib_32)
else
$(test_lib_64)
endif

How to conditionally set Makefile variable to something if it is empty?

I want to set a variable if it is empty. I tried in this way:
....
TEST := $(something)
...
TEST ?= $(something else)
The first $(something) may return an empty string, however the conditional assignment ?= works only if the previous variable is not set, not if empty.
Any elegant solution to set the variable if empty?
EDIT
I found this solution:
....
TEST := $(something)
...
TEST += $(something else)
TEST := $(word 1, $(TEST))
but I think that there will be one more elegant.
Any elegant solution to set the variable if empty?
GNU make is hardly known for elegant solutions. Unless you find trapdoors and minefields to be elegant. I know only of the two ways to accomplish what you want:
The standard ifeq/endif solution:
ifeq ($(TEST),)
TEST := $(something else)
endif
Use the $(if) function:
TEST := $(if $(TEST),$(TEST),$(something else))
One can try to package that construct into a function too, but that is inadvisable. The function would have the hidden pitfall of occasionally breaking the $(something else) if it contains the , (for which there are only wayward workarounds). (The built-in functions like $(if) are immune to the , bug.)
Elegance test is up to you.
Here's another alternative that I personally find quite elegant, because it's a one-liner and doesn't need the redundant else-branch:
TEST := $(or $(TEST),$(something else))
From GNU make, chapter 7.2, Syntax of Conditionals:
"Often you want to test if a variable has a non-empty value. When the value results from complex expansions of variables and functions, expansions you would consider empty may actually contain whitespace characters and thus are not seen as empty. However, you can use the strip function to avoid interpreting whitespace as a non-empty value. For example:
ifeq ($(strip $(foo)),)
text-if-empty
endif
will evaluate text-if-empty even if the expansion of $(foo) contains whitespace characters."
Folks, I think there's a simpler solution
KDIR ?= "foo"
From: What is ?= in Makefile
Just in case anyone stumbled upon putting the condition in the rule itself. below how I did it, thought it might help others.
In Makefile, suppose we have the following rule with check target and we need to check whether var was passed.
check:
#[ "${var}" ] && echo "all good" || ( echo "var is not set"; exit 1 )
To test this out, run the following commands
$ make check
var is not set
make: *** [check] Error 1
$ make check var=test
all good
So, Now we can pass the variable value or a default value in case it was not passed to a bash script that will be responsible to do the logic. something like the following:
#[ "${var}" ] && ./b.sh ${var} || ./b.sh 'ss'
Here's below what b.sh might look like, though you can add more logic to it.
#!/bin/sh
echo $1
In case you need to distinguish if a variable is undefined or just has an empty value, use $(origin VARNAME) function:
ifeq ($(origin VARNAME),undefined)
VARNAME := "now it's finally defined"
endif
Note that VARNAME is not surrounded by $() - you literally give the name of the variable.
Setting value to variable in Makefile if value defined
ifdef RELEASE_BRANCH
GIT_TAG=$(shell cat projects/${RELEASE_BRANCH}/GIT_TAG)
else
GIT_TAG=$(shell cat release/DEFAULT_GIT_TAG)
endif

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"

Exporting environment variables to Makefile shell

I want to do immediate expansion of a shell command within a Makefile, but I want the shell command to have access to the environment variables within the Makefile. If I use the $(shell ...), it expands immediately, but there is no access to the variables. If I use the backquotes, the expansion is not immediate, and it causes problems for me later in the Makefile. I'm wondering if there is any way to make the backquotes expand immediately, or to pass the current environment to a $(shell) command.
For example, the following makefile:
SOME_VAR := some_val
export SOME_VAR
VAR1 := `echo $$SOME_VAR`
export VAR1
VAR2 := `echo $$VAR1`
all:
#echo VAR1=$(VAR1)
#echo VAR2=$(VAR2)
Will output:
~/tmp/t2> make
VAR1=some_val
VAR2=`echo $SOME_VAR`
Where I want it to print "VAR2=some_val". The real example is a bit more complicated (environment variables are inherited from parent makefiles, and I'm trying to use a perl script to edit the variables), but the principle is the same.
Any help is appreciated.
Is this what you want?
VAR2 := $(shell VAR1="$(VAR1)" script_that_uses_var1)
What's wrong with this?
VAR1 := $(shell echo $(SOME_VAR))
VAR2 := $(shell echo $(VAR1))
You may try to use Special Built-in Target Name: .EXPORT_ALL_VARIABLES
.EXPORT_ALL_VARIABLES:
MY_VAR = foo
test:
#echo $$MY_VAR
As I mentioned in some of the comments, my actual goal was to make the script generate filenames based on the settings the object was being compiled with. I then need another script to generate a specially formatted list of all the filenames generated (the target is an embedded system which doesn't have a JIT compiler on it). At any given time, there are over thirty settings which can potentially effect the binary, and this may be used on more than one module in the future, so I'd like something scalable.
My solution is as follows. Instead of passing the variables in, I modified my script to output a makefile-parsable string based on the settings:
-include $(SOME_MK_FILE)
$(SOME_MK_FILE) : .phony
script.pl $(SETTINGS_OF_INTEREST_LIST) > $(SOME_MK_FILE)
someFilename := $(shell script2.pl $(VAR1))
script.pl outputs a string that looks something like:
VAR1 := CONFIG_X1=$(CONFIG_X1) CONFIG_X2=$(CONFIG_X2) CONFIG_X33=$(CONFIG_X33)
and script2 outputs a filename that looks something like 'someFilename.X1_y.X2_n.elf'
and then, later on, in another rule, I have:
someobj: somedep
script3.pl $(someFilename) >> builtfiles.txt
which properly builds builtfiles.txt (which in turn is the input for yet another script...). In the end this is a workaround to the fact that make cannot pass its environement to $(shell). It's not overly pretty but it works.
John
Here you go:
export FOO=bar
Here's a page with a lot more info:
http://www.cmcrossroads.com/article/basics-getting-environment-variables-gnu-make?page=0%2C1

Resources