summaryrefslogtreecommitdiff
path: root/vendor/cc/src/flags.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cc/src/flags.rs')
-rw-r--r--vendor/cc/src/flags.rs522
1 files changed, 522 insertions, 0 deletions
diff --git a/vendor/cc/src/flags.rs b/vendor/cc/src/flags.rs
new file mode 100644
index 00000000..91e9470a
--- /dev/null
+++ b/vendor/cc/src/flags.rs
@@ -0,0 +1,522 @@
+use crate::target::TargetInfo;
+use crate::{Build, Error, ErrorKind, Tool, ToolFamily};
+use std::borrow::Cow;
+use std::ffi::OsString;
+
+#[derive(Debug, PartialEq, Default)]
+pub(crate) struct RustcCodegenFlags<'a> {
+ branch_protection: Option<&'a str>,
+ code_model: Option<&'a str>,
+ no_vectorize_loops: bool,
+ no_vectorize_slp: bool,
+ profile_generate: Option<&'a str>,
+ profile_use: Option<&'a str>,
+ control_flow_guard: Option<&'a str>,
+ lto: Option<&'a str>,
+ relocation_model: Option<&'a str>,
+ embed_bitcode: Option<bool>,
+ force_frame_pointers: Option<bool>,
+ no_redzone: Option<bool>,
+ soft_float: Option<bool>,
+ dwarf_version: Option<u32>,
+}
+
+impl<'this> RustcCodegenFlags<'this> {
+ // Parse flags obtained from CARGO_ENCODED_RUSTFLAGS
+ pub(crate) fn parse(rustflags_env: &'this str) -> Result<Self, Error> {
+ fn is_flag_prefix(flag: &str) -> bool {
+ [
+ "-Z",
+ "-C",
+ "--codegen",
+ "-L",
+ "-l",
+ "-o",
+ "-W",
+ "--warn",
+ "-A",
+ "--allow",
+ "-D",
+ "--deny",
+ "-F",
+ "--forbid",
+ ]
+ .contains(&flag)
+ }
+
+ fn handle_flag_prefix<'a>(prev: &'a str, curr: &'a str) -> (&'a str, &'a str) {
+ match prev {
+ "--codegen" | "-C" => ("-C", curr),
+ // Handle flags passed like --codegen=code-model=small
+ _ if curr.starts_with("--codegen=") => ("-C", &curr[10..]),
+ "-Z" => ("-Z", curr),
+ "-L" | "-l" | "-o" => (prev, curr),
+ // Handle lint flags
+ "-W" | "--warn" => ("-W", curr),
+ "-A" | "--allow" => ("-A", curr),
+ "-D" | "--deny" => ("-D", curr),
+ "-F" | "--forbid" => ("-F", curr),
+ _ => ("", curr),
+ }
+ }
+
+ let mut codegen_flags = Self::default();
+
+ let mut prev_prefix = None;
+ for curr in rustflags_env.split("\u{1f}") {
+ let prev = prev_prefix.take().unwrap_or("");
+ if prev.is_empty() && is_flag_prefix(curr) {
+ prev_prefix = Some(curr);
+ continue;
+ }
+
+ let (prefix, rustc_flag) = handle_flag_prefix(prev, curr);
+ codegen_flags.set_rustc_flag(prefix, rustc_flag)?;
+ }
+
+ Ok(codegen_flags)
+ }
+
+ fn set_rustc_flag(&mut self, prefix: &str, flag: &'this str) -> Result<(), Error> {
+ // Convert a textual representation of a bool-like rustc flag argument into an actual bool
+ fn arg_to_bool(arg: impl AsRef<str>) -> Option<bool> {
+ match arg.as_ref() {
+ "y" | "yes" | "on" | "true" => Some(true),
+ "n" | "no" | "off" | "false" => Some(false),
+ _ => None,
+ }
+ }
+
+ fn arg_to_u32(arg: impl AsRef<str>) -> Option<u32> {
+ arg.as_ref().parse().ok()
+ }
+
+ let (flag, value) = if let Some((flag, value)) = flag.split_once('=') {
+ (flag, Some(value))
+ } else {
+ (flag, None)
+ };
+ let flag = if prefix.is_empty() {
+ Cow::Borrowed(flag)
+ } else {
+ Cow::Owned(format!("{prefix}{flag}"))
+ };
+
+ fn flag_ok_or<'flag>(
+ flag: Option<&'flag str>,
+ msg: &'static str,
+ ) -> Result<&'flag str, Error> {
+ flag.ok_or(Error::new(ErrorKind::InvalidFlag, msg))
+ }
+
+ match flag.as_ref() {
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#code-model
+ "-Ccode-model" => {
+ self.code_model = Some(flag_ok_or(value, "-Ccode-model must have a value")?);
+ }
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-loops
+ "-Cno-vectorize-loops" => self.no_vectorize_loops = true,
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-slp
+ "-Cno-vectorize-slp" => self.no_vectorize_slp = true,
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-generate
+ "-Cprofile-generate" => {
+ self.profile_generate =
+ Some(flag_ok_or(value, "-Cprofile-generate must have a value")?);
+ }
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-use
+ "-Cprofile-use" => {
+ self.profile_use = Some(flag_ok_or(value, "-Cprofile-use must have a value")?);
+ }
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#control-flow-guard
+ "-Ccontrol-flow-guard" => self.control_flow_guard = value.or(Some("true")),
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#lto
+ "-Clto" => self.lto = value.or(Some("true")),
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model
+ "-Crelocation-model" => {
+ self.relocation_model =
+ Some(flag_ok_or(value, "-Crelocation-model must have a value")?);
+ }
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#embed-bitcode
+ "-Cembed-bitcode" => self.embed_bitcode = value.map_or(Some(true), arg_to_bool),
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#force-frame-pointers
+ "-Cforce-frame-pointers" => {
+ self.force_frame_pointers = value.map_or(Some(true), arg_to_bool)
+ }
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-redzone
+ "-Cno-redzone" => self.no_redzone = value.map_or(Some(true), arg_to_bool),
+ // https://doc.rust-lang.org/rustc/codegen-options/index.html#soft-float
+ // Note: This flag is now deprecated in rustc.
+ "-Csoft-float" => self.soft_float = value.map_or(Some(true), arg_to_bool),
+ // https://doc.rust-lang.org/beta/unstable-book/compiler-flags/branch-protection.html
+ // FIXME: Drop the -Z variant and update the doc link once the option is stabilised
+ "-Zbranch-protection" | "-Cbranch-protection" => {
+ self.branch_protection =
+ Some(flag_ok_or(value, "-Zbranch-protection must have a value")?);
+ }
+ // https://doc.rust-lang.org/beta/unstable-book/compiler-flags/dwarf-version.html
+ // FIXME: Drop the -Z variant and update the doc link once the option is stablized
+ "-Zdwarf-version" | "-Cdwarf-version" => {
+ self.dwarf_version = Some(value.and_then(arg_to_u32).ok_or(Error::new(
+ ErrorKind::InvalidFlag,
+ "-Zdwarf-version must have a value",
+ ))?);
+ }
+ _ => {}
+ }
+ Ok(())
+ }
+
+ // Rust and clang/cc don't agree on what equivalent flags should look like.
+ pub(crate) fn cc_flags(&self, build: &Build, tool: &mut Tool, target: &TargetInfo<'_>) {
+ let family = tool.family;
+ // Push `flag` to `flags` if it is supported by the currently used CC
+ let mut push_if_supported = |flag: OsString| {
+ if build
+ .is_flag_supported_inner(&flag, tool, target)
+ .unwrap_or(false)
+ {
+ tool.args.push(flag);
+ } else {
+ build.cargo_output.print_warning(&format!(
+ "Inherited flag {:?} is not supported by the currently used CC",
+ flag
+ ));
+ }
+ };
+
+ let clang_or_gnu =
+ matches!(family, ToolFamily::Clang { .. }) || matches!(family, ToolFamily::Gnu);
+
+ // Flags shared between clang and gnu
+ if clang_or_gnu {
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mbranch-protection
+ // https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mbranch-protection (Aarch64)
+ // https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html#index-mbranch-protection-1 (ARM)
+ // https://developer.arm.com/documentation/101754/0619/armclang-Reference/armclang-Command-line-Options/-mbranch-protection
+ if let Some(value) = self.branch_protection {
+ push_if_supported(
+ format!("-mbranch-protection={}", value.replace(",", "+")).into(),
+ );
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mcmodel
+ // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mcmodel=`).
+ // FIXME(madsmtm): Parse the model, to make sure we pass the correct value (depending on arch).
+ if let Some(value) = self.code_model {
+ push_if_supported(format!("-mcmodel={value}").into());
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-vectorize
+ // https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html
+ if self.no_vectorize_loops {
+ push_if_supported("-fno-vectorize".into());
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-slp-vectorize
+ // https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html
+ if self.no_vectorize_slp {
+ push_if_supported("-fno-slp-vectorize".into());
+ }
+ if let Some(value) = self.relocation_model {
+ let cc_flag = match value {
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIC
+ // https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIC
+ "pic" => Some("-fPIC"),
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIE
+ // https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIE
+ "pie" => Some("-fPIE"),
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mdynamic-no-pic
+ // https://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html#index-mdynamic-no-pic
+ "dynamic-no-pic" => Some("-mdynamic-no-pic"),
+ _ => None,
+ };
+ if let Some(cc_flag) = cc_flag {
+ push_if_supported(cc_flag.into());
+ }
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-omit-frame-pointer
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer
+ // https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fomit-frame-pointer
+ if let Some(value) = self.force_frame_pointers {
+ let cc_flag = if value {
+ "-fno-omit-frame-pointer"
+ } else {
+ "-fomit-frame-pointer"
+ };
+ push_if_supported(cc_flag.into());
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mno-red-zone
+ // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mno-red-zone
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mred-zone
+ // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mred-zone
+ if let Some(value) = self.no_redzone {
+ let cc_flag = if value { "-mno-red-zone" } else { "-mred-zone" };
+ push_if_supported(cc_flag.into());
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-msoft-float
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mhard-float
+ // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-msoft-float`).
+ // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mhard-float`).
+ if let Some(value) = self.soft_float {
+ let cc_flag = if value {
+ "-msoft-float"
+ } else {
+ // Do not use -mno-soft-float, that's basically just an alias for -mno-implicit-float.
+ "-mhard-float"
+ };
+ push_if_supported(cc_flag.into());
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gdwarf-2
+ // https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#index-gdwarf
+ if let Some(value) = self.dwarf_version {
+ push_if_supported(format!("-gdwarf-{value}").into());
+ }
+ }
+
+ // Compiler-exclusive flags
+ match family {
+ ToolFamily::Clang { .. } => {
+ // GNU and Clang compilers both support the same PGO flags, but they use different libraries and
+ // different formats for the profile files which are not compatible.
+ // clang and rustc both internally use llvm, so we want to inherit the PGO flags only for clang.
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-generate
+ if let Some(value) = self.profile_generate {
+ push_if_supported(format!("-fprofile-generate={value}").into());
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-use
+ if let Some(value) = self.profile_use {
+ push_if_supported(format!("-fprofile-use={value}").into());
+ }
+
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fembed-bitcode
+ if let Some(value) = self.embed_bitcode {
+ let cc_val = if value { "all" } else { "off" };
+ push_if_supported(format!("-fembed-bitcode={cc_val}").into());
+ }
+
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-flto
+ if let Some(value) = self.lto {
+ let cc_val = match value {
+ "y" | "yes" | "on" | "true" | "fat" => Some("full"),
+ "thin" => Some("thin"),
+ _ => None,
+ };
+ if let Some(cc_val) = cc_val {
+ push_if_supported(format!("-flto={cc_val}").into());
+ }
+ }
+ // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mguard
+ if let Some(value) = self.control_flow_guard {
+ let cc_val = match value {
+ "y" | "yes" | "on" | "true" | "checks" => Some("cf"),
+ "nochecks" => Some("cf-nochecks"),
+ "n" | "no" | "off" | "false" => Some("none"),
+ _ => None,
+ };
+ if let Some(cc_val) = cc_val {
+ push_if_supported(format!("-mguard={cc_val}").into());
+ }
+ }
+ }
+ ToolFamily::Gnu => {}
+ ToolFamily::Msvc { .. } => {
+ // https://learn.microsoft.com/en-us/cpp/build/reference/guard-enable-control-flow-guard
+ if let Some(value) = self.control_flow_guard {
+ let cc_val = match value {
+ "y" | "yes" | "on" | "true" | "checks" => Some("cf"),
+ "n" | "no" | "off" | "false" => Some("cf-"),
+ _ => None,
+ };
+ if let Some(cc_val) = cc_val {
+ push_if_supported(format!("/guard:{cc_val}").into());
+ }
+ }
+ // https://learn.microsoft.com/en-us/cpp/build/reference/oy-frame-pointer-omission
+ if let Some(value) = self.force_frame_pointers {
+ // Flag is unsupported on 64-bit arches
+ if !target.arch.contains("64") {
+ let cc_flag = if value { "/Oy-" } else { "/Oy" };
+ push_if_supported(cc_flag.into());
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[track_caller]
+ fn check(env: &str, expected: &RustcCodegenFlags) {
+ let actual = RustcCodegenFlags::parse(env).unwrap();
+ assert_eq!(actual, *expected);
+ }
+
+ #[test]
+ fn codegen_type() {
+ let expected = RustcCodegenFlags {
+ code_model: Some("tiny"),
+ ..RustcCodegenFlags::default()
+ };
+ check("-Ccode-model=tiny", &expected);
+ check("-C\u{1f}code-model=tiny", &expected);
+ check("--codegen\u{1f}code-model=tiny", &expected);
+ check("--codegen=code-model=tiny", &expected);
+ }
+
+ #[test]
+ fn precedence() {
+ check(
+ "-ccode-model=tiny\u{1f}-Ccode-model=small",
+ &RustcCodegenFlags {
+ code_model: Some("small"),
+ ..RustcCodegenFlags::default()
+ },
+ );
+ }
+
+ #[test]
+ fn two_valid_prefixes() {
+ let expected = RustcCodegenFlags::default();
+ check("-L\u{1f}-Clto", &expected);
+ }
+
+ #[test]
+ fn three_valid_prefixes() {
+ let expected = RustcCodegenFlags {
+ lto: Some("true"),
+ ..RustcCodegenFlags::default()
+ };
+ check("-L\u{1f}-L\u{1f}-Clto", &expected);
+ }
+
+ #[test]
+ fn all_rustc_flags() {
+ // Throw all possible flags at the parser to catch false positives
+ let flags = [
+ // Set all the flags we recognise first
+ "-Ccode-model=tiny",
+ "-Ccontrol-flow-guard=yes",
+ "-Cembed-bitcode=no",
+ "-Cforce-frame-pointers=yes",
+ "-Clto=false",
+ "-Clink-dead-code=yes",
+ "-Cno-redzone=yes",
+ "-Cno-vectorize-loops",
+ "-Cno-vectorize-slp",
+ "-Cprofile-generate=fooprofile",
+ "-Cprofile-use=fooprofile",
+ "-Crelocation-model=pic",
+ "-Csoft-float=yes",
+ "-Zbranch-protection=bti,pac-ret,leaf",
+ "-Zdwarf-version=5",
+ // Set flags we don't recognise but rustc supports next
+ // rustc flags
+ "--cfg",
+ "a",
+ "--check-cfg 'cfg(verbose)",
+ "-L",
+ "/usr/lib/foo",
+ "-l",
+ "static:+whole-archive=mylib",
+ "--crate-type=dylib",
+ "--crate-name=foo",
+ "--edition=2021",
+ "--emit=asm",
+ "--print=crate-name",
+ "-g",
+ "-O",
+ "-o",
+ "foooutput",
+ "--out-dir",
+ "foooutdir",
+ "--target",
+ "aarch64-unknown-linux-gnu",
+ "-W",
+ "missing-docs",
+ "-D",
+ "unused-variables",
+ "--force-warn",
+ "dead-code",
+ "-A",
+ "unused",
+ "-F",
+ "unused",
+ "--cap-lints",
+ "warn",
+ "--version",
+ "--verbose",
+ "-v",
+ "--extern",
+ "foocrate",
+ "--sysroot",
+ "fooroot",
+ "--error-format",
+ "human",
+ "--color",
+ "auto",
+ "--diagnostic-width",
+ "80",
+ "--remap-path-prefix",
+ "foo=bar",
+ "--json=artifact",
+ // Codegen flags
+ "-Car",
+ "-Ccodegen-units=1",
+ "-Ccollapse-macro-debuginfo=yes",
+ "-Cdebug-assertions=yes",
+ "-Cdebuginfo=1",
+ "-Cdefault-linker-libraries=yes",
+ "-Cdlltool=foo",
+ "-Cextra-filename=foo",
+ "-Cforce-unwind-tables=yes",
+ "-Cincremental=foodir",
+ "-Cinline-threshold=6",
+ "-Cinstrument-coverage",
+ "-Clink-arg=-foo",
+ "-Clink-args=-foo",
+ "-Clink-self-contained=yes",
+ "-Clinker=lld",
+ "-Clinker-flavor=ld.lld",
+ "-Clinker-plugin-lto=yes",
+ "-Cllvm-args=foo",
+ "-Cmetadata=foo",
+ "-Cno-prepopulate-passes",
+ "-Cno-stack-check",
+ "-Copt-level=3",
+ "-Coverflow-checks=yes",
+ "-Cpanic=abort",
+ "-Cpasses=foopass",
+ "-Cprefer-dynamic=yes",
+ "-Crelro-level=partial",
+ "-Cremark=all",
+ "-Crpath=yes",
+ "-Csave-temps=yes",
+ "-Csplit-debuginfo=packed",
+ "-Cstrip=symbols",
+ "-Csymbol-mangling-version=v0",
+ "-Ctarget-cpu=native",
+ "-Ctarget-feature=+sve",
+ // Unstable options
+ "-Ztune-cpu=machine",
+ ];
+ check(
+ &flags.join("\u{1f}"),
+ &RustcCodegenFlags {
+ code_model: Some("tiny"),
+ control_flow_guard: Some("yes"),
+ embed_bitcode: Some(false),
+ force_frame_pointers: Some(true),
+ lto: Some("false"),
+ no_redzone: Some(true),
+ no_vectorize_loops: true,
+ no_vectorize_slp: true,
+ profile_generate: Some("fooprofile"),
+ profile_use: Some("fooprofile"),
+ relocation_model: Some("pic"),
+ soft_float: Some(true),
+ branch_protection: Some("bti,pac-ret,leaf"),
+ dwarf_version: Some(5),
+ },
+ );
+ }
+}