Skip to content

Commit

Permalink
Add MSVC 2017 detection
Browse files Browse the repository at this point in the history
This teaches gcc-rs to find tools installed by MSVC 2017. It's not
obvious to what extent the new COM interfaces are expected to be
used. This patch only uses it to find the product installation
directory, then infers everything else. A lot of COM scaffolding to do
very little. The toolchains seem to have a different directory
structure in this release to better support cross-compilation.

It looks to me like all the lib/include logic is pretty much the same
as it always has been.

This is tested to fix the rustup installation logic, and to fix
rustc in basic use cases on x86_64.

cc #143
cc rust-lang/rustup#1003
cc rust-lang/rust#38584
cc alexcrichton/curl-rust#161
  • Loading branch information
brson committed May 24, 2017
1 parent 1bc6010 commit 4ea110e
Show file tree
Hide file tree
Showing 5 changed files with 694 additions and 7 deletions.
125 changes: 125 additions & 0 deletions 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)
}
}

10 changes: 10 additions & 0 deletions src/lib.rs
Expand Up @@ -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.
Expand Down

0 comments on commit 4ea110e

Please sign in to comment.