From b48444736fe30ef472fc8ce630ff61f7eee292b4 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 15 Jul 2026 21:37:28 +0000 Subject: [PATCH 1/3] test: cargo trim-paths env vars cargo with `-Ztrim-paths` nightly feature would set `CARGO_TRIM_PATHS_{SCOPE,REMAP}` for build scripts, so they can forward path remaps to C/C++ compilers. https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option --- tests/trim_paths.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/trim_paths.rs diff --git a/tests/trim_paths.rs b/tests/trim_paths.rs new file mode 100644 index 000000000..be87ba502 --- /dev/null +++ b/tests/trim_paths.rs @@ -0,0 +1,139 @@ +//! Tests for inheriting path remap from cargo's unstable `-Ztrim-paths` feature. +//! +//! This test is in its own module because it modifies the environment and +//! would affect other tests when run in parallel with them. + +// Windows only beccause `-f*-prefix-map` flag family is GNU/Clang-only anyway. +// If needed, Windows support will be added in the future. +#![cfg(not(windows))] + +mod support; + +use crate::support::Test; + +// `=` pairs as cargo passes them (`;` on Windows though not supported in cc-rs yet) + +const REMAP: &str = + "/path/to/pkg=foo-0.1.0:/path/to/sysroot/lib/rustlib/src/rust=/rustc/1234567890abcdef"; + +const MACRO_FLAGS: &[&str] = &[ + "-fmacro-prefix-map=/path/to/pkg=foo-0.1.0", + "-fmacro-prefix-map=/path/to/sysroot/lib/rustlib/src/rust=/rustc/1234567890abcdef", +]; + +const OBJECT_FLAGS: &[&str] = &[ + "-fdebug-prefix-map=/path/to/pkg=foo-0.1.0", + "-fdebug-prefix-map=/path/to/sysroot/lib/rustlib/src/rust=/rustc/1234567890abcdef", +]; + +#[test] +fn scope_all() { + let mut test = Test::gnu(); + test.env.set("CARGO_TRIM_PATHS_SCOPE", "all"); + test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) { + cmd.must_not_have(flag); + } +} + +#[test] +fn scope_macro() { + let mut test = Test::gnu(); + test.env.set("CARGO_TRIM_PATHS_SCOPE", "macro"); + test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in MACRO_FLAGS { + cmd.must_not_have(flag); + } + for flag in OBJECT_FLAGS { + cmd.must_not_have(flag); + } +} + +#[test] +fn scope_object() { + let mut test = Test::gnu(); + test.env.set("CARGO_TRIM_PATHS_SCOPE", "object"); + test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in OBJECT_FLAGS { + cmd.must_not_have(flag); + } + for flag in MACRO_FLAGS { + cmd.must_not_have(flag); + } +} + +/// `diagnostics` has no C compiler equivalent; combined with `macro` only +/// the macro remap flags apply. +#[test] +fn scope_macro_and_diagnostics() { + let mut test = Test::gnu(); + test.env.set("CARGO_TRIM_PATHS_SCOPE", "diagnostics,macro"); + test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in MACRO_FLAGS { + cmd.must_not_have(flag); + } + for flag in OBJECT_FLAGS { + cmd.must_not_have(flag); + } +} + +/// `none` disables path sanitization; no remap flags should ever be emitted. +#[test] +fn scope_none() { + let mut test = Test::gnu(); + test.env.set("CARGO_TRIM_PATHS_SCOPE", "none"); + test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) { + cmd.must_not_have(flag); + } +} + +/// Without the cargo-provided env vars nothing is emitted. +#[test] +fn no_env_vars() { + let mut test = Test::gnu(); + test.env.remove("CARGO_TRIM_PATHS_SCOPE"); + test.env.remove("CARGO_TRIM_PATHS_REMAP"); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) { + cmd.must_not_have(flag); + } +} + +/// `Build::inherit_trim_paths(false)` opts out of the inheritance. +#[test] +fn opt_out() { + let mut test = Test::gnu(); + test.env.set("CARGO_TRIM_PATHS_SCOPE", "all"); + test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); + + test.gcc().file("foo.c").compile("foo"); + + let cmd = test.cmd(0); + for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) { + cmd.must_not_have(flag); + } +} From f177385b1ea7d0a8b3a66ada9e0199c3c6428745 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 15 Jul 2026 21:40:33 +0000 Subject: [PATCH 2/3] feat: inherit path remap rules from cargo trim-paths Cargo with `-Ztrim-paths` feature would set `CARGO_TRIM_PATHS_SCOPE` and `CARGO_TRIM_PATHS_REMAP` for build scripts, so they can forward path remaps to C/C++ compilers. * `macro` scope -> `-fmacro-prefix-map` (`__FILE__` and friends) * `object` scope -> `-fdebug-prefix-map` (debug info) * `all` scope -> both * `diagnostics` and `none` scopes have no C equivalent MSVC is skipped as it has no equivalent flag family. It seems to have an undocumented `/pathmap` though, see bazelbuild/bazel 9466 This is inherited by default. Rationale: * Mirroring `inherit_rustflags` * The env vars only exist when the user opted into Cargo profile trim-paths. * It is easy to opt-out from user. Cargo only looks at your local package's profile, and you can also do a per-dependency profile override. Do note this is nightly only feature from Cargo's point of view. --- src/lib.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++ tests/trim_paths.rs | 15 ++++--- 2 files changed, 108 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5b802f625..4ccb5f3cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -420,6 +420,7 @@ pub struct Build { shell_escaped_flags: Option, build_cache: Arc, inherit_rustflags: bool, + inherit_trim_paths: bool, prefer_clang_cl_over_msvc: bool, } @@ -549,6 +550,7 @@ impl Build { shell_escaped_flags: None, build_cache: Arc::default(), inherit_rustflags: true, + inherit_trim_paths: true, prefer_clang_cl_over_msvc: false, } } @@ -1380,6 +1382,29 @@ impl Build { self } + /// Configure whether cc should automatically inherit path remap rules + /// from cargo's [`trim-paths`] profile setting, + /// and translate them into `-fmacro-prefix-map`/ `-fdebug-prefix-map` flags. + /// + /// This option defaults to `true`. + /// + /// This option doesn't support Windows MSVC cl.exe yet. + /// Only clang and GCC are supported. + /// + ///
+ /// + /// [`trim-paths`] is currently an unstable cargo feature, + /// only available on nightly with `-Ztrim-paths`. + /// The contract around this option may change as the cargo feature evolves. + /// + ///
+ /// + /// [`trim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option + pub fn inherit_trim_paths(&mut self, inherit_trim_paths: bool) -> &mut Build { + self.inherit_trim_paths = inherit_trim_paths; + self + } + /// Prefer to use clang-cl over msvc. /// /// This option defaults to `false`. @@ -1495,6 +1520,7 @@ impl Build { .cpp(self.cpp) .cuda(self.cuda) .inherit_rustflags(false) + .inherit_trim_paths(false) .emit_rerun_if_env_changed(self.emit_rerun_if_env_changed); if let Some(target) = &self.target { cfg.target(target); @@ -2055,6 +2081,11 @@ impl Build { self.add_inherited_rustflags(&mut cmd, &target)?; } + // Add path remap flags inherited from cargo's `-Ztrim-paths`. + if self.inherit_trim_paths { + self.add_trim_paths_flags(&mut cmd)?; + } + // Set flags configured in the builder (do this second-to-last, to allow these to override // everything above). for flag in self.flags.iter() { @@ -2668,6 +2699,74 @@ impl Build { Ok(()) } + /// Translate cargo's `-Ztrim-paths` remap rules into compiler flags. + /// + /// [`trim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option + fn add_trim_paths_flags(&self, cmd: &mut Tool) -> Result<(), Error> { + // MSVC has no equivalent of the `-f*-prefix-map` flag family. + // Left out until there is demand for it. + if cmd.is_like_msvc() { + return Ok(()); + } + let scope = match cargo_env_var_os("CARGO_TRIM_PATHS_SCOPE") { + Some(scope) => scope, + None => return Ok(()), + }; + let remap = match cargo_env_var_os("CARGO_TRIM_PATHS_REMAP") { + Some(remap) => remap, + None => return Ok(()), + }; + + // * `macro` scope -> `-fmacro-prefix-map` + // * `object` scope -> `-fmacro-prefix-map` + `-fdebug-prefix-map` + // * `all` scope -> both + // * `diagnostics` and `none` scopes have no C equivalent + let mut macro_scope = false; + let mut object_scope = false; + for scope in scope.to_string_lossy().split(',') { + match scope { + "all" => { + macro_scope = true; + object_scope = true; + break; + } + // `__FILE__` and friends + "macro" => macro_scope = true, + // Everything embedded in object files. + // rustc defines this scope as macro + debuginfo. + // Both `__FILE__` strings and debug info end up in the object, + // so the C analogue must remap both as well. + "object" => { + macro_scope = true; + object_scope = true; + break; + } + _ => {} + } + } + if !macro_scope && !object_scope { + return Ok(()); + } + + for pair in env::split_paths(&remap) { + let pair = pair.as_os_str(); + if pair.is_empty() { + continue; + } + if macro_scope { + let mut flag = OsString::from("-fmacro-prefix-map="); + flag.push(pair); + cmd.push_cc_arg(flag); + } + if object_scope { + let mut flag = OsString::from("-fdebug-prefix-map="); + flag.push(pair); + cmd.push_cc_arg(flag); + } + } + Ok(()) + } + fn msvc_macro_assembler(&self) -> Result { let target = self.get_target()?; let tool = match target.arch { diff --git a/tests/trim_paths.rs b/tests/trim_paths.rs index be87ba502..4205fd6b5 100644 --- a/tests/trim_paths.rs +++ b/tests/trim_paths.rs @@ -36,7 +36,7 @@ fn scope_all() { let cmd = test.cmd(0); for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) { - cmd.must_not_have(flag); + cmd.must_have(flag); } } @@ -50,7 +50,7 @@ fn scope_macro() { let cmd = test.cmd(0); for flag in MACRO_FLAGS { - cmd.must_not_have(flag); + cmd.must_have(flag); } for flag in OBJECT_FLAGS { cmd.must_not_have(flag); @@ -67,10 +67,10 @@ fn scope_object() { let cmd = test.cmd(0); for flag in OBJECT_FLAGS { - cmd.must_not_have(flag); + cmd.must_have(flag); } for flag in MACRO_FLAGS { - cmd.must_not_have(flag); + cmd.must_have(flag); } } @@ -86,7 +86,7 @@ fn scope_macro_and_diagnostics() { let cmd = test.cmd(0); for flag in MACRO_FLAGS { - cmd.must_not_have(flag); + cmd.must_have(flag); } for flag in OBJECT_FLAGS { cmd.must_not_have(flag); @@ -130,7 +130,10 @@ fn opt_out() { test.env.set("CARGO_TRIM_PATHS_SCOPE", "all"); test.env.set("CARGO_TRIM_PATHS_REMAP", REMAP); - test.gcc().file("foo.c").compile("foo"); + test.gcc() + .inherit_trim_paths(false) + .file("foo.c") + .compile("foo"); let cmd = test.cmd(0); for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) { From d871a81e6a0af1f419352809f12eb2cb91e22c13 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 15 Jul 2026 22:34:05 +0000 Subject: [PATCH 3/3] feat: probe `-f*-prefix-map` support before emission We probe these flags and skip with a warning when the compiler rejects it. * `-fdebug-prefix-map`: supported since GCC 4.3 (2008-03), Clang 3.8 (2016-03): * * * `-fmacro-prefix-map`: supported since GCC 8.1 (2018-05), Clang 10.0 (2020-03) * * --- src/lib.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4ccb5f3cc..38b6137d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,6 +360,11 @@ struct CompilerFlag { flag: Box, } +enum PrefixMapFlag { + Macro, + Debug, +} + #[derive(Debug, Default)] struct BuildCache { apple_sdk_root_cache: RwLock, Arc>>, @@ -2083,7 +2088,7 @@ impl Build { // Add path remap flags inherited from cargo's `-Ztrim-paths`. if self.inherit_trim_paths { - self.add_trim_paths_flags(&mut cmd)?; + self.add_trim_paths_flags(&mut cmd, &target)?; } // Set flags configured in the builder (do this second-to-last, to allow these to override @@ -2702,7 +2707,7 @@ impl Build { /// Translate cargo's `-Ztrim-paths` remap rules into compiler flags. /// /// [`trim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option - fn add_trim_paths_flags(&self, cmd: &mut Tool) -> Result<(), Error> { + fn add_trim_paths_flags(&self, cmd: &mut Tool, target: &TargetInfo<'_>) -> Result<(), Error> { // MSVC has no equivalent of the `-f*-prefix-map` flag family. // Left out until there is demand for it. if cmd.is_like_msvc() { @@ -2744,6 +2749,12 @@ impl Build { _ => {} } } + + let macro_scope = + macro_scope && self.probe_prefix_map_flag(PrefixMapFlag::Macro, cmd, target); + let object_scope = + object_scope && self.probe_prefix_map_flag(PrefixMapFlag::Debug, cmd, target); + if !macro_scope && !object_scope { return Ok(()); } @@ -2767,6 +2778,45 @@ impl Build { Ok(()) } + /// Check if `-f*-prefix-map` flag is supported. + /// + /// * `-fdebug-prefix-map`: supported since GCC 4.3 (2008-03), Clang 3.8 (2016-03): + /// * + /// * + /// * `-fmacro-prefix-map`: supported since GCC 8.1 (2018-05), Clang 10.0 (2020-03) + /// * + /// * + fn probe_prefix_map_flag( + &self, + flag: PrefixMapFlag, + cmd: &Tool, + target: &TargetInfo<'_>, + ) -> bool { + let (flag, unsupported_warning) = match flag { + PrefixMapFlag::Macro => ( + "-fmacro-prefix-map", + "paths embedded by macros will not be remapped", + ), + PrefixMapFlag::Debug => ( + "-fdebug-prefix-map", + "paths embedded in debug info will not be remapped", + ), + }; + let probe = format!("{flag}=/probe=/probe"); + let supported = self + .is_flag_supported_inner(OsStr::new(&probe), cmd, target) + .unwrap_or(false); + + if !supported { + self.cargo_output.print_warning(&format_args!( + "{flag} is not supported by {:?}, {unsupported_warning}", + cmd.path() + )); + } + + supported + } + fn msvc_macro_assembler(&self) -> Result { let target = self.get_target()?; let tool = match target.arch {