Files
codebase-memory-mcp/tests/test_codeql_retrace.sh

111 lines
6.2 KiB
Bash

#!/usr/bin/env bash
# Exercise the real production Make rules with tiny C fixtures. CodeQL starts
# tracing after cbm-third-party, so every affected object must compile again,
# while unrelated objects must remain reused. No dry-run/timestamp assertions.
set -euo pipefail
ROOT="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
python3 - "$ROOT" "$BASH" <<'PY'
import os
import pathlib
import re
import subprocess
import sys
import tempfile
root, bash = pathlib.Path(sys.argv[1]), sys.argv[2]
failures = []
with tempfile.TemporaryDirectory(prefix="cbm-codeql-retrace-") as directory:
work = pathlib.Path(directory)
def write(path, text):
dest = work / path
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(text, encoding="utf-8")
for path in ("Makefile.cbm", "scripts/ci/codeql-retrace-vendored.sh",
"scripts/ci/select-lanes.sh", ".github/codeql/codeql-config.yml"):
write(path, (root / path).read_text(encoding="utf-8"))
# Nested and aliased includes deliberately escape the Makefile's shallow
# wildcard dependencies. The compiler really compiles these translation units.
write("vendored/mimalloc/src/static.c",
'#include "mimalloc/internal.h"\n#include "prim/osx/prim.c"\n')
write("vendored/mimalloc/include/mimalloc/internal.h", "#define MI_VALUE 7\n")
write("vendored/mimalloc/src/prim/osx/prim.c", '#include "../unix/prim.c"\n')
write("vendored/mimalloc/src/prim/unix/prim.c", "int mi_value(void) { return MI_VALUE; }\n")
write("internal/cbm/vendored/common/scanner.h", "#define SHARED_VALUE 3\n")
for language in ("go", "json"):
write(f"internal/cbm/grammar_{language}.c",
f'#include "vendored/grammars/{language}/parser.c"\n')
write(f"internal/cbm/vendored/grammars/{language}/parser.c",
'#include "nested/value.h"\n#include "../../common/scanner.h"\n'
f'int grammar_{language}(void) {{ return VALUE + SHARED_VALUE; }}\n')
write(f"internal/cbm/vendored/grammars/{language}/nested/value.h", "#define VALUE 2\n")
write("internal/cbm/vendored/grammars/shared/shared.h", "/* future shared grammar helper */\n")
write("internal/cbm/ts_runtime.c", '#include "vendored/ts_runtime/src/lib.c"\n')
write("internal/cbm/vendored/ts_runtime/src/lib.c", "int runtime_value(void) { return 1; }\n")
for path, symbol in (("vendored/sqlite3/sqlite3.c", "sqlite_value"),
("vendored/tre/tre_all.c", "tre_value"),
("internal/cbm/vendored/lz4/lz4.c", "lz4_value"),
("internal/cbm/vendored/lz4/lz4hc.c", "lz4hc_value"),
("internal/cbm/vendored/zstd/zstd.c", "zstd_value")):
write(path, f"int {symbol}(void) {{ return 1; }}\n")
write("internal/cbm/vendored/lz4/lz4.h", "/* shared LZ4 header */\n")
write("vendored/sqlite3/sqlite3.h", "/* sqlite header */\n")
write("internal/cbm/vendored/zstd/zstd.h", "/* zstd header */\n")
write("vendored/nomic/code_vectors_blob.S", ".text\n")
write("vendored/nomic/code_vectors.bin", "")
write("src/main.c", "int main(void) { return 0; }\n")
def run(args):
proc = subprocess.run(args, cwd=work, capture_output=True, text=True)
if proc.returncode:
raise RuntimeError(f"{args}: exit {proc.returncode}\n{proc.stdout}\n{proc.stderr}")
return proc.stdout
make = ["make", "--no-print-directory", "-f", "Makefile.cbm", "cbm-third-party",
"BUILD_DIR=build/c", "CC=" + os.environ.get("CC", "cc")]
def compiled(output):
return set(re.findall(r" -c -o build/c/([^\s]+\.o)(?:\s|$)", output))
prebuilt = compiled(run(make))
expected = {"prod_mimalloc.o", "prod_sqlite3.o", "prod_grammar_go.o",
"prod_grammar_json.o", "prod_ts_runtime.o", "prod_lz4.o",
"prod_lz4hc.o", "prod_zstd.o", "unixcoder_blob.o"}
# TRE is a Windows-only production object. All other objects are mandatory.
if prebuilt not in (expected, expected | {"prod_tre.o"}):
raise RuntimeError(f"incomplete prebuild: {sorted(prebuilt)}")
if compiled(run(make)):
raise RuntimeError("unchanged prebuild is not reused")
cases = [
("nested allocator header", ["vendored/mimalloc/include/mimalloc/internal.h"], {"prod_mimalloc.o"}),
("aliased allocator source", ["vendored/mimalloc/src/prim/unix/prim.c"], {"prod_mimalloc.o"}),
("nested grammar header", ["internal/cbm/vendored/grammars/go/nested/value.h"], {"prod_grammar_go.o"}),
("grammar wrapper", ["internal/cbm/grammar_go.c"], {"prod_grammar_go.o"}),
("runtime wrapper", ["internal/cbm/ts_runtime.c"], {"prod_ts_runtime.o"}),
("runtime source", ["internal/cbm/vendored/ts_runtime/src/lib.c"], {"prod_ts_runtime.o"}),
("shared grammar header", ["internal/cbm/vendored/common/scanner.h"], {"prod_grammar_go.o", "prod_grammar_json.o"}),
("unknown grammar family", ["internal/cbm/vendored/grammars/shared/shared.h"], {"prod_grammar_go.o", "prod_grammar_json.o"}),
("LZ4 header", ["internal/cbm/vendored/lz4/lz4.h"], {"prod_lz4.o", "prod_lz4hc.o"}),
("SQLite header", ["vendored/sqlite3/sqlite3.h"], {"prod_sqlite3.o"}),
("zstd header", ["internal/cbm/vendored/zstd/zstd.h"], {"prod_zstd.o"}),
("first-party only", ["src/main.c"], set()),
("duplicate changes", ["internal/cbm/grammar_go.c", "internal/cbm/grammar_go.c"], {"prod_grammar_go.o"}),
]
for label, paths, expected_objects in cases:
write("changed.txt", "\n".join(paths) + "\n")
run([bash, "scripts/ci/codeql-retrace-vendored.sh", "--changed", "changed.txt", "retrace.txt"])
retrace = (work / "retrace.txt").read_text(encoding="utf-8").splitlines()
actual = compiled(run(make + [arg for path in retrace for arg in ("-W", path)]))
if actual != expected_objects:
failures.append(f"{label}: expected {sorted(expected_objects)}, compiled {sorted(actual)}")
else:
print(f"PASS {label}: {len(actual)}/{len(prebuilt)} third-party objects retraced")
if failures:
print("CODEQL RETRACE CONTRACT VIOLATED:\n " + "\n ".join(failures))
sys.exit(1)
print("CodeQL retrace contract OK (real compiler + production Make rules)")
PY