use std::process::Command;
use Tool;
pub fn find(target: &str, tool: &str) -> Option<Command> {
find_tool(target, tool).map(|c| c.to_command())
}
#[cfg(not(windows))]
pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
None
}
#[cfg(windows)]
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
use std::env;
use std::ffi::OsString;
use std::io;
use std::fs;
use std::path::{Path, PathBuf};
use registry::{RegistryKey, LOCAL_MACHINE};
if !target.contains("msvc") { return None }
if tool.contains("msbuild") {
return find_msbuild(target)
}
let extra = if target.starts_with("i686") {
""
} else if target.starts_with("x86_64") {
"amd64"
} else if target.starts_with("arm") {
"arm"
} else {
return None
};
let vs_install_dir = get_vs_install_dir();
let mut path_to_add = None;
let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| {
let mut p = PathBuf::from(dir);
p.push("bin");
p.push(extra);
let tool = p.join(tool);
if fs::metadata(&tool).is_ok() {
path_to_add = Some(p);
Some(tool)
} else {
None
}
}).or_else(|| {
env::var_os("PATH").and_then(|path| {
env::split_paths(&path).map(|p| p.join(tool)).find(|path| {
fs::metadata(path).is_ok()
})
})
}).or_else(|| {
vs_install_dir.as_ref().and_then(|p| {
let mut p = p.join("VC/bin");
p.push(extra);
let tool = p.join(tool);
if fs::metadata(&tool).is_ok() {
path_to_add = Some(p);
Some(tool)
} else {
None
}
})
}).map(|tool| {
Tool::new(tool.into())
}).unwrap_or_else(|| {
Tool::new(tool.into())
});
let mut paths = Vec::new();
if let Some(path) = path_to_add {
paths.push(path);
if let Some(root) = get_windows_sdk_bin_path(target) {
paths.push(root);
}
}
if let Some(path) = env::var_os("PATH") {
paths.extend(env::split_paths(&path));
}
cmd.env.push(("PATH".into(), env::join_paths(&paths).unwrap().into()));
if env::var_os("INCLUDE").is_none() {
let mut includes = Vec::new();
if let Some(ref vs_install_dir) = vs_install_dir {
includes.push(vs_install_dir.join("VC/include"));
if let Some((ucrt_root, vers)) = ucrt_install_dir(vs_install_dir) {
let include = ucrt_root.join("Include").join(vers);
includes.push(include.join("ucrt"));
includes.push(include.join("um"));
includes.push(include.join("winrt"));
includes.push(include.join("shared"));
}
}
if let Some((path, major)) = get_windows_sdk_path() {
if major >= 8 {
includes.push(path.join("include/shared"));
includes.push(path.join("include/um"));
includes.push(path.join("include/winrt"));
} else {
includes.push(path.join("include"));
}
} else if let Some(ref vs_install_dir) = vs_install_dir {
includes.push(vs_install_dir.clone());
}
cmd.env.push(("INCLUDE".into(),
env::join_paths(&includes).unwrap().into()));
}
if env::var_os("LIB").is_none() {
let mut libs = Vec::new();
if let Some(ref vs_install_dir) = vs_install_dir {
libs.push(vs_install_dir.join("VC/lib").join(extra));
if let Some((ucrt_root, vers)) = ucrt_install_dir(vs_install_dir) {
if let Some(arch) = windows_sdk_v8_subdir(target) {
let lib = ucrt_root.join("Lib").join(vers);
libs.push(lib.join("ucrt").join(arch));
libs.push(lib.join("um").join(arch));
}
}
}
if let Some(path) = get_windows_sdk_lib_path(target) {
libs.push(path);
}
cmd.env.push(("LIB".into(), env::join_paths(&libs).unwrap().into()));
}
return Some(cmd);
fn get_vs_install_dir() -> Option<PathBuf> {
LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| {
LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref())
}).ok().and_then(|key| {
max_version(&key).and_then(|(_vers, key)| {
key.query_str("InstallDir").ok()
})
}).or_else(|| {
env::var_os("VS120COMNTOOLS")
}).or_else(|| {
env::var_os("VS100COMNTOOLS")
}).or_else(|| {
env::var_os("VS90COMNTOOLS")
}).or_else(|| {
env::var_os("VS80COMNTOOLS")
}).map(PathBuf::from).and_then(|mut dir| {
if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") {
dir.pop();
dir.pop();
Some(dir)
} else {
None
}
})
}
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_left_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));
}
}
}
return max_key
}
fn get_windows_sdk_path() -> Option<(PathBuf, usize)> {
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
let key = LOCAL_MACHINE.open(key.as_ref());
let (n, k) = match key.ok().as_ref().and_then(max_version) {
Some(p) => p,
None => return None,
};
let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, ".");
let major = parts.next().unwrap().parse::<usize>().unwrap();
let _minor = parts.next().unwrap().parse::<usize>().unwrap();
k.query_str("InstallationFolder").ok().map(|p| {
(PathBuf::from(p), major)
})
}
fn get_windows_sdk_lib_path(target: &str) -> Option<PathBuf> {
let (mut root, major) = match get_windows_sdk_path() {
Some(pair) => pair,
None => return None,
};
root.push("Lib");
if major <= 7 {
if target.starts_with("i686") {
Some(root)
} else if target.starts_with("x86_64") {
Some(root.join("x64"))
} else {
None
}
} else {
let extra = match windows_sdk_v8_subdir(target) {
Some(extra) => extra,
None => return None,
};
["winv6.3", "win8", "win7"].iter().map(|p| root.join(p)).find(|part| {
fs::metadata(part).is_ok()
}).map(|path| {
path.join("um").join(extra)
})
}
}
fn get_windows_sdk_bin_path(target: &str) -> Option<PathBuf> {
let (mut root, major) = match get_windows_sdk_path() {
Some(pair) => pair,
None => return None,
};
root.push("bin");
if major <= 7 {
None } else {
root.push(match windows_sdk_v8_subdir(target) {
Some(extra) => extra,
None => return None,
});
if fs::metadata(&root).is_ok() {Some(root)} else {None}
}
}
fn windows_sdk_v8_subdir(target: &str) -> Option<&'static str> {
if target.starts_with("i686") {
Some("x86")
} else if target.starts_with("x86_64") {
Some("x64")
} else if target.starts_with("arm") {
Some("arm")
} else {
None
}
}
fn ucrt_install_dir(vs_install_dir: &Path) -> Option<(PathBuf, String)> {
let is_vs_14 = vs_install_dir.iter().filter_map(|p| p.to_str()).any(|s| {
s == "Microsoft Visual Studio 14.0"
});
if !is_vs_14 {
return None
}
let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
let sdk_dir = LOCAL_MACHINE.open(key.as_ref()).and_then(|p| {
p.query_str("KitsRoot10")
}).map(PathBuf::from);
let sdk_dir = match sdk_dir {
Ok(p) => p,
Err(..) => return None,
};
(move || -> io::Result<_> {
let mut max = None;
let mut max_s = None;
for entry in try!(fs::read_dir(&sdk_dir.join("Lib"))) {
let entry = try!(entry);
if let Ok(s) = entry.file_name().into_string() {
if let Ok(u) = s.replace(".", "").parse::<usize>() {
if Some(u) > max {
max = Some(u);
max_s = Some(s);
}
}
}
}
Ok(max_s.map(|m| (sdk_dir, m)))
})().ok().and_then(|x| x)
}
fn find_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
})
}
}