tvarga
tvarga

Reputation: 11

Gnumake rules with multiple targets

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?

Upvotes: 0

Views: 1094

Answers (2)

bobbogo
bobbogo

Reputation: 15483

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?)

Upvotes: 0

Etan Reisner
Etan Reisner

Reputation: 80931

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.

Upvotes: 1

Related Questions