diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index eb8bbceb..0c09610e 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -5,6 +5,7 @@ permissions: contents: write on: + workflow_dispatch: # Allow running on-demand schedule: - cron: "0 3 * * 5" diff --git a/CHANGELOG.md b/CHANGELOG.md index 06a8156e..3af5e28f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.2.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.2...cc-v1.2.3) - 2024-12-06 + +### Other + +- Improve detection of environment when compiling from msbuild or msvc ([#1310](https://github.com/rust-lang/cc-rs/pull/1310)) +- Better error message when failing on unknown targets ([#1313](https://github.com/rust-lang/cc-rs/pull/1313)) +- Optimize RustcCodegenFlags ([#1305](https://github.com/rust-lang/cc-rs/pull/1305)) + ## [1.2.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.1...cc-v1.2.2) - 2024-11-29 ### Other diff --git a/Cargo.toml b/Cargo.toml index 8dded2d5..2ca35c82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cc" -version = "1.2.2" +version = "1.2.3" authors = ["Alex Crichton "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cc-rs" diff --git a/src/flags.rs b/src/flags.rs index a94743fc..81834cf6 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -5,16 +5,16 @@ use std::ffi::OsString; use std::path::Path; #[derive(Debug, PartialEq, Default)] -pub(crate) struct RustcCodegenFlags { - branch_protection: Option, - code_model: Option, +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, - profile_use: Option, - control_flow_guard: Option, - lto: Option, - relocation_model: Option, + 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, force_frame_pointers: Option, link_dead_code: Option, @@ -22,9 +22,9 @@ pub(crate) struct RustcCodegenFlags { soft_float: Option, } -impl RustcCodegenFlags { +impl<'this> RustcCodegenFlags<'this> { // Parse flags obtained from CARGO_ENCODED_RUSTFLAGS - pub(crate) fn parse(rustflags_env: &str) -> Result { + pub(crate) fn parse(rustflags_env: &'this str) -> Result { fn is_flag_prefix(flag: &str) -> bool { [ "-Z", @@ -45,19 +45,19 @@ impl RustcCodegenFlags { .contains(&flag) } - fn handle_flag_prefix<'a>(prev: &'a str, curr: &'a str) -> Cow<'a, str> { + fn handle_flag_prefix<'a>(prev: &'a str, curr: &'a str) -> (&'a str, &'a str) { match prev { - "--codegen" | "-C" => Cow::from(format!("-C{}", curr)), + "--codegen" | "-C" => ("-C", curr), // Handle flags passed like --codegen=code-model=small - _ if curr.starts_with("--codegen=") => Cow::from(format!("-C{}", &curr[10..])), - "-Z" => Cow::from(format!("-Z{}", curr)), - "-L" | "-l" | "-o" => Cow::from(format!("{}{}", prev, curr)), + _ if curr.starts_with("--codegen=") => ("-C", &curr[10..]), + "-Z" => ("-Z", curr), + "-L" | "-l" | "-o" => (prev, curr), // Handle lint flags - "-W" | "--warn" => Cow::from(format!("-W{}", curr)), - "-A" | "--allow" => Cow::from(format!("-A{}", curr)), - "-D" | "--deny" => Cow::from(format!("-D{}", curr)), - "-F" | "--forbid" => Cow::from(format!("-F{}", curr)), - _ => Cow::from(curr), + "-W" | "--warn" => ("-W", curr), + "-A" | "--allow" => ("-A", curr), + "-D" | "--deny" => ("-D", curr), + "-F" | "--forbid" => ("-F", curr), + _ => ("", curr), } } @@ -71,14 +71,14 @@ impl RustcCodegenFlags { continue; } - let rustc_flag = handle_flag_prefix(prev, curr); - codegen_flags.set_rustc_flag(&rustc_flag)?; + 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, flag: &str) -> Result<(), Error> { + 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) -> Option { match arg.as_ref() { @@ -89,16 +89,24 @@ impl RustcCodegenFlags { } let (flag, value) = if let Some((flag, value)) = flag.split_once('=') { - (flag, Some(value.to_owned())) + (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: Option, msg: &'static str) -> Result { + 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 { + 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")?); @@ -117,9 +125,9 @@ impl RustcCodegenFlags { 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".into())), + "-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".into())), + "-Clto" => self.lto = value.or(Some("true")), // https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model "-Crelocation-model" => { self.relocation_model = @@ -176,13 +184,13 @@ impl RustcCodegenFlags { match family { ToolFamily::Clang { .. } | ToolFamily::Gnu => { // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mbranch-protection - if let Some(value) = &self.branch_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 - if let Some(value) = &self.code_model { + 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 @@ -194,16 +202,16 @@ impl RustcCodegenFlags { push_if_supported("-fno-slp-vectorize".into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-generate - if let Some(value) = &self.profile_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 { + if let Some(value) = self.profile_use { push_if_supported(format!("-fprofile-use={value}").into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mguard - if let Some(value) = &self.control_flow_guard { - let cc_val = match value.as_str() { + 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"), @@ -214,8 +222,8 @@ impl RustcCodegenFlags { } } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-flto - if let Some(value) = &self.lto { - let cc_val = match value.as_str() { + if let Some(value) = self.lto { + let cc_val = match value { "y" | "yes" | "on" | "true" | "fat" => Some("full"), "thin" => Some("thin"), _ => None, @@ -227,8 +235,8 @@ impl RustcCodegenFlags { // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIC // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIE // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mdynamic-no-pic - if let Some(value) = &self.relocation_model { - let cc_flag = match value.as_str() { + if let Some(value) = self.relocation_model { + let cc_flag = match value { "pic" => Some("-fPIC"), "pie" => Some("-fPIE"), "dynamic-no-pic" => Some("-mdynamic-no-pic"), @@ -239,14 +247,14 @@ impl RustcCodegenFlags { } } // 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" }; + 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-fno-omit-frame-pointer // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer - if let Some(value) = &self.force_frame_pointers { - let cc_flag = if *value { + if let Some(value) = self.force_frame_pointers { + let cc_flag = if value { "-fno-omit-frame-pointer" } else { "-fomit-frame-pointer" @@ -254,25 +262,19 @@ impl RustcCodegenFlags { push_if_supported(cc_flag.into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-dead_strip - if let Some(value) = &self.link_dead_code { - if !value { - push_if_supported("-dead_strip".into()); - } + if let Some(false) = self.link_dead_code { + push_if_supported("-dead_strip".into()); } // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mno-red-zone // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mred-zone - if let Some(value) = &self.no_redzone { - let cc_flag = if *value { - "-mno-red-zone" - } else { - "-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-mno-soft-float - if let Some(value) = &self.soft_float { - let cc_flag = if *value { + if let Some(value) = self.soft_float { + let cc_flag = if value { "-msoft-float" } else { "-mno-soft-float" @@ -282,8 +284,8 @@ impl RustcCodegenFlags { } 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.as_str() { + 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, @@ -293,8 +295,8 @@ impl RustcCodegenFlags { } } // https://learn.microsoft.com/en-us/cpp/build/reference/oy-frame-pointer-omission - if let Some(value) = &self.force_frame_pointers { - let cc_flag = if *value { "/Oy-" } else { "/Oy" }; + if let Some(value) = self.force_frame_pointers { + let cc_flag = if value { "/Oy-" } else { "/Oy" }; push_if_supported(cc_flag.into()); } } diff --git a/src/lib.rs b/src/lib.rs index 2d0daa6d..3488e1f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -351,6 +351,8 @@ enum ErrorKind { ToolFamilyMacroNotFound, /// Invalid target. InvalidTarget, + /// Unknown target. + UnknownTarget, /// Invalid rustc flag. InvalidFlag, #[cfg(feature = "parallel")] @@ -3979,7 +3981,7 @@ impl Default for Build { } fn fail(s: &str) -> ! { - eprintln!("\n\nerror occurred: {}\n\n", s); + eprintln!("\n\nerror occurred in cc-rs: {}\n\n", s); std::process::exit(1); } diff --git a/src/target.rs b/src/target.rs index 20fce321..b00a9da4 100644 --- a/src/target.rs +++ b/src/target.rs @@ -59,8 +59,20 @@ impl FromStr for TargetInfo<'_> { Ok(info.clone()) } else { Err(Error::new( - ErrorKind::InvalidTarget, - format!("unknown target `{target_triple}`"), + ErrorKind::UnknownTarget, + format!( + "unknown target `{target_triple}`. + +NOTE: `cc-rs` only supports a fixed set of targets when not in a build script. +- If adding a new target, you will need to fork of `cc-rs` until the target + has landed on nightly and the auto-generated list has been updated. See also + the `rustc` dev guide on adding a new target: + https://rustc-dev-guide.rust-lang.org/building/new-target.html +- If using a custom target, prefer to upstream it to `rustc` if possible, + otherwise open an issue with `cc-rs`: + https://github.com/rust-lang/cc-rs/issues/new +" + ), )) } } diff --git a/src/windows/find_tools.rs b/src/windows/find_tools.rs index c596b1d0..fb7a3541 100644 --- a/src/windows/find_tools.rs +++ b/src/windows/find_tools.rs @@ -353,20 +353,69 @@ mod impl_ { } } + /// Checks to see if the target's arch matches the VS environment. Returns `None` if the + /// environment is unknown. + fn is_vscmd_target(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { + is_vscmd_target_env(target, env_getter).or_else(|| is_vscmd_target_cl(target, env_getter)) + } + /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the /// given target's arch. Returns `None` if the variable does not exist. - fn is_vscmd_target(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { + fn is_vscmd_target_env(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { let vscmd_arch = env_getter.get_env("VSCMD_ARG_TGT_ARCH")?; - // Convert the Rust target arch to its VS arch equivalent. - let arch = match target.into() { - "x86_64" => "x64", - "aarch64" | "arm64ec" => "arm64", - "i686" | "i586" => "x86", - "thumbv7a" => "arm", + if let Some(arch) = vsarch_from_target(target) { + Some(arch == vscmd_arch.as_ref()) + } else { + Some(false) + } + } + + /// Checks if the cl.exe target matches the given target's arch. Returns `None` if anything + /// fails. + fn is_vscmd_target_cl(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option { + let cmd_target = vscmd_target_cl(env_getter)?; + Some(vsarch_from_target(target) == Some(cmd_target)) + } + + /// Convert the Rust target arch to its VS arch equivalent. + fn vsarch_from_target(target: TargetArch<'_>) -> Option<&'static str> { + match target.into() { + "x86_64" => Some("x64"), + "aarch64" | "arm64ec" => Some("arm64"), + "i686" | "i586" => Some("x86"), + "thumbv7a" => Some("arm"), // An unrecognized arch. - _ => return Some(false), - }; - Some(vscmd_arch.as_ref() == arch) + _ => None, + } + } + + /// Detect the target architecture of `cl.exe`` in the current path, and return `None` if this + /// fails for any reason. + fn vscmd_target_cl(env_getter: &dyn EnvGetter) -> Option<&'static str> { + let cl_exe = env_getter.get_env("PATH").and_then(|path| { + env::split_paths(&path) + .map(|p| p.join("cl.exe")) + .find(|p| p.exists()) + })?; + let mut cl = Command::new(cl_exe); + cl.stderr(std::process::Stdio::piped()) + .stdout(std::process::Stdio::null()); + + let out = cl.output().ok()?; + let cl_arch = out + .stderr + .split(|&b| b == b'\n' || b == b'\r') + .next()? + .rsplit(|&b| b == b' ') + .next()?; + + match cl_arch { + b"x64" => Some("x64"), + b"x86" => Some("x86"), + b"ARM64" => Some("arm64"), + b"ARM" => Some("arm"), + _ => None, + } } /// Attempt to find the tool using environment variables set by vcvars.