mirror of
https://github.com/openai/codex.git
synced 2026-09-10 20:26:47 +00:00
## What changed Add the manual `//third_party/voice:native_runtime` target for macOS and GNU Linux. It consumes the native prefix archive, validates the completed build receipt against the target and source manifest, inspects libraries, and prepares verified copies using the existing platform policies. Record the build commit through Bazel workspace status and include it in runtime receipts. Reject invalid or ambiguous commit metadata and escaping library aliases. Keep macOS preparation local and allow the system `libc++.1.dylib` dependency. ## Testing Add tests for receipt validation, inspection ordering and failures, invalid commit metadata, and escaping archive links. Add a macOS integration test for preparing real libraries from a sandbox-linked archive with native aliases and either an absent or empty output directory. GitOrigin-RevId: 23717ab33d1d3da5fb0042c1cc8e8efcd333c386
169 lines
5.9 KiB
Python
169 lines
5.9 KiB
Python
"""Project a verified macOS prefix into a private, relocatable development runtime."""
|
|
|
|
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
# Import only this script's siblings, including under PYTHONSAFEPATH.
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
from runtime import Binary as MachO
|
|
from runtime import PLUGINS, RuntimeFormat, digest, prepare, required_library_paths
|
|
|
|
SYSTEM_IMPORTS = frozenset(
|
|
{
|
|
"/usr/lib/libSystem.B.dylib",
|
|
"/usr/lib/libc++.1.dylib",
|
|
"/usr/lib/libobjc.A.dylib",
|
|
"/usr/lib/libiconv.2.dylib",
|
|
"/usr/lib/libresolv.9.dylib",
|
|
"/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit",
|
|
"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
|
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
|
|
"/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
|
|
}
|
|
)
|
|
|
|
|
|
def run(command):
|
|
return subprocess.check_output(
|
|
command, text=True, stderr=subprocess.STDOUT, timeout=30
|
|
)
|
|
|
|
|
|
def inspect(path, target):
|
|
cpu = {"aarch64-apple-darwin": "ARM64", "x86_64-apple-darwin": "X86_64"}[target]
|
|
try:
|
|
output = run(
|
|
[
|
|
"/usr/bin/xcrun",
|
|
"llvm-objdump",
|
|
"--macho",
|
|
"--universal-headers",
|
|
"--private-headers",
|
|
str(path),
|
|
]
|
|
)
|
|
except subprocess.CalledProcessError as error:
|
|
raise ValueError("LLVM rejected the Mach-O library") from error
|
|
header, *commands = re.split(r"(?m)^Load command [0-9]+\n", output)
|
|
match = re.fullmatch(
|
|
r"Mach header\n[^\n]+\nMH_MAGIC_64 +"
|
|
+ cpu
|
|
+ r" +\S+ +\S+ +DYLIB +(\d+) +(\d+) +[^\n]+\n",
|
|
header.removeprefix(str(path) + ":\n"),
|
|
)
|
|
if not match or len(commands) != int(match[1]) or int(match[2]) > 1024 * 1024:
|
|
raise ValueError(f"expected a thin {target} dylib with bounded load commands")
|
|
identities, imports, rpaths = [], [], []
|
|
for block in commands:
|
|
match = re.match(r" +cmd (\S+)\n +cmdsize ([0-9]+)\n", block)
|
|
if not match:
|
|
raise ValueError("unrecognized LLVM load-command output")
|
|
command = match[1]
|
|
if command in {
|
|
"LC_PREBOUND_DYLIB",
|
|
"LC_REEXPORT_DYLIB",
|
|
"LC_LAZY_LOAD_DYLIB",
|
|
"LC_LOAD_UPWARD_DYLIB",
|
|
"LC_DYLD_ENVIRONMENT",
|
|
"LC_DYLIB_CODE_SIGN_DRS",
|
|
"LC_LAZY_LOAD_DYLIB_INFO",
|
|
"?(0x0000003a)",
|
|
}:
|
|
raise ValueError(f"unsupported loader command: {command}")
|
|
if command not in {
|
|
"LC_ID_DYLIB",
|
|
"LC_LOAD_DYLIB",
|
|
"LC_LOAD_WEAK_DYLIB",
|
|
"LC_RPATH",
|
|
}:
|
|
continue
|
|
# Match the entire command to reject multiline names masquerading as metadata.
|
|
pattern = r" +path ([^\r\n]+) \(offset [0-9]+\)\n"
|
|
if command != "LC_RPATH":
|
|
pattern = (
|
|
r" +name ([^\r\n]+) \(offset [0-9]+\)\n"
|
|
r" +time stamp [^\n]+\n +current version [^\n]+\ncompatibility version [^\n]+\n"
|
|
)
|
|
value = re.fullmatch(pattern, block[match.end() :])
|
|
if not value:
|
|
raise ValueError(
|
|
"undeclared native dependency or unrecognized LLVM loader output"
|
|
)
|
|
if command == "LC_ID_DYLIB":
|
|
identities.append(value[1])
|
|
elif command == "LC_RPATH":
|
|
rpaths.append(value[1])
|
|
else:
|
|
imports.append(value[1])
|
|
if len(identities) != 1 or not re.fullmatch(
|
|
r"[A-Za-z0-9_+.-]+\.dylib", Path(identities[0]).name
|
|
):
|
|
raise ValueError("expected one valid native library identity")
|
|
return MachO(identities[0], tuple(imports), tuple(rpaths))
|
|
|
|
|
|
def finalize_copy(destination, metadata, dependency_paths):
|
|
identity = f"@rpath/{destination.name}"
|
|
command = ["/usr/bin/install_name_tool", "-id", identity]
|
|
imports = []
|
|
for dependency in metadata.imports:
|
|
rewritten = dependency
|
|
if dependency not in SYSTEM_IMPORTS:
|
|
rewritten = "@loader_path/" + os.path.relpath(
|
|
dependency_paths[dependency], destination.parent
|
|
)
|
|
command.extend(["-change", dependency, rewritten])
|
|
imports.append(rewritten)
|
|
for rpath in metadata.rpaths:
|
|
command.extend(["-delete_rpath", rpath])
|
|
run([*command, str(destination)])
|
|
# Development code signatures only; no identity, entitlement or trust-policy changes.
|
|
run(
|
|
[
|
|
"/usr/bin/codesign",
|
|
"--force",
|
|
"--sign",
|
|
"-",
|
|
"--timestamp=none",
|
|
str(destination),
|
|
]
|
|
)
|
|
run(["/usr/bin/codesign", "--verify", "--strict", str(destination)])
|
|
return MachO(identity, tuple(imports), ())
|
|
|
|
|
|
def project(prefix, receipts, target, output):
|
|
if sys.platform != "darwin" or target not in (
|
|
"aarch64-apple-darwin",
|
|
"x86_64-apple-darwin",
|
|
):
|
|
raise ValueError(
|
|
"runtime projection requires macOS and an explicit macOS target"
|
|
)
|
|
format = RuntimeFormat(
|
|
tuple(
|
|
Path(f"lib/gstreamer-1.0/libgst{name}.dylib") for name in sorted(PLUGINS)
|
|
),
|
|
SYSTEM_IMPORTS,
|
|
inspect,
|
|
finalize_copy,
|
|
required_libraries=tuple(
|
|
Path(path).name for path in required_library_paths(target)
|
|
),
|
|
)
|
|
prepare(prefix, receipts, target, output, format)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--prefix", type=Path, required=True)
|
|
parser.add_argument("--receipts", type=Path, required=True)
|
|
parser.add_argument("--target", required=True)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
project(args.prefix, args.receipts, args.target, args.output)
|