Gnumake rules with multiple targets - makefile

I'm looking for a way for me to convince gnumake to build 'all'
targets of a rule as a unit AND to require that they get re-built if
there is any reason that ANY one of the targets is missing or
out-of-date.
Consider this simple Makefile:
b.foo :
touch b.foo
b.bar1 b.bar2 : b.foo
touch b.bar1
touch b.bar2
b.zoo1 : b.bar1
touch b.zoo1
b.zoo2 : b.bar2
touch b.zoo2
# Building b.zoo1 works as expected
> make4 b.zoo1
touch b.foo
touch b.bar1
touch b.bar2
touch b.zoo1
> make b.zoo1
make: 'b.zoo1' is up to date.
# Building b.zoo2 also works as expected
> make b.zoo2
touch b.zoo2
> make b.zoo2
make: 'b.zoo2' is up to date.
# Now I remove one of the peers built with the 2nd rule
> rm b.bar2
# I see that b.zoo1 stays up-to-date because its dependency still exists.
# However, this is NOT the behavior that I'm looking for. With b.bar2
# now missing, I want b.zoo1 AND b.zoo2 to be out-of-date.
> make b.zoo1
make: 'b.zoo1' is up to date.
# But it's not. Worse yet, building b.zoo2 does force b.bar1 and b.bar2 to be rebuilt
> make b.zoo2
touch b.bar1
touch b.bar2
touch b.zoo2
# which now makes b.zoo1 out-of-date
> make b.zoo1
touch b.zoo1
So, is there any way to code up a rule that builds multiple targets to behave as I wish? Or is there a way to use the gnumake standard library to
accomplish this?

b.bar1 b.bar2 : b.foo this rule tells make that there are two targets b.bar1 and b.bar2 both of which have a dependency on b.foo and both of which can be built by the listed rule. It does not tell make that they are related targets that get built by the same rule invocation. With GNU make you can tell make about the latter information by using a pattern rule like %.bar1 %.bar2: %.foo.
I don't know that I fully understand the problem as you've explained it but I think this information (and the pattern rule) might be of use here.

Yep, writing many targets in a rule is just shorthand for writing them out individually.
b.bar1 b.bar2 : b.foo
touch b.bar1
touch b.bar2
is exactly the same as
b.bar1: b.foo
touch b.bar1
touch b.bar2
b.bar2: b.foo
touch b.bar1
touch b.bar2
Clearly wrong. You could write
b.foo:
touch b.foo
b.bar1: b.foo
touch b.bar1
b.bar2: b.foo
touch b.bar2
b.zoo1: b.bar1 b.bar2
touch b.zoo1
b.zoo2: b.bar1 b.bar2
touch b.zoo2
Tidy this up by using $# in a recipe as the target name
b.foo:
touch $#
b.bar1: b.foo
touch $#
b.bar2: b.foo
touch $#
b.zoo1: b.bar1 b.bar2
touch $#
b.zoo2: b.bar1 b.bar2
touch $#
Now you see the utility of many-targets-in-a-rule-is-the-same-as-writing-the-rules-out-individually. We can write this as
b.foo:
touch $#
b.bar1 b.bar2: b.foo
touch $#
b.zoo1 b.zoo2: b.bar1 b.bar2
touch $#
Nice. This fixes your original problem.
(I suspect though that this may not fix your actual problem. Is both b.bar1 and b.bar2 created by just a single run of some utility?)

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.

Make extra newline in prerequisite in define recipe

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

Is this a make bug?

Most likely it is not but I've been struggling with this one for an hour now:
$ cat Makefile
${FILE1}:
touch $#
a: ${FILE1}
${FILE2}: a
touch $#
$ make FILE1=foo FILE2=bar bar
touch foo
touch bar
$ ls
bar foo Makefile
$ make FILE1=foo FILE2=bar bar
touch bar
Why is the bar rule still activated?
If I change Makefile to:
${FILE1}:
touch $#
${FILE2}: ${FILE1}
touch $#
Everything works, i.e. bar is not touched again.
The target bar has a prerequisite, a. You have a rule for a, but it does not actually build a file named "a". So every time you ask Make to rebuild bar (if necessary), Make sees that the prerequisite a does not exist, and therefore it must attempt to rebuild both targets.

Why does this Makefile fail to build make the final target, but all intermediate targets work fine?

Say we have the following Makefile:
.PHONY: build
build:
cd .. && make
%.hl.a.json: %.c
touch $#
%.hl.b.json: %.hl.a.json
touch $#
%.x.p.json: %.hl.b.json
touch $#
%.x.hl.a.json: %.x.p.json
touch $#
%.x.hl.b.json: %.x.hl.a.json
touch $#
.PHONY: clean
clean:
$(RM) *.json
Let us assume we have file called small.c, and that I want to build small.hl.b.json. However, running make small.hl.b.json (with GNU Make 3.81) gives me:
$ make small.x.hl.b.json
make: *** No rule to make target `small.x.hl.b.json'. Stop.
However, the Makefile works for building small.x.hl.a.json which produces
$ make small.x.hl.a.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
rm small.x.p.json small.hl.a.json small.hl.b.json
Thus, if I run make small.x.hl.a.json first, and then make small.x.hl.b.json, then everything works.
And now it gets interesting. If I do the following:
$ make small.x.hl.a.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
rm small.x.p.json small.hl.a.json small.hl.b.json
$ make clean
rm -f *.json
$ make small.x.hl.b.json
make: *** No rule to make target `small.x.hl.b.json'. Stop.
it does not work. But it does work if I do this:
$ make small.x.hl.a.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
rm small.x.p.json small.hl.a.json small.hl.b.json
$ make clean small.x.hl.b.json
rm -f *.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
touch small.x.hl.b.json
rm small.x.p.json small.hl.a.json small.hl.b.json
Can someone tell me what's going on? And why can't I build small.x.hl.b.json directly but instead must first build small.x.hl.a.json and then small.x.hl.b.json?
I may be wrong, and I'll delete this answer if anyone can provide a better one, but this looks to me like a bug in GNUMake 3.81:
Make first considers the target small.x.hl.b.json, and finds two pattern rules that match it:
%.hl.b.json: %.hl.a.json # call this rule 2
...
%.x.hl.b.json: %.x.hl.a.json # call this rule 5
...
Note that both of these rules have the same prerequisite, namely small.x.hl.a.json.
It looks as if Make then makes a mistake. (Here's where I go out on a limb.) It goes searching for a way to build small.x.hl.a.json, remembering that it has already used rules 2 and 5, but failing to remember that they are parallel alternatives, not sequential steps. Later on, when Make needs small.hl.b.json, the only rule that will serve is rule 2, which Make rejects on the grounds that it must not use the same pattern rule twice in a chain. Never mind that this invocation would use a shorter stem, never mind that the chain in question is actually two chains, one of which has not yet used Rule 2, Make considers this a dead end.
This fits the results of the other experiments as well. Make can build small.x.hl.b.json without this self-collision, and once that target is built, Make can then build small.x.hl.b.json in a single step without a troublesome chain.
I found a tutorial saying that:
The rule with the shorted stem will be tried first, and if there is a tie the rule that appears first will be selected.
So I tried swapping the order of the rules so that .x. targets appear first, and then everything works!

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)

Resources