swayfx-mosaic/tooling/probes/step20c-corec-v2/gpu/reproduce.py

90 lines
3.8 KiB
Python

"""Apply and verify G2 against an explicitly supplied reconstructed CORE tree.
Source-only operation: no build, compositor, Wayland or GPU work is performed.
"""
import argparse
import ctypes
import hashlib
import json
import resource
import shutil
import subprocess
from pathlib import Path
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
assert ctypes.CDLL(None).prctl(4, 0, 0, 0, 0) == 0
KIT = Path(__file__).resolve().parent
EXPECTED = {
"render/fx_renderer/fx_pass.c": "c2886ef259fc91431dfe97841387082420ab80a0fe2f07e7dcdb71fc95731181",
"render/fx_renderer/g2_gl.h": "cd175030be6a2e5d0af036c714c03455ef417f78f20360e00f7e6d1d7be7ab13",
"render/fx_renderer/g2_native.h": "0dfa9e089853a078094736da7f4e133d7fd648500d380dd7f536a7a6f80fdd8e",
}
PATCH_SHA256 = "77d7445ceaf643fd5cc708d31c3f2c77feaeb80f8130c419c55e5514ecc8a75d"
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def tree(path):
return {
str(file.relative_to(path)): sha(file)
for file in sorted(path.rglob("*"))
if file.is_file() and ".git" not in file.relative_to(path).parts
}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--core", required=True, type=Path, help="reconstructed CORE SceneFX source tree")
parser.add_argument("--destination", required=True, type=Path, help="new, absent output directory")
parser.add_argument("--work", required=True, type=Path, help="existing disposable directory for receipts/logs")
args = parser.parse_args()
core = args.core.resolve(strict=True)
destination = args.destination.resolve()
work = args.work.resolve(strict=True)
if not core.is_dir() or not work.is_dir():
parser.error("--core and --work must be existing directories")
if destination.exists():
parser.error("--destination must not already exist")
if destination == core or destination in core.parents or core in destination.parents:
parser.error("--destination must be separate from --core")
patch_file = KIT / "g2-native.patch"
if sha(patch_file) != PATCH_SHA256:
parser.error("kit patch does not match its embedded publication hash")
before = tree(core)
shutil.copytree(core, destination, ignore=shutil.ignore_patterns(".git"))
command = ["patch", "--batch", "--forward", "-p1", "-i", str(patch_file)]
result = subprocess.run(command, cwd=destination, text=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
actual = tree(destination)
changed = {name for name in before.keys() | actual.keys() if before.get(name) != actual.get(name)}
expected_changed = set(EXPECTED)
native_hashes = {name: actual.get(name) for name in sorted(expected_changed)}
receipt = {
"schema": "G2-portable-source-reproduction-v1",
"status": "PASS" if result.returncode == 0 and changed == expected_changed and native_hashes == EXPECTED else "FAIL",
"core": str(core),
"destination": str(destination),
"work": str(work),
"patch_sha256": sha(patch_file),
"command": command,
"exit": result.returncode,
"stdout": result.stdout,
"changed_files": sorted(changed),
"expected_native_sha256": EXPECTED,
"actual_native_sha256": native_hashes,
"all_changed_files_match_embedded_manifest": changed == expected_changed and native_hashes == EXPECTED,
"core_unchanged": tree(core) == before,
"runtime_or_build_started": False,
}
receipt_path = work / "gpu-patch-reproduction.json"
receipt_path.write_text(json.dumps(receipt, indent=2) + "\n")
print(json.dumps(receipt, indent=2))
raise SystemExit(0 if receipt["status"] == "PASS" and receipt["core_unchanged"] else 1)
if __name__ == "__main__":
main()