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)
93 /// Hidden variant that should not be matched on. Callers that want to
94 /// handle an enumeration of `VsVers` instances should always have a default
95 /// case meaning that it's a VS version they don't understand.
98 __Nonexhaustive_do_not_match_this_or_your_code_will_break
,
101 /// Find the most recent installed version of Visual Studio
103 /// This is used by the cmake crate to figure out the correct
106 pub fn find_vs_version() -> Result
<VsVers
, String
> {
107 Err(format
!("not windows"))
112 pub fn find_vs_version() -> Result
<VsVers
, String
> {
115 match env
::var("VisualStudioVersion") {
116 Ok(version
) => match &version
[..] {
117 "16.0" => Ok(VsVers
::Vs16
),
118 "15.0" => Ok(VsVers
::Vs15
),
119 "14.0" => Ok(VsVers
::Vs14
),
120 "12.0" => Ok(VsVers
::Vs12
),
123 unsupported or unknown VisualStudio version: {}\n\
124 if another version is installed consider running \
125 the appropriate vcvars script before building this \
132 // Check for the presence of a specific registry key
133 // that indicates visual studio is installed.
134 if impl_
::has_msbuild_version("16.0") {
136 } else if impl_
::has_msbuild_version("15.0") {
138 } else if impl_
::has_msbuild_version("14.0") {
140 } else if impl_
::has_msbuild_version("12.0") {
145 couldn't determine visual studio generator\n\
146 if VisualStudio is installed, however, consider \
147 running the appropriate vcvars script before building \
159 use crate::registry
::{RegistryKey, LOCAL_MACHINE}
;
160 use crate::setup_config
::SetupConfiguration
;
161 use crate::vs_instances
::{VsInstances, VswhereInstance}
;
162 use std
::convert
::TryFrom
;
164 use std
::ffi
::OsString
;
169 use std
::path
::{Path, PathBuf}
;
170 use std
::process
::Command
;
171 use std
::str::FromStr
;
173 use super::MSVC_FAMILY
;
180 include
: Vec
<PathBuf
>,
184 fn new(tool
: PathBuf
) -> MsvcTool
{
193 fn into_tool(self) -> Tool
{
200 let mut tool
= Tool
::with_family(tool
.into(), MSVC_FAMILY
);
201 add_env(&mut tool
, "LIB", libs
);
202 add_env(&mut tool
, "PATH", path
);
203 add_env(&mut tool
, "INCLUDE", include
);
208 /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
209 /// given target's arch. Returns `None` if the variable does not exist.
211 fn is_vscmd_target(target
: &str) -> Option
<bool
> {
212 let vscmd_arch
= env
::var("VSCMD_ARG_TGT_ARCH").ok()?
;
213 // Convert the Rust target arch to its VS arch equivalent.
214 let arch
= match target
.split("-").next() {
215 Some("x86_64") => "x64",
216 Some("aarch64") => "arm64",
217 Some("i686") | Some("i586") => "x86",
218 Some("thumbv7a") => "arm",
219 // An unrecognized arch.
220 _
=> return Some(false),
222 Some(vscmd_arch
== arch
)
225 /// Attempt to find the tool using environment variables set by vcvars.
226 pub fn find_msvc_environment(target
: &str, tool
: &str) -> Option
<Tool
> {
227 // Early return if the environment doesn't contain a VC install.
228 if env
::var_os("VCINSTALLDIR").is_none() {
231 let vs_install_dir
= env
::var_os("VSINSTALLDIR")?
.into();
233 // If the vscmd target differs from the requested target then
234 // attempt to get the tool using the VS install directory.
235 if is_vscmd_target(target
) == Some(false) {
236 // We will only get here with versions 15+.
237 tool_from_vs15plus_instance(tool
, target
, &vs_install_dir
)
239 // Fallback to simply using the current environment.
242 env
::split_paths(&path
)
243 .map(|p
| p
.join(tool
))
244 .find(|p
| p
.exists())
246 .map(|path
| Tool
::with_family(path
.into(), MSVC_FAMILY
))
250 #[allow(bare_trait_objects)]
251 fn vs16_instances(target
: &str) -> Box
<Iterator
<Item
= PathBuf
>> {
252 let instances
= if let Some(instances
) = vs15plus_instances(target
) {
255 return Box
::new(iter
::empty());
257 Box
::new(instances
.into_iter().filter_map(|instance
| {
258 let installation_name
= instance
.installation_name()?
;
259 if installation_name
.starts_with("VisualStudio/16.") {
260 Some(instance
.installation_path()?
)
261 } else if installation_name
.starts_with("VisualStudioPreview/16.") {
262 Some(instance
.installation_path()?
)
269 fn find_tool_in_vs16_path(tool
: &str, target
: &str) -> Option
<Tool
> {
270 vs16_instances(target
)
272 let path
= path
.join(tool
);
276 let mut tool
= Tool
::with_family(path
, MSVC_FAMILY
);
277 if target
.contains("x86_64") {
278 tool
.env
.push(("Platform".into(), "X64".into()));
280 if target
.contains("aarch64") {
281 tool
.env
.push(("Platform".into(), "ARM64".into()));
288 fn find_msbuild_vs16(target
: &str) -> Option
<Tool
> {
289 find_tool_in_vs16_path(r
"MSBuild\Current\Bin\MSBuild.exe", target
)
292 // In MSVC 15 (2017) MS once again changed the scheme for locating
293 // the tooling. Now we must go through some COM interfaces, which
294 // is super fun for Rust.
296 // Note that much of this logic can be found [online] wrt paths, COM, etc.
298 // [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
300 // Returns MSVC 15+ instances (15, 16 right now), the order should be consider undefined.
302 // However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64.
303 // Hence, as the last resort we try to use vswhere.exe to list available instances.
304 fn vs15plus_instances(target
: &str) -> Option
<VsInstances
> {
305 vs15plus_instances_using_com().or_else(|| vs15plus_instances_using_vswhere(target
))
308 fn vs15plus_instances_using_com() -> Option
<VsInstances
> {
309 com
::initialize().ok()?
;
311 let config
= SetupConfiguration
::new().ok()?
;
312 let enum_setup_instances
= config
.enum_all_instances().ok()?
;
314 Some(VsInstances
::ComBased(enum_setup_instances
))
317 fn vs15plus_instances_using_vswhere(target
: &str) -> Option
<VsInstances
> {
318 let program_files_path
: PathBuf
= env
::var("ProgramFiles(x86)")
319 .or_else(|_
| env
::var("ProgramFiles"))
324 program_files_path
.join(r
"Microsoft Visual Studio\Installer\vswhere.exe");
326 if !vswhere_path
.exists() {
330 let arch
= target
.split('
-'
).next().unwrap();
331 let tools_arch
= match arch
{
332 "i586" | "i686" | "x86_64" => Some("x86.x64"),
333 "arm" | "thumbv7a" => Some("ARM"),
334 "aarch64" => Some("ARM64"),
338 let vswhere_output
= Command
::new(vswhere_path
)
344 &format
!("Microsoft.VisualStudio.Component.VC.Tools.{}", tools_arch?
),
349 .stderr(std
::process
::Stdio
::inherit())
354 VsInstances
::VswhereBased(VswhereInstance
::try_from(&vswhere_output
.stdout
).ok()?
);
359 // Inspired from official microsoft/vswhere ParseVersionString
360 // i.e. at most four u16 numbers separated by '.'
361 fn parse_version(version
: &str) -> Option
<Vec
<u16>> {
364 .map(|chunk
| u16::from_str(chunk
).ok())
368 pub fn find_msvc_15plus(tool
: &str, target
: &str) -> Option
<Tool
> {
369 let iter
= vs15plus_instances(target
)?
;
371 .filter_map(|instance
| {
372 let version
= parse_version(&instance
.installation_version()?
)?
;
373 let instance_path
= instance
.installation_path()?
;
374 let tool
= tool_from_vs15plus_instance(tool
, target
, &instance_path
)?
;
375 Some((version
, tool
))
377 .max_by(|(a_version
, _
), (b_version
, _
)| a_version
.cmp(b_version
))
378 .map(|(_version
, tool
)| tool
)
381 // While the paths to Visual Studio 2017's devenv and MSBuild could
382 // potentially be retrieved from the registry, finding them via
383 // SetupConfiguration has shown to be [more reliable], and is preferred
384 // according to Microsoft. To help head off potential regressions though,
385 // we keep the registry method as a fallback option.
387 // [more reliable]: https://github.com/alexcrichton/cc-rs/pull/331
388 fn find_tool_in_vs15_path(tool
: &str, target
: &str) -> Option
<Tool
> {
389 let mut path
= match vs15plus_instances(target
) {
390 Some(instances
) => instances
392 .filter_map(|instance
| instance
.installation_path())
393 .map(|path
| path
.join(tool
))
394 .find(|ref path
| path
.is_file()),
399 let key
= r
"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
403 .and_then(|key
| key
.query_str("15.0").ok())
404 .map(|path
| PathBuf
::from(path
).join(tool
))
405 .and_then(|path
| if path
.is_file() { Some(path) }
else { None }
);
409 let mut tool
= Tool
::with_family(path
, MSVC_FAMILY
);
410 if target
.contains("x86_64") {
411 tool
.env
.push(("Platform".into(), "X64".into()));
413 if target
.contains("aarch64") {
414 tool
.env
.push(("Platform".into(), "ARM64".into()));
420 fn tool_from_vs15plus_instance(
423 instance_path
: &PathBuf
,
425 let (bin_path
, host_dylib_path
, lib_path
, include_path
) =
426 vs15plus_vc_paths(target
, instance_path
)?
;
427 let tool_path
= bin_path
.join(tool
);
428 if !tool_path
.exists() {
432 let mut tool
= MsvcTool
::new(tool_path
);
433 tool
.path
.push(bin_path
.clone());
434 tool
.path
.push(host_dylib_path
);
435 tool
.libs
.push(lib_path
);
436 tool
.include
.push(include_path
);
438 if let Some((atl_lib_path
, atl_include_path
)) = atl_paths(target
, &bin_path
) {
439 tool
.libs
.push(atl_lib_path
);
440 tool
.include
.push(atl_include_path
);
443 add_sdks(&mut tool
, target
)?
;
445 Some(tool
.into_tool())
448 fn vs15plus_vc_paths(
450 instance_path
: &PathBuf
,
451 ) -> Option
<(PathBuf
, PathBuf
, PathBuf
, PathBuf
)> {
453 instance_path
.join(r
"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
454 let mut version_file
= File
::open(version_path
).ok()?
;
455 let mut version
= String
::new();
456 version_file
.read_to_string(&mut version
).ok()?
;
457 let version
= version
.trim();
458 let host
= match host_arch() {
461 // There is no natively hosted compiler on ARM64.
462 // Instead, use the x86 toolchain under emulation (there is no x64 emulation).
466 let target
= lib_subdir(target
)?
;
467 // The directory layout here is MSVC/bin/Host$host/$target/
468 let path
= instance_path
.join(r
"VC\Tools\MSVC").join(version
);
469 // This is the path to the toolchain for a particular target, running
473 .join(&format
!("Host{}", host
))
475 // But! we also need PATH to contain the target directory for the host
476 // architecture, because it contains dlls like mspdb140.dll compiled for
477 // the host architecture.
478 let host_dylib_path
= path
480 .join(&format
!("Host{}", host
))
481 .join(&host
.to_lowercase());
482 let lib_path
= path
.join("lib").join(&target
);
483 let include_path
= path
.join("include");
484 Some((bin_path
, host_dylib_path
, lib_path
, include_path
))
487 fn atl_paths(target
: &str, path
: &Path
) -> Option
<(PathBuf
, PathBuf
)> {
488 let atl_path
= path
.join("atlmfc");
489 let sub
= lib_subdir(target
)?
;
490 if atl_path
.exists() {
491 Some((atl_path
.join("lib").join(sub
), atl_path
.join("include")))
497 // For MSVC 14 we need to find the Universal CRT as well as either
498 // the Windows 10 SDK or Windows 8.1 SDK.
499 pub fn find_msvc_14(tool
: &str, target
: &str) -> Option
<Tool
> {
500 let vcdir
= get_vc_dir("14.0")?
;
501 let mut tool
= get_tool(tool
, &vcdir
, target
)?
;
502 add_sdks(&mut tool
, target
)?
;
503 Some(tool
.into_tool())
506 fn add_sdks(tool
: &mut MsvcTool
, target
: &str) -> Option
<()> {
507 let sub
= lib_subdir(target
)?
;
508 let (ucrt
, ucrt_version
) = get_ucrt_dir()?
;
510 let host
= match host_arch() {
518 .push(ucrt
.join("bin").join(&ucrt_version
).join(host
));
520 let ucrt_include
= ucrt
.join("include").join(&ucrt_version
);
521 tool
.include
.push(ucrt_include
.join("ucrt"));
523 let ucrt_lib
= ucrt
.join("lib").join(&ucrt_version
);
524 tool
.libs
.push(ucrt_lib
.join("ucrt").join(sub
));
526 if let Some((sdk
, version
)) = get_sdk10_dir() {
527 tool
.path
.push(sdk
.join("bin").join(host
));
528 let sdk_lib
= sdk
.join("lib").join(&version
);
529 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
530 let sdk_include
= sdk
.join("include").join(&version
);
531 tool
.include
.push(sdk_include
.join("um"));
532 tool
.include
.push(sdk_include
.join("cppwinrt"));
533 tool
.include
.push(sdk_include
.join("winrt"));
534 tool
.include
.push(sdk_include
.join("shared"));
535 } else if let Some(sdk
) = get_sdk81_dir() {
536 tool
.path
.push(sdk
.join("bin").join(host
));
537 let sdk_lib
= sdk
.join("lib").join("winv6.3");
538 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
539 let sdk_include
= sdk
.join("include");
540 tool
.include
.push(sdk_include
.join("um"));
541 tool
.include
.push(sdk_include
.join("winrt"));
542 tool
.include
.push(sdk_include
.join("shared"));
548 // For MSVC 12 we need to find the Windows 8.1 SDK.
549 pub fn find_msvc_12(tool
: &str, target
: &str) -> Option
<Tool
> {
550 let vcdir
= get_vc_dir("12.0")?
;
551 let mut tool
= get_tool(tool
, &vcdir
, target
)?
;
552 let sub
= lib_subdir(target
)?
;
553 let sdk81
= get_sdk81_dir()?
;
554 tool
.path
.push(sdk81
.join("bin").join(sub
));
555 let sdk_lib
= sdk81
.join("lib").join("winv6.3");
556 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
557 let sdk_include
= sdk81
.join("include");
558 tool
.include
.push(sdk_include
.join("shared"));
559 tool
.include
.push(sdk_include
.join("um"));
560 tool
.include
.push(sdk_include
.join("winrt"));
561 Some(tool
.into_tool())
564 // For MSVC 11 we need to find the Windows 8 SDK.
565 pub fn find_msvc_11(tool
: &str, target
: &str) -> Option
<Tool
> {
566 let vcdir
= get_vc_dir("11.0")?
;
567 let mut tool
= get_tool(tool
, &vcdir
, target
)?
;
568 let sub
= lib_subdir(target
)?
;
569 let sdk8
= get_sdk8_dir()?
;
570 tool
.path
.push(sdk8
.join("bin").join(sub
));
571 let sdk_lib
= sdk8
.join("lib").join("win8");
572 tool
.libs
.push(sdk_lib
.join("um").join(sub
));
573 let sdk_include
= sdk8
.join("include");
574 tool
.include
.push(sdk_include
.join("shared"));
575 tool
.include
.push(sdk_include
.join("um"));
576 tool
.include
.push(sdk_include
.join("winrt"));
577 Some(tool
.into_tool())
580 fn add_env(tool
: &mut Tool
, env
: &str, paths
: Vec
<PathBuf
>) {
581 let prev
= env
::var_os(env
).unwrap_or(OsString
::new());
582 let prev
= env
::split_paths(&prev
);
583 let new
= paths
.into_iter().chain(prev
);
585 .push((env
.to_string().into(), env
::join_paths(new
).unwrap()));
588 // Given a possible MSVC installation directory, we look for the linker and
589 // then add the MSVC library path.
590 fn get_tool(tool
: &str, path
: &Path
, target
: &str) -> Option
<MsvcTool
> {
595 path
.join("bin").join(sub
).join(tool
),
596 path
.join("bin").join(host
),
599 .filter(|&(ref path
, _
)| path
.is_file())
600 .map(|(path
, host
)| {
601 let mut tool
= MsvcTool
::new(path
);
602 tool
.path
.push(host
);
605 .filter_map(|mut tool
| {
606 let sub
= vc_lib_subdir(target
)?
;
607 tool
.libs
.push(path
.join("lib").join(sub
));
608 tool
.include
.push(path
.join("include"));
609 let atlmfc_path
= path
.join("atlmfc");
610 if atlmfc_path
.exists() {
611 tool
.libs
.push(atlmfc_path
.join("lib").join(sub
));
612 tool
.include
.push(atlmfc_path
.join("include"));
619 // To find MSVC we look in a specific registry key for the version we are
621 fn get_vc_dir(ver
: &str) -> Option
<PathBuf
> {
622 let key
= r
"SOFTWARE\Microsoft\VisualStudio\SxS\VC7";
623 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
624 let path
= key
.query_str(ver
).ok()?
;
628 // To find the Universal CRT we look in a specific registry key for where
629 // all the Universal CRTs are located and then sort them asciibetically to
630 // find the newest version. While this sort of sorting isn't ideal, it is
631 // what vcvars does so that's good enough for us.
633 // Returns a pair of (root, version) for the ucrt dir if found
634 fn get_ucrt_dir() -> Option
<(PathBuf
, String
)> {
635 let key
= r
"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
636 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
637 let root
= key
.query_str("KitsRoot10").ok()?
;
638 let readdir
= Path
::new(&root
).join("lib").read_dir().ok()?
;
639 let max_libdir
= readdir
640 .filter_map(|dir
| dir
.ok())
641 .map(|dir
| dir
.path())
645 .and_then(|c
| c
.as_os_str().to_str())
646 .map(|c
| c
.starts_with("10.") && dir
.join("ucrt").is_dir())
650 let version
= max_libdir
.components().last().unwrap();
651 let version
= version
.as_os_str().to_str().unwrap().to_string();
652 Some((root
.into(), version
))
655 // Vcvars finds the correct version of the Windows 10 SDK by looking
656 // for the include `um\Windows.h` because sometimes a given version will
657 // only have UCRT bits without the rest of the SDK. Since we only care about
658 // libraries and not includes, we instead look for `um\x64\kernel32.lib`.
659 // Since the 32-bit and 64-bit libraries are always installed together we
660 // only need to bother checking x64, making this code a tiny bit simpler.
661 // Like we do for the Universal CRT, we sort the possibilities
662 // asciibetically to find the newest one as that is what vcvars does.
663 fn get_sdk10_dir() -> Option
<(PathBuf
, String
)> {
664 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
665 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
666 let root
= key
.query_str("InstallationFolder").ok()?
;
667 let readdir
= Path
::new(&root
).join("lib").read_dir().ok()?
;
668 let mut dirs
= readdir
669 .filter_map(|dir
| dir
.ok())
670 .map(|dir
| dir
.path())
671 .collect
::<Vec
<_
>>();
676 .filter(|dir
| dir
.join("um").join("x64").join("kernel32.lib").is_file())
678 let version
= dir
.components().last().unwrap();
679 let version
= version
.as_os_str().to_str().unwrap().to_string();
680 Some((root
.into(), version
))
683 // Interestingly there are several subdirectories, `win7` `win8` and
684 // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
685 // applies to us. Note that if we were targeting kernel mode drivers
686 // instead of user mode applications, we would care.
687 fn get_sdk81_dir() -> Option
<PathBuf
> {
688 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
689 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
690 let root
= key
.query_str("InstallationFolder").ok()?
;
694 fn get_sdk8_dir() -> Option
<PathBuf
> {
695 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0";
696 let key
= LOCAL_MACHINE
.open(key
.as_ref()).ok()?
;
697 let root
= key
.query_str("InstallationFolder").ok()?
;
701 const PROCESSOR_ARCHITECTURE_INTEL
: u16 = 0;
702 const PROCESSOR_ARCHITECTURE_AMD64
: u16 = 9;
703 const PROCESSOR_ARCHITECTURE_ARM64
: u16 = 12;
704 const X86
: u16 = PROCESSOR_ARCHITECTURE_INTEL
;
705 const X86_64
: u16 = PROCESSOR_ARCHITECTURE_AMD64
;
706 const AARCH64
: u16 = PROCESSOR_ARCHITECTURE_ARM64
;
708 // When choosing the tool to use, we have to choose the one which matches
709 // the target architecture. Otherwise we end up in situations where someone
710 // on 32-bit Windows is trying to cross compile to 64-bit and it tries to
711 // invoke the native 64-bit compiler which won't work.
713 // For the return value of this function, the first member of the tuple is
714 // the folder of the tool we will be invoking, while the second member is
715 // the folder of the host toolchain for that tool which is essential when
716 // using a cross linker. We return a Vec since on x64 there are often two
717 // linkers that can target the architecture we desire. The 64-bit host
718 // linker is preferred, and hence first, due to 64-bit allowing it more
719 // address space to work with and potentially being faster.
720 fn bin_subdir(target
: &str) -> Vec
<(&'
static str, &'
static str)> {
721 let arch
= target
.split('
-'
).next().unwrap();
722 match (arch
, host_arch()) {
723 ("i586", X86
) | ("i686", X86
) => vec
![("", "")],
724 ("i586", X86_64
) | ("i686", X86_64
) => vec
![("amd64_x86", "amd64"), ("", "")],
725 ("x86_64", X86
) => vec
![("x86_amd64", "")],
726 ("x86_64", X86_64
) => vec
![("amd64", "amd64"), ("x86_amd64", "")],
727 ("arm", X86
) | ("thumbv7a", X86
) => vec
![("x86_arm", "")],
728 ("arm", X86_64
) | ("thumbv7a", X86_64
) => vec
![("amd64_arm", "amd64"), ("x86_arm", "")],
733 fn lib_subdir(target
: &str) -> Option
<&'
static str> {
734 let arch
= target
.split('
-'
).next().unwrap();
736 "i586" | "i686" => Some("x86"),
737 "x86_64" => Some("x64"),
738 "arm" | "thumbv7a" => Some("arm"),
739 "aarch64" => Some("arm64"),
744 // MSVC's x86 libraries are not in a subfolder
745 fn vc_lib_subdir(target
: &str) -> Option
<&'
static str> {
746 let arch
= target
.split('
-'
).next().unwrap();
748 "i586" | "i686" => Some(""),
749 "x86_64" => Some("amd64"),
750 "arm" | "thumbv7a" => Some("arm"),
751 "aarch64" => Some("arm64"),
757 fn host_arch() -> u16 {
760 type LPVOID
= *mut u8;
761 type DWORD_PTR
= usize;
765 wProcessorArchitecture
: WORD
,
768 _lpMinimumApplicationAddress
: LPVOID
,
769 _lpMaximumApplicationAddress
: LPVOID
,
770 _dwActiveProcessorMask
: DWORD_PTR
,
771 _dwNumberOfProcessors
: DWORD
,
772 _dwProcessorType
: DWORD
,
773 _dwAllocationGranularity
: DWORD
,
774 _wProcessorLevel
: WORD
,
775 _wProcessorRevision
: WORD
,
779 fn GetNativeSystemInfo(lpSystemInfo
: *mut SYSTEM_INFO
);
783 let mut info
= mem
::zeroed();
784 GetNativeSystemInfo(&mut info
);
785 info
.wProcessorArchitecture
789 // Given a registry key, look at all the sub keys and find the one which has
790 // the maximal numeric value.
792 // Returns the name of the maximal key as well as the opened maximal key.
793 fn max_version(key
: &RegistryKey
) -> Option
<(OsString
, RegistryKey
)> {
794 let mut max_vers
= 0;
795 let mut max_key
= None
;
796 for subkey
in key
.iter().filter_map(|k
| k
.ok()) {
799 .and_then(|s
| s
.trim_left_matches("v").replace(".", "").parse().ok());
800 let val
= match val
{
805 if let Ok(k
) = key
.open(&subkey
) {
807 max_key
= Some((subkey
, k
));
814 pub fn has_msbuild_version(version
: &str) -> bool
{
817 find_msbuild_vs16("x86_64-pc-windows-msvc").is_some()
818 || find_msbuild_vs16("i686-pc-windows-msvc").is_some()
819 || find_msbuild_vs16("aarch64-pc-windows-msvc").is_some()
822 find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
823 || find_msbuild_vs15("i686-pc-windows-msvc").is_some()
824 || find_msbuild_vs15("aarch64-pc-windows-msvc").is_some()
826 "12.0" | "14.0" => LOCAL_MACHINE
827 .open(&OsString
::from(format
!(
828 "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
836 pub fn find_devenv(target
: &str) -> Option
<Tool
> {
837 find_devenv_vs15(&target
)
840 fn find_devenv_vs15(target
: &str) -> Option
<Tool
> {
841 find_tool_in_vs15_path(r
"Common7\IDE\devenv.exe", target
)
844 // see http://stackoverflow.com/questions/328017/path-to-msbuild
845 pub fn find_msbuild(target
: &str) -> Option
<Tool
> {
846 // VS 15 (2017) changed how to locate msbuild
847 if let Some(r
) = find_msbuild_vs16(target
) {
849 } else if let Some(r
) = find_msbuild_vs15(target
) {
852 find_old_msbuild(target
)
856 fn find_msbuild_vs15(target
: &str) -> Option
<Tool
> {
857 find_tool_in_vs15_path(r
"MSBuild\15.0\Bin\MSBuild.exe", target
)
860 fn find_old_msbuild(target
: &str) -> Option
<Tool
> {
861 let key
= r
"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
866 max_version(&key
).and_then(|(_vers
, key
)| key
.query_str("MSBuildToolsPath").ok())
869 let mut path
= PathBuf
::from(path
);
870 path
.push("MSBuild.exe");
871 let mut tool
= Tool
::with_family(path
, MSVC_FAMILY
);
872 if target
.contains("x86_64") {
873 tool
.env
.push(("Platform".into(), "X64".into()));