diff options
Diffstat (limited to 'gcc/src')
| -rw-r--r-- | gcc/src/com.rs | 125 | ||||
| -rw-r--r-- | gcc/src/lib.rs | 108 | ||||
| -rw-r--r-- | gcc/src/setup_config.rs | 257 | ||||
| -rw-r--r-- | gcc/src/winapi.rs | 214 | ||||
| -rw-r--r-- | gcc/src/windows_registry.rs | 267 | 
5 files changed, 911 insertions, 60 deletions
| 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() | 
