Define in Makefile - 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.

Related

.PHONY pattern rule: match pattern with leading slash

I am trying to simplify a Makefile. In that, I tried to have a pattern rule for removing files:
.PHONY:
rm-%:
rm $*
where % would contain the name of the file (usually the absolute path with a leading slash).
And then I just state that rm-/some/file is a dependency of make uninstall, and expect make to match this pattern rule.
However, it bite me. I did some tests and realized that the problem was that a leading slash did not match that pattern rule:
~/src/test$ cat Makefile
.PHONY:
a-/%:
$(info /$*)
.PHONY:
b-%:
$(info $*)
.PHONY:
foo: a-/as/d
#:
.PHONY:
bar: b-/as/d
#:
~/src/test$ make foo
/as/d
~/src/test$ make bar
make: *** No rule to make target 'b-/as/d', needed by 'bar'. Stop.
~/src/test$
Why do I need to specify that leading slash in the pattern rule (but not other slashes)?
The GNU make documentation says this:
the ‘%’ matches any nonempty substring, while other characters match
only themselves
https://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html#Pattern-Intro
EDIT: Fix some typos about .PHONY:, use a suffix instead of a prefix as suggested in the answers/comments, and test further:
As I initially suspected, .PHONY: is doing something weird:
~/src/test/$ tree a b c d
a
└── a
b
└── b
c
└── c
d
└── d
0 directories, 4 files
~/src/test$ cat Makefile
%-rmdir:
rmdir $(#D)
%-rm:
rm $*
.PHONY: ./a/.-rmdir
./a/.-rmdir: ./a/a-rm
.PHONY: ./b/-rmdir
./b/-rmdir: ./b/b-rm
./c/.-rmdir: ./c/c-rm
./d/-rmdir: ./d/d-rm
.PHONY: foo
foo: ./a/.-rmdir ./b/-rmdir ./c/.-rmdir ./d/-rmdir
$(info foo)
~/src/test$ make foo
rm a/a
rm b/b
rm c/c
rmdir c
rm d/d
rmdir d
foo
You need to examine the section on How Patterns Match where you will find this text:
When the target pattern does not contain a slash (and it usually does
not), directory names in the file names are removed from the file name
before it is compared with the target prefix and suffix.
(and more detail about how this works).
As per documentation and #MadScientist answer, this is how patterns match. But workaround is very simple: replace your prefix rules with suffix rules and this directory mangling will not stand in the way.
Example:
$ cat Makefile
.PHONY: %-clean
%-clean:
$(info $*)
.PHONY: foo
foo: /as/d-clean
#:
.PHONY: bar
bar: ./as/d-clean
#:
Output:
$ make foo
/as/d
$ make bar
as/d
BTW. Please note that .PHONY: requires target name, it does not automagically apply to the target that follows.

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.

patsubst's returned value when no pattern found

I'm trying to send "sub-make" commands to a subdirectory. I have a Parent Directory with a "top_level" Makefile and a child directory with its own Makefile.
My parent Makefile has a target line as follow :
target%:
make -C sub_dir $(patsubst target-%,%,$#)
I can do in the parent folder:
make target-clean && make target-all
It will be interpreted as :
make -C sub_dir clean && make -C sub_dir all
I want to be able to do:
make target
but in this case, I get :
make -C sub_dir target.o
I was expecting that while "patsubst" does not find the pattern, it will return either nothing or the tested expression. But it returns this "target.o".
Can someone explain it to me ? How can I manage to get nothing ?
I tried these expressions without success:
make -C sub_dir $(patsubst target%,%,$#)
make -C sub_dir $(patsubst -%,%,$(patsubst target%,%,$#))
make -C sub_dir $($(patsubst -%,%,$(patsubst target%,%,$#)):.o=)
The last one is tricky, it gives:
make -C sub_dir
make[1]: Entering directory /home/aurelien/Documents/Projects/parent/sub_dir'
make[1]: 'subtarget' is up to date.
make[1]: Leaving directory '/home/aurelien/Documents/Projects/parent/sub_dir'
cc target.o -o target
cc: target.o: No such file or directory
cc: no input files
make: *** [target] Error 1
The target% pattern matches but only with at least one character for the %, not zero. From the GNU make manual:
A pattern rule contains the character ‘%’ (exactly one of them) in the
target; otherwise, it looks exactly like an ordinary rule. The target
is a pattern for matching file names; the ‘%’ matches any nonempty
substring, while other characters match only themselves.
So target does not match and make uses its implicit rules to build target: build target.o and then target from target.o. When trying to find a way to build target.o, the pattern rule matches and the recipe:
make -C sub_dir $(patsubst target-%,%,$#)
is expanded. But as target.o does not match target-%, patsubst does nothing and your recipe becomes:
make -C sub_dir target.o
The problem is that a pattern must match one or more characters. So, this pattern:
target%:
does not match the target name target, because there are no characters left over to match the pattern.
So, make looks for another implicit rule that can be used to build target and it finds the built-in rule:
% : %.o
so it tries to build target.o. This target will match the pattern target% with a stem of .o, so it tries to use your rule to build that.
To specifically answer your question, any word that does not match the substitution pattern in patsubst will be passed through without changing it.

wildcards in cygwin make in cmd.exe

im trying to make a simple makefile that will build multiple artifacts named after subdirs of the src directory. here is my dir structure:
>find .
.
./makefile
./src
./src/binA
./src/binA/main.java
./src/binB
./src/binB/main.cpp
./src/binC
./src/binC/main.scala
and here is the makefile i am trying to use. for some reason the binary target refuses to expand its dependencies with when i use both a pattern and a wildcard in the same rule
>cat makefile
dirs := $(shell find src -mindepth 1 -type d)
all: $(dirs:src/%=bin/%.exe)
#echo $(dirs)
bin/%.exe: src/%/*
#echo "$# <-- $^"
i know dirs is getting set properly
src/binA src/binB src/binC
this is the error i get
>make
make: *** No rule to make target `bin/binA.exe', needed by `all'. Stop.
how can i make a generic rule that will properly expand its dependencies to be the contents of a subdir that is based on its name
i was able to get this to work by changing the rule matcher to bin/%.exe: $(wildcard src/%/*) and add a .SECONDARYEXPANSION: before that.

Resources