Browse Source

Run `cargo fmt`

Also add a builder to CI to ensure we're styled right!
cl-test
Alex Crichton 7 years ago
parent
commit
f3a9c9cbdc
  1. 4
      .travis.yml
  2. 20
      cc-test/build.rs
  3. 56
      src/com.rs
  4. 229
      src/lib.rs
  5. 112
      src/registry.rs
  6. 62
      src/setup_config.rs
  7. 16
      src/winapi.rs
  8. 151
      src/windows_registry.rs
  9. 19
      tests/support/mod.rs
  10. 89
      tests/test.rs

4
.travis.yml

@ -16,6 +16,10 @@ matrix:
env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1 env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1
- rust: nightly - rust: nightly
env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1 env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1
- install: rustup component add rustfmt-preview
script:
- cargo fmt -- --write-mode diff
- (cd cc-test && cargo fmt -- --write-mode diff)
- rust: nightly - rust: nightly
before_script: before_script:

20
cc-test/build.rs

@ -25,12 +25,12 @@ fn main() {
let target = std::env::var("TARGET").unwrap(); let target = std::env::var("TARGET").unwrap();
let file = target.split("-").next().unwrap(); let file = target.split("-").next().unwrap();
let file = format!("src/{}.{}", let file = format!(
file, "src/{}.{}",
if target.contains("msvc") { "asm" } else { "S" }); file,
cc::Build::new() if target.contains("msvc") { "asm" } else { "S" }
.file(file) );
.compile("asm"); cc::Build::new().file(file).compile("asm");
cc::Build::new() cc::Build::new()
.file("src/baz.cpp") .file("src/baz.cpp")
@ -38,9 +38,7 @@ fn main() {
.compile("baz"); .compile("baz");
if target.contains("windows") { if target.contains("windows") {
cc::Build::new() cc::Build::new().file("src/windows.c").compile("windows");
.file("src/windows.c")
.compile("windows");
} }
// Test that the `windows_registry` module will set PATH by looking for // Test that the `windows_registry` module will set PATH by looking for
@ -86,9 +84,7 @@ fn main() {
.file("src/opt_linkage.c") .file("src/opt_linkage.c")
.compile("OptLinkage"); .compile("OptLinkage");
let out = cc::Build::new() let out = cc::Build::new().file("src/expand.c").expand();
.file("src/expand.c")
.expand();
let out = String::from_utf8(out).unwrap(); let out = String::from_utf8(out).unwrap();
assert!(out.contains("hello world")); assert!(out.contains("hello world"));
} }

56
src/com.rs

@ -19,7 +19,7 @@ use winapi::CoInitializeEx;
use winapi::COINIT_MULTITHREADED; use winapi::COINIT_MULTITHREADED;
use winapi::{SysFreeString, SysStringLen}; use winapi::{SysFreeString, SysStringLen};
use winapi::IUnknown; use winapi::IUnknown;
use winapi::{S_OK, S_FALSE, HRESULT}; use winapi::{HRESULT, S_FALSE, S_OK};
pub fn initialize() -> Result<(), HRESULT> { pub fn initialize() -> Result<(), HRESULT> {
let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) }; let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) };
@ -30,8 +30,13 @@ pub fn initialize() -> Result<(), HRESULT> {
Ok(()) Ok(())
} }
pub struct ComPtr<T>(*mut T) where T: Interface; pub struct ComPtr<T>(*mut T)
impl<T> ComPtr<T> where T: Interface { where
T: Interface;
impl<T> ComPtr<T>
where
T: Interface,
{
/// Creates a `ComPtr` to wrap a raw pointer. /// Creates a `ComPtr` to wrap a raw pointer.
/// It takes ownership over the pointer which means it does __not__ call `AddRef`. /// It takes ownership over the pointer which means it does __not__ call `AddRef`.
/// `T` __must__ be a COM interface that inherits from `IUnknown`. /// `T` __must__ be a COM interface that inherits from `IUnknown`.
@ -40,7 +45,11 @@ impl<T> ComPtr<T> where T: Interface {
ComPtr(ptr) ComPtr(ptr)
} }
/// Casts up the inheritance chain /// Casts up the inheritance chain
pub fn up<U>(self) -> ComPtr<U> where T: Deref<Target=U>, U: Interface { pub fn up<U>(self) -> ComPtr<U>
where
T: Deref<Target = U>,
U: Interface,
{
ComPtr(self.into_raw() as *mut U) ComPtr(self.into_raw() as *mut U)
} }
/// Extracts the raw pointer. /// Extracts the raw pointer.
@ -55,20 +64,31 @@ impl<T> ComPtr<T> where T: Interface {
unsafe { &*(self.0 as *mut IUnknown) } unsafe { &*(self.0 as *mut IUnknown) }
} }
/// Performs QueryInterface fun. /// Performs QueryInterface fun.
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32> where U: Interface { pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
where
U: Interface,
{
let mut obj = null_mut(); let mut obj = null_mut();
let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) }; let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(unsafe { ComPtr::from_raw(obj as *mut U) }) Ok(unsafe { ComPtr::from_raw(obj as *mut U) })
} }
} }
impl<T> Deref for ComPtr<T> where T: Interface { impl<T> Deref for ComPtr<T>
where
T: Interface,
{
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
unsafe { &*self.0 } unsafe { &*self.0 }
} }
} }
impl<T> Clone for ComPtr<T> where T: Interface { impl<T> Clone for ComPtr<T>
where
T: Interface,
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
unsafe { unsafe {
self.as_unknown().AddRef(); self.as_unknown().AddRef();
@ -76,9 +96,14 @@ impl<T> Clone for ComPtr<T> where T: Interface {
} }
} }
} }
impl<T> Drop for ComPtr<T> where T: Interface { impl<T> Drop for ComPtr<T>
where
T: Interface,
{
fn drop(&mut self) { fn drop(&mut self) {
unsafe { self.as_unknown().Release(); } unsafe {
self.as_unknown().Release();
}
} }
} }
pub struct BStr(BSTR); pub struct BStr(BSTR);
@ -102,7 +127,10 @@ pub trait ToWide {
fn to_wide(&self) -> Vec<u16>; fn to_wide(&self) -> Vec<u16>;
fn to_wide_null(&self) -> Vec<u16>; fn to_wide_null(&self) -> Vec<u16>;
} }
impl<T> ToWide for T where T: AsRef<OsStr> { impl<T> ToWide for T
where
T: AsRef<OsStr>,
{
fn to_wide(&self) -> Vec<u16> { fn to_wide(&self) -> Vec<u16> {
self.as_ref().encode_wide().collect() self.as_ref().encode_wide().collect()
} }
@ -110,7 +138,10 @@ impl<T> ToWide for T where T: AsRef<OsStr> {
self.as_ref().encode_wide().chain(Some(0)).collect() self.as_ref().encode_wide().chain(Some(0)).collect()
} }
} }
pub trait FromWide where Self: Sized { pub trait FromWide
where
Self: Sized,
{
fn from_wide(wide: &[u16]) -> Self; fn from_wide(wide: &[u16]) -> Self;
fn from_wide_null(wide: &[u16]) -> Self { fn from_wide_null(wide: &[u16]) -> Self {
let len = wide.iter().take_while(|&&c| c != 0).count(); let len = wide.iter().take_while(|&&c| c != 0).count();
@ -122,4 +153,3 @@ impl FromWide for OsString {
OsStringExt::from_wide(wide) OsStringExt::from_wide(wide)
} }
} }

229
src/lib.rs

@ -61,16 +61,15 @@
extern crate rayon; extern crate rayon;
use std::env; use std::env;
use std::ffi::{OsString, OsStr}; use std::ffi::{OsStr, OsString};
use std::fs; use std::fs;
use std::path::{PathBuf, Path}; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio, Child}; use std::process::{Child, Command, Stdio};
use std::io::{self, BufReader, BufRead, Read, Write}; use std::io::{self, BufRead, BufReader, Read, Write};
use std::thread::{self, JoinHandle}; use std::thread::{self, JoinHandle};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
// These modules are all glue to support reading the MSVC version from // These modules are all glue to support reading the MSVC version from
// the registry and from COM interfaces // the registry and from COM interfaces
#[cfg(windows)] #[cfg(windows)]
@ -242,8 +241,7 @@ impl ToolFamily {
fn nvcc_debug_flag(&self) -> &'static str { fn nvcc_debug_flag(&self) -> &'static str {
match *self { match *self {
ToolFamily::Msvc => unimplemented!(), ToolFamily::Msvc => unimplemented!(),
ToolFamily::Gnu | ToolFamily::Gnu | ToolFamily::Clang => "-G",
ToolFamily::Clang => "-G",
} }
} }
@ -252,8 +250,7 @@ impl ToolFamily {
fn nvcc_redirect_flag(&self) -> &'static str { fn nvcc_redirect_flag(&self) -> &'static str {
match *self { match *self {
ToolFamily::Msvc => unimplemented!(), ToolFamily::Msvc => unimplemented!(),
ToolFamily::Gnu | ToolFamily::Gnu | ToolFamily::Clang => "-Xcompiler",
ToolFamily::Clang => "-Xcompiler",
} }
} }
} }
@ -270,10 +267,7 @@ struct Object {
impl Object { impl Object {
/// Create a new source file -> object file pair. /// Create a new source file -> object file pair.
fn new(src: PathBuf, dst: PathBuf) -> Object { fn new(src: PathBuf, dst: PathBuf) -> Object {
Object { Object { src: src, dst: dst }
src: src,
dst: dst,
}
} }
} }
@ -346,10 +340,8 @@ impl Build {
/// .compile("foo"); /// .compile("foo");
/// ``` /// ```
pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build { pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build {
self.definitions.push(( self.definitions
var.to_string(), .push((var.to_string(), val.into().map(|s| s.to_string())));
val.into().map(|s| s.to_string()),
));
self self
} }
@ -405,10 +397,12 @@ impl Build {
/// `known_flag_support` field. If `is_flag_supported(flag)` /// `known_flag_support` field. If `is_flag_supported(flag)`
/// is called again, the result will be read from the hash table. /// is called again, the result will be read from the hash table.
pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> { pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
let known_status = self.known_flag_support_status.lock().ok() let known_status = self.known_flag_support_status
.lock()
.ok()
.and_then(|flag_status| flag_status.get(flag).cloned()); .and_then(|flag_status| flag_status.get(flag).cloned());
if let Some(is_supported) = known_status { if let Some(is_supported) = known_status {
return Ok(is_supported) return Ok(is_supported);
} }
let out_dir = self.get_out_dir()?; let out_dir = self.get_out_dir()?;
@ -426,8 +420,7 @@ impl Build {
let compiler = cfg.try_get_compiler()?; let compiler = cfg.try_get_compiler()?;
let mut cmd = compiler.to_command(); let mut cmd = compiler.to_command();
let is_arm = target.contains("aarch64") || target.contains("arm"); let is_arm = target.contains("aarch64") || target.contains("arm");
command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false, command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false, is_arm);
is_arm);
// We need to explicitly tell msvc not to link and create an exe // We need to explicitly tell msvc not to link and create an exe
// in the root directory of the crate // in the root directory of the crate
@ -796,9 +789,8 @@ impl Build {
A: AsRef<OsStr>, A: AsRef<OsStr>,
B: AsRef<OsStr>, B: AsRef<OsStr>,
{ {
self.env.push( self.env
(a.as_ref().to_owned(), b.as_ref().to_owned()), .push((a.as_ref().to_owned(), b.as_ref().to_owned()));
);
self self
} }
@ -939,9 +931,7 @@ impl Build {
compiler compiler
.path .path
.file_name() .file_name()
.ok_or_else(|| { .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
Error::new(ErrorKind::IOError, "Failed to get compiler path.")
})?
.to_string_lossy() .to_string_lossy()
.into_owned(), .into_owned(),
) )
@ -979,9 +969,7 @@ impl Build {
let name = compiler let name = compiler
.path .path
.file_name() .file_name()
.ok_or_else(|| { .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
Error::new(ErrorKind::IOError, "Failed to get compiler path.")
})?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
@ -1226,14 +1214,13 @@ impl Build {
if self.cpp { if self.cpp {
match (self.cpp_set_stdlib.as_ref(), cmd.family) { match (self.cpp_set_stdlib.as_ref(), cmd.family) {
(None, _) => {} (None, _) => {}
(Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
(Some(stdlib), ToolFamily::Clang) => {
cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into()); cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
} }
_ => { _ => {
println!( println!(
"cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \ "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
does not support this option, ignored", does not support this option, ignored",
cmd.family cmd.family
); );
} }
@ -1325,13 +1312,8 @@ impl Build {
if target.contains("msvc") { if target.contains("msvc") {
let mut cmd = match self.archiver { let mut cmd = match self.archiver {
Some(ref s) => self.cmd(s), Some(ref s) => self.cmd(s),
None => { None => windows_registry::find(&target, "lib.exe")
windows_registry::find(&target, "lib.exe").unwrap_or_else( .unwrap_or_else(|| self.cmd("lib.exe")),
|| {
self.cmd("lib.exe")
},
)
}
}; };
let mut out = OsString::from("/OUT:"); let mut out = OsString::from("/OUT:");
@ -1340,10 +1322,13 @@ impl Build {
// Similar to https://github.com/rust-lang/rust/pull/47507 // Similar to https://github.com/rust-lang/rust/pull/47507
// and https://github.com/rust-lang/rust/pull/48548 // and https://github.com/rust-lang/rust/pull/48548
let estimated_command_line_len = let estimated_command_line_len = objects
objects.iter().chain(&self.objects).map(|a| a.as_os_str().len()).sum::<usize>(); .iter()
.chain(&self.objects)
.map(|a| a.as_os_str().len())
.sum::<usize>();
if estimated_command_line_len > 1024 * 6 { if estimated_command_line_len > 1024 * 6 {
let mut args = String::from("\u{FEFF}"); // BOM let mut args = String::from("\u{FEFF}"); // BOM
for arg in objects.iter().chain(&self.objects) { for arg in objects.iter().chain(&self.objects) {
args.push('"'); args.push('"');
for c in arg.to_str().unwrap().chars() { for c in arg.to_str().unwrap().chars() {
@ -1364,7 +1349,10 @@ impl Build {
let mut args_file = OsString::from(dst); let mut args_file = OsString::from(dst);
args_file.push(".args"); args_file.push(".args");
fs::File::create(&args_file).unwrap().write_all(&utf16le).unwrap(); fs::File::create(&args_file)
.unwrap()
.write_all(&utf16le)
.unwrap();
let mut args_file_arg = OsString::from("@"); let mut args_file_arg = OsString::from("@");
args_file_arg.push(args_file); args_file_arg.push(args_file);
@ -1493,41 +1481,44 @@ impl Build {
}; };
// On Solaris, c++/cc unlikely to exist or be correct. // On Solaris, c++/cc unlikely to exist or be correct.
let default = if host.contains("solaris") { gnu } else { traditional }; let default = if host.contains("solaris") {
gnu
let tool_opt: Option<Tool> = } else {
self.env_tool(env) traditional
.map(|(tool, cc, args)| { };
// chop off leading/trailing whitespace to work around
// semi-buggy build scripts which are shared in let tool_opt: Option<Tool> = self.env_tool(env)
// makefiles/configure scripts (where spaces are far more .map(|(tool, cc, args)| {
// lenient) // chop off leading/trailing whitespace to work around
let mut t = Tool::new(PathBuf::from(tool.trim())); // semi-buggy build scripts which are shared in
if let Some(cc) = cc { // makefiles/configure scripts (where spaces are far more
t.cc_wrapper_path = Some(PathBuf::from(cc)); // lenient)
} let mut t = Tool::new(PathBuf::from(tool.trim()));
for arg in args { if let Some(cc) = cc {
t.cc_wrapper_args.push(arg.into()); t.cc_wrapper_path = Some(PathBuf::from(cc));
} }
t for arg in args {
}) t.cc_wrapper_args.push(arg.into());
.or_else(|| { }
if target.contains("emscripten") { t
let tool = if self.cpp { "em++" } else { "emcc" }; })
// Windows uses bat file so we have to be a bit more specific .or_else(|| {
if cfg!(windows) { if target.contains("emscripten") {
let mut t = Tool::new(PathBuf::from("cmd")); let tool = if self.cpp { "em++" } else { "emcc" };
t.args.push("/c".into()); // Windows uses bat file so we have to be a bit more specific
t.args.push(format!("{}.bat", tool).into()); if cfg!(windows) {
Some(t) let mut t = Tool::new(PathBuf::from("cmd"));
} else { t.args.push("/c".into());
Some(Tool::new(PathBuf::from(tool))) t.args.push(format!("{}.bat", tool).into());
} Some(t)
} else { } else {
None Some(Tool::new(PathBuf::from(tool)))
} }
}) } else {
.or_else(|| windows_registry::find_tool(&target, "cl.exe")); None
}
})
.or_else(|| windows_registry::find_tool(&target, "cl.exe"));
let tool = match tool_opt { let tool = match tool_opt {
Some(t) => t, Some(t) => t,
@ -1601,14 +1592,18 @@ impl Build {
}; };
let tool = if self.cuda { let tool = if self.cuda {
assert!(tool.args.is_empty(), assert!(
"CUDA compilation currently assumes empty pre-existing args"); tool.args.is_empty(),
"CUDA compilation currently assumes empty pre-existing args"
);
let nvcc = match self.get_var("NVCC") { let nvcc = match self.get_var("NVCC") {
Err(_) => "nvcc".into(), Err(_) => "nvcc".into(),
Ok(nvcc) => nvcc, Ok(nvcc) => nvcc,
}; };
let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), self.cuda); let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), self.cuda);
nvcc_tool.args.push(format!("-ccbin={}", tool.path.display()).into()); nvcc_tool
.args
.push(format!("-ccbin={}", tool.path.display()).into());
nvcc_tool nvcc_tool
} else { } else {
tool tool
@ -1631,10 +1626,7 @@ impl Build {
Some(res) => Ok(res), Some(res) => Ok(res),
None => Err(Error::new( None => Err(Error::new(
ErrorKind::EnvVarNotFound, ErrorKind::EnvVarNotFound,
&format!( &format!("Could not find environment variable {}.", var_base),
"Could not find environment variable {}.",
var_base
),
)), )),
} }
} }
@ -1648,7 +1640,6 @@ impl Build {
.collect() .collect()
} }
/// Returns compiler path, optional modifier name from whitelist, and arguments vec /// Returns compiler path, optional modifier name from whitelist, and arguments vec
fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> { fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> {
let tool = match self.get_var(name) { let tool = match self.get_var(name) {
@ -1674,14 +1665,18 @@ impl Build {
None => return None, None => return None,
}; };
let file_stem = Path::new(maybe_wrapper).file_stem().unwrap().to_str().unwrap(); let file_stem = Path::new(maybe_wrapper)
.file_stem()
.unwrap()
.to_str()
.unwrap();
if whitelist.contains(&file_stem) { if whitelist.contains(&file_stem) {
if let Some(compiler) = parts.next() { if let Some(compiler) = parts.next() {
return Some(( return Some((
compiler.to_string(), compiler.to_string(),
Some(maybe_wrapper.to_string()), Some(maybe_wrapper.to_string()),
parts.map(|s| s.to_string()).collect(), parts.map(|s| s.to_string()).collect(),
)) ));
} }
} }
@ -1786,10 +1781,7 @@ impl Build {
Some(s) => Ok(s), Some(s) => Ok(s),
None => Err(Error::new( None => Err(Error::new(
ErrorKind::EnvVarNotFound, ErrorKind::EnvVarNotFound,
&format!( &format!("Environment variable {} not defined.", v.to_string()),
"Environment variable {} not defined.",
v.to_string()
),
)), )),
} }
} }
@ -1817,8 +1809,9 @@ impl Tool {
let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) { let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
if fname.contains("clang") { if fname.contains("clang") {
ToolFamily::Clang ToolFamily::Clang
} else if fname.contains("cl") && !fname.contains("cloudabi") && } else if fname.contains("cl") && !fname.contains("cloudabi")
!fname.contains("uclibc") { && !fname.contains("uclibc")
{
ToolFamily::Msvc ToolFamily::Msvc
} else { } else {
ToolFamily::Gnu ToolFamily::Gnu
@ -1861,8 +1854,8 @@ impl Tool {
cmd.arg(&self.path); cmd.arg(&self.path);
cmd.args(&self.cc_wrapper_args); cmd.args(&self.cc_wrapper_args);
cmd cmd
}, }
None => Command::new(&self.path) None => Command::new(&self.path),
}; };
cmd.args(&self.args); cmd.args(&self.args);
for &(ref k, ref v) in self.env.iter() { for &(ref k, ref v) in self.env.iter() {
@ -1908,10 +1901,8 @@ impl Tool {
cc_env.push(arg); cc_env.push(arg);
} }
cc_env cc_env
},
None => {
OsString::from("")
} }
None => OsString::from(""),
} }
} }
@ -1954,8 +1945,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
ErrorKind::ToolExecError, ErrorKind::ToolExecError,
&format!( &format!(
"Failed to wait on spawned child process, command {:?} with args {:?}.", "Failed to wait on spawned child process, command {:?} with args {:?}.",
cmd, cmd, program
program
), ),
)) ))
} }
@ -1970,9 +1960,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
ErrorKind::ToolExecError, ErrorKind::ToolExecError,
&format!( &format!(
"Command {:?} with args {:?} did not execute successfully (status code {}).", "Command {:?} with args {:?} did not execute successfully (status code {}).",
cmd, cmd, program, status
program,
status
), ),
)) ))
} }
@ -1995,8 +1983,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
ErrorKind::ToolExecError, ErrorKind::ToolExecError,
&format!( &format!(
"Failed to wait on spawned child process, command {:?} with args {:?}.", "Failed to wait on spawned child process, command {:?} with args {:?}.",
cmd, cmd, program
program
), ),
)) ))
} }
@ -2011,9 +1998,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
ErrorKind::ToolExecError, ErrorKind::ToolExecError,
&format!( &format!(
"Command {:?} with args {:?} did not execute successfully (status code {}).", "Command {:?} with args {:?} did not execute successfully (status code {}).",
cmd, cmd, program, status
program,
status
), ),
)) ))
} }
@ -2029,39 +2014,30 @@ fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Er
match cmd.stderr(Stdio::piped()).spawn() { match cmd.stderr(Stdio::piped()).spawn() {
Ok(mut child) => { Ok(mut child) => {
let stderr = BufReader::new(child.stderr.take().unwrap()); let stderr = BufReader::new(child.stderr.take().unwrap());
let print = thread::spawn(move || for line in stderr.split(b'\n').filter_map( let print = thread::spawn(move || {
|l| l.ok(), for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
) print!("cargo:warning=");
{ std::io::stdout().write_all(&line).unwrap();
print!("cargo:warning="); println!("");
std::io::stdout().write_all(&line).unwrap(); }
println!("");
}); });
Ok((child, print)) Ok((child, print))
} }
Err(ref e) if e.kind() == io::ErrorKind::NotFound => { Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
let extra = if cfg!(windows) { let extra = if cfg!(windows) {
" (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \ " (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \
for help)" for help)"
} else { } else {
"" ""
}; };
Err(Error::new( Err(Error::new(
ErrorKind::ToolNotFound, ErrorKind::ToolNotFound,
&format!( &format!("Failed to find tool. Is `{}` installed?{}", program, extra),
"Failed to find tool. Is `{}` installed?{}",
program,
extra
),
)) ))
} }
Err(_) => Err(Error::new( Err(_) => Err(Error::new(
ErrorKind::ToolExecError, ErrorKind::ToolExecError,
&format!( &format!("Command {:?} with args {:?} failed to start.", cmd, program),
"Command {:?} with args {:?} failed to start.",
cmd,
program
),
)), )),
} }
} }
@ -2070,7 +2046,6 @@ fn fail(s: &str) -> ! {
panic!("\n\nInternal error occurred: {}\n\n", s) panic!("\n\nInternal error occurred: {}\n\n", s)
} }
fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool, is_arm: bool) { fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool, is_arm: bool) {
if msvc && is_asm && is_arm { if msvc && is_asm && is_arm {
cmd.arg("-o").arg(&dst); cmd.arg("-o").arg(&dst);

112
src/registry.rs

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::ffi::{OsString, OsStr}; use std::ffi::{OsStr, OsString};
use std::io; use std::io;
use std::ops::RangeFrom; use std::ops::RangeFrom;
use std::os::raw; use std::os::raw;
@ -36,28 +36,31 @@ const KEY_WOW64_32KEY: DWORD = 0x200;
#[link(name = "advapi32")] #[link(name = "advapi32")]
extern "system" { extern "system" {
fn RegOpenKeyExW(key: HKEY, fn RegOpenKeyExW(
lpSubKey: LPCWSTR, key: HKEY,
ulOptions: DWORD, lpSubKey: LPCWSTR,
samDesired: REGSAM, ulOptions: DWORD,
phkResult: PHKEY) samDesired: REGSAM,
-> LONG; phkResult: PHKEY,
fn RegEnumKeyExW(key: HKEY, ) -> LONG;
dwIndex: DWORD, fn RegEnumKeyExW(
lpName: LPWSTR, key: HKEY,
lpcName: LPDWORD, dwIndex: DWORD,
lpReserved: LPDWORD, lpName: LPWSTR,
lpClass: LPWSTR, lpcName: LPDWORD,
lpcClass: LPDWORD, lpReserved: LPDWORD,
lpftLastWriteTime: PFILETIME) lpClass: LPWSTR,
-> LONG; lpcClass: LPDWORD,
fn RegQueryValueExW(hKey: HKEY, lpftLastWriteTime: PFILETIME,
lpValueName: LPCWSTR, ) -> LONG;
lpReserved: LPDWORD, fn RegQueryValueExW(
lpType: LPDWORD, hKey: HKEY,
lpData: LPBYTE, lpValueName: LPCWSTR,
lpcbData: LPDWORD) lpReserved: LPDWORD,
-> LONG; lpType: LPDWORD,
lpData: LPBYTE,
lpcbData: LPDWORD,
) -> LONG;
fn RegCloseKey(hKey: HKEY) -> LONG; fn RegCloseKey(hKey: HKEY) -> LONG;
} }
@ -90,11 +93,13 @@ impl RegistryKey {
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>(); let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut ret = 0 as *mut _; let mut ret = 0 as *mut _;
let err = unsafe { let err = unsafe {
RegOpenKeyExW(self.raw(), RegOpenKeyExW(
key.as_ptr(), self.raw(),
0, key.as_ptr(),
KEY_READ | KEY_WOW64_32KEY, 0,
&mut ret) KEY_READ | KEY_WOW64_32KEY,
&mut ret,
)
}; };
if err == ERROR_SUCCESS as LONG { if err == ERROR_SUCCESS as LONG {
Ok(RegistryKey(Repr::Owned(OwnedKey(ret)))) Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
@ -116,29 +121,36 @@ impl RegistryKey {
let mut len = 0; let mut len = 0;
let mut kind = 0; let mut kind = 0;
unsafe { unsafe {
let err = RegQueryValueExW(self.raw(), let err = RegQueryValueExW(
name.as_ptr(), self.raw(),
0 as *mut _, name.as_ptr(),
&mut kind, 0 as *mut _,
0 as *mut _, &mut kind,
&mut len); 0 as *mut _,
&mut len,
);
if err != ERROR_SUCCESS as LONG { if err != ERROR_SUCCESS as LONG {
return Err(io::Error::from_raw_os_error(err as i32)); return Err(io::Error::from_raw_os_error(err as i32));
} }
if kind != REG_SZ { if kind != REG_SZ {
return Err(io::Error::new(io::ErrorKind::Other, "registry key wasn't a string")); return Err(io::Error::new(
io::ErrorKind::Other,
"registry key wasn't a string",
));
} }
// The length here is the length in bytes, but we're using wide // The length here is the length in bytes, but we're using wide
// characters so we need to be sure to halve it for the capacity // characters so we need to be sure to halve it for the capacity
// passed in. // passed in.
let mut v = Vec::with_capacity(len as usize / 2); let mut v = Vec::with_capacity(len as usize / 2);
let err = RegQueryValueExW(self.raw(), let err = RegQueryValueExW(
name.as_ptr(), self.raw(),
0 as *mut _, name.as_ptr(),
0 as *mut _, 0 as *mut _,
v.as_mut_ptr() as *mut _, 0 as *mut _,
&mut len); v.as_mut_ptr() as *mut _,
&mut len,
);
if err != ERROR_SUCCESS as LONG { if err != ERROR_SUCCESS as LONG {
return Err(io::Error::from_raw_os_error(err as i32)); return Err(io::Error::from_raw_os_error(err as i32));
} }
@ -169,14 +181,16 @@ impl<'a> Iterator for Iter<'a> {
self.idx.next().and_then(|i| unsafe { self.idx.next().and_then(|i| unsafe {
let mut v = Vec::with_capacity(256); let mut v = Vec::with_capacity(256);
let mut len = v.capacity() as DWORD; let mut len = v.capacity() as DWORD;
let ret = RegEnumKeyExW(self.key.raw(), let ret = RegEnumKeyExW(
i, self.key.raw(),
v.as_mut_ptr(), i,
&mut len, v.as_mut_ptr(),
0 as *mut _, &mut len,
0 as *mut _, 0 as *mut _,
0 as *mut _, 0 as *mut _,
0 as *mut _); 0 as *mut _,
0 as *mut _,
);
if ret == ERROR_NO_MORE_ITEMS as LONG { if ret == ERROR_NO_MORE_ITEMS as LONG {
None None
} else if ret != ERROR_SUCCESS as LONG { } else if ret != ERROR_SUCCESS as LONG {

62
src/setup_config.rs

@ -15,7 +15,7 @@ use winapi::{LPFILETIME, ULONG};
use winapi::S_FALSE; use winapi::S_FALSE;
use winapi::BSTR; use winapi::BSTR;
use winapi::LPCOLESTR; use winapi::LPCOLESTR;
use winapi::{CLSCTX_ALL, CoCreateInstance}; use winapi::{CoCreateInstance, CLSCTX_ALL};
use winapi::LPSAFEARRAY; use winapi::LPSAFEARRAY;
use winapi::{IUnknown, IUnknownVtbl}; use winapi::{IUnknown, IUnknownVtbl};
use winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG}; use winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG};
@ -155,7 +155,7 @@ interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {
}} }}
DEFINE_GUID!{CLSID_SetupConfiguration, DEFINE_GUID!{CLSID_SetupConfiguration,
0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d} 0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d}
// Safe wrapper around the COM interfaces // Safe wrapper around the COM interfaces
pub struct SetupConfiguration(ComPtr<ISetupConfiguration>); pub struct SetupConfiguration(ComPtr<ISetupConfiguration>);
@ -163,31 +163,44 @@ pub struct SetupConfiguration(ComPtr<ISetupConfiguration>);
impl SetupConfiguration { impl SetupConfiguration {
pub fn new() -> Result<SetupConfiguration, i32> { pub fn new() -> Result<SetupConfiguration, i32> {
let mut obj = null_mut(); let mut obj = null_mut();
let err = unsafe { CoCreateInstance( let err = unsafe {
&CLSID_SetupConfiguration, null_mut(), CLSCTX_ALL, CoCreateInstance(
&ISetupConfiguration::uuidof(), &mut obj, &CLSID_SetupConfiguration,
) }; null_mut(),
if err < 0 { return Err(err); } CLSCTX_ALL,
&ISetupConfiguration::uuidof(),
&mut obj,
)
};
if err < 0 {
return Err(err);
}
let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) }; let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) };
Ok(SetupConfiguration(obj)) Ok(SetupConfiguration(obj))
} }
pub fn get_instance_for_current_process(&self) -> Result<SetupInstance, i32> { pub fn get_instance_for_current_process(&self) -> Result<SetupInstance, i32> {
let mut obj = null_mut(); let mut obj = null_mut();
let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) }; let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(unsafe { SetupInstance::from_raw(obj) }) Ok(unsafe { SetupInstance::from_raw(obj) })
} }
pub fn enum_instances(&self) -> Result<EnumSetupInstances, i32> { pub fn enum_instances(&self) -> Result<EnumSetupInstances, i32> {
let mut obj = null_mut(); let mut obj = null_mut();
let err = unsafe { self.0.EnumInstances(&mut obj) }; let err = unsafe { self.0.EnumInstances(&mut obj) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(unsafe { EnumSetupInstances::from_raw(obj) }) Ok(unsafe { EnumSetupInstances::from_raw(obj) })
} }
pub fn enum_all_instances(&self) -> Result<EnumSetupInstances, i32> { pub fn enum_all_instances(&self) -> Result<EnumSetupInstances, i32> {
let mut obj = null_mut(); let mut obj = null_mut();
let this = try!(self.0.cast::<ISetupConfiguration2>()); let this = try!(self.0.cast::<ISetupConfiguration2>());
let err = unsafe { this.EnumAllInstances(&mut obj) }; let err = unsafe { this.EnumAllInstances(&mut obj) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(unsafe { EnumSetupInstances::from_raw(obj) }) Ok(unsafe { EnumSetupInstances::from_raw(obj) })
} }
} }
@ -202,28 +215,36 @@ impl SetupInstance {
let mut s = null_mut(); let mut s = null_mut();
let err = unsafe { self.0.GetInstanceId(&mut s) }; let err = unsafe { self.0.GetInstanceId(&mut s) };
let bstr = unsafe { BStr::from_raw(s) }; let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring()) Ok(bstr.to_osstring())
} }
pub fn installation_name(&self) -> Result<OsString, i32> { pub fn installation_name(&self) -> Result<OsString, i32> {
let mut s = null_mut(); let mut s = null_mut();
let err = unsafe { self.0.GetInstallationName(&mut s) }; let err = unsafe { self.0.GetInstallationName(&mut s) };
let bstr = unsafe { BStr::from_raw(s) }; let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring()) Ok(bstr.to_osstring())
} }
pub fn installation_path(&self) -> Result<OsString, i32> { pub fn installation_path(&self) -> Result<OsString, i32> {
let mut s = null_mut(); let mut s = null_mut();
let err = unsafe { self.0.GetInstallationPath(&mut s) }; let err = unsafe { self.0.GetInstallationPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) }; let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring()) Ok(bstr.to_osstring())
} }
pub fn installation_version(&self) -> Result<OsString, i32> { pub fn installation_version(&self) -> Result<OsString, i32> {
let mut s = null_mut(); let mut s = null_mut();
let err = unsafe { self.0.GetInstallationVersion(&mut s) }; let err = unsafe { self.0.GetInstallationVersion(&mut s) };
let bstr = unsafe { BStr::from_raw(s) }; let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring()) Ok(bstr.to_osstring())
} }
pub fn product_path(&self) -> Result<OsString, i32> { pub fn product_path(&self) -> Result<OsString, i32> {
@ -231,7 +252,9 @@ impl SetupInstance {
let this = try!(self.0.cast::<ISetupInstance2>()); let this = try!(self.0.cast::<ISetupInstance2>());
let err = unsafe { this.GetProductPath(&mut s) }; let err = unsafe { this.GetProductPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) }; let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); } if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring()) Ok(bstr.to_osstring())
} }
} }
@ -249,9 +272,12 @@ impl Iterator for EnumSetupInstances {
fn next(&mut self) -> Option<Result<SetupInstance, i32>> { fn next(&mut self) -> Option<Result<SetupInstance, i32>> {
let mut obj = null_mut(); let mut obj = null_mut();
let err = unsafe { self.0.Next(1, &mut obj, null_mut()) }; let err = unsafe { self.0.Next(1, &mut obj, null_mut()) };
if err < 0 { return Some(Err(err)); } if err < 0 {
if err == S_FALSE { return None; } return Some(Err(err));
}
if err == S_FALSE {
return None;
}
Some(Ok(unsafe { SetupInstance::from_raw(obj) })) Some(Ok(unsafe { SetupInstance::from_raw(obj) }))
} }
} }

16
src/winapi.rs

@ -44,8 +44,8 @@ pub const CLSCTX_INPROC_HANDLER: CLSCTX = 0x2;
pub const CLSCTX_LOCAL_SERVER: CLSCTX = 0x4; pub const CLSCTX_LOCAL_SERVER: CLSCTX = 0x4;
pub const CLSCTX_REMOTE_SERVER: CLSCTX = 0x10; pub const CLSCTX_REMOTE_SERVER: CLSCTX = 0x10;
pub const CLSCTX_ALL: CLSCTX = CLSCTX_INPROC_SERVER | pub const CLSCTX_ALL: CLSCTX =
CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER; CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -69,13 +69,17 @@ pub trait Interface {
#[link(name = "ole32")] #[link(name = "ole32")]
#[link(name = "oleaut32")] #[link(name = "oleaut32")]
extern { } extern "C" {}
extern "system" { extern "system" {
pub fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) -> HRESULT; pub fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) -> HRESULT;
pub fn CoCreateInstance(rclsid: REFCLSID, pUnkOuter: LPUNKNOWN, pub fn CoCreateInstance(
dwClsContext: DWORD, riid: REFIID, rclsid: REFCLSID,
ppv: *mut LPVOID) -> HRESULT; pUnkOuter: LPUNKNOWN,
dwClsContext: DWORD,
riid: REFIID,
ppv: *mut LPVOID,
) -> HRESULT;
pub fn SysFreeString(bstrString: BSTR); pub fn SysFreeString(bstrString: BSTR);
pub fn SysStringLen(pbstr: BSTR) -> UINT; pub fn SysStringLen(pbstr: BSTR) -> UINT;
} }

151
src/windows_registry.rs

@ -69,7 +69,11 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// should just find whatever that indicates. // should just find whatever that indicates.
if env::var_os("VCINSTALLDIR").is_some() { if env::var_os("VCINSTALLDIR").is_some() {
return env::var_os("PATH") return env::var_os("PATH")
.and_then(|path| env::split_paths(&path).map(|p| p.join(tool)).find(|p| p.exists())) .and_then(|path| {
env::split_paths(&path)
.map(|p| p.join(tool))
.find(|p| p.exists())
})
.map(|path| Tool::new(path.into())); .map(|path| Tool::new(path.into()));
} }
@ -119,19 +123,20 @@ pub fn find_vs_version() -> Result<VsVers, String> {
use std::env; use std::env;
match env::var("VisualStudioVersion") { match env::var("VisualStudioVersion") {
Ok(version) => { Ok(version) => match &version[..] {
match &version[..] { "15.0" => Ok(VsVers::Vs15),
"15.0" => Ok(VsVers::Vs15), "14.0" => Ok(VsVers::Vs14),
"14.0" => Ok(VsVers::Vs14), "12.0" => Ok(VsVers::Vs12),
"12.0" => Ok(VsVers::Vs12), vers => Err(format!(
vers => Err(format!("\n\n\ "\n\n\
unsupported or unknown VisualStudio version: {}\n\ unsupported or unknown VisualStudio version: {}\n\
if another version is installed consider running \ if another version is installed consider running \
the appropriate vcvars script before building this \ the appropriate vcvars script before building this \
crate\n\ crate\n\
", vers)), ",
} vers
} )),
},
_ => { _ => {
// Check for the presense of a specific registry key // Check for the presense of a specific registry key
// that indicates visual studio is installed. // that indicates visual studio is installed.
@ -142,12 +147,14 @@ pub fn find_vs_version() -> Result<VsVers, String> {
} else if impl_::has_msbuild_version("12.0") { } else if impl_::has_msbuild_version("12.0") {
Ok(VsVers::Vs12) Ok(VsVers::Vs12)
} else { } else {
Err(format!("\n\n\ Err(format!(
couldn't determine visual studio generator\n\ "\n\n\
if VisualStudio is installed, however, consider \ couldn't determine visual studio generator\n\
running the appropriate vcvars script before building \ if VisualStudio is installed, however, consider \
this crate\n\ running the appropriate vcvars script before building \
")) this crate\n\
"
))
} }
} }
} }
@ -185,7 +192,12 @@ mod impl_ {
} }
fn into_tool(self) -> Tool { fn into_tool(self) -> Tool {
let MsvcTool { tool, libs, path, include } = self; let MsvcTool {
tool,
libs,
path,
include,
} = self;
let mut tool = Tool::new(tool.into()); let mut tool = Tool::new(tool.into());
add_env(&mut tool, "LIB", libs); add_env(&mut tool, "LIB", libs);
add_env(&mut tool, "PATH", path); add_env(&mut tool, "PATH", path);
@ -217,11 +229,13 @@ mod impl_ {
None None
} }
fn tool_from_vs15_instance(tool: &str, target: &str, fn tool_from_vs15_instance(tool: &str, target: &str, instance: &SetupInstance) -> Option<Tool> {
instance: &SetupInstance) -> Option<Tool> { let (bin_path, host_dylib_path, lib_path, include_path) =
let (bin_path, host_dylib_path, lib_path, include_path) = otry!(vs15_vc_paths(target, instance)); otry!(vs15_vc_paths(target, instance));
let tool_path = bin_path.join(tool); let tool_path = bin_path.join(tool);
if !tool_path.exists() { return None }; if !tool_path.exists() {
return None;
};
let mut tool = MsvcTool::new(tool_path); let mut tool = MsvcTool::new(tool_path);
tool.path.push(host_dylib_path); tool.path.push(host_dylib_path);
@ -238,9 +252,13 @@ mod impl_ {
Some(tool.into_tool()) Some(tool.into_tool())
} }
fn vs15_vc_paths(target: &str, instance: &SetupInstance) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf)> { fn vs15_vc_paths(
target: &str,
instance: &SetupInstance,
) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf)> {
let instance_path: PathBuf = otry!(instance.installation_path().ok()).into(); 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 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_file = otry!(File::open(version_path).ok());
let mut version = String::new(); let mut version = String::new();
otry!(version_file.read_to_string(&mut version).ok()); otry!(version_file.read_to_string(&mut version).ok());
@ -255,11 +273,15 @@ mod impl_ {
let path = instance_path.join(r"VC\Tools\MSVC").join(version); let path = instance_path.join(r"VC\Tools\MSVC").join(version);
// This is the path to the toolchain for a particular target, running // This is the path to the toolchain for a particular target, running
// on a given host // on a given host
let bin_path = path.join("bin").join(&format!("Host{}", host)).join(&target); let bin_path = path.join("bin")
.join(&format!("Host{}", host))
.join(&target);
// But! we also need PATH to contain the target directory for the host // But! we also need PATH to contain the target directory for the host
// architecture, because it contains dlls like mspdb140.dll compiled for // architecture, because it contains dlls like mspdb140.dll compiled for
// the host architecture. // the host architecture.
let host_dylib_path = path.join("bin").join(&format!("Host{}", host)).join(&host.to_lowercase()); let host_dylib_path = path.join("bin")
.join(&format!("Host{}", host))
.join(&host.to_lowercase());
let lib_path = path.join("lib").join(&target); let lib_path = path.join("lib").join(&target);
let include_path = path.join("include"); let include_path = path.join("include");
Some((bin_path, host_dylib_path, lib_path, include_path)) Some((bin_path, host_dylib_path, lib_path, include_path))
@ -288,7 +310,8 @@ mod impl_ {
let sub = otry!(lib_subdir(target)); let sub = otry!(lib_subdir(target));
let (ucrt, ucrt_version) = otry!(get_ucrt_dir()); let (ucrt, ucrt_version) = otry!(get_ucrt_dir());
tool.path.push(ucrt.join("bin").join(&ucrt_version).join(sub)); tool.path
.push(ucrt.join("bin").join(&ucrt_version).join(sub));
let ucrt_include = ucrt.join("include").join(&ucrt_version); let ucrt_include = ucrt.join("include").join(&ucrt_version);
tool.include.push(ucrt_include.join("ucrt")); tool.include.push(ucrt_include.join("ucrt"));
@ -353,7 +376,8 @@ mod impl_ {
let prev = env::var_os(env).unwrap_or(OsString::new()); let prev = env::var_os(env).unwrap_or(OsString::new());
let prev = env::split_paths(&prev); let prev = env::split_paths(&prev);
let new = paths.into_iter().chain(prev); let new = paths.into_iter().chain(prev);
tool.env.push((env.to_string().into(), env::join_paths(new).unwrap())); tool.env
.push((env.to_string().into(), env::join_paths(new).unwrap()));
} }
// Given a possible MSVC installation directory, we look for the linker and // Given a possible MSVC installation directory, we look for the linker and
@ -361,7 +385,12 @@ mod impl_ {
fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> { fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> {
bin_subdir(target) bin_subdir(target)
.into_iter() .into_iter()
.map(|(sub, host)| (path.join("bin").join(sub).join(tool), path.join("bin").join(host))) .map(|(sub, host)| {
(
path.join("bin").join(sub).join(tool),
path.join("bin").join(host),
)
})
.filter(|&(ref path, _)| path.is_file()) .filter(|&(ref path, _)| path.is_file())
.map(|(path, host)| { .map(|(path, host)| {
let mut tool = MsvcTool::new(path); let mut tool = MsvcTool::new(path);
@ -402,16 +431,17 @@ mod impl_ {
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok()); let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
let root = otry!(key.query_str("KitsRoot10").ok()); let root = otry!(key.query_str("KitsRoot10").ok());
let readdir = otry!(Path::new(&root).join("lib").read_dir().ok()); let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
let max_libdir = otry!(readdir.filter_map(|dir| dir.ok()) let max_libdir = otry!(
.map(|dir| dir.path()) readdir
.filter(|dir| { .filter_map(|dir| dir.ok())
dir.components() .map(|dir| dir.path())
.filter(|dir| dir.components()
.last() .last()
.and_then(|c| c.as_os_str().to_str()) .and_then(|c| c.as_os_str().to_str())
.map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir()) .map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir())
.unwrap_or(false) .unwrap_or(false))
}) .max()
.max()); );
let version = max_libdir.components().last().unwrap(); let version = max_libdir.components().last().unwrap();
let version = version.as_os_str().to_str().unwrap().to_string(); let version = version.as_os_str().to_str().unwrap().to_string();
Some((root.into(), version)) Some((root.into(), version))
@ -430,14 +460,17 @@ mod impl_ {
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok()); let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
let root = otry!(key.query_str("InstallationFolder").ok()); let root = otry!(key.query_str("InstallationFolder").ok());
let readdir = otry!(Path::new(&root).join("lib").read_dir().ok()); let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
let mut dirs = readdir.filter_map(|dir| dir.ok()) let mut dirs = readdir
.filter_map(|dir| dir.ok())
.map(|dir| dir.path()) .map(|dir| dir.path())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
dirs.sort(); dirs.sort();
let dir = otry!(dirs.into_iter() let dir = otry!(
.rev() dirs.into_iter()
.filter(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file()) .rev()
.next()); .filter(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file())
.next()
);
let version = dir.components().last().unwrap(); let version = dir.components().last().unwrap();
let version = version.as_os_str().to_str().unwrap().to_string(); let version = version.as_os_str().to_str().unwrap().to_string();
Some((root.into(), version)) Some((root.into(), version))
@ -555,7 +588,8 @@ mod impl_ {
let mut max_vers = 0; let mut max_vers = 0;
let mut max_key = None; let mut max_key = None;
for subkey in key.iter().filter_map(|k| k.ok()) { for subkey in key.iter().filter_map(|k| k.ok()) {
let val = subkey.to_str() let val = subkey
.to_str()
.and_then(|s| s.trim_left_matches("v").replace(".", "").parse().ok()); .and_then(|s| s.trim_left_matches("v").replace(".", "").parse().ok());
let val = match val { let val = match val {
Some(s) => s, Some(s) => s,
@ -574,15 +608,16 @@ mod impl_ {
pub fn has_msbuild_version(version: &str) -> bool { pub fn has_msbuild_version(version: &str) -> bool {
match version { match version {
"15.0" => { "15.0" => {
find_msbuild_vs15("x86_64-pc-windows-msvc").is_some() || find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
find_msbuild_vs15("i686-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 "12.0" | "14.0" => LOCAL_MACHINE
.open(&OsString::from(format!(
"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
version
)))
.is_ok(),
_ => false,
} }
} }
@ -601,11 +636,10 @@ mod impl_ {
// or that find_msvc_15 could just use this registry key // or that find_msvc_15 could just use this registry key
// instead of the COM interface. // instead of the COM interface.
let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
LOCAL_MACHINE.open(key.as_ref()) LOCAL_MACHINE
.open(key.as_ref())
.ok() .ok()
.and_then(|key| { .and_then(|key| key.query_str("15.0").ok())
key.query_str("15.0").ok()
})
.map(|path| { .map(|path| {
let path = PathBuf::from(path).join(r"MSBuild\15.0\Bin\MSBuild.exe"); let path = PathBuf::from(path).join(r"MSBuild\15.0\Bin\MSBuild.exe");
let mut tool = Tool::new(path); let mut tool = Tool::new(path);
@ -618,7 +652,8 @@ mod impl_ {
fn find_old_msbuild(target: &str) -> Option<Tool> { fn find_old_msbuild(target: &str) -> Option<Tool> {
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions"; let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
LOCAL_MACHINE.open(key.as_ref()) LOCAL_MACHINE
.open(key.as_ref())
.ok() .ok()
.and_then(|key| { .and_then(|key| {
max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok()) max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok())

19
tests/support/mod.rs

@ -85,7 +85,9 @@ impl Test {
.unwrap() .unwrap()
.read_to_string(&mut s) .read_to_string(&mut s)
.unwrap(); .unwrap();
Execution { args: s.lines().map(|s| s.to_string()).collect() } Execution {
args: s.lines().map(|s| s.to_string()).collect(),
}
} }
} }
@ -111,11 +113,18 @@ impl Execution {
} }
pub fn must_have_in_order(&self, before: &str, after: &str) -> &Execution { pub fn must_have_in_order(&self, before: &str, after: &str) -> &Execution {
let before_position = self.args.iter().rposition(|x| OsStr::new(x) == OsStr::new(before)); let before_position = self.args
let after_position = self.args.iter().rposition(|x| OsStr::new(x) == OsStr::new(after)); .iter()
.rposition(|x| OsStr::new(x) == OsStr::new(before));
let after_position = self.args
.iter()
.rposition(|x| OsStr::new(x) == OsStr::new(after));
match (before_position, after_position) { match (before_position, after_position) {
(Some(b), Some(a)) if b < a => {}, (Some(b), Some(a)) if b < a => {}
(b, a) => { panic!("{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})", before, b, after, a) }, (b, a) => panic!(
"{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})",
before, b, after, a
),
}; };
self self
} }

89
tests/test.rs

@ -8,9 +8,7 @@ mod support;
#[test] #[test]
fn gnu_smoke() { fn gnu_smoke() {
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc().file("foo.c").compile("foo");
.file("foo.c")
.compile("foo");
test.cmd(0) test.cmd(0)
.must_have("-O2") .must_have("-O2")
@ -25,23 +23,15 @@ fn gnu_smoke() {
#[test] #[test]
fn gnu_opt_level_1() { fn gnu_opt_level_1() {
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc().opt_level(1).file("foo.c").compile("foo");
.opt_level(1)
.file("foo.c")
.compile("foo");
test.cmd(0) test.cmd(0).must_have("-O1").must_not_have("-O2");
.must_have("-O1")
.must_not_have("-O2");
} }
#[test] #[test]
fn gnu_opt_level_s() { fn gnu_opt_level_s() {
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc().opt_level_str("s").file("foo.c").compile("foo");
.opt_level_str("s")
.file("foo.c")
.compile("foo");
test.cmd(0) test.cmd(0)
.must_have("-Os") .must_have("-Os")
@ -54,10 +44,7 @@ fn gnu_opt_level_s() {
#[test] #[test]
fn gnu_debug() { fn gnu_debug() {
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc().debug(true).file("foo.c").compile("foo");
.debug(true)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("-g"); test.cmd(0).must_have("-g");
} }
@ -81,8 +68,7 @@ fn gnu_warnings() {
.file("foo.c") .file("foo.c")
.compile("foo"); .compile("foo");
test.cmd(0).must_have("-Wall") test.cmd(0).must_have("-Wall").must_have("-Wextra");
.must_have("-Wextra");
} }
#[test] #[test]
@ -94,7 +80,8 @@ fn gnu_warnings_overridable() {
.file("foo.c") .file("foo.c")
.compile("foo"); .compile("foo");
test.cmd(0).must_have_in_order("-Wall", "-Wno-missing-field-initializers"); test.cmd(0)
.must_have_in_order("-Wall", "-Wno-missing-field-initializers");
} }
#[test] #[test]
@ -108,9 +95,7 @@ fn gnu_x86_64() {
.file("foo.c") .file("foo.c")
.compile("foo"); .compile("foo");
test.cmd(0) test.cmd(0).must_have("-fPIC").must_have("-m64");
.must_have("-fPIC")
.must_have("-m64");
} }
} }
@ -141,8 +126,7 @@ fn gnu_i686() {
.file("foo.c") .file("foo.c")
.compile("foo"); .compile("foo");
test.cmd(0) test.cmd(0).must_have("-m32");
.must_have("-m32");
} }
} }
@ -176,10 +160,7 @@ fn gnu_set_stdlib() {
#[test] #[test]
fn gnu_include() { fn gnu_include() {
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc().include("foo/bar").file("foo.c").compile("foo");
.include("foo/bar")
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("-I").must_have("foo/bar"); test.cmd(0).must_have("-I").must_have("foo/bar");
} }
@ -199,9 +180,7 @@ fn gnu_define() {
#[test] #[test]
fn gnu_compile_assembly() { fn gnu_compile_assembly() {
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc().file("foo.S").compile("foo");
.file("foo.S")
.compile("foo");
test.cmd(0).must_have("foo.S"); test.cmd(0).must_have("foo.S");
} }
@ -214,15 +193,13 @@ fn gnu_shared() {
.static_flag(false) .static_flag(false)
.compile("foo"); .compile("foo");
test.cmd(0) test.cmd(0).must_have("-shared").must_not_have("-static");
.must_have("-shared")
.must_not_have("-static");
} }
#[test] #[test]
fn gnu_flag_if_supported() { fn gnu_flag_if_supported() {
if cfg!(windows) { if cfg!(windows) {
return return;
} }
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc()
@ -241,7 +218,7 @@ fn gnu_flag_if_supported() {
#[test] #[test]
fn gnu_flag_if_supported_cpp() { fn gnu_flag_if_supported_cpp() {
if cfg!(windows) { if cfg!(windows) {
return return;
} }
let test = Test::gnu(); let test = Test::gnu();
test.gcc() test.gcc()
@ -250,8 +227,7 @@ fn gnu_flag_if_supported_cpp() {
.flag_if_supported("-std=c++11") .flag_if_supported("-std=c++11")
.compile("foo"); .compile("foo");
test.cmd(0) test.cmd(0).must_have("-std=c++11");
.must_have("-std=c++11");
} }
#[test] #[test]
@ -263,17 +239,13 @@ fn gnu_static() {
.static_flag(true) .static_flag(true)
.compile("foo"); .compile("foo");
test.cmd(0) test.cmd(0).must_have("-static").must_not_have("-shared");
.must_have("-static")
.must_not_have("-shared");
} }
#[test] #[test]
fn msvc_smoke() { fn msvc_smoke() {
let test = Test::msvc(); let test = Test::msvc();
test.gcc() test.gcc().file("foo.c").compile("foo");
.file("foo.c")
.compile("foo");
test.cmd(0) test.cmd(0)
.must_have("/O2") .must_have("/O2")
@ -287,10 +259,7 @@ fn msvc_smoke() {
#[test] #[test]
fn msvc_opt_level_0() { fn msvc_opt_level_0() {
let test = Test::msvc(); let test = Test::msvc();
test.gcc() test.gcc().opt_level(0).file("foo.c").compile("foo");
.opt_level(0)
.file("foo.c")
.compile("foo");
test.cmd(0).must_not_have("/O2"); test.cmd(0).must_not_have("/O2");
} }
@ -298,20 +267,14 @@ fn msvc_opt_level_0() {
#[test] #[test]
fn msvc_debug() { fn msvc_debug() {
let test = Test::msvc(); let test = Test::msvc();
test.gcc() test.gcc().debug(true).file("foo.c").compile("foo");
.debug(true)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("/Z7"); test.cmd(0).must_have("/Z7");
} }
#[test] #[test]
fn msvc_include() { fn msvc_include() {
let test = Test::msvc(); let test = Test::msvc();
test.gcc() test.gcc().include("foo/bar").file("foo.c").compile("foo");
.include("foo/bar")
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("/I").must_have("foo/bar"); test.cmd(0).must_have("/I").must_have("foo/bar");
} }
@ -331,10 +294,7 @@ fn msvc_define() {
#[test] #[test]
fn msvc_static_crt() { fn msvc_static_crt() {
let test = Test::msvc(); let test = Test::msvc();
test.gcc() test.gcc().static_crt(true).file("foo.c").compile("foo");
.static_crt(true)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("/MT"); test.cmd(0).must_have("/MT");
} }
@ -342,10 +302,7 @@ fn msvc_static_crt() {
#[test] #[test]
fn msvc_no_static_crt() { fn msvc_no_static_crt() {
let test = Test::msvc(); let test = Test::msvc();
test.gcc() test.gcc().static_crt(false).file("foo.c").compile("foo");
.static_crt(false)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("/MD"); test.cmd(0).must_have("/MD");
} }

Loading…
Cancel
Save