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 7, 2024
1 parent 290a629 commit 306b961
Showing 1 changed file with 197 additions and 0 deletions.
197 changes: 197 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 @@ -1313,6 +1315,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 +1915,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)?;
}

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

fn add_inherited_rustflags(&self, cmd: &mut Tool) -> 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))
.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 +4263,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=") => {
let (_, rustc_val) = flag.split_once("=").unwrap();
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())
}
_ if flag.starts_with("embed-bitcode=") => {
let (_, rustc_val) = flag.split_once("=").unwrap();
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())
}
_ if flag.starts_with("force-frame-pointers") => {
let force_frame_pointers = if flag.contains("=") {
let (_, rustc_val) = flag.split_once("=").unwrap();
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") => {
if flag.contains("=") {
let (_, rustc_val) = flag.split_once("=").unwrap();
match rustc_val {
"n" | "no" | "off" | "false" => Some("-dead_strip".into()),
_ => None,
}
} else {
None
}
}
_ if flag.starts_with("lto") => {
let lto_mode = if flag.contains("=") {
let (_, rustc_val) = flag.split_once("=").unwrap();
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 flag.contains("=") {
let (_, rustc_val) = flag.split_once("=").unwrap();
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=") => {
let (_, rustc_val) = flag.split_once("=").unwrap();
let cc_flag = match rustc_val {
"pic" => "-fPIC",
"pie" => "-fPIE",
"dynamic-no-pic" => "-mdynamic-no-pic",
_ => return None,
};
Some(cc_flag.into())
}
_ if flag.starts_with("soft-float") => {
let soft_float = if flag.contains("=") {
let (_, rustc_val) = flag.split_once("=").unwrap();
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=") => {
let (_, rustc_val) = flag.split_once("=").unwrap();
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())
}
_ if flag.starts_with("force-frame-pointers") => {
let force_frame_pointers = if flag.contains("=") {
let (_, rustc_val) = flag.split_once("=").unwrap();
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

0 comments on commit 306b961

Please sign in to comment.