feat: add support for building with Bazel (#8875)

This PR configures Codex CLI so it can be built with
[Bazel](https://bazel.build) in addition to Cargo. The `.bazelrc`
includes configuration so that remote builds can be done using
[BuildBuddy](https://www.buildbuddy.io).

If you are familiar with Bazel, things should work as you expect, e.g.,
run `bazel test //... --keep-going` to run all the tests in the repo,
but we have also added some new aliases in the `justfile` for
convenience:

- `just bazel-test` to run tests locally
- `just bazel-remote-test` to run tests remotely (currently, the remote
build is for x86_64 Linux regardless of your host platform). Note we are
currently seeing the following test failures in the remote build, so we
still need to figure out what is happening here:

```
failures:
    suite::compact::manual_compact_twice_preserves_latest_user_messages
    suite::compact_resume_fork::compact_resume_after_second_compaction_preserves_history
    suite::compact_resume_fork::compact_resume_and_fork_preserve_model_history_view
```

- `just build-for-release` to build release binaries for all
platforms/architectures remotely

To setup remote execution:
- [Create a buildbuddy account](https://app.buildbuddy.io/) (OpenAI
employees should also request org access at
https://openai.buildbuddy.io/join/ with their `@openai.com` email
address.)
- [Copy your API key](https://app.buildbuddy.io/docs/setup/) to
`~/.bazelrc` (add the line `build
--remote_header=x-buildbuddy-api-key=YOUR_KEY`)
- Use `--config=remote` in your `bazel` invocations (or add `common
--config=remote` to your `~/.bazelrc`, or use the `just` commands)

## CI

In terms of CI, this PR introduces `.github/workflows/bazel.yml`, which
uses Bazel to run the tests _locally_ on Mac and Linux GitHub runners
(we are working on supporting Windows, but that is not ready yet). Note
that the failures we are seeing in `just bazel-remote-test` do not occur
on these GitHub CI jobs, so everything in `.github/workflows/bazel.yml`
is green right now.

The `bazel.yml` uses extra config in `.github/workflows/ci.bazelrc` so
that macOS CI jobs build _remotely_ on Linux hosts (using the
`docker://docker.io/mbolin491/codex-bazel` Docker image declared in the
root `BUILD.bazel`) using cross-compilation to build the macOS
artifacts. Then these artifacts are downloaded locally to GitHub's macOS
runner so the tests can be executed natively. This is the relevant
config that enables this:

```
common:macos --config=remote
common:macos --strategy=remote
common:macos --strategy=TestRunner=darwin-sandbox,local
```

Because of the remote caching benefits we get from BuildBuddy, these new
CI jobs can be extremely fast! For example, consider these two jobs that
ran all the tests on Linux x86_64:

- Bazel 1m37s
https://github.com/openai/codex/actions/runs/20861063212/job/59940545209?pr=8875
- Cargo 9m20s
https://github.com/openai/codex/actions/runs/20861063192/job/59940559592?pr=8875

For now, we will continue to run both the Bazel and Cargo jobs for PRs,
but once we add support for Windows and running Clippy, we should be
able to cutover to using Bazel exclusively for PRs, which should still
speed things up considerably. We will probably continue to run the Cargo
jobs post-merge for commits that land on `main` as a sanity check.

Release builds will also continue to be done by Cargo for now.

Earlier attempt at this PR: https://github.com/openai/codex/pull/8832
Earlier attempt to add support for Buck2, now abandoned:
https://github.com/openai/codex/pull/8504

---------

Co-authored-by: David Zbarsky <dzbarsky@gmail.com>
Co-authored-by: Michael Bolin <mbolin@openai.com>
This commit is contained in:
zbarsky-openai
2026-01-09 14:09:43 -05:00
committed by GitHub
parent 7daaabc795
commit 2a06d64bc9
69 changed files with 2256 additions and 0 deletions

0
patches/BUILD.bazel Normal file
View File

View File

@@ -0,0 +1,52 @@
diff --git a/toolchain/cc_toolchain.bzl b/toolchain/cc_toolchain.bzl
index 58da8ec..bf1fbdd 100644
--- a/toolchain/cc_toolchain.bzl
+++ b/toolchain/cc_toolchain.bzl
@@ -26,6 +26,7 @@ def cc_toolchain(name, tool_map):
cc_feature_set(
name = name + "_runtimes_only_known_features",
all_of = [
+ "//toolchain/features:archive_param_file",
# Always last (contains user_compile_flags and user_link_flags who should apply last).
"@rules_cc//cc/toolchains/args:experimental_replace_legacy_action_config_features",
],
@@ -45,6 +46,7 @@ def cc_toolchain(name, tool_map):
],
"@platforms//os:none": [],
}) + [
+ "//toolchain/features:archive_param_file",
"//toolchain/features/legacy:all_legacy_builtin_features",
# Always last (contains user_compile_flags and user_link_flags who should apply last).
"@rules_cc//cc/toolchains/args:experimental_replace_legacy_action_config_features",
@@ -54,6 +56,7 @@ def cc_toolchain(name, tool_map):
cc_feature_set(
name = name + "_runtimes_only_enabled_features",
all_of = [
+ "//toolchain/features:archive_param_file",
# Always last (contains user_compile_flags and user_link_flags who should apply last).
"@rules_cc//cc/toolchains/args:experimental_replace_legacy_action_config_features",
],
diff --git a/toolchain/features/BUILD.bazel b/toolchain/features/BUILD.bazel
index e787484..bccd45b 100644
--- a/toolchain/features/BUILD.bazel
+++ b/toolchain/features/BUILD.bazel
@@ -17,6 +17,11 @@ cc_feature(
overrides = "@rules_cc//cc/toolchains/features:static_link_cpp_runtimes",
)
+cc_feature(
+ name = "archive_param_file",
+ feature_name = "archive_param_file",
+)
+
cc_args(
name = "opt_link_flags",
actions = [
@@ -124,6 +129,7 @@ cc_feature(
cc_feature_set(
name = "all_non_legacy_builtin_features",
all_of = [
+ ":archive_param_file",
":opt",
":opt_stub",
":dbg",

34
patches/rules_rust.patch Normal file
View File

@@ -0,0 +1,34 @@
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index a28ad50b7..af627fe50 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -2361,19 +2361,19 @@ def _get_make_link_flag_funcs(target_os, target_abi, use_direct_link_driver):
- callable: The function for producing link args.
- callable: The function for formatting link library names.
"""
+
+ get_lib_name = get_lib_name_default
+
if target_os == "windows":
- make_link_flags_windows_msvc = _make_link_flags_windows_msvc_direct if use_direct_link_driver else _make_link_flags_windows_msvc_indirect
- make_link_flags_windows_gnu = _make_link_flags_windows_gnu_direct if use_direct_link_driver else _make_link_flags_windows_gnu_indirect
- make_link_flags = make_link_flags_windows_msvc if target_abi == "msvc" else make_link_flags_windows_gnu
- get_lib_name = get_lib_name_for_windows
+ if target_abi == "msvc":
+ make_link_flags = _make_link_flags_windows_msvc_direct if use_direct_link_driver else _make_link_flags_windows_msvc_indirect
+ get_lib_name = get_lib_name_for_windows
+ else:
+ make_link_flags = _make_link_flags_windows_gnu_direct if use_direct_link_driver else _make_link_flags_windows_gnu_indirect
elif target_os.startswith(("mac", "darwin", "ios")):
- make_link_flags_darwin = _make_link_flags_darwin_direct if use_direct_link_driver else _make_link_flags_darwin_indirect
- make_link_flags = make_link_flags_darwin
- get_lib_name = get_lib_name_default
+ make_link_flags = _make_link_flags_darwin_direct if use_direct_link_driver else _make_link_flags_darwin_indirect
else:
- make_link_flags_default = _make_link_flags_default_direct if use_direct_link_driver else _make_link_flags_default_indirect
- make_link_flags = make_link_flags_default
- get_lib_name = get_lib_name_default
+ make_link_flags = _make_link_flags_default_direct if use_direct_link_driver else _make_link_flags_default_indirect
return (make_link_flags, get_lib_name)

View File

@@ -0,0 +1,27 @@
diff -uNr rust/platform/triple_mappings.bzl rust/platform/triple_mappings.bzl
--- a/rust/platform/triple_mappings.bzl
+++ b/rust/platform/triple_mappings.bzl
@@ -1,6 +1,7 @@
"""Helpers for constructing supported Rust platform triples"""
load("//rust/platform:triple.bzl", "triple")
+load("@@toolchains_llvm_bootstrapped+//constraints/libc:libc_versions.bzl", "DEFAULT_LIBC")
def _support(*, std = False, host_tools = False):
"""Identify the type of support an associated platform triple has.
@@ -334,10 +335,11 @@
all_abi_constraints = []
- # add constraints for MUSL static compilation and linking
- # to separate the MUSL from the non-MUSL toolchain on x86_64
- # if abi == "musl" and system == "linux" and arch == "x86_64":
- # all_abi_constraints.append("//rust/platform/constraints:musl_on")
+ if system == "linux":
+ if abi == "musl":
+ all_abi_constraints.append("@@toolchains_llvm_bootstrapped+//constraints/libc:musl")
+ elif abi.startswith("gnu"):
+ all_abi_constraints.append("@@toolchains_llvm_bootstrapped+//constraints/libc:{}".format(DEFAULT_LIBC))
# add constraints for iOS + watchOS simulator and device triples
if system in ["ios", "watchos"]:

View File

@@ -0,0 +1,58 @@
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index af627fe50..d1c5142cb 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -560,9 +560,12 @@ def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib):
# Take the absolute value of hash() since it could be negative.
path_hash = abs(hash(lib.path))
- lib_name = get_lib_name_for_windows(lib) if toolchain.target_os.startswith("windows") else get_lib_name_default(lib)
-
- if toolchain.target_os.startswith("windows"):
+ if toolchain.target_os.startswith("windows") and toolchain.target_abi == "msvc":
+ lib_name = get_lib_name_for_windows(lib)
+ else:
+ lib_name = get_lib_name_default(lib)
+
+ if toolchain.target_os.startswith("windows") and toolchain.target_abi == "msvc":
prefix = ""
extension = ".lib"
elif lib_name.endswith(".pic"):
@@ -1495,7 +1498,7 @@ def rustc_compile_action(
pdb_file = None
dsym_folder = None
if crate_info.type in ("cdylib", "bin") and not experimental_use_cc_common_link:
- if toolchain.target_os == "windows" and compilation_mode.strip_level == "none":
+ if toolchain.target_os == "windows" and toolchain.target_abi == "msvc" and compilation_mode.strip_level == "none":
pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output)
action_outputs.append(pdb_file)
elif toolchain.target_os in ["macos", "darwin"]:
@@ -1626,7 +1629,7 @@ def rustc_compile_action(
additional_linker_outputs = []
- if crate_info.type in ("cdylib", "bin") and cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "generate_pdb_file"):
+ if crate_info.type in ("cdylib", "bin") and toolchain.target_abi == "msvc" and cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "generate_pdb_file"):
pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output)
additional_linker_outputs.append(pdb_file)
@@ -2248,8 +2251,8 @@ def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows
]
else:
return [
- "-lstatic=%s" % get_lib_name(artifact),
- "-Clink-arg=-l{}".format(artifact.basename),
+ "-lstatic=%s" % get_lib_name(artifact),
+ "-Clink-arg=-l{}".format(get_lib_name(artifact)),
]
else:
return [
@@ -2281,7 +2284,8 @@ def _make_link_flags_windows(make_link_flags_args, flavor_msvc, use_direct_drive
("-Clink-arg=%s--no-whole-archive" % prefix),
])
elif include_link_flags:
- ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc))
+ get_name_fn = get_lib_name_for_windows if flavor_msvc else get_lib_name_default
+ ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_name_fn, for_windows = True, flavor_msvc = flavor_msvc))
_add_user_link_flags(ret, linker_input)
return ret

View File

@@ -0,0 +1,10 @@
diff --git a/src/lib.rs b/src/lib.rs
index 2d5a2a2..6e8c4cd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,4 @@
-#![doc = include_str!("../readme.md")]
+#![doc = "windows-link"]
#![no_std]
/// Defines an external function to import.