Skip to content

build: generate header prerequisites with -MMD -MP (#1741) - #1758

Merged
JustVugg merged 12 commits into
JustVugg:devfrom
Kenneth-Javier:build/makefile-autodeps
Oct 1, 2026
Merged

JustVugg merged 12 commits into
JustVugg:devfrom
Kenneth-Javier:build/makefile-autodeps

Conversation

@Kenneth-Javier

@Kenneth-Javier Kenneth-Javier commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Implements #1741 as specified there: every engine, test, benchmark and object that gcc or clang compiles with $(CFLAGS) now takes its header prerequisites from the compiler, and those rules no longer list headers by hand. What still keeps a list, and why, is under point 3.

What changes

  • CFLAGS += -MMD -MP, after the platform blocks, so it is part of BUILD_CONFIG.
  • Header lists removed from 202 rules, 1786 header names in all: 9 engines, 162 tests, 10 benchmarks, the XDNA probe, 11 rules that build the segment library under build/segment/, 5 objects under build/ownership/, 3 backend objects and the ctypes library. colibri$(EXE): is now colibri.c $(CUDA_OBJ) $(METAL_OBJ) $(VK_OBJ) $(VK_SPV) $(XDNA_OBJ) .build-config.
  • Eight tests and the XDNA probe compiled a helper .c on their own command line; each helper is now its own object in tests/, compiled with the flags that test gave it. qwen36_tier.c likewise becomes qwen36_tier.o, and QWEN36_TIER_SRC is renamed QWEN36_TIER_OBJ.
  • The .d files are -included at the end of the Makefile, next to AUTODEP_BINS, the 239 targets they cover. The engines and every rule under tests/ are derived from one sed pass, the way TEST_RULES was, so a new one joins on its own; the objects come from the variables that already list them.
  • clean.py removes the .d files (c/, tests/, tools/), the new tests/*.o, and backend_vulkan.o and backend_xdna.o, which it never removed. It now removes build/segment/ as a whole, as it already did build/ownership/: it used to leave the segment objects behind, and one left without its .d is a built target whose headers nothing tracks. c/.gitignore ignores *.d.
  • test_makefile_deps.py is rewritten, see below; it reads rules spelled through variables ($(SEGMENT_BUILD_DIR)/glm.o:, $(RANSLIB):, the static pattern rule for the V4 units) by asking make for those variables. test_registry_engine_agreement.py follows the renamed tier variable, and test_glm53_metal_source.py no longer expects backend_metal.h in the glm53 rule.

Against the five points in #1741

1. Multi-source recipes. Confirmed: a command compiling two .c files writes a .d for the last one only. Of the four you named, test_segment_runtime, test_segment_conformance and test_edge_runtime now link their helpers as objects; dsv4_decode_loader_test is built by the dsv4-cuda-loader-test run target, which rebuilds every time (point 3), so it keeps its list untouched. Asking make for every command it would run found five more of the $< other.c shape (test_xdna_qt_state, test_xdna_failure, test_qwen38_tokenizer, test_qwen38_native_weights, test_native_quant), handled the same way, and one in a tool of mine from #1261: xdna_physical_probe compiled backend_xdna.c beside its own source and now links tests/backend_xdna_lane.o, which has exactly its flags. One more only appears with CUDA, CUDA_DLL, HIP or HIP_DLL: qwen36_tier.c on the command line of qwen36, qwen38, eleven tests and a benchmark. The helper objects:

tests/backend_xdna_lane.o                $(CFLAGS) -DCOLI_XDNA
tests/edge_runtime_nocuda.o              $(NOCUDA_CFLAGS)
tests/segment_runtime_nocuda.o           $(NOCUDA_CFLAGS)
tests/segment_runtime.o                  $(CFLAGS)
tests/segment_conformance_fixtures.o     $(CFLAGS)
tests/deepseek_v4_native_quant.o         $(CFLAGS) -DCOLI_V4_UNIT_NATIVE_QUANT
tests/edge_runtime.o                     $(CFLAGS)

A test asks make for the command of every rule in AUTODEP_BINS and fails on any that compiles more than one unit or lacks -MMD. It does so in the default build and again in every build flavour the host accepts, because a header under #if reaches a .d only through the compile that reads it: here CUDA_DLL, HIP_DLL, VK and XDNA on Windows, CUDA, HIP (with a fixed HIP_ARCH), VK and XDNA on Linux, and METAL on macOS.

2. -MMD -MP in CFLAGS. Done. One thing the forced rebuild does not reach, though: only 6 of those 202 rules depend on .build-config (colibri, deepseek_v41, qwen36 and three objects). The other 196, six of the nine engines among them, would stay "up to date" after this change with no .d, and then miss every header edit. So each target also depends on its own .d through an empty rule: a missing .d makes the target out of date, and the rebuild writes it. Measured by building on dev, then checking this branch out over the same tree:

                                                  Linux    Windows
make -q colibri, glm53, test_qwen36_ctx           1 1 1    1 1 1      1 = rebuild
  the same without the per-target .d rule         - 0 0    - 0 0      glm53 and the test would stay stale
after that one rebuild                            0 0 0    0 0 0      3 .d files written
make -q -W st.h colibri, glm53                    1 1      1 1
make -q -W uring.h colibri                        1        0          uring.h is read on Linux only

3. First slice. gcc and clang only. The nvcc recipes, the MSVC-hosted CUDA build, Metal and Makefile.deepseek-v4.units keep their lists, as agreed; nvcc and hipcc receive NVCCFLAGS and HIPCCFLAGS, never CFLAGS, so nothing there writes a .d. Also keeping a list, each named in the Makefile:

  • AUTODEP_ELSEWHERE: six gcc tests with an explicit list, the other option point 1 allows. test_deepseek_v4 and test_v4_serve_framing are built by Makefile.deepseek-v4, and the four segment/edge adapter tests compile three units in one command.
  • AUTODEP_OWN_FLAGS: three rules under tests/ that do not compile with $(CFLAGS), so they write no .d: bench_cuda_resident_batch (the GPU compiler) and two benchmarks pinned to their own flags, whose sources include no local header.
  • fuzz-rans, dsv4-cuda-test and dsv4-cuda-loader-test are run targets whose name is not a file, so they rebuild every time and a list changes nothing there.

A new rule under tests/ joins the generated set on its own; one that compiles more than one unit, or without $(CFLAGS), fails the one-unit test until it is listed above.

4. macOS. Shown by this PR's CI with Apple clang and the make Xcode ships, with one thing the log does not print; see CI on this PR.

5. clean.py and test_makefile_deps.py. Rule names are unchanged. test_glm53_metal_source.py asserted backend_metal.h inside the glm53 rule; that line now documents that the header comes from glm53.d.

What the hand lists were missing

Method. In fresh trees on both platforms, the tree was built with this branch, so every target in AUTODEP_BINS that was built has a .d naming the headers its compile actually read: 224 targets on Linux, 226 on Windows. Besides make check and every engine, the benchmarks, the XDNA probe and the segment and ownership objects were built too: 52 of 53 on Windows, where bench_idot stops at its own #error unless built with ARCH=native, and 52 of 53 on Linux, where the XDNA probe compiles but links on Windows only. Both are the same on dev, and both compiles still wrote their .d, so both are compared. The seven new helper objects have no rule on dev to compare with. Not built in that configuration, so not compared: on Linux test_xdna_execution, test_xdna_failure, test_qwen38_tier_engine, test_e8x4g64_loader, backend_vulkan.o, backend_loader.o, backend_xdna.o, qwen36_tier.o; on Windows test_qwen38_tier_engine, test_uring, test_e8x4g64_loader, backend_vulkan.o, backend_xdna.o, qwen36_tier.o. Each .d was compared with that target's prerequisite list in dev's Makefile at 499757d: headers only, paths normalised so tests/../st.h and st.h count as the same file.

Result.

targets compared read headers their list on dev omits engines tests benchmarks segment / ownership objects missing entries
Linux 224 149 6 of 9 96 9 32 / 5 1290
Windows 226 149 6 of 9 96 9 32 / 5 1291

The most frequent, identical on both: decode_batch.h (102), pin_pool.h (94), cli_args.h (91), tok_unicode_o200k.h (82), omp_tune.h (77) targets. The platforms differ only where the builds do: test_uring is built on Linux only and test_xdna_failure is built on Windows only, and uring.h is read on Linux only. The previous test_makefile_deps.py already named some of these in a docstring and left them for a separate fix. Two of them are still there at 499757d (tok_unicode_o200k.h in colibri and deepseek_v41, decode_batch.h in five engines); the other two (sse41_kernels.h, edge_adapter_internal.h) have since been added by hand.

One gap is outside both default runs and is mine: backend_xdna.o, built only with XDNA=1, includes compat.h unconditionally. Its rule from #1261 on dev is backend_xdna.o: backend_xdna.c backend_xdna.h .build-config; its .d from the XDNA=1 build on Windows is backend_xdna.o: backend_xdna.c backend_xdna.h compat.h.

What that means in practice, shown on dev itself with the exit status of make -q (0: "up to date", nothing rebuilt; 1: rebuild needed), the same on both platforms:

# dev at 499757d, both targets built                        Linux  Windows
touch serve_poll.h        ; make -q tests/test_qwen36_ctx     0      0
touch tok_unicode_o200k.h ; make -q colibri                   0      0
# the same two steps on this branch                           1/1    1/1

In the other build flavours. The same comparison after building everything this host builds with each flavour, against a baseline built the same way in the default configuration (231 targets compared on Windows, 153 with gaps; 228 and 152 on Linux). The last two columns are what the flavour adds: targets whose list on dev misses a header only that flavour reads.

flavour platform targets compared with gaps with gaps only this flavour has headers
XDNA=1 Windows 231 153 48 backend_xdna.h (48)
CUDA_DLL=1 Windows 231 153 55 backend_cuda.h (51), backend_cuda_ink.h (4)
HIP_DLL=1 Windows 231 153 55 backend_cuda.h (51), backend_cuda_ink.h (4)
VK=1 Windows 231 153 57 backend_vulkan.h (57)
CUDA=1 Linux 228 152 54 backend_cuda.h (50), backend_cuda_ink.h (4)
HIP=1 Linux, ROCm container; colibri and qwen36 only 2 1 0 -

In each GPU or NPU flavour, about fifty targets read that backend's own header without their list on dev naming it, so editing it rebuilds nothing there. The flavour adds entries to targets that already had gaps, which is why the count with gaps does not grow.

Limits of this measurement. Linux and Windows, the default configuration and the flavours above; not macOS, so not METAL=1. A missing entry means make will not rebuild after that header changes; whether the stale binary then behaves differently depends on the edit.

All 150 targets, with the headers each reads but does not list on dev
colibri$(EXE)                                tok_unicode_o200k.h
deepseek_v41$(EXE)                           decode_batch.h tok_unicode_o200k.h
inkling$(EXE)                                decode_batch.h
kimi_k3$(EXE)                                decode_batch.h
olmoe$(EXE)                                  decode_batch.h
qwen38$(EXE)                                 decode_batch.h
build/ownership/COLI_V4_UNIT_CONFIG.o        compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h st.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/ownership/COLI_V4_UNIT_NATIVE_QUANT.o  native_quant.h tensor.h
build/ownership/COLI_V4_UNIT_RUNTIME.o       compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h st.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/ownership/COLI_V4_UNIT_ST.o            compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/ownership/expert_store_registry.o      deepseek_v4.h tensor.h
build/segment/COLI_V4_UNIT_ATTENTION.o       compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_ATTENTION_BATCH.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_ATTENTION_TRANSACTION.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_BLOCK_HYBRID.o    compat.h deepseek_v4_hybrid.h expert_store.h expert_store_registry.h hybrid_split.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_COMPRESSOR.o      compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_COMPRESSOR_SNAPSHOT.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_CONFIG.o          compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_EXPERT_ROWS16.o   compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_EXPERT_STORE_AUTO.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_EXPERT_STORE_HOT_ROWS16.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h route_trace.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_HEAD_CACHE.o      compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_INDEXER.o         compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_INDEXER_SNAPSHOT.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_LAYER_RESIDENT.o  compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_MATH.o            compat.h expert_store.h expert_store_registry.h hyper_connections.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_NATIVE_QUANT.o    fp8_format.h idot.h native_quant.h quant.h tensor.h
build/segment/COLI_V4_UNIT_NATIVE_QUANT_BATCH.o fp8_format.h idot.h native_quant.h native_quant_batch.h quant.h tensor.h
build/segment/COLI_V4_UNIT_NATIVE_QUANT_DUAL.o fp8_format.h idot.h native_quant.h native_quant_dual.h quant.h tensor.h
build/segment/COLI_V4_UNIT_NATIVE_QUANT_ROWS16.o native_quant.h native_quant_fp4_rows16.h tensor.h
build/segment/COLI_V4_UNIT_RESOURCE_PLAN.o   compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_RUNTIME.o         compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_SEGMENT_ADAPTER.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_SPARSE_ATTENTION.o compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/COLI_V4_UNIT_ST.o              compat.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/expert_store_registry.o        deepseek_v4.h tensor.h
build/segment/glm.o                          abl.h cli_args.h compat.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h grammar.h idot.h json.h kv_fp8.h kv_persist.h kv_tq.h omp_tune.h pin_pool.h quant.h route_trace.h sample.h schema_gbnf.h telemetry.h tier.h tok.h tok_unicode.h tok_unicode_o200k.h uring.h (Linux)
build/segment/glm53.o                        cli_args.h compat.h decode_batch.h idot.h json.h omp_tune.h pin_pool.h route_trace.h serve_poll.h stop_ids.h tok_unicode.h tok_unicode_o200k.h
build/segment/inkling.o                      cli_args.h compat.h decode_batch.h json.h kv_prefix.h omp_tune.h pin_pool.h route_trace.h serve_budget.h serve_codec.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/kimi.o                         cli_args.h compat.h decode_batch.h fp8_format.h hybrid_split.h idot.h json.h kv_prefix.h omp_tune.h pin_pool.h quant.h route_trace.h serve_budget.h serve_codec.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/olmoe.o                        cli_args.h compat.h decode_batch.h fused_simd.h json.h kv_prefix.h omp_tune.h pin_pool.h route_trace.h sample.h serve_budget.h serve_codec.h tok.h tok_unicode.h tok_unicode_o200k.h
build/segment/qwen36.o                       cli_args.h compat.h decode_batch.h expert_ffn.h idot.h json.h kv_prefix.h omp_tune.h pin_pool.h qwen36_tier.h serve_poll.h
build/segment/qwen38.o                       cli_args.h decode_batch.h idot.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h serve_codec.h
tests/bench_dsa_select$(EXE)                 abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/bench_gemv_stream$(EXE)                abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/bench_i4p_gidot$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/bench_idot$(EXE)                       abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/bench_inkling_shared_batch$(EXE)       cli_args.h decode_batch.h pin_pool.h serve_budget.h
tests/bench_mla_simd$(EXE)                   abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/bench_qwen36_dense_batch$(EXE)         cli_args.h decode_batch.h expert_ffn.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/bench_router_select$(EXE)              abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/bench_topp$(EXE)                       abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_ablate_mode$(EXE)                 cli_args.h decode_batch.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_cap_mixed_width$(EXE)             abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_cap_precedence$(EXE)              abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_cfg_topk$(EXE)                    abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_cluster_protocol$(EXE)            abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_corpus_draft$(EXE)                abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_dsa_select$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_dsv41_serve_budget$(EXE)          decode_batch.h tok_unicode_o200k.h
tests/test_eslot_inflight$(EXE)              abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_fp8_load$(EXE)                    abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_glm_cache_index$(EXE)             abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_grammar_cache$(EXE)               abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_i4_grouped$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_i4_grouped_sse41$(EXE)            abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_i4_grouped_sse41_no_contract$(EXE) abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_i4_grouped_sse41_o1$(EXE)         abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_idot$(EXE)                        abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_inkling_cache_index$(EXE)         cli_args.h decode_batch.h pin_pool.h serve_budget.h
tests/test_inkling_serve_framing$(EXE)       cli_args.h decode_batch.h pin_pool.h
tests/test_inkling_shared_batch$(EXE)        cli_args.h decode_batch.h pin_pool.h serve_budget.h
tests/test_int3$(EXE)                        abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_int3_load$(EXE)                   abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_k3_chat_tools$(EXE)               cli_args.h decode_batch.h hybrid_split.h pin_pool.h serve_budget.h
tests/test_k3_mmap$(EXE)                     cli_args.h decode_batch.h hybrid_split.h json.h pin_pool.h serve_budget.h tok_unicode.h tok_unicode_o200k.h
tests/test_k3_ram_budget$(EXE)               cli_args.h decode_batch.h hybrid_split.h json.h pin_pool.h serve_budget.h tok_unicode.h tok_unicode_o200k.h
tests/test_kimi_cache_index$(EXE)            cli_args.h decode_batch.h hybrid_split.h pin_pool.h serve_budget.h
tests/test_kimi_cuda_expert$(EXE)            cli_args.h decode_batch.h hybrid_split.h pin_pool.h serve_budget.h
tests/test_kimi_request_state$(EXE)          cli_args.h decode_batch.h hybrid_split.h pin_pool.h serve_budget.h
tests/test_kimi_serve_framing$(EXE)          cli_args.h decode_batch.h hybrid_split.h pin_pool.h
tests/test_kv_alloc$(EXE)                    abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h uring.h (Linux)
tests/test_kv_disk$(EXE)                     abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h uring.h (Linux)
tests/test_kvb_notice$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_logit_nan$(EXE)                   abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h idot.h kv_fp8.h kv_persist.h kv_tq.h omp_tune.h pin_pool.h quant.h route_trace.h sample.h schema_gbnf.h telemetry.h tok_unicode_o200k.h
tests/test_logprob_status$(EXE)              abl.h cli_args.h decode_batch.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_mirror_stripe_split$(EXE)         abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_moe_gs_guard$(EXE)                abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_olmoe_cache_index$(EXE)           cli_args.h decode_batch.h fused_simd.h kv_prefix.h pin_pool.h serve_budget.h
tests/test_olmoe_dot_i8_16$(EXE)             cli_args.h decode_batch.h fused_simd.h kv_prefix.h pin_pool.h serve_budget.h
tests/test_olmoe_dot_i8_16_sse41$(EXE)       cli_args.h decode_batch.h kv_prefix.h pin_pool.h serve_budget.h
tests/test_olmoe_matmul_q$(EXE)              cli_args.h decode_batch.h fused_simd.h kv_prefix.h pin_pool.h serve_budget.h
tests/test_olmoe_serve_framing$(EXE)         cli_args.h decode_batch.h fused_simd.h kv_prefix.h pin_pool.h
tests/test_pilot_ring$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_pipe_block$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h idot.h kv_fp8.h kv_persist.h kv_tq.h omp_tune.h pin_pool.h quant.h route_trace.h sample.h schema_gbnf.h telemetry.h tok_unicode_o200k.h
tests/test_qt_addrow$(EXE)                   abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_qwen36_cache_index$(EXE)          cli_args.h decode_batch.h idot.h kv_prefix.h pin_pool.h serve_poll.h
tests/test_qwen36_cache_route$(EXE)          cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_ctx$(EXE)                  cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_dense_batch$(EXE)          cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_dense_idot$(EXE)           cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_dnproj_batch$(EXE)         cli_args.h decode_batch.h expert_ffn.h idot.h json.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h st.h tier.h
tests/test_qwen36_encode_oom$(EXE)           cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_json_escape$(EXE)          cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_slot_int8$(EXE)            cli_args.h decode_batch.h expert_ffn.h gsgemv.h idot.h kv_prefix.h omp_tune.h pin_pool.h qgemv.h serve_poll.h sse41_kernels.h
tests/test_qwen36_tier_autoplace$(EXE)       tier.h
tests/test_qwen36_tier_dense$(EXE)           compat.h tier.h
tests/test_qwen36_tier_fill_wait$(EXE)       tier.h
tests/test_qwen36_tier_fp8$(EXE)             tier.h
tests/test_qwen36_tier_init_failure$(EXE)    tier.h
tests/test_qwen36_tier_int8$(EXE)            tier.h
tests/test_qwen36_tier_int8_decode$(EXE)     decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h tier.h
tests/test_qwen36_tier_int8_engine$(EXE)     decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h tier.h
tests/test_qwen36_tier_invariants$(EXE)      tier.h
tests/test_qwen36_tier_multidev$(EXE)        tier.h
tests/test_qwen36_tier_release$(EXE)         tier.h
tests/test_qwen36_tier_rollback$(EXE)        tier.h
tests/test_qwen36_tier_scale_budget$(EXE)    tier.h
tests/test_qwen36_tier_shutdown$(EXE)        tier.h
tests/test_qwen36_tier_take_error$(EXE)      tier.h
tests/test_qwen36_tier_withdraw$(EXE)        compat.h tier.h
tests/test_qwen36_tok_merges$(EXE)           cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_tok_truncated$(EXE)        cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_tokenizer$(EXE)            cli_args.h decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h
tests/test_qwen36_trunk_dense$(EXE)          decode_batch.h idot.h kv_prefix.h omp_tune.h pin_pool.h serve_poll.h tier.h
tests/test_qwen38_config$(EXE)               cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h serve_codec.h
tests/test_qwen38_idot$(EXE)                 cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h serve_codec.h
tests/test_qwen38_metrics$(EXE)              cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h
tests/test_qwen38_native_weights$(EXE)       cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h
tests/test_qwen38_prefix$(EXE)               cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h
tests/test_qwen38_serve_framing$(EXE)        cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h
tests/test_qwen38_tokenizer$(EXE)            cli_args.h decode_batch.h omp_tune.h pin_pool.h qwen36_tier.h qwen38_vision.h serve_codec.h
tests/test_ram_clamp$(EXE)                   abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_rope_invfreq$(EXE)                abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_router_nan$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h idot.h kv_fp8.h kv_persist.h kv_tq.h omp_tune.h pin_pool.h quant.h route_trace.h sample.h schema_gbnf.h telemetry.h tok_unicode_o200k.h
tests/test_sample_nan$(EXE)                  abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_serve_codec$(EXE)                 decode_batch.h
tests/test_serve_sentinel$(EXE)              decode_batch.h
tests/test_shard_kvb_refuse$(EXE)            abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h idot.h kv_fp8.h kv_tq.h omp_tune.h oracle.h pin_pool.h route_trace.h schema_gbnf.h sse41_kernels.h tok_unicode_o200k.h
tests/test_spec_decode_state$(EXE)           abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_ssd_probe$(EXE)                   abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h route_trace.h schema_gbnf.h tok_unicode_o200k.h
tests/test_stops$(EXE)                       abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_temp_env$(EXE)                    abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_topp$(EXE)                        abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h
tests/test_uring$(EXE)                       abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h kv_fp8.h kv_tq.h omp_tune.h pin_pool.h schema_gbnf.h tok_unicode_o200k.h  [Linux only]
tests/test_v4_ownership$(EXE)                compat.h deepseek_v4.h deepseek_v4_internal.h expert_store.h expert_store_registry.h json.h kv_prefix.h native_quant.h native_quant_batch.h native_quant_dual.h native_quant_fp4_rows16.h st.h tensor.h tok.h tok_unicode.h tok_unicode_o200k.h
tests/test_xdna_failure$(EXE)                abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h idot.h kv_fp8.h kv_tq.h omp_tune.h oracle.h pin_pool.h schema_gbnf.h sse41_kernels.h tok_unicode_o200k.h  [Windows only]
tests/test_xdna_qt_state$(EXE)               abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h idot.h kv_fp8.h kv_tq.h omp_tune.h oracle.h pin_pool.h schema_gbnf.h sse41_kernels.h tok_unicode_o200k.h
tests/test_xdna_registry$(EXE)               compat.h
tests/xdna_physical_probe$(EXE)              abl.h cli_args.h decode_batch.h evidence_digest.h exact_dot.h fp8_format.h idot.h kv_fp8.h kv_tq.h omp_tune.h oracle.h pin_pool.h schema_gbnf.h sse41_kernels.h tok_unicode_o200k.h
The comparison script

Run on a tree built with this branch, with dev's Makefile saved alongside: python3 gaps.py c/ dev.Makefile. It asks make for AUTODEP_BINS, so it compares exactly the targets this PR moves to generated dependencies, and for the variables rule names are spelled with.

"""Headers each built target really reads (its .d) that dev's hand list omits.

Usage: python3 gaps.py <c-dir of a tree built with this branch> <dev's c/Makefile>
"""
import os
import re
import subprocess
import sys

C, ORIG = sys.argv[1:3]
RULE = re.compile(r"(?m)^([^\s#:=][^:=\n]*?):(?![=:])(.*)$")
VAR = re.compile(r"\$\(([A-Za-z0-9_]+)\)")


def make_vars(names):
    """Variables as the branch's make expands them (target-side names such as
    $(SEGMENT_BUILD_DIR) or $(RANSLIB) are spelled the same on dev)."""
    proc = subprocess.run(["make", "-s", "--no-print-directory", "-f", "Makefile", "-f", "-"]
                          + ["print-" + n for n in names], cwd=C,
                          input="print-%: ; @echo $*=$($*)\n",
                          capture_output=True, text=True, check=True)
    out = {}
    for line in proc.stdout.splitlines():
        k, _, v = line.partition("=")
        out[k] = v.split()
    return out


text = open(ORIG, encoding="utf-8").read().replace("\r", "")
lines = [(t, r) for t, r in RULE.findall(re.sub(r"\\\n[ \t]*", " ", text))
         if not re.search(r"\$\([a-z-]+ ", t)]
values = make_vars(sorted({v for t, _ in lines for v in VAR.findall(t)} | {"AUTODEP_BINS", "EXE", "RANSLIB"}))
exe = (values.get("EXE") or [""])[0]
rules = {}
for target, rest in lines:
    pre = rest.split("#", 1)[0]
    pattern, sep, after = pre.partition(":")
    if sep and "%" in pattern:  # static pattern rule: objs: dir/%.o: prereqs
        pre = after
    for name in VAR.sub(lambda m: " ".join(values.get(m.group(1), [])), target).split():
        rules.setdefault(name, set()).update(pre.split())


def form(target):
    """One spelling for both platforms: colibri$(EXE), $(RANSLIB), build/segment/glm.o."""
    if target in values.get("RANSLIB", []):
        return "$(RANSLIB)"
    if target.endswith(".o"):
        return target
    return (target[:-len(exe)] if exe and target.endswith(exe) else target) + "$(EXE)"


# Only targets whose headers now come from -MMD.
bins = set(values["AUTODEP_BINS"])
gaps, checked, new = [], 0, []
for sub in ("", "tests", "tools", os.path.join("build", "segment"), os.path.join("build", "ownership")):
    root = os.path.join(C, sub)
    if not os.path.isdir(root):
        continue
    for f in sorted(os.listdir(root)):
        if not f.endswith(".d"):
            continue
        body = re.sub(r"\\\r?\n", " ", open(os.path.join(root, f), encoding="utf-8", errors="replace").read())
        target, _, deps = body.split("\n", 1)[0].partition(": ")
        if target not in bins:
            continue  # not generated: its .d names the last unit only, or none
        if target not in rules:
            new.append(form(target))  # a new rule: nothing on dev to compare with
            continue
        checked += 1
        listed = {os.path.normpath(w) for w in rules[target]}
        read = {os.path.normpath(d) for d in deps.split() if d.endswith(".h")}
        missing = sorted(read - listed)
        if missing:
            gaps.append((form(target), missing))

print("targets compared: %d, with headers read but not listed on dev: %d" % (checked, len(gaps)))
print("new on this branch, not compared: %s" % (" ".join(sorted(new)) or "-"))
for name, missing in sorted(gaps):
    print("  %-44s %s" % (name, " ".join(missing)))

The new test_makefile_deps.py

test checks
engine list is derived and not empty as before, cross-checked against the family registry
every engine gets generated deps each engine is in AUTODEP_BINS; the Makefile's derivation matches the test's
no generated rule lists headers by hand keeps the lists from creeping back; rules spelled through variables are resolved by asking make
one unit with -MMD, default build point 1
one unit with -MMD, every build flavour point 1 under #if: the flavours this host accepts
the checks leave .build-config alone parsing another flavour rewrites it; the suite must not
every built target has a .d covering its includes points 2 and 5: every unconditional #include "..." of its source is in its .d
make rebuilds a target when its header changes make -q -W <header>, spelled as the .d spells it

Each check was seen to fail before being trusted: the change on the left, made on its own, turns the test on the right red, on both platforms.

st.h listed by hand in colibri's rule                  no generated rule lists headers by hand
st.h listed in $(SEGMENT_BUILD_DIR)/glm.o's rule       no generated rule lists headers by hand
backend_xdna.c back on the probe's command line        one unit with -MMD, default build
segment_runtime.c back on a test's command line        one unit with -MMD, default build
qwen36_tier.c back on qwen36's command line            one unit with -MMD, every build flavour
backend_xdna.c on colibri's line, XDNA=1 only          one unit with -MMD, every build flavour
test_v4_serve_framing out of AUTODEP_ELSEWHERE         one unit with -MMD, default build
the -include of the .d files removed                   make rebuilds a target when its header changes
the test's .build-config guard removed                 the checks leave .build-config alone

Testing

Windows  gcc.exe (Rev5, Built by MSYS2 project) 16.1.0 | GNU Make 4.4.1
         make check                          Ran 1572 tests OK (skipped=192)
Linux    gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 | GNU Make 4.3
         make check                          Ran 1482 tests OK (skipped=149)
both     make colibri XDNA=1, then make check
         Linux                               Ran 1482 tests OK (skipped=149)
         Windows                             Ran 1572 tests OK (skipped=192)

The CI jobs that build differently from make check, run locally the way the workflow runs them:

sanitizers           make test-asan; make fuzz-rans                     exit 0, exit 0
qwen36-tiny-check    make qwen36; make clean; make qwen36 EXTRA_CFLAGS=<asan>
                     ASan symbols before/files left by clean/after: 0/0/31
qwen38-tiny-check    make -B qwen38 EXTRA_CFLAGS=<asan>                 exit/ASan symbols: 0/31
python               every engine built, then make test-python          Ran 1482 tests OK (skipped=145)
engines (windows)    every engine with XDNA=1, then the binary check    exit 0; OK imports=12
windows-cuda-build   colibri, qwen36, kimi_k3 with CUDA_DLL=1 (gcc host side): exit 0; qwen36_tier.o built
HIP_DLL=1            qwen36 with qwen36_tier.o: exit 0; -W st.h and -W qwen36_tier.h: 1 1

Windows HIP, built and run for real. No hosted runner has a Windows HIP toolchain or an AMD GPU, so no CI job covers this path (docs/windows.md says so); it was run here, on the machine #788 was validated on, following docs/windows.md: TheRock HIP 7.14.60850-2b22ab01, MSVC 14.44.35207 pinned, HIP_ARCH=gfx1151, in a fresh worktree of this branch.

make hip-dll HIP_DLL=1 ...          exit 0; coli_hip.dll, 1 gfx1151 code object; .d files from hipcc: 0
make colibri.exe HIP_DLL=1 ...      exit 0; colibri.d and backend_loader.d written
make -q after the build             0
make -q -W backend_cuda.h backend_loader.o, -W st.h colibri.exe: 1, 1

glm_tiny teacher-forcing oracle, the model/oracle pair #788 was validated with
  CPU                               30/32
  GPU, twice                        30/32, 30/32; 46 dense tensors resident on AMD Radeon(TM) 8060S Graphics; [HIP] refusals: 0
  dev 499757d, built the same way  CPU 30/32, GPU 30/32, 30/32; the same positions (5, 25) on both
tests/test_glm_oracle.py            Ran 17 tests OK

The two mismatches are in dev as well, on the CPU and on the GPU alike, so they do not come from this change; test_glm_oracle.py allows two floating-point near ties in teacher forcing.

Every number in this description comes from two scripted runs in fresh trees of this branch at fbeef6a on dev 499757d, one per platform (the Windows one includes the HIP build), the same HIP script run once on dev for comparison, the Windows VK=1, Linux CUDA=1 and Linux HIP=1 builds, the every-header and per-flavour runs after CI, and reading the two Makefiles straight from git; the real-hardware HIP build is huppiflupp's. I can attach the scripts if that helps.

CI on this PR

All 29 checks passed on the merge of fbeef6a into dev 4e28e39. The ones that close what this description listed as missing when it was opened:

macOS (colibri + V4 platform gate)   macos-26-arm64 (image 20260907.0351.1), Apple clang, make -C c check:
                                     205 commands with -MMD -MP; Ran 1484 tests OK (skipped=156)
All engines (macos)                  every engine; then colibri, inkling, kimi_k3 with METAL=1, compiled with
                                     -DCOLI_METAL -MMD -MP, one unit per command
Vulkan (Lavapipe, software)          make colibri VK=1, then the backend initialised against Lavapipe
CUDA build (Windows, MSVC host)      make cuda-dll, then colibri, qwen36 and kimi_k3 with CUDA_DLL=1
HIP syntax check                     make gpu-compile HIP=1 HIP_ARCH=gfx1100 (the hipcc side)
musl libc (Alpine), Container image, ARM (engines + NEON kernel exactness + tiny oracle):
                                     gcc builds through the same rules

On macOS $(MAKE) is /Applications/Xcode_26.6.app/Contents/Developer/usr/bin/make (check job, engines job). The Python suite runs quietly there, so the log does not name test_makefile_deps, but its wiring and coverage tests cannot have been skipped: they skip only when make is missing or nothing in AUTODEP_BINS is built, and a failure to read the Makefile through -f - is an error, not a skip. So under that make the new Makefile lines and the -f - read work, and every target the job built had a .d covering its source's includes, which is the question whether Apple clang writes <output>.d for a one-command compile and link. backend_metal.o is built by clang++ without $(CFLAGS) and keeps its list, as planned.

Built here after CI

Three build flavours no CI job builds or links through this Makefile, run on the branch at fbeef6a after the checks above:

Windows VK=1    MSYS2 vulkan-headers/loader 1.4.357.0, glslc 2026.3, gcc 16.1
                -W backend_vulkan.h: 1 for colibri.exe, 1 for backend_vulkan.o; -W st.h: 1; make -q after the build: 0
                the CI's init probe, against the Radeon 8060S driver instead of Lavapipe:
                [VK] ready: AMD Radeon(TM) 8060S Graphics, compute qfam 0, memtype 2, fused gate+up, absorb attention, expert tier active; the same [VK] lines as dev built the same way
Linux CUDA=1    nvcc 13.4, cuBLAS 13.8.0.4, gcc 13.3, CUDA_ARCH=portable; colibri and qwen36 build and link, as on dev
                nvcc writes no .d; colibri.d names backend_cuda.h; -W backend_cuda.h: 1; -W st.h: 1
                -W qwen36_tier.h: 1 for qwen36, 1 for qwen36_tier.o; make -q after the build: 0; test_makefile_deps: OK
                no NVIDIA GPU here, so the binary ran its CPU path: glm_tiny oracle 30/32, as dev's CUDA=1 build and the default build
Linux HIP=1     rocm/dev-ubuntu-24.04:7.2.4 (HIP 7.2.53211-97f5574fe2), HIP_ARCH=gfx1151: colibri and qwen36 build and link, as on dev
                gcc compiles colibri.c, qwen36.c and qwen36_tier.c with -MMD; hipcc writes no .d; colibri.d names backend_cuda.h,
                qwen36.d names qwen36_tier.h; -W qwen36_tier.h qwen36_tier.o: 1; test_makefile_deps: OK

The HIP CI job's comment says a container cannot link the engine; with the 7.2.4 image it does, on dev as well.

On real hardware. @huppiflupp built it on a Strix Halo under Linux (comment): Nobara 44, gfx1151, Fedora's ROCm 7.1 (hipcc 7.1.52802, rocwmma-devel 7.1.0), make colibri HIP=1 ROCM_HOME=/usr HIP_ARCH=gfx1151 at fbeef6a. It builds and links libamdhip64.so.7, colibri.d names backend_cuda.h, make -q -W backend_cuda.h colibri … answers 1, and test_makefile_deps is OK. Thank you.

Every target, every header. make -q -W <header> <target> for every built, up-to-date target and every header its .d names:

Windows, default   236 targets, 3026 checks: all answer rebuild
Linux, default     235 targets, 3095 checks: all answer rebuild
Linux, CUDA=1      167 targets, 1383 checks: all answer rebuild

With CUDA=1 on Linux, 72 targets do not build, and the same 72 do not build on dev: most are tests and benchmarks that compile colibri.c with -DCOLI_CUDA but do not link the CUDA object (undefined reference to coli_cuda_pipe_scratch); olmoe's link keeps -lcublas without the CUDA library path; backend_vulkan.o needs Vulkan headers that environment does not have.

One thing found on the way, on dev as well and not touched here: under MSYS2 the Makefile's -lvulkan does not link (cannot find -lvulkan). The loader ships libvulkan-1.dll.a, and the Windows link is -static, which makes ld skip import libraries for -l. The build above links that import library by path.

Still not verified

All on macOS, which I do not have:

# not verified why what would show it
1 the make version neither the CI log nor the runner image's software list prints it; it is Xcode's make, the one the Makefile's own #1732 note describes as GNU Make 3.81 on macOS a make --version line in the macOS job, if you want it pinned
2 which build flavours the every-flavour test accepted the suite runs quietly there a verbose run; METAL=1 itself is built for real by All engines (macos)
3 the header-gap comparison, and so METAL=1's measured on Linux and Windows only not needed for correctness, since the compiler now writes the list everywhere

Not listed, because this change cannot affect it: CUDA or HIP kernels at run time on NVIDIA or Linux AMD hardware. The change decides which files make rebuilds, not what they compile to; the kernels that did run (HIP on gfx1151, Vulkan on the Radeon, the CPU path of the CUDA build) match dev.

On timing: as you said, this will conflict once with open PRs that edit the Makefile. Where it does, the conflict is on a rule's prerequisite line and the resolution is the same each time: take the incoming line without its headers. A new rule that arrives with a header list fails test_no_generated_rule_lists_headers_by_hand, which names it.

Every engine and test rule listed its headers by hand on one line, the
most edited line in the Makefile and a merge conflict for every open PR
touching the same rule. The lists also drifted: many built targets on dev
read headers their rule does not name (measured in the PR), so editing
one of those leaves a stale binary while make reports success.

-MMD -MP in CFLAGS now writes <target>.d beside each binary and object,
and the Makefile includes those files. Header lists are removed from 166
rules (9 engines, 154 tests, 3 objects).

Three cases the compiler cannot cover on its own:

- A command compiling several .c files writes the last unit's deps only.
  Eight such tests keep their lists (AUTODEP_MULTI_TU). With CUDA/HIP,
  qwen36_tier.c joined 14 command lines the same way; it is now its own
  object, qwen36_tier.o, and QWEN36_TIER_SRC is renamed QWEN36_TIER_OBJ.
- Only 6 of the 166 rules depend on .build-config, so the CFLAGS change
  would not rebuild the rest and they would have no .d. Each target
  therefore depends on its own .d through an empty rule: a missing .d
  makes it out of date, and the rebuild writes one.
- Rules built through Makefile.deepseek-v4 or the adapter targets
  (AUTODEP_ELSEWHERE), and the nvcc, MSVC-hosted CUDA and Metal objects,
  keep their lists; nvcc never receives CFLAGS.

test_makefile_deps.py now checks that every rule in AUTODEP_BINS
compiles one unit with -MMD in the default and the GPU configuration,
that no such rule lists headers by hand, that every built target's .d
covers its source's unconditional includes, and that make -q -W on a
header from a .d answers "rebuild". clean.py removes *.d; c/.gitignore
ignores them. test_glm53_metal_source.py no longer expects
backend_metal.h in the glm53 rule.
test_registry_engine_agreement treats an engine as accelerated when its
rule names a backend object. QWEN36_TIER_SRC became QWEN36_TIER_OBJ in
the previous commit, so the old entry matched nothing; qwen36 and qwen38
still passed through CUDA_OBJ, which hid it.
- test_makefile_deps: every make call keeps .build-config's contents and
  timestamp. Parsing with CUDA=1 or CUDA_DLL=1 rewrote it, so running the
  suite switched the tree's recorded configuration and the next make
  relinked everything that depends on it. A new test pins this.
- clean.py: remove the .d files under tools/ and build/segment/ too, and
  the backend_vulkan.o and backend_xdna.o objects. After a clean that
  removed their .d, those objects stayed in the tree with no record of the
  headers they read, and the coverage test rightly flagged them.
- One sed pass over the rule names now serves both TEST_RULES and
  ENGINE_RULES; both variables are unchanged.
- A stale comment still said the qwen36 tests compile qwen36_tier.c; the
  deepseek_v41 rule loses a leftover line continuation.
Eight tests compiled a second .c on their own command line
(segment_runtime.c, edge_runtime.c, backend_xdna.c, deepseek_v4.c and a
fixtures file). GCC writes a .d for the last unit only, so they kept
hand-written header lists as exceptions, and those lists had the same
kind of gaps as the ones -MMD replaced elsewhere.

Each helper is now its own object in tests/, compiled with exactly the
flags that test gave it before: -DCOLI_XDNA for the XDNA lane object,
-DCOLI_V4_UNIT_NATIVE_QUANT for deepseek_v4.c, and NOCUDA_CFLAGS for the
two qwen38 tests, which get their own copies of segment_runtime.o and
edge_runtime.o since those flags differ from CFLAGS in GPU builds. Each
unit was already compiled on its own, so the result is the same.

AUTODEP_MULTI_TU is gone, and with it the last hand-written header lists
on gcc-built tests; the one-unit check in test_makefile_deps.py now
points at objects instead of an exception list. clean.py removes
tests/*.o.
Headers under #if reach a .d only through the compile that reads them:
backend_xdna.h with XDNA=1, backend_vulkan.h with VK=1, the qwen36 tier
only with CUDA or HIP. The coverage test cannot require them from the
source text, so what stands behind them is the one-unit check, and that
only ran in the default and one GPU configuration.

It now runs in every flavour the host's make accepts: CUDA=1, CUDA_DLL=1,
HIP=1 (with a fixed HIP_ARCH, as the CI syntax job uses), HIP_DLL=1, VK=1,
XDNA=1 and METAL=1, each recognised by the define colibri then gets. On
Windows that is CUDA_DLL, HIP_DLL, VK and XDNA; on Linux CUDA, HIP, VK
and XDNA. Putting backend_xdna.c on colibri's command line under XDNA=1
passes the default check and fails this one, in XDNA=1 only.
The comment said all six are built outside this Makefile's own $(CC)
recipes. Only the two DeepSeek V4 tests are; the four segment/edge adapter
tests are compiled here, three units in one command.
The first slice covered the engines, the tests/test_* rules and the objects
they link. The other rules gcc compiles with $(CFLAGS) still listed their
headers by hand, and drifted the same way: the benchmarks under tests/,
the XDNA probe, the objects of the segment library and of the V4
ownership test, and the rANS ctypes library.

- AUTODEP_BINS now takes every rule under tests/, not only tests/test_*,
  plus $(SEGMENT_ALL_OBJS), $(SEGMENT_RUNTIME_OBJS), $(V4_OWNERSHIP_OBJS)
  and $(RANSLIB). Their header lists are gone.
- AUTODEP_OWN_FLAGS names the three tests/ rules that do not compile with
  $(CFLAGS) (a GPU compiler, and two benchmarks pinned to their own flags
  whose sources include no local header), so they write no .d.
- xdna_physical_probe compiled backend_xdna.c beside its own source; it now
  links tests/backend_xdna_lane.o, which has exactly its flags.
- clean.py removes build/segment/ as a whole, as it does build/ownership/.
  It used to leave the segment objects, and one left without its .d is a
  built target whose headers nothing tracks.
- test_makefile_deps.py resolves rules spelled through variables
  ($(SEGMENT_BUILD_DIR)/glm.o:, $(RANSLIB):, the V4 static pattern rule) by
  asking make for them, so the hand-list and coverage checks see those
  rules instead of skipping them.

The run targets fuzz-rans, dsv4-cuda-test and dsv4-cuda-loader-test keep
their lists: their names are not files, so they rebuild every time.
@Kenneth-Javier
Kenneth-Javier marked this pull request as ready for review September 26, 2026 10:41
@Kenneth-Javier

Copy link
Copy Markdown
Contributor Author

@JustVugg macOS is green, so this is out of draft. What the macOS logs do and don't show is under CI on this PR in the description.

@Kenneth-Javier

Copy link
Copy Markdown
Contributor Author

@huppiflupp a small, optional ask, since you run a Strix Halo on Linux: if ROCm is already installed there, could you build one engine from this branch with HIP=1? CI only compiles the hipcc side on Linux (make gpu-compile), so the gcc half of a Linux HIP build is the one path in this PR nobody has built yet (row 3 under Still not verified).

git clone --depth 1 -b build/makefile-autodeps https://github.com/Kenneth-Javier/colibri colibri-1758
cd colibri-1758/c
/opt/rocm/bin/hipcc --version | head -1
make colibri HIP=1                                 # HIP_ARCH defaults to native
grep -c backend_cuda.h colibri.d                   # expect 1: a header only a CUDA/HIP build reads
make -q -W backend_cuda.h colibri HIP=1; echo $?   # expect 1: make would rebuild after it changes
python3 -m unittest tests.test_makefile_deps       # expect OK (a skip or two is fine)

The last line or two of each is plenty. No rush, and if ROCm isn't on that machine, never mind: it isn't worth installing for this.

@Kenneth-Javier

Copy link
Copy Markdown
Contributor Author

@huppiflupp one correction for Nobara, going by your HARDWARE.md and setup.sh: Fedora's ROCm packages install under /usr, not /opt/rocm, and a gfx1151 build compiles the rocWMMA kernels, so the lines become:

hipcc --version | head -1
rpm -q rocwmma-devel    # gfx1151 needs its headers; if it isn't installed, skip the whole thing
make colibri HIP=1 ROCM_HOME=/usr HIP_ARCH=gfx1151
grep -c backend_cuda.h colibri.d
make -q -W backend_cuda.h colibri HIP=1 ROCM_HOME=/usr HIP_ARCH=gfx1151; echo $?
python3 -m unittest tests.test_makefile_deps

(HIP_ARCH is spelled out so the build doesn't depend on where rocm_agent_enumerator lives.)

@huppiflupp

Copy link
Copy Markdown
Contributor

Built on Nobara 44 / gfx1151 (hipcc 7.1.52802, rocwmma-devel 7.1.0) with make colibri HIP=1 ROCM_HOME=/usr HIP_ARCH=gfx1151 at fbeef6a: builds, links libamdhip64.so.7. grep -c backend_cuda.h colibri.d → 2 (the prerequisite line plus the -MP phony backend_cuda.h: target, so as intended). make -q -W backend_cuda.h colibri … → 1. tests.test_makefile_deps: OK (8 tests, skipped=1, the #1284 end-to-end case). One gcc warning from the gcc half: g_cuda_raw_experts defined but not used (colibri.c:691).

@Kenneth-Javier

Copy link
Copy Markdown
Contributor Author

Thanks, that closes the last gap outside macOS, on the Fedora packaging I couldn't test. You're right about the count: my "expect 1" should have said 2, and the -MP phony target is intended. The skip is expected too: that test only checks targets that are up to date under the default flags, and yours was built with HIP=1. The g_cuda_raw_experts warning is on dev as well (a dev build with HIP=1 in the rocm/dev-ubuntu-24.04:7.2.4 container shows the same line, and colibri.c isn't touched here), so I've left it out of this PR. Your result is in the description now.

Kenneth-Javier added a commit to Kenneth-Javier/colibri that referenced this pull request Sep 26, 2026
The VK_OBJ test errored in setUpClass on the macOS runner: it read
$(CC) and $(EXE) through `make --eval`, and /usr/bin/make on macOS is
GNU Make 3.81, which predates --eval (3.82). Its stdout was empty; the
rest of the suite passed (1479 run, 156 skipped, this one error).

$(EXE) now comes from make's own database (`make -pn`), which 3.81
prints the same way, and the compiler is simply the first word of each
link line.

Two holes in reading `make -n` go with it:
- recipe continuations are printed as written, so the five link
  commands that span lines (test_deepseek_v4 and the four
  segment/edge adapter tests) were never checked. They are folded
  first now.
- a rule that writes a differently named file (fuzz-rans,
  bench-omp-grain, the dsv4 CUDA tests, glm53-metal-check) was skipped,
  because its output was not a rule name. Every printed command that
  compiles a .c into an output without -c is checked now, and such a
  rule is reported by its output name if it ever needs $(VK_OBJ).

The scan covers 204 link lines on dev plus this branch (198 before).
None of the added ones calls the backend, so the results against the
linker are unchanged:
- Linux (WSL Ubuntu 24.04, make 4.3): dev fails with the thirteen
  rules; this branch passes, and the 60 lines that call the backend are
  the 60 that link backend_vulkan.o; with JustVugg#1338 merged only
  test_qwen36_slot_int8 is reported. About 22 s.
- Windows (make 4.4.1): the same, with twelve on dev, since bench_idot
  cannot preprocess without ARCH=native. About 20 s.
- dev + JustVugg#1758 + this branch, conflicts resolved: passes together with
  JustVugg#1758's test_makefile_deps and writes no .d file.

Not verified locally: GNU Make 3.81 itself. The macOS runner is the
check.
dev added tok_unicode_deepseek.h to 84 hand-written prerequisite lists
(JustVugg#1775). All 84 rules take their headers from -MMD here, so each conflict
resolves to the line without headers, and the new tests/test_tok_deepseek
rule keeps its recipe without a list. dev touched none of the rules that
still carry a list.
dev added qwen38_vision.h to 19 hand-written prerequisite lists (JustVugg#1777).
All 19 rules take their headers from -MMD here, so each conflict resolves
to the line without headers; dev touched none of the rules that still
carry a list. JustVugg#1762's Windows Vulkan link merges cleanly.
JustVugg pushed a commit that referenced this pull request Sep 28, 2026
The first version of test_makefile_vk_obj.py read preprocessor
conditionals as text: a rule needed $(VK_OBJ) when a .c it compiled
reached a file with `#if ... COLI_VULKAN`. That is exact on dev, but
not in general. With #1338 (the qwen36 Vulkan expert tier) merged on
top, it flagged 25 rules where the linker fails one:

- the qwen36 tier tests pass -DCOLI_CUDA, and qwen36_tier.c picks CUDA
  over Vulkan with #elif;
- qwen36.c's Vulkan hook only reads COLI_VULKAN from the environment;
- test_qwen36_tier_vk_fake defines the coli_vk_* functions itself.

Skipping lines that pass -DCOLI_CUDA is not a fix either: kimi_k3.c
keeps independent #ifdef COLI_VULKAN blocks, and test_kimi_cuda_expert
does need backend_vulkan.o.

The test now asks the tools. `make -Bn VK=1` gives each expanded link
line, and every .c on it is preprocessed with that line's own flags
(link-only and -MMD/-MF arguments dropped). A line needs $(VK_OBJ) when
the surviving code of this tree, outside backend_vulkan.h, calls a
function backend_vulkan.h declares and does not define it. String
literals are ignored. A line this host cannot preprocess is left to the
platforms that can build it. .build-config is restored afterwards, so
the dry run does not make the next real build relink. The engines whose
own source calls the backend anchor the non-vacuity check.

Measured against the linker (make -k test-c VK=1 plus the on-demand
targets):
- dev 4e28e39: flags exactly the thirteen rules the previous commit
  fixed on Linux; on Windows twelve, since bench_idot cannot even
  preprocess there without ARCH=native (its #error).
- dev + the previous commit: passes. On Linux the 60 lines it says call
  the backend are exactly the 60 that link backend_vulkan.o.
- dev + #1338 + this branch: flags only test_qwen36_slot_int8, which is
  also the only link the linker fails there.
- dev + #1758 + this branch, conflicts resolved: passes together with
  #1758's test_makefile_deps; with -MMD -MP in CFLAGS no .d file is
  written.

Runtime: 15 s on Windows (gcc 16.1.0, 32 threads), 20 s on WSL Ubuntu
24.04 (gcc 13.3.0). The first version took 1.5 s.

Not verified: macOS / Apple clang, where the linemarker format is the
same but the test was not run.
@JustVugg

Copy link
Copy Markdown
Owner

This is still wanted: dev has had many missing header dependencies. Two things before it can go in, and it should be the last of the current batch.

  1. tests: link $(VK_OBJ) into the rest of the engine-including rules #1763 was merged, so the Makefile conflicts again (10 mechanical hunks: take your rules and add $(VK_OBJ) where tests: link $(VK_OBJ) into the rest of the engine-including rules #1763 put it).
  2. Your own test_makefile_deps fails on three rules that came in with Qwen-Image (Qwen-Image-2.1: text-to-image in colibri (C engine, images in the terminal, web, OpenAI images API) #1780), which still list headers:
-qwenimage$(EXE): qwenimage.c qi_gemm.h qwenimage_vae.h qwenimage_preview.h st.h json.h tok.h tok_unicode.h tok_unicode_o200k.h tok_unicode_deepseek.h qwen38_nfc.h qwen38_nfc_tables.h serve_poll.h compat.h .build-config
+qwenimage$(EXE): qwenimage.c .build-config
-tests/test_qi_gemm$(EXE): tests/test_qi_gemm.c qi_gemm.h
+tests/test_qi_gemm$(EXE): tests/test_qi_gemm.c
-tests/test_qwenimage_vae$(EXE): tests/test_qwenimage_vae.c qwenimage_vae.h qi_gemm.h st.h json.h compat.h
+tests/test_qwenimage_vae$(EXE): tests/test_qwenimage_vae.c

With those, the guards pass and touching qi_gemm.h makes both tests out of date, as it should. TEST_BINS is 172 on both sides and test_tok_deepseek survives; please keep it that way when resolving.

dev brought new rules with hand-written header lists and new multi-source
compiles (JustVugg#1323, JustVugg#1343, JustVugg#1780); here they take their headers from -MMD:

- qwenimage, test_qi_gemm, test_affine_quant, test_affine_dequant,
  test_qwenimage_vae and test_qwen36_dense_affine list no headers.
- qwen36's qpack sources (METAL=1 only) are objects, qwen36_qpack.o and
  qpack.o, compiled with $(QWEN36_CFLAGS) as qwen36's command line gave
  them; test_qwen36_qpack and test_qpack link helper objects compiled with
  their own flags. clean.py removes the two root objects.
- QWEN36_TIER_SRC in dev's new lines is QWEN36_TIER_OBJ here.

qpack-inspect keeps its two-unit compile and its list: the '-' in its name
keeps it out of the generated set. The Metal rules keep theirs.
JustVugg#1763 added $(VK_OBJ) to the prerequisites and link line of 13 test and
benchmark rules; each conflict keeps this branch's rule and adds
$(VK_OBJ) where JustVugg#1763 put it. JustVugg#1790's bench_matmul_f32 and test_matmul_f32
list no headers here. TEST_BINS is the same 178 targets as on dev, and
test_tok_deepseek is among them.
@Kenneth-Javier

Copy link
Copy Markdown
Contributor Author

Done, merged up to dev at eefa57a (4baa66c). #1763's $(VK_OBJ) is in all 13 rules where it put it, and the Qwen-Image rules (qwenimage, test_qi_gemm, test_qwenimage_vae) list no headers. The later merges needed one more step: qwen36's qpack sources (METAL=1), test_qwen36_qpack and test_qpack compiled several units per command, so they link objects now, compiled with the flags their commands gave them; test_qwen36_dense_affine and #1790's matmul_f32 rules follow the same pattern. TEST_BINS is 178 on both sides and test_tok_deepseek is in it. Locally on Windows the deps tests pass in every flavour and the touched targets build with and without VK=1; make check passes apart from two symlink tests in test_qpack_install_policy, which need a privilege Windows withholds by default (WinError 1314). The qpack objects under METAL=1 I can only check through the macOS job.

@JustVugg

JustVugg commented Oct 1, 2026

Copy link
Copy Markdown
Owner

The batch before this one is now in (#1739, #1740, #1761, #1798, #1799, #1812 and #1813 among others), so this is the moment for the final rebase. Two things on top of what we wrote before:

  1. c/Makefile conflicts again, in 9 hunks: matmul_f32.h: one vectorized f32 matmul for quant.h, olmoe, inkling and qwen36 (#442) #1798 added matmul_f32.h to several rules. Your generated dependencies make those additions unnecessary, so taking your side of each rule is right.
  2. MiMo-V2.6 (Flash, Pro): engine, vision, tool calling, read as released #1813 added the MiMo engine, and its rule lists 19 headers by hand, so test_no_generated_rule_lists_headers_by_hand fails on mimo$(EXE). The fix, checked locally:
-mimo$(EXE): mimo.c mimo_vision.h sse41_kernels.h cli_args.h compat.h json.h st.h quant.h idot.h fp8_format.h \
-    ... .build-config
+mimo$(EXE): mimo.c .build-config

With it mimo.d lists 23 headers and touching quant.h makes mimo out of date. Please keep TEST_BINS and test_tok_deepseek as before when resolving.

JustVugg#1798 added matmul_f32.h to 10 hand-written prerequisite lists; all 10
rules take their headers from -MMD here, so each conflict resolves to the
line without headers. JustVugg#1813's mimo and the new test_dsv41_untrusted_load
list no headers either: mimo.d names the 19 headers mimo's list had plus
matmul_f32.h, which it did not. TEST_BINS is the same 179 targets as on
dev, test_tok_deepseek among them.
@Kenneth-Javier

Copy link
Copy Markdown
Contributor Author

Merged dev at f565fc3 (d00ad2f). The matmul_f32.h hunks take this side. #1739's new test_dsv41_untrusted_load landed next to a rule this branch changed, so it conflicted as well; it lists no headers here. mimo$(EXE): mimo.c .build-config as you suggested. TEST_BINS is 179 on both sides with no difference and test_tok_deepseek is in it; make -q -W quant.h mimo answers 1. On Windows mimo.d names 20 headers: the 19 from the old list plus matmul_f32.h, which that list was already missing.

@JustVugg
JustVugg merged commit 3ff4cd6 into JustVugg:dev Oct 1, 2026
31 checks passed
jtinbergen added a commit to jtinbergen/colibri that referenced this pull request Oct 1, 2026
…ustVugg#1758)

JustVugg#1758 removed hand-written header lists from 202 rules and renamed
QWEN36_TIER_SRC to QWEN36_TIER_OBJ. The rule this PR added predates it and
named five headers plus the old variable, so after the rebase onto dev it no
longer resolved.

Now spelled exactly like its neighbour tests/test_qwen36_slot_int8: one
translation unit, $(QWEN36_CFLAGS) so -MMD -MP writes the .d, no header list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants