build: make each grammar object depend on the sources it includes

A grammar unity object, grammar_<lang>.c, #includes its own vendored parser.c
and, where the grammar has one, scanner.c. Those files were prerequisites of
nothing. Editing a vendored scanner therefore left the object untouched and the
edit silently absent from the binary.

This is not hypothetical: the properties scanner fix in the previous commit
appeared not to work at all. An A/B said 3 stalls against 2 - no effect - and
the reason was that prod_grammar_properties.o predated the edit by five minutes
and `nm` still showed the old `_reached_eof` symbol sitting in it. The measured
"no effect" was a stale object, not a bad fix.

The build already treats its other two unity objects this way and says why:
LSP_UNITY_DEPS and TS_RUNTIME_DEPS both list the sources and headers they pull
in, because this Makefile deliberately does not use compiler auto-dependency
(-MMD) generation. Grammars were the gap, and MANIFEST.md's "Local source
patches" table means patching a vendored scanner is an expected thing to do -
so the trap was reachable by design.

Secondary expansion is what lets one pattern rule name each grammar's own
directory ($$* is the stem, grammar_properties -> properties). Applied to all
three grammar object rules: production, test and tsan. Non-grammar objects
match no directory and gain no prerequisite.

Verified by touching internal/cbm/vendored/grammars/properties/scanner.c and
watching exactly one object rebuild.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-09-19 15:50:31 +02:00
parent 2f33999044
commit 6f290e11d0
+20 -3
View File
@@ -861,7 +861,24 @@ test-foundation: $(BUILD_DIR)/test-foundation
# ── Grammar/TS/LSP object files (compiled with relaxed warnings) ─
$(BUILD_DIR)/%.o: $(CBM_DIR)/%.c | $(BUILD_DIR)
# Each grammar_<lang>.c is a unity object that #includes its own vendored
# parser.c and, where the grammar has one, scanner.c. Those includes were
# prerequisites of nothing, so editing a vendored scanner left the object
# untouched and the edit silently absent from the binary: on 2026-09-19 a fix
# to properties/scanner.c appeared not to work at all, and `nm` showed the old
# symbol still sitting in prod_grammar_properties.o. MANIFEST.md's "Local
# source patches" table means patching a vendored scanner is expected, so this
# trap is reachable by design. Same reasoning as LSP_UNITY_DEPS and
# TS_RUNTIME_DEPS above; secondary expansion is what lets one pattern rule name
# each grammar's own directory ($$* is the stem, grammar_properties ->
# properties). Non-grammar objects match no directory and gain no prerequisite.
GRAMMAR_VENDOR_DIR = $(CBM_DIR)/vendored/grammars/$$(subst grammar_,,$$*)
GRAMMAR_UNITY_DEPS = $$(wildcard $(GRAMMAR_VENDOR_DIR)/*.c) \
$$(wildcard $(GRAMMAR_VENDOR_DIR)/*.h) \
$$(wildcard $(GRAMMAR_VENDOR_DIR)/tree_sitter/*.h)
.SECONDEXPANSION:
$(BUILD_DIR)/%.o: $(CBM_DIR)/%.c $(GRAMMAR_UNITY_DEPS) | $(BUILD_DIR)
$(CC) $(GRAMMAR_CFLAGS_TEST) -c -o $@ $<
$(BUILD_DIR)/ts_runtime.o: $(TS_RUNTIME_DEPS) | $(BUILD_DIR)
@@ -873,7 +890,7 @@ $(BUILD_DIR)/lsp_all.o: $(LSP_UNITY_DEPS) | $(BUILD_DIR)
$(BUILD_DIR)/preprocessor.o: $(CBM_DIR)/preprocessor.cpp | $(BUILD_DIR)
$(CXX) $(CXXFLAGS_TEST) -w -I$(CBM_DIR)/vendored -c -o $@ $<
$(BUILD_DIR)/tsan_%.o: $(CBM_DIR)/%.c | $(BUILD_DIR)
$(BUILD_DIR)/tsan_%.o: $(CBM_DIR)/%.c $(GRAMMAR_UNITY_DEPS) | $(BUILD_DIR)
$(CC) $(GRAMMAR_CFLAGS_TSAN) -c -o $@ $<
$(BUILD_DIR)/tsan_ts_runtime.o: $(TS_RUNTIME_DEPS) | $(BUILD_DIR)
@@ -1103,7 +1120,7 @@ TS_RUNTIME_OBJ_PROD = $(BUILD_DIR)/prod_ts_runtime.o
LSP_OBJ_PROD = $(BUILD_DIR)/prod_lsp_all.o
PP_OBJ_PROD = $(BUILD_DIR)/prod_preprocessor.o
$(BUILD_DIR)/prod_%.o: $(CBM_DIR)/%.c | $(BUILD_DIR)
$(BUILD_DIR)/prod_%.o: $(CBM_DIR)/%.c $(GRAMMAR_UNITY_DEPS) | $(BUILD_DIR)
$(CC) $(GRAMMAR_CFLAGS) -c -o $@ $<
$(BUILD_DIR)/prod_ts_runtime.o: $(TS_RUNTIME_DEPS) | $(BUILD_DIR)