mirror of https://github.com/lukechilds/cc-rs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
26 KiB
716 lines
26 KiB
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
//! A helper module to probe the Windows Registry when looking for
|
|
//! windows-specific tools.
|
|
|
|
use std::process::Command;
|
|
|
|
use Tool;
|
|
|
|
#[cfg(windows)]
|
|
macro_rules! otry {
|
|
($expr:expr) => (match $expr {
|
|
Some(val) => val,
|
|
None => return None,
|
|
})
|
|
}
|
|
|
|
/// Attempts to find a tool within an MSVC installation using the Windows
|
|
/// registry as a point to search from.
|
|
///
|
|
/// The `target` argument is the target that the tool should work for (e.g.
|
|
/// compile or link for) and the `tool` argument is the tool to find (e.g.
|
|
/// `cl.exe` or `link.exe`).
|
|
///
|
|
/// This function will return `None` if the tool could not be found, or it will
|
|
/// return `Some(cmd)` which represents a command that's ready to execute the
|
|
/// tool with the appropriate environment variables set.
|
|
///
|
|
/// Note that this function always returns `None` for non-MSVC targets.
|
|
pub fn find(target: &str, tool: &str) -> Option<Command> {
|
|
find_tool(target, tool).map(|c| c.to_command())
|
|
}
|
|
|
|
/// Similar to the `find` function above, this function will attempt the same
|
|
/// operation (finding a MSVC tool in a local install) but instead returns a
|
|
/// `Tool` which may be introspected.
|
|
#[cfg(not(windows))]
|
|
pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
|
|
None
|
|
}
|
|
|
|
/// Documented above.
|
|
#[cfg(windows)]
|
|
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
|
|
use std::env;
|
|
|
|
// This logic is all tailored for MSVC, if we're not that then bail out
|
|
// early.
|
|
if !target.contains("msvc") {
|
|
return None;
|
|
}
|
|
|
|
// 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.
|
|
if tool.contains("msbuild") {
|
|
return impl_::find_msbuild(target);
|
|
}
|
|
|
|
if tool.contains("devenv") {
|
|
return impl_::find_devenv(target);
|
|
}
|
|
|
|
// If VCINSTALLDIR is set, then someone's probably already run vcvars and we
|
|
// should just find whatever that indicates.
|
|
if env::var_os("VCINSTALLDIR").is_some() {
|
|
return env::var_os("PATH")
|
|
.and_then(|path| {
|
|
env::split_paths(&path)
|
|
.map(|p| p.join(tool))
|
|
.find(|p| p.exists())
|
|
})
|
|
.map(|path| Tool::new(path.into()));
|
|
}
|
|
|
|
// Ok, if we're here, now comes the fun part of the probing. Default shells
|
|
// or shells like MSYS aren't really configured to execute `cl.exe` and the
|
|
// various compiler tools shipped as part of Visual Studio. Here we try to
|
|
// first find the relevant tool, then we also have to be sure to fill in
|
|
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
|
|
// the tool is actually usable.
|
|
|
|
return impl_::find_msvc_15(tool, target)
|
|
.or_else(|| impl_::find_msvc_14(tool, target))
|
|
.or_else(|| impl_::find_msvc_12(tool, target))
|
|
.or_else(|| impl_::find_msvc_11(tool, target));
|
|
}
|
|
|
|
/// A version of Visual Studio
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
pub enum VsVers {
|
|
/// Visual Studio 12 (2013)
|
|
Vs12,
|
|
/// Visual Studio 14 (2015)
|
|
Vs14,
|
|
/// Visual Studio 15 (2017)
|
|
Vs15,
|
|
/// Visual Studio 16 (2019)
|
|
Vs16,
|
|
|
|
/// Hidden variant that should not be matched on. Callers that want to
|
|
/// handle an enumeration of `VsVers` instances should always have a default
|
|
/// case meaning that it's a VS version they don't understand.
|
|
#[doc(hidden)]
|
|
#[allow(bad_style)]
|
|
__Nonexhaustive_do_not_match_this_or_your_code_will_break,
|
|
}
|
|
|
|
/// Find the most recent installed version of Visual Studio
|
|
///
|
|
/// This is used by the cmake crate to figure out the correct
|
|
/// generator.
|
|
#[cfg(not(windows))]
|
|
pub fn find_vs_version() -> Result<VsVers, String> {
|
|
Err(format!("not windows"))
|
|
}
|
|
|
|
/// Documented above
|
|
#[cfg(windows)]
|
|
pub fn find_vs_version() -> Result<VsVers, String> {
|
|
use std::env;
|
|
|
|
match env::var("VisualStudioVersion") {
|
|
Ok(version) => match &version[..] {
|
|
"16.0" => Ok(VsVers::Vs16),
|
|
"15.0" => Ok(VsVers::Vs15),
|
|
"14.0" => Ok(VsVers::Vs14),
|
|
"12.0" => Ok(VsVers::Vs12),
|
|
vers => Err(format!(
|
|
"\n\n\
|
|
unsupported or unknown VisualStudio version: {}\n\
|
|
if another version is installed consider running \
|
|
the appropriate vcvars script before building this \
|
|
crate\n\
|
|
",
|
|
vers
|
|
)),
|
|
},
|
|
_ => {
|
|
// Check for the presense of a specific registry key
|
|
// that indicates visual studio is installed.
|
|
if impl_::has_msbuild_version("16.0") {
|
|
Ok(VsVers::Vs16)
|
|
} else if impl_::has_msbuild_version("15.0") {
|
|
Ok(VsVers::Vs15)
|
|
} else if impl_::has_msbuild_version("14.0") {
|
|
Ok(VsVers::Vs14)
|
|
} else if impl_::has_msbuild_version("12.0") {
|
|
Ok(VsVers::Vs12)
|
|
} else {
|
|
Err(format!(
|
|
"\n\n\
|
|
couldn't determine visual studio generator\n\
|
|
if VisualStudio is installed, however, consider \
|
|
running the appropriate vcvars script before building \
|
|
this crate\n\
|
|
"
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
mod impl_ {
|
|
use std::env;
|
|
use std::ffi::OsString;
|
|
use std::mem;
|
|
use std::path::{Path, PathBuf};
|
|
use std::fs::File;
|
|
use std::io::Read;
|
|
use registry::{RegistryKey, LOCAL_MACHINE};
|
|
use com;
|
|
use setup_config::{EnumSetupInstances, SetupConfiguration, SetupInstance};
|
|
|
|
use Tool;
|
|
|
|
struct MsvcTool {
|
|
tool: PathBuf,
|
|
libs: Vec<PathBuf>,
|
|
path: Vec<PathBuf>,
|
|
include: Vec<PathBuf>,
|
|
}
|
|
|
|
impl MsvcTool {
|
|
fn new(tool: PathBuf) -> MsvcTool {
|
|
MsvcTool {
|
|
tool: tool,
|
|
libs: Vec::new(),
|
|
path: Vec::new(),
|
|
include: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn into_tool(self) -> Tool {
|
|
let MsvcTool {
|
|
tool,
|
|
libs,
|
|
path,
|
|
include,
|
|
} = self;
|
|
let mut tool = Tool::new(tool.into());
|
|
add_env(&mut tool, "LIB", libs);
|
|
add_env(&mut tool, "PATH", path);
|
|
add_env(&mut tool, "INCLUDE", include);
|
|
tool
|
|
}
|
|
}
|
|
|
|
// In MSVC 15 (2017) MS once again changed the scheme for locating
|
|
// the tooling. Now we must go through some COM interfaces, which
|
|
// is super fun for Rust.
|
|
//
|
|
// Note that much of this logic can be found [online] wrt paths, COM, etc.
|
|
//
|
|
// [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
|
|
fn vs15_instances() -> Option<EnumSetupInstances> {
|
|
otry!(com::initialize().ok());
|
|
|
|
let config = otry!(SetupConfiguration::new().ok());
|
|
config.enum_all_instances().ok()
|
|
}
|
|
|
|
pub fn find_msvc_15(tool: &str, target: &str) -> Option<Tool> {
|
|
let iter = otry!(vs15_instances());
|
|
for instance in iter {
|
|
let instance = otry!(instance.ok());
|
|
let tool = tool_from_vs15_instance(tool, target, &instance);
|
|
if tool.is_some() {
|
|
return tool;
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
// While the paths to Visual Studio 2017's devenv and MSBuild could
|
|
// potentially be retrieved from the registry, finding them via
|
|
// SetupConfiguration has shown to be [more reliable], and is preferred
|
|
// according to Microsoft. To help head off potential regressions though,
|
|
// we keep the registry method as a fallback option.
|
|
//
|
|
// [more reliable]: https://github.com/alexcrichton/cc-rs/pull/331
|
|
fn find_tool_in_vs15_path(tool: &str, target: &str) -> Option<Tool> {
|
|
let mut path = match vs15_instances() {
|
|
Some(instances) => instances
|
|
.filter_map(|instance| {
|
|
instance
|
|
.ok()
|
|
.and_then(|instance| instance.installation_path().ok())
|
|
}).map(|path| PathBuf::from(path).join(tool))
|
|
.find(|ref path| path.is_file()),
|
|
None => None,
|
|
};
|
|
|
|
if path.is_none() {
|
|
let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
|
|
path = LOCAL_MACHINE
|
|
.open(key.as_ref())
|
|
.ok()
|
|
.and_then(|key| key.query_str("15.0").ok())
|
|
.map(|path| PathBuf::from(path).join(tool))
|
|
.filter(|ref path| path.is_file());
|
|
}
|
|
|
|
path.map(|path| {
|
|
let mut tool = Tool::new(path);
|
|
if target.contains("x86_64") {
|
|
tool.env.push(("Platform".into(), "X64".into()));
|
|
}
|
|
tool
|
|
})
|
|
}
|
|
|
|
fn tool_from_vs15_instance(tool: &str, target: &str, instance: &SetupInstance) -> Option<Tool> {
|
|
let (bin_path, host_dylib_path, lib_path, include_path) =
|
|
otry!(vs15_vc_paths(target, instance));
|
|
let tool_path = bin_path.join(tool);
|
|
if !tool_path.exists() {
|
|
return None;
|
|
};
|
|
|
|
let mut tool = MsvcTool::new(tool_path);
|
|
tool.path.push(host_dylib_path);
|
|
tool.libs.push(lib_path);
|
|
tool.include.push(include_path);
|
|
|
|
if let Some((atl_lib_path, atl_include_path)) = atl_paths(target, &bin_path) {
|
|
tool.libs.push(atl_lib_path);
|
|
tool.include.push(atl_include_path);
|
|
}
|
|
|
|
otry!(add_sdks(&mut tool, target));
|
|
|
|
Some(tool.into_tool())
|
|
}
|
|
|
|
fn vs15_vc_paths(
|
|
target: &str,
|
|
instance: &SetupInstance,
|
|
) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf)> {
|
|
let instance_path: PathBuf = otry!(instance.installation_path().ok()).into();
|
|
let version_path =
|
|
instance_path.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
|
|
let mut version_file = otry!(File::open(version_path).ok());
|
|
let mut version = String::new();
|
|
otry!(version_file.read_to_string(&mut version).ok());
|
|
let version = version.trim();
|
|
let host = match host_arch() {
|
|
X86 => "X86",
|
|
X86_64 => "X64",
|
|
_ => return None,
|
|
};
|
|
let target = otry!(lib_subdir(target));
|
|
// The directory layout here is MSVC/bin/Host$host/$target/
|
|
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
|
|
// This is the path to the toolchain for a particular target, running
|
|
// on a given host
|
|
let bin_path = path.join("bin")
|
|
.join(&format!("Host{}", host))
|
|
.join(&target);
|
|
// But! we also need PATH to contain the target directory for the host
|
|
// architecture, because it contains dlls like mspdb140.dll compiled for
|
|
// the host architecture.
|
|
let host_dylib_path = path.join("bin")
|
|
.join(&format!("Host{}", host))
|
|
.join(&host.to_lowercase());
|
|
let lib_path = path.join("lib").join(&target);
|
|
let include_path = path.join("include");
|
|
Some((bin_path, host_dylib_path, lib_path, include_path))
|
|
}
|
|
|
|
fn atl_paths(target: &str, path: &Path) -> Option<(PathBuf, PathBuf)> {
|
|
let atl_path = path.join("atlfmc");
|
|
let sub = otry!(lib_subdir(target));
|
|
if atl_path.exists() {
|
|
Some((atl_path.join("lib").join(sub), atl_path.join("include")))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
// 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<Tool> {
|
|
let vcdir = otry!(get_vc_dir("14.0"));
|
|
let mut tool = otry!(get_tool(tool, &vcdir, target));
|
|
otry!(add_sdks(&mut tool, target));
|
|
Some(tool.into_tool())
|
|
}
|
|
|
|
fn add_sdks(tool: &mut MsvcTool, target: &str) -> Option<()> {
|
|
let sub = otry!(lib_subdir(target));
|
|
let (ucrt, ucrt_version) = otry!(get_ucrt_dir());
|
|
|
|
tool.path
|
|
.push(ucrt.join("bin").join(&ucrt_version).join(sub));
|
|
|
|
let ucrt_include = ucrt.join("include").join(&ucrt_version);
|
|
tool.include.push(ucrt_include.join("ucrt"));
|
|
|
|
let ucrt_lib = ucrt.join("lib").join(&ucrt_version);
|
|
tool.libs.push(ucrt_lib.join("ucrt").join(sub));
|
|
|
|
if let Some((sdk, version)) = get_sdk10_dir() {
|
|
tool.path.push(sdk.join("bin").join(sub));
|
|
let sdk_lib = sdk.join("lib").join(&version);
|
|
tool.libs.push(sdk_lib.join("um").join(sub));
|
|
let sdk_include = sdk.join("include").join(&version);
|
|
tool.include.push(sdk_include.join("um"));
|
|
tool.include.push(sdk_include.join("cppwinrt"));
|
|
tool.include.push(sdk_include.join("winrt"));
|
|
tool.include.push(sdk_include.join("shared"));
|
|
} else if let Some(sdk) = get_sdk81_dir() {
|
|
tool.path.push(sdk.join("bin").join(sub));
|
|
let sdk_lib = sdk.join("lib").join("winv6.3");
|
|
tool.libs.push(sdk_lib.join("um").join(sub));
|
|
let sdk_include = sdk.join("include");
|
|
tool.include.push(sdk_include.join("um"));
|
|
tool.include.push(sdk_include.join("winrt"));
|
|
tool.include.push(sdk_include.join("shared"));
|
|
}
|
|
|
|
Some(())
|
|
}
|
|
|
|
// For MSVC 12 we need to find the Windows 8.1 SDK.
|
|
pub fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
|
|
let vcdir = otry!(get_vc_dir("12.0"));
|
|
let mut tool = otry!(get_tool(tool, &vcdir, target));
|
|
let sub = otry!(lib_subdir(target));
|
|
let sdk81 = otry!(get_sdk81_dir());
|
|
tool.path.push(sdk81.join("bin").join(sub));
|
|
let sdk_lib = sdk81.join("lib").join("winv6.3");
|
|
tool.libs.push(sdk_lib.join("um").join(sub));
|
|
let sdk_include = sdk81.join("include");
|
|
tool.include.push(sdk_include.join("shared"));
|
|
tool.include.push(sdk_include.join("um"));
|
|
tool.include.push(sdk_include.join("winrt"));
|
|
Some(tool.into_tool())
|
|
}
|
|
|
|
// For MSVC 11 we need to find the Windows 8 SDK.
|
|
pub fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
|
|
let vcdir = otry!(get_vc_dir("11.0"));
|
|
let mut tool = otry!(get_tool(tool, &vcdir, target));
|
|
let sub = otry!(lib_subdir(target));
|
|
let sdk8 = otry!(get_sdk8_dir());
|
|
tool.path.push(sdk8.join("bin").join(sub));
|
|
let sdk_lib = sdk8.join("lib").join("win8");
|
|
tool.libs.push(sdk_lib.join("um").join(sub));
|
|
let sdk_include = sdk8.join("include");
|
|
tool.include.push(sdk_include.join("shared"));
|
|
tool.include.push(sdk_include.join("um"));
|
|
tool.include.push(sdk_include.join("winrt"));
|
|
Some(tool.into_tool())
|
|
}
|
|
|
|
fn add_env(tool: &mut Tool, env: &str, paths: Vec<PathBuf>) {
|
|
let prev = env::var_os(env).unwrap_or(OsString::new());
|
|
let prev = env::split_paths(&prev);
|
|
let new = paths.into_iter().chain(prev);
|
|
tool.env
|
|
.push((env.to_string().into(), env::join_paths(new).unwrap()));
|
|
}
|
|
|
|
// 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<MsvcTool> {
|
|
bin_subdir(target)
|
|
.into_iter()
|
|
.map(|(sub, host)| {
|
|
(
|
|
path.join("bin").join(sub).join(tool),
|
|
path.join("bin").join(host),
|
|
)
|
|
})
|
|
.filter(|&(ref path, _)| path.is_file())
|
|
.map(|(path, host)| {
|
|
let mut tool = MsvcTool::new(path);
|
|
tool.path.push(host);
|
|
tool
|
|
})
|
|
.filter_map(|mut tool| {
|
|
let sub = otry!(vc_lib_subdir(target));
|
|
tool.libs.push(path.join("lib").join(sub));
|
|
tool.include.push(path.join("include"));
|
|
let atlmfc_path = path.join("atlmfc");
|
|
if atlmfc_path.exists() {
|
|
tool.libs.push(atlmfc_path.join("lib").join(sub));
|
|
tool.include.push(atlmfc_path.join("include"));
|
|
}
|
|
Some(tool)
|
|
})
|
|
.next()
|
|
}
|
|
|
|
// To find MSVC we look in a specific registry key for the version we are
|
|
// trying to find.
|
|
fn get_vc_dir(ver: &str) -> Option<PathBuf> {
|
|
let key = r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7";
|
|
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
|
|
let path = otry!(key.query_str(ver).ok());
|
|
Some(path.into())
|
|
}
|
|
|
|
// To find the Universal CRT we look in a specific registry key for where
|
|
// all the Universal CRTs are located and then sort them asciibetically to
|
|
// find the newest version. While this sort of sorting isn't ideal, it is
|
|
// what vcvars does so that's good enough for us.
|
|
//
|
|
// Returns a pair of (root, version) for the ucrt dir if found
|
|
fn get_ucrt_dir() -> Option<(PathBuf, String)> {
|
|
let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
|
|
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
|
|
let root = otry!(key.query_str("KitsRoot10").ok());
|
|
let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
|
|
let max_libdir = otry!(
|
|
readdir
|
|
.filter_map(|dir| dir.ok())
|
|
.map(|dir| dir.path())
|
|
.filter(|dir| dir.components()
|
|
.last()
|
|
.and_then(|c| c.as_os_str().to_str())
|
|
.map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir())
|
|
.unwrap_or(false))
|
|
.max()
|
|
);
|
|
let version = max_libdir.components().last().unwrap();
|
|
let version = version.as_os_str().to_str().unwrap().to_string();
|
|
Some((root.into(), version))
|
|
}
|
|
|
|
// Vcvars finds the correct version of the Windows 10 SDK by looking
|
|
// for the include `um\Windows.h` because sometimes a given version will
|
|
// only have UCRT bits without the rest of the SDK. Since we only care about
|
|
// libraries and not includes, we instead look for `um\x64\kernel32.lib`.
|
|
// Since the 32-bit and 64-bit libraries are always installed together we
|
|
// only need to bother checking x64, making this code a tiny bit simpler.
|
|
// Like we do for the Universal CRT, we sort the possibilities
|
|
// asciibetically to find the newest one as that is what vcvars does.
|
|
fn get_sdk10_dir() -> Option<(PathBuf, String)> {
|
|
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
|
|
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
|
|
let root = otry!(key.query_str("InstallationFolder").ok());
|
|
let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
|
|
let mut dirs = readdir
|
|
.filter_map(|dir| dir.ok())
|
|
.map(|dir| dir.path())
|
|
.collect::<Vec<_>>();
|
|
dirs.sort();
|
|
let dir = otry!(
|
|
dirs.into_iter()
|
|
.rev()
|
|
.filter(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file())
|
|
.next()
|
|
);
|
|
let version = dir.components().last().unwrap();
|
|
let version = version.as_os_str().to_str().unwrap().to_string();
|
|
Some((root.into(), version))
|
|
}
|
|
|
|
// Interestingly there are several subdirectories, `win7` `win8` and
|
|
// `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
|
|
// applies to us. Note that if we were targetting kernel mode drivers
|
|
// instead of user mode applications, we would care.
|
|
fn get_sdk81_dir() -> Option<PathBuf> {
|
|
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
|
|
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
|
|
let root = otry!(key.query_str("InstallationFolder").ok());
|
|
Some(root.into())
|
|
}
|
|
|
|
fn get_sdk8_dir() -> Option<PathBuf> {
|
|
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0";
|
|
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
|
|
let root = otry!(key.query_str("InstallationFolder").ok());
|
|
Some(root.into())
|
|
}
|
|
|
|
const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0;
|
|
const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9;
|
|
const X86: u16 = PROCESSOR_ARCHITECTURE_INTEL;
|
|
const X86_64: u16 = PROCESSOR_ARCHITECTURE_AMD64;
|
|
|
|
// When choosing the tool to use, we have to choose the one which matches
|
|
// the target architecture. Otherwise we end up in situations where someone
|
|
// on 32-bit Windows is trying to cross compile to 64-bit and it tries to
|
|
// invoke the native 64-bit compiler which won't work.
|
|
//
|
|
// For the return value of this function, the first member of the tuple is
|
|
// the folder of the tool we will be invoking, while the second member is
|
|
// the folder of the host toolchain for that tool which is essential when
|
|
// using a cross linker. We return a Vec since on x64 there are often two
|
|
// 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()) {
|
|
("i586", X86) | ("i686", X86) => vec![("", "")],
|
|
("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
|
|
("x86_64", X86) => vec![("x86_amd64", "")],
|
|
("x86_64", X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")],
|
|
("arm", X86) | ("thumbv7a", X86) => vec![("x86_arm", "")],
|
|
("arm", X86_64) | ("thumbv7a", X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")],
|
|
_ => vec![],
|
|
}
|
|
}
|
|
|
|
fn lib_subdir(target: &str) -> Option<&'static str> {
|
|
let arch = target.split('-').next().unwrap();
|
|
match arch {
|
|
"i586" | "i686" => Some("x86"),
|
|
"x86_64" => Some("x64"),
|
|
"arm" | "thumbv7a" => Some("arm"),
|
|
"aarch64" => 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 {
|
|
"i586" | "i686" => Some(""),
|
|
"x86_64" => Some("amd64"),
|
|
"arm" | "thumbv7a" => Some("arm"),
|
|
"aarch64" => Some("arm64"),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
#[allow(bad_style)]
|
|
fn host_arch() -> u16 {
|
|
type DWORD = u32;
|
|
type WORD = u16;
|
|
type LPVOID = *mut u8;
|
|
type DWORD_PTR = usize;
|
|
|
|
#[repr(C)]
|
|
struct SYSTEM_INFO {
|
|
wProcessorArchitecture: WORD,
|
|
_wReserved: WORD,
|
|
_dwPageSize: DWORD,
|
|
_lpMinimumApplicationAddress: LPVOID,
|
|
_lpMaximumApplicationAddress: LPVOID,
|
|
_dwActiveProcessorMask: DWORD_PTR,
|
|
_dwNumberOfProcessors: DWORD,
|
|
_dwProcessorType: DWORD,
|
|
_dwAllocationGranularity: DWORD,
|
|
_wProcessorLevel: WORD,
|
|
_wProcessorRevision: WORD,
|
|
}
|
|
|
|
extern "system" {
|
|
fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
|
|
}
|
|
|
|
unsafe {
|
|
let mut info = mem::zeroed();
|
|
GetNativeSystemInfo(&mut info);
|
|
info.wProcessorArchitecture
|
|
}
|
|
}
|
|
|
|
// Given a registry key, look at all the sub keys and find the one which has
|
|
// the maximal numeric value.
|
|
//
|
|
// Returns the name of the maximal key as well as the opened maximal key.
|
|
fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
|
|
let mut max_vers = 0;
|
|
let mut max_key = None;
|
|
for subkey in key.iter().filter_map(|k| k.ok()) {
|
|
let val = subkey
|
|
.to_str()
|
|
.and_then(|s| s.trim_start_matches("v").replace(".", "").parse().ok());
|
|
let val = match val {
|
|
Some(s) => s,
|
|
None => continue,
|
|
};
|
|
if val > max_vers {
|
|
if let Ok(k) = key.open(&subkey) {
|
|
max_vers = val;
|
|
max_key = Some((subkey, k));
|
|
}
|
|
}
|
|
}
|
|
max_key
|
|
}
|
|
|
|
pub fn has_msbuild_version(version: &str) -> bool {
|
|
match version {
|
|
"15.0" => {
|
|
find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
|
|
|| find_msbuild_vs15("i686-pc-windows-msvc").is_some()
|
|
}
|
|
"12.0" | "14.0" => LOCAL_MACHINE
|
|
.open(&OsString::from(format!(
|
|
"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
|
|
version
|
|
)))
|
|
.is_ok(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn find_devenv(target: &str) -> Option<Tool> {
|
|
find_devenv_vs15(&target)
|
|
}
|
|
|
|
fn find_devenv_vs15(target: &str) -> Option<Tool> {
|
|
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<Tool> {
|
|
// VS 15 (2017) changed how to locate msbuild
|
|
if let Some(r) = find_msbuild_vs15(target) {
|
|
return Some(r);
|
|
} else {
|
|
find_old_msbuild(target)
|
|
}
|
|
}
|
|
|
|
fn find_msbuild_vs15(target: &str) -> Option<Tool> {
|
|
find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target)
|
|
}
|
|
|
|
fn find_old_msbuild(target: &str) -> Option<Tool> {
|
|
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
|
|
LOCAL_MACHINE
|
|
.open(key.as_ref())
|
|
.ok()
|
|
.and_then(|key| {
|
|
max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok())
|
|
})
|
|
.map(|path| {
|
|
let mut path = PathBuf::from(path);
|
|
path.push("MSBuild.exe");
|
|
let mut tool = Tool::new(path);
|
|
if target.contains("x86_64") {
|
|
tool.env.push(("Platform".into(), "X64".into()));
|
|
}
|
|
tool
|
|
})
|
|
}
|
|
}
|
|
|