feat: introduce codex-utils-cargo-bin as an alternative to assert_cmd::Command (#8496)

This PR introduces a `codex-utils-cargo-bin` utility crate that
wraps/replaces our use of `assert_cmd::Command` and
`escargot::CargoBuild`.

As you can infer from the introduction of `buck_project_root()` in this
PR, I am attempting to make it possible to build Codex under
[Buck2](https://buck2.build) as well as `cargo`. With Buck2, I hope to
achieve faster incremental local builds (largely due to Buck2's
[dice](https://buck2.build/docs/insights_and_knowledge/modern_dice/)
build strategy, as well as benefits from its local build daemon) as well
as faster CI builds if we invest in remote execution and caching.

See
https://buck2.build/docs/getting_started/what_is_buck2/#why-use-buck2-key-advantages
for more details about the performance advantages of Buck2.

Buck2 enforces stronger requirements in terms of build and test
isolation. It discourages assumptions about absolute paths (which is key
to enabling remote execution). Because the `CARGO_BIN_EXE_*` environment
variables that Cargo provides are absolute paths (which
`assert_cmd::Command` reads), this is a problem for Buck2, which is why
we need this `codex-utils-cargo-bin` utility.

My WIP-Buck2 setup sets the `CARGO_BIN_EXE_*` environment variables
passed to a `rust_test()` build rule as relative paths.
`codex-utils-cargo-bin` will resolve these values to absolute paths,
when necessary.


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8496).
* #8498
* __->__ #8496
This commit is contained in:
Michael Bolin
2025-12-23 19:29:32 -08:00
committed by GitHub
parent 96a65ff0ed
commit e61bae12e3
37 changed files with 248 additions and 101 deletions

View File

@@ -0,0 +1,127 @@
use std::ffi::OsString;
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum CargoBinError {
#[error("failed to read current exe")]
CurrentExe {
#[source]
source: std::io::Error,
},
#[error("failed to read current directory")]
CurrentDir {
#[source]
source: std::io::Error,
},
#[error("CARGO_BIN_EXE env var {key} resolved to {path:?}, but it does not exist")]
ResolvedPathDoesNotExist { key: String, path: PathBuf },
#[error("could not locate binary {name:?}; tried env vars {env_keys:?}; {fallback}")]
NotFound {
name: String,
env_keys: Vec<String>,
fallback: String,
},
}
/// Returns an absolute path to a binary target built for the current test run.
///
/// In `cargo test`, `CARGO_BIN_EXE_*` env vars are absolute, but Buck2 may set
/// them to project-relative paths (e.g. `buck-out/...`). Those paths break if a
/// test later changes its working directory. This helper makes the path
/// absolute up-front so callers can safely `chdir` afterwards.
pub fn cargo_bin(name: &str) -> Result<PathBuf, CargoBinError> {
let env_keys = cargo_bin_env_keys(name);
for key in &env_keys {
if let Some(value) = std::env::var_os(key) {
return resolve_bin_from_env(key, value);
}
}
match assert_cmd::Command::cargo_bin(name) {
Ok(cmd) => {
let abs = absolutize_from_buck_or_cwd(PathBuf::from(cmd.get_program()))?;
if abs.exists() {
Ok(abs)
} else {
Err(CargoBinError::ResolvedPathDoesNotExist {
key: "assert_cmd::Command::cargo_bin".to_owned(),
path: abs,
})
}
}
Err(err) => Err(CargoBinError::NotFound {
name: name.to_owned(),
env_keys,
fallback: format!("assert_cmd fallback failed: {err}"),
}),
}
}
fn cargo_bin_env_keys(name: &str) -> Vec<String> {
let mut keys = Vec::with_capacity(2);
keys.push(format!("CARGO_BIN_EXE_{name}"));
// Cargo replaces dashes in target names when exporting env vars.
let underscore_name = name.replace('-', "_");
if underscore_name != name {
keys.push(format!("CARGO_BIN_EXE_{underscore_name}"));
}
keys
}
fn resolve_bin_from_env(key: &str, value: OsString) -> Result<PathBuf, CargoBinError> {
let abs = absolutize_from_buck_or_cwd(PathBuf::from(value))?;
if abs.exists() {
Ok(abs)
} else {
Err(CargoBinError::ResolvedPathDoesNotExist {
key: key.to_owned(),
path: abs,
})
}
}
fn absolutize_from_buck_or_cwd(path: PathBuf) -> Result<PathBuf, CargoBinError> {
if path.is_absolute() {
return Ok(path);
}
if let Some(root) =
buck_project_root().map_err(|source| CargoBinError::CurrentExe { source })?
{
return Ok(root.join(path));
}
Ok(std::env::current_dir()
.map_err(|source| CargoBinError::CurrentDir { source })?
.join(path))
}
/// Best-effort attempt to find the Buck project root for the currently running
/// process.
///
/// Prefer this over `env!("CARGO_MANIFEST_DIR")` when running under Buck2: our
/// Buck generator sets `CARGO_MANIFEST_DIR="."` for compilation, which makes
/// `env!("CARGO_MANIFEST_DIR")` unusable for locating workspace files.
pub fn buck_project_root() -> Result<Option<PathBuf>, std::io::Error> {
if let Some(root) = std::env::var_os("BUCK_PROJECT_ROOT") {
let root = PathBuf::from(root);
if root.is_absolute() {
return Ok(Some(root));
}
}
// Fall back to deriving the project root from the location of the test
// runner executable:
// <project>/buck-out/v2/gen/.../__tests__/test-binary
let exe = std::env::current_exe()?;
for ancestor in exe.ancestors() {
if ancestor.file_name().is_some_and(|name| name == "buck-out") {
return Ok(ancestor.parent().map(PathBuf::from));
}
}
Ok(None)
}