From 654c6e8c75be3dfe0e2cf64a44f84164e6e66a08 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Thu, 10 Sep 2026 07:10:44 +0000 Subject: [PATCH] fix(build): close capnp codegen missing-dependency-edge race (bug-1798) libxce_src listed only gen_srcs[0] (controller.capnp.c) as a source of the static_library('xce', ...) target, never controller.capnp.h -- which several library files (utils.c and others) reach only via #include. Ninja had no build edge forcing the capnp custom_target to finish before those files compiled, so a from-scratch build could schedule them first, intermittently failing with "controller.capnp.h: No such file or directory". This is the same symptom recorded across 5+ prior recurrences in buglog.json since 2026-07-16, previously worked around by retrying the build rather than root-caused. Fix: pass the whole gen_srcs custom_target (not gen_srcs[0]) into libxce_src -- Meson still compiles only its .c output, but now orders the entire target's build after both outputs are generated. Verified via two consecutive from-scratch `xce-build.sh lib debug` builds (no retry needed), the full lib test suite (313/313), and an incremental controller asan rebuild (1331/1331, 0 ASAN diagnostics) to confirm no regression. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_019jaMf9QEwpugH2W9KAuc7f --- .wolf/buglog.json | 24 ++++++++++++++++++++++++ src/lib/meson.build | 15 ++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/.wolf/buglog.json b/.wolf/buglog.json index 2f6bbb3d..be7af0de 100644 --- a/.wolf/buglog.json +++ b/.wolf/buglog.json @@ -21642,6 +21642,30 @@ ], "occurrences": 3, "last_seen": "2026-09-09T23:30:00Z" + }, + { + "id": "bug-1798", + "timestamp": "2026-09-10T06:57:00Z", + "error_message": "Fresh (from-scratch) build fails intermittently with `fatal error: controller.capnp.h: No such file or directory` for one or more consumers of utils.h (e.g. utils.c.o), reported live by the user running `./tools/xce-build.sh all debug`. This is the root-cause fix for a class of failures already recorded across many prior recurrences in this buglog (see related_bugs) as an unexplained/unfixed 'ninja-race' -- this entry identifies and fixes the actual missing-dependency-edge cause at the meson.build level.", + "file": "src/lib/meson.build", + "root_cause": "The capnp code-generation custom_target (`gen_srcs`, line 6) produces TWO outputs, controller.capnp.c and controller.capnp.h, but `libxce_src` (line ~65) only listed `gen_srcs[0]` (the .c output) as a source of the `static_library('xce', ...)` target. controller.capnp.h is never itself listed as a source anywhere -- it is reached only via `#include \"controller.capnp.h\"` from utils.c and several other library files (hook.c, threshold.c, xlist.c, window.c, xceapi.c, wrap.c, zarray.c, htab.c, xstring.c, etc., all also part of libxce_src). Because indexing a multi-output custom_target (`gen_srcs[0]`) extracts only that one output for Meson's dependency-graph purposes, Ninja has no declared build edge requiring the codegen custom_target to finish before compiling those other, unrelated .c files. On an INCREMENTAL build this is invisible (the generated header from a prior build already exists on disk), which is why this session's own extensive controller-only `--incremental` rebuilds throughout the xce-test-cli-ux spec never hit it -- it only manifests on a genuinely from-scratch build (`rm -rf` + fresh `meson setup`, which `xce-build.sh ` does by default without `--incremental`), and even then only when ninja's scheduler happens to pick an unlucky order under parallelism -- explaining the long history of this exact symptom being reported as an intermittent, unfixed 'ninja-race' across at least 5 prior recorded recurrences (2026-07-16 through 2026-07-26) with no meson.build-level fix ever applied, only the workaround 'retry the build once'.", + "fix": "FIXED (2026-09-10). Changed `libxce_src`'s first element from `gen_srcs[0]` to the unindexed `gen_srcs` (src/lib/meson.build). Passing the whole custom_target object (both outputs) to a target's `sources:` list is Meson's documented pattern for exactly this case: Meson still compiles only the recognized-source (.c) output, but now correctly orders the ENTIRE `static_library('xce', ...)` target's build (every compile job in it) after BOTH of the custom_target's outputs are generated -- closing the missing-dependency-edge gap with no other code or behavior change. (Tried first: adding a `depends: [gen_srcs]` kwarg directly to the `static_library()` call -- rejected immediately by this project's pinned Meson 1.9.1 with 'Got unknown keyword arguments \"depends\"', that kwarg is not supported on `static_library()`/`build_target()` in this version; reverted before landing on the working fix.) Verified: two consecutive from-scratch `src/tools/xce-build.sh lib debug` builds (the exact race scenario -- clean `rm -rf` + fresh `meson setup`, no `--incremental`) both succeeded on the first invocation with no retry; the full `lib` unit test suite (`xcelib_test`) passed 313/313 with 0 failures; a subsequent incremental `src/tools/xce-build.sh controller asan xce.build --incremental` rebuild (this session's own established validation command) also passed clean, 1331 Tests 0 Failures 0 Ignored, 9/9 meson suites OK, confirming no regression to the controller/xce_test build this session's own xce-test-cli-ux spec work depends on. A single or even double clean-build success does not, by the race's own probabilistic nature, provide 100% certainty the underlying scheduling race can never recur in some other unlucky ninja ordering -- but the fix addresses the actual, previously-undiagnosed structural root cause (a genuinely missing build-graph edge), not just a timing workaround, so it is expected to eliminate the race class entirely rather than merely reduce its likelihood. Does not address the separate, distinct apiserver<-libxce.a CGO-link race noted in one prior recurrence (2026-07-26) -- that is a different meson target (frontend/apiserver's go_build custom_target referencing @BUILD_ROOT@/lib/libxce.a via an env-var string, not this custom_target/static_library relationship) and remains unfixed.", + "tags": [ + "meson", + "build-system", + "capnp", + "controller.capnp.h", + "ninja-race", + "missing-dependency-edge", + "libxce", + "src/lib/meson.build", + "root-cause-fix" + ], + "related_bugs": [ + "bug-1439" + ], + "occurrences": 1, + "last_seen": "2026-09-10T06:57:00Z" } ] } diff --git a/src/lib/meson.build b/src/lib/meson.build index 971f5a73..9a65a16b 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -62,7 +62,20 @@ xce_select_parser = custom_target( ], build_by_default: true) -libxce_src = [ gen_srcs[0], +libxce_src = [ # bug-1798: pass the whole gen_srcs custom_target (both + # controller.capnp.c AND controller.capnp.h), not just + # gen_srcs[0] -- several files below (utils.c and others) + # reach controller.capnp.h only via #include, which ninja + # cannot see. Indexing to gen_srcs[0] dropped that header + # from this target's declared outputs entirely, so a + # from-scratch build had no dependency edge forcing capnp + # codegen to finish before those files compiled, + # intermittently failing with "controller.capnp.h: No such + # file or directory" (recurring since 2026-07-16). Passing + # the full custom_target compiles only its .c output (as + # before) while ordering the ENTIRE target's build after + # BOTH outputs are generated. + gen_srcs, section_parser[0], rusage_parser[0], xce_resreq_parser[0],