BuildLinux.sh hashes a sorted manifest of compiled inputs into VOXELFORGE_SOURCE_STAMP, printed first by every executable; Standalone/Tools/verify/verify.py checks it before and after each tier and records binary/input hashes and the resolved recipe, style map and layout. Fast (163 s) runs on the canonical owner world only and asserts FI Style 2 / SW Style 1; medium (8 min) and full (26 min) add the style matrix, GCC, determinism and cost. Controls: stale source, mislabelled style-0 owner, all-black capture, L30/L31, L35/L36 and the L38 step are all rejected. No geometry change; owner hashes identical to L37. Research R6: leak table, generator contract and a 10-step metre-native migration plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
99 lines
3.6 KiB
Python
99 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Build and verify the final-source manifest used by Linux executables."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
|
|
def sha256(data: bytes) -> str:
|
|
return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def manifest_entries(root: Path, include_unreal: bool = False) -> list[tuple[str, bytes]]:
|
|
standalone = root / "Standalone"
|
|
files: set[Path] = set()
|
|
|
|
core = standalone / "Core"
|
|
for suffix in ("*.h", "*.hpp", "*.cpp", "*.txt"):
|
|
files.update(core.rglob(suffix))
|
|
|
|
source = standalone / "Source"
|
|
for suffix in ("*.h", "*.hpp", "*.cpp"):
|
|
files.update(source.glob(suffix))
|
|
|
|
for path in standalone.glob("Build*"):
|
|
if path.is_file():
|
|
files.add(path)
|
|
for suffix in ("*.cmake", "*.mk", "Makefile", "CMakeLists.txt"):
|
|
files.update(standalone.glob(suffix))
|
|
core_build = standalone / "Core"
|
|
for suffix in ("*.cmake", "*.mk", "Makefile", "CMakeLists.txt"):
|
|
files.update(core_build.glob(suffix))
|
|
helper = standalone / "Tools/verify/source_stamp.py"
|
|
if helper.is_file():
|
|
files.add(helper)
|
|
|
|
if include_unreal:
|
|
unreal = source / "VoxelForge"
|
|
for suffix in ("*.h", "*.hpp", "*.cpp", "*.cs"):
|
|
files.update(unreal.rglob(suffix))
|
|
|
|
entries = [(path.relative_to(root).as_posix(), path.read_bytes())
|
|
for path in files if path.is_file()]
|
|
|
|
# BuildLinux creates one namespace wrapper per listed Core implementation.
|
|
# Include each exact generated wrapper as a virtual source input.
|
|
source_list = core / "core_sources.txt"
|
|
for raw in source_list.read_text(encoding="utf-8").splitlines():
|
|
name = raw.split("#", 1)[0].strip()
|
|
if not name or name == "Core/src/VoxelForgeCore.cpp":
|
|
continue
|
|
source_path = standalone / name
|
|
if not source_path.is_file():
|
|
raise SystemExit(f"missing source listed in Core/core_sources.txt: {name}")
|
|
wrapper_name = "vfcore_" + "".join(c if c.isalnum() else "_" for c in name)
|
|
wrapper = ('#include "CoreNamespacePreamble.h"\n'
|
|
"namespace vfcore::detail\n{\n"
|
|
f'#include "{source_path}"\n'
|
|
"}\n").encode("utf-8")
|
|
entries.append((f"generated/wrappers/{wrapper_name}.cpp", wrapper))
|
|
|
|
return sorted(entries, key=lambda item: item[0])
|
|
|
|
|
|
def rendered_manifest(root: Path, include_unreal: bool = False) -> bytes:
|
|
lines = [f"{sha256(data)} {name}" for name, data in
|
|
manifest_entries(root, include_unreal)]
|
|
return ("\n".join(lines) + "\n").encode("utf-8")
|
|
|
|
|
|
def source_stamp(root: Path, include_unreal: bool = False) -> str:
|
|
return sha256(rendered_manifest(root, include_unreal))
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("command", choices=("stamp", "write-manifest"))
|
|
parser.add_argument("--root", type=Path,
|
|
default=Path(__file__).resolve().parents[3])
|
|
parser.add_argument("--output", type=Path)
|
|
parser.add_argument("--include-unreal-adapter", action="store_true")
|
|
args = parser.parse_args()
|
|
root = args.root.resolve()
|
|
manifest = rendered_manifest(root, args.include_unreal_adapter)
|
|
stamp = sha256(manifest)
|
|
if args.command == "write-manifest":
|
|
if args.output is None:
|
|
parser.error("write-manifest requires --output")
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_bytes(manifest)
|
|
print(stamp)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|