Makefile - export declarations to sub-makefiles - makefile

I know variables can be exported to sub-makefiles: Communicating Variables to a Sub-make
Example:
Makefile:
export PWD := $(shell pwd)
target:
#echo $(PWD)
#cd somewhere; $(MAKE)
somewhere/Makefile
target:
#echo $(PWD)
Supposing that the first Makefile is located at /path/to/first/makefile, the code above will print:
/path/to/first/makefile
/path/to/first/makefile
My question is: is there a way to let the variable PWD be implicitly evaluated inside sub-makefiles?
The output should look like this:
/path/to/first/makefile
/path/to/first/makefile/somewhere
So far I can only think of:
Exporting the literal declaration and use the function eval
Do it somehow with .SECONDEXPANSION
Put the declaration into a separate file and include it
in both the first and the second Makefile
All this solution are explicits: they imply code to be added to the sub-makefiles.
What I'm searching is an implicit solution which will change only the code inside the first Makefile.
Plus, honestly...the first two solutions are so ugly I would rather declare manually PWD in every sub-makefile.
[EDIT]
Just to make it more clear: the variable PWD is just an example, I'm not trying to obtain the path of every Makefile.

Use ${MAKEFILE_LIST} variable and let make change directories for you:
[max#earth:~/tmp]$ cat Makefile
target:
#echo $(abspath $(lastword ${MAKEFILE_LIST}))
${MAKE} -C somewhere $#
[max#earth:~/tmp]$ cat somewhere/Makefile
target:
#echo $(abspath $(lastword ${MAKEFILE_LIST}))
[max#earth:~/tmp]$ make target
/home/max/tmp/Makefile
make -C somewhere target
make[1]: Entering directory '/home/max/tmp/somewhere'
/home/max/tmp/somewhere/Makefile
make[1]: Leaving directory '/home/max/tmp/somewhere'
That prints the full path to the makefile being processed. Use $(dir $(abspath $(lastword ${MAKEFILE_LIST}))) to chop off filename Makefile.

Related

Why does makefile lazy evaluation find a file in a "parent" recipe but not the current one?

This question is a follow-up to What makefile lazy evaluation rule governs this behavior?. I'm still trying to grok some of the rules of gnu make's lazy evaluation.
I want to have a make variable for the content of a directory after that directory has been updated by a recipe.
This Makefile demonstrates that $(A_FILE) is evaluated to find the created file when it's in the "parent" of the recipe that actually creates the file:
A_FILE = $(wildcard subdir/*)
all: a
#echo $(A_FILE)
a:
#mkdir ./subdir
#touch subdir/b
$ rm -rf ./subdir/ && make
subdir/b
$
But the following Makefile has a seemingly trivial change: $(A_FILE) is referenced in the recipe where its containing directory is updated - but now the variable is empty:
A_FILE = $(wildcard subdir/*)
all: a
#echo $(A_FILE)
a:
#mkdir ./subdir
#touch subdir/b
#sleep 1
#echo $(A_FILE)
$ rm -rf ./subdir/ && make
$
I added the sleep to rule out timing issues of the directory being trawled too quickly after it had been updated.
What gives? Why does $(A_FILE) get evaluated against the updated subdir content if it's referenced in the higher-layer recipe but not in the lower-layer recipe where it's actually updated?
GNU make evaluates all lines in the recipe before it starts running any line in the recipe. So, when it is getting ready to run your recipe for the rule a it first expands all the lines, including the last line with $(A_FILE) in it. At that point no parts of the recipe have been run yet so the result is empty.
Then after all the expansion, the shell is invoked to run the lines in the recipe.

Why does including a file in my Makefile change my Makefile-directory variable?

In order invoke my Makefile from different locations without messing up relative paths, I reference paths using a Makefile variable as given in another answer:
DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
I get that MAKEFILE_LIST differs when I include other files, but since I store its value in a variable before making any includes, I am surprised that the variable value differs.
Example:
$ tree .
.
├── another_file
└── subdirectory
└── Makefile
$ cat Makefile
DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
test:
#echo $(DIR)
#include $(DIR)/../another_file
$ make
/subdirectory
Just as expected. But if I uncomment the include line, I get
$ make
/
Which does not make sense to me, because another_file is still included without errors indicating that the value of $(DIR) is /subdirectory.
Note that the make target is placed before the include statement, and the behavior does not change when the order is switched. Guess this is due to preprocessing, but it still does not explain to me why $(DIR) seems to have different values.
$ make --version
GNU Make 3.81
...
This program built for i386-apple-darwin11.3.0
this is because the value of MAKEFILE_LIST changes after include and the expansion of the variable DIR happens at use time.
I sprinkled your Makefile with info for demonstration
DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
$(info list1 $(MAKEFILE_LIST))
$(info dir1 $(DIR))
test:
#echo $(DIR)
include $(DIR)/../another_file
$(info list2 $(MAKEFILE_LIST))
$(info dir2 $(DIR))
output
$ make
list1 Makefile
dir1 /home/lesmana/tmp/maek/subdir
list2 Makefile /home/lesmana/tmp/maek/subdir/../another_file
dir2 /home/lesmana/tmp/maek
/home/lesmana/tmp/maek
note how the value of MAKEFILE_LIST changed after the include and with it the value of DIR.
one way to fix this is by forcing immediate expansion of DIR by using := instead of =
DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
that way the value of DIR is calculated once and does not change even if MAKEFILE_LIST changed.
another way would be to use firstword instead of lastword.
also note that the expansion of DIR in the recipe for test happens just before executing that recipe. That is why it does not matter where the include happens relative to test.
read here for more info:
https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
https://www.gnu.org/software/make/manual/html_node/Flavors.html
What is the difference between the GNU Makefile variable assignments =, ?=, := and +=?
I do not know how to feel about this shell construct to get the dir of the Makefile. Usually Makefiles from higher up include the Makefile from the subdirs. But you have your use case. I will not argue about that here. I hope my explanation of the flavors of variable helps.

Change directory before running any targets

I have the following use case where I read a variant at the top of a Makefile and then according this variable I must change directory to execute all the targets. I'd avoid to repeat in each target something like
my_target:
cd $(MY_DIR) &...
Any approach I could use to achieve that?
It's not 100% clear to me what you need but something like the following should work
ifndef submake
export submake=1
variant := somedir
$(MAKECMDGOALS):
$(MAKE) -C $(variant) -f $(realpath $(MAKEFILE_LIST)) $#
else
#actual targets defined here
foo:
#echo $#
bar:
#echo $#
endif
Go to the directory (cd) where you want Make to operate, and then
make -f /path/to/Makefile

How to pass down option -f SomeMakefile to a sub-make? [here with GNUMake]

When invoking itself recursively via some $(MAKE) foo in recipes, GNUMake passes down some of the options it was called with, but not all of them. In particular it does not pass down a -f SomeMakefile option. See section 5.7.3 of manual.
How can I find whether make was invoked with some -f option and how can I pass it down to a sub-make ?
To make the question concrete, here is what my SomeMakefile contains:
%.pdf : %.tex
pdflatex $(PDFLATEXFLAGS) $*
#if [ -f $*.log ] ; then $(MAKE) --silent $*.slw; fi
The problem is that how to make foo.slw is defined in SomeMakefile and the recursive make won't use it.
You can get the name of the makefile from MAKEFILE_LIST variable. E.g.:
${MAKE} -f $(lastword $(MAKEFILE_LIST))
If your makefile includes other makefiles you may like to store the name of the makefile early into an immediately assigned variable, e.g.:
# Somewhere at the top of your makefile, before any includes
this_makefile := $(lastword ${MAKEFILE_LIST})
# and use it later
some_rule :
${MAKE} -f ${this_makefile}
Alternatively, if you know that your makefile is always the first one read by make, then it is going to be in the front of MAKEFILE_LIST, e.g. $(firstword ${MAKEFILE_LIST}).

How to suppress command-line defintions from being exported to a sub-make?

The makefile may be something like the following:
MAKEOVERRIDES=
all:
#$(MAKE) recursive
recursive:
#echo $(foo)
.PHONY: all
.PHONY: recursive
If I run:
$ make foo=bar
I get:
make[1]: Entering directory '/home/myname'
bar
make[1]: Leaving directory '/home/myname'
The idea to suppress command-line definitions is described here:
The command line variable definitions really appear in the variable
'MAKEOVERRIDES', and 'MAKEFLAGS' contains a reference to this variable.
If you do want to pass flags down normally, but don't want to pass down
the command line variable definitions, you can reset 'MAKEOVERRIDES' to
empty, like this:
MAKEOVERRIDES =
To suppress a variable from being exported to sub-make use unexport:
MAKEOVERRIDES=
unexport foo
all:
#$(MAKE) recursive
recursive:
#echo $(foo)
.PHONY: all
.PHONY: recursive

Resources