Skip to content

Glob exclusions and bin members still bypass the single path space, so !pattern is ignored out-of-tree #195

Description

@typeless

Summary

PR #193 put the glob input half of expand_glob_pattern into one path space, which fixed literal exclusions out-of-tree. Two siblings still feed build-prefixed strings into the same list that apply_exclusions filters in source-relative space, so an exclusion is silently ignored out-of-tree while working in-tree.

Both reproduced on fix/191-glob-path-space, and both behave identically on main — pre-existing, not regressions.

1. Exclusions that are themselves globs

apply_exclusions (src/graph/builder.cpp:736) expands a glob exclusion against the filesystem only, so it never sees generated files.

: keep.in |> cp %f %o |> keep.gen
: skip.in |> cp %f %o |> skip.gen
: *.gen !s*.gen |> echo %f > %o |> out.txt

out-of-tree:  echo build/keep.gen build/skip.gen > build/out.txt     <- ignored

!skip.gen (literal) is honoured after #193; !s*.gen — the obvious spelling — is not.

In-tree it is worse than ignored, it is unstable: the exclusion expands against what is on disk, so build 1 renders echo keep.gen skip.gen and build 2 renders echo keep.gen, because skip.gen only exists after the first build. The function's own doc comment says the match set must not be a function of filesystem state. (This is not #188 — that is parse-traversal dependence on the input side; this is execution-state dependence on the exclusion side.)

2. Bin {group} members

expand_inputs (src/graph/builder.cpp:910) pushes bin members via get_full_path, i.e. build-prefixed.

: x.in |> cp %f %o |> x.dat {bin}
: y.in |> cp %f %o |> y.dat {bin}
: {bin} !x.dat |> echo %f > %o |> out.txt

in-tree:      echo y.dat > out.txt                              <- honoured
out-of-tree:  echo build/x.dat build/y.dat > build/out.txt      <- ignored

Fix

Both are the same shape as #191 and take the same treatment:

  • bin members: push strip_path_prefix(path_sv, get_build_root_name(...)), exactly as the glob half now does. The BuildRoot-first resolution in resolve_input_node carries them back to the node.
  • glob exclusions: rather than re-expanding against the filesystem, match the exclusion pattern directly against the already-merged single-space match list — one Glob::matches per entry and no filesystem scan at all, which also removes the in-tree instability.

Provenance

Found by an adversarial review of PR #193; both reproduced independently before filing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TLob4Ef3qyMd3Dnp9q3XmA

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions