diff --git a/src/lib.rs b/src/lib.rs index 699facfea..7eec6aa52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1933,6 +1933,8 @@ impl Build { } else { if target.contains("i586") { cmd.push_cc_arg("-arch:IA32".into()); + } else if target.contains("arm64ec") { + cmd.push_cc_arg("-arm64EC".into()); } } @@ -3016,10 +3018,14 @@ impl Build { if lib.is_empty() { name = String::from("lib.exe"); - match windows_registry::find(&target, "lib.exe") { + let mut cmd = match windows_registry::find(&target, "lib.exe") { Some(t) => t, None => self.cmd("lib.exe"), + }; + if target.contains("arm64ec") { + cmd.arg("/machine:arm64ec"); } + cmd } else { name = lib; self.cmd(&name) diff --git a/src/windows_registry.rs b/src/windows_registry.rs index 76fb3fafc..cf1c2edb7 100644 --- a/src/windows_registry.rs +++ b/src/windows_registry.rs @@ -55,6 +55,9 @@ pub fn find_tool(target: &str, tool: &str) -> Option { return None; } + // Split the target to get the arch. + let target = impl_::TargetArch(target.split_once('-')?.0); + // Looks like msbuild isn't located in the same location as other tools like // cl.exe and lib.exe. To handle this we probe for it manually with // dedicated registry keys. @@ -174,6 +177,21 @@ mod impl_ { use super::MSVC_FAMILY; use crate::Tool; + #[derive(Copy, Clone)] + pub struct TargetArch<'a>(pub &'a str); + + impl PartialEq<&str> for TargetArch<'_> { + fn eq(&self, other: &&str) -> bool { + self.0 == *other + } + } + + impl<'a> From> for &'a str { + fn from(target: TargetArch<'a>) -> Self { + target.0 + } + } + struct MsvcTool { tool: PathBuf, libs: Vec, @@ -209,14 +227,14 @@ mod impl_ { /// 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. #[cfg(windows)] - fn is_vscmd_target(target: &str) -> Option { + fn is_vscmd_target(target: TargetArch<'_>) -> Option { let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?; // Convert the Rust target arch to its VS arch equivalent. - let arch = match target.split('-').next() { - Some("x86_64") => "x64", - Some("aarch64") => "arm64", - Some("i686") | Some("i586") => "x86", - Some("thumbv7a") => "arm", + let arch = match target.into() { + "x86_64" => "x64", + "aarch64" | "arm64ec" => "arm64", + "i686" | "i586" => "x86", + "thumbv7a" => "arm", // An unrecognized arch. _ => return Some(false), }; @@ -224,7 +242,7 @@ mod impl_ { } /// Attempt to find the tool using environment variables set by vcvars. - pub fn find_msvc_environment(tool: &str, target: &str) -> Option { + pub fn find_msvc_environment(tool: &str, target: TargetArch<'_>) -> Option { // Early return if the environment doesn't contain a VC install. if env::var_os("VCINSTALLDIR").is_none() { return None; @@ -248,12 +266,15 @@ mod impl_ { } } - fn find_msbuild_vs17(target: &str) -> Option { + fn find_msbuild_vs17(target: TargetArch<'_>) -> Option { find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17") } #[allow(bare_trait_objects)] - fn vs16plus_instances(target: &str, version: &'static str) -> Box> { + fn vs16plus_instances( + target: TargetArch<'_>, + version: &'static str, + ) -> Box> { let instances = if let Some(instances) = vs15plus_instances(target) { instances } else { @@ -271,7 +292,11 @@ mod impl_ { })) } - fn find_tool_in_vs16plus_path(tool: &str, target: &str, version: &'static str) -> Option { + fn find_tool_in_vs16plus_path( + tool: &str, + target: TargetArch<'_>, + version: &'static str, + ) -> Option { vs16plus_instances(target, version) .filter_map(|path| { let path = path.join(tool); @@ -279,10 +304,10 @@ mod impl_ { return None; } let mut tool = Tool::with_family(path, MSVC_FAMILY); - if target.contains("x86_64") { + if target == "x86_64" { tool.env.push(("Platform".into(), "X64".into())); } - if target.contains("aarch64") { + if target == "aarch64" || target == "arm64ec" { tool.env.push(("Platform".into(), "ARM64".into())); } Some(tool) @@ -290,7 +315,7 @@ mod impl_ { .next() } - fn find_msbuild_vs16(target: &str) -> Option { + fn find_msbuild_vs16(target: TargetArch<'_>) -> Option { find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16") } @@ -306,7 +331,7 @@ mod impl_ { // // However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64. // Hence, as the last resort we try to use vswhere.exe to list available instances. - fn vs15plus_instances(target: &str) -> Option { + fn vs15plus_instances(target: TargetArch<'_>) -> Option { vs15plus_instances_using_com().or_else(|| vs15plus_instances_using_vswhere(target)) } @@ -319,7 +344,7 @@ mod impl_ { Some(VsInstances::ComBased(enum_setup_instances)) } - fn vs15plus_instances_using_vswhere(target: &str) -> Option { + fn vs15plus_instances_using_vswhere(target: TargetArch<'_>) -> Option { let program_files_path: PathBuf = env::var("ProgramFiles(x86)") .or_else(|_| env::var("ProgramFiles")) .ok()? @@ -332,11 +357,10 @@ mod impl_ { return None; } - let arch = target.split('-').next().unwrap(); - let tools_arch = match arch { + let tools_arch = match target.into() { "i586" | "i686" | "x86_64" => Some("x86.x64"), "arm" | "thumbv7a" => Some("ARM"), - "aarch64" => Some("ARM64"), + "aarch64" | "arm64ec" => Some("ARM64"), _ => None, }; @@ -370,7 +394,7 @@ mod impl_ { .collect() } - pub fn find_msvc_15plus(tool: &str, target: &str) -> Option { + pub fn find_msvc_15plus(tool: &str, target: TargetArch<'_>) -> Option { let iter = vs15plus_instances(target)?; iter.into_iter() .filter_map(|instance| { @@ -390,7 +414,7 @@ mod impl_ { // we keep the registry method as a fallback option. // // [more reliable]: https://github.com/rust-lang/cc-rs/pull/331 - fn find_tool_in_vs15_path(tool: &str, target: &str) -> Option { + fn find_tool_in_vs15_path(tool: &str, target: TargetArch<'_>) -> Option { let mut path = match vs15plus_instances(target) { Some(instances) => instances .into_iter() @@ -412,10 +436,9 @@ mod impl_ { path.map(|path| { let mut tool = Tool::with_family(path, MSVC_FAMILY); - if target.contains("x86_64") { + if target == "x86_64" { tool.env.push(("Platform".into(), "X64".into())); - } - if target.contains("aarch64") { + } else if target == "aarch64" { tool.env.push(("Platform".into(), "ARM64".into())); } tool @@ -424,10 +447,10 @@ mod impl_ { fn tool_from_vs15plus_instance( tool: &str, - target: &str, + target: TargetArch<'_>, instance_path: &PathBuf, ) -> Option { - let (root_path, bin_path, host_dylib_path, lib_path, include_path) = + let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) = vs15plus_vc_paths(target, instance_path)?; let tool_path = bin_path.join(tool); if !tool_path.exists() { @@ -437,6 +460,9 @@ mod impl_ { let mut tool = MsvcTool::new(tool_path); tool.path.push(bin_path.clone()); tool.path.push(host_dylib_path); + if let Some(alt_lib_path) = alt_lib_path { + tool.libs.push(alt_lib_path); + } tool.libs.push(lib_path); tool.include.push(include_path); @@ -451,9 +477,9 @@ mod impl_ { } fn vs15plus_vc_paths( - target: &str, + target: TargetArch<'_>, instance_path: &Path, - ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, PathBuf)> { + ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option, PathBuf)> { let version = vs15plus_vc_read_version(instance_path)?; let host = match host_arch() { @@ -478,8 +504,16 @@ mod impl_ { .join(format!("Host{}", host)) .join(host.to_lowercase()); let lib_path = path.join("lib").join(target); + let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec")); let include_path = path.join("include"); - Some((path, bin_path, host_dylib_path, lib_path, include_path)) + Some(( + path, + bin_path, + host_dylib_path, + lib_path, + alt_lib_path, + include_path, + )) } fn vs15plus_vc_read_version(dir: &Path) -> Option { @@ -519,7 +553,7 @@ mod impl_ { Some(version) } - fn atl_paths(target: &str, path: &Path) -> Option<(PathBuf, PathBuf)> { + fn atl_paths(target: TargetArch<'_>, path: &Path) -> Option<(PathBuf, PathBuf)> { let atl_path = path.join("atlmfc"); let sub = lib_subdir(target)?; if atl_path.exists() { @@ -531,14 +565,14 @@ mod impl_ { // For MSVC 14 we need to find the Universal CRT as well as either // the Windows 10 SDK or Windows 8.1 SDK. - pub fn find_msvc_14(tool: &str, target: &str) -> Option { + pub fn find_msvc_14(tool: &str, target: TargetArch<'_>) -> Option { let vcdir = get_vc_dir("14.0")?; let mut tool = get_tool(tool, &vcdir, target)?; add_sdks(&mut tool, target)?; Some(tool.into_tool()) } - fn add_sdks(tool: &mut MsvcTool, target: &str) -> Option<()> { + fn add_sdks(tool: &mut MsvcTool, target: TargetArch<'_>) -> Option<()> { let sub = lib_subdir(target)?; let (ucrt, ucrt_version) = get_ucrt_dir()?; @@ -581,7 +615,7 @@ mod impl_ { } // For MSVC 12 we need to find the Windows 8.1 SDK. - pub fn find_msvc_12(tool: &str, target: &str) -> Option { + pub fn find_msvc_12(tool: &str, target: TargetArch<'_>) -> Option { let vcdir = get_vc_dir("12.0")?; let mut tool = get_tool(tool, &vcdir, target)?; let sub = lib_subdir(target)?; @@ -597,7 +631,7 @@ mod impl_ { } // For MSVC 11 we need to find the Windows 8 SDK. - pub fn find_msvc_11(tool: &str, target: &str) -> Option { + pub fn find_msvc_11(tool: &str, target: TargetArch<'_>) -> Option { let vcdir = get_vc_dir("11.0")?; let mut tool = get_tool(tool, &vcdir, target)?; let sub = lib_subdir(target)?; @@ -622,7 +656,7 @@ mod impl_ { // Given a possible MSVC installation directory, we look for the linker and // then add the MSVC library path. - fn get_tool(tool: &str, path: &Path, target: &str) -> Option { + fn get_tool(tool: &str, path: &Path, target: TargetArch<'_>) -> Option { bin_subdir(target) .into_iter() .map(|(sub, host)| { @@ -760,9 +794,8 @@ mod impl_ { // linkers that can target the architecture we desire. The 64-bit host // linker is preferred, and hence first, due to 64-bit allowing it more // address space to work with and potentially being faster. - fn bin_subdir(target: &str) -> Vec<(&'static str, &'static str)> { - let arch = target.split('-').next().unwrap(); - match (arch, host_arch()) { + fn bin_subdir(target: TargetArch<'_>) -> Vec<(&'static str, &'static str)> { + match (target.into(), host_arch()) { ("i586", X86) | ("i686", X86) => vec![("", "")], ("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")], ("x86_64", X86) => vec![("x86_amd64", "")], @@ -773,21 +806,19 @@ mod impl_ { } } - fn lib_subdir(target: &str) -> Option<&'static str> { - let arch = target.split('-').next().unwrap(); - match arch { + fn lib_subdir(target: TargetArch<'_>) -> Option<&'static str> { + match target.into() { "i586" | "i686" => Some("x86"), "x86_64" => Some("x64"), "arm" | "thumbv7a" => Some("arm"), - "aarch64" => Some("arm64"), + "aarch64" | "arm64ec" => Some("arm64"), _ => None, } } // MSVC's x86 libraries are not in a subfolder - fn vc_lib_subdir(target: &str) -> Option<&'static str> { - let arch = target.split('-').next().unwrap(); - match arch { + fn vc_lib_subdir(target: TargetArch<'_>) -> Option<&'static str> { + match target.into() { "i586" | "i686" => Some(""), "x86_64" => Some("amd64"), "arm" | "thumbv7a" => Some("arm"), @@ -857,19 +888,19 @@ mod impl_ { pub fn has_msbuild_version(version: &str) -> bool { match version { "17.0" => { - find_msbuild_vs17("x86_64-pc-windows-msvc").is_some() - || find_msbuild_vs17("i686-pc-windows-msvc").is_some() - || find_msbuild_vs17("aarch64-pc-windows-msvc").is_some() + find_msbuild_vs17(TargetArch("x86_64")).is_some() + || find_msbuild_vs17(TargetArch("i686")).is_some() + || find_msbuild_vs17(TargetArch("aarch64")).is_some() } "16.0" => { - find_msbuild_vs16("x86_64-pc-windows-msvc").is_some() - || find_msbuild_vs16("i686-pc-windows-msvc").is_some() - || find_msbuild_vs16("aarch64-pc-windows-msvc").is_some() + find_msbuild_vs16(TargetArch("x86_64")).is_some() + || find_msbuild_vs16(TargetArch("i686")).is_some() + || find_msbuild_vs16(TargetArch("aarch64")).is_some() } "15.0" => { - find_msbuild_vs15("x86_64-pc-windows-msvc").is_some() - || find_msbuild_vs15("i686-pc-windows-msvc").is_some() - || find_msbuild_vs15("aarch64-pc-windows-msvc").is_some() + find_msbuild_vs15(TargetArch("x86_64")).is_some() + || find_msbuild_vs15(TargetArch("i686")).is_some() + || find_msbuild_vs15(TargetArch("aarch64")).is_some() } "12.0" | "14.0" => LOCAL_MACHINE .open(&OsString::from(format!( @@ -881,16 +912,16 @@ mod impl_ { } } - pub fn find_devenv(target: &str) -> Option { + pub fn find_devenv(target: TargetArch<'_>) -> Option { find_devenv_vs15(target) } - fn find_devenv_vs15(target: &str) -> Option { + fn find_devenv_vs15(target: TargetArch<'_>) -> Option { find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target) } // see http://stackoverflow.com/questions/328017/path-to-msbuild - pub fn find_msbuild(target: &str) -> Option { + pub fn find_msbuild(target: TargetArch<'_>) -> Option { // VS 15 (2017) changed how to locate msbuild if let Some(r) = find_msbuild_vs17(target) { Some(r) @@ -903,11 +934,11 @@ mod impl_ { } } - fn find_msbuild_vs15(target: &str) -> Option { + fn find_msbuild_vs15(target: TargetArch<'_>) -> Option { find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target) } - fn find_old_msbuild(target: &str) -> Option { + fn find_old_msbuild(target: TargetArch<'_>) -> Option { let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions"; LOCAL_MACHINE .open(key.as_ref()) @@ -919,7 +950,7 @@ mod impl_ { let mut path = PathBuf::from(path); path.push("MSBuild.exe"); let mut tool = Tool::with_family(path, MSVC_FAMILY); - if target.contains("x86_64") { + if target == "x86_64" { tool.env.push(("Platform".into(), "X64".into())); } tool