# SPDX-License-Identifier: AGPL-3.0-or-later
# Commercial license available
# © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
# © Code 2020–2026 Miroslav Šotek. All rights reserved.
# ORCID: 0009-0009-3560-0851
# Contact: www.anulum.li | protoscience@anulum.li
# SCPN Fusion Core — pinned RustBCA native reference build
"""Build an optional RustBCA wheel and retain source-to-binary observations."""
from __future__ import annotations
import argparse
import hashlib
import importlib
import importlib.metadata
import json
import os
import signal
import subprocess
import tarfile
import time
from pathlib import Path
from typing import Any, BinaryIO, cast
from validation.rustbca_build_receipt import (
CARGO_LOCK_SHA256,
SOURCE_COMMIT,
SOURCE_MANIFEST_SHA256,
verify_rustbca_build_receipt,
)
ARCHIVE_SHA256 = "3aded40a7a3282ac41e4a1c90198511f26bf8d418ec2994914b6bf930424f4b7"
def _sha(path: Path) -> str:
"""Return the SHA-256 digest of the exact retained file bytes."""
return hashlib.sha256(path.read_bytes()).hexdigest()
def _wheel(binary: Path, source: Path, output: Path, runtime: dict[str, str]) -> Path:
"""Package the local platform extension with upstream licence and source identity."""
tag = f"{runtime['interpreter']}-{runtime['abi']}-{runtime['platform']}"
destination = output / f"rustbca-3.0.0-{tag}.whl"
info = "rustbca-3.0.0.dist-info/"
wheel_file = importlib.import_module("wheel.wheelfile").WheelFile
with wheel_file(str(destination), "w") as archive:
archive.writestr("libRustBCA" + runtime["suffix"], binary.read_bytes())
archive.writestr(
info + "WHEEL",
"Wheel-Version: 1.0\nGenerator: scpn-rustbca-build\nRoot-Is-Purelib: false\nTag: "
+ tag
+ "\n",
)
archive.writestr(
info + "METADATA",
"Metadata-Version: 2.1\nName: RustBCA\nVersion: 3.0.0\nLicense: GPLv3 (see included upstream LICENSE)\nHome-page: https://github.com/lcpp-org/RustBCA\n\nLocally compiled reference from upstream commit "
+ SOURCE_COMMIT
+ ".\n",
)
archive.writestr(info + "LICENSE", (source / "LICENSE").read_bytes())
return destination
def _run_cargo_phase(
command: list[str],
source: Path,
output: Path,
env: dict[str, str],
deadline: float,
record: dict[str, Any],
phase: str,
log_name: str,
) -> None:
"""Run one Cargo phase within the shared deadline and account for its process group."""
remaining = deadline - time.monotonic()
if remaining <= 0:
raise subprocess.TimeoutExpired(command, 0)
with (output / log_name).open("wb") as log:
child = subprocess.Popen(
command,
cwd=source,
env=env,
stdout=log,
stderr=subprocess.STDOUT,
start_new_session=True,
)
try:
record[phase + "_pid"] = child.pid
record[phase + "_pgid"] = child.pid
name = "process.json" if phase == "compiler" else phase + "-process.json"
(output / name).write_text(
json.dumps({"pid": child.pid, "pgid": child.pid, "deadline_monotonic": deadline})
+ "\n"
)
code = child.wait(timeout=max(0.001, deadline - time.monotonic()))
if code:
raise subprocess.CalledProcessError(code, command)
except BaseException:
cleanup_deadline = time.monotonic() + 5.0
record[phase + "_cleanup_deadline_monotonic"] = cleanup_deadline
try:
os.killpg(child.pid, signal.SIGKILL)
except BaseException as cleanup_error:
record[phase + "_signal_error"] = {
"type": type(cleanup_error).__name__,
"message": str(cleanup_error),
}
try:
child.wait(timeout=max(0.001, cleanup_deadline - time.monotonic()))
except BaseException as cleanup_error:
record[phase + "_reap_error"] = {
"type": type(cleanup_error).__name__,
"message": str(cleanup_error),
}
raise
finally:
record[phase + "_returncode"] = child.returncode
record[phase + "_reaped"] = child.returncode is not None
record[phase + "_log_sha256"] = _sha(output / log_name)
[docs]
def build_rustbca_reference(
source_archive: Path,
output: Path,
*,
python: Path,
cargo: Path,
rustc: Path,
timeout_seconds: float = 600.0,
fetch_dependencies: bool = False,
) -> dict[str, Any]:
"""Build a fresh frozen native library, local-platform wheel and custody receipt.
Parameters
----------
source_archive : Path
Exact pinned upstream tar.gz, already acquired by the caller.
output : Path
New retained build directory. Existing directories are never overwritten.
python : Path
CPython interpreter whose ABI the optional extension targets.
cargo, rustc : Path
Explicit compiler executable paths, hashed before and after compilation.
Required dependencies must already be available to Cargo's frozen resolver.
timeout_seconds : float
Finite total subprocess budget in seconds, greater than zero and at most 1800.
Failed phases additionally allow at most five seconds to reap their leader;
failure receipts retain the PID and any signalling or reaping error.
fetch_dependencies : bool
Explicitly permit Cargo fetch --locked before frozen compilation. Both phases
share the deadline; downloaded dependencies remain pinned by the lockfile.
Returns
-------
dict[str, Any]
Local observation binding sources, compiler identities, binary and wheel.
This does not certify hermetic builds, independent attestation or physics.
Raises
------
ValueError
For invalid limits, source identity, unsupported runtime or changed inputs.
FileExistsError
If output already exists.
subprocess.SubprocessError
If a compiler/probe fails or exceeds the remaining deadline.
OSError
If required input files or executables are unavailable.
Notes
-----
The builder uses two Cargo jobs and a fresh target directory. On failure it
retains result.json with a failure status, signals its compiler process group
and attempts bounded leader reaping. The receipt distinguishes an unreaped
leader from successful reaping; reaping alone does not prove group absence.
wheel 0.47.0 is required to write checked RECORD entries. The generated
wheel uses the local platform tag, never an unaudited manylinux claim.
"""
if type(timeout_seconds) not in (int, float) or not 0 < timeout_seconds <= 1800:
raise ValueError("timeout_seconds must be finite and in (0, 1800]")
if type(fetch_dependencies) is not bool:
raise ValueError("fetch_dependencies must be a boolean")
if importlib.metadata.version("wheel") != "0.47.0":
raise ValueError("RustBCA wheel writer requires wheel==0.47.0")
if _sha(source_archive) != ARCHIVE_SHA256:
raise ValueError("RustBCA archive digest mismatch")
data = Path(__file__).resolve().parents[1] / "validation/reference_data"
files = json.loads((data / "rustbca_source_files.json").read_bytes())
if (
hashlib.sha256(
json.dumps(files, sort_keys=True, separators=(",", ":")).encode()
).hexdigest()
!= SOURCE_MANIFEST_SHA256
):
raise ValueError("RustBCA source inventory mismatch")
lock = data / "rustbca.Cargo.lock"
if _sha(lock) != CARGO_LOCK_SHA256:
raise ValueError("RustBCA Cargo lock mismatch")
output = output.absolute()
python, cargo, rustc = python.absolute(), cargo.absolute(), rustc.absolute()
output.mkdir(parents=True, exist_ok=False)
started = time.time()
deadline = time.monotonic() + timeout_seconds
record: dict[str, Any] = {
"schema": "scpn-fusion.rustbca-native-build-observation.v1",
"started_unix": started,
"returncode": -1,
}
try:
source = output / "source"
source.mkdir()
seen = set()
with tarfile.open(source_archive, "r:gz") as archive:
for member in archive:
if member.isdir():
continue
prefix, _, name = member.name.partition("/")
if (
prefix != "RustBCA-" + SOURCE_COMMIT
or name not in files
or name in seen
or not member.isfile()
):
raise ValueError("Unexpected RustBCA source archive member")
# isfile() above establishes extractfile()'s regular-file return type.
with cast(BinaryIO, archive.extractfile(member)) as stream:
raw = stream.read()
if hashlib.sha256(raw).hexdigest() != files[name]:
raise ValueError("RustBCA archive member digest mismatch")
target = source / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(raw)
seen.add(name)
if seen != set(files):
raise ValueError("Incomplete RustBCA source archive")
(source / "Cargo.lock").write_bytes(lock.read_bytes())
recipe = output / "build_source.py"
recipe.write_bytes(Path(__file__).read_bytes())
env = {
key: os.environ[key]
for key in ("HOME", "PATH", "CARGO_HOME", "RUSTUP_HOME")
if key in os.environ
}
env.update(
RUSTC=str(rustc),
PYO3_PYTHON=str(python),
CARGO_TARGET_DIR=str(output / "target"),
CARGO_INCREMENTAL="0",
LC_ALL="C",
)
query = "import sys,sysconfig,json; print(json.dumps({'implementation':sys.implementation.name,'interpreter':'cp'+str(sys.version_info.major)+str(sys.version_info.minor),'abi':'cp'+sysconfig.get_config_var('SOABI').split('-')[1],'platform':sysconfig.get_platform().replace('-','_').replace('.','_'),'suffix':sysconfig.get_config_var('EXT_SUFFIX')}))"
runtime = json.loads(
subprocess.check_output(
[str(python), "-c", query],
env=env,
text=True,
timeout=max(0.001, deadline - time.monotonic()),
)
)
if runtime["implementation"] != "cpython" or not runtime["platform"].startswith("linux_"):
raise ValueError("RustBCA build currently requires a Linux CPython runtime")
command = [
str(cargo),
"build",
"--frozen",
"--release",
"--lib",
"--features",
"python,parry3d,pythonize,pyo3/extension-module",
"--jobs",
"2",
]
record.update(
upstream={
"repository": "https://github.com/lcpp-org/RustBCA",
"commit": SOURCE_COMMIT,
"archive_sha256": ARCHIVE_SHA256,
"license_sha256": files["LICENSE"],
},
source_files=files,
cargo_lock_sha256=CARGO_LOCK_SHA256,
command=command,
environment=env,
cargo_sha256=_sha(cargo),
rustc_sha256=_sha(rustc),
python_sha256=_sha(python.resolve()),
recipe_sha256=_sha(recipe),
wheel_writer_version="0.47.0",
runtime=runtime,
fetch_dependencies=fetch_dependencies,
independent_attestation_verified=False,
reproducible_binary_verified=False,
)
(output / "inputs.json").write_text(json.dumps(record, indent=2) + "\n")
if fetch_dependencies:
fetch_command = [str(cargo), "fetch", "--locked"]
record["dependency_fetch_command"] = fetch_command
_run_cargo_phase(
fetch_command,
source,
output,
env,
deadline,
record,
"dependency_fetch",
"dependency-fetch.log",
)
if _sha(source / "Cargo.lock") != CARGO_LOCK_SHA256:
raise ValueError("RustBCA dependency fetch changed the locked graph")
_run_cargo_phase(command, source, output, env, deadline, record, "compiler", "build.log")
binary = output / "target/release/liblibRustBCA.so"
record.update(
returncode=0,
binary=str(binary),
binary_sha256=_sha(binary),
log_sha256=_sha(output / "build.log"),
source_unchanged=all(_sha(source / name) == digest for name, digest in files.items()),
lock_unchanged=_sha(source / "Cargo.lock") == CARGO_LOCK_SHA256,
tools_unchanged=_sha(cargo) == record["cargo_sha256"]
and _sha(rustc) == record["rustc_sha256"]
and _sha(python.resolve()) == record["python_sha256"],
)
result = output / "result.json"
result.write_text(json.dumps(record, indent=2) + "\n")
verify_rustbca_build_receipt(
result,
expected_receipt_sha256=_sha(result),
expected_binary_sha256=record["binary_sha256"],
)
wheel = _wheel(binary, source, output, runtime)
record.update(
wheel=str(wheel),
wheel_sha256=_sha(wheel),
wheel_built=True,
finished_unix=time.time(),
status="complete_local_observation",
)
result.write_text(json.dumps(record, indent=2) + "\n")
return record
except BaseException as exc:
record.update(
returncode=-1,
status="failed",
error_type=type(exc).__name__,
error=str(exc),
finished_unix=time.time(),
)
(output / "result.json").write_text(json.dumps(record, indent=2) + "\n")
raise
[docs]
def main() -> None:
"""Build the pinned optional reference from caller-selected local tools and archive."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("archive", type=Path)
parser.add_argument("output", type=Path)
parser.add_argument("--python", type=Path, required=True)
parser.add_argument("--cargo", type=Path, required=True)
parser.add_argument("--rustc", type=Path, required=True)
parser.add_argument("--timeout-seconds", type=float, default=600.0)
parser.add_argument("--fetch-dependencies", action="store_true")
args = parser.parse_args()
build_rustbca_reference(
args.archive,
args.output,
python=args.python,
cargo=args.cargo,
rustc=args.rustc,
timeout_seconds=args.timeout_seconds,
fetch_dependencies=args.fetch_dependencies,
)
if __name__ == "__main__":
main()