How to print expanded variables from Makefile? - makefile

Given Makefile in which libdir is defined as libdir = ${exec_prefix}/lib, how to print its final (expanded) value?
Running make -pns | grep -e "^libdir" | uniq prints libdir = ${exec_prefix}/lib, whereas I rather want to see /usr/local/lib (provided that exec_prefix is /usr/local).
Is it possible without appending that Makefile with yet another rule?

What about:
printf 'include Makefile\nfoo:\n\t#echo "$(libdir)"\n' | make -f - foo
Note that the concept of final value is difficult to define with make. It may depend on the specific target...

The problem is what you mean by "a final value". For recursive variable it's a literal string $(exec_prefix}/lib. And the expansion only takes place when the variable is referred with ${libdir}.
However simple variables are immediately expanded. So libdir:=${libdir} and make -np will produce the desired result. But other way it's not possible.
BTW. You can write a common "printing" rule like this (seen in GMSL):
print-%:
#echo value of "$*" is "$($*)"
And then just do make print-libdir

Related

Makefile: shell function in lazy variable is not lazy

Consider the following Makefile.
$(shell touch /tmp/example.txt)
FILE := /tmp/example.txt
CONTENTS = $(shell cat $(FILE); bash -c 'echo [debugging id: $$RANDOM]')
.PHONY: all
all:
#cat $(FILE)
#echo '$$(CONTENTS):' $(CONTENTS)
bash -c 'echo file-contents-$$RANDOM' > $(FILE)
#cat $(FILE)
#echo '$$(CONTENTS):' $(CONTENTS) # This line outputs the old contents. Why?
It prints the contents of the file, overwrites with new contents and prints the contents again. It shows as (after second shots of make):
file-contents-1543
$(CONTENTS): file-contents-1543 [debugging id: 15172]
bash -c 'echo file-contents-$RANDOM' > /tmp/example.txt
file-contents-22441
$(CONTENTS): file-contents-1543 [debugging id: 151]
The old content is file-contents-1543 and new content is file-contents-22441 (the numbers are random), but the last line echo $(CONTENTS) does not print the new contents.
I think the command is actually called twice as debugging ids show but shell function in the lazy variable seems to be executed before writing the new contents to the file.
I expect that lazy variable in Makefile is evaluated every time it is referred, the echo $(CONTENTS) command always prints the latest file contents. What am I wrong?
By the way, I found that using CONTENTS = $$(cat $(FILE)) works as I expect. I will using this instead of shell function but is it ok?
I expect that lazy variable in Makefile is evaluated every time it is referred, the echo $(CONTENTS) command always prints the latest file contents. What am I wrong?
First of all, in make's slang these variables are called recursive, not lazy. And, yes, they get expanded (i.e. recursively substituted) each time they are referred with $(CONTENTS). Considering that $(eval...) and $(shell...) (as pretty much anything looking as $(...)) also went through the same (recursive) expansion procedure (albeit, with some "side-effects"), each expansion of such variable could also result in some sort of "evaluation" or "execution".
Next, the order of expansion in make is a bit specific. In particular, the recipes (i.e. the lines starting with [tab]) are expanded after the whole makefile was (pre-)processed, but before the first line of the recipe gets executed by shell. Which is the main source of your confusion, I suppose.
I found that using CONTENTS = $$(cat $(FILE)) works as I expect
$$ is a way to get a single literal $ after an expansion procedure. So $$(cat $(FILE)) when expanded becomes $(cat /tmp/example.txt) which is a legal syntax for command substitution in bash. This means it will work only as part of a bash command (recipe line). If that is what you want then it's okay.

Makefile produces different results when run second time

I have a rule like this in a makefile:
target : dependencies
rm -rd somedirectory
runcodecoverage.exe # this generates somefile
$(eval COVERAGE=$(shell grep "blah" somefile))
#echo $(COVERAGE)
When I run this file the first time (after make cleaning) the echo doesn't print anything. But the second and times after that, it prints the correct result. If I replace the $(eval ..) line with just grep "blah" somefile I get the result I want, so the problem must be in the use of $(eval) and $(shell). Why is this happening?
Edit: I solved this by adding a new dependency, so it now looks like this:
generatesomefile :
runcodecoverage.exe # this generates somefile
target : dependencies generatesomefile
rm -rd somedirectory
$(eval COVERAGE=$(shell grep "blah" somefile))
#echo $(COVERAGE)
It seems like $(eval) was being substituted with the result of grep as soon as the "target" target was entered, even though I wanted it to be run after runcodecoverage.exe had run. In this sense the answer I accepted as correct wasn't quite right - the docs say this on variable expansion:
Rule Definition
A rule is always expanded the same way, regardless of the form:
immediate : immediate ; deferred
deferred
The eval function doesn't work the way you think it does.
The first time you run Make, it expands the eval function and carries out the variable assignment before executing any rule. And since there is no somefile, the grep returns nothing and COVERAGE stays empty. When Make executes the rule, it passes the empty variable to the echo which duly reports nothing.
The second time you run Make, once again it expands the eval function, executes the grep on somefile (which was built in the first run), and stores the result in COVERAGE. Then it executes the rule and passes COVERAGE to the echo, which puts it up on your screen.
The $(eval ...) runs when your Makefile is parsed, not when the recipe is executed.
It's not clear to me what you expect to happen; setting a Make variable from within a recipe does not seem like a sustainable approach, but it depends a lot on where and how you need to use this variable. If all you need is to output the coverage from the same recipe, replace the last couple of lines with just
grep "blah" somefile

Make commands with variables

I've been learning make and am struggling to figure something out. I have some rules with this general structure.
FILE = "myfile.txt"
test :
YOUR = $(subst my,your,$(FILE));\
cat $(FILE) $(YOUR)
I would expect the end result to be running the command:
cat myfile.txt yourfile.txt
Instead I get the following...
YOUR = "yourfile.txt";\
cat "myfile.txt"
/bin/sh: YOUR: command not found
make: *** [test] Error 1
If instead of using the subst function, I just do YOUR="yourfile" in the makefile, everything looks fine. Any suggestions or have I missed something pretty fundamental? I should add that I'm using tabs and not spaces to start the lines for the commands within the rule.
FILE = "myfile.txt"
test :
$(eval YOUR = $(subst my,your,$(FILE)))
cp $(FILE) $(YOUR)
You have to use the eval function in the recipe (Define make variable at rule execution time)
You need to distinguish between what make executes and what the shell executes. Your line with YOUR = starts with a tab and is part of the actions of a rule, so it is executed by the shell, which can't find a program YOUR to execute with some arguments.
Place the expansion outside the rule:
YOUR = $(subst my,your,$(FILE))
test:
cat $(FILE) $(YOUR)
Note that shell assignments require no space around the equals sign, and use ${} rather than $() to reference variables: YOUR=${FILE/my/your} in Bash (and if written in a make rule, you'd need $$ in place of $ so that the shell sees a single dollar sign and make does not try the variable expansion that it doesn't understand). The shell uses $() to execute the command contained within, and the result is often captured in a variable: YOUR=$(echo "${FILE}" | sed 's/my/your/').
If you only need the variable in the shell recipe and not in the make context then you don't need to bother playing with eval (which are hoisted) and can just assign to shell variables instead.
For example:
FILE = "myfile.txt"
test :
YOUR='$(subst my,your,$(FILE))';\
cat $(FILE) "$${YOUR}"

Recursive make: correct way to insert `$(MAKEFLAGS)`

How can I use $(MAKEFLAGS) (or another way of passing variables defined on the command line to sub-make) in a way that supports invocation from shell with both make VAR=val and make -args?
I need my subprojects configurable, but I hate autotools, so I'm using make variables for this, e.g. invoking from shell:
$ make USE_SSE3=1
and USE_SSE3 needs to apply to all builds in all sub-makefiles.
The manual states that:
if you do ‘make -ks’ then MAKEFLAGS gets the value ‘ks’.
Therefore I'm using -$(MAKEFLAGS) (with a dash prefix) in my Makefile.
However, that expands into invalid arguments when variables with no flags are used. If I run:
$ make FOO=bar
then sub-make gets invalid -FOO=bar. OTOH without the dash prefix variable definitions work, then but make -s, etc. don't.
Is there a syntax/variable/hack that makes passing of arguments and lone variable definitions work with sub-makefiles?
The legacy $(MKFLAGS) doesn't have the weird dash prefix problem, but it doesn't include variable definitions either. I've tried fixing the variable with $(patsubst), but that only made things worse by trimming whitespace.
I need the solution to be compatible with the outdated GNU Make 3.81 shipped with Mac OS X Mavericks.
foo:
$(MAKE) -C subproject -$(MAKEFLAGS)
$ make foo -s # MAKEFLAGS = 's'
$ make foo BAR=baz # MAKEFLAGS = 'BAR=baz'
$ make foo -j8 # MAKEFLAGS = ' --job-server=…'
You shouldn't set MAKEFLAGS at all. Why do you want to? You didn't give any reason to do so.
MAKEFLAGS is intended, really, to be an internal implementation passing arguments from a parent make to a child make. It's not intended, generally, to be modified by a makefile. About the only thing you can usefully do to it is add new flags.
If you just run the recursive make using the $(MAKE) variable rather than hardcoding make, it will Just Work:
recurse:
#$(MAKE) all FOO=bar
or whatever.
Years too late I got your answer if I got it right.
You can construct $(MAKEARGS) manually yourself like:
MAKEARGS := $(strip \
$(foreach v,$(.VARIABLES),\
$(if $(filter command\ line,$(origin $(v))),\
$(v)=$(value $(v)) ,)))
MAKEARGS := assign static
strip cleans leading and trailing whitespaces.
foreach v iterate over all variable names.
origin $(v) check if variable origin is "command line".
$(v)=$(value $(v)) output env assignment string.
Alternatively you can unpick the $(MAKEFLAGS) like:
MAKEARGS := $(wordlist 2,$(words $(MAKEFLAGS)),$(MAKEFLAGS))
MAKEFLAGS := $(firstword $(MAKEFLAGS))
Which can leave you with cleaner code for further recursions IMHO. I say this because I sometimes need to keep apart arguments and flags in certain cases. Especially as you get caught in debugging a recursion djungle.
But for any specific case one should consult the manual about recursive options processing.
Changing the $(MAKEFLAGS) can lead to unwanted malfunction.
Another useful information for the willing user could be that the $(MAKEFLAGS) variable is basically the whole argument list passed to make, not only the flag characters. So $(info MAKEFLAGS = $(MAKEFLAGS)) can give you something like:
MAKEFLAGS = rRw -- VAR=val
Cheers
To check if -B is present in make flags i do :
BB_CLOBBER := $(shell echo $(MAKEFLAGS) | grep wB)
ifeq (,$(BB_CLOBBER))
# also force clobber make if these files are missing
BB_CLOBBER := $(shell (test -e $(bb_gen)/minimal/.config && test -e $(bb_gen)/full/.config) || echo "B")
endif
bb_prepare:
ifneq (,$(BB_CLOBBER))
#rm -rf $(bb_gen)/full
...

functions in makefiles?

I have a slew of makefile targets that do the same thing:
${SOME_FILE}:
${FILES} | ${DIST_DIR}
##cat ${FILES} | \
sed 's/#DATE/'"${DATE}"'/' | \
sed 's/#VERSION/'"${CR_VER}"'/' \
> ${OUT_FILE};
where ${FILES} and ${OUT_FILE} are the only things changing. I'm trying to figure out if it's possible to simplify these targets to something like:
${SOME_FILE}:
compile(${FILES},${OUT_FILE})
Thanks for any insight.
GNU make has this:
http://www.gnu.org/software/make/manual/make.html#Call-Function
To define a multi-line function, you would use this syntax:
http://www.gnu.org/software/make/manual/make.html#Canned-Recipes
Links to docs (like in the accepted answer) are good but good example is better :)
define my_func
$(eval $#_PROTOCOL = "https:")
$(eval $#_HOSTNAME = $(1))
$(eval $#_PORT = $(2))
echo "${$#_PROTOCOL}//${$#_HOSTNAME}:${$#_PORT}/"
endef
my-target:
#$(call my_func,"example.com",8000)
Take into consideration the following:
There are no custom "functions" in Makefile language. So "my_func" is actually a variable that simply contains a text.
That "function" doesn't have its own scope. All the content of that "function" is copied into the recipe as is. All variables in the body will be used after that as a part of the recipe.
Don't use spaces near the commas to prettify param list of "call" function.
Args are passed like $(1), $(2), ... that is not very handy, so re-declare them with meaningful names. This is optional but recommended for bigger "function" bodies.
Variables are declared with $#_ prefix that makes them "local" to the rule (actually not local but prefixed by the target name).
So we have imitation of functions with imitation of local variables but generally this works good.
If you don't want to restrict yourself to GNUmake, your best bet is probably to generate makefile fragments yourself and then include them.

Resources