Skip to content

_stage

Stage runtime tree structure.

Functions

system_fingerprint()

Returns '{os}-{arch}-r{major}.{minor}' string.

Source code in brmspy/_runtime/_platform.py
def system_fingerprint() -> str:
    """Returns '{os}-{arch}-r{major}.{minor}' string."""
    os_name = get_os()
    arch = get_arch()
    r_ver = get_r_version()
    if not r_ver:
        raise RuntimeError("R version could not be determined")
    major, minor, _ = r_ver
    return f"{os_name}-{arch}-r{major}.{minor}"

log(*msg, method_name=None, level=logging.INFO)

Log a message with automatic method name detection.

Parameters:

Name Type Description Default
msg str

The message to log

()
method_name str

The name of the method/function. If None, will auto-detect from call stack.

None
level int

Logging level (default: logging.INFO)

INFO
Source code in brmspy/helpers/log.py
def log(*msg: str, method_name: str | None = None, level: int = logging.INFO):
    """
    Log a message with automatic method name detection.

    Parameters
    ----------
    msg : str
        The message to log
    method_name : str, optional
        The name of the method/function. If None, will auto-detect from call stack.
    level : int, optional
        Logging level (default: logging.INFO)
    """
    if method_name is None:
        method_name = _get_caller_name()

    msg_str = " ".join(str(v) for v in msg)

    logger = get_logger()
    logger.log(level, msg_str, extra={"method_name": method_name})

_generate_manifest_hash(manifest)

Generate deterministic SHA256 hash of runtime manifest.

Source code in brmspy/_build/_stage.py
def _generate_manifest_hash(manifest: dict) -> str:
    """Generate deterministic SHA256 hash of runtime manifest."""
    manifest_string = json.dumps(manifest, sort_keys=True, separators=(',', ':'))
    return hashlib.sha256(manifest_string.encode('utf-8')).hexdigest()

stage_runtime_tree(base_dir, metadata, runtime_version)

Create runtime directory structure and copy all required files.

Builds the complete runtime directory tree by: 1. Creating fingerprint-specific directory structure 2. Copying all R packages to Rlib/ 3. Copying CmdStan installation to cmdstan/ 4. Generating manifest.json with checksums

Parameters:

Name Type Description Default
base_dir Path

Base directory for runtime tree

required
metadata dict

Metadata from collect_runtime_metadata()

required
runtime_version str

Runtime schema version (e.g., "0.1.0")

required

Returns:

Type Description
Path

Path to the runtime root directory

Source code in brmspy/_build/_stage.py
def stage_runtime_tree(base_dir: Path, metadata: dict, runtime_version: str) -> Path:
    """
    Create runtime directory structure and copy all required files.

    Builds the complete runtime directory tree by:
    1. Creating fingerprint-specific directory structure
    2. Copying all R packages to Rlib/
    3. Copying CmdStan installation to cmdstan/
    4. Generating manifest.json with checksums

    Parameters
    ----------
    base_dir : Path
        Base directory for runtime tree
    metadata : dict
        Metadata from collect_runtime_metadata()
    runtime_version : str
        Runtime schema version (e.g., "0.1.0")

    Returns
    -------
    Path
        Path to the runtime root directory
    """
    fingerprint = system_fingerprint()
    if fingerprint is None:
        raise RuntimeError("system_fingerprint() returned None; cannot build runtime bundle.")

    runtime_root = base_dir / f"{fingerprint}-{runtime_version}"
    rlib_dir = runtime_root / "Rlib"
    cmdstan_dir = runtime_root / "cmdstan"

    runtime_root.mkdir(parents=True, exist_ok=True)
    rlib_dir.mkdir(parents=True, exist_ok=True)

    # Copy R packages into Rlib/
    pkgs = metadata.get("packages", [])
    if not pkgs:
        raise RuntimeError("No package metadata returned from R; cannot build runtime.")

    for pkg in pkgs:
        name = pkg["Package"]
        libpath = pkg["LibPath"]
        src = Path(libpath) / name
        dest = rlib_dir / name

        if not src.exists():
            raise RuntimeError(f"Package directory not found: {src}")

        log(f"[Rlib] Copying {name} from {src} to {dest}")
        shutil.copytree(src, dest, dirs_exist_ok=True)

    # Copy CmdStan tree into cmdstan/
    cmdstan_path = Path(metadata["cmdstan_path"])
    if not cmdstan_path.exists():
        raise RuntimeError(f"cmdstan_path does not exist on disk: {cmdstan_path}")

    log(f"[cmdstan] Copying CmdStan from {cmdstan_path} to {cmdstan_dir}")
    shutil.copytree(cmdstan_path, cmdstan_dir, dirs_exist_ok=True)


    # On macOS, drop precompiled headers (PCH) to avoid SDK/PCH mismatch issues
    if sys.platform == "darwin":
        model_dir = cmdstan_dir / "stan" / "src" / "stan" / "model"
        if model_dir.exists():
            for entry in model_dir.iterdir():
                # Clang stores PCH in dirs like "model_header.hpp.gch/"
                if entry.is_dir() and entry.name.endswith(".hpp.gch"):
                    log(f"[cmdstan] Removing PCH directory on macOS: {entry}")
                    shutil.rmtree(entry, ignore_errors=True)

    # Write manifest.json
    r_pkg_versions = {pkg["Package"]: pkg["Version"] for pkg in pkgs}

    manifest = {
        "runtime_version": runtime_version,
        "fingerprint": fingerprint,
        "r_version": metadata["r_version"],
        "cmdstan_version": metadata["cmdstan_version"],
        "r_packages": r_pkg_versions
    }

    hash_val = _generate_manifest_hash(manifest)
    manifest['manifest_hash'] = hash_val
    manifest['built_at'] = datetime.now(timezone.utc).isoformat()

    manifest_path = runtime_root / "manifest.json"
    with manifest_path.open("w", encoding="utf-8") as f:
        json.dump(manifest, f, indent=2)
    log(f"[manifest] Wrote {manifest_path}")

    return runtime_root