Skip to content

Commit a9098fa

Browse files
committed
build: Inherit flags from rustc
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.
1 parent 290a629 commit a9098fa

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

src/lib.rs

+204
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ pub struct Build {
312312
emit_rerun_if_env_changed: bool,
313313
cached_compiler_family: Arc<RwLock<HashMap<Box<Path>, ToolFamily>>>,
314314
shell_escaped_flags: Option<bool>,
315+
inherit_rustflags: bool,
315316
}
316317

317318
/// Represents the types of errors that may occur while using cc-rs.
@@ -437,6 +438,7 @@ impl Build {
437438
emit_rerun_if_env_changed: true,
438439
cached_compiler_family: Arc::default(),
439440
shell_escaped_flags: None,
441+
inherit_rustflags: true,
440442
}
441443
}
442444

@@ -664,6 +666,7 @@ impl Build {
664666
.debug(false)
665667
.cpp(self.cpp)
666668
.cuda(self.cuda)
669+
.inherit_rustflags(false)
667670
.emit_rerun_if_env_changed(self.emit_rerun_if_env_changed);
668671
if let Some(target) = &self.target {
669672
cfg.target(target);
@@ -1313,6 +1316,15 @@ impl Build {
13131316
self
13141317
}
13151318

1319+
/// Configure whether cc should automatically inherit compatible flags passed to rustc
1320+
/// from `CARGO_ENCODED_RUSTFLAGS`.
1321+
///
1322+
/// This option defaults to `true`.
1323+
pub fn inherit_rustflags(&mut self, inherit_rustflags: bool) -> &mut Build {
1324+
self.inherit_rustflags = inherit_rustflags;
1325+
self
1326+
}
1327+
13161328
#[doc(hidden)]
13171329
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build
13181330
where
@@ -1904,6 +1916,11 @@ impl Build {
19041916
cmd.args.push((**flag).into());
19051917
}
19061918

1919+
// Add cc flags inherited from matching rustc flags
1920+
if self.inherit_rustflags {
1921+
self.add_inherited_rustflags(&mut cmd, &target)?;
1922+
}
1923+
19071924
for flag in self.flags_supported.iter() {
19081925
if self
19091926
.is_flag_supported_inner(flag, &cmd.path, &target)
@@ -2439,6 +2456,38 @@ impl Build {
24392456
Ok(())
24402457
}
24412458

2459+
fn add_inherited_rustflags(&self, cmd: &mut Tool, target: &Target) -> Result<(), Error> {
2460+
let env_os = match self.getenv("CARGO_ENCODED_RUSTFLAGS") {
2461+
Some(env) => env,
2462+
// No encoded RUSTFLAGS -> nothing to do
2463+
None => return Ok(()),
2464+
};
2465+
2466+
let mut cc_flags: Vec<OsString> = env_os
2467+
.to_string_lossy()
2468+
.split("\u{1f}")
2469+
// Strip prefixes from rustc flags
2470+
.flat_map(|flag| {
2471+
if flag == "-Z" || flag == "-C" {
2472+
None
2473+
} else if flag.starts_with("-Z") || flag.starts_with("-C") {
2474+
Some(&flag[2..])
2475+
} else {
2476+
Some(flag)
2477+
}
2478+
})
2479+
.flat_map(|flag| rustc_to_cc_flag(flag, cmd.family))
2480+
// Filter out flags not supported by the currently used CC
2481+
.filter(|flag| {
2482+
self.is_flag_supported_inner(flag, &cmd.path, &target)
2483+
.unwrap_or(false)
2484+
})
2485+
.collect();
2486+
2487+
cmd.args.append(&mut cc_flags);
2488+
Ok(())
2489+
}
2490+
24422491
fn has_flags(&self) -> bool {
24432492
let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
24442493
let flags_env_var_value = self.getenv_with_target_prefixes(flags_env_var_name);
@@ -4221,6 +4270,161 @@ fn map_darwin_target_from_rust_to_compiler_architecture(target: &Target) -> &str
42214270
}
42224271
}
42234272

4273+
// Rust and clang/cc don't agree on what equivalent flags should look like either.
4274+
fn rustc_to_cc_flag(flag: &str, family: ToolFamily) -> Option<OsString> {
4275+
match family {
4276+
ToolFamily::Clang { .. } | ToolFamily::Gnu => match flag {
4277+
_ if flag.starts_with("branch-protection") => {
4278+
Some(format!("-m{}", flag.replace(",", "+")).into())
4279+
}
4280+
_ if flag.starts_with("code-model") => {
4281+
Some(flag.replace("code-model", "-mcmodel").into())
4282+
}
4283+
_ if flag.starts_with("no-vectorize-loops") => Some("-fno-vectorize".into()),
4284+
_ if flag.starts_with("no-vectorize-slp") => Some("-fno-slp-vectorize".into()),
4285+
_ if flag.starts_with("profile-generate") => Some(format!("-f{flag}").into()),
4286+
_ if flag.starts_with("profile-use") => Some(format!("-f{flag}").into()),
4287+
_ if flag.starts_with("control-flow-guard") => {
4288+
if let Some((_, rustc_val)) = flag.split_once("=") {
4289+
let cc_val = match rustc_val {
4290+
"y" | "yes" | "on" | "true" | "checks" => "cf",
4291+
"nochecks" => "cf-nochecks",
4292+
"n" | "no" | "off" | "false" => "none",
4293+
_ => return None,
4294+
};
4295+
Some(format!("-mguard={cc_val}").into())
4296+
} else {
4297+
None
4298+
}
4299+
}
4300+
_ if flag.starts_with("embed-bitcode") => {
4301+
if let Some((_, rustc_val)) = flag.split_once("=") {
4302+
let cc_val = match rustc_val {
4303+
"y" | "yes" | "on" | "true" => "all",
4304+
"n" | "no" | "off" | "false" => "off",
4305+
_ => return None,
4306+
};
4307+
Some(format!("-fembed-bitcode={cc_val}").into())
4308+
} else {
4309+
None
4310+
}
4311+
}
4312+
_ if flag.starts_with("force-frame-pointers") => {
4313+
let force_frame_pointers = if let Some((_, rustc_val)) = flag.split_once("=") {
4314+
match rustc_val {
4315+
"y" | "yes" | "on" | "true" => true,
4316+
"n" | "no" | "off" | "false" => false,
4317+
_ => return None,
4318+
}
4319+
} else {
4320+
true
4321+
};
4322+
let cc_flag = if force_frame_pointers {
4323+
"-fno-omit-frame-pointer"
4324+
} else {
4325+
"-fomit-frame-pointer"
4326+
};
4327+
Some(cc_flag.into())
4328+
}
4329+
_ if flag.starts_with("link-dead-code") => match flag.split_once("=") {
4330+
Some((_, "n" | "no" | "off" | "false")) => Some("-dead_strip".into()),
4331+
_ => None,
4332+
},
4333+
_ if flag.starts_with("lto") => {
4334+
let lto_mode = if let Some((_, rustc_val)) = flag.split_once("=") {
4335+
match rustc_val {
4336+
"y" | "yes" | "on" | "true" | "fat" => "full",
4337+
"thin" => "thin",
4338+
_ => return None,
4339+
}
4340+
} else {
4341+
"full"
4342+
};
4343+
4344+
Some(format!("-flto={lto_mode}").into())
4345+
}
4346+
_ if flag.starts_with("no-redzone") => {
4347+
let no_redzone = if let Some((_, rustc_val)) = flag.split_once("=") {
4348+
match rustc_val {
4349+
"y" | "yes" | "on" | "true" => true,
4350+
"n" | "no" | "off" | "false" => false,
4351+
_ => return None,
4352+
}
4353+
} else {
4354+
true
4355+
};
4356+
4357+
let cc_flag = if no_redzone {
4358+
"-mno-red-zone"
4359+
} else {
4360+
"-mred-zone"
4361+
};
4362+
Some(cc_flag.into())
4363+
}
4364+
_ if flag.starts_with("relocation-model") => {
4365+
if let Some((_, rustc_val)) = flag.split_once("=") {
4366+
let cc_flag = match rustc_val {
4367+
"pic" => "-fPIC",
4368+
"pie" => "-fPIE",
4369+
"dynamic-no-pic" => "-mdynamic-no-pic",
4370+
_ => return None,
4371+
};
4372+
Some(cc_flag.into())
4373+
} else {
4374+
None
4375+
}
4376+
}
4377+
_ if flag.starts_with("soft-float") => {
4378+
let soft_float = if let Some((_, rustc_val)) = flag.split_once("=") {
4379+
match rustc_val {
4380+
"y" | "yes" | "on" | "true" => true,
4381+
"n" | "no" | "off" | "false" => false,
4382+
_ => return None,
4383+
}
4384+
} else {
4385+
true
4386+
};
4387+
4388+
let cc_flag = if soft_float {
4389+
"-msoft-float"
4390+
} else {
4391+
"-mno-soft-float"
4392+
};
4393+
Some(cc_flag.into())
4394+
}
4395+
_ => None,
4396+
},
4397+
ToolFamily::Msvc { .. } => match flag {
4398+
_ if flag.starts_with("control-flow-guard") => {
4399+
if let Some((_, rustc_val)) = flag.split_once("=") {
4400+
let cc_val = match rustc_val {
4401+
"y" | "yes" | "on" | "true" | "checks" => "cf",
4402+
"n" | "no" | "off" | "false" => "cf-",
4403+
_ => return None,
4404+
};
4405+
Some(format!("/guard:{cc_val}").into())
4406+
} else {
4407+
None
4408+
}
4409+
}
4410+
_ if flag.starts_with("force-frame-pointers") => {
4411+
let force_frame_pointers = if let Some((_, rustc_val)) = flag.split_once("=") {
4412+
match rustc_val {
4413+
"y" | "yes" | "on" | "true" => true,
4414+
"n" | "no" | "off" | "false" => false,
4415+
_ => return None,
4416+
}
4417+
} else {
4418+
true
4419+
};
4420+
let cc_flag = if force_frame_pointers { "/Oy-" } else { "/Oy" };
4421+
Some(cc_flag.into())
4422+
}
4423+
_ => None,
4424+
},
4425+
}
4426+
}
4427+
42244428
#[derive(Clone, Copy, PartialEq)]
42254429
enum AsmFileExt {
42264430
/// `.asm` files. On MSVC targets, we assume these should be passed to MASM

tests/rustflags.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::support::Test;
2+
mod support;
3+
4+
/// This test is in its own module because it modifies the environment and would affect other tests
5+
/// when run in parallel with them.
6+
#[test]
7+
#[cfg(not(windows))]
8+
fn inherits_rustflags() {
9+
// Sanity check - no flags
10+
std::env::set_var("CARGO_ENCODED_RUSTFLAGS", "");
11+
let test = Test::gnu();
12+
test.gcc().file("foo.c").compile("foo");
13+
test.cmd(0)
14+
.must_not_have("-fno-omit-frame-pointer")
15+
.must_not_have("-mcmodel=small")
16+
.must_not_have("-msoft-float");
17+
18+
// Correctly inherits flags from rustc
19+
std::env::set_var(
20+
"CARGO_ENCODED_RUSTFLAGS",
21+
"-Cforce-frame-pointers=true\u{1f}-Ccode-model=small\u{1f}-Csoft-float",
22+
);
23+
let test = Test::gnu();
24+
test.gcc().file("foo.c").compile("foo");
25+
test.cmd(0)
26+
.must_have("-fno-omit-frame-pointer")
27+
.must_have("-mcmodel=small")
28+
.must_have("-msoft-float");
29+
}

0 commit comments

Comments
 (0)