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

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.

Related

variable doesn't get exanded for patsubst in makefile

I'm trying to write a rule that would replace a set of .c files with .o files for a variable.
To make sure it works, I'm echoing their values.
the .c files are obtained from the current working directory via a shell wildcard
sources := *.c
However, nothing gets printed out for the .o files, as 'sources' doesn't seem to get expanded for this, whether I do with using patsubst or the handier $(src:%.c=%.o).
On the other hand, if I set the value of 'manually', everything works as expected, e.g.
sources := source1.c source2.c source3.c
The makefile:
sources := *.c
srcs = one.c two.c
objects := $(sources:.c=.o)
objs := $(patsubst %.c, %.o, $(srcs))
prints:
#echo sources
#echo $(sources)
#echo objects
#echo $(objects)
#echo $(objs)
Output:
sources
source1.c source2.c source3.c source4.c
objects
*.o
one.o two.o
As you can see, 'objects' (based on the wildcard-using 'sources') is a no-go, but 'objs' (based on the manally-set 'srcs' works as expected).
What am I missing. I know it must have to do with when or how the expansion takes place.
Make is not the shell. This line:
sources := *.c
sets the make variable sources to the literal string *.c. Then this line:
objects := $(sources:.c=.o)
sets objects to the literal string *.o.
Then this line:
#echo $(sources)
Sends echo *.c to the shell, and the SHELL expands the *.c to the list of files that match that glob.
Then this line:
#echo $(objects)
sends echo *.o to the shell, and since there are no files that match the *.o glob, it just prints the glob itself *.o.
If you want to get a list of files in make you have to use the wildcard function:
sources := $(wildcard *.c)
Also, you have fallen prey to two different common errors people make when they are trying to debug makefiles:
First, you should never, EVER use # in your recipe lines to hide the commands. This is like trying to debug your makefile with a blindfold on. Seeing what make is sending to the shell gives you absolutely vital information as to what is happening here. I personally don't use # even after my makefile is working, except for some trivial operations, and I definitely never use it while trying to debug.
Second, if you want to see the contents of make variables in recipes you should always use single quotes around them. If you leave them unquoted or use double quotes, then the shell will come along and mess with those values (depending on what the values are) and you won't be able to see what they really are. Either use '' around them or else use a make function like $(info ...) to see their value rather than a shell program like echo.
If you had written your test makefile like this:
prints:
echo sources
echo '$(sources)'
echo objects
echo '$(objects)'
echo '$(objs)'
it would have been more immediately obvious what the problem was.

Makefile - get directory of itself from within included file

Say a makefile includes multiple other makefiles. How can these included makefiles get to know the path to themselves relative to the main makefile?
An example structure is as follows:
main make:
include ../dir1/dir2/make1.mk
include dir3/dir4/dir5/make2.mk
.PHONY: print_paths
print_paths:
#echo $(dir1) && #echo $(dir2)
make1
dir1 = <some code>
make2
dir2 = <some code>
My expected output would be:
../dir1/dir2
dir3/dir4/dir5
I was able to solve this for a single include file through:
dir1 = $(dir $(lastword $(MAKEFILE_LIST)))
However, this does not seem to work for multiple files, as both dir1 and dir2 will be set equal to the directory of makefile two. (which is fair I guess? It is the last file included after all)
Alternatively them getting to know their absolute path would be fine as well.
You could simply add:
dir1 := $(dir $(lastword $(MAKEFILE_LIST)))
at the beginning of ../dir1/dir2/make1.mk and:
dir2 := $(dir $(lastword $(MAKEFILE_LIST)))
at the beginning of dir3/dir4/dir5/make2.mk.

Makefile - export declarations to sub-makefiles

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.

Define in Makefile

I've got such a structure of files and directories:
.
├── Makefile
└── packages
├── Makefile
└── subdir
└── Makefile
and top Makefile looks like this:
define aa
make -C $1 $2
endef
packages=$(shell find ./packages -type d)
p1=$(filter-out . .., $(packages))
all:
$(foreach f,$(p1),$(call aa,$(f),compile))
and both of Makefiles in ./packages/ and ./packages/subdir/ have "compile" target.
I'm trying to invoke all of Makefiles automatically in "packages" subdirectory without adding them individually into Makefile.
When I run make in top directory I got error:
make -C ./packages compile make -C ./packages/subdir compile
make[1]: *** packages/subdir: No such file or directory. Stop.
Makefile:16: recipe for target 'all' failed
make: *** [all] Error 2
I'm wondering why both invocations of make (which should be separate invocations) are placed in one line?
When I add end-of-line at the end of the "aa" macro like this:
define aa
make -C $1 $2
endef
everything works as expected.
My question is why this macro doesn't work without this end-of-line?
Because a the variable definition starts after the newline which follows the define, and ends before the newline preceding the endef.
Quoting the gnumake manual :
The value in an ordinary assignment cannot contain a newline; but the newlines that separate the lines of the value in a define become part of the variable’s value (except for the final newline which precedes the endef and is not considered part of the value).
So your define is equivalent to aa = make -C $1 $2 and since foreach does not add anything between each expansion, you got that result.

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}).

Resources