diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 604c1f02..43416473 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: run: bash scripts/check-no-test-skips.sh - name: Check whitespace run: git diff --check + - name: Check build source lists + run: bash scripts/check-build-sources.sh - name: Validate GitHub Actions contract run: bash tests/test_github_workflows.sh - name: Test pinned-SDK workflow diff --git a/Makefile.cbm b/Makefile.cbm index 5cf541f8..85d75af9 100644 --- a/Makefile.cbm +++ b/Makefile.cbm @@ -532,7 +532,7 @@ PP_OBJ_TEST = $(BUILD_DIR)/preprocessor.o # ── Targets ────────────────────────────────────────────────────── -.PHONY: test test-cli test-mcp test-repro test-foundation test-tsan cbm check-zova test-zova test-zova-c-sql-functions test-zova-bridge zova-test-runner clean-c lint lint-tidy lint-cppcheck lint-format security +.PHONY: test test-cli test-mcp test-repro test-foundation test-tsan cbm check-zova test-zova test-zova-c-sql-functions test-zova-bridge zova-test-runner clean-c print-build-sources lint lint-tidy lint-cppcheck lint-format security $(BUILD_DIR): mkdir -p $(BUILD_DIR) @@ -803,6 +803,17 @@ $(BUILD_DIR)/codebase-memory-mcp: $(MAIN_SRC) $(PROD_SRCS) $(EXTRACTION_SRCS) $( cbm: $(ZIG) build cbm $(ZIG_CBM_ARGS) +# Sources this Makefile builds, one " " line each, for +# scripts/check-build-sources.sh to compare with build.zig. The vendored LZ4 +# and zstd paths are written out because their object rules name them directly. +print-build-sources: + @printf 'prod %s\n' $(MAIN_SRC) $(PROD_SRCS) $(EXTRACTION_SRCS) $(AC_LZ4_SRCS) $(ZSTD_SRCS) \ + $(MIMALLOC_SRC) $(SQLITE3_SRC) $(TRE_SRC) $(GRAMMAR_SRCS) $(TS_RUNTIME_SRC) $(LSP_SRCS) \ + $(PREPROCESSOR_SRC) $(UNIXCODER_BLOB_SRC) \ + $(CBM_DIR)/vendored/lz4/lz4.c $(CBM_DIR)/vendored/lz4/lz4hc.c $(CBM_DIR)/vendored/zstd/zstd.c + @printf 'test %s\n' $(ALL_TEST_SRCS) + @printf 'zig %s\n' $(filter %.zig,$(CBM_ZIG_SRCS) $(CBM_ZOVA_BRIDGE_ZIG_SRC)) + clean-c: rm -rf $(BUILD_DIR) diff --git a/scripts/check-build-sources.sh b/scripts/check-build-sources.sh new file mode 100644 index 00000000..26adc0fa --- /dev/null +++ b/scripts/check-build-sources.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# check-build-sources.sh — Fail when build.zig and Makefile.cbm build different +# source files. +# +# build.zig builds the PR CI binaries and test runners; Makefile.cbm builds the +# release binaries and the ASan test runner. Each keeps its own source list, so +# a file added to one and missed in the other only breaks the other build. +# +# Compared groups: +# prod C/C++/assembly sources linked into the binary +# test test sources linked into the full test runner +# zig Zig sources: the files build.zig reaches by @import from its roots, +# and the files Makefile.cbm lists as Zig library inputs +# +# Usage: bash scripts/check-build-sources.sh (exit 0 = same sets, 1 = drift) +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" +cd "$ROOT" + +# Intentional differences: " ". +ALLOWED=( + # Windows regex; build.zig does not target Windows. + "make prod vendored/tre/tre_all.c" +) + +# Quoted strings between "const = ..." and the closing "};". +zig_array() { + awk -v start="^const $1 = " '$0 ~ start {inb = 1; next} inb && /^};/ {inb = 0} inb' build.zig | + grep -oE '"[^"]+"' | tr -d '"' +} + +# b.path("...") arguments inside "fn (" up to the closing "}". +zig_fn_paths() { + awk -v start="^fn $1[(]" '$0 ~ start {inb = 1} inb && /^}/ {inb = 0} inb' build.zig | + grep -oE 'b\.path\("[^"]+"\)' | sed -E 's/^b\.path\("(.*)"\)$/\1/' +} + +# Zig files reachable by relative @import from the given roots. +zig_imports() { + local -a queue=("$@") + local seen=" " file dir dep path + while ((${#queue[@]})); do + file=${queue[0]} + queue=("${queue[@]:1}") + [[ $seen == *" $file "* ]] && continue + seen+="$file " + printf '%s\n' "$file" + dir=$(dirname "$file") + while IFS= read -r dep; do + path=$(cd "$dir/$(dirname "$dep")" && pwd -P)/$(basename "$dep") + queue+=("${path#"$ROOT"/}") + done < <(grep -oE '@import\("[^"]+\.zig"\)' "$file" | sed -E 's/^@import\("(.*)"\)$/\1/') + done +} + +zig_build_sources() { + { + zig_fn_paths addCbmExecutable + zig_fn_paths addNativeSources + zig_array native_sources + # grammarSources(): every internal/cbm/grammar_*.c + ls internal/cbm/grammar_*.c + } | sed 's/^/prod /' + zig_array all_test_sources | sed 's/^/test /' + zig_imports src/cbm_zig.zig "$(zig_fn_paths addZovaBridgeObject | grep '\.zig$')" | sed 's/^/zig /' +} + +zig_list=$(zig_build_sources | sort -u) +make_list=$(make -s -f Makefile.cbm print-build-sources | sort -u) +allowed=$(printf '%s\n' "${ALLOWED[@]}") + +failures=0 +report() { # + local side=$1 other=$2 line + while IFS= read -r line; do + [[ -z $line ]] && continue + grep -qxF "$side $line" <<<"$allowed" && continue + echo "[build-sources] only in $side build, missing from $other: $line" + failures=$((failures + 1)) + done <<<"$3" +} +report zig "Makefile.cbm" "$(comm -23 <(printf '%s\n' "$zig_list") <(printf '%s\n' "$make_list"))" +report make "build.zig" "$(comm -13 <(printf '%s\n' "$zig_list") <(printf '%s\n' "$make_list"))" + +# The focused build.zig runners must only use tests the full runner also has. +all_tests=$(zig_array all_test_sources) +for name in zova_test_sources mcp_test_sources pipeline_test_sources; do + while IFS= read -r test; do + grep -qxF "$test" <<<"$all_tests" && continue + echo "[build-sources] $name in build.zig lists $test, which all_test_sources does not" + failures=$((failures + 1)) + done < <(zig_array "$name") +done + +if ((failures)); then + echo "" + echo "[build-sources] FAIL: $failures difference(s). Add each file to both build.zig and Makefile.cbm," + echo " or to ALLOWED in scripts/check-build-sources.sh if the difference is intentional." + exit 1 +fi + +echo "[build-sources] OK — build.zig and Makefile.cbm build the same sources" diff --git a/tests/test_github_workflows.sh b/tests/test_github_workflows.sh index 051b232a..973b7151 100644 --- a/tests/test_github_workflows.sh +++ b/tests/test_github_workflows.sh @@ -38,6 +38,7 @@ grep -q 'zova_c.lib' "$ROOT/.github/actions/setup-zova/action.yml" grep -q 'pull_request:' "$WORKFLOWS/ci.yml" grep -q 'push:' "$WORKFLOWS/ci.yml" grep -q 'tests/test_zova_build_workflow.sh' "$WORKFLOWS/ci.yml" +grep -q 'scripts/check-build-sources.sh' "$WORKFLOWS/ci.yml" grep -q '^ sdk:' "$WORKFLOWS/ci.yml" grep -q '^ build:' "$WORKFLOWS/ci.yml" grep -q '^ test-build:' "$WORKFLOWS/ci.yml"