From 27a9788ac87d1fc325d5182ce1ec1284ca88f903 Mon Sep 17 00:00:00 2001 From: bong-water-water-bong Date: Tue, 10 Nov 2026 15:21:12 -0400 Subject: [PATCH] build: the Makefile generator refuses `--target 1bit` by name instead of silently doing nothing (engine#1993) `1bit` is the OUTPUT NAME of the target `onebit` (CMakeLists.txt:135). With CMake's default **Makefile** generator, the file `1bit` sitting in the build directory is a satisfied FILE rule with no dependencies, so cmake --build --target 1bit -> prints nothing, exits 0, builds nothing and a stale binary keeps its old timestamp. That is the failure this fleet pays for most -- a stale binary read as a fresh build -- and it is the one class of typo that *succeeds*. Measured on both generators before and after: generator command before after Unix Makefiles cmake --build . --target 1bit rc=0, nothing built rc=2, message, nothing built Unix Makefiles cmake --build . --target onebit rc=0, builds rc=0, builds (1bit produced) Ninja cmake --build . --target 1bit rc=0 (file target) rc=0 (unchanged, correct) Ninja cmake --build . --target onebit rc=0, builds rc=0, builds The Ninja row is why this is gated to the Makefile generator rather than applied to both: ninja has no file rules, so it resolves `1bit` to the output file and builds its producer and dependencies -- correct behaviour, verified by watching it rebuild ten targets from a stale binary and say "no work to do" once fresh. Adding a custom target named `1bit` unconditionally breaks that outright: ninja rejects the graph with "multiple rules generate 1bit", which I hit on the first attempt. Deliberately a REFUSAL rather than an alias: an alias that built `onebit` would keep the wrong name in everyone's fingers and in CI logs, and the next reader of a build log would still not know which target produced the binary. One line of friction removes the class. Ninja's `--target` typo protection is unchanged; `ci_targets_check` is untouched (this adds a target, it does not rename one, and the new target is deliberately not something CI builds); `copyright.py --check` rc 0. --- CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94ebb8def..26f05685b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,20 @@ file(WRITE ${CMAKE_BINARY_DIR}/generated/hrx_archs.h "static const char* const kHrxGgufArchs[] = {\n${_onebit_archs}};\n") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/registry/architectures.json) set_target_properties(onebit PROPERTIES OUTPUT_NAME 1bit) + +# engine#1993: `1bit` is the OUTPUT NAME, not a target -- the target is `onebit`. With the MAKEFILE generator (CMake's +# default) the output file `1bit` in the build directory makes `--target 1bit` a satisfied FILE rule, so the command prints +# nothing, exits 0 and builds nothing while a stale binary keeps its old timestamp: a typo that succeeds, which is the +# failure this fleet pays for most. Ninja errors on that command already (`unknown target '1bit'`), because it has no file +# rules -- so this refusal is added ONLY for the Makefile generator: a custom target with that name is a second rule for an +# output `onebit` already produces, which ninja rejects outright ("multiple rules generate 1bit") and Makefile absorbs as a +# phony. Deliberately not an alias: an alias that built `onebit` would keep the wrong name in everyone's fingers and logs. +if(CMAKE_GENERATOR MATCHES "Makefiles") + add_custom_target(1bit + COMMAND ${CMAKE_COMMAND} -E echo "error: the build target is onebit; 1bit is only its output file name. Nothing was built. Use: cmake --build --target onebit" + COMMAND ${CMAKE_COMMAND} -E false + VERBATIM) +endif() target_link_libraries(onebit PRIVATE nlohmann_json::nlohmann_json httplib::httplib Threads::Threads) # ── The chat-template renderer (engine #963, docs/npu-chat-template.md) ──────