Skip to content

Refusing an output under a non-directory depends on rule order, because putup learns a path's type only when a rule names it #495

Description

@typeless

Symptom

Whether putup refuses an output under a non-directory depends on rule order. PR #494 refuses sub/x.o when a rule has already named the file sub. Put the output rule first, and the command runs and fails in the shell instead:

: |> echo hi > %o |> sub/x.o
: sub |> cat %f > %o |> a.txt

sub is a regular file in the source tree.

$ putup -B .
/bin/sh: 1: cannot create sub/x.o: Directory nonexistent
[.] Build completed: 1 commands (1 failed) in 1ms

The reverse case has the same cause. A path that already holds a Directory node can be claimed as an output, and the command then fails in the shell:

: |> echo b > %o |> gen/x
: |> echo a > %o |> gen
/bin/sh: 1: cannot create gen: Is a directory

Both were run against build/putup from the #494 branch.

Upstream

tup parse refuses both at parse time:

tup error: Unable to output to a different directory because 'sub' is a normal file
tup error: Failed to find directory ID for dir 'sub/x.o' relative to '.'
tup error: Error parsing Tupfile line 1
tup error: Attempting to insert 'gen' as a generated node when it already exists as a different type (generated directory). ...
tup error: Error parsing Tupfile line 2

The first refusal comes from find_dir_tupid_dt_pg; tup knows sub is a file from its filesystem scan, so rule order does not matter. The second comes from validate_output.

Cause

putup learns a source path's type only when a rule names it:

  • ensure_file_node (src/graph/dag.cpp) creates each missing intermediate component as NodeType::Directory without consulting the disk. So the output rule mints a Directory node for sub. The later input sub then finds that node through path_to_node and never retypes it.
  • ensure_file_node(…, NodeType::Generated) promotes an existing Ghost or File node to Generated, but returns an existing Directory node unchanged.

Fix direction

Both cases are type checks at the point where ensure_file_node binds an existing or new node:

  • An intermediate component that exists on disk as a non-directory should be refused.
  • A Generated request that lands on a Directory node should be refused, following validate_output.

REQ-OUTPUT-UNDER-DIRECTORY (spec/requirements/output-paths.ears.md, added in #494) records the first half of this gap in its reference.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions