From 3de5fb2fbc7731a533e01acde7f9ee0b3410251c Mon Sep 17 00:00:00 2001 From: Niclas Overby Date: Wed, 29 Jul 2026 05:02:50 +0200 Subject: [PATCH] fix(nix-ninja): module-scope genIncsOut -- stop generated -I dirs shadowing standard headers (#79) The first genIncsOut appended the $out-relative -I dir of EVERY generated header in the graph to every compile (478 -I flags on a migcom compile in the libSystem graph). That polluted the include search path: libsyscall/mach/string.h shadowed for migcom, whose transitive <_types.h> then failed. Scope it to the compiles OWN source module (moduleKey: src/external/) like the per-edge migHeaderIncsFor -- asl.c still gets its sibling aslcommon (same module src/external/syslog) for , but migcom no longer sees libsyscall dirs. Covers mig + non-mig generated headers via a fresh per-module map. Signed-off-by: Niclas Overby --- nix/lib/nix-ninja/build/lower.nix | 53 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/nix/lib/nix-ninja/build/lower.nix b/nix/lib/nix-ninja/build/lower.nix index ce2dc4e07..6b16df578 100644 --- a/nix/lib/nix-ninja/build/lower.nix +++ b/nix/lib/nix-ninja/build/lower.nix @@ -392,29 +392,31 @@ in { && lib.any isHeaderPath (edgeOutputs (elemAt edges i))) (indices edges))); - # GROUP variant of generatedHeaderIncs + migHeaderIncs: $out-relative -I flags for - # EVERY generated-header directory in the graph (mig and non-mig alike). In a group - # all generated headers are materialised in $out (internal topo order + external - # dependency groups staged via extGroupDrvs), so a compile that reaches a generated - # header through a cmake target-include -- not a literal -I and not a declared input - # (e.g. syslog's asl.c -> generated in aslcommon) -- can still resolve - # it. Unlike the per-edge `-I` form these carry NO derivation reference, - # so there is no edgeDrvs eval cycle and the whole set is safe on every compile - # unconditionally (no per-producer closure filter needed; clang ignores absent -I). - genIncsOut = lib.unique (concatMap ( - i: - concatMap ( - o: let + # GROUP variant of migHeaderIncs, MODULE-SCOPED. A group compile takes the + # $out-relative -I dirs of generated headers produced in ITS OWN source module only. + # Graph-wide would pollute the search path with unrelated generated dirs that shadow + # standard headers (libsyscall/mach/string.h shadowing for migcom). In a + # group these headers are materialised in $out (internal topo order + external groups + # staged via extGroupDrvs through cmake order-only deps), and -I$out carries NO + # derivation reference so there is no edgeDrvs eval cycle -- no per-producer closure + # filter needed. Covers mig AND non-mig generated headers (migHeadersByModule is + # mig-only), so build a fresh module map over every header-producing edge. moduleKey + # is defined further down; the recursive let makes the forward reference fine. + genHeaderDirsByModule = lib.foldl' ( + acc: i: + lib.foldl' ( + acc2: o: let rel = if underAnyRoot o then relUnder o else o; - dirs = lib.init (filter (x: x != "") (lib.splitString "/" rel)); - ancestors = lib.genList (n: builtins.concatStringsSep "/" (lib.take n dirs)) (length dirs + 1); - in - map (a: "-I$out" + lib.optionalString (a != "") "/${a}") ancestors - ) (filter isHeaderPath (edgeOutputs (elemAt edges i))) - ) - (filter - (i: !(isNoOp (elemAt edges i)) && lib.any isHeaderPath (edgeOutputs (elemAt edges i))) - (indices edges))); + mod = moduleKey rel; + in acc2 // { ${mod} = lib.unique ((acc2.${mod} or [ ]) ++ [ (builtins.dirOf rel) ]); } + ) acc (filter isHeaderPath (edgeOutputs (elemAt edges i))) + ) { } + (filter (i: !(isNoOp (elemAt edges i)) && lib.any isHeaderPath (edgeOutputs (elemAt edges i))) + (indices edges)); + genIncsOutFor = i: let + outs = edgeOutputs (elemAt edges i); + mod = if outs == [ ] then "" else moduleKey (let o = builtins.head outs; in if underAnyRoot o then relUnder o else o); + in map (d: "-I$out" + lib.optionalString (d != "" && d != ".") "/${d}") (genHeaderDirsByModule.${mod} or [ ]); # A generated mig header may be `#include <...>`d by a compile in the same # source module without the ninja graph declaring the dependency, and (unlike @@ -1221,10 +1223,11 @@ in { stripped = if rewriteRoots == [] then e.command else stripRoots e.command; withSubs = builtins.replaceStrings (map (s: s.from) subs) (map (s: s.to) subs) stripped; base = builtins.replaceStrings (map (s: s.from) toolPathSubs) (map (s: s.to) toolPathSubs) withSubs; - # Append the $out-relative generated-header -I dirs to compile commands so a + # Append this compile's OWN-MODULE $out-relative generated-header -I dirs so a # `` reached via a cmake target-include (not a literal -I / declared - # input) resolves from where topo/external staging materialised it in $out. - in base + lib.optionalString (isCompile e) (" " + lib.concatStringsSep " " genIncsOut); + # input) resolves from where topo/external staging materialised it in $out -- + # module-scoped so unrelated generated dirs cannot shadow standard headers. + in base + lib.optionalString (isCompile e) (" " + lib.concatStringsSep " " (genIncsOutFor i)); in '' # Subshell resetting to $out: edges run sequentially in one shell, and a # compile command that `cd`s into its WORKING_DIRECTORY (and does not -- 2.51.2