aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/.travis.yml5
-rw-r--r--gcc/Cargo.toml4
-rw-r--r--gcc/src/com.rs125
-rw-r--r--gcc/src/lib.rs108
-rw-r--r--gcc/src/setup_config.rs257
-rw-r--r--gcc/src/winapi.rs214
-rw-r--r--gcc/src/windows_registry.rs267
-rw-r--r--gcc/tests/test.rs25
8 files changed, 939 insertions, 66 deletions
diff --git a/gcc/.travis.yml b/gcc/.travis.yml
index 10d3d13..ff57da5 100644
--- a/gcc/.travis.yml
+++ b/gcc/.travis.yml
@@ -14,8 +14,7 @@ sudo: false
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi
- export TARGET=$ARCH-$OS
- - curl https://static.rust-lang.org/rustup.sh |
- sh -s -- --add-target=$TARGET --disable-sudo -y --prefix=`rustc --print sysroot`
+ - if [ -z "$NO_ADD" ]; then rustup target add $TARGET; fi
before_script:
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
script:
@@ -35,7 +34,7 @@ env:
secure: "CBtqrudgE0PS8x3kTr44jKbC2D4nfnmdYVecooNm0qnER4B4TSvZpZSQoCgKK6k4BYQuOSyFTOwYx6M79w39ZMOgyCP9ytB+tyMWL0/+ZuUQL04yVg4M5vd3oJMkOaXbvG56ncgPyFrseY+FPDg+mXAzvJk/nily37YXjkQj2D0="
matrix:
- - ARCH=x86_64
+ - ARCH=x86_64 NO_ADD=1
- ARCH=i686
notifications:
email:
diff --git a/gcc/Cargo.toml b/gcc/Cargo.toml
index b9ebcd6..45acf07 100644
--- a/gcc/Cargo.toml
+++ b/gcc/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "gcc"
-version = "0.3.45"
+version = "0.3.48"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/gcc-rs"
@@ -18,7 +18,7 @@ travis-ci = { repository = "alexcrichton/gcc-rs" }
appveyor = { repository = "alexcrichton/gcc-rs" }
[dependencies]
-rayon = { version = "0.6", optional = true }
+rayon = { version = "0.7", optional = true }
[features]
parallel = ["rayon"]
diff --git a/gcc/src/com.rs b/gcc/src/com.rs
new file mode 100644
index 0000000..bd8cce7
--- /dev/null
+++ b/gcc/src/com.rs
@@ -0,0 +1,125 @@
+// Copyright © 2017 winapi-rs developers
+// 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.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+use std::ffi::{OsStr, OsString};
+use std::mem::forget;
+use std::ops::Deref;
+use std::os::windows::ffi::{OsStrExt, OsStringExt};
+use std::ptr::null_mut;
+use std::slice::from_raw_parts;
+use winapi::Interface;
+use winapi::BSTR;
+use winapi::CoInitializeEx;
+use winapi::COINIT_MULTITHREADED;
+use winapi::{SysFreeString, SysStringLen};
+use winapi::IUnknown;
+use winapi::{S_OK, S_FALSE, HRESULT};
+
+pub fn initialize() -> Result<(), HRESULT> {
+ let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) };
+ if err != S_OK && err != S_FALSE {
+ // S_FALSE just means COM is already initialized
+ return Err(err);
+ }
+ Ok(())
+}
+
+pub struct ComPtr<T>(*mut T) where T: Interface;
+impl<T> ComPtr<T> where T: Interface {
+ /// Creates a `ComPtr` to wrap a raw pointer.
+ /// It takes ownership over the pointer which means it does __not__ call `AddRef`.
+ /// `T` __must__ be a COM interface that inherits from `IUnknown`.
+ pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
+ assert!(!ptr.is_null());
+ ComPtr(ptr)
+ }
+ /// Casts up the inheritance chain
+ pub fn up<U>(self) -> ComPtr<U> where T: Deref<Target=U>, U: Interface {
+ ComPtr(self.into_raw() as *mut U)
+ }
+ /// Extracts the raw pointer.
+ /// You are now responsible for releasing it yourself.
+ pub fn into_raw(self) -> *mut T {
+ let p = self.0;
+ forget(self);
+ p
+ }
+ /// For internal use only.
+ fn as_unknown(&self) -> &IUnknown {
+ unsafe { &*(self.0 as *mut IUnknown) }
+ }
+ /// Performs QueryInterface fun.
+ pub fn cast<U>(&self) -> Result<ComPtr<U>, i32> where U: Interface {
+ let mut obj = null_mut();
+ let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) };
+ if err < 0 { return Err(err); }
+ Ok(unsafe { ComPtr::from_raw(obj as *mut U) })
+ }
+}
+impl<T> Deref for ComPtr<T> where T: Interface {
+ type Target = T;
+ fn deref(&self) -> &T {
+ unsafe { &*self.0 }
+ }
+}
+impl<T> Clone for ComPtr<T> where T: Interface {
+ fn clone(&self) -> Self {
+ unsafe {
+ self.as_unknown().AddRef();
+ ComPtr::from_raw(self.0)
+ }
+ }
+}
+impl<T> Drop for ComPtr<T> where T: Interface {
+ fn drop(&mut self) {
+ unsafe { self.as_unknown().Release(); }
+ }
+}
+pub struct BStr(BSTR);
+impl BStr {
+ pub unsafe fn from_raw(s: BSTR) -> BStr {
+ BStr(s)
+ }
+ pub fn to_osstring(&self) -> OsString {
+ let len = unsafe { SysStringLen(self.0) };
+ let slice = unsafe { from_raw_parts(self.0, len as usize) };
+ OsStringExt::from_wide(slice)
+ }
+}
+impl Drop for BStr {
+ fn drop(&mut self) {
+ unsafe { SysFreeString(self.0) };
+ }
+}
+
+pub trait ToWide {
+ fn to_wide(&self) -> Vec<u16>;
+ fn to_wide_null(&self) -> Vec<u16>;
+}
+impl<T> ToWide for T where T: AsRef<OsStr> {
+ fn to_wide(&self) -> Vec<u16> {
+ self.as_ref().encode_wide().collect()
+ }
+ fn to_wide_null(&self) -> Vec<u16> {
+ self.as_ref().encode_wide().chain(Some(0)).collect()
+ }
+}
+pub trait FromWide where Self: Sized {
+ fn from_wide(wide: &[u16]) -> Self;
+ fn from_wide_null(wide: &[u16]) -> Self {
+ let len = wide.iter().take_while(|&&c| c != 0).count();
+ Self::from_wide(&wide[..len])
+ }
+}
+impl FromWide for OsString {
+ fn from_wide(wide: &[u16]) -> OsString {
+ OsStringExt::from_wide(wide)
+ }
+}
+
diff --git a/gcc/src/lib.rs b/gcc/src/lib.rs
index 85d7d22..7d19e13 100644
--- a/gcc/src/lib.rs
+++ b/gcc/src/lib.rs
@@ -57,8 +57,18 @@ use std::process::{Command, Stdio, Child};
use std::io::{self, BufReader, BufRead, Read, Write};
use std::thread::{self, JoinHandle};
+// These modules are all glue to support reading the MSVC version from
+// the registry and from COM interfaces
#[cfg(windows)]
mod registry;
+#[cfg(windows)]
+#[macro_use]
+mod winapi;
+#[cfg(windows)]
+mod com;
+#[cfg(windows)]
+mod setup_config;
+
pub mod windows_registry;
/// Extra configuration to pass to gcc.
@@ -81,6 +91,7 @@ pub struct Config {
archiver: Option<PathBuf>,
cargo_metadata: bool,
pic: Option<bool>,
+ static_crt: Option<bool>,
}
/// Configuration used to represent an invocation of a C compiler.
@@ -187,6 +198,7 @@ impl Config {
archiver: None,
cargo_metadata: true,
pic: None,
+ static_crt: None,
}
}
@@ -363,6 +375,14 @@ impl Config {
self
}
+ /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools.
+ ///
+ /// This option defaults to `false`, and affect only msvc targets.
+ pub fn static_crt(&mut self, static_crt: bool) -> &mut Config {
+ self.static_crt = Some(static_crt);
+ self
+ }
+
#[doc(hidden)]
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Config
@@ -434,12 +454,13 @@ impl Config {
let mut cfg = rayon::Configuration::new();
if let Ok(amt) = env::var("NUM_JOBS") {
if let Ok(amt) = amt.parse() {
- cfg = cfg.set_num_threads(amt);
+ cfg = cfg.num_threads(amt);
}
}
drop(rayon::initialize(cfg));
- objs.par_iter().weight_max().for_each(|&(ref src, ref dst)| self.compile_object(src, dst));
+ objs.par_iter().with_max_len(1)
+ .for_each(|&(ref src, ref dst)| self.compile_object(src, dst));
}
#[cfg(not(feature = "parallel"))]
@@ -533,13 +554,22 @@ impl Config {
match cmd.family {
ToolFamily::Msvc => {
cmd.args.push("/nologo".into());
- let features = env::var("CARGO_CFG_TARGET_FEATURE")
+
+ let crt_flag = match self.static_crt {
+ Some(true) => "/MT",
+ Some(false) => "/MD",
+ None => {
+ let features = env::var("CARGO_CFG_TARGET_FEATURE")
.unwrap_or(String::new());
- if features.contains("crt-static") {
- cmd.args.push("/MT".into());
- } else {
- cmd.args.push("/MD".into());
- }
+ if features.contains("crt-static") {
+ "/MT"
+ } else {
+ "/MD"
+ }
+ },
+ };
+ cmd.args.push(crt_flag.into());
+
match &opt_level[..] {
"z" | "s" => cmd.args.push("/Os".into()),
"1" => cmd.args.push("/O1".into()),
@@ -593,7 +623,7 @@ impl Config {
}
// armv7 targets get to use armv7 instructions
- if target.starts_with("armv7-unknown-linux-") {
+ if target.starts_with("armv7-") && target.contains("-linux-") {
cmd.args.push("-march=armv7-a".into());
}
@@ -602,6 +632,7 @@ impl Config {
if target.starts_with("armv7-linux-androideabi") {
cmd.args.push("-march=armv7-a".into());
cmd.args.push("-mfpu=vfpv3-d16".into());
+ cmd.args.push("-mfloat-abi=softfp".into());
}
// For us arm == armv6 by default
@@ -610,6 +641,15 @@ impl Config {
cmd.args.push("-marm".into());
}
+ // We can guarantee some settings for FRC
+ if target.starts_with("arm-frc-") {
+ cmd.args.push("-march=armv7-a".into());
+ cmd.args.push("-mcpu=cortex-a9".into());
+ cmd.args.push("-mfpu=vfpv3".into());
+ cmd.args.push("-mfloat-abi=softfp".into());
+ cmd.args.push("-marm".into());
+ }
+
// Turn codegen down on i586 to avoid some instructions.
if target.starts_with("i586-unknown-linux-") {
cmd.args.push("-march=pentium".into());
@@ -683,7 +723,7 @@ impl Config {
for &(ref key, ref value) in self.definitions.iter() {
let lead = if let ToolFamily::Msvc = cmd.family {"/"} else {"-"};
- if let &Some(ref value) = value {
+ if let Some(ref value) = *value {
cmd.args.push(format!("{}D{}={}", lead, key, value).into());
} else {
cmd.args.push(format!("{}D{}", lead, key).into());
@@ -704,7 +744,7 @@ impl Config {
cmd.arg("/I").arg(directory);
}
for &(ref key, ref value) in self.definitions.iter() {
- if let &Some(ref value) = value {
+ if let Some(ref value) = *value {
cmd.arg(&format!("/D{}={}", key, value));
} else {
cmd.arg(&format!("/D{}", key));
@@ -730,7 +770,7 @@ impl Config {
if target.contains("msvc") {
let mut cmd = match self.archiver {
Some(ref s) => self.cmd(s),
- None => windows_registry::find(&target, "lib.exe").unwrap_or(self.cmd("lib.exe")),
+ None => windows_registry::find(&target, "lib.exe").unwrap_or_else(|| self.cmd("lib.exe")),
};
let mut out = OsString::from("/OUT:");
out.push(dst);
@@ -750,7 +790,6 @@ impl Config {
// if hard-link fails, just copy (ignoring the number of bytes written)
fs::copy(&dst, &lib_dst).map(|_| ())
})
- .ok()
.expect("Copying from {:?} to {:?} failed.");;
} else {
let ar = self.get_ar();
@@ -816,7 +855,7 @@ impl Config {
for &(ref a, ref b) in self.env.iter() {
cmd.env(a, b);
}
- return cmd;
+ cmd
}
fn get_base_compiler(&self) -> Tool {
@@ -846,15 +885,26 @@ impl Config {
for arg in args {
t.args.push(arg.into());
}
- return t;
+ t
})
.or_else(|| {
if target.contains("emscripten") {
- if self.cpp {
- Some(Tool::new(PathBuf::from("em++")))
+ //Windows uses bat file so we have to be a bit more specific
+ let tool = if self.cpp {
+ if cfg!(windows) {
+ "em++.bat"
+ } else {
+ "em++"
+ }
} else {
- Some(Tool::new(PathBuf::from("emcc")))
- }
+ if cfg!(windows) {
+ "emcc.bat"
+ } else {
+ "emcc"
+ }
+ };
+
+ Some(Tool::new(PathBuf::from(tool)))
} else {
None
}
@@ -876,6 +926,7 @@ impl Config {
let prefix = cross_compile.or(match &target[..] {
"aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
"arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
+ "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
"arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
"arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
"arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
@@ -948,7 +999,7 @@ impl Config {
self.get_var(name).ok().map(|tool| {
let whitelist = ["ccache", "distcc"];
for t in whitelist.iter() {
- if tool.starts_with(t) && tool[t.len()..].starts_with(" ") {
+ if tool.starts_with(t) && tool[t.len()..].starts_with(' ') {
return (t.to_string(), vec![tool[t.len()..].trim_left().to_string()]);
}
}
@@ -981,7 +1032,14 @@ impl Config {
if self.get_target().contains("android") {
PathBuf::from(format!("{}-ar", self.get_target().replace("armv7", "arm")))
} else if self.get_target().contains("emscripten") {
- PathBuf::from("emar")
+ //Windows use bat files so we have to be a bit more specific
+ let tool = if cfg!(windows) {
+ "emar.bat"
+ } else {
+ "emar"
+ };
+
+ PathBuf::from(tool)
} else {
PathBuf::from("ar")
}
@@ -1034,7 +1092,7 @@ impl Tool {
let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
if fname.contains("clang") {
ToolFamily::Clang
- } else if fname.contains("cl") {
+ } else if fname.contains("cl") && !fname.contains("uclibc") {
ToolFamily::Msvc
} else {
ToolFamily::Gnu
@@ -1091,7 +1149,7 @@ fn run(cmd: &mut Command, program: &str) {
let (mut child, print) = spawn(cmd, program);
let status = child.wait().expect("failed to wait on child process");
print.join().unwrap();
- println!("{:?}", status);
+ println!("{}", status);
if !status.success() {
fail(&format!("command did not execute successfully, got: {}", status));
}
@@ -1104,11 +1162,11 @@ fn run_output(cmd: &mut Command, program: &str) -> Vec<u8> {
child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap();
let status = child.wait().expect("failed to wait on child process");
print.join().unwrap();
- println!("{:?}", status);
+ println!("{}", status);
if !status.success() {
fail(&format!("command did not execute successfully, got: {}", status));
}
- return stdout
+ stdout
}
fn spawn(cmd: &mut Command, program: &str) -> (Child, JoinHandle<()>) {
diff --git a/gcc/src/setup_config.rs b/gcc/src/setup_config.rs
new file mode 100644
index 0000000..175b7f1
--- /dev/null
+++ b/gcc/src/setup_config.rs
@@ -0,0 +1,257 @@
+// Copyright © 2017 winapi-rs developers
+// 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.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(bad_style)]
+#![allow(unused)]
+
+use std::ffi::OsString;
+use std::ptr::null_mut;
+use winapi::Interface;
+use winapi::{LPFILETIME, ULONG};
+use winapi::S_FALSE;
+use winapi::BSTR;
+use winapi::LPCOLESTR;
+use winapi::{CLSCTX_ALL, CoCreateInstance};
+use winapi::LPSAFEARRAY;
+use winapi::{IUnknown, IUnknownVtbl};
+use winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG};
+
+use com::{BStr, ComPtr};
+
+// Bindings to the Setup.Configuration stuff
+pub type InstanceState = u32;
+
+pub const eNone: InstanceState = 0;
+pub const eLocal: InstanceState = 1;
+pub const eRegistered: InstanceState = 2;
+pub const eNoRebootRequired: InstanceState = 4;
+pub const eComplete: InstanceState = -1i32 as u32;
+
+RIDL!{#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)]
+interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) {
+ fn GetInstanceId(
+ pbstrInstanceId: *mut BSTR,
+ ) -> HRESULT,
+ fn GetInstallDate(
+ pInstallDate: LPFILETIME,
+ ) -> HRESULT,
+ fn GetInstallationName(
+ pbstrInstallationName: *mut BSTR,
+ ) -> HRESULT,
+ fn GetInstallationPath(
+ pbstrInstallationPath: *mut BSTR,
+ ) -> HRESULT,
+ fn GetInstallationVersion(
+ pbstrInstallationVersion: *mut BSTR,
+ ) -> HRESULT,
+ fn GetDisplayName(
+ lcid: LCID,
+ pbstrDisplayName: *mut BSTR,
+ ) -> HRESULT,
+ fn GetDescription(
+ lcid: LCID,
+ pbstrDescription: *mut BSTR,
+ ) -> HRESULT,
+ fn ResolvePath(
+ pwszRelativePath: LPCOLESTR,
+ pbstrAbsolutePath: *mut BSTR,
+ ) -> HRESULT,
+}}
+
+RIDL!{#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)]
+interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtbl) {
+ fn GetState(
+ pState: *mut InstanceState,
+ ) -> HRESULT,
+ fn GetPackages(
+ ppsaPackages: *mut LPSAFEARRAY,
+ ) -> HRESULT,
+ fn GetProduct(
+ ppPackage: *mut *mut ISetupPackageReference,
+ ) -> HRESULT,
+ fn GetProductPath(
+ pbstrProductPath: *mut BSTR,
+ ) -> HRESULT,
+}}
+
+RIDL!{#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)]
+interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) {
+ fn Next(
+ celt: ULONG,
+ rgelt: *mut *mut ISetupInstance,
+ pceltFetched: *mut ULONG,
+ ) -> HRESULT,
+ fn Skip(
+ celt: ULONG,
+ ) -> HRESULT,
+ fn Reset() -> HRESULT,
+ fn Clone(
+ ppenum: *mut *mut IEnumSetupInstances,
+ ) -> HRESULT,
+}}
+
+RIDL!{#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)]
+interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) {
+ fn EnumInstances(
+ ppEnumInstances: *mut *mut IEnumSetupInstances,
+ ) -> HRESULT,
+ fn GetInstanceForCurrentProcess(
+ ppInstance: *mut *mut ISetupInstance,
+ ) -> HRESULT,
+ fn GetInstanceForPath(
+ wzPath: LPCWSTR,
+ ppInstance: *mut *mut ISetupInstance,
+ ) -> HRESULT,
+}}
+
+RIDL!{#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)]
+interface ISetupConfiguration2(ISetupConfiguration2Vtbl):
+ ISetupConfiguration(ISetupConfigurationVtbl) {
+ fn EnumAllInstances(
+ ppEnumInstances: *mut *mut IEnumSetupInstances,
+ ) -> HRESULT,
+}}
+
+RIDL!{#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)]
+interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownVtbl) {
+ fn GetId(
+ pbstrId: *mut BSTR,
+ ) -> HRESULT,
+ fn GetVersion(
+ pbstrVersion: *mut BSTR,
+ ) -> HRESULT,
+ fn GetChip(
+ pbstrChip: *mut BSTR,
+ ) -> HRESULT,
+ fn GetLanguage(
+ pbstrLanguage: *mut BSTR,
+ ) -> HRESULT,
+ fn GetBranch(
+ pbstrBranch: *mut BSTR,
+ ) -> HRESULT,
+ fn GetType(
+ pbstrType: *mut BSTR,
+ ) -> HRESULT,
+ fn GetUniqueId(
+ pbstrUniqueId: *mut BSTR,
+ ) -> HRESULT,
+}}
+
+RIDL!{#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)]
+interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {
+ fn ParseVersion(
+ pwszVersion: LPCOLESTR,
+ pullVersion: PULONGLONG,
+ ) -> HRESULT,
+ fn ParseVersionRange(
+ pwszVersionRange: LPCOLESTR,
+ pullMinVersion: PULONGLONG,
+ pullMaxVersion: PULONGLONG,
+ ) -> HRESULT,
+}}
+
+DEFINE_GUID!{CLSID_SetupConfiguration,
+ 0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d}
+
+// Safe wrapper around the COM interfaces
+pub struct SetupConfiguration(ComPtr<ISetupConfiguration>);
+
+impl SetupConfiguration {
+ pub fn new() -> Result<SetupConfiguration, i32> {
+ let mut obj = null_mut();
+ let err = unsafe { CoCreateInstance(
+ &CLSID_SetupConfiguration, null_mut(), CLSCTX_ALL,
+ &ISetupConfiguration::uuidof(), &mut obj,
+ ) };
+ if err < 0 { return Err(err); }
+ let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) };
+ Ok(SetupConfiguration(obj))
+ }
+ pub fn get_instance_for_current_process(&self) -> Result<SetupInstance, i32> {
+ let mut obj = null_mut();
+ let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) };
+ if err < 0 { return Err(err); }
+ Ok(unsafe { SetupInstance::from_raw(obj) })
+ }
+ pub fn enum_instances(&self) -> Result<EnumSetupInstances, i32> {
+ let mut obj = null_mut();
+ let err = unsafe { self.0.EnumInstances(&mut obj) };
+ if err < 0 { return Err(err); }
+ Ok(unsafe { EnumSetupInstances::from_raw(obj) })
+ }
+ pub fn enum_all_instances(&self) -> Result<EnumSetupInstances, i32> {
+ let mut obj = null_mut();
+ let this = try!(self.0.cast::<ISetupConfiguration2>());
+ let err = unsafe { this.EnumAllInstances(&mut obj) };
+ if err < 0 { return Err(err); }
+ Ok(unsafe { EnumSetupInstances::from_raw(obj) })
+ }
+}
+
+pub struct SetupInstance(ComPtr<ISetupInstance>);
+
+impl SetupInstance {
+ pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance {
+ SetupInstance(ComPtr::from_raw(obj))
+ }
+ pub fn instance_id(&self) -> Result<OsString, i32> {
+ let mut s = null_mut();
+ let err = unsafe { self.0.GetInstanceId(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 { return Err(err); }
+ Ok(bstr.to_osstring())
+ }
+ pub fn installation_name(&self) -> Result<OsString, i32> {
+ let mut s = null_mut();
+ let err = unsafe { self.0.GetInstallationName(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 { return Err(err); }
+ Ok(bstr.to_osstring())
+ }
+ pub fn installation_path(&self) -> Result<OsString, i32> {
+ let mut s = null_mut();
+ let err = unsafe { self.0.GetInstallationPath(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 { return Err(err); }
+ Ok(bstr.to_osstring())
+ }
+ pub fn installation_version(&self) -> Result<OsString, i32> {
+ let mut s = null_mut();
+ let err = unsafe { self.0.GetInstallationVersion(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 { return Err(err); }
+ Ok(bstr.to_osstring())
+ }
+ pub fn product_path(&self) -> Result<OsString, i32> {
+ let mut s = null_mut();
+ let this = try!(self.0.cast::<ISetupInstance2>());
+ let err = unsafe { this.GetProductPath(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 { return Err(err); }
+ Ok(bstr.to_osstring())
+ }
+}
+
+pub struct EnumSetupInstances(ComPtr<IEnumSetupInstances>);
+
+impl EnumSetupInstances {
+ pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances {
+ EnumSetupInstances(ComPtr::from_raw(obj))
+ }
+}
+
+impl Iterator for EnumSetupInstances {
+ type Item = Result<SetupInstance, i32>;
+ fn next(&mut self) -> Option<Result<SetupInstance, i32>> {
+ let mut obj = null_mut();
+ let err = unsafe { self.0.Next(1, &mut obj, null_mut()) };
+ if err < 0 { return Some(Err(err)); }
+ if err == S_FALSE { return None; }
+ Some(Ok(unsafe { SetupInstance::from_raw(obj) }))
+ }
+}
+
diff --git a/gcc/src/winapi.rs b/gcc/src/winapi.rs
new file mode 100644
index 0000000..010d165
--- /dev/null
+++ b/gcc/src/winapi.rs
@@ -0,0 +1,214 @@
+// Copyright © 2015-2017 winapi-rs developers
+// 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.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(bad_style)]
+
+use std::os::raw;
+
+pub type wchar_t = u16;
+
+pub type UINT = raw::c_uint;
+pub type LPUNKNOWN = *mut IUnknown;
+pub type REFIID = *const IID;
+pub type IID = GUID;
+pub type REFCLSID = *const IID;
+pub type PVOID = *mut raw::c_void;
+pub type USHORT = raw::c_ushort;
+pub type ULONG = raw::c_ulong;
+pub type LONG = raw::c_long;
+pub type DWORD = u32;
+pub type LPVOID = *mut raw::c_void;
+pub type HRESULT = raw::c_long;
+pub type LPFILETIME = *mut FILETIME;
+pub type BSTR = *mut OLECHAR;
+pub type OLECHAR = WCHAR;
+pub type WCHAR = wchar_t;
+pub type LPCOLESTR = *const OLECHAR;
+pub type LCID = DWORD;
+pub type LPCWSTR = *const WCHAR;
+pub type PULONGLONG = *mut ULONGLONG;
+pub type ULONGLONG = u64;
+
+pub const S_OK: HRESULT = 0;
+pub const S_FALSE: HRESULT = 1;
+pub const COINIT_MULTITHREADED: u32 = 0x0;
+
+pub type CLSCTX = u32;
+
+pub const CLSCTX_INPROC_SERVER: CLSCTX = 0x1;
+pub const CLSCTX_INPROC_HANDLER: CLSCTX = 0x2;
+pub const CLSCTX_LOCAL_SERVER: CLSCTX = 0x4;
+pub const CLSCTX_REMOTE_SERVER: CLSCTX = 0x10;
+
+pub const CLSCTX_ALL: CLSCTX = CLSCTX_INPROC_SERVER |
+ CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct GUID {
+ pub Data1: raw::c_ulong,
+ pub Data2: raw::c_ushort,
+ pub Data3: raw::c_ushort,
+ pub Data4: [raw::c_uchar; 8],
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct FILETIME {
+ pub dwLowDateTime: DWORD,
+ pub dwHighDateTime: DWORD,
+}
+
+pub trait Interface {
+ fn uuidof() -> GUID;
+}
+
+#[link(name = "Ole32")]
+#[link(name = "OleAut32")]
+extern { }
+
+extern "system" {
+ pub fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) -> HRESULT;
+ pub fn CoCreateInstance(rclsid: REFCLSID, pUnkOuter: LPUNKNOWN,
+ dwClsContext: DWORD, riid: REFIID,
+ ppv: *mut LPVOID) -> HRESULT;
+ pub fn SysFreeString(bstrString: BSTR);
+ pub fn SysStringLen(pbstr: BSTR) -> UINT;
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct SAFEARRAYBOUND {
+ pub cElements: ULONG,
+ pub lLbound: LONG,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct SAFEARRAY {
+ pub cDims: USHORT,
+ pub fFeatures: USHORT,
+ pub cbElements: ULONG,
+ pub cLocks: ULONG,
+ pub pvData: PVOID,
+ pub rgsabound: [SAFEARRAYBOUND; 1],
+}
+
+pub type LPSAFEARRAY = *mut SAFEARRAY;
+
+macro_rules! DEFINE_GUID {
+ (
+ $name:ident, $l:expr, $w1:expr, $w2:expr,
+ $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
+ ) => {
+ pub const $name: $crate::winapi::GUID = $crate::winapi::GUID {
+ Data1: $l,
+ Data2: $w1,
+ Data3: $w2,
+ Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
+ };
+ }
+}
+
+macro_rules! RIDL {
+ (#[uuid($($uuid:expr),+)]
+ interface $interface:ident ($vtbl:ident) {$(
+ fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
+ )+}) => (
+ #[repr(C)]
+ pub struct $vtbl {
+ $(pub $method: unsafe extern "system" fn(
+ This: *mut $interface,
+ $($p: $t),*
+ ) -> $rtr,)+
+ }
+ #[repr(C)]
+ pub struct $interface {
+ pub lpVtbl: *const $vtbl,
+ }
+ RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
+ RIDL!{@uuid $interface $($uuid),+}
+ );
+ (#[uuid($($uuid:expr),+)]
+ interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {
+ }) => (
+ #[repr(C)]
+ pub struct $vtbl {
+ pub parent: $pvtbl,
+ }
+ #[repr(C)]
+ pub struct $interface {
+ pub lpVtbl: *const $vtbl,
+ }
+ RIDL!{@deref $interface $pinterface}
+ RIDL!{@uuid $interface $($uuid),+}
+ );
+ (#[uuid($($uuid:expr),+)]
+ interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {$(
+ fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
+ )+}) => (
+ #[repr(C)]
+ pub struct $vtbl {
+ pub parent: $pvtbl,
+ $(pub $method: unsafe extern "system" fn(
+ This: *mut $interface,
+ $($p: $t,)*
+ ) -> $rtr,)+
+ }
+ #[repr(C)]
+ pub struct $interface {
+ pub lpVtbl: *const $vtbl,
+ }
+ RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
+ RIDL!{@deref $interface $pinterface}
+ RIDL!{@uuid $interface $($uuid),+}
+ );
+ (@deref $interface:ident $pinterface:ident) => (
+ impl ::std::ops::Deref for $interface {
+ type Target = $pinterface;
+ #[inline]
+ fn deref(&self) -> &$pinterface {
+ unsafe { &*(self as *const $interface as *const $pinterface) }
+ }
+ }
+ );
+ (@impl $interface:ident {$(
+ fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
+ )+}) => (
+ impl $interface {
+ $(#[inline] pub unsafe fn $method(&self, $($p: $t,)*) -> $rtr {
+ ((*self.lpVtbl).$method)(self as *const _ as *mut _, $($p,)*)
+ })+
+ }
+ );
+ (@uuid $interface:ident
+ $l:expr, $w1:expr, $w2:expr,
+ $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
+ ) => (
+ impl $crate::winapi::Interface for $interface {
+ #[inline]
+ fn uuidof() -> $crate::winapi::GUID {
+ $crate::winapi::GUID {
+ Data1: $l,
+ Data2: $w1,
+ Data3: $w2,
+ Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
+ }
+ }
+ }
+ );
+}
+
+RIDL!{#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)]
+interface IUnknown(IUnknownVtbl) {
+ fn QueryInterface(
+ riid: REFIID,
+ ppvObject: *mut *mut raw::c_void,
+ ) -> HRESULT,
+ fn AddRef() -> ULONG,
+ fn Release() -> ULONG,
+}}
diff --git a/gcc/src/windows_registry.rs b/gcc/src/windows_registry.rs
index 08b1df5..d05fc1d 100644
--- a/gcc/src/windows_registry.rs
+++ b/gcc/src/windows_registry.rs
@@ -15,6 +15,7 @@ use std::process::Command;
use Tool;
+#[cfg(windows)]
macro_rules! otry {
($expr:expr) => (match $expr {
Some(val) => val,
@@ -50,10 +51,119 @@ pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
#[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 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
+pub enum VsVers {
+ /// Visual Studio 12 (2013)
+ Vs12,
+ /// Visual Studio 14 (2015)
+ Vs14,
+ /// Visual Studio 15 (2017)
+ Vs15,
+
+ /// 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)]
+ __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[..] {
+ "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("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::{SetupConfiguration, SetupInstance};
+
+ use Tool;
struct MsvcTool {
tool: PathBuf,
@@ -82,47 +192,91 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
}
}
- // This logic is all tailored for MSVC, if we're not that then bail out
- // early.
- if !target.contains("msvc") {
- return None;
- }
+ // 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.
+ pub fn find_msvc_15(tool: &str, target: &str) -> Option<Tool> {
+ otry!(com::initialize().ok());
+
+ let config = otry!(SetupConfiguration::new().ok());
+ let iter = otry!(config.enum_all_instances().ok());
+ for instance in iter {
+ let instance = otry!(instance.ok());
+ let tool = tool_from_vs15_instance(tool, target, &instance);
+ if tool.is_some() {
+ return tool;
+ }
+ }
- // 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 find_msbuild(target);
+ None
}
- // 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()));
+ fn tool_from_vs15_instance(tool: &str, target: &str,
+ instance: &SetupInstance) -> Option<Tool> {
+ let (bin_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(bin_path.clone());
+ 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())
}
- // 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.
+ fn vs15_vc_paths(target: &str, instance: &SetupInstance) -> Option<(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));
+ let path = instance_path.join(r"VC\Tools\MSVC").join(version);
+ let bin_path = path.join("bin").join(&format!("Host{}", host)).join(&target);
+ let lib_path = path.join("lib").join(&target);
+ let include_path = path.join("include");
+ Some((bin_path, lib_path, include_path))
+ }
- return find_msvc_latest(tool, target, "15.0")
- .or_else(|| find_msvc_latest(tool, target, "14.0"))
- .or_else(|| find_msvc_12(tool, target))
- .or_else(|| find_msvc_11(tool, target));
+ 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 or newer we need to find the Universal CRT as well as either
+ // For MSVC 14 we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
- fn find_msvc_latest(tool: &str, target: &str, ver: &str) -> Option<Tool> {
- let vcdir = otry!(get_vc_dir(ver));
+ 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"));
@@ -145,14 +299,13 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
tool.include.push(sdk_include.join("um"));
tool.include.push(sdk_include.join("winrt"));
tool.include.push(sdk_include.join("shared"));
- } else {
- return None;
}
- Some(tool.into_tool())
+
+ Some(())
}
// For MSVC 12 we need to find the Windows 8.1 SDK.
- fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
+ 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));
@@ -168,7 +321,7 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
}
// For MSVC 11 we need to find the Windows 8 SDK.
- fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
+ 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));
@@ -403,8 +556,52 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
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
+ }
+ }
+
// see http://stackoverflow.com/questions/328017/path-to-msbuild
- fn find_msbuild(target: &str) -> Option<Tool> {
+ 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> {
+ // Seems like this could also go through SetupConfiguration,
+ // or that find_msvc_15 could just use this registry key
+ // instead of the COM interface.
+ let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
+ LOCAL_MACHINE.open(key.as_ref())
+ .ok()
+ .and_then(|key| {
+ key.query_str("15.0").ok()
+ })
+ .map(|path| {
+ let path = PathBuf::from(path).join(r"MSBuild\15.0\Bin\MSBuild.exe");
+ let mut tool = Tool::new(path);
+ if target.contains("x86_64") {
+ tool.env.push(("Platform".into(), "X64".into()));
+ }
+ tool
+ })
+ }
+
+ fn find_old_msbuild(target: &str) -> Option<Tool> {
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
LOCAL_MACHINE.open(key.as_ref())
.ok()
diff --git a/gcc/tests/test.rs b/gcc/tests/test.rs
index 1c51e09..8fda3ed 100644
--- a/gcc/tests/test.rs
+++ b/gcc/tests/test.rs
@@ -180,7 +180,8 @@ fn msvc_smoke() {
.must_have("/O2")
.must_have("foo.c")
.must_not_have("/Z7")
- .must_have("/c");
+ .must_have("/c")
+ .must_have("/MD");
test.cmd(1).must_have(test.td.path().join("foo.o"));
}
@@ -227,3 +228,25 @@ fn msvc_define() {
test.cmd(0).must_have("/DFOO=bar").must_have("/DBAR");
}
+
+#[test]
+fn msvc_static_crt() {
+ let test = Test::msvc();
+ test.gcc()
+ .static_crt(true)
+ .file("foo.c")
+ .compile("libfoo.a");
+
+ test.cmd(0).must_have("/MT");
+}
+
+#[test]
+fn msvc_no_static_crt() {
+ let test = Test::msvc();
+ test.gcc()
+ .static_crt(false)
+ .file("foo.c")
+ .compile("libfoo.a");
+
+ test.cmd(0).must_have("/MD");
+}