%o expands to only the first output, while the docs and tup both promise all of them.
The divergence
docs/reference.md:1010 documents %o as "All output files". Tup, which putup's Tupfile syntax
follows, expands %o to every output separated by spaces — src/tup/parser.c:3946+ iterates the
whole output list (TAILQ_FOREACH(nle, &onl->entries, list)).
putup emits only the first output. Verified at the line in all three independent expanders:
src/graph/dag.cpp:933 — case 'o': appends get_operand_path(cmd->outputs[0])
src/index/entry.cpp:446 — case 'o': resolves cmd.outputs[0] only
src/parser/eval.cpp:507 — bare %o appends the single flags.output
Notably the multi-output data is already present: eval.cpp:449-450 implements the numbered form
(%2o) against flags.all_outputs. Bare %o simply does not consult it.
Why it matters beyond the obvious
A rule with two or more outputs silently passes only the first to its command. For the common
one-output rule the behaviour is indistinguishable, which is why this has gone unnoticed.
It also has a testing consequence that reaches past this bug. Any test asserting that some output is
absent from %o passes vacuously today, because %o never emits more than outputs[0]
regardless. A pin of that shape looks like coverage and is not. A discriminating pin must use the
numbered form (%2o) or assert the full expansion of a genuinely multi-output rule.
Disposition
Upstream wins: putup's %o should expand to all outputs, matching both tup and putup's own
documentation. The three expanders should agree by construction rather than by three separate
correct implementations — they are the same question asked in three places.
Found while inventorying output consumers for #370 (extra outputs); #370's discriminating test
depends on this being fixed or accounted for, so the two are ordered.
%oexpands to only the first output, while the docs and tup both promise all of them.The divergence
docs/reference.md:1010documents%oas "All output files". Tup, which putup's Tupfile syntaxfollows, expands
%oto every output separated by spaces —src/tup/parser.c:3946+iterates thewhole output list (
TAILQ_FOREACH(nle, &onl->entries, list)).putup emits only the first output. Verified at the line in all three independent expanders:
src/graph/dag.cpp:933—case 'o':appendsget_operand_path(cmd->outputs[0])src/index/entry.cpp:446—case 'o':resolvescmd.outputs[0]onlysrc/parser/eval.cpp:507— bare%oappends the singleflags.outputNotably the multi-output data is already present:
eval.cpp:449-450implements the numbered form(
%2o) againstflags.all_outputs. Bare%osimply does not consult it.Why it matters beyond the obvious
A rule with two or more outputs silently passes only the first to its command. For the common
one-output rule the behaviour is indistinguishable, which is why this has gone unnoticed.
It also has a testing consequence that reaches past this bug. Any test asserting that some output is
absent from
%opasses vacuously today, because%onever emits more thanoutputs[0]regardless. A pin of that shape looks like coverage and is not. A discriminating pin must use the
numbered form (
%2o) or assert the full expansion of a genuinely multi-output rule.Disposition
Upstream wins: putup's
%oshould expand to all outputs, matching both tup and putup's owndocumentation. The three expanders should agree by construction rather than by three separate
correct implementations — they are the same question asked in three places.
Found while inventorying output consumers for #370 (extra outputs); #370's discriminating test
depends on this being fixed or accounted for, so the two are ordered.