1#![allow(clippy::upper_case_acronyms)]
16
17use std::{
18 env,
19 ffi::{OsStr, OsString},
20 ops::Deref,
21 path::PathBuf,
22 process::Command,
23 sync::Arc,
24};
25
26use crate::Tool;
27use crate::ToolFamily;
28
29const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false };
30
31#[derive(Copy, Clone)]
32struct TargetArch<'a>(pub &'a str);
33
34impl PartialEq<&str> for TargetArch<'_> {
35 fn eq(&self, other: &&str) -> bool {
36 self.0 == *other
37 }
38}
39
40impl<'a> From<TargetArch<'a>> for &'a str {
41 fn from(target: TargetArch<'a>) -> Self {
42 target.0
43 }
44}
45
46pub(crate) enum Env {
47 Owned(OsString),
48 Arced(Arc<OsStr>),
49}
50
51impl AsRef<OsStr> for Env {
52 fn as_ref(&self) -> &OsStr {
53 self.deref()
54 }
55}
56
57impl Deref for Env {
58 type Target = OsStr;
59
60 fn deref(&self) -> &Self::Target {
61 match self {
62 Env::Owned(os_str) => os_str,
63 Env::Arced(os_str) => os_str,
64 }
65 }
66}
67
68impl From<Env> for PathBuf {
69 fn from(env: Env) -> Self {
70 match env {
71 Env::Owned(os_str) => PathBuf::from(os_str),
72 Env::Arced(os_str) => PathBuf::from(os_str.deref()),
73 }
74 }
75}
76
77pub(crate) trait EnvGetter {
78 fn get_env(&self, name: &'static str) -> Option<Env>;
79}
80
81struct StdEnvGetter;
82
83impl EnvGetter for StdEnvGetter {
84 #[allow(clippy::disallowed_methods)]
85 fn get_env(&self, name: &'static str) -> Option<Env> {
86 env::var_os(name).map(Env::Owned)
87 }
88}
89
90pub fn find(arch_or_target: &str, tool: &str) -> Option<Command> {
113 find_tool(arch_or_target, tool).map(|c| c.to_command())
114}
115
116pub fn find_tool(arch_or_target: &str, tool: &str) -> Option<Tool> {
120 let full_arch = if let Some((full_arch, rest)) = arch_or_target.split_once("-") {
121 if !rest.contains("msvc") {
124 return None;
125 }
126 full_arch
127 } else {
128 arch_or_target
129 };
130 find_tool_inner(full_arch, tool, &StdEnvGetter)
131}
132
133pub(crate) fn find_tool_inner(
134 full_arch: &str,
135 tool: &str,
136 env_getter: &dyn EnvGetter,
137) -> Option<Tool> {
138 let target = TargetArch(full_arch);
140
141 if tool.contains("msbuild") {
144 return impl_::find_msbuild(target, env_getter);
145 }
146
147 if tool.contains("devenv") {
150 return impl_::find_devenv(target, env_getter);
151 }
152
153 impl_::find_msvc_environment(tool, target, env_getter)
161 .or_else(|| impl_::find_msvc_15plus(tool, target, env_getter))
162 .or_else(|| impl_::find_msvc_14(tool, target, env_getter))
163}
164
165#[derive(Debug, PartialEq, Eq, Copy, Clone)]
167#[non_exhaustive]
168pub enum VsVers {
169 #[deprecated(
171 note = "Visual Studio 12 is no longer supported. cc will never return this value."
172 )]
173 Vs12,
174 Vs14,
176 Vs15,
178 Vs16,
180 Vs17,
182}
183
184#[allow(clippy::disallowed_methods)]
189pub fn find_vs_version() -> Result<VsVers, String> {
190 fn has_msbuild_version(version: &str) -> bool {
191 impl_::has_msbuild_version(version, &StdEnvGetter)
192 }
193
194 match std::env::var("VisualStudioVersion") {
195 Ok(version) => match &version[..] {
196 "17.0" => Ok(VsVers::Vs17),
197 "16.0" => Ok(VsVers::Vs16),
198 "15.0" => Ok(VsVers::Vs15),
199 "14.0" => Ok(VsVers::Vs14),
200 vers => Err(format!(
201 "\n\n\
202 unsupported or unknown VisualStudio version: {}\n\
203 if another version is installed consider running \
204 the appropriate vcvars script before building this \
205 crate\n\
206 ",
207 vers
208 )),
209 },
210 _ => {
211 if has_msbuild_version("17.0") {
214 Ok(VsVers::Vs17)
215 } else if has_msbuild_version("16.0") {
216 Ok(VsVers::Vs16)
217 } else if has_msbuild_version("15.0") {
218 Ok(VsVers::Vs15)
219 } else if has_msbuild_version("14.0") {
220 Ok(VsVers::Vs14)
221 } else {
222 Err("\n\n\
223 couldn't determine visual studio generator\n\
224 if VisualStudio is installed, however, consider \
225 running the appropriate vcvars script before building \
226 this crate\n\
227 "
228 .to_string())
229 }
230 }
231 }
232}
233
234#[cfg(windows)]
236mod impl_ {
237 use crate::windows::com;
238 use crate::windows::registry::{RegistryKey, LOCAL_MACHINE};
239 use crate::windows::setup_config::SetupConfiguration;
240 use crate::windows::vs_instances::{VsInstances, VswhereInstance};
241 use crate::windows::windows_sys::{
242 GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE,
243 IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK,
244 };
245 use std::convert::TryFrom;
246 use std::env;
247 use std::ffi::OsString;
248 use std::fs::File;
249 use std::io::Read;
250 use std::iter;
251 use std::mem;
252 use std::path::{Path, PathBuf};
253 use std::process::Command;
254 use std::str::FromStr;
255 use std::sync::atomic::{AtomicBool, Ordering};
256 use std::sync::Once;
257
258 use super::{EnvGetter, TargetArch, MSVC_FAMILY};
259 use crate::Tool;
260
261 struct MsvcTool {
262 tool: PathBuf,
263 libs: Vec<PathBuf>,
264 path: Vec<PathBuf>,
265 include: Vec<PathBuf>,
266 }
267
268 struct LibraryHandle(HMODULE);
269
270 impl LibraryHandle {
271 fn new(name: &[u8]) -> Option<Self> {
272 let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
273 (!handle.is_null()).then_some(Self(handle))
274 }
275
276 unsafe fn get_proc_address<F>(&self, name: &[u8]) -> Option<F> {
285 let symbol = GetProcAddress(self.0, name.as_ptr() as _);
286 symbol.map(|symbol| mem::transmute_copy(&symbol))
287 }
288 }
289
290 type GetMachineTypeAttributesFuncType =
291 unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32;
292 const _: () = {
293 let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes;
297 };
298
299 fn is_amd64_emulation_supported_inner() -> Option<bool> {
300 let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?;
302 let get_machine_type_attributes = unsafe {
304 kernel32
305 .get_proc_address::<GetMachineTypeAttributesFuncType>(b"GetMachineTypeAttributes\0")
306 }?;
307 let mut attributes = Default::default();
308 if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK
309 {
310 Some((attributes & UserEnabled) != 0)
311 } else {
312 Some(false)
313 }
314 }
315
316 fn is_amd64_emulation_supported() -> bool {
317 static LOAD_VALUE: Once = Once::new();
319 static IS_SUPPORTED: AtomicBool = AtomicBool::new(false);
320
321 LOAD_VALUE.call_once(|| {
323 IS_SUPPORTED.store(
324 is_amd64_emulation_supported_inner().unwrap_or(false),
325 Ordering::Relaxed,
326 );
327 });
328 IS_SUPPORTED.load(Ordering::Relaxed)
329 }
330
331 impl MsvcTool {
332 fn new(tool: PathBuf) -> MsvcTool {
333 MsvcTool {
334 tool,
335 libs: Vec::new(),
336 path: Vec::new(),
337 include: Vec::new(),
338 }
339 }
340
341 fn into_tool(self, env_getter: &dyn EnvGetter) -> Tool {
342 let MsvcTool {
343 tool,
344 libs,
345 path,
346 include,
347 } = self;
348 let mut tool = Tool::with_family(tool, MSVC_FAMILY);
349 add_env(&mut tool, "LIB", libs, env_getter);
350 add_env(&mut tool, "PATH", path, env_getter);
351 add_env(&mut tool, "INCLUDE", include, env_getter);
352 tool
353 }
354 }
355
356 fn is_vscmd_target(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<bool> {
359 let vscmd_arch = env_getter.get_env("VSCMD_ARG_TGT_ARCH")?;
360 let arch = match target.into() {
362 "x86_64" => "x64",
363 "aarch64" | "arm64ec" => "arm64",
364 "i686" | "i586" => "x86",
365 "thumbv7a" => "arm",
366 _ => return Some(false),
368 };
369 Some(vscmd_arch.as_ref() == arch)
370 }
371
372 pub(super) fn find_msvc_environment(
374 tool: &str,
375 target: TargetArch<'_>,
376 env_getter: &dyn EnvGetter,
377 ) -> Option<Tool> {
378 if env_getter.get_env("VCINSTALLDIR").is_none()
383 && env_getter.get_env("VSTEL_MSBuildProjectFullPath").is_none()
384 {
385 return None;
386 }
387
388 if is_vscmd_target(target, env_getter) == Some(false) {
391 let vs_install_dir: PathBuf = env_getter.get_env("VSINSTALLDIR")?.into();
393 tool_from_vs15plus_instance(tool, target, &vs_install_dir, env_getter)
394 } else {
395 env_getter
397 .get_env("PATH")
398 .and_then(|path| {
399 env::split_paths(&path)
400 .map(|p| p.join(tool))
401 .find(|p| p.exists())
402 })
403 .map(|path| Tool::with_family(path, MSVC_FAMILY))
404 }
405 }
406
407 fn find_msbuild_vs17(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<Tool> {
408 find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17", env_getter)
409 }
410
411 #[allow(bare_trait_objects)]
412 fn vs16plus_instances(
413 target: TargetArch<'_>,
414 version: &'static str,
415 env_getter: &dyn EnvGetter,
416 ) -> Box<Iterator<Item = PathBuf>> {
417 let instances = if let Some(instances) = vs15plus_instances(target, env_getter) {
418 instances
419 } else {
420 return Box::new(iter::empty());
421 };
422 Box::new(instances.into_iter().filter_map(move |instance| {
423 let installation_name = instance.installation_name()?;
424 if installation_name.starts_with(&format!("VisualStudio/{}.", version))
425 || installation_name.starts_with(&format!("VisualStudioPreview/{}.", version))
426 {
427 Some(instance.installation_path()?)
428 } else {
429 None
430 }
431 }))
432 }
433
434 fn find_tool_in_vs16plus_path(
435 tool: &str,
436 target: TargetArch<'_>,
437 version: &'static str,
438 env_getter: &dyn EnvGetter,
439 ) -> Option<Tool> {
440 vs16plus_instances(target, version, env_getter)
441 .filter_map(|path| {
442 let path = path.join(tool);
443 if !path.is_file() {
444 return None;
445 }
446 let mut tool = Tool::with_family(path, MSVC_FAMILY);
447 if target == "x86_64" {
448 tool.env.push(("Platform".into(), "X64".into()));
449 }
450 if target == "aarch64" || target == "arm64ec" {
451 tool.env.push(("Platform".into(), "ARM64".into()));
452 }
453 Some(tool)
454 })
455 .next()
456 }
457
458 fn find_msbuild_vs16(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<Tool> {
459 find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16", env_getter)
460 }
461
462 fn vs15plus_instances(
475 target: TargetArch<'_>,
476 env_getter: &dyn EnvGetter,
477 ) -> Option<VsInstances> {
478 vs15plus_instances_using_com()
479 .or_else(|| vs15plus_instances_using_vswhere(target, env_getter))
480 }
481
482 fn vs15plus_instances_using_com() -> Option<VsInstances> {
483 com::initialize().ok()?;
484
485 let config = SetupConfiguration::new().ok()?;
486 let enum_setup_instances = config.enum_all_instances().ok()?;
487
488 Some(VsInstances::ComBased(enum_setup_instances))
489 }
490
491 fn vs15plus_instances_using_vswhere(
492 target: TargetArch<'_>,
493 env_getter: &dyn EnvGetter,
494 ) -> Option<VsInstances> {
495 let program_files_path = env_getter
496 .get_env("ProgramFiles(x86)")
497 .or_else(|| env_getter.get_env("ProgramFiles"))?;
498
499 let program_files_path = Path::new(program_files_path.as_ref());
500
501 let vswhere_path =
502 program_files_path.join(r"Microsoft Visual Studio\Installer\vswhere.exe");
503
504 if !vswhere_path.exists() {
505 return None;
506 }
507
508 let tools_arch = match target.into() {
509 "i586" | "i686" | "x86_64" => Some("x86.x64"),
510 "arm" | "thumbv7a" => Some("ARM"),
511 "aarch64" | "arm64ec" => Some("ARM64"),
512 _ => None,
513 };
514
515 let vswhere_output = Command::new(vswhere_path)
516 .args([
517 "-latest",
518 "-products",
519 "*",
520 "-requires",
521 &format!("Microsoft.VisualStudio.Component.VC.Tools.{}", tools_arch?),
522 "-format",
523 "text",
524 "-nologo",
525 ])
526 .stderr(std::process::Stdio::inherit())
527 .output()
528 .ok()?;
529
530 let vs_instances =
531 VsInstances::VswhereBased(VswhereInstance::try_from(&vswhere_output.stdout).ok()?);
532
533 Some(vs_instances)
534 }
535
536 fn parse_version(version: &str) -> Option<Vec<u16>> {
539 version
540 .split('.')
541 .map(|chunk| u16::from_str(chunk).ok())
542 .collect()
543 }
544
545 pub(super) fn find_msvc_15plus(
546 tool: &str,
547 target: TargetArch<'_>,
548 env_getter: &dyn EnvGetter,
549 ) -> Option<Tool> {
550 let iter = vs15plus_instances(target, env_getter)?;
551 iter.into_iter()
552 .filter_map(|instance| {
553 let version = parse_version(&instance.installation_version()?)?;
554 let instance_path = instance.installation_path()?;
555 let tool = tool_from_vs15plus_instance(tool, target, &instance_path, env_getter)?;
556 Some((version, tool))
557 })
558 .max_by(|(a_version, _), (b_version, _)| a_version.cmp(b_version))
559 .map(|(_version, tool)| tool)
560 }
561
562 fn find_tool_in_vs15_path(
570 tool: &str,
571 target: TargetArch<'_>,
572 env_getter: &dyn EnvGetter,
573 ) -> Option<Tool> {
574 let mut path = match vs15plus_instances(target, env_getter) {
575 Some(instances) => instances
576 .into_iter()
577 .filter_map(|instance| instance.installation_path())
578 .map(|path| path.join(tool))
579 .find(|path| path.is_file()),
580 None => None,
581 };
582
583 if path.is_none() {
584 let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
585 path = LOCAL_MACHINE
586 .open(key.as_ref())
587 .ok()
588 .and_then(|key| key.query_str("15.0").ok())
589 .map(|path| PathBuf::from(path).join(tool))
590 .and_then(|path| if path.is_file() { Some(path) } else { None });
591 }
592
593 path.map(|path| {
594 let mut tool = Tool::with_family(path, MSVC_FAMILY);
595 if target == "x86_64" {
596 tool.env.push(("Platform".into(), "X64".into()));
597 } else if target == "aarch64" {
598 tool.env.push(("Platform".into(), "ARM64".into()));
599 }
600 tool
601 })
602 }
603
604 fn tool_from_vs15plus_instance(
605 tool: &str,
606 target: TargetArch<'_>,
607 instance_path: &Path,
608 env_getter: &dyn EnvGetter,
609 ) -> Option<Tool> {
610 let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) =
611 vs15plus_vc_paths(target, instance_path, env_getter)?;
612 let tool_path = bin_path.join(tool);
613 if !tool_path.exists() {
614 return None;
615 };
616
617 let mut tool = MsvcTool::new(tool_path);
618 tool.path.push(bin_path.clone());
619 tool.path.push(host_dylib_path);
620 if let Some(alt_lib_path) = alt_lib_path {
621 tool.libs.push(alt_lib_path);
622 }
623 tool.libs.push(lib_path);
624 tool.include.push(include_path);
625
626 if let Some((atl_lib_path, atl_include_path)) = atl_paths(target, &root_path) {
627 tool.libs.push(atl_lib_path);
628 tool.include.push(atl_include_path);
629 }
630
631 add_sdks(&mut tool, target, env_getter)?;
632
633 Some(tool.into_tool(env_getter))
634 }
635
636 fn vs15plus_vc_paths(
637 target: TargetArch<'_>,
638 instance_path: &Path,
639 env_getter: &dyn EnvGetter,
640 ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option<PathBuf>, PathBuf)> {
641 let version = vs15plus_vc_read_version(instance_path)?;
642
643 let hosts = match host_arch() {
644 X86 => &["X86"],
645 X86_64 => &["X64"],
646 AARCH64 => {
651 if is_amd64_emulation_supported() {
652 &["ARM64", "X64", "X86"][..]
653 } else {
654 &["ARM64", "X86"]
655 }
656 }
657 _ => return None,
658 };
659 let target = lib_subdir(target)?;
660 let path = instance_path.join(r"VC\Tools\MSVC").join(version);
662 let (host_path, host) = hosts.iter().find_map(|&x| {
664 let candidate = path.join("bin").join(format!("Host{}", x));
665 if candidate.join(target).exists() {
666 Some((candidate, x))
667 } else {
668 None
669 }
670 })?;
671 let bin_path = host_path.join(target);
674 let host_dylib_path = host_path.join(host.to_lowercase());
678 let lib_fragment = if use_spectre_mitigated_libs(env_getter) {
679 r"lib\spectre"
680 } else {
681 "lib"
682 };
683 let lib_path = path.join(lib_fragment).join(target);
684 let alt_lib_path = (target == "arm64ec").then(|| path.join(lib_fragment).join("arm64ec"));
685 let include_path = path.join("include");
686 Some((
687 path,
688 bin_path,
689 host_dylib_path,
690 lib_path,
691 alt_lib_path,
692 include_path,
693 ))
694 }
695
696 fn vs15plus_vc_read_version(dir: &Path) -> Option<String> {
697 let mut version_path: PathBuf =
699 dir.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
700 let mut version_file = if let Ok(f) = File::open(&version_path) {
701 f
702 } else {
703 let mut version_file = String::new();
708 version_path.pop();
709 for file in version_path.read_dir().ok()? {
710 let name = file.ok()?.file_name();
711 let name = name.to_str()?;
712 if name.starts_with("Microsoft.VCToolsVersion.v")
713 && name.ends_with(".default.txt")
714 && name > &version_file
715 {
716 version_file.replace_range(.., name);
717 }
718 }
719 if version_file.is_empty() {
720 return None;
721 }
722 version_path.push(version_file);
723 File::open(version_path).ok()?
724 };
725
726 let mut version = String::new();
728 version_file.read_to_string(&mut version).ok()?;
729 version.truncate(version.trim_end().len());
730 Some(version)
731 }
732
733 fn use_spectre_mitigated_libs(env_getter: &dyn EnvGetter) -> bool {
734 env_getter
735 .get_env("VSCMD_ARG_VCVARS_SPECTRE")
736 .map(|env| env.as_ref() == "spectre")
737 .unwrap_or_default()
738 }
739
740 fn atl_paths(target: TargetArch<'_>, path: &Path) -> Option<(PathBuf, PathBuf)> {
741 let atl_path = path.join("atlmfc");
742 let sub = lib_subdir(target)?;
743 if atl_path.exists() {
744 Some((atl_path.join("lib").join(sub), atl_path.join("include")))
745 } else {
746 None
747 }
748 }
749
750 pub(super) fn find_msvc_14(
753 tool: &str,
754 target: TargetArch<'_>,
755 env_getter: &dyn EnvGetter,
756 ) -> Option<Tool> {
757 let vcdir = get_vc_dir("14.0")?;
758 let mut tool = get_tool(tool, &vcdir, target)?;
759 add_sdks(&mut tool, target, env_getter)?;
760 Some(tool.into_tool(env_getter))
761 }
762
763 fn add_sdks(
764 tool: &mut MsvcTool,
765 target: TargetArch<'_>,
766 env_getter: &dyn EnvGetter,
767 ) -> Option<()> {
768 let sub = lib_subdir(target)?;
769 let (ucrt, ucrt_version) = get_ucrt_dir()?;
770
771 let host = match host_arch() {
772 X86 => "x86",
773 X86_64 => "x64",
774 AARCH64 => "arm64",
775 _ => return None,
776 };
777
778 tool.path
779 .push(ucrt.join("bin").join(&ucrt_version).join(host));
780
781 let ucrt_include = ucrt.join("include").join(&ucrt_version);
782 tool.include.push(ucrt_include.join("ucrt"));
783
784 let ucrt_lib = ucrt.join("lib").join(&ucrt_version);
785 tool.libs.push(ucrt_lib.join("ucrt").join(sub));
786
787 if let Some((sdk, version)) = get_sdk10_dir(env_getter) {
788 tool.path.push(sdk.join("bin").join(host));
789 let sdk_lib = sdk.join("lib").join(&version);
790 tool.libs.push(sdk_lib.join("um").join(sub));
791 let sdk_include = sdk.join("include").join(&version);
792 tool.include.push(sdk_include.join("um"));
793 tool.include.push(sdk_include.join("cppwinrt"));
794 tool.include.push(sdk_include.join("winrt"));
795 tool.include.push(sdk_include.join("shared"));
796 } else if let Some(sdk) = get_sdk81_dir() {
797 tool.path.push(sdk.join("bin").join(host));
798 let sdk_lib = sdk.join("lib").join("winv6.3");
799 tool.libs.push(sdk_lib.join("um").join(sub));
800 let sdk_include = sdk.join("include");
801 tool.include.push(sdk_include.join("um"));
802 tool.include.push(sdk_include.join("winrt"));
803 tool.include.push(sdk_include.join("shared"));
804 }
805
806 Some(())
807 }
808
809 fn add_env(
810 tool: &mut Tool,
811 env: &'static str,
812 paths: Vec<PathBuf>,
813 env_getter: &dyn EnvGetter,
814 ) {
815 let prev = env_getter.get_env(env);
816 let prev = prev.as_ref().map(AsRef::as_ref).unwrap_or_default();
817 let prev = env::split_paths(&prev);
818 let new = paths.into_iter().chain(prev);
819 tool.env
820 .push((env.to_string().into(), env::join_paths(new).unwrap()));
821 }
822
823 fn get_tool(tool: &str, path: &Path, target: TargetArch<'_>) -> Option<MsvcTool> {
826 bin_subdir(target)
827 .into_iter()
828 .map(|(sub, host)| {
829 (
830 path.join("bin").join(sub).join(tool),
831 path.join("bin").join(host),
832 )
833 })
834 .filter(|(path, _)| path.is_file())
835 .map(|(path, host)| {
836 let mut tool = MsvcTool::new(path);
837 tool.path.push(host);
838 tool
839 })
840 .filter_map(|mut tool| {
841 let sub = vc_lib_subdir(target)?;
842 tool.libs.push(path.join("lib").join(sub));
843 tool.include.push(path.join("include"));
844 let atlmfc_path = path.join("atlmfc");
845 if atlmfc_path.exists() {
846 tool.libs.push(atlmfc_path.join("lib").join(sub));
847 tool.include.push(atlmfc_path.join("include"));
848 }
849 Some(tool)
850 })
851 .next()
852 }
853
854 fn get_vc_dir(ver: &str) -> Option<PathBuf> {
857 let key = r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7";
858 let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
859 let path = key.query_str(ver).ok()?;
860 Some(path.into())
861 }
862
863 fn get_ucrt_dir() -> Option<(PathBuf, String)> {
870 let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
871 let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
872 let root = key.query_str("KitsRoot10").ok()?;
873 let readdir = Path::new(&root).join("lib").read_dir().ok()?;
874 let max_libdir = readdir
875 .filter_map(|dir| dir.ok())
876 .map(|dir| dir.path())
877 .filter(|dir| {
878 dir.components()
879 .last()
880 .and_then(|c| c.as_os_str().to_str())
881 .map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir())
882 .unwrap_or(false)
883 })
884 .max()?;
885 let version = max_libdir.components().last().unwrap();
886 let version = version.as_os_str().to_str().unwrap().to_string();
887 Some((root.into(), version))
888 }
889
890 fn get_sdk10_dir(env_getter: &dyn EnvGetter) -> Option<(PathBuf, String)> {
902 if let (Some(root), Some(version)) = (
903 env_getter.get_env("WindowsSdkDir"),
904 env_getter
905 .get_env("WindowsSDKVersion")
906 .as_ref()
907 .and_then(|version| version.as_ref().to_str()),
908 ) {
909 return Some((
910 PathBuf::from(root),
911 version.trim_end_matches('\\').to_string(),
912 ));
913 }
914
915 let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
916 let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
917 let root = key.query_str("InstallationFolder").ok()?;
918 let readdir = Path::new(&root).join("lib").read_dir().ok()?;
919 let mut dirs = readdir
920 .filter_map(|dir| dir.ok())
921 .map(|dir| dir.path())
922 .collect::<Vec<_>>();
923 dirs.sort();
924 let dir = dirs
925 .into_iter()
926 .rev()
927 .find(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file())?;
928 let version = dir.components().last().unwrap();
929 let version = version.as_os_str().to_str().unwrap().to_string();
930 Some((root.into(), version))
931 }
932
933 fn get_sdk81_dir() -> Option<PathBuf> {
938 let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
939 let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
940 let root = key.query_str("InstallationFolder").ok()?;
941 Some(root.into())
942 }
943
944 const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0;
945 const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9;
946 const PROCESSOR_ARCHITECTURE_ARM64: u16 = 12;
947 const X86: u16 = PROCESSOR_ARCHITECTURE_INTEL;
948 const X86_64: u16 = PROCESSOR_ARCHITECTURE_AMD64;
949 const AARCH64: u16 = PROCESSOR_ARCHITECTURE_ARM64;
950
951 fn bin_subdir(target: TargetArch<'_>) -> Vec<(&'static str, &'static str)> {
964 match (target.into(), host_arch()) {
965 ("i586", X86) | ("i686", X86) => vec![("", "")],
966 ("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
967 ("x86_64", X86) => vec![("x86_amd64", "")],
968 ("x86_64", X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")],
969 ("arm", X86) | ("thumbv7a", X86) => vec![("x86_arm", "")],
970 ("arm", X86_64) | ("thumbv7a", X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")],
971 _ => vec![],
972 }
973 }
974
975 fn lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
976 match target.into() {
977 "i586" | "i686" => Some("x86"),
978 "x86_64" => Some("x64"),
979 "arm" | "thumbv7a" => Some("arm"),
980 "aarch64" | "arm64ec" => Some("arm64"),
981 _ => None,
982 }
983 }
984
985 fn vc_lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
987 match target.into() {
988 "i586" | "i686" => Some(""),
989 "x86_64" => Some("amd64"),
990 "arm" | "thumbv7a" => Some("arm"),
991 "aarch64" => Some("arm64"),
992 _ => None,
993 }
994 }
995
996 #[allow(bad_style)]
997 fn host_arch() -> u16 {
998 type DWORD = u32;
999 type WORD = u16;
1000 type LPVOID = *mut u8;
1001 type DWORD_PTR = usize;
1002
1003 #[repr(C)]
1004 struct SYSTEM_INFO {
1005 wProcessorArchitecture: WORD,
1006 _wReserved: WORD,
1007 _dwPageSize: DWORD,
1008 _lpMinimumApplicationAddress: LPVOID,
1009 _lpMaximumApplicationAddress: LPVOID,
1010 _dwActiveProcessorMask: DWORD_PTR,
1011 _dwNumberOfProcessors: DWORD,
1012 _dwProcessorType: DWORD,
1013 _dwAllocationGranularity: DWORD,
1014 _wProcessorLevel: WORD,
1015 _wProcessorRevision: WORD,
1016 }
1017
1018 extern "system" {
1019 fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
1020 }
1021
1022 unsafe {
1023 let mut info = mem::zeroed();
1024 GetNativeSystemInfo(&mut info);
1025 info.wProcessorArchitecture
1026 }
1027 }
1028
1029 fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
1034 let mut max_vers = 0;
1035 let mut max_key = None;
1036 for subkey in key.iter().filter_map(|k| k.ok()) {
1037 let val = subkey
1038 .to_str()
1039 .and_then(|s| s.trim_start_matches('v').replace('.', "").parse().ok());
1040 let val = match val {
1041 Some(s) => s,
1042 None => continue,
1043 };
1044 if val > max_vers {
1045 if let Ok(k) = key.open(&subkey) {
1046 max_vers = val;
1047 max_key = Some((subkey, k));
1048 }
1049 }
1050 }
1051 max_key
1052 }
1053
1054 #[inline(always)]
1055 pub(super) fn has_msbuild_version(version: &str, env_getter: &dyn EnvGetter) -> bool {
1056 match version {
1057 "17.0" => {
1058 find_msbuild_vs17(TargetArch("x86_64"), env_getter).is_some()
1059 || find_msbuild_vs17(TargetArch("i686"), env_getter).is_some()
1060 || find_msbuild_vs17(TargetArch("aarch64"), env_getter).is_some()
1061 }
1062 "16.0" => {
1063 find_msbuild_vs16(TargetArch("x86_64"), env_getter).is_some()
1064 || find_msbuild_vs16(TargetArch("i686"), env_getter).is_some()
1065 || find_msbuild_vs16(TargetArch("aarch64"), env_getter).is_some()
1066 }
1067 "15.0" => {
1068 find_msbuild_vs15(TargetArch("x86_64"), env_getter).is_some()
1069 || find_msbuild_vs15(TargetArch("i686"), env_getter).is_some()
1070 || find_msbuild_vs15(TargetArch("aarch64"), env_getter).is_some()
1071 }
1072 "14.0" => LOCAL_MACHINE
1073 .open(&OsString::from(format!(
1074 "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
1075 version
1076 )))
1077 .is_ok(),
1078 _ => false,
1079 }
1080 }
1081
1082 pub(super) fn find_devenv(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<Tool> {
1083 find_devenv_vs15(target, env_getter)
1084 }
1085
1086 fn find_devenv_vs15(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<Tool> {
1087 find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target, env_getter)
1088 }
1089
1090 pub(super) fn find_msbuild(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<Tool> {
1092 if let Some(r) = find_msbuild_vs17(target, env_getter) {
1094 Some(r)
1095 } else if let Some(r) = find_msbuild_vs16(target, env_getter) {
1096 return Some(r);
1097 } else if let Some(r) = find_msbuild_vs15(target, env_getter) {
1098 return Some(r);
1099 } else {
1100 find_old_msbuild(target)
1101 }
1102 }
1103
1104 fn find_msbuild_vs15(target: TargetArch<'_>, env_getter: &dyn EnvGetter) -> Option<Tool> {
1105 find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target, env_getter)
1106 }
1107
1108 fn find_old_msbuild(target: TargetArch<'_>) -> Option<Tool> {
1109 let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
1110 LOCAL_MACHINE
1111 .open(key.as_ref())
1112 .ok()
1113 .and_then(|key| {
1114 max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok())
1115 })
1116 .map(|path| {
1117 let mut path = PathBuf::from(path);
1118 path.push("MSBuild.exe");
1119 let mut tool = Tool::with_family(path, MSVC_FAMILY);
1120 if target == "x86_64" {
1121 tool.env.push(("Platform".into(), "X64".into()));
1122 }
1123 tool
1124 })
1125 }
1126}
1127
1128#[cfg(not(windows))]
1130mod impl_ {
1131 use std::{env, ffi::OsStr};
1132
1133 use super::{EnvGetter, TargetArch, MSVC_FAMILY};
1134 use crate::Tool;
1135
1136 #[inline(always)]
1139 pub(super) fn find_msbuild(_target: TargetArch<'_>, _: &dyn EnvGetter) -> Option<Tool> {
1140 None
1141 }
1142
1143 #[inline(always)]
1146 pub(super) fn find_devenv(_target: TargetArch<'_>, _: &dyn EnvGetter) -> Option<Tool> {
1147 None
1148 }
1149
1150 pub(super) fn find_msvc_environment(
1152 tool: &str,
1153 _target: TargetArch<'_>,
1154 env_getter: &dyn EnvGetter,
1155 ) -> Option<Tool> {
1156 let vc_install_dir = env_getter.get_env("VCINSTALLDIR")?;
1158 let vs_install_dir = env_getter.get_env("VSINSTALLDIR")?;
1159
1160 let get_tool = |install_dir: &OsStr| {
1161 env::split_paths(install_dir)
1162 .map(|p| p.join(tool))
1163 .find(|p| p.exists())
1164 .map(|path| Tool::with_family(path, MSVC_FAMILY))
1165 };
1166
1167 get_tool(vc_install_dir.as_ref())
1169 .or_else(|| get_tool(vs_install_dir.as_ref()))
1171 .or_else(|| {
1173 env_getter
1174 .get_env("PATH")
1175 .as_ref()
1176 .map(|path| path.as_ref())
1177 .and_then(get_tool)
1178 })
1179 }
1180
1181 #[inline(always)]
1182 pub(super) fn find_msvc_15plus(
1183 _tool: &str,
1184 _target: TargetArch<'_>,
1185 _: &dyn EnvGetter,
1186 ) -> Option<Tool> {
1187 None
1188 }
1189
1190 #[inline(always)]
1193 pub(super) fn find_msvc_14(
1194 _tool: &str,
1195 _target: TargetArch<'_>,
1196 _: &dyn EnvGetter,
1197 ) -> Option<Tool> {
1198 None
1199 }
1200
1201 #[inline(always)]
1202 pub(super) fn has_msbuild_version(_version: &str, _: &dyn EnvGetter) -> bool {
1203 false
1204 }
1205}