diff --git a/scripts/codex_package/README.md b/scripts/codex_package/README.md index 6eb45eef47..c4abb58afd 100644 --- a/scripts/codex_package/README.md +++ b/scripts/codex_package/README.md @@ -22,6 +22,12 @@ The builder creates a canonical Codex package directory: The package directory is the primary artifact. Archive formats such as `.tar.gz`, `.tar.zst`, and `.zip` are serializations of that directory. +If `--target` is omitted, the builder uses the release target for the current +host platform. On Linux, that default is a musl target to match Codex release +artifacts; pass a GNU Linux target explicitly for native glibc local builds. If +`--package-dir` is omitted, the builder creates a new temporary directory and +prints its path after the package is built. + ## Source-built artifacts Artifacts built from this repository are always built by the package builder in @@ -32,7 +38,8 @@ one grouped `cargo build` command per package: - Windows targets: `codex-command-runner` and `codex-windows-sandbox-setup` The default cargo profile is `dev-small` because local iteration should favor -fast, small builds. Release jobs should pass `--cargo-profile release`. +fast, small builds. Release jobs should pass `--cargo-profile release` and an +explicit target. `rg` is not built from this repository, so the builder fetches it from the DotSlash manifest at `codex-cli/bin/rg`. Downloaded archives are cached under diff --git a/scripts/codex_package/cli.py b/scripts/codex_package/cli.py index c80f50a1a0..6b2598445f 100644 --- a/scripts/codex_package/cli.py +++ b/scripts/codex_package/cli.py @@ -1,6 +1,7 @@ """Command-line interface for building Codex package directories.""" import argparse +import tempfile from pathlib import Path from .archive import write_archive @@ -11,6 +12,7 @@ from .layout import validate_package_dir from .ripgrep import resolve_rg_bin from .targets import TARGET_SPECS from .targets import PackageInputs +from .targets import default_target def parse_args() -> argparse.Namespace: @@ -20,9 +22,12 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( "--target", - required=True, + default=argparse.SUPPRESS, choices=sorted(TARGET_SPECS), - help="Rust target triple for the package.", + help=( + "Rust target triple for the package. Defaults to the release target " + "for this host platform." + ), ) parser.add_argument( "--version", @@ -37,8 +42,11 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--package-dir", type=Path, - required=True, - help="Output directory to create as the package root.", + default=argparse.SUPPRESS, + help=( + "Output directory to create as the package root. Defaults to a new " + "temporary directory." + ), ) parser.add_argument( "--archive-output", @@ -79,8 +87,13 @@ def parse_args() -> argparse.Namespace: def main() -> int: args = parse_args() - spec = TARGET_SPECS[args.target] - package_dir = args.package_dir.resolve() + spec = TARGET_SPECS[getattr(args, "target", None) or default_target()] + package_dir_arg = getattr(args, "package_dir", None) + package_dir = ( + package_dir_arg.resolve() + if package_dir_arg is not None + else Path(tempfile.mkdtemp(prefix="codex-package-")).resolve() + ) source_outputs = build_source_binaries( spec, diff --git a/scripts/codex_package/targets.py b/scripts/codex_package/targets.py index 0cb964a4cc..d4f85953c2 100644 --- a/scripts/codex_package/targets.py +++ b/scripts/codex_package/targets.py @@ -1,5 +1,6 @@ """Supported package targets and default binary discovery.""" +import platform import stat from dataclasses import dataclass from pathlib import Path @@ -39,12 +40,24 @@ class PackageInputs: TARGET_SPECS: dict[str, TargetSpec] = { + "x86_64-unknown-linux-gnu": TargetSpec( + target="x86_64-unknown-linux-gnu", + is_windows=False, + is_linux=True, + dotslash_platform="linux-x86_64", + ), "x86_64-unknown-linux-musl": TargetSpec( target="x86_64-unknown-linux-musl", is_windows=False, is_linux=True, dotslash_platform="linux-x86_64", ), + "aarch64-unknown-linux-gnu": TargetSpec( + target="aarch64-unknown-linux-gnu", + is_windows=False, + is_linux=True, + dotslash_platform="linux-aarch64", + ), "aarch64-unknown-linux-musl": TargetSpec( target="aarch64-unknown-linux-musl", is_windows=False, @@ -78,6 +91,29 @@ TARGET_SPECS: dict[str, TargetSpec] = { } +HOST_RELEASE_TARGETS: dict[tuple[str, str], str] = { + ("darwin", "aarch64"): "aarch64-apple-darwin", + ("darwin", "x86_64"): "x86_64-apple-darwin", + ("linux", "aarch64"): "aarch64-unknown-linux-musl", + ("linux", "x86_64"): "x86_64-unknown-linux-musl", + ("windows", "aarch64"): "aarch64-pc-windows-msvc", + ("windows", "x86_64"): "x86_64-pc-windows-msvc", +} + + +def default_target() -> str: + system = platform.system().lower() + machine = normalize_machine(platform.machine()) + target = HOST_RELEASE_TARGETS.get((system, machine)) + if target is None: + supported = ", ".join(sorted(TARGET_SPECS)) + raise RuntimeError( + f"Unsupported host platform {platform.system()}/{platform.machine()}. " + f"Pass --target explicitly. Supported targets: {supported}" + ) + return target + + def resolve_input_path( explicit_path: Path | None, description: str, @@ -96,3 +132,12 @@ def resolve_input_path( def is_executable(path: Path) -> bool: return bool(path.stat().st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)) + + +def normalize_machine(machine: str) -> str: + machine = machine.lower() + if machine in ("amd64", "x86_64"): + return "x86_64" + if machine in ("aarch64", "arm64"): + return "aarch64" + return machine