0

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?

tripleee
  • 175,061
  • 34
  • 275
  • 318
tvarga
  • 11
  • 3
  • It seems that you could simply declare zoo1 as having a dependency on bar2 and vice versa for zoo2 and bar1. You still can't force "make zoo1" to build zoo2 unless you also introduce a dependency between them, but you can't make a circular dependency anyway. What's the real use case for this? – tripleee Jun 27 '14 at 21:25
  • I use gnumake for engineering workflows. In this environment, many tools generate multiple outputs. It is very often the case that a downstream tool only depends on one of the prior tool's targets. I don't want to have subsequent targets depend on the prior tool's other targets that are not real dependencies. However, it is crucial that the prior tool's multiple targets stay consistent with each other ... and exist too. – tvarga Jun 27 '14 at 21:44
  • *"I don't want... subsequent targets [to] depend on the prior tool's other targets... However, it is crucial that the prior tool's multiple targets [exist and] stay consistent with each other."* Which is it? Do you want Make to ensure that the multiple targets exists and are consistent, before it executes the downstream tool? – Beta Jun 28 '14 at 22:33

2 Answers2

1

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.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • This the answer - and the issue ffrequently appears on the GNU Make mailing lists and in bug reports - see e.g. [issue 8297](https://savannah.gnu.org/bugs/?8297), [issue 19108](https://savannah.gnu.org/bugs/?19108), [issue 42125](https://savannah.gnu.org/bugs/?42125) – reinierpost Jun 30 '14 at 08:07
0

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

bobbogo
  • 14,989
  • 3
  • 48
  • 57
  • A single large rule creates multiple targets simultaneously. We have hundreds of rules like that. I don't want to duplicate huge rules. A particular downstream target likely only depends on one of those multiple targets. Another downstream target may depend on another one of those outputs. Hence, those targets must appear to have be generated atomically, because they were. I just found an interesting work-around at http://www.cmcrossroads.com/article/atomic-rules-gnu-make?page=0%2C0 which does work. But it's ugly and has a runtime hit. Gnumake should behave this way by default. – tvarga Jun 30 '14 at 19:08
  • @tvarga The solution above matches your original formulation which used two separate `touch` commands to generate the original _bar_ files. If you have a _single_ command that produces _two_ (or more) targets (which I think is what you have) then there are two options: (i) a pure pattern rule or (ii) a sentinel file. Personally I don't like the ad-hoc nature of pattern rules, and therefore turn to sentinel files. This pattern happens quite a lot (e.g., linking a .dll on Windows will spit out a .pdb and a .lib file at the same time), but not many people code it properly. – bobbogo Jul 01 '14 at 10:46