Skip to content

Commit

Permalink
build: Inherit flags from rustc
Browse files Browse the repository at this point in the history
Where applicable, detect which RUSTFLAGS were set for rustc and convert
them into their corresponding cc flags in order to ensure consistent
codegen across Rust and non-Rust modules.
  • Loading branch information
mrkajetanp committed Nov 12, 2024
1 parent 290a629 commit a9098fa
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 0 deletions.
204 changes: 204 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ pub struct Build {
emit_rerun_if_env_changed: bool,
cached_compiler_family: Arc<RwLock<HashMap<Box<Path>, ToolFamily>>>,
shell_escaped_flags: Option<bool>,
inherit_rustflags: bool,
}

/// Represents the types of errors that may occur while using cc-rs.
Expand Down Expand Up @@ -437,6 +438,7 @@ impl Build {
emit_rerun_if_env_changed: true,
cached_compiler_family: Arc::default(),
shell_escaped_flags: None,
inherit_rustflags: true,
}
}

Expand Down Expand Up @@ -664,6 +666,7 @@ impl Build {
.debug(false)
.cpp(self.cpp)
.cuda(self.cuda)
.inherit_rustflags(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 @@ -1313,6 +1316,15 @@ impl Build {
self
}

/// Configure whether cc should automatically inherit compatible flags passed to rustc
/// from `CARGO_ENCODED_RUSTFLAGS`.
///
/// This option defaults to `true`.
pub fn inherit_rustflags(&mut self, inherit_rustflags: bool) -> &mut Build {
self.inherit_rustflags = inherit_rustflags;
self
}

#[doc(hidden)]
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build
where
Expand Down Expand Up @@ -1904,6 +1916,11 @@ impl Build {
cmd.args.push((**flag).into());
}

// Add cc flags inherited from matching rustc flags
if self.inherit_rustflags {
self.add_inherited_rustflags(&mut cmd, &target)?;
}

for flag in self.flags_supported.iter() {
if self
.is_flag_supported_inner(flag, &cmd.path, &target)
Expand Down Expand Up @@ -2439,6 +2456,38 @@ impl Build {
Ok(())
}

fn add_inherited_rustflags(&self, cmd: &mut Tool, target: &Target) -> Result<(), Error> {
let env_os = match self.getenv("CARGO_ENCODED_RUSTFLAGS") {
Some(env) => env,
// No encoded RUSTFLAGS -> nothing to do
None => return Ok(()),
};

let mut cc_flags: Vec<OsString> = env_os
.to_string_lossy()
.split("\u{1f}")
// Strip prefixes from rustc flags
.flat_map(|flag| {
if flag == "-Z" || flag == "-C" {
None
} else if flag.starts_with("-Z") || flag.starts_with("-C") {
Some(&flag[2..])
} else {
Some(flag)
}
})
.flat_map(|flag| rustc_to_cc_flag(flag, cmd.family))
// Filter out flags not supported by the currently used CC
.filter(|flag| {
self.is_flag_supported_inner(flag, &cmd.path, &target)
.unwrap_or(false)
})
.collect();

cmd.args.append(&mut cc_flags);
Ok(())
}

fn has_flags(&self) -> bool {
let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
let flags_env_var_value = self.getenv_with_target_prefixes(flags_env_var_name);
Expand Down Expand Up @@ -4221,6 +4270,161 @@ fn map_darwin_target_from_rust_to_compiler_architecture(target: &Target) -> &str
}
}

// Rust and clang/cc don't agree on what equivalent flags should look like either.
fn rustc_to_cc_flag(flag: &str, family: ToolFamily) -> Option<OsString> {
match family {
ToolFamily::Clang { .. } | ToolFamily::Gnu => match flag {
_ if flag.starts_with("branch-protection") => {
Some(format!("-m{}", flag.replace(",", "+")).into())
}
_ if flag.starts_with("code-model") => {
Some(flag.replace("code-model", "-mcmodel").into())
}
_ if flag.starts_with("no-vectorize-loops") => Some("-fno-vectorize".into()),
_ if flag.starts_with("no-vectorize-slp") => Some("-fno-slp-vectorize".into()),
_ if flag.starts_with("profile-generate") => Some(format!("-f{flag}").into()),
_ if flag.starts_with("profile-use") => Some(format!("-f{flag}").into()),
_ if flag.starts_with("control-flow-guard") => {
if let Some((_, rustc_val)) = flag.split_once("=") {
let cc_val = match rustc_val {
"y" | "yes" | "on" | "true" | "checks" => "cf",
"nochecks" => "cf-nochecks",
"n" | "no" | "off" | "false" => "none",
_ => return None,
};
Some(format!("-mguard={cc_val}").into())
} else {
None
}
}
_ if flag.starts_with("embed-bitcode") => {
if let Some((_, rustc_val)) = flag.split_once("=") {
let cc_val = match rustc_val {
"y" | "yes" | "on" | "true" => "all",
"n" | "no" | "off" | "false" => "off",
_ => return None,
};
Some(format!("-fembed-bitcode={cc_val}").into())
} else {
None
}
}
_ if flag.starts_with("force-frame-pointers") => {
let force_frame_pointers = if let Some((_, rustc_val)) = flag.split_once("=") {
match rustc_val {
"y" | "yes" | "on" | "true" => true,
"n" | "no" | "off" | "false" => false,
_ => return None,
}
} else {
true
};
let cc_flag = if force_frame_pointers {
"-fno-omit-frame-pointer"
} else {
"-fomit-frame-pointer"
};
Some(cc_flag.into())
}
_ if flag.starts_with("link-dead-code") => match flag.split_once("=") {
Some((_, "n" | "no" | "off" | "false")) => Some("-dead_strip".into()),
_ => None,
},
_ if flag.starts_with("lto") => {
let lto_mode = if let Some((_, rustc_val)) = flag.split_once("=") {
match rustc_val {
"y" | "yes" | "on" | "true" | "fat" => "full",
"thin" => "thin",
_ => return None,
}
} else {
"full"
};

Some(format!("-flto={lto_mode}").into())
}
_ if flag.starts_with("no-redzone") => {
let no_redzone = if let Some((_, rustc_val)) = flag.split_once("=") {
match rustc_val {
"y" | "yes" | "on" | "true" => true,
"n" | "no" | "off" | "false" => false,
_ => return None,
}
} else {
true
};

let cc_flag = if no_redzone {
"-mno-red-zone"
} else {
"-mred-zone"
};
Some(cc_flag.into())
}
_ if flag.starts_with("relocation-model") => {
if let Some((_, rustc_val)) = flag.split_once("=") {
let cc_flag = match rustc_val {
"pic" => "-fPIC",
"pie" => "-fPIE",
"dynamic-no-pic" => "-mdynamic-no-pic",
_ => return None,
};
Some(cc_flag.into())
} else {
None
}
}
_ if flag.starts_with("soft-float") => {
let soft_float = if let Some((_, rustc_val)) = flag.split_once("=") {
match rustc_val {
"y" | "yes" | "on" | "true" => true,
"n" | "no" | "off" | "false" => false,
_ => return None,
}
} else {
true
};

let cc_flag = if soft_float {
"-msoft-float"
} else {
"-mno-soft-float"
};
Some(cc_flag.into())
}
_ => None,
},
ToolFamily::Msvc { .. } => match flag {
_ if flag.starts_with("control-flow-guard") => {
if let Some((_, rustc_val)) = flag.split_once("=") {
let cc_val = match rustc_val {
"y" | "yes" | "on" | "true" | "checks" => "cf",
"n" | "no" | "off" | "false" => "cf-",
_ => return None,
};
Some(format!("/guard:{cc_val}").into())
} else {
None
}
}
_ if flag.starts_with("force-frame-pointers") => {
let force_frame_pointers = if let Some((_, rustc_val)) = flag.split_once("=") {
match rustc_val {
"y" | "yes" | "on" | "true" => true,
"n" | "no" | "off" | "false" => false,
_ => return None,
}
} else {
true
};
let cc_flag = if force_frame_pointers { "/Oy-" } else { "/Oy" };
Some(cc_flag.into())
}
_ => None,
},
}
}

#[derive(Clone, Copy, PartialEq)]
enum AsmFileExt {
/// `.asm` files. On MSVC targets, we assume these should be passed to MASM
Expand Down
29 changes: 29 additions & 0 deletions tests/rustflags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::support::Test;
mod support;

/// This test is in its own module because it modifies the environment and would affect other tests
/// when run in parallel with them.
#[test]
#[cfg(not(windows))]
fn inherits_rustflags() {
// Sanity check - no flags
std::env::set_var("CARGO_ENCODED_RUSTFLAGS", "");
let test = Test::gnu();
test.gcc().file("foo.c").compile("foo");
test.cmd(0)
.must_not_have("-fno-omit-frame-pointer")
.must_not_have("-mcmodel=small")
.must_not_have("-msoft-float");

// Correctly inherits flags from rustc
std::env::set_var(
"CARGO_ENCODED_RUSTFLAGS",
"-Cforce-frame-pointers=true\u{1f}-Ccode-model=small\u{1f}-Csoft-float",
);
let test = Test::gnu();
test.gcc().file("foo.c").compile("foo");
test.cmd(0)
.must_have("-fno-omit-frame-pointer")
.must_have("-mcmodel=small")
.must_have("-msoft-float");
}

0 comments on commit a9098fa

Please sign in to comment.