repro-formal-problem-solving-framework-benchmark / code /full_official_environment_audit.py
ProCreations's picture
Replace formal benchmark shim with full Mathlib audit
243a534
Raw
History Blame Contribute Delete
4.01 kB
#!/usr/bin/env python3
"""Run the official FPS/DFPS sources under their pinned Mathlib environment.
The audit deliberately uses the archived project and its Lake manifest. It
does not replace Mathlib with a shim or reimplement the examples. With no
argument it creates a temporary checkout, fetches the manifest-pinned
dependencies, builds them, and runs the three official Lean files. The
``--project`` option is useful for rerunning the same checks in an already
built checkout without rebuilding it.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import shutil
import subprocess
import tarfile
import tempfile
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
ARCHIVE = REPO / "source" / "official-current.tar.gz"
EXPECTED_BASIC_SHA256 = "a908c060d1031506ac5844b838b819391a001474eee88a5df184fa9bdac97dc4"
FILES = (
"FormalProblemSolving/Basic.lean",
"FormalProblemSolving/FPS_Example.lean",
"FormalProblemSolving/DFPS_Example.lean",
)
def run(command: list[str], cwd: Path, env: dict[str, str]) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
cwd=cwd,
env=env,
capture_output=True,
text=True,
check=False,
)
def project_from_archive(destination: Path) -> Path:
with tarfile.open(ARCHIVE, "r:gz") as archive:
archive.extractall(destination)
top = archive.getnames()[0].split("/", 1)[0]
project = destination / top / "data" / "formal_problem_solving"
packages = project / ".lake" / "packages"
if packages.is_symlink():
packages.unlink()
packages.mkdir(parents=True, exist_ok=True)
return project
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"--project",
type=Path,
help="already prepared formal_problem_solving project; skips extraction and Lake update/build",
)
args = parser.parse_args()
lake = os.environ.get("LAKE", "/Users/sshpro/.elan/bin/lake")
if not Path(lake).exists():
lake = shutil.which("lake") or lake
if not Path(lake).exists():
raise SystemExit("lake executable is unavailable")
basic_bytes = None
project = args.project.resolve() if args.project else None
with tempfile.TemporaryDirectory(prefix="fps-full-audit-") as temporary:
if project is None:
project = project_from_archive(Path(temporary))
update = run([lake, "update", "-v"], project, os.environ.copy())
if update.returncode:
raise SystemExit(update.stdout + update.stderr)
build = run([lake, "build"], project, os.environ.copy())
if build.returncode:
raise SystemExit(build.stdout + build.stderr)
basic = project / FILES[0]
basic_bytes = basic.read_bytes()
if hashlib.sha256(basic_bytes).hexdigest() != EXPECTED_BASIC_SHA256:
raise SystemExit("archived Basic.lean hash does not match the pinned source")
results = {}
environment = os.environ.copy()
for relative in FILES:
completed = run([lake, "env", "lean", relative], project, environment)
results[relative] = {
"exit_status": completed.returncode,
"stdout": completed.stdout,
"stderr": completed.stderr,
}
if completed.returncode:
raise SystemExit(json.dumps(results, indent=2, sort_keys=True))
print(
json.dumps(
{
"basic_sha256": hashlib.sha256(basic_bytes).hexdigest(),
"lake_project": str(project),
"manifest": "lake-manifest.json",
"official_files": results,
"full_dependency_environment": True,
},
indent=2,
sort_keys=True,
)
)
if __name__ == "__main__":
main()