[bazel] Improve runfiles handling (#10098)

we can't use runfiles directory on Windows due to path lengths, so swap
to manifest strategy. Parsing the manifest is a bit complex and the
format is changing in Bazel upstream, so pull in the official Rust
library (via a small hack to make it importable...) and cleanup all the
associated logic to work cleanly in both bazel and cargo without extra
confusion
This commit is contained in:
zbarsky-openai
2026-01-28 19:15:44 -05:00
committed by GitHub
parent 83d7c44500
commit 8497163363
17 changed files with 203 additions and 103 deletions

View File

@@ -1,3 +1,4 @@
# Without this, Bazel will consider BUILD.bazel files in
# .git/sl/origbackups (which can be populated by Sapling SCM).
.git
codex-rs/target

View File

@@ -1,5 +1,7 @@
common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
common --repo_env=BAZEL_NO_APPLE_CPP_TOOLCHAIN=1
# Dummy xcode config so we don't need to build xcode_locator in repo rule.
common --xcode_version_config=//:disable_xcode
common --disk_cache=~/.cache/bazel-disk-cache
common --repo_contents_cache=~/.cache/bazel-repo-contents-cache
@@ -9,6 +11,9 @@ startup --experimental_remote_repo_contents_cache
common --experimental_platform_in_output_dir
# Runfiles strategy rationale: codex-rs/utils/cargo-bin/README.md
common --noenable_runfiles
common --enable_platform_specific_config
# TODO(zbarsky): We need to untangle these libc constraints to get linux remote builds working.
common:linux --host_platform=//:local
@@ -44,4 +49,3 @@ common --jobs=30
common:remote --extra_execution_platforms=//:rbe
common:remote --remote_executor=grpcs://remote.buildbuddy.io
common:remote --jobs=800

View File

@@ -1,3 +1,7 @@
load("@apple_support//xcode:xcode_config.bzl", "xcode_config")
xcode_config(name = "disable_xcode")
# We mark the local platform as glibc-compatible so that rust can grab a toolchain for us.
# TODO(zbarsky): Upstream a better libc constraint into rules_rust.
# We only enable this on linux though for sanity, and because it breaks remote execution.

View File

@@ -27,6 +27,8 @@ register_toolchains(
"@toolchains_llvm_bootstrapped//toolchain:all",
)
# Needed to disable xcode...
bazel_dep(name = "apple_support", version = "2.1.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "rules_platform", version = "0.1.0")
bazel_dep(name = "rules_rust", version = "0.68.1")
@@ -90,6 +92,11 @@ crate.annotation(
inject_repo(crate, "openssl")
crate.annotation(
crate = "runfiles",
workspace_cargo_toml = "rust/runfiles/Cargo.toml",
)
# Fix readme inclusions
crate.annotation(
crate = "windows-link",

4
MODULE.bazel.lock generated
View File

@@ -21,7 +21,8 @@
"https://bcr.bazel.build/modules/apple_support/1.23.0/MODULE.bazel": "317d47e3f65b580e7fb4221c160797fda48e32f07d2dfff63d754ef2316dcd25",
"https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8",
"https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4",
"https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d",
"https://bcr.bazel.build/modules/apple_support/2.1.0/MODULE.bazel": "b15c125dabed01b6803c129cd384de4997759f02f8ec90dc5136bcf6dfc5086a",
"https://bcr.bazel.build/modules/apple_support/2.1.0/source.json": "78064cfefe18dee4faaf51893661e0d403784f3efe88671d727cdcdc67ed8fb3",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/source.json": "ffab9254c65ba945f8369297ad97ca0dec213d3adc6e07877e23a48624a8b456",
@@ -790,6 +791,7 @@
"gimli_0.31.1": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"fallible-iterator\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0.0\"},{\"default_features\":false,\"name\":\"stable_deref_trait\",\"optional\":true,\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"test-assembler\",\"req\":\"^0.1.3\"}],\"features\":{\"default\":[\"read-all\",\"write\"],\"endian-reader\":[\"read\",\"dep:stable_deref_trait\"],\"fallible-iterator\":[\"dep:fallible-iterator\"],\"read\":[\"read-core\"],\"read-all\":[\"read\",\"std\",\"fallible-iterator\",\"endian-reader\"],\"read-core\":[],\"rustc-dep-of-std\":[\"dep:core\",\"dep:alloc\",\"dep:compiler_builtins\"],\"std\":[\"fallible-iterator?/std\",\"stable_deref_trait?/std\"],\"write\":[\"dep:indexmap\"]}}",
"git+https://github.com/JakkuSakura/tokio-tungstenite?rev=2ae536b0de793f3ddf31fc2f22d445bf1ef2023d#2ae536b0de793f3ddf31fc2f22d445bf1ef2023d_tokio-tungstenite": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"sink\",\"std\"],\"name\":\"futures-util\",\"optional\":false},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"default_features\":false,\"features\":[],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"tokio-native-tls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tokio-rustls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tungstenite\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\",\"tokio-rustls\",\"stream\",\"tungstenite/__rustls-tls\",\"handshake\"],\"connect\":[\"stream\",\"tokio/net\",\"handshake\"],\"default\":[\"connect\",\"handshake\"],\"handshake\":[\"tungstenite/handshake\"],\"native-tls\":[\"native-tls-crate\",\"tokio-native-tls\",\"stream\",\"tungstenite/native-tls\",\"handshake\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\",\"tungstenite/native-tls-vendored\"],\"proxy\":[\"tungstenite/proxy\",\"tokio/net\",\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"stream\":[],\"url\":[\"tungstenite/url\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/JakkuSakura/tungstenite-rs?rev=f514de8644821113e5d18a027d6d28a5c8cc0a6e#f514de8644821113e5d18a027d6d28a5c8cc0a6e_tungstenite": "{\"dependencies\":[{\"name\":\"bytes\"},{\"default_features\":true,\"features\":[],\"name\":\"data-encoding\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"httparse\",\"optional\":true},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"name\":\"rand\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"sha1\",\"optional\":true},{\"name\":\"thiserror\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true},{\"name\":\"utf-8\"},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\"],\"default\":[\"handshake\"],\"handshake\":[\"data-encoding\",\"http\",\"httparse\",\"sha1\"],\"native-tls\":[\"native-tls-crate\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\"],\"proxy\":[\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"url\":[\"dep:url\"]},\"strip_prefix\":\"\"}",
"git+https://github.com/dzbarsky/rules_rust?rev=b56cbaa8465e74127f1ea216f813cd377295ad81#b56cbaa8465e74127f1ea216f813cd377295ad81_runfiles": "{\"dependencies\":[],\"features\":{},\"strip_prefix\":\"\"}",
"git+https://github.com/helix-editor/nucleo.git?rev=4253de9faabb4e5c6d81d946a5e35a90f87347ee#4253de9faabb4e5c6d81d946a5e35a90f87347ee_nucleo": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"nucleo-matcher\",\"optional\":false},{\"default_features\":true,\"features\":[\"send_guard\",\"arc_lock\"],\"name\":\"parking_lot\",\"optional\":false},{\"name\":\"rayon\"}],\"features\":{},\"strip_prefix\":\"\"}",
"git+https://github.com/helix-editor/nucleo.git?rev=4253de9faabb4e5c6d81d946a5e35a90f87347ee#4253de9faabb4e5c6d81d946a5e35a90f87347ee_nucleo-matcher": "{\"dependencies\":[{\"name\":\"memchr\"},{\"default_features\":true,\"features\":[],\"name\":\"unicode-segmentation\",\"optional\":true}],\"features\":{\"default\":[\"unicode-normalization\",\"unicode-casefold\",\"unicode-segmentation\"],\"unicode-casefold\":[],\"unicode-normalization\":[],\"unicode-segmentation\":[\"dep:unicode-segmentation\"]},\"strip_prefix\":\"matcher\"}",
"git+https://github.com/nornagon/crossterm?branch=nornagon%2Fcolor-query#87db8bfa6dc99427fd3b071681b07fc31c6ce995_crossterm": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"bitflags\",\"optional\":false},{\"default_features\":false,\"features\":[],\"name\":\"futures-core\",\"optional\":true},{\"name\":\"parking_lot\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"filedescriptor\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[],\"name\":\"libc\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"os-poll\"],\"name\":\"mio\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[\"std\",\"stdio\",\"termios\"],\"name\":\"rustix\",\"optional\":false,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[],\"name\":\"signal-hook\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"support-v1_0\"],\"name\":\"signal-hook-mio\",\"optional\":true,\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[],\"name\":\"crossterm_winapi\",\"optional\":true,\"target\":\"cfg(windows)\"},{\"default_features\":true,\"features\":[\"winuser\",\"winerror\"],\"name\":\"winapi\",\"optional\":true,\"target\":\"cfg(windows)\"}],\"features\":{\"bracketed-paste\":[],\"default\":[\"bracketed-paste\",\"windows\",\"events\"],\"event-stream\":[\"dep:futures-core\",\"events\"],\"events\":[\"dep:mio\",\"dep:signal-hook\",\"dep:signal-hook-mio\"],\"serde\":[\"dep:serde\",\"bitflags/serde\"],\"use-dev-tty\":[\"filedescriptor\",\"rustix/process\"],\"windows\":[\"dep:winapi\",\"dep:crossterm_winapi\"]},\"strip_prefix\":\"\"}",

7
codex-rs/Cargo.lock generated
View File

@@ -1975,7 +1975,7 @@ name = "codex-utils-cargo-bin"
version = "0.0.0"
dependencies = [
"assert_cmd",
"path-absolutize",
"runfiles",
"thiserror 2.0.17",
]
@@ -6592,6 +6592,11 @@ dependencies = [
"zeroize",
]
[[package]]
name = "runfiles"
version = "0.1.0"
source = "git+https://github.com/dzbarsky/rules_rust?rev=b56cbaa8465e74127f1ea216f813cd377295ad81#b56cbaa8465e74127f1ea216f813cd377295ad81"
[[package]]
name = "rustc-demangle"
version = "0.1.25"

View File

@@ -186,6 +186,7 @@ regex = "1.12.2"
regex-lite = "0.1.8"
reqwest = "0.12"
rmcp = { version = "0.12.0", default-features = false }
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
schemars = "0.8.22"
seccompiler = "0.5.0"
sentry = "0.46.0"

View File

@@ -16,7 +16,7 @@ use wiremock::matchers::path;
fn repo_root() -> std::path::PathBuf {
#[expect(clippy::expect_used)]
find_resource!(".").expect("failed to resolve repo root")
codex_utils_cargo_bin::repo_root().expect("failed to resolve repo root")
}
fn cli_responses_fixture() -> std::path::PathBuf {

View File

@@ -1,5 +1,4 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use codex_utils_cargo_bin::find_resource;
use core_test_support::responses::ev_completed;
use core_test_support::responses::mount_sse_once_match;
use core_test_support::responses::sse;
@@ -11,7 +10,7 @@ use wiremock::matchers::header;
async fn exec_uses_codex_api_key_env_var() -> anyhow::Result<()> {
let test = test_codex_exec();
let server = start_mock_server().await;
let repo_root = find_resource!(".")?;
let repo_root = codex_utils_cargo_bin::repo_root()?;
mount_sse_once_match(
&server,

View File

@@ -113,7 +113,7 @@ fn exec_fixture() -> anyhow::Result<std::path::PathBuf> {
}
fn exec_repo_root() -> anyhow::Result<std::path::PathBuf> {
Ok(find_resource!(".")?)
Ok(codex_utils_cargo_bin::repo_root()?)
}
#[test]

View File

@@ -3,4 +3,10 @@ load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "cargo-bin",
crate_name = "codex_utils_cargo_bin",
compile_data = ["repo_root.marker"],
lib_data_extra = ["repo_root.marker"],
test_data_extra = ["repo_root.marker"],
rustc_env = {
"CODEX_REPO_ROOT_MARKER": "$(rlocationpath :repo_root.marker)",
},
)

View File

@@ -9,5 +9,5 @@ workspace = true
[dependencies]
assert_cmd = { workspace = true }
path-absolutize = { workspace = true }
runfiles = { workspace = true }
thiserror = { workspace = true }

View File

@@ -0,0 +1,20 @@
# codex-utils-cargo-bin runfiles strategy
We disable the directory-based runfiles strategy and rely on the manifest
strategy across all platforms. This avoids Windows path length issues and keeps
behavior consistent in local and remote builds on all platforms. Bazel sets
`RUNFILES_MANIFEST_FILE`, and the `codex-utils-cargo-bin` helpers use the
`runfiles` crate to resolve runfiles via that manifest.
Function behavior:
- `cargo_bin`: reads `CARGO_BIN_EXE_*` environment variables (set by Cargo or
Bazel) and resolves them via the runfiles manifest when `RUNFILES_MANIFEST_FILE`
is present. When not under runfiles, it only accepts absolute paths from
`CARGO_BIN_EXE_*` and returns an error otherwise.
- `find_resource!`: used by tests to locate fixtures. It chooses the Bazel
runfiles resolution path when `RUNFILES_MANIFEST_FILE` is set, otherwise it
falls back to a `CARGO_MANIFEST_DIR`-relative path for Cargo runs.
Background:
- https://bazel.build/docs/runfiles
- https://bazel.build/docs/runfiles#runfiles-manifest

View File

@@ -0,0 +1 @@

View File

@@ -1,7 +1,12 @@
use std::ffi::OsString;
use std::io;
use std::path::Path;
use std::path::PathBuf;
pub use path_absolutize;
pub use runfiles;
/// Bazel sets this when runfiles directories are disabled, which we do on all platforms for consistency.
const RUNFILES_MANIFEST_ONLY_ENV: &str = "RUNFILES_MANIFEST_ONLY";
#[derive(Debug, thiserror::Error)]
pub enum CargoBinError {
@@ -27,10 +32,9 @@ pub enum CargoBinError {
/// 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.
/// In `cargo test`, `CARGO_BIN_EXE_*` env vars are absolute.
/// In `bazel test`, `CARGO_BIN_EXE_*` env vars are rlocationpaths, intended to be consumed by `rlocation`.
/// This helper allows callers to transparently support both.
pub fn cargo_bin(name: &str) -> Result<PathBuf, CargoBinError> {
let env_keys = cargo_bin_env_keys(name);
for key in &env_keys {
@@ -38,16 +42,20 @@ pub fn cargo_bin(name: &str) -> Result<PathBuf, CargoBinError> {
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)
let mut path = PathBuf::from(cmd.get_program());
if !path.is_absolute() {
path = std::env::current_dir()
.map_err(|source| CargoBinError::CurrentDir { source })?
.join(path);
}
if path.exists() {
Ok(path)
} else {
Err(CargoBinError::ResolvedPathDoesNotExist {
key: "assert_cmd::Command::cargo_bin".to_owned(),
path: abs,
path,
})
}
}
@@ -72,6 +80,31 @@ fn cargo_bin_env_keys(name: &str) -> Vec<String> {
keys
}
pub fn runfiles_available() -> bool {
std::env::var_os(RUNFILES_MANIFEST_ONLY_ENV).is_some()
}
fn resolve_bin_from_env(key: &str, value: OsString) -> Result<PathBuf, CargoBinError> {
let raw = PathBuf::from(&value);
if runfiles_available() {
let runfiles = runfiles::Runfiles::create().map_err(|err| CargoBinError::CurrentExe {
source: std::io::Error::other(err),
})?;
if let Some(resolved) = runfiles::rlocation!(runfiles, &raw)
&& resolved.exists()
{
return Ok(resolved);
}
} else if raw.is_absolute() && raw.exists() {
return Ok(raw);
}
Err(CargoBinError::ResolvedPathDoesNotExist {
key: key.to_owned(),
path: raw,
})
}
/// Macro that derives the path to a test resource at runtime, the value of
/// which depends on whether Cargo or Bazel is being used to build and run a
/// test. Note the return value may be a relative or absolute path.
@@ -84,97 +117,109 @@ fn cargo_bin_env_keys(name: &str) -> Vec<String> {
#[macro_export]
macro_rules! find_resource {
($resource:expr) => {{
// When this code is built and run with Bazel:
// - we inject `BAZEL_PACKAGE` as a compile-time environment variable
// that points to native.package_name()
// - at runtime, Bazel will set `RUNFILES_DIR` to the runfiles directory
//
// Therefore, the compile-time value of `BAZEL_PACKAGE` will always be
// included in the compiled binary (even if it is built with Cargo), but
// we only check it at runtime if `RUNFILES_DIR` is set.
let resource = std::path::Path::new(&$resource);
match std::env::var("RUNFILES_DIR") {
Ok(bazel_runtime_files) => match option_env!("BAZEL_PACKAGE") {
Some(bazel_package) => {
use $crate::path_absolutize::Absolutize;
let manifest_dir = std::path::PathBuf::from(bazel_runtime_files)
.join("_main")
.join(bazel_package)
.join(resource);
// Note we also have to normalize (but not canonicalize!)
// the path for _Bazel_ because the original value ends with
// `codex-rs/exec-server/tests/common/../suite/bash`, but
// the `tests/common` folder will not exist at runtime under
// Bazel. As such, we have to normalize it before passing it
// to `dotslash fetch`.
manifest_dir.absolutize().map(|p| p.to_path_buf())
}
None => Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"BAZEL_PACKAGE not set in Bazel build",
)),
},
Err(_) => {
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
Ok(manifest_dir.join(resource))
}
if $crate::runfiles_available() {
// When this code is built and run with Bazel:
// - we inject `BAZEL_PACKAGE` as a compile-time environment variable
// that points to native.package_name()
// - at runtime, Bazel will set runfiles-related env vars
$crate::resolve_bazel_runfile(option_env!("BAZEL_PACKAGE"), resource)
} else {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
Ok(manifest_dir.join(resource))
}
}};
}
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 })?
pub fn resolve_bazel_runfile(
bazel_package: Option<&str>,
resource: &Path,
) -> std::io::Result<PathBuf> {
let runfiles = runfiles::Runfiles::create()
.map_err(|err| std::io::Error::other(format!("failed to create runfiles: {err}")))?;
let runfile_path = match bazel_package {
Some(bazel_package) => PathBuf::from("_main").join(bazel_package).join(resource),
None => {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"BAZEL_PACKAGE was not set at compile time",
));
}
};
let runfile_path = normalize_runfile_path(&runfile_path);
if let Some(resolved) = runfiles::rlocation!(runfiles, &runfile_path)
&& resolved.exists()
{
return Ok(root.join(path));
return Ok(resolved);
}
Ok(std::env::current_dir()
.map_err(|source| CargoBinError::CurrentDir { source })?
.join(path))
let runfile_path_display = runfile_path.display();
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("runfile does not exist at: {runfile_path_display}"),
))
}
/// 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));
pub fn resolve_cargo_runfile(resource: &Path) -> std::io::Result<PathBuf> {
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
Ok(manifest_dir.join(resource))
}
pub fn repo_root() -> io::Result<PathBuf> {
let marker = if runfiles_available() {
let runfiles = runfiles::Runfiles::create()
.map_err(|err| io::Error::other(format!("failed to create runfiles: {err}")))?;
let marker_path = option_env!("CODEX_REPO_ROOT_MARKER")
.map(PathBuf::from)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"CODEX_REPO_ROOT_MARKER was not set at compile time",
)
})?;
runfiles::rlocation!(runfiles, &marker_path).ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"repo_root.marker not available in runfiles",
)
})?
} else {
resolve_cargo_runfile(Path::new("repo_root.marker"))?
};
let mut root = marker;
for _ in 0..4 {
root = root
.parent()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"repo_root.marker did not have expected parent depth",
)
})?
.to_path_buf();
}
Ok(root)
}
fn normalize_runfile_path(path: &Path) -> PathBuf {
let mut components = Vec::new();
for component in path.components() {
match component {
std::path::Component::CurDir => {}
std::path::Component::ParentDir => {
if matches!(components.last(), Some(std::path::Component::Normal(_))) {
components.pop();
} else {
components.push(component);
}
}
_ => components.push(component),
}
}
// 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)
components
.into_iter()
.fold(PathBuf::new(), |mut acc, component| {
acc.push(component.as_os_str());
acc
})
}

View File

@@ -36,6 +36,8 @@ def codex_rust_crate(
crate_edition = None,
build_script_data = [],
compile_data = [],
lib_data_extra = [],
rustc_env = {},
deps_extra = [],
integration_deps_extra = [],
integration_compile_data_extra = [],
@@ -63,6 +65,8 @@ def codex_rust_crate(
You probably don't want this, it's only here for a single caller.
build_script_data: Data files exposed to the build script at runtime.
compile_data: Non-Rust compile-time data for the library target.
lib_data_extra: Extra runtime data for the library target.
rustc_env: Extra rustc_env entries to merge with defaults.
deps_extra: Extra normal deps beyond @crates resolution.
Typically only needed when features add additional deps.
integration_deps_extra: Extra deps for integration tests only.
@@ -85,7 +89,7 @@ def codex_rust_crate(
rustc_env = {
"BAZEL_PACKAGE": native.package_name(),
}
} | rustc_env
binaries = DEP_DATA.get(native.package_name())["binaries"]
@@ -112,6 +116,7 @@ def codex_rust_crate(
deps = deps,
proc_macro_deps = proc_macro_deps,
compile_data = compile_data,
data = lib_data_extra,
srcs = lib_srcs,
edition = crate_edition,
rustc_env = rustc_env,
@@ -138,7 +143,7 @@ def codex_rust_crate(
for binary, main in binaries.items():
#binary = binary.replace("-", "_")
sanitized_binaries.append(binary)
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rootpath :%s)" % binary
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath :%s)" % binary
rust_binary(
name = binary,
@@ -154,7 +159,7 @@ def codex_rust_crate(
for binary_label in extra_binaries:
sanitized_binaries.append(binary_label)
binary = Label(binary_label).name
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rootpath %s)" % binary_label
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath %s)" % binary_label
for test in native.glob(["tests/*.rs"], allow_empty = True):
test_name = name + "-" + test.removeprefix("tests/").removesuffix(".rs").replace("/", "-")

View File

@@ -31,4 +31,4 @@ index a28ad50b7..af627fe50 100644
+ 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)