diff --git a/.bazelignore b/.bazelignore index eda018aeb29..2e90753cb8e 100644 --- a/.bazelignore +++ b/.bazelignore @@ -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 diff --git a/.bazelrc b/.bazelrc index 764754c216d..ce0c2ee5d55 100644 --- a/.bazelrc +++ b/.bazelrc @@ -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 - diff --git a/BUILD.bazel b/BUILD.bazel index 883432655cf..dc57103b6bf 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -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. diff --git a/MODULE.bazel b/MODULE.bazel index df0df9b25ca..f4c593b3f32 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -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", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 7444d3fdbe2..63b7ed16ee9 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -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\":\"\"}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 38d44c44b5b..c6b006c2034 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -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" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 9b0ce2e16d3..51962139913 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -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" diff --git a/codex-rs/core/tests/suite/cli_stream.rs b/codex-rs/core/tests/suite/cli_stream.rs index 57142b07f22..9ca2c6bd68f 100644 --- a/codex-rs/core/tests/suite/cli_stream.rs +++ b/codex-rs/core/tests/suite/cli_stream.rs @@ -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 { diff --git a/codex-rs/exec/tests/suite/auth_env.rs b/codex-rs/exec/tests/suite/auth_env.rs index 4f8018e808f..d55da946e21 100644 --- a/codex-rs/exec/tests/suite/auth_env.rs +++ b/codex-rs/exec/tests/suite/auth_env.rs @@ -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, diff --git a/codex-rs/exec/tests/suite/resume.rs b/codex-rs/exec/tests/suite/resume.rs index 6e24934f219..4169c60dd1e 100644 --- a/codex-rs/exec/tests/suite/resume.rs +++ b/codex-rs/exec/tests/suite/resume.rs @@ -113,7 +113,7 @@ fn exec_fixture() -> anyhow::Result { } fn exec_repo_root() -> anyhow::Result { - Ok(find_resource!(".")?) + Ok(codex_utils_cargo_bin::repo_root()?) } #[test] diff --git a/codex-rs/utils/cargo-bin/BUILD.bazel b/codex-rs/utils/cargo-bin/BUILD.bazel index 77d65180126..b5b5e6ba339 100644 --- a/codex-rs/utils/cargo-bin/BUILD.bazel +++ b/codex-rs/utils/cargo-bin/BUILD.bazel @@ -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)", + }, ) diff --git a/codex-rs/utils/cargo-bin/Cargo.toml b/codex-rs/utils/cargo-bin/Cargo.toml index fe3a410547d..6cbe923f976 100644 --- a/codex-rs/utils/cargo-bin/Cargo.toml +++ b/codex-rs/utils/cargo-bin/Cargo.toml @@ -9,5 +9,5 @@ workspace = true [dependencies] assert_cmd = { workspace = true } -path-absolutize = { workspace = true } +runfiles = { workspace = true } thiserror = { workspace = true } diff --git a/codex-rs/utils/cargo-bin/README.md b/codex-rs/utils/cargo-bin/README.md new file mode 100644 index 00000000000..05af2f34bf0 --- /dev/null +++ b/codex-rs/utils/cargo-bin/README.md @@ -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 diff --git a/codex-rs/utils/cargo-bin/repo_root.marker b/codex-rs/utils/cargo-bin/repo_root.marker new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/codex-rs/utils/cargo-bin/repo_root.marker @@ -0,0 +1 @@ + diff --git a/codex-rs/utils/cargo-bin/src/lib.rs b/codex-rs/utils/cargo-bin/src/lib.rs index 40fa40c62fd..84f69af3581 100644 --- a/codex-rs/utils/cargo-bin/src/lib.rs +++ b/codex-rs/utils/cargo-bin/src/lib.rs @@ -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 { let env_keys = cargo_bin_env_keys(name); for key in &env_keys { @@ -38,16 +42,20 @@ pub fn cargo_bin(name: &str) -> Result { 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 { 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 { + 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 { #[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 { - 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 { - 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 { + 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); } + 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}"), + )) +} - Ok(std::env::current_dir() - .map_err(|source| CargoBinError::CurrentDir { source })? - .join(path)) +pub fn resolve_cargo_runfile(resource: &Path) -> std::io::Result { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + Ok(manifest_dir.join(resource)) } -/// 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, 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 repo_root() -> io::Result { + 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) +} - // Fall back to deriving the project root from the location of the test - // runner executable: - // /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)); +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), } } - Ok(None) + components + .into_iter() + .fold(PathBuf::new(), |mut acc, component| { + acc.push(component.as_os_str()); + acc + }) } diff --git a/defs.bzl b/defs.bzl index 249c3a45833..40112969c84 100644 --- a/defs.bzl +++ b/defs.bzl @@ -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("/", "-") diff --git a/patches/rules_rust.patch b/patches/rules_rust.patch index 298eb74c519..3020ac2282d 100644 --- a/patches/rules_rust.patch +++ b/patches/rules_rust.patch @@ -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) - +