Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .buckconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[cells]
root = .
toolchains = codex-rs/toolchains
prelude = prelude
none = none

[cell_aliases]
# Buck2 prelude expects some common aliases to exist in some environments.
config = prelude
ovr_config = prelude
fbcode = none
fbsource = none
fbcode_macros = none
buck = none

# Use the Buck2 prelude bundled with the buck2 binary.
[external_cells]
prelude = bundled

[parser]
target_platform_detector_spec = target://...->prelude//platforms:default

[codex]
# Local-only knob used by codex-rs/buck2 to approximate Cargo profiles.
# Override on the command line with:
# ./scripts/buck2 build -c codex.rust_profile=release //codex-rs/cli:codex
rust_profile = dev

[build]
execution_platforms = prelude//platforms:default
1 change: 1 addition & 0 deletions .buckroot
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

56 changes: 56 additions & 0 deletions .github/workflows/buck2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Buck2 (Experimental, non-blocking for PRs)

on:
pull_request: {}
push:
branches:
- main
workflow_dispatch:

concurrency:
group: buck2-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
buck2-test:
name: buck2 test //codex-rs/...
runs-on: ubuntu-24.04
# Non-blocking while Buck2 support is still experimental.
# continue-on-error: true
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v4

# scripts/buck2, scripts/reindeer, etc. are DotSlash wrappers.
- name: Install DotSlash
uses: facebook/install-dotslash@v2

- name: Setup Rust toolchain
uses: dtolnay/[email protected]
with:
# Match codex-rs/rust-toolchain.toml (and include rust-src for Buck2 toolchains).
components: rustfmt, clippy, rust-src

- name: Install system deps (Linux)
shell: bash
run: |
set -euxo pipefail
sudo apt-get update
sudo apt-get install -y pkg-config libssl-dev

- name: Setup Buck2 (local)
shell: bash
run: |
set -euxo pipefail
./scripts/setup_buck2_local.sh

- name: Run Buck2 tests
shell: bash
run: |
set -euxo pipefail
./scripts/buck2 test -c test.rule_timeout_ms=1800000 --test-executor-stdout=- --test-executor-stderr=- //codex-rs/...
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ build/
out/
storybook-static/

# buck2
buck-out/
**/BUCK
codex-rs/third-party/
codex-rs/cli/Cargo.lock

# ignore README for publishing
codex-cli/README.md

Expand Down Expand Up @@ -89,4 +95,3 @@ CHANGELOG.ignore.md
# Python bytecode files
__pycache__/
*.pyc

1 change: 1 addition & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 99 additions & 0 deletions codex-rs/buck2/codex_rust_toolchain.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
load("@prelude//rust:rust_toolchain.bzl", "PanicRuntime", "RustToolchainInfo")

_DEFAULT_TRIPLE = select({
"prelude//os:linux": select({
"prelude//cpu:arm64": "aarch64-unknown-linux-gnu",
"prelude//cpu:riscv64": "riscv64gc-unknown-linux-gnu",
"prelude//cpu:x86_64": "x86_64-unknown-linux-gnu",
}),
"prelude//os:macos": select({
"prelude//cpu:arm64": "aarch64-apple-darwin",
"prelude//cpu:x86_64": "x86_64-apple-darwin",
}),
"prelude//os:windows": select({
"prelude//cpu:arm64": select({
# Rustup's default ABI for the host on Windows is MSVC, not GNU.
"DEFAULT": "aarch64-pc-windows-msvc",
"prelude//abi:gnu": "aarch64-pc-windows-gnu",
"prelude//abi:msvc": "aarch64-pc-windows-msvc",
}),
"prelude//cpu:x86_64": select({
"DEFAULT": "x86_64-pc-windows-msvc",
"prelude//abi:gnu": "x86_64-pc-windows-gnu",
"prelude//abi:msvc": "x86_64-pc-windows-msvc",
}),
}),
})


def _codex_rust_toolchain_impl(ctx):
# Buck doesn't have a built-in notion of "Cargo profiles", but it's useful
# to provide a simple local knob that roughly matches `cargo build` vs
# `cargo build --release`.
#
# Default is "dev" to match local development expectations.
rust_profile = read_config("codex", "rust_profile", "dev")
extra_rustc_flags = []
if rust_profile == "release":
# Roughly mirrors Cargo's release defaults (not a perfect match).
extra_rustc_flags = [
"-C",
"opt-level=3",
"-C",
"debuginfo=0",
]

return [
DefaultInfo(),
RustToolchainInfo(
allow_lints = ctx.attrs.allow_lints,
clippy_driver = RunInfo(args = [ctx.attrs.clippy_driver]),
clippy_toml = ctx.attrs.clippy_toml[DefaultInfo].default_outputs[0] if ctx.attrs.clippy_toml else None,
compiler = RunInfo(args = [ctx.attrs.rustc]),
default_edition = ctx.attrs.default_edition,
deny_lints = ctx.attrs.deny_lints,
doctests = ctx.attrs.doctests,
nightly_features = ctx.attrs.nightly_features,
panic_runtime = PanicRuntime("unwind"),
report_unused_deps = ctx.attrs.report_unused_deps,
rustc_binary_flags = ctx.attrs.rustc_binary_flags,
rustc_flags = ctx.attrs.rustc_flags + extra_rustc_flags,
rustc_target_triple = ctx.attrs.rustc_target_triple,
rustc_test_flags = ctx.attrs.rustc_test_flags,
rustdoc = RunInfo(args = [ctx.attrs.rustdoc]),
rustdoc_flags = ctx.attrs.rustdoc_flags,
warn_lints = ctx.attrs.warn_lints,
# Enable the prelude's "metadata-only rlib" behavior consistently
# across the crate graph. This avoids rustc "found possibly newer
# version of crate ..." (E0460) mismatches between binaries and
# libraries in large Rust graphs.
advanced_unstable_linking = ctx.attrs.advanced_unstable_linking,
),
]


codex_rust_toolchain = rule(
impl = _codex_rust_toolchain_impl,
attrs = {
"advanced_unstable_linking": attrs.bool(default = True),
"allow_lints": attrs.list(attrs.string(), default = []),
# Prefer explicit tool paths so the Buck execution directory doesn't
# affect rustup toolchain resolution.
"clippy_driver": attrs.string(default = "clippy-driver"),
"clippy_toml": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None),
"default_edition": attrs.option(attrs.string(), default = None),
"deny_lints": attrs.list(attrs.string(), default = []),
"doctests": attrs.bool(default = False),
"nightly_features": attrs.bool(default = False),
"report_unused_deps": attrs.bool(default = False),
"rustc": attrs.string(default = "rustc"),
"rustc_binary_flags": attrs.list(attrs.arg(), default = []),
"rustc_flags": attrs.list(attrs.arg(), default = []),
"rustc_target_triple": attrs.string(default = _DEFAULT_TRIPLE),
"rustc_test_flags": attrs.list(attrs.arg(), default = []),
"rustdoc": attrs.string(default = "rustdoc"),
"rustdoc_flags": attrs.list(attrs.arg(), default = []),
"warn_lints": attrs.list(attrs.string(), default = []),
},
is_toolchain_rule = True,
)
111 changes: 111 additions & 0 deletions codex-rs/buck2/reindeer_macros.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
load("@prelude//rust:cargo_buildscript.bzl", _prelude_buildscript_run = "buildscript_run")
load("@prelude//rust:cargo_package.bzl", "cargo")


def codex_noop_alias(**_kwargs):
# Reindeer normally emits aliases like `alias(name = "rand", actual = ":rand-0.8.5")`
# to provide stable unversioned target names. In a non-trivial workspace it's
# common to have multiple versions of the same crate in one graph, which
# leads to duplicate alias target names and buckification failures.
#
# For local Buck experiments (where we don't check in generated third-party
# BUCK files), it's simplest to disable these aliases and depend on
# versioned targets directly.
pass


def _codex_extra_srcs_for_manifest_dir(manifest_dir):
if not manifest_dir:
return []

# Use per-crate globs rooted at the crate's vendored manifest dir, which is
# passed through by Reindeer as CARGO_MANIFEST_DIR (e.g. vendor/foo-1.2.3).
#
# We include *all* files under the crate root so `include_str!` and
# `include_bytes!` work without per-crate Reindeer fixups. This is local-only
# buckification, so we prefer robustness over a minimal srcs list.
return glob(
["{}/**".format(manifest_dir)],
exclude = [
"{}/target/**".format(manifest_dir),
"{}/.git/**".format(manifest_dir),
],
)


def codex_rust_library(**kwargs):
# Make generated third-party targets consumable from anywhere in the repo.
kwargs["visibility"] = ["PUBLIC"]
env = kwargs.get("env", {})
manifest_dir = env.get("CARGO_MANIFEST_DIR")
srcs = list(kwargs.get("srcs", []))
srcs.extend(_codex_extra_srcs_for_manifest_dir(manifest_dir))
kwargs["srcs"] = srcs
cargo.rust_library(**kwargs)


def codex_rust_binary(**kwargs):
kwargs["visibility"] = ["PUBLIC"]
env = kwargs.get("env", {})
manifest_dir = env.get("CARGO_MANIFEST_DIR")
srcs = list(kwargs.get("srcs", []))
srcs.extend(_codex_extra_srcs_for_manifest_dir(manifest_dir))
kwargs["srcs"] = srcs
cargo.rust_binary(**kwargs)


def codex_buildscript_run(**kwargs):
# Many build scripts (especially those using `cc`/`cc-rs`) expect Cargo to
# provide a handful of profile env vars. Buck does not set these by default.
env = dict(kwargs.get("env", {}))
rust_profile = read_config("codex", "rust_profile", "dev")
if rust_profile == "release":
env.setdefault("OPT_LEVEL", "3")
env.setdefault("PROFILE", "release")
env.setdefault("DEBUG", "false")
else:
env.setdefault("OPT_LEVEL", "0")
env.setdefault("PROFILE", "debug")
env.setdefault("DEBUG", "true")

# Provide common Cargo cfg env vars that some build scripts expect.
env.setdefault(
"CARGO_CFG_TARGET_OS",
select({
"prelude//os:linux": "linux",
"prelude//os:macos": "macos",
"prelude//os:windows": "windows",
"DEFAULT": "",
}),
)
env.setdefault(
"CARGO_CFG_TARGET_ARCH",
select({
"prelude//cpu:arm64": "aarch64",
"prelude//cpu:x86_64": "x86_64",
"DEFAULT": "",
}),
)
env.setdefault("CARGO_CFG_TARGET_ENDIAN", "little")
env.setdefault(
"CARGO_CFG_TARGET_ENV",
select({
"prelude//os:linux": "gnu",
"DEFAULT": "",
}),
)

# Forward native link directives emitted by build scripts into rustc flags.
# Without this, crates like `ring` and `tree-sitter-*` will compile but fail
# to link due to missing native symbols.
kwargs.setdefault("rustc_link_lib", True)
kwargs.setdefault("rustc_link_search", True)

# `CARGO_MANIFEST_DIR` is expected by many build scripts. We can usually
# derive it from the `manifest_dir` parameter that buildscript_run uses.
manifest_dir = kwargs.get("manifest_dir")
if type(manifest_dir) == type(""):
env.setdefault("CARGO_MANIFEST_DIR", "$(location {})".format(manifest_dir))

kwargs["env"] = env
_prelude_buildscript_run(**kwargs)
1 change: 1 addition & 0 deletions codex-rs/linux-sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ libc = { workspace = true }
seccompiler = { workspace = true }

[target.'cfg(target_os = "linux")'.dev-dependencies]
codex-utils-cargo-bin = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = [
"io-std",
Expand Down
12 changes: 8 additions & 4 deletions codex-rs/linux-sandbox/tests/suite/landlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ async fn run_cmd(cmd: &[&str], writable_roots: &[PathBuf], timeout_ms: u64) {
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let sandbox_program = env!("CARGO_BIN_EXE_codex-linux-sandbox");
let codex_linux_sandbox_exe = Some(PathBuf::from(sandbox_program));
let codex_linux_sandbox_exe = Some(
codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox")
.expect("codex-linux-sandbox binary should be available for tests"),
);
let res = process_exec_tool_call(
params,
&sandbox_policy,
Expand Down Expand Up @@ -154,8 +156,10 @@ async fn assert_network_blocked(cmd: &[&str]) {
};

let sandbox_policy = SandboxPolicy::new_read_only_policy();
let sandbox_program = env!("CARGO_BIN_EXE_codex-linux-sandbox");
let codex_linux_sandbox_exe: Option<PathBuf> = Some(PathBuf::from(sandbox_program));
let codex_linux_sandbox_exe: Option<PathBuf> = Some(
codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox")
.expect("codex-linux-sandbox binary should be available for tests"),
);
let result = process_exec_tool_call(
params,
&sandbox_policy,
Expand Down
Loading
Loading