diff --git a/src/com.rs b/src/com.rs new file mode 100644 index 000000000..bd8cce714 --- /dev/null +++ b/src/com.rs @@ -0,0 +1,125 @@ +// Copyright © 2017 winapi-rs developers +// Licensed under the Apache License, Version 2.0 +// or the MIT license +// , 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(*mut T) where T: Interface; +impl ComPtr 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 { + assert!(!ptr.is_null()); + ComPtr(ptr) + } + /// Casts up the inheritance chain + pub fn up(self) -> ComPtr where T: Deref, 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(&self) -> Result, 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 Deref for ComPtr where T: Interface { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.0 } + } +} +impl Clone for ComPtr where T: Interface { + fn clone(&self) -> Self { + unsafe { + self.as_unknown().AddRef(); + ComPtr::from_raw(self.0) + } + } +} +impl Drop for ComPtr 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; + fn to_wide_null(&self) -> Vec; +} +impl ToWide for T where T: AsRef { + fn to_wide(&self) -> Vec { + self.as_ref().encode_wide().collect() + } + fn to_wide_null(&self) -> Vec { + 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/src/lib.rs b/src/lib.rs index 79b926d91..7d19e135e 100644 --- a/src/lib.rs +++ b/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. diff --git a/src/setup_config.rs b/src/setup_config.rs new file mode 100644 index 000000000..175b7f157 --- /dev/null +++ b/src/setup_config.rs @@ -0,0 +1,257 @@ +// Copyright © 2017 winapi-rs developers +// Licensed under the Apache License, Version 2.0 +// or the MIT license +// , 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); + +impl SetupConfiguration { + pub fn new() -> Result { + 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 { + 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 { + 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 { + let mut obj = null_mut(); + let this = try!(self.0.cast::()); + let err = unsafe { this.EnumAllInstances(&mut obj) }; + if err < 0 { return Err(err); } + Ok(unsafe { EnumSetupInstances::from_raw(obj) }) + } +} + +pub struct SetupInstance(ComPtr); + +impl SetupInstance { + pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance { + SetupInstance(ComPtr::from_raw(obj)) + } + pub fn instance_id(&self) -> Result { + 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 { + 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 { + 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 { + 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 { + let mut s = null_mut(); + let this = try!(self.0.cast::()); + 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); + +impl EnumSetupInstances { + pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances { + EnumSetupInstances(ComPtr::from_raw(obj)) + } +} + +impl Iterator for EnumSetupInstances { + type Item = Result; + fn next(&mut self) -> Option> { + 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/src/winapi.rs b/src/winapi.rs new file mode 100644 index 000000000..010d165c7 --- /dev/null +++ b/src/winapi.rs @@ -0,0 +1,214 @@ +// Copyright © 2015-2017 winapi-rs developers +// Licensed under the Apache License, Version 2.0 +// or the MIT license +// , 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/src/windows_registry.rs b/src/windows_registry.rs index 08b1df5f9..9b1b5b9b0 100644 --- a/src/windows_registry.rs +++ b/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, @@ -53,7 +54,11 @@ pub fn find_tool(target: &str, tool: &str) -> Option { 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}; struct MsvcTool { tool: PathBuf, @@ -110,19 +115,96 @@ pub fn find_tool(target: &str, tool: &str) -> Option { // environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that // the tool is actually usable. - return find_msvc_latest(tool, target, "15.0") - .or_else(|| find_msvc_latest(tool, target, "14.0")) + return find_msvc_17(tool, target) + .or_else(|| find_msvc_14_or_15(tool, target, "15.0")) + .or_else(|| find_msvc_14_or_15(tool, target, "14.0")) .or_else(|| find_msvc_12(tool, target)) .or_else(|| find_msvc_11(tool, target)); - // For MSVC 14 or newer we need to find the Universal CRT as well as either + // In MSVC 17 MS once again changed the scheme for locating the tooling. + // Now we must go through some COM interfaces, which is super fun for Rust. + fn find_msvc_17(tool: &str, target: &str) -> Option { + 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_vs17_instance(tool, target, &instance); + if tool.is_some() { + return tool; + } + } + + None + } + + fn tool_from_vs17_instance(tool: &str, target: &str, + instance: &SetupInstance) -> Option { + let (bin_path, lib_path, include_path) = otry!(vs17_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()) + } + + fn vs17_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)) + } + + 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 and 15 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 { + fn find_msvc_14_or_15(tool: &str, target: &str, ver: &str) -> Option { let vcdir = otry!(get_vc_dir(ver)); 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,10 +227,9 @@ pub fn find_tool(target: &str, tool: &str) -> Option { 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.