Make extra newline in prerequisite in define recipe - makefile

I am trying to create a Makefile which uses a recipe created using the define syntax. However, I have run into a problem where Make believes that the last prerequisite has a newline appended to it, resulting in a "no rule found" error. A minimal working example is given below, the problem is triggered by trying to make the foo/bar target.
foo:
mkdir foo
define FIZZ
foo/bar: foo
touch foo/bar
endef
$(call FIZZ)
The exact error message is
make: *** No rule to make target 'foo
', needed by 'foo/bar'. Stop.
I have tried versions 4.1 and 4.2.1 of GNU Make.
Can anyone tell me what I am doing wrong (or is this a bug)?

What you are trying to do here is expand the variable FIZZ to inject the
definition of the target foo/bar (its prerequisites and recipe) into the
expanded makefile. $(call FIZZ) is not the tool for that:
$(call variable,param,param,...)
may behave surprisingly if the arguments contain embedded whitespace. You want
8.9 The eval Function,
as in:
Makefile
foo:
mkdir foo
define FIZZ
foo/bar: foo
touch foo/bar
endef
$(eval $(FIZZ))
Then you'll get:
$ make foo/bar
mkdir foo
touch foo/bar

Related

Patterns in makefile targets

Makefile:
%_file:
touch $#
foo_%: %_file
bar: foo_bar
Results:
>make foo_bar
make: *** No rule to make target 'foo_bar'. Stop.
>make bar
make: *** No rule to make target 'foo_bar', needed by 'bar'. Stop.
I have intentionally omitted adding .PHONY rules because I know the prerequisites of phony rules are always interpreted as literals. I do not see any reason that make foo_bar or make bar should not run without error. What am I missing?
You have a dependency between foo_% and %_file, but you have defined any rules on how to build foo_%. So when you run:
make foo_bar
Make figures out it first needs to build bar_file, but can't find any rules for building foo_bar. If you were to write instead:
%_file:
touch $#
foo_%: %_file
cp $< $#
bar: foo_bar
Then running make foo_bar would result in:
touch bar_file
cp bar_file foo_bar
rm bar_file
To clarify #larsks answer, the GNU make manual says:
You can cancel a built-in implicit rule by defining a pattern rule with the same target and prerequisites, but no recipe.
So your pattern rule:
foo_%: %_file
does not create a pattern rule, it deletes a (non-existent anyway) pattern rule.
If you want a pattern rule it must have a recipe.

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.

Match anything pattern rule with dependency

File Name: Makefile.mk
%: foo
#echo %: $# with foo
foo:
#echo foo
Run
$ make -f Makefile.mk test
Output:
foo
%: Makefile.mk with foo
%: test with foo
I am running this in GNU Make 3.81 version.
I Don't understand, why file name also printed(%: Makefile.mk with foo).
Can some one please explain me?
This is because of how makefiles are remade. That is to say that
Sometimes makefiles can be remade from other files
and
If a makefile can be remade from other files, you probably want make to get an up-to-date version of the makefile to read in
so
after reading in all makefiles, make will consider each as a goal target and attempt to update it.
Which then matches against your match-anything rule and triggers the way you see.
If you add an explicit Makefile.mk: ; target to your makefile it will override the match-anything target and prevent this.

Rule for all targets in make - even if the file exists

I want to create a Makefile that outputs foo no matter what target name is given to make.
So all of these should work:
$ make
foo
$ make a
foo
$ make foobar
foo
The following Makefile does almost what I want:
all %:
#echo foo
.PHONY: all
However it fails if there exists a file with the same name as the target:
$ touch abc
$ make abc
make: `abc' is up to date.
As .PHONY doesn't accept pattern rules, I don't know how I can get make to ignore every file.
How about:
all $(MAKECMDGOALS): ; #echo foo
.PHONY: all $(MAKECMDGOALS)

How to generate Makefile rule

I want to do generate rules in Makefile by this:
# $(call cc-defs, ccfiles)
define cc-defs
$1.files = $(patsubst %.cc,%.proto,$1)
$1: $1.files
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs, $(ccfile))))
but failed with error message:
Makefile:19: *** commands commence before first target. Stop.
Instead that, I can do this by:
# $(call cc-defs, ccfiles)
define cc-defs
$1.files = $(patsubst %.cc,%.proto,$1)
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs, $(ccfile))))
$(foreach ccfile,$(ccfiles), $(eval $(ccfile):$($(ccfile).files)))
How to make the 1st method works?
Which version of make are you using? $(eval) only appeared in 3.80 (and it only properly works in 3.81 IMHO).
To debug makefiles you'll often have to revert to printf debugging. To see what's going on, replace eval with warning. This shows what you are giving to make:
$ make --warn
Makefile:6: warning: undefined variable `ccfiles'
make: *** No targets. Stop.
(Aside: --warn-undefined-variables is always useful. Undefined variables are untidy.)
O.K., so we need to define $ccfiles. Now we get the for loop firing:
$ make --warn ccfiles=1.cc
Makefile:6: 1.c.files = 1.cc
1.cc: 1.c.files
make: *** No targets. Stop.
Fine. You have given make no recipes, nor a default target. You also have missed out on some variable expansion, and have an extra space in the $(for) invocation (naughty!). Try this:
$ cat Makefile
# $(call cc-defs,ccfiles)
define cc-defs
$1.files = $(patsubst %.cc,%.proto,$1)
$1: $$($1.files) ; echo '[$$#]'
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs,$(ccfile))))
$ make ccfiles=1.cc
make: *** No rule to make target `1.proto', needed by `1.cc'. Stop.
Note that if all you want to do is for all files in a variable to depend on (or be made from) .proto files, you don't need $(eval).
A pattern rule will do (and will work in older versions of GNU Make too):
$(ccfiles): %.cc: %.proto
echo '[$#]'
This does have the side effect of complaining when the ccfiles variable contains any entries not named *.cc (although it still executes the rule in that case).
$ make ccfiles=hello.cc
make: *** No rule to make target `hello.proto', needed by `hello.cc'. Stop.
$ touch hello.proto
$ make ccfiles=hello.cc
[hello.cc]
$ make ccfiles=hello.c
Makefile:1: target `hello.c' doesn't match the target pattern
[hello.c]
If the variable can contain many things but you only want to add this processing to .cc files, simply add a filter:
$(filter %.cc,$(ccfiles)): %.cc: %.proto
echo '[$#]'
This then results in:
$ make ccfiles=hello.cc
[hello.cc]
$ make ccfiles=hello.c
make: *** No targets. Stop.

Resources