1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! A helper module to probe the Windows Registry when looking for
12 //! windows-specific tools.
14 use std
::process
::Command
;
18 use crate::ToolFamily
;
21 const MSVC_FAMILY
: ToolFamily
= ToolFamily
::Msvc { clang_cl: false }
;
23 /// Attempts to find a tool within an MSVC installation using the Windows
24 /// registry as a point to search from.
26 /// The `target` argument is the target that the tool should work for (e.g.
27 /// compile or link for) and the `tool` argument is the tool to find (e.g.
28 /// `cl.exe` or `link.exe`).
30 /// This function will return `None` if the tool could not be found, or it will
31 /// return `Some(cmd)` which represents a command that's ready to execute the
32 /// tool with the appropriate environment variables set.
34 /// Note that this function always returns `None` for non-MSVC targets.
35 pub fn find(target
: &str, tool
: &str) -> Option
<Command
> {
36 find_tool(target
, tool
).map(|c
| c
.to_command())
39 /// Similar to the `find` function above, this function will attempt the same
40 /// operation (finding a MSVC tool in a local install) but instead returns a
41 /// `Tool` which may be introspected.
43 pub fn find_tool(_target
: &str, _tool
: &str) -> Option
<Tool
> {
49 pub fn find_tool(target
: &str, tool
: &str) -> Option
<Tool
> {
50 // This logic is all tailored for MSVC, if we're not that then bail out
52 if !target
.contains("msvc") {
56 // Looks like msbuild isn't located in the same location as other tools like
57 // cl.exe and lib.exe. To handle this we probe for it manually with
58 // dedicated registry keys.
59 if tool
.contains("msbuild") {
60 return impl_
::find_msbuild(target
);
63 if tool
.contains("devenv") {
64 return impl_
::find_devenv(target
);
67 // Ok, if we're here, now comes the fun part of the probing. Default shells
68 // or shells like MSYS aren't really configured to execute `cl.exe` and the
69 // various compiler tools shipped as part of Visual Studio. Here we try to
70 // first find the relevant tool, then we also have to be sure to fill in
71 // environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
72 // the tool is actually usable.
74 return impl_
::find_msvc_environment(tool
, target
)
75 .or_else(|| impl_
::find_msvc_15plus(tool
, target
))
76 .or_else(|| impl_
::find_msvc_14(tool
, target
))
77 .or_else(|| impl_
::find_msvc_12(tool
, target
))
78 .or_else(|| impl_
::find_msvc_11(tool
, target
));
81 /// A version of Visual Studio
82 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
84 /// Visual Studio 12 (2013)
86 /// Visual Studio 14 (2015)
88 /// Visual Studio 15 (2017)
90 /// Visual Studio 16 (2019)
92 /// Visual Studio 17 (2022)
95 /// Hidden variant that should not be matched on. Callers that want to
96 /// handle an enumeration of `VsVers` instances should always have a default
97 /// case meaning that it's a VS version they don't understand.
100 __Nonexhaustive_do_not_match_this_or_your_code_will_break
,
103 /// Find the most recent installed version of Visual Studio
105 /// This is used by the cmake crate to figure out the correct
108 pub fn find_vs_version() -> Result
<VsVers
, String
> {
109 Err(format
!("not windows"))
114 pub fn find_vs_version() -> Result
<VsVers
, String
> {
117 match env
::var("VisualStudioVersion") {
118 Ok(version
) => match &version
[..] {
119 "17.0" => Ok(VsVers
::Vs17
),
120 "16.0" => Ok(VsVers
::Vs16
),
121 "15.0" => Ok(VsVers
::Vs15
),
122 "14.0" => Ok(VsVers
::Vs14
),
123 "12.0" => Ok(VsVers
::Vs12
),
126 unsupported or unknown VisualStudio version: {}\n\
127 if another version is installed consider running \
128 the appropriate vcvars script before building this \
135 // Check for the presence of a specific registry key
136 // that indicates visual studio is installed.
137 if impl_
::has_msbuild_version("16.0") {
139 } else if impl_
::has_msbuild_version("15.0") {
141 } else if impl_
::has_msbuild_version("14.0") {
143 } else if impl_
::has_msbuild_version("12.0") {
148 couldn't determine visual studio generator\n\
149 if VisualStudio is installed, however, consider \
150 running the appropriate vcvars script before building \
162 use crate::registry
::{RegistryKey, LOCAL_MACHINE}
;
163 use crate::setup_config
::SetupConfiguration
;
164 use crate::vs_instances
::{VsInstances, VswhereInstance}
;
165 use std
::convert
::TryFrom
;
167 use std
::ffi
::OsString
;
172 use std
::path
::{Path, PathBuf}
;
173 use std
::process
::Command
;
174 use std
::str::FromStr
;
176 use super::MSVC_FAMILY
;
183 include
: Vec
<PathBuf
>,
187 fn new(tool
: PathBuf
) -> MsvcTool
{
196 fn into_tool(self) -> Tool
{
203 let mut tool
= Tool
::with_family(tool
.into(), MSVC_FAMILY
);
204 add_env(&mut tool
, "LIB", libs
);
205 add_env(&mut tool
, "PATH", path
);
206 add_env(&mut tool
, "INCLUDE", include
);
211 /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
212 /// given target's arch. Returns `None` if the variable does not exist.
214 fn is_vscmd_target(target
: &str) -> Option
<bool
> {
215 let vscmd_arch
= env
::var("VSCMD_ARG_TGT_ARCH").ok()?
;
216 // Convert the Rust target arch to its VS arch equivalent.
217 let arch
= match target
.split("-").next() {
218 Some("x86_64") => "x64",
219 Some("aarch64") => "arm64",
220 Some("i686") | Some("i586") => "x86",
221 Some("thumbv7a") => "arm",
222 // An unrecognized arch.
223 _
=> return Some(false),
225 Some(vscmd_arch
== arch
)
228 /// Attempt to find the tool using environment variables set by vcvars.
229 pub fn find_msvc_environment(tool
: &str, target
: &str) -> Option
<Tool
> {
230 // Early return if the environment doesn't contain a VC install.
231 if env
::var_os("VCINSTALLDIR").is_none() {
234 let vs_install_dir
= env
::var_os("VSINSTALLDIR")?
.into();
236 // If the vscmd target differs from the requested target then
237 // attempt to get the tool using the VS install directory.
238 if is_vscmd_target(target
) == Some(false) {
239 // We will only get here with versions 15+.
240 tool_from_vs15plus_instance(tool
, target
, &vs_install_dir
)
242 // Fallback to simply using the current environment.
245 env
::split_paths(&path
)
246 .map(|p
| p
.join(tool
))
247 .find(|p
| p
.exists())
249 .map(|path
| Tool
::with_family(path
.into(), MSVC_FAMILY
))
253 #[allow(bare_trait_objects)]
254 fn vs16_instances(target
: &str) -> Box
<Iterator
<Item
= PathBuf
>> {
255 let instances
= if let Some(instances
) = vs15plus_instances(target
) {
258 return Box
::new(iter
::empty());
260 Box
::new(instances
.into_iter().filter_map(|instance
| {
261 let installation_name
= instance
.installation_name()?
;
262 if installation_name
.starts_with("VisualStudio/16.") {
263 Some(instance
.installation_path()?
)
264 } else if installation_name
.starts_with("VisualStudioPreview/16.") {
265 Some(instance
.installation_path()?
)
272 fn find_tool_in_vs16_path(tool
: &str, target
: &str) -> Option
<Tool
> {
273 vs16_instances(target
)
275 let path
= path
.join(tool
);
279 let mut tool
= Tool
::with_family(path
, MSVC_FAMILY
);
280 if target
.contains("x86_64") {
281 tool
.env
.push(("Platform".into(), "X64".into()));
283 if target
.contains("aarch64") {
284 tool
.env
.push(("Platform".into(), "ARM64".into()));
291 fn find_msbuild_vs16(target
: &str) -> Option
<Tool
> {
292 find_tool_in_vs16_path(r
"MSBuild\Current\Bin\MSBuild.exe", target
)
295 // In MSVC 15 (2017) MS once again changed the scheme for locating
296 // the tooling. Now we must go through some COM interfaces, which
297 // is super fun for Rust.
299 // Note that much of this logic can be found [online] wrt paths, COM, etc.
301 // [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
303 // Returns MSVC 15+ instances (15, 16 right now), the order should be consider undefined.
305 // However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64.
306 // Hence, as the last resort we try to use vswhere.exe to list available instances.
307 fn vs15plus_instances(target
: &str) -> Option
<VsInstances
> {
308 vs15plus_instances_using_com().or_else(|| vs15plus_instances_using_vswhere(target
))
311 fn vs15plus_instances_using_com() -> Option
<VsInstances
> {
312 com
::initialize().ok()?
;
314 let config
= SetupConfiguration
::new().ok()?
;
315 let enum_setup_instances
= config
.enum_all_instances().ok()?
;
317 Some(VsInstances
::ComBased(enum_setup_instances
))
320 fn vs15plus_instances_using_vswhere(target
: &str) -> Option
<VsInstances
> {
321 let program_files_path
: PathBuf
= env
::var("ProgramFiles(x86)")
322 .or_else(|_
| env
::var("ProgramFiles"))
327 program_files_path
.join(r
"Microsoft Visual Studio\Installer\vswhere.exe");
329 if !vswhere_path
.exists() {
333 let arch
= target
.split('
-'
).next().unwrap();
334 let tools_arch
= match arch
{
335 "i586" | "i686" | "x86_64" => Some("x86.x64"),
336 "arm" | "thumbv7a" => Some("ARM"),
337 "aarch64" => Some("ARM64"),
341 let vswhere_output
= Command
::new(vswhere_path
)
347 &format
!("Microsoft.VisualStudio.Component.VC.Tools.{}", tools_arch?
),
352 .stderr(std
::process
::Stdio
::inherit())
357 VsInstances
::VswhereBased(VswhereInstance
::try_from(&vswhere_output
.stdout
).ok()?
);
362 // Inspired from official microsoft/vswhere ParseVersionString
363 // i.e. at most four u16 numbers separated by '.'
364 fn parse_version(version
: &str) -> Option
<Vec
<u16>> {
367 .map(|chunk
| u16::from_str(chunk
).ok())
371 pub fn find_msvc_15plus(tool
: &str, target
: &str) -> Option
<Tool
> {
372 let iter
= vs15plus_instances(target
)?
;
374 .filter_map(|instance
| {
375 let version
= parse_version(&instance
.installation_version()?
)?
;
376 let instance_path
= instance
.installation_path()?
;
377 let tool
= tool_from_vs15plus_instance(tool
, target
, &instance_path
)?
;
378 Some((version
, tool
))
380 .max_by(|(a_version
, _
), (b_version
, _
)| a_version
.cmp(b_version
))
381 .map(|(_version
, tool
)| tool
)
384 // While the paths to Visual Studio 2017's devenv and MSBuild could
385 // potentially be retrieved from the registry, finding them via
386 // SetupConfiguration has shown to be [more reliable], and is preferred
387 // according to Microsoft. To help head off potential regressions though,
388 // we keep the registry method as a fallback option.
390 // [more reliable]: https://github.com/alexcrichton/cc-rs/pull/331
391 fn find_tool_in_vs15_path(tool
: &str, target
: &str) -> Option
<Tool
> {
392 let mut path
= match vs15plus_instances(target
) {
393 Some(instances
) => instances
395 .filter_map(|instance
| instance
.installation_path())
396 .map(|path
| path
.join(tool
))
397 .find(|ref path
| path
.is_file()),
402 let key
= r
"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
406 .and_then(|key
| key
.query_str("15.0").ok())
407 .map(|path
| PathBuf
::from(path
).join(tool
))
408 .and_then(|path
| if path
.is_file() { Some(path) }
else { None }
);
412 let mut tool
= Tool
::with_family(path
, MSVC_FAMILY
);
413 if target
.contains("x86_64") {
414 tool
.env
.push(("Platform".into(), "X64".into()));
416 if target
.contains("aarch64") {
417 tool
.env
.push(("Platform".into(), "ARM64".into()));
423 fn tool_from_vs15plus_instance(
426 instance_path
: &PathBuf
,
428 let (bin_path
, host_dylib_path
, lib_path
, include_path
) =
429 vs15plus_vc_paths(target
, instance_path
)?
;
430 let tool_path
= bin_path
.join(tool
);
431 if !tool_path
.exists() {
435 let mut tool
= MsvcTool
::new(tool_path
);
436 tool
.path
.push(bin_path
.clone());
437 tool
.path
.push(host_dylib_path
);
438 tool
.libs
.push(lib_path
);
439 tool
.include
.push(include_path
);
441 if let Some((atl_lib_path
, atl_include_path
)) = atl_paths(target
, &bin_path
) {
442 tool
.libs
.push(atl_lib_path
);
443 tool
.include
.push(atl_include_path
);
446 add_sdks(&mut tool
, target
)?
;
448 Some(tool
.into_tool())
451 fn vs15plus_vc_paths(
453 instance_path
: &PathBuf
,
454 ) -> Option
<(PathBuf
, PathBuf
, PathBuf
, PathBuf
)> {
456 instance_path
.join(r
"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
457 let mut version_file
= File
::open(version_path
).ok()?
;
458 let mut version
= String
::new();
459 version_file
.read_to_string(&mut version
).ok()?
;
460 let version
= version
.trim();
461 let host
= match host_arch() {
464 // There is no natively hosted compiler on ARM64.
465 // Instead, use the x86 toolchain under emulation (there is no x64 emulation).
469 let target
= lib_subdir(target
)?
;
470 // The directory layout here is MSVC/bin/Host$host/$target/
471 let path
= instance_path
.join(r
"VC\Tools\MSVC").join(version
);
472 // This is the path to the toolchain for a particular target, running
476 .join(&format
!("Host{}", host
))
478 // But! we also need PATH to contain the target directory for the host
479 // architecture, because it contains dlls like mspdb140.dll compiled for
480 // the host architecture.
481 let host_dylib_path
= path
483 .join(&format
!("Host{}", host
))
484 .join(&host
.to_lowercase());
485 let lib_path
= path
.join("lib").join(&target
);
486 let include_path
= path
.join("include");
487 Some((bin_path
, host_dylib_path
, lib_path
, include_path
))
490 fn atl_paths(target
: &str, path
: &Path
) -> Option
<(PathBuf
, PathBuf
)> {
491 let atl_path
= path
.join("atlmfc");
492 let sub
= lib_subdir(target
)?
;
493 if atl_path
.exists() {
494 Some((atl_path
.join("lib").join(sub
), atl_path
.join("include")))
500 // For MSVC 14 we need to find the Universal CRT as well as either
501 // the Windows 10 SDK or Windows 8.1 SDK.
502 pub fn find_msvc_14(tool
: &str, target
: &str) -> Option
<Tool
> {
503 let vcdir
= get_vc_dir("14.0")?
;
504 let mut tool
= get_tool(tool
, &vcdir
, target
)?
;
505 add_sdks(&mut tool
, target
)?
;
506 Some(tool
.into_tool())
509 fn add_sdks(tool
: &mut MsvcTool
, target
: &str) -> Option
<()> {
510 let sub
= lib_subdir(target
)?
;
511 let (ucrt
, ucrt_version
) = get_ucrt_dir()?
;
513 let host
= match host_arch() {
521 .push(ucrt
.join("bin").join(&ucrt_version
).join(host
));
523 let ucrt_include
= ucrt
.join("include").join(&ucrt_version
);
524 tool
.include
.push(ucrt_include
.join("ucrt"));
526 let ucrt_lib
= ucrt
.join("lib").join(&ucrt_version
);
527 tool
.libs
.push(ucrt_lib
.join("ucrt").join(sub
));
529 if let Some((sdk
, version
)) = get_sdk10_dir() {
530 tool
.path
.push(sdk
.join("bin").join(host
));
531 let sdk_lib
= sdk
.join("lib").join(&version
);
532 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
533 let sdk_include
= sdk
.join("include").join(&version
);
534 tool
.include
.push(sdk_include
.join("um"));
535 tool
.include
.push(sdk_include
.join("cppwinrt"));
536 tool
.include
.push(sdk_include
.join("winrt"));
537 tool
.include
.push(sdk_include
.join("shared"));
538 } else if let Some(sdk
) = get_sdk81_dir() {
539 tool
.path
.push(sdk
.join("bin").join(host
));
540 let sdk_lib
= sdk
.join("lib").join("winv6.3");
541 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
542 let sdk_include
= sdk
.join("include");
543 tool
.include
.push(sdk_include
.join("um"));
544 tool
.include
.push(sdk_include
.join("winrt"));
545 tool
.include
.push(sdk_include
.join("shared"));
551 // For MSVC 12 we need to find the Windows 8.1 SDK.
552 pub fn find_msvc_12(tool
: &str, target
: &str) -> Option
<Tool
> {
553 let vcdir
= get_vc_dir("12.0")?
;
554 let mut tool
= get_tool(tool
, &vcdir
, target
)?
;
555 let sub
= lib_subdir(target
)?
;
556 let sdk81
= get_sdk81_dir()?
;
557 tool
.path
.push(sdk81
.join("bin").join(sub
));
558 let sdk_lib
= sdk81
.join("lib").join("winv6.3");
559 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
560 let sdk_include
= sdk81
.join("include");
561 tool
.include
.push(sdk_include
.join("shared"));
562 tool
.include
.push(sdk_include
.join("um"));
563 tool
.include
.push(sdk_include
.join("winrt"));
564 Some(tool
.into_tool())
567 // For MSVC 11 we need to find the Windows 8 SDK.
568 pub fn find_msvc_11(tool
: &str, target
: &str) -> Option
<Tool
> {
569 let vcdir
= get_vc_dir("11.0")?
;
570 let mut tool
= get_tool(tool
, &vcdir
, target
)?
;
571 let sub
= lib_subdir(target
)?
;
572 let sdk8
= get_sdk8_dir()?
;
573 tool
.path
.push(sdk8
.join("bin").join(sub
));
574 let sdk_lib
= sdk8
.join("lib").join("win8");
575 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
576 let sdk_include
= sdk8
.join("include");
577 tool
.include
.push(sdk_include
.join("shared"));
578 tool
.include
.push(sdk_include
.join("um"));
579 tool
.include
.push(sdk_include
.join("winrt"));
580 Some(tool
.into_tool())
583 fn add_env(tool
: &mut Tool
, env
: &str, paths
: Vec
<PathBuf
>) {
584 let prev
= env
::var_os(env
).unwrap_or(OsString
::new());
585 let prev
= env
::split_paths(&prev
);
586 let new
= paths
.into_iter().chain(prev
);
588 .push((env
.to_string().into(), env
::join_paths(new
).unwrap()));
591 // Given a possible MSVC installation directory, we look for the linker and
592 // then add the MSVC library path.
593 fn get_tool(tool
: &str, path
: &Path
, target
: &str) -> Option
<MsvcTool
> {
598 path
.join("bin").join(sub
).join(tool
),
599 path
.join("bin").join(host
),
602 .filter(|&(ref path
, _
)| path
.is_file())
603 .map(|(path
, host
)| {
604 let mut tool
= MsvcTool
::new(path
);
605 tool
.path
.push(host
);
608 .filter_map(|mut tool
| {
609 let sub
= vc_lib_subdir(target
)?
;
610 tool
.libs
.push(path
.join("lib").join(sub
));
611 tool
.include
.push(path
.join("include"));
612 let atlmfc_path
= path
.join("atlmfc");
613 if atlmfc_path
.exists() {
614 tool
.libs
.push(atlmfc_path
.join("lib").join(sub
));
615 tool
.include
.push(atlmfc_path
.join("include"));
622 // To find MSVC we look in a specific registry key for the version we are
624 fn get_vc_dir(ver
: &str) -> Option
<PathBuf
> {
625 let key
= r
"SOFTWARE\Microsoft\VisualStudio\SxS\VC7";
626 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
627 let path
= key
.query_str(ver
).ok()?
;
631 // To find the Universal CRT we look in a specific registry key for where
632 // all the Universal CRTs are located and then sort them asciibetically to
633 // find the newest version. While this sort of sorting isn't ideal, it is
634 // what vcvars does so that's good enough for us.
636 // Returns a pair of (root, version) for the ucrt dir if found
637 fn get_ucrt_dir() -> Option
<(PathBuf
, String
)> {
638 let key
= r
"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
639 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
640 let root
= key
.query_str("KitsRoot10").ok()?
;
641 let readdir
= Path
::new(&root
).join("lib").read_dir().ok()?
;
642 let max_libdir
= readdir
643 .filter_map(|dir
| dir
.ok())
644 .map(|dir
| dir
.path())
648 .and_then(|c
| c
.as_os_str().to_str())
649 .map(|c
| c
.starts_with("10.") && dir
.join("ucrt").is_dir())
653 let version
= max_libdir
.components().last().unwrap();
654 let version
= version
.as_os_str().to_str().unwrap().to_string();
655 Some((root
.into(), version
))
658 // Vcvars finds the correct version of the Windows 10 SDK by looking
659 // for the include `um\Windows.h` because sometimes a given version will
660 // only have UCRT bits without the rest of the SDK. Since we only care about
661 // libraries and not includes, we instead look for `um\x64\kernel32.lib`.
662 // Since the 32-bit and 64-bit libraries are always installed together we
663 // only need to bother checking x64, making this code a tiny bit simpler.
664 // Like we do for the Universal CRT, we sort the possibilities
665 // asciibetically to find the newest one as that is what vcvars does.
666 fn get_sdk10_dir() -> Option
<(PathBuf
, String
)> {
667 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
668 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
669 let root
= key
.query_str("InstallationFolder").ok()?
;
670 let readdir
= Path
::new(&root
).join("lib").read_dir().ok()?
;
671 let mut dirs
= readdir
672 .filter_map(|dir
| dir
.ok())
673 .map(|dir
| dir
.path())
674 .collect
::<Vec
<_
>>();
679 .filter(|dir
| dir
.join("um").join("x64").join("kernel32.lib").is_file())
681 let version
= dir
.components().last().unwrap();
682 let version
= version
.as_os_str().to_str().unwrap().to_string();
683 Some((root
.into(), version
))
686 // Interestingly there are several subdirectories, `win7` `win8` and
687 // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
688 // applies to us. Note that if we were targeting kernel mode drivers
689 // instead of user mode applications, we would care.
690 fn get_sdk81_dir() -> Option
<PathBuf
> {
691 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
692 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
693 let root
= key
.query_str("InstallationFolder").ok()?
;
697 fn get_sdk8_dir() -> Option
<PathBuf
> {
698 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0";
699 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
700 let root
= key
.query_str("InstallationFolder").ok()?
;
704 const PROCESSOR_ARCHITECTURE_INTEL
: u16 = 0;
705 const PROCESSOR_ARCHITECTURE_AMD64
: u16 = 9;
706 const PROCESSOR_ARCHITECTURE_ARM64
: u16 = 12;
707 const X86
: u16 = PROCESSOR_ARCHITECTURE_INTEL
;
708 const X86_64
: u16 = PROCESSOR_ARCHITECTURE_AMD64
;
709 const AARCH64
: u16 = PROCESSOR_ARCHITECTURE_ARM64
;
711 // When choosing the tool to use, we have to choose the one which matches
712 // the target architecture. Otherwise we end up in situations where someone
713 // on 32-bit Windows is trying to cross compile to 64-bit and it tries to
714 // invoke the native 64-bit compiler which won't work.
716 // For the return value of this function, the first member of the tuple is
717 // the folder of the tool we will be invoking, while the second member is
718 // the folder of the host toolchain for that tool which is essential when
719 // using a cross linker. We return a Vec since on x64 there are often two
720 // linkers that can target the architecture we desire. The 64-bit host
721 // linker is preferred, and hence first, due to 64-bit allowing it more
722 // address space to work with and potentially being faster.
723 fn bin_subdir(target
: &str) -> Vec
<(&'
static str, &'
static str)> {
724 let arch
= target
.split('
-'
).next().unwrap();
725 match (arch
, host_arch()) {
726 ("i586", X86
) | ("i686", X86
) => vec
![("", "")],
727 ("i586", X86_64
) | ("i686", X86_64
) => vec
![("amd64_x86", "amd64"), ("", "")],
728 ("x86_64", X86
) => vec
![("x86_amd64", "")],
729 ("x86_64", X86_64
) => vec
![("amd64", "amd64"), ("x86_amd64", "")],
730 ("arm", X86
) | ("thumbv7a", X86
) => vec
![("x86_arm", "")],
731 ("arm", X86_64
) | ("thumbv7a", X86_64
) => vec
![("amd64_arm", "amd64"), ("x86_arm", "")],
736 fn lib_subdir(target
: &str) -> Option
<&'
static str> {
737 let arch
= target
.split('
-'
).next().unwrap();
739 "i586" | "i686" => Some("x86"),
740 "x86_64" => Some("x64"),
741 "arm" | "thumbv7a" => Some("arm"),
742 "aarch64" => Some("arm64"),
747 // MSVC's x86 libraries are not in a subfolder
748 fn vc_lib_subdir(target
: &str) -> Option
<&'
static str> {
749 let arch
= target
.split('
-'
).next().unwrap();
751 "i586" | "i686" => Some(""),
752 "x86_64" => Some("amd64"),
753 "arm" | "thumbv7a" => Some("arm"),
754 "aarch64" => Some("arm64"),
760 fn host_arch() -> u16 {
763 type LPVOID
= *mut u8;
764 type DWORD_PTR
= usize;
768 wProcessorArchitecture
: WORD
,
771 _lpMinimumApplicationAddress
: LPVOID
,
772 _lpMaximumApplicationAddress
: LPVOID
,
773 _dwActiveProcessorMask
: DWORD_PTR
,
774 _dwNumberOfProcessors
: DWORD
,
775 _dwProcessorType
: DWORD
,
776 _dwAllocationGranularity
: DWORD
,
777 _wProcessorLevel
: WORD
,
778 _wProcessorRevision
: WORD
,
782 fn GetNativeSystemInfo(lpSystemInfo
: *mut SYSTEM_INFO
);
786 let mut info
= mem
::zeroed();
787 GetNativeSystemInfo(&mut info
);
788 info
.wProcessorArchitecture
792 // Given a registry key, look at all the sub keys and find the one which has
793 // the maximal numeric value.
795 // Returns the name of the maximal key as well as the opened maximal key.
796 fn max_version(key
: &RegistryKey
) -> Option
<(OsString
, RegistryKey
)> {
797 let mut max_vers
= 0;
798 let mut max_key
= None
;
799 for subkey
in key
.iter().filter_map(|k
| k
.ok()) {
802 .and_then(|s
| s
.trim_left_matches("v").replace(".", "").parse().ok());
803 let val
= match val
{
808 if let Ok(k
) = key
.open(&subkey
) {
810 max_key
= Some((subkey
, k
));
817 pub fn has_msbuild_version(version
: &str) -> bool
{
820 find_msbuild_vs16("x86_64-pc-windows-msvc").is_some()
821 || find_msbuild_vs16("i686-pc-windows-msvc").is_some()
822 || find_msbuild_vs16("aarch64-pc-windows-msvc").is_some()
825 find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
826 || find_msbuild_vs15("i686-pc-windows-msvc").is_some()
827 || find_msbuild_vs15("aarch64-pc-windows-msvc").is_some()
829 "12.0" | "14.0" => LOCAL_MACHINE
830 .open(&OsString
::from(format
!(
831 "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
839 pub fn find_devenv(target
: &str) -> Option
<Tool
> {
840 find_devenv_vs15(&target
)
843 fn find_devenv_vs15(target
: &str) -> Option
<Tool
> {
844 find_tool_in_vs15_path(r
"Common7\IDE\devenv.exe", target
)
847 // see http://stackoverflow.com/questions/328017/path-to-msbuild
848 pub fn find_msbuild(target
: &str) -> Option
<Tool
> {
849 // VS 15 (2017) changed how to locate msbuild
850 if let Some(r
) = find_msbuild_vs16(target
) {
852 } else if let Some(r
) = find_msbuild_vs15(target
) {
855 find_old_msbuild(target
)
859 fn find_msbuild_vs15(target
: &str) -> Option
<Tool
> {
860 find_tool_in_vs15_path(r
"MSBuild\15.0\Bin\MSBuild.exe", target
)
863 fn find_old_msbuild(target
: &str) -> Option
<Tool
> {
864 let key
= r
"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
869 max_version(&key
).and_then(|(_vers
, key
)| key
.query_str("MSBuildToolsPath").ok())
872 let mut path
= PathBuf
::from(path
);
873 path
.push("MSBuild.exe");
874 let mut tool
= Tool
::with_family(path
, MSVC_FAMILY
);
875 if target
.contains("x86_64") {
876 tool
.env
.push(("Platform".into(), "X64".into()));