Simple inference rule in makefile - makefile

I'm currently learning how to use makefiles. But I'm struggling with % pattern rules. I've boiled down my failing makefile to this very simple example:
I fill an empty directory with:
echo aaa > a.in && echo bbb > b.in
A first makefile like this works very well:
a.out : a.in
cat $< > $#
as
make && echo *.out && cat *.out
returns
cat a.in > a.out
a.out
aaa
but when I try to use a pattern rule modifying the makefile as follows:
%.out : %.in
cat $< > $#
make then returns me:
make: *** No targets. Stop.
It seems like a very simple problem but I can't get to what I am missing...

If you have a makefile with no targets lists (only patterns), and you just type make, then you haven't told make that it should build anything specific, so it won't do anything.
A pattern rule doesn't mean "go find all the files that match this pattern and build them". A pattern rule tells make "if you need to find a way to build a file that matches this target pattern, then here's how you do it".
If you type make a.out so make knows that you want to build a target a.out, then make will use your pattern rule to build it.
Alternatively, you can add the target to your makefile, something like this:
.PHONY: all
all: a.out
%.out : %.in
cat $< > $#

Related

Match patten rule before explicit rule

I'm trying to generically add some behaviour to every target in a Makefile, without modifying the targets.
My current attempt is thus:
%: $*
#echo 'Logging $* target'
.PHONY: test
test:
#echo 'Inside explicit test target'
When I run make test, I'd like to match the % pattern rule, which would execute test as a prerequisite ($* expanding to the pattern stem), and then log the target that was run.
$ make test
Inside explicit test target
Logging test target
Instead, what happens is that make test matches the explicit test target (presumably since it's a closer match):
$ make test
Inside explicit test target
How can I get this to work, without changing the explicit test target?
EDIT:
Another attempt...
.SECONDEXPANSION:
%: $$*
#echo 'Logging $* target'
results in
$ make test
make: Circular Makefile <- Makefile dependency dropped.
inside actual test target
I appears from your own answer, which has beaten me to the punch, that
you're concerned only to trigger a preliminary action for targets that are
mentioned on the commandline - $(MAKECMDGOALS). From the posting I took
it that you wanted such an action for "every target in a Makefile", which
would include all targets that are prerequisite to the commandline targets or,
if there are no commandline targets, to the default target.
Anyhow, you may still be interested in a solution to the more general problem.
You want a preliminary action to be executed before the recipe for every target.
Your question is: how to match a patten rule before explicit rule?
This is an XY way of posing the problem, because make will consult pattern
rules to find a way of making a target only if you don't give it an explicit
recipe. You know, for example, that make has a pre-defined pattern rule for
making an .o file from a .c file. Even so, if my makefile is:
test.o:
#echo $#
then make prints test.o, without any attempt to find test.c and compile it.
And if my make file is:
test.o: test.c
#echo $#
test.c:
#echo $#
then make prints:
test.c
test.o
needing no resort to the pattern rule. But if my makefile is:
test.o: test.c
Then make says:
make: *** No rule to make target 'test.c', needed by 'test.o'. Stop
So you can't do what you're after in the way your question supposes,
because the preliminary action you want to provoke from the pattern
rule could be provoked only if there were no other action for the target.
In that case the reasons for the failures of your two posted attempts are fairly academic,
and you may wish to scroll to The Chase.
In your first attempt, with:
%: $*
#echo 'Logging $* target'
The pattern rule - which is unemployed by make test - is equivalent to:
%:
#echo 'Logging $* target'
because $* only assumes a value in the recipe, not in the pattern rule. You
can make this pattern rule be employed by making any target for which the
makefile does not provide a recipe, e.g. make nonsuch will print Logging nonsuch target;
but that is of no use.
The second attempt, with:
.SECONDEXPANSION:
%: $$*
#echo 'Logging $* target'
does the right thing to create the rule you intend to create. But the
meaning of that rule is:
<target>: <target>
#echo 'Logging <target> target'
making every target to which this rule is applied a prerequisite of itself.
Inevitably this will result in a circular dependency error for all such targets.
As you saw, this circularity does not affect the your test target because
it has an explicit recipe and does not employ the rule. But it does provoke
the surprising error:
make: Circular Makefile <- Makefile dependency dropped.
That happens because the first target that make automatically considers is
the makefile itself. Unlike the test target, you have no recipe for
the makefile; so the pattern rule applies to it, making the makefile dependent
on itself.
The Chase
You can achieve what you want by a different approach. In a actual project
it is more than likely that in any makefile you can compute a list of
all possible targets. From this you can generate a corresponding list of
auxiliary targets, say, target => target.prelim, where the
sole purpose of target.prelim is to provoke, when it should and not
otherwise, the required preliminary action for target; and you can get make
to generate a list of order-only rules, target: | target.prelim,
for each target, such that target.prelim will not be considered in determining whether target
must be made, but will be made before target whenever target needs to be made.
Here is an illustration:
SRCS := main.c foo.c
OBJS := $(SRCS:.c=.o)
TARGETS := all prog $(OBJS)
PRELIMS := $(patsubst %,%.prelim,$(TARGETS))
define prelim_rule =
$(1): | $(1).prelim
endef
$(foreach target,$(TARGETS),$(eval $(call prelim_rule,$(target))))
.PHONY: all
all: prog
prog: $(OBJS)
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $# $(OBJS) $(LIBS)
clean:
rm -f $(OBJS) $(PRELIMS) prog
%.prelim:
#echo "Logging target $(#:%.prelim=%)"
#touch $#
And a sample session:
$ make
Logging target all
Logging target main.o
cc -c -o main.o main.c
Logging target foo.o
cc -c -o foo.o foo.c
Logging target prog
cc -o prog main.o foo.o
$ make
make: Nothing to be done for 'all'.
$ make clean
rm -f main.o foo.o all.prelim prog.prelim main.o.prelim foo.o.prelim prog
$ make main.o
Logging target main.o
cc -c -o main.o main.c
$ make main.o
make: 'main.o' is up to date.
$ # A prelim can't out-date its target...
$ touch main.o.prelim
$ make main.o
make: 'main.o' is up to date.
I realise that this isn't answering my question as asked, but it has the effect I want - executing a shell command as late in the Makefile processing as possible.
MYVAR?=foo
.PHONY: test
test:
#echo 'Inside test target'
LOG=$(shell echo 'Logging $(MAKECMDGOALS), myvar=$(MYVAR)' > log)
.SECONDEXPANSION:
force: $$(LOG)
LOG is a deferred variable, so is not expanded until Make evaluates the prerequisite list of the force target.
In a single Makefile, the .SECONDEXPANSION: part is not needed, since the force target is evaluated after MYVAR is set.
However, if I move the LOG variable and force variable into a sub-makefile, it would be easy to include subMakefile before the MYVAR?= line - which would not work.
By specifying .SECONDEXPANSION for force, the reliance on ordering is removed.

How does % in Makefiles work?

I am trying to understand how makefiles work. If I try the following
1.out : 1.inp
cat 1.inp
it, works as expected (if 1.inp is newer as 1.out or if 1.out does not exist, the content of 1.inp is printed).
Then I need to do the following:
cat 1.inp
cat 2.inp
cat 3.inp
I thought that for that I can use
%.out : %.inp
cat $<
but it does not work. Can anybody please explain to me the use of %?
In your first makefile:
1.out: 1.inp
cat 1.inp
1.out is a target with a prerequisite 1.inp and a command cat 1.inp. This constitutes a rule for
making 1.out and if you run the makefile, specifying either no explicit target or the target 1.out, the
rule will be executed.
In your second makefile:
%.out: %.inp
cat $<
you have only expressed a pattern rule for making a target of the form something.out from a prerequisite of the form something.inp You have not specified any actual targets that the makefile can make.
You can specify 1.out as a target with a makefile like this:
%.out: %.inp
cat $<
1.out:
In that case 1.inp will be cat-ed when 1.inp is newer that 1.out
Or you can specify a phony target, e.g. all, that has prerequisite targets 1.out, 2.out, 3.out in a makefile like this:
.PHONY: all
%.out: %.inp
cat $<
all: 1.out 2.out 3.out
In this case each of 1.inp, 2.inp and 3.inp will be cat-ed when it is newer than the corresponding .out file.
Whenever make discovers that it has to make a target of the form something.out, it will recognize that the pattern rule is applicable and will execute the matching instance of the pattern rule.

Makefile pattern rule either ignores phony rule or spontaneously deletes output file

I'm trying to write a makefile to produce several output files for each of several sources, using pattern rules.
I have the following Makefile (GNU Make 3.8.1):
all : foo.all bar.all
%.all : %.pdf %.svg
#echo Made $*
%.pdf :
touch $#
%.svg :
touch $#
.PHONY: foo.all bar.all
Since *.all do not represent real output files, I tried marking them as .PHONY. However, running make then doesn't work:
$ ls
Makefile
$ make
make: Nothing to be done for `all'.
According to make -d:
No implicit rule found for `all'.
Considering target file `foo.all'.
File `foo.all' does not exist.
Finished prerequisites of target file `foo.all'.
Must remake target `foo.all'.
Successfully remade target file `foo.all'.
Considering target file `bar.all'.
File `bar.all' does not exist.
Finished prerequisites of target file `bar.all'.
Must remake target `bar.all'.
Successfully remade target file `bar.all'.
Finished prerequisites of target file `all'.
Must remake target `all'.
Successfully remade target file `all'.
make: Nothing to be done for `all'.
which seems to be pretending to run the %.all rules, but skipping the bodies.
But with the .PHONY line commented out, Make runs the targets, but then spontaneously decides to delete the output files:
$ make
touch foo.pdf
touch foo.svg
Made foo
touch bar.pdf
touch bar.svg
Made bar
rm foo.pdf foo.svg bar.pdf bar.svg
According to make -d, it says:
Removing intermediate files...
Minimal example
A minimal example giving anomalous behavior:
%.all: %.out
#echo Made $*
%.out:
touch $#
I expect running make somefile.all to cause it to create the file somefile.out, but it gets deleted:
$ make somefile.all
touch somefile.out
Made somefile
rm somefile.out
Keeping make from deleting intermediary files
I recommend against using .PRECIOUS (see below as to why). Using .SECONDARY would preserve the .out files:
TARGETS=foo bar
all: $(TARGETS:=.all)
%.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
$(TARGETS:=.all) just appends .all to all names in TARGETS. $(TARGETS:=.out) appends .out. We apparently cannot use %.out as a target of .SECONDARY. These just save having to relist all targets individually.
I prefer to not use .PRECIOUS for this because the documentation says
if make is killed or interrupted during the execution of their recipes, the target is not deleted.
This can leave corrupted files in the file system. Here's an example.
all: foo.all bar.all
%.all: %.out
#echo Made $*
%.out:
sh -e -c 'echo "{1, 2, 3" > $#; FAIL!; echo "}" >> $#'
.PRECIOUS: %.out
The FAIL! command simulates a tool that crashes in the middle of its work. Here's a shell session working with the Makefile above:
$ ls
Makefile
$ make
sh -e -c 'echo "{1, 2, 3" > foo.out; FAIL!; echo "}" >> foo.out'
sh: 1: FAIL!: not found
make: *** [foo.out] Error 127
$ cat foo.out
{1, 2, 3
Yikes... my foo.out file is incomplete. Let's try making again:
$ make
Made foo
sh -e -c 'echo "{1, 2, 3" > bar.out; FAIL!; echo "}" >> bar.out'
sh: 1: FAIL!: not found
make: *** [bar.out] Error 127
$ cat *.out
{1, 2, 3
{1, 2, 3
Make is none the wiser about files left around by earlier runs so when you run make again, it will take the corrupted files at face value. foo.out was not remade (despite the "Made foo" message) because it already exists and the Makefile went straight to trying to make bar.
.SECONDARY makes it so that:
The targets which .SECONDARY depends on are treated as intermediate files, except that they are never automatically deleted.
This means they are never automatically deleted just because they are intermediate files. The default make behavior of deleting targets that were being rebuilt if the tool rebuilding them crashed is not affected.
Using .PHONY with pattern rules
It seems though that .PHONY works only for targets that are explicit, not inferred. I've not found documentation confirming this. However, this works:
TARGETS:=foo bar
TARGETS_all:=$(TARGETS:=.all)
.PHONY: all
all: $(TARGETS_all)
.PHONY: $(TARGETS_all)
$(TARGETS_all): %.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
In this rule $(TARGETS_all): %.all: %.out $(TARGETS_all): gives the list of targets to which the pattern can be applied. It makes foo.all and bar.all explicit targets. Without this, they would be inferred targets.
You can test that it works by creating file called foo.all in your directory and run make over and over. The foo.all file has no effect on make.
Your somefile.out files are considered intermediate by GNU make, which is why they are automatically deleted in your example. You can instruct GNU make to preserve these files by use the of .PRECIOUS special target, like this:
%.all: %.out
#echo Made $*
%.out:
touch $#
.PRECIOUS: %.out

Wildcard in implicit rule's prerequisites

BUILT_DIR = /tmp/obj
SRC = /source/dir
/tmp/obj/%/builtin.o : $(SRC)/%/*.c
gcc $^ -o $#
But you know :
In order for the pattern rule to apply, its target pattern must match the file name under consideration and all of its prerequisites (after pattern substitution) must name files that exist or can be made.
If i execute make /tmp/obj/hfa/builtin.o,make will complain :
make: *** No rule to make target/tmp/obj/hfa/builtin.o'. Stop.`
How can i modify the Makefile to satisfy my requirement?
You can use Secondary Expansion
BUILT_DIR = /tmp/obj
SRC = /source/dir
.SECONDEXPANSION:
/tmp/obj/%/builtin.o : $$(wildcard $(SRC)/%/*.c)
gcc $^ -o $#
The error you see indicates that there are no .c files which match the pattern $(SRC)/hfa/*.c since the % translates into hfa. So make cannot use the rule you've defined.
Make then starts to use the implicit rules for building and it would not match either.
Finally make gives up.
I just confirmed that the same Makefile and I get the same error only when there are no *.c files in the $(SRC)/hfa directory.
Otherwise, I see the gcc command getting executed.
And from your question, it is not quite clear what your requirement is.
The rule configuration that worked for me:
bar-%-foo:
#touch $#
.SECONDEXPANSION:
foo-%-bar: bar-$$*-foo
#echo "#: $#"
#echo "<: $<"
#touch $#
foo-bar: foo-biz-bar foo-baz-bar
.PHONY: foo-bar
And demo:
$:make foo-bar
#: foo-biz-bar
<: bar-biz-foo
#: foo-baz-bar
<: bar-baz-foo
rm bar-biz-foo bar-baz-foo
$:ls | grep foo
foo-baz-bar
foo-biz-bar

Why does Make ignore my wildcard rule?

Why doesn't Make link to foo.o?
$ ls
foo.c foo_test.c Makefile
$ cat Makefile
.PHONY: test
test: foo_test
%_test: %_test.o foo.o
$ make
cc foo_test.c -o foo_test
Pattern rules MUST have a recipe associated with them. Any pattern rule without a recipe tells GNU make to delete that pattern rule. So, your line:
%_test: %_test.o foo.o
does nothing except delete the non-existent pattern rule to build %_test from %_test.o. You need to create a recipe if you want it to take effect:
%_test: %_test.o foo.o
$(CC) -o $# $(LIBS) $^
or whatever. However, this is completely not necessary for your example. You don't need any rule at all for that, just write:
foo_test: foo_test.o foo.o
and let make's built-in rules handle it.

Resources