Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,11 @@ struct CompilerFlag {
flag: Box<OsStr>,
}

enum PrefixMapFlag {
Macro,
Debug,
}

#[derive(Debug, Default)]
struct BuildCache {
apple_sdk_root_cache: RwLock<HashMap<Box<str>, Arc<OsStr>>>,
Expand Down Expand Up @@ -420,6 +425,7 @@ pub struct Build {
shell_escaped_flags: Option<bool>,
build_cache: Arc<BuildCache>,
inherit_rustflags: bool,
inherit_trim_paths: bool,
prefer_clang_cl_over_msvc: bool,
}

Expand Down Expand Up @@ -549,6 +555,7 @@ impl Build {
shell_escaped_flags: None,
build_cache: Arc::default(),
inherit_rustflags: true,
inherit_trim_paths: true,
prefer_clang_cl_over_msvc: false,
}
}
Expand Down Expand Up @@ -1380,6 +1387,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.
///
/// <div class="warning">
///
/// [`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.
///
/// </div>
///
/// [`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`.
Expand Down Expand Up @@ -1495,6 +1525,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);
Expand Down Expand Up @@ -2055,6 +2086,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, &target)?;
}

// Set flags configured in the builder (do this second-to-last, to allow these to override
// everything above).
for flag in self.flags.iter() {
Expand Down Expand Up @@ -2668,6 +2704,119 @@ 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, 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() {
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;
Comment thread
NobodyXu marked this conversation as resolved.
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;
Comment thread
NobodyXu marked this conversation as resolved.
break;
}
_ => {}
}
}

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(());
}

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(())
}

/// Check if `-f*-prefix-map` flag is supported.
///
/// * `-fdebug-prefix-map`: supported since GCC 4.3 (2008-03), Clang 3.8 (2016-03):
/// * <https://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Debugging-Options.html>
/// * <https://github.com/llvm/llvm-project/commit/436256a71316a1e6ad68ebee8439c88d75>
/// * `-fmacro-prefix-map`: supported since GCC 8.1 (2018-05), Clang 10.0 (2020-03)
/// * <https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Option-Summary.html>
/// * <https://releases.llvm.org/10.0.0/tools/clang/docs/ReleaseNotes.html>
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<Command, Error> {
let target = self.get_target()?;
let tool = match target.arch {
Expand Down
142 changes: 142 additions & 0 deletions tests/trim_paths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! 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;

// `<from>=<to>` 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_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_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_have(flag);
}
for flag in MACRO_FLAGS {
cmd.must_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_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()
.inherit_trim_paths(false)
.file("foo.c")
.compile("foo");

let cmd = test.cmd(0);
for flag in MACRO_FLAGS.iter().chain(OBJECT_FLAGS) {
cmd.must_not_have(flag);
}
}
Loading