diff options
Diffstat (limited to 'cc')
-rw-r--r-- | cc/.travis.yml | 23 | ||||
-rw-r--r-- | cc/Cargo.toml | 5 | ||||
-rw-r--r-- | cc/README.md | 2 | ||||
-rw-r--r-- | cc/cc-test/build.rs | 20 | ||||
-rw-r--r-- | cc/cc-test/src/aarch64.S | 10 | ||||
-rw-r--r-- | cc/src/com.rs | 56 | ||||
-rw-r--r-- | cc/src/lib.rs | 709 | ||||
-rw-r--r-- | cc/src/registry.rs | 112 | ||||
-rw-r--r-- | cc/src/setup_config.rs | 62 | ||||
-rw-r--r-- | cc/src/winapi.rs | 16 | ||||
-rw-r--r-- | cc/src/windows_registry.rs | 186 | ||||
-rw-r--r-- | cc/tests/cc_env.rs | 46 | ||||
-rw-r--r-- | cc/tests/support/mod.rs | 19 | ||||
-rw-r--r-- | cc/tests/test.rs | 142 |
14 files changed, 951 insertions, 457 deletions
diff --git a/cc/.travis.yml b/cc/.travis.yml index 6f3c53d..70349e6 100644 --- a/cc/.travis.yml +++ b/cc/.travis.yml @@ -3,7 +3,7 @@ sudo: false matrix: include: - - rust: 1.13.0 + - rust: 1.16.0 install: script: cargo build - rust: stable @@ -12,6 +12,8 @@ matrix: env: TARGET=i686-unknown-linux-gnu - os: osx env: TARGET=x86_64-apple-darwin NO_ADD=1 + - os: osx + env: TARGET=aarch64-apple-ios NO_RUN=--no-run TARGET_SYSROOT=$(xcrun -sdk iphoneos --show-sdk-path) - rust: beta env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1 - rust: nightly @@ -31,11 +33,20 @@ install: script: - cargo build --verbose - - cargo test --verbose - - cargo test --verbose --features parallel - - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET - - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --features parallel - - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --release + # FIXME: no idea why `--test-threads=1` is required on the OSX builder, it + # just randomly broke one day when the travis image was upgraded, and + # debugging turned up no easily found source of bugs... + # + # good build - https://travis-ci.org/alexcrichton/cc-rs/builds/409602374 + # bad build - https://travis-ci.org/alexcrichton/cc-rs/builds/410489079 + # + # Those are using the same compiler, same commit, same... everything. Except + # the OSX image! No idea what changed... + - cargo test --verbose $NO_RUN -- --test-threads=1 + - cargo test --verbose --features parallel $NO_RUN -- --test-threads=1 + - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET $NO_RUN + - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --features parallel $NO_RUN + - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --release $NO_RUN - cargo doc - cargo clean && cargo build - rustdoc --test README.md -L target/debug -L target/debug/deps diff --git a/cc/Cargo.toml b/cc/Cargo.toml index cd87a6f..f5004be 100644 --- a/cc/Cargo.toml +++ b/cc/Cargo.toml @@ -1,7 +1,6 @@ [package] - name = "cc" -version = "1.0.4" +version = "1.0.25" authors = ["Alex Crichton <alex@alexcrichton.com>"] license = "MIT/Apache-2.0" repository = "https://github.com/alexcrichton/cc-rs" @@ -21,7 +20,7 @@ travis-ci = { repository = "alexcrichton/cc-rs" } appveyor = { repository = "alexcrichton/cc-rs" } [dependencies] -rayon = { version = "0.9", optional = true } +rayon = { version = "1.0", optional = true } [features] parallel = ["rayon"] diff --git a/cc/README.md b/cc/README.md index 94efa43..f65d0c0 100644 --- a/cc/README.md +++ b/cc/README.md @@ -198,5 +198,5 @@ at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in Serde by you, as defined in the Apache-2.0 license, shall be +for inclusion in cc-rs by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/cc/cc-test/build.rs b/cc/cc-test/build.rs index 5a7b178..677161e 100644 --- a/cc/cc-test/build.rs +++ b/cc/cc-test/build.rs @@ -25,12 +25,12 @@ fn main() { let target = std::env::var("TARGET").unwrap(); let file = target.split("-").next().unwrap(); - let file = format!("src/{}.{}", - file, - if target.contains("msvc") { "asm" } else { "S" }); - cc::Build::new() - .file(file) - .compile("asm"); + let file = format!( + "src/{}.{}", + file, + if target.contains("msvc") { "asm" } else { "S" } + ); + cc::Build::new().file(file).compile("asm"); cc::Build::new() .file("src/baz.cpp") @@ -38,9 +38,7 @@ fn main() { .compile("baz"); if target.contains("windows") { - cc::Build::new() - .file("src/windows.c") - .compile("windows"); + cc::Build::new().file("src/windows.c").compile("windows"); } // Test that the `windows_registry` module will set PATH by looking for @@ -86,9 +84,7 @@ fn main() { .file("src/opt_linkage.c") .compile("OptLinkage"); - let out = cc::Build::new() - .file("src/expand.c") - .expand(); + let out = cc::Build::new().file("src/expand.c").expand(); let out = String::from_utf8(out).unwrap(); assert!(out.contains("hello world")); } diff --git a/cc/cc-test/src/aarch64.S b/cc/cc-test/src/aarch64.S new file mode 100644 index 0000000..1d9062d --- /dev/null +++ b/cc/cc-test/src/aarch64.S @@ -0,0 +1,10 @@ +.globl asm +asm: + mov w0, 7 + ret + +.globl _asm +_asm: + mov w0, 7 + ret + diff --git a/cc/src/com.rs b/cc/src/com.rs index bd8cce7..2b16475 100644 --- a/cc/src/com.rs +++ b/cc/src/com.rs @@ -19,7 +19,7 @@ use winapi::CoInitializeEx; use winapi::COINIT_MULTITHREADED; use winapi::{SysFreeString, SysStringLen}; use winapi::IUnknown; -use winapi::{S_OK, S_FALSE, HRESULT}; +use winapi::{HRESULT, S_FALSE, S_OK}; pub fn initialize() -> Result<(), HRESULT> { let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) }; @@ -30,8 +30,13 @@ pub fn initialize() -> Result<(), HRESULT> { Ok(()) } -pub struct ComPtr<T>(*mut T) where T: Interface; -impl<T> ComPtr<T> where T: Interface { +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`. @@ -40,7 +45,11 @@ impl<T> ComPtr<T> where T: Interface { ComPtr(ptr) } /// 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) } /// Extracts the raw pointer. @@ -55,20 +64,31 @@ impl<T> ComPtr<T> where T: Interface { unsafe { &*(self.0 as *mut IUnknown) } } /// 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 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) }) } } -impl<T> Deref for ComPtr<T> where T: Interface { +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 { +impl<T> Clone for ComPtr<T> +where + T: Interface, +{ fn clone(&self) -> Self { unsafe { 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) { - unsafe { self.as_unknown().Release(); } + unsafe { + self.as_unknown().Release(); + } } } pub struct BStr(BSTR); @@ -102,7 +127,10 @@ 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> { +impl<T> ToWide for T +where + T: AsRef<OsStr>, +{ fn to_wide(&self) -> Vec<u16> { 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() } } -pub trait FromWide where Self: Sized { +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(); @@ -122,4 +153,3 @@ impl FromWide for OsString { OsStringExt::from_wide(wide) } } - diff --git a/cc/src/lib.rs b/cc/src/lib.rs index 67d8f6f..7672cf4 100644 --- a/cc/src/lib.rs +++ b/cc/src/lib.rs @@ -61,15 +61,14 @@ extern crate rayon; use std::env; -use std::ffi::{OsString, OsStr}; +use std::ffi::{OsStr, OsString}; use std::fs; -use std::path::{PathBuf, Path}; -use std::process::{Command, Stdio, Child}; -use std::io::{self, BufReader, BufRead, Read, Write}; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, Stdio}; +use std::io::{self, BufRead, BufReader, Read, Write}; use std::thread::{self, JoinHandle}; - -#[cfg(feature = "parallel")] -use std::sync::Mutex; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; // These modules are all glue to support reading the MSVC version from // the registry and from COM interfaces @@ -97,6 +96,7 @@ pub struct Build { objects: Vec<PathBuf>, flags: Vec<String>, flags_supported: Vec<String>, + known_flag_support_status: Arc<Mutex<HashMap<String, bool>>>, files: Vec<PathBuf>, cpp: bool, cpp_link_stdlib: Option<Option<String>>, @@ -116,7 +116,9 @@ pub struct Build { shared_flag: Option<bool>, static_flag: Option<bool>, warnings_into_errors: bool, - warnings: bool, + warnings: Option<bool>, + extra_warnings: Option<bool>, + env_cache: Arc<Mutex<HashMap<String, Option<String>>>>, } /// Represents the types of errors that may occur while using cc-rs. @@ -174,6 +176,7 @@ pub struct Tool { env: Vec<(OsString, OsString)>, family: ToolFamily, cuda: bool, + removed_args: Vec<OsString>, } /// Represents the family of tools this tool belongs to. @@ -189,22 +192,27 @@ enum ToolFamily { /// and its cross-compilation approach is different. Clang, /// Tool is the MSVC cl.exe. - Msvc, + Msvc { clang_cl: bool }, } impl ToolFamily { /// What the flag to request debug info for this family of tools look like - fn debug_flag(&self) -> &'static str { + fn add_debug_flags(&self, cmd: &mut Tool) { match *self { - ToolFamily::Msvc => "/Z7", - ToolFamily::Gnu | ToolFamily::Clang => "-g", + ToolFamily::Msvc { .. } => { + cmd.push_cc_arg("/Z7".into()); + } + ToolFamily::Gnu | ToolFamily::Clang => { + cmd.push_cc_arg("-g".into()); + cmd.push_cc_arg("-fno-omit-frame-pointer".into()); + } } } /// What the flag to include directories into header search path looks like fn include_flag(&self) -> &'static str { match *self { - ToolFamily::Msvc => "/I", + ToolFamily::Msvc { .. } => "/I", ToolFamily::Gnu | ToolFamily::Clang => "-I", } } @@ -212,26 +220,31 @@ impl ToolFamily { /// What the flag to request macro-expanded source output looks like fn expand_flag(&self) -> &'static str { match *self { - ToolFamily::Msvc => "/E", + ToolFamily::Msvc { .. } => "/E", ToolFamily::Gnu | ToolFamily::Clang => "-E", } } /// What the flags to enable all warnings - fn warnings_flags(&self) -> &'static [&'static str] { - static MSVC_FLAGS: &'static [&'static str] = &["/W4"]; - static GNU_CLANG_FLAGS: &'static [&'static str] = &["-Wall", "-Wextra"]; + fn warnings_flags(&self) -> &'static str { + match *self { + ToolFamily::Msvc { .. } => "/W4", + ToolFamily::Gnu | ToolFamily::Clang => "-Wall", + } + } + /// What the flags to enable extra warnings + fn extra_warnings_flags(&self) -> Option<&'static str> { match *self { - ToolFamily::Msvc => &MSVC_FLAGS, - ToolFamily::Gnu | ToolFamily::Clang => &GNU_CLANG_FLAGS, + ToolFamily::Msvc { .. } => None, + ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"), } } /// What the flag to turn warning into errors fn warnings_to_errors_flag(&self) -> &'static str { match *self { - ToolFamily::Msvc => "/WX", + ToolFamily::Msvc { .. } => "/WX", ToolFamily::Gnu | ToolFamily::Clang => "-Werror", } } @@ -240,9 +253,8 @@ impl ToolFamily { /// debug info flag passed to the C++ compiler. fn nvcc_debug_flag(&self) -> &'static str { match *self { - ToolFamily::Msvc => unimplemented!(), - ToolFamily::Gnu | - ToolFamily::Clang => "-G", + ToolFamily::Msvc { .. } => unimplemented!(), + ToolFamily::Gnu | ToolFamily::Clang => "-G", } } @@ -250,11 +262,14 @@ impl ToolFamily { /// compiler. fn nvcc_redirect_flag(&self) -> &'static str { match *self { - ToolFamily::Msvc => unimplemented!(), - ToolFamily::Gnu | - ToolFamily::Clang => "-Xcompiler", + ToolFamily::Msvc { .. } => unimplemented!(), + ToolFamily::Gnu | ToolFamily::Clang => "-Xcompiler", } } + + fn verbose_stderr(&self) -> bool { + *self == ToolFamily::Clang + } } /// Represents an object. @@ -269,10 +284,7 @@ struct Object { impl Object { /// Create a new source file -> object file pair. fn new(src: PathBuf, dst: PathBuf) -> Object { - Object { - src: src, - dst: dst, - } + Object { src: src, dst: dst } } } @@ -289,6 +301,7 @@ impl Build { objects: Vec::new(), flags: Vec::new(), flags_supported: Vec::new(), + known_flag_support_status: Arc::new(Mutex::new(HashMap::new())), files: Vec::new(), shared_flag: None, static_flag: None, @@ -307,8 +320,10 @@ impl Build { cargo_metadata: true, pic: None, static_crt: None, - warnings: true, + warnings: None, + extra_warnings: None, warnings_into_errors: false, + env_cache: Arc::new(Mutex::new(HashMap::new())), } } @@ -344,10 +359,8 @@ impl Build { /// .compile("foo"); /// ``` pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build { - self.definitions.push(( - var.to_string(), - val.into().map(|s| s.to_string()), - )); + self.definitions + .push((var.to_string(), val.into().map(|s| s.to_string()))); self } @@ -398,22 +411,40 @@ impl Build { /// /// It may return error if it's unable to run the compilier with a test file /// (e.g. the compiler is missing or a write to the `out_dir` failed). + /// + /// Note: Once computed, the result of this call is stored in the + /// `known_flag_support` field. If `is_flag_supported(flag)` + /// is called again, the result will be read from the hash table. pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> { + let mut known_status = self.known_flag_support_status.lock().unwrap(); + if let Some(is_supported) = known_status.get(flag).cloned() { + return Ok(is_supported); + } + let out_dir = self.get_out_dir()?; let src = self.ensure_check_file()?; let obj = out_dir.join("flag_check"); let target = self.get_target()?; + let host = self.get_host()?; let mut cfg = Build::new(); cfg.flag(flag) .target(&target) .opt_level(0) - .host(&target) + .host(&host) .debug(false) .cpp(self.cpp) .cuda(self.cuda); - let compiler = cfg.try_get_compiler()?; + let mut compiler = cfg.try_get_compiler()?; + + // Clang uses stderr for verbose output, which yields a false positive + // result if the CFLAGS/CXXFLAGS include -v to aid in debugging. + if compiler.family.verbose_stderr() { + compiler.remove_arg("-v".into()); + } + let mut cmd = compiler.to_command(); - command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false); + let is_arm = target.contains("aarch64") || target.contains("arm"); + command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false, is_arm); // We need to explicitly tell msvc not to link and create an exe // in the root directory of the crate @@ -424,7 +455,10 @@ impl Build { cmd.arg(&src); let output = cmd.output()?; - Ok(output.stderr.is_empty()) + let is_supported = output.stderr.is_empty(); + + known_status.insert(flag.to_owned(), is_supported); + Ok(is_supported) } /// Add an arbitrary flag to the invocation of the compiler if it supports it @@ -565,7 +599,30 @@ impl Build { /// .compile("libfoo.a"); /// ``` pub fn warnings(&mut self, warnings: bool) -> &mut Build { - self.warnings = warnings; + self.warnings = Some(warnings); + self.extra_warnings = Some(warnings); + self + } + + /// Set extra warnings flags. + /// + /// Adds some flags: + /// - nothing for MSVC. + /// - "-Wextra" for GNU and Clang. + /// + /// Enabled by default. + /// + /// # Example + /// + /// ```no_run + /// // Disables -Wextra, -Wall remains enabled: + /// cc::Build::new() + /// .file("src/foo.c") + /// .extra_warnings(false) + /// .compile("libfoo.a"); + /// ``` + pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build { + self.extra_warnings = Some(warnings); self } @@ -575,6 +632,8 @@ impl Build { /// The default value of this property depends on the current target: On /// OS X `Some("c++")` is used, when compiling for a Visual Studio based /// target `None` is used and for other targets `Some("stdc++")` is used. + /// If the `CXXSTDLIB` environment variable is set, its value will + /// override the default value. /// /// A value of `None` indicates that no automatic linking should happen, /// otherwise cargo will link against the specified library. @@ -777,9 +836,8 @@ impl Build { A: AsRef<OsStr>, B: AsRef<OsStr>, { - self.env.push( - (a.as_ref().to_owned(), b.as_ref().to_owned()), - ); + self.env + .push((a.as_ref().to_owned(), b.as_ref().to_owned())); self } @@ -880,31 +938,19 @@ impl Build { fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { use self::rayon::prelude::*; - let mut cfg = rayon::Configuration::new(); if let Ok(amt) = env::var("NUM_JOBS") { if let Ok(amt) = amt.parse() { - cfg = cfg.num_threads(amt); + let _ = rayon::ThreadPoolBuilder::new() + .num_threads(amt) + .build_global(); } } - drop(rayon::initialize(cfg)); - - let results: Mutex<Vec<Result<(), Error>>> = Mutex::new(Vec::new()); - - objs.par_iter().with_max_len(1).for_each( - |obj| { - let res = self.compile_object(obj); - results.lock().unwrap().push(res) - }, - ); // Check for any errors and return the first one found. - for result in results.into_inner().unwrap().iter() { - if result.is_err() { - return result.clone(); - } - } - - Ok(()) + objs.par_iter() + .with_max_len(1) + .map(|obj| self.compile_object(obj)) + .collect() } #[cfg(not(feature = "parallel"))] @@ -917,7 +963,8 @@ impl Build { fn compile_object(&self, obj: &Object) -> Result<(), Error> { let is_asm = obj.src.extension().and_then(|s| s.to_str()) == Some("asm"); - let msvc = self.get_target()?.contains("msvc"); + let target = self.get_target()?; + let msvc = target.contains("msvc"); let (mut cmd, name) = if msvc && is_asm { self.msvc_macro_assembler()? } else { @@ -931,15 +978,17 @@ impl Build { compiler .path .file_name() - .ok_or_else(|| { - Error::new(ErrorKind::IOError, "Failed to get compiler path.") - })? + .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))? .to_string_lossy() .into_owned(), ) }; - command_add_output_file(&mut cmd, &obj.dst, msvc, is_asm); - cmd.arg(if msvc { "/c" } else { "-c" }); + let is_arm = target.contains("aarch64") || target.contains("arm"); + command_add_output_file(&mut cmd, &obj.dst, msvc, is_asm, is_arm); + // armasm and armasm64 don't requrie -c option + if !msvc || !is_asm || !is_arm { + cmd.arg(if msvc { "/c" } else { "-c" }); + } cmd.arg(&obj.src); run(&mut cmd, &name)?; @@ -967,9 +1016,7 @@ impl Build { let name = compiler .path .file_name() - .ok_or_else(|| { - Error::new(ErrorKind::IOError, "Failed to get compiler path.") - })? + .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))? .to_string_lossy() .into_owned(); @@ -1032,7 +1079,7 @@ impl Build { // Non-target flags // If the flag is not conditioned on target variable, it belongs here :) match cmd.family { - ToolFamily::Msvc => { + ToolFamily::Msvc { .. } => { assert!(!self.cuda, "CUDA C++ compilation not supported for MSVC, yet... but you are welcome to implement it :)"); @@ -1054,8 +1101,8 @@ impl Build { cmd.args.push(crt_flag.into()); match &opt_level[..] { - "z" | "s" => cmd.args.push("/Os".into()), - "1" => cmd.args.push("/O1".into()), + // Msvc uses /O1 to enable all optimizations that minimize code size. + "z" | "s" | "1" => cmd.args.push("/O1".into()), // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. "2" | "3" => cmd.args.push("/O2".into()), _ => {} @@ -1070,8 +1117,10 @@ impl Build { cmd.args.push(format!("-O{}", opt_level).into()); } - cmd.push_cc_arg("-ffunction-sections".into()); - cmd.push_cc_arg("-fdata-sections".into()); + if !target.contains("-ios") { + cmd.push_cc_arg("-ffunction-sections".into()); + cmd.push_cc_arg("-fdata-sections".into()); + } if self.pic.unwrap_or(!target.contains("windows-gnu")) { cmd.push_cc_arg("-fPIC".into()); } @@ -1086,8 +1135,8 @@ impl Build { let nvcc_debug_flag = cmd.family.nvcc_debug_flag().into(); cmd.args.push(nvcc_debug_flag); } - let debug_flag = cmd.family.debug_flag().into(); - cmd.push_cc_arg(debug_flag); + let family = cmd.family; + family.add_debug_flags(&mut cmd); } // Target flags @@ -1095,9 +1144,20 @@ impl Build { ToolFamily::Clang => { cmd.args.push(format!("--target={}", target).into()); } - ToolFamily::Msvc => { - if target.contains("i586") { - cmd.args.push("/ARCH:IA32".into()); + ToolFamily::Msvc { clang_cl } => { + if clang_cl { + if target.contains("x86_64") { + cmd.args.push("-m64".into()); + } else if target.contains("i586") { + cmd.args.push("-m32".into()); + cmd.args.push("/arch:IA32".into()); + } else { + cmd.args.push("-m32".into()); + } + } else { + if target.contains("i586") { + cmd.args.push("/ARCH:IA32".into()); + } } } ToolFamily::Gnu => { @@ -1109,24 +1169,35 @@ impl Build { cmd.args.push("-m64".into()); } - if self.static_flag.is_none() && target.contains("musl") { - cmd.args.push("-static".into()); + if self.static_flag.is_none() { + let features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new()); + if features.contains("crt-static") { + cmd.args.push("-static".into()); + } } // armv7 targets get to use armv7 instructions - if target.starts_with("armv7-") && target.contains("-linux-") { + if (target.starts_with("armv7") || target.starts_with("thumbv7")) && target.contains("-linux-") { cmd.args.push("-march=armv7-a".into()); } - // On android we can guarantee some extra float instructions - // (specified in the android spec online) - if target.starts_with("armv7-linux-androideabi") { - cmd.args.push("-march=armv7-a".into()); + // (x86 Android doesn't say "eabi") + if target.contains("-androideabi") && target.contains("v7") { + // -march=armv7-a handled above cmd.args.push("-mthumb".into()); - cmd.args.push("-mfpu=vfpv3-d16".into()); + if !target.contains("neon") { + // On android we can guarantee some extra float instructions + // (specified in the android spec online) + // NEON guarantees even more; see below. + cmd.args.push("-mfpu=vfpv3-d16".into()); + } cmd.args.push("-mfloat-abi=softfp".into()); } + if target.contains("neon") { + cmd.args.push("-mfpu=neon-vfpv4".into()); + } + if target.starts_with("armv4t-unknown-linux-") { cmd.args.push("-march=armv4t".into()); cmd.args.push("-marm".into()); @@ -1169,7 +1240,7 @@ impl Build { // linker that we're generating 32-bit executables as well. This'll // typically only be used for build scripts which transitively use // these flags that try to compile executables. - if target == "i686-unknown-linux-musl" { + if target == "i686-unknown-linux-musl" || target == "i586-unknown-linux-musl" { cmd.args.push("-Wl,-melf_i386".into()); } @@ -1193,6 +1264,31 @@ impl Build { if target.starts_with("thumbv7m") { cmd.args.push("-march=armv7-m".into()); } + if target.starts_with("armebv7r") | target.starts_with("armv7r") { + if target.starts_with("armeb") { + cmd.args.push("-mbig-endian".into()); + } else { + cmd.args.push("-mlittle-endian".into()); + } + + // ARM mode + cmd.args.push("-marm".into()); + + // R Profile + cmd.args.push("-march=armv7-r".into()); + + if target.ends_with("eabihf") { + // Calling convention + cmd.args.push("-mfloat-abi=hard".into()); + + // lowest common denominator FPU + // (see Cortex-R4 technical reference manual) + cmd.args.push("-mfpu=vfpv3-d16".into()) + } else { + // Calling convention + cmd.args.push("-mfloat-abi=soft".into()); + } + } } } @@ -1212,14 +1308,13 @@ impl Build { if self.cpp { match (self.cpp_set_stdlib.as_ref(), cmd.family) { (None, _) => {} - (Some(stdlib), ToolFamily::Gnu) | - (Some(stdlib), ToolFamily::Clang) => { + (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => { cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into()); } _ => { println!( "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \ - does not support this option, ignored", + does not support this option, ignored", cmd.family ); } @@ -1231,9 +1326,19 @@ impl Build { cmd.args.push(directory.into()); } - if self.warnings { - for flag in cmd.family.warnings_flags().iter() { - cmd.push_cc_arg(flag.into()); + // If warnings and/or extra_warnings haven't been explicitly set, + // then we set them only if the environment doesn't already have + // CFLAGS/CXXFLAGS, since those variables presumably already contain + // the desired set of warnings flags. + + if self.warnings.unwrap_or(if self.has_flags() { false } else { true }) { + let wflags = cmd.family.warnings_flags().into(); + cmd.push_cc_arg(wflags); + } + + if self.extra_warnings.unwrap_or(if self.has_flags() { false } else { true }) { + if let Some(wflags) = cmd.family.extra_warnings_flags() { + cmd.push_cc_arg(wflags.into()); } } @@ -1248,7 +1353,7 @@ impl Build { } for &(ref key, ref value) in self.definitions.iter() { - let lead = if let ToolFamily::Msvc = cmd.family { + let lead = if let ToolFamily::Msvc { .. } = cmd.family { "/" } else { "-" @@ -1268,10 +1373,20 @@ impl Build { Ok(cmd) } + fn has_flags(&self) -> bool { + let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" }; + let flags_env_var_value = self.get_var(flags_env_var_name); + if let Ok(_) = flags_env_var_value { true } else { false } + } + fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> { let target = self.get_target()?; let tool = if target.contains("x86_64") { "ml64.exe" + } else if target.contains("arm") { + "armasm.exe" + } else if target.contains("aarch64") { + "armasm64.exe" } else { "ml.exe" }; @@ -1307,20 +1422,55 @@ impl Build { 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_else( - || { - 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); - run( - cmd.arg(out).arg("/nologo").args(&objects).args(&self.objects), - "lib.exe", - )?; + cmd.arg(out).arg("/nologo"); + + // Similar to https://github.com/rust-lang/rust/pull/47507 + // and https://github.com/rust-lang/rust/pull/48548 + let estimated_command_line_len = objects + .iter() + .chain(&self.objects) + .map(|a| a.as_os_str().len()) + .sum::<usize>(); + if estimated_command_line_len > 1024 * 6 { + let mut args = String::from("\u{FEFF}"); // BOM + for arg in objects.iter().chain(&self.objects) { + args.push('"'); + for c in arg.to_str().unwrap().chars() { + if c == '"' { + args.push('\\') + } + args.push(c) + } + args.push('"'); + args.push('\n'); + } + + let mut utf16le = Vec::new(); + for code_unit in args.encode_utf16() { + utf16le.push(code_unit as u8); + utf16le.push((code_unit >> 8) as u8); + } + + let mut args_file = OsString::from(dst); + args_file.push(".args"); + fs::File::create(&args_file) + .unwrap() + .write_all(&utf16le) + .unwrap(); + + let mut args_file_arg = OsString::from("@"); + args_file_arg.push(args_file); + cmd.arg(args_file_arg); + } else { + cmd.args(&objects).args(&self.objects); + } + run(&mut cmd, "lib.exe")?; // The Rust compiler will look for libfoo.a and foo.lib, but the // MSVC linker will also be passed foo.lib, so be sure that both @@ -1412,6 +1562,18 @@ impl Build { cmd.args.push("-isysroot".into()); cmd.args.push(sdk_path.trim().into()); + cmd.args.push("-fembed-bitcode".into()); + /* + * TODO we probably ultimatedly want the -fembed-bitcode-marker flag + * but can't have it now because of an issue in LLVM: + * https://github.com/alexcrichton/cc-rs/issues/301 + * https://github.com/rust-lang/rust/pull/48896#comment-372192660 + */ + /* + if self.get_opt_level()? == "0" { + cmd.args.push("-fembed-bitcode-marker".into()); + } + */ Ok(()) } @@ -1430,44 +1592,53 @@ impl Build { } let host = self.get_host()?; let target = self.get_target()?; - let (env, msvc, gnu, traditional) = if self.cpp { - ("CXX", "cl.exe", "g++", "c++") + let (env, msvc, gnu, traditional, clang) = if self.cpp { + ("CXX", "cl.exe", "g++", "c++", "clang++") } else { - ("CC", "cl.exe", "gcc", "cc") + ("CC", "cl.exe", "gcc", "cc", "clang") }; // On Solaris, c++/cc unlikely to exist or be correct. - let default = if host.contains("solaris") { gnu } else { traditional }; - - let tool_opt: Option<Tool> = - self.env_tool(env) - .map(|(tool, cc, args)| { - let mut t = Tool::new(PathBuf::from(tool)); - if let Some(cc) = cc { - t.cc_wrapper_path = Some(PathBuf::from(cc)); - } - for arg in args { - t.cc_wrapper_args.push(arg.into()); - } - t - }) - .or_else(|| { - if target.contains("emscripten") { - let tool = if self.cpp { "em++" } else { "emcc" }; - // Windows uses bat file so we have to be a bit more specific - if cfg!(windows) { - let mut t = Tool::new(PathBuf::from("cmd")); - t.args.push("/c".into()); - t.args.push(format!("{}.bat", tool).into()); - Some(t) - } else { - Some(Tool::new(PathBuf::from(tool))) - } + let default = if host.contains("solaris") { + gnu + } else { + traditional + }; + + let cl_exe = windows_registry::find_tool(&target, "cl.exe"); + + let tool_opt: Option<Tool> = self.env_tool(env) + .map(|(tool, cc, args)| { + // chop off leading/trailing whitespace to work around + // semi-buggy build scripts which are shared in + // makefiles/configure scripts (where spaces are far more + // lenient) + let mut t = Tool::new(PathBuf::from(tool.trim())); + if let Some(cc) = cc { + t.cc_wrapper_path = Some(PathBuf::from(cc)); + } + for arg in args { + t.cc_wrapper_args.push(arg.into()); + } + t + }) + .or_else(|| { + if target.contains("emscripten") { + let tool = if self.cpp { "em++" } else { "emcc" }; + // Windows uses bat file so we have to be a bit more specific + if cfg!(windows) { + let mut t = Tool::new(PathBuf::from("cmd")); + t.args.push("/c".into()); + t.args.push(format!("{}.bat", tool).into()); + Some(t) } else { - None + Some(Tool::new(PathBuf::from(tool))) } - }) - .or_else(|| windows_registry::find_tool(&target, "cl.exe")); + } else { + None + } + }) + .or_else(|| cl_exe.clone()); let tool = match tool_opt { Some(t) => t, @@ -1479,7 +1650,20 @@ impl Build { format!("{}.exe", gnu) } } else if target.contains("android") { - format!("{}-{}", target.replace("armv7", "arm"), gnu) + let target = target + .replace("armv7neon", "arm") + .replace("armv7", "arm") + .replace("thumbv7neon", "arm") + .replace("thumbv7", "arm"); + let gnu_compiler = format!("{}-{}", target, gnu); + let clang_compiler = format!("{}-{}", target, clang); + // Check if gnu compiler is present + // if not, use clang + if Command::new(&gnu_compiler).spawn().is_ok() { + gnu_compiler + } else { + clang_compiler + } } else if target.contains("cloudabi") { format!("{}-{}", target, traditional) } else if self.get_host()? != target { @@ -1489,6 +1673,7 @@ impl Build { let prefix = cross_compile.or(match &target[..] { "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"), "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"), + "aarch64-unknown-netbsd" => Some("aarch64--netbsd"), "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), @@ -1500,7 +1685,14 @@ impl Build { "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"), "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"), + "i586-unknown-linux-musl" => Some("musl"), "i686-pc-windows-gnu" => Some("i686-w64-mingw32"), "i686-unknown-linux-musl" => Some("musl"), "i686-unknown-netbsd" => Some("i486--netbsdelf"), @@ -1509,13 +1701,19 @@ impl Build { "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"), "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"), + "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"), "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"), "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), + "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"), "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"), "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), + "armebv7r-none-eabi" => Some("arm-none-eabi"), + "armebv7r-none-eabihf" => Some("arm-none-eabi"), + "armv7r-none-eabi" => Some("arm-none-eabi"), + "armv7r-none-eabihf" => Some("arm-none-eabi"), "thumbv6m-none-eabi" => Some("arm-none-eabi"), "thumbv7em-none-eabi" => Some("arm-none-eabi"), "thumbv7em-none-eabihf" => Some("arm-none-eabi"), @@ -1537,20 +1735,45 @@ impl Build { } }; - let tool = if self.cuda { - assert!(tool.args.is_empty(), - "CUDA compilation currently assumes empty pre-existing args"); + let mut tool = if self.cuda { + assert!( + tool.args.is_empty(), + "CUDA compilation currently assumes empty pre-existing args" + ); let nvcc = match self.get_var("NVCC") { Err(_) => "nvcc".into(), Ok(nvcc) => nvcc, }; 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 } else { tool }; + // If we found `cl.exe` in our environment, the tool we're returning is + // an MSVC-like tool, *and* no env vars were set then set env vars for + // the tool that we're returning. + // + // Env vars are needed for things like `link.exe` being put into PATH as + // well as header include paths sometimes. These paths are automatically + // included by default but if the `CC` or `CXX` env vars are set these + // won't be used. This'll ensure that when the env vars are used to + // configure for invocations like `clang-cl` we still get a "works out + // of the box" experience. + if let Some(cl_exe) = cl_exe { + if tool.family == (ToolFamily::Msvc { clang_cl: true }) && + tool.env.len() == 0 && + target.contains("msvc") + { + for &(ref k, ref v) in cl_exe.env.iter() { + tool.env.push((k.to_owned(), v.to_owned())); + } + } + } + Ok(tool) } @@ -1568,10 +1791,7 @@ impl Build { Some(res) => Ok(res), None => Err(Error::new( ErrorKind::EnvVarNotFound, - &format!( - "Could not find environment variable {}.", - var_base - ), + &format!("Could not find environment variable {}.", var_base), )), } } @@ -1585,21 +1805,68 @@ impl Build { .collect() } - /// Returns compiler path, optional modifier name from whitelist, and arguments vec fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> { - self.get_var(name).ok().map(|tool| { - let whitelist = ["ccache", "distcc", "sccache"]; + let tool = match self.get_var(name) { + Ok(tool) => tool, + Err(_) => return None, + }; - for t in whitelist.iter() { - if tool.starts_with(t) && tool[t.len()..].starts_with(' ') { - let args = tool.split_whitespace().collect::<Vec<_>>(); + // If this is an exact path on the filesystem we don't want to do any + // interpretation at all, just pass it on through. This'll hopefully get + // us to support spaces-in-paths. + if Path::new(&tool).exists() { + return Some((tool, None, Vec::new())); + } + + // Ok now we want to handle a couple of scenarios. We'll assume from + // here on out that spaces are splitting separate arguments. Two major + // features we want to support are: + // + // CC='sccache cc' + // + // aka using `sccache` or any other wrapper/caching-like-thing for + // compilations. We want to know what the actual compiler is still, + // though, because our `Tool` API support introspection of it to see + // what compiler is in use. + // + // additionally we want to support + // + // CC='cc -flag' + // + // where the CC env var is used to also pass default flags to the C + // compiler. + // + // It's true that everything here is a bit of a pain, but apparently if + // you're not literally make or bash then you get a lot of bug reports. + let known_wrappers = ["ccache", "distcc", "sccache", "icecc"]; + + let mut parts = tool.split_whitespace(); + let maybe_wrapper = match parts.next() { + Some(s) => s, + None => return None, + }; - return (args[1].to_string(), Some(t.to_string()), args[2..].iter().map(|s| s.to_string()).collect()); - } + let file_stem = Path::new(maybe_wrapper) + .file_stem() + .unwrap() + .to_str() + .unwrap(); + if known_wrappers.contains(&file_stem) { + if let Some(compiler) = parts.next() { + return Some(( + compiler.to_string(), + Some(maybe_wrapper.to_string()), + parts.map(|s| s.to_string()).collect(), + )); } - (tool, None, Vec::new()) - }) + } + + Some(( + maybe_wrapper.to_string(), + None, + parts.map(|s| s.to_string()).collect(), + )) } /// Returns the default C++ standard library for the current target: `libc++` @@ -1608,17 +1875,25 @@ impl Build { match self.cpp_link_stdlib.clone() { Some(s) => Ok(s), None => { - let target = self.get_target()?; - if target.contains("msvc") { - Ok(None) - } else if target.contains("darwin") { - Ok(Some("c++".to_string())) - } else if target.contains("freebsd") { - Ok(Some("c++".to_string())) - } else if target.contains("openbsd") { - Ok(Some("c++".to_string())) + if let Ok(stdlib) = self.get_var("CXXSTDLIB") { + if stdlib.is_empty() { + Ok(None) + } else { + Ok(Some(stdlib)) + } } else { - Ok(Some("stdc++".to_string())) + let target = self.get_target()?; + if target.contains("msvc") { + Ok(None) + } else if target.contains("apple") { + Ok(Some("c++".to_string())) + } else if target.contains("freebsd") { + Ok(Some("c++".to_string())) + } else if target.contains("openbsd") { + Ok(Some("c++".to_string())) + } else { + Ok(Some("stdc++".to_string())) + } } } } @@ -1690,8 +1965,13 @@ impl Build { } fn getenv(&self, v: &str) -> Option<String> { + let mut cache = self.env_cache.lock().unwrap(); + if let Some(val) = cache.get(v) { + return val.clone() + } let r = env::var(v).ok(); self.print(&format!("{} = {:?}", v, r)); + cache.insert(v.to_string(), r.clone()); r } @@ -1700,10 +1980,7 @@ impl Build { Some(s) => Ok(s), None => Err(Error::new( ErrorKind::EnvVarNotFound, - &format!( - "Environment variable {} not defined.", - v.to_string() - ), + &format!("Environment variable {} not defined.", v.to_string()), )), } } @@ -1729,11 +2006,15 @@ impl Tool { fn with_features(path: PathBuf, cuda: bool) -> Tool { // Try to detect family of the tool from its name, falling back to Gnu. let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) { - if fname.contains("clang") { + if fname.contains("clang-cl") { + ToolFamily::Msvc { clang_cl: true } + } else if fname.contains("cl") && + !fname.contains("cloudabi") && + !fname.contains("uclibc") && + !fname.contains("clang") { + ToolFamily::Msvc { clang_cl: false } + } else if fname.contains("clang") { ToolFamily::Clang - } else if fname.contains("cl") && !fname.contains("cloudabi") && - !fname.contains("uclibc") { - ToolFamily::Msvc } else { ToolFamily::Gnu } @@ -1748,9 +2029,15 @@ impl Tool { env: Vec::new(), family: family, cuda: cuda, + removed_args: Vec::new(), } } + /// Add an argument to be stripped from the final command arguments. + fn remove_arg(&mut self, flag: OsString) { + self.removed_args.push(flag); + } + /// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler". /// /// Currently this is only used for compiling CUDA sources, since NVCC only @@ -1773,12 +2060,15 @@ impl Tool { Some(ref cc_wrapper_path) => { let mut cmd = Command::new(&cc_wrapper_path); cmd.arg(&self.path); - cmd.args(&self.cc_wrapper_args); cmd - }, - None => Command::new(&self.path) + } + None => Command::new(&self.path), }; - cmd.args(&self.args); + cmd.args(&self.cc_wrapper_args); + + let value = self.args.iter().filter(|a| !self.removed_args.contains(a)).collect::<Vec<_>>(); + cmd.args(&value); + for &(ref k, ref v) in self.env.iter() { cmd.env(k, v); } @@ -1822,10 +2112,8 @@ impl Tool { cc_env.push(arg); } cc_env - }, - None => { - OsString::from("") } + None => OsString::from(""), } } @@ -1855,7 +2143,10 @@ impl Tool { /// Whether the tool is MSVC-like. pub fn is_like_msvc(&self) -> bool { - self.family == ToolFamily::Msvc + match self.family { + ToolFamily::Msvc { .. } => true, + _ => false, + } } } @@ -1868,8 +2159,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> { ErrorKind::ToolExecError, &format!( "Failed to wait on spawned child process, command {:?} with args {:?}.", - cmd, - program + cmd, program ), )) } @@ -1884,9 +2174,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> { ErrorKind::ToolExecError, &format!( "Command {:?} with args {:?} did not execute successfully (status code {}).", - cmd, - program, - status + cmd, program, status ), )) } @@ -1909,8 +2197,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> { ErrorKind::ToolExecError, &format!( "Failed to wait on spawned child process, command {:?} with args {:?}.", - cmd, - program + cmd, program ), )) } @@ -1925,9 +2212,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> { ErrorKind::ToolExecError, &format!( "Command {:?} with args {:?} did not execute successfully (status code {}).", - cmd, - program, - status + cmd, program, status ), )) } @@ -1943,39 +2228,30 @@ fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Er match cmd.stderr(Stdio::piped()).spawn() { Ok(mut child) => { let stderr = BufReader::new(child.stderr.take().unwrap()); - let print = thread::spawn(move || for line in stderr.split(b'\n').filter_map( - |l| l.ok(), - ) - { - print!("cargo:warning="); - std::io::stdout().write_all(&line).unwrap(); - println!(""); + let print = thread::spawn(move || { + for line in stderr.split(b'\n').filter_map(|l| l.ok()) { + print!("cargo:warning="); + std::io::stdout().write_all(&line).unwrap(); + println!(""); + } }); Ok((child, print)) } Err(ref e) if e.kind() == io::ErrorKind::NotFound => { let extra = if cfg!(windows) { " (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \ - for help)" + for help)" } else { "" }; Err(Error::new( ErrorKind::ToolNotFound, - &format!( - "Failed to find tool. Is `{}` installed?{}", - program, - extra - ), + &format!("Failed to find tool. Is `{}` installed?{}", program, extra), )) } Err(_) => Err(Error::new( ErrorKind::ToolExecError, - &format!( - "Command {:?} with args {:?} failed to start.", - cmd, - program - ), + &format!("Command {:?} with args {:?} failed to start.", cmd, program), )), } } @@ -1984,9 +2260,10 @@ fn fail(s: &str) -> ! { panic!("\n\nInternal error occurred: {}\n\n", s) } - -fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool) { - if msvc && is_asm { +fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool, is_arm: bool) { + if msvc && is_asm && is_arm { + cmd.arg("-o").arg(&dst); + } else if msvc && is_asm { cmd.arg("/Fo").arg(dst); } else if msvc { let mut s = OsString::from("/Fo"); diff --git a/cc/src/registry.rs b/cc/src/registry.rs index a452723..2ac2fa6 100644 --- a/cc/src/registry.rs +++ b/cc/src/registry.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::ffi::{OsString, OsStr}; +use std::ffi::{OsStr, OsString}; use std::io; use std::ops::RangeFrom; use std::os::raw; @@ -36,28 +36,31 @@ const KEY_WOW64_32KEY: DWORD = 0x200; #[link(name = "advapi32")] extern "system" { - fn RegOpenKeyExW(key: HKEY, - lpSubKey: LPCWSTR, - ulOptions: DWORD, - samDesired: REGSAM, - phkResult: PHKEY) - -> LONG; - fn RegEnumKeyExW(key: HKEY, - dwIndex: DWORD, - lpName: LPWSTR, - lpcName: LPDWORD, - lpReserved: LPDWORD, - lpClass: LPWSTR, - lpcClass: LPDWORD, - lpftLastWriteTime: PFILETIME) - -> LONG; - fn RegQueryValueExW(hKey: HKEY, - lpValueName: LPCWSTR, - lpReserved: LPDWORD, - lpType: LPDWORD, - lpData: LPBYTE, - lpcbData: LPDWORD) - -> LONG; + fn RegOpenKeyExW( + key: HKEY, + lpSubKey: LPCWSTR, + ulOptions: DWORD, + samDesired: REGSAM, + phkResult: PHKEY, + ) -> LONG; + fn RegEnumKeyExW( + key: HKEY, + dwIndex: DWORD, + lpName: LPWSTR, + lpcName: LPDWORD, + lpReserved: LPDWORD, + lpClass: LPWSTR, + lpcClass: LPDWORD, + lpftLastWriteTime: PFILETIME, + ) -> LONG; + fn RegQueryValueExW( + hKey: HKEY, + lpValueName: LPCWSTR, + lpReserved: LPDWORD, + lpType: LPDWORD, + lpData: LPBYTE, + lpcbData: LPDWORD, + ) -> LONG; fn RegCloseKey(hKey: HKEY) -> LONG; } @@ -90,11 +93,13 @@ impl RegistryKey { let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>(); let mut ret = 0 as *mut _; let err = unsafe { - RegOpenKeyExW(self.raw(), - key.as_ptr(), - 0, - KEY_READ | KEY_WOW64_32KEY, - &mut ret) + RegOpenKeyExW( + self.raw(), + key.as_ptr(), + 0, + KEY_READ | KEY_WOW64_32KEY, + &mut ret, + ) }; if err == ERROR_SUCCESS as LONG { Ok(RegistryKey(Repr::Owned(OwnedKey(ret)))) @@ -116,29 +121,36 @@ impl RegistryKey { let mut len = 0; let mut kind = 0; unsafe { - let err = RegQueryValueExW(self.raw(), - name.as_ptr(), - 0 as *mut _, - &mut kind, - 0 as *mut _, - &mut len); + let err = RegQueryValueExW( + self.raw(), + name.as_ptr(), + 0 as *mut _, + &mut kind, + 0 as *mut _, + &mut len, + ); if err != ERROR_SUCCESS as LONG { return Err(io::Error::from_raw_os_error(err as i32)); } 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 // characters so we need to be sure to halve it for the capacity // passed in. let mut v = Vec::with_capacity(len as usize / 2); - let err = RegQueryValueExW(self.raw(), - name.as_ptr(), - 0 as *mut _, - 0 as *mut _, - v.as_mut_ptr() as *mut _, - &mut len); + let err = RegQueryValueExW( + self.raw(), + name.as_ptr(), + 0 as *mut _, + 0 as *mut _, + v.as_mut_ptr() as *mut _, + &mut len, + ); if err != ERROR_SUCCESS as LONG { 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 { let mut v = Vec::with_capacity(256); let mut len = v.capacity() as DWORD; - let ret = RegEnumKeyExW(self.key.raw(), - i, - v.as_mut_ptr(), - &mut len, - 0 as *mut _, - 0 as *mut _, - 0 as *mut _, - 0 as *mut _); + let ret = RegEnumKeyExW( + self.key.raw(), + i, + v.as_mut_ptr(), + &mut len, + 0 as *mut _, + 0 as *mut _, + 0 as *mut _, + 0 as *mut _, + ); if ret == ERROR_NO_MORE_ITEMS as LONG { None } else if ret != ERROR_SUCCESS as LONG { diff --git a/cc/src/setup_config.rs b/cc/src/setup_config.rs index 175b7f1..7c03d38 100644 --- a/cc/src/setup_config.rs +++ b/cc/src/setup_config.rs @@ -15,7 +15,7 @@ use winapi::{LPFILETIME, ULONG}; use winapi::S_FALSE; use winapi::BSTR; use winapi::LPCOLESTR; -use winapi::{CLSCTX_ALL, CoCreateInstance}; +use winapi::{CoCreateInstance, CLSCTX_ALL}; use winapi::LPSAFEARRAY; use winapi::{IUnknown, IUnknownVtbl}; use winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG}; @@ -155,7 +155,7 @@ interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) { }} 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 pub struct SetupConfiguration(ComPtr<ISetupConfiguration>); @@ -163,31 +163,44 @@ 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 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); } + 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); } + 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); } + if err < 0 { + return Err(err); + } Ok(unsafe { EnumSetupInstances::from_raw(obj) }) } } @@ -202,28 +215,36 @@ impl SetupInstance { 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); } + 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); } + 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); } + 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); } + if err < 0 { + return Err(err); + } Ok(bstr.to_osstring()) } pub fn product_path(&self) -> Result<OsString, i32> { @@ -231,7 +252,9 @@ impl SetupInstance { 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); } + if err < 0 { + return Err(err); + } Ok(bstr.to_osstring()) } } @@ -249,9 +272,12 @@ impl Iterator for EnumSetupInstances { 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; } + if err < 0 { + return Some(Err(err)); + } + if err == S_FALSE { + return None; + } Some(Ok(unsafe { SetupInstance::from_raw(obj) })) } } - diff --git a/cc/src/winapi.rs b/cc/src/winapi.rs index 3fb0408..cc83963 100644 --- a/cc/src/winapi.rs +++ b/cc/src/winapi.rs @@ -44,8 +44,8 @@ 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; +pub const CLSCTX_ALL: CLSCTX = + CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER; #[repr(C)] #[derive(Copy, Clone)] @@ -69,13 +69,17 @@ pub trait Interface { #[link(name = "ole32")] #[link(name = "oleaut32")] -extern { } +extern "C" {} 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 CoCreateInstance( + rclsid: REFCLSID, + pUnkOuter: LPUNKNOWN, + dwClsContext: DWORD, + riid: REFIID, + ppv: *mut LPVOID, + ) -> HRESULT; pub fn SysFreeString(bstrString: BSTR); pub fn SysStringLen(pbstr: BSTR) -> UINT; } diff --git a/cc/src/windows_registry.rs b/cc/src/windows_registry.rs index 9099e0f..bbcbb09 100644 --- a/cc/src/windows_registry.rs +++ b/cc/src/windows_registry.rs @@ -65,11 +65,19 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> { return impl_::find_msbuild(target); } + if tool.contains("devenv") { + return impl_::find_devenv(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())) + .and_then(|path| { + env::split_paths(&path) + .map(|p| p.join(tool)) + .find(|p| p.exists()) + }) .map(|path| Tool::new(path.into())); } @@ -119,19 +127,20 @@ 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)), - } - } + 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. @@ -142,12 +151,14 @@ pub fn find_vs_version() -> Result<VsVers, String> { } 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\ - ")) + 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\ + " + )) } } } @@ -185,7 +196,12 @@ mod impl_ { } 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()); add_env(&mut tool, "LIB", libs); add_env(&mut tool, "PATH", path); @@ -217,11 +233,13 @@ mod impl_ { None } - fn tool_from_vs15_instance(tool: &str, target: &str, - instance: &SetupInstance) -> Option<Tool> { - let (bin_path, host_dylib_path, lib_path, include_path) = otry!(vs15_vc_paths(target, instance)); + fn tool_from_vs15_instance(tool: &str, target: &str, instance: &SetupInstance) -> Option<Tool> { + let (bin_path, host_dylib_path, lib_path, include_path) = + otry!(vs15_vc_paths(target, instance)); 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); tool.path.push(host_dylib_path); @@ -238,9 +256,13 @@ mod impl_ { 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 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 = String::new(); otry!(version_file.read_to_string(&mut version).ok()); @@ -255,11 +277,15 @@ mod impl_ { let path = instance_path.join(r"VC\Tools\MSVC").join(version); // This is the path to the toolchain for a particular target, running // 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 // architecture, because it contains dlls like mspdb140.dll compiled for // 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 include_path = path.join("include"); Some((bin_path, host_dylib_path, lib_path, include_path)) @@ -288,7 +314,8 @@ mod impl_ { 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)); + 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")); @@ -302,6 +329,7 @@ mod impl_ { tool.libs.push(sdk_lib.join("um").join(sub)); let sdk_include = sdk.join("include").join(&version); tool.include.push(sdk_include.join("um")); + tool.include.push(sdk_include.join("cppwinrt")); tool.include.push(sdk_include.join("winrt")); tool.include.push(sdk_include.join("shared")); } else if let Some(sdk) = get_sdk81_dir() { @@ -353,7 +381,8 @@ mod impl_ { let prev = env::var_os(env).unwrap_or(OsString::new()); let prev = env::split_paths(&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 @@ -361,7 +390,12 @@ mod impl_ { fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> { bin_subdir(target) .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()) .map(|(path, host)| { let mut tool = MsvcTool::new(path); @@ -402,16 +436,17 @@ mod impl_ { let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok()); let root = otry!(key.query_str("KitsRoot10").ok()); let readdir = otry!(Path::new(&root).join("lib").read_dir().ok()); - let max_libdir = otry!(readdir.filter_map(|dir| dir.ok()) - .map(|dir| dir.path()) - .filter(|dir| { - dir.components() + let max_libdir = otry!( + readdir + .filter_map(|dir| dir.ok()) + .map(|dir| dir.path()) + .filter(|dir| dir.components() .last() .and_then(|c| c.as_os_str().to_str()) .map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir()) - .unwrap_or(false) - }) - .max()); + .unwrap_or(false)) + .max() + ); let version = max_libdir.components().last().unwrap(); let version = version.as_os_str().to_str().unwrap().to_string(); Some((root.into(), version)) @@ -430,14 +465,17 @@ mod impl_ { let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok()); let root = otry!(key.query_str("InstallationFolder").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()) .collect::<Vec<_>>(); dirs.sort(); - let dir = otry!(dirs.into_iter() - .rev() - .filter(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file()) - .next()); + let dir = otry!( + dirs.into_iter() + .rev() + .filter(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file()) + .next() + ); let version = dir.components().last().unwrap(); let version = version.as_os_str().to_str().unwrap().to_string(); Some((root.into(), version)) @@ -485,8 +523,8 @@ mod impl_ { ("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")], ("x86_64", X86) => vec![("x86_amd64", "")], ("x86_64", X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")], - ("arm", X86) => vec![("x86_arm", "")], - ("arm", X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")], + ("arm", X86) | ("thumbv7a", X86) => vec![("x86_arm", "")], + ("arm", X86_64) | ("thumbv7a", X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")], _ => vec![], } } @@ -496,7 +534,8 @@ mod impl_ { match arch { "i586" | "i686" => Some("x86"), "x86_64" => Some("x64"), - "arm" => Some("arm"), + "arm" | "thumbv7a" => Some("arm"), + "aarch64" => Some("arm64"), _ => None, } } @@ -507,7 +546,8 @@ mod impl_ { match arch { "i586" | "i686" => Some(""), "x86_64" => Some("amd64"), - "arm" => Some("arm"), + "arm" | "thumbv7a" => Some("arm"), + "aarch64" => Some("arm64"), _ => None, } } @@ -553,7 +593,8 @@ mod impl_ { let mut max_vers = 0; let mut max_key = None; 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()); let val = match val { Some(s) => s, @@ -572,18 +613,39 @@ mod impl_ { 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() + find_msbuild_vs15("x86_64-pc-windows-msvc").is_some() + || find_msbuild_vs15("i686-pc-windows-msvc").is_some() } - _ => false + "12.0" | "14.0" => LOCAL_MACHINE + .open(&OsString::from(format!( + "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}", + version + ))) + .is_ok(), + _ => false, } } + pub fn find_devenv(target: &str) -> Option<Tool> { + find_devenv_vs15(&target) + } + + fn find_devenv_vs15(target: &str) -> Option<Tool> { + 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"Common7\IDE\devenv.exe"); + let mut tool = Tool::new(path); + if target.contains("x86_64") { + tool.env.push(("Platform".into(), "X64".into())); + } + tool + }) + } + // see http://stackoverflow.com/questions/328017/path-to-msbuild pub fn find_msbuild(target: &str) -> Option<Tool> { // VS 15 (2017) changed how to locate msbuild @@ -599,11 +661,10 @@ mod impl_ { // 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()) + LOCAL_MACHINE + .open(key.as_ref()) .ok() - .and_then(|key| { - key.query_str("15.0").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); @@ -616,7 +677,8 @@ mod impl_ { fn find_old_msbuild(target: &str) -> Option<Tool> { let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions"; - LOCAL_MACHINE.open(key.as_ref()) + LOCAL_MACHINE + .open(key.as_ref()) .ok() .and_then(|key| { max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok()) diff --git a/cc/tests/cc_env.rs b/cc/tests/cc_env.rs index 8ffe651..f9386d7 100644 --- a/cc/tests/cc_env.rs +++ b/cc/tests/cc_env.rs @@ -14,6 +14,10 @@ fn main() { distcc(); ccache_spaces(); ccache_env_flags(); + leading_spaces(); + extra_flags(); + path_to_ccache(); + more_spaces(); } fn ccache() { @@ -71,3 +75,45 @@ fn ccache_env_flags() { env::set_var("CC", ""); } + +fn leading_spaces() { + let test = Test::gnu(); + test.shim("ccache"); + + env::set_var("CC", " test "); + let compiler = test.gcc().file("foo.c").get_compiler(); + assert_eq!(compiler.path(), Path::new("test")); + + env::set_var("CC", ""); +} + +fn extra_flags() { + let test = Test::gnu(); + test.shim("ccache"); + + env::set_var("CC", "ccache cc -m32"); + let compiler = test.gcc().file("foo.c").get_compiler(); + assert_eq!(compiler.path(), Path::new("cc")); +} + +fn path_to_ccache() { + let test = Test::gnu(); + test.shim("ccache"); + + env::set_var("CC", "/path/to/ccache.exe cc -m32"); + let compiler = test.gcc().file("foo.c").get_compiler(); + assert_eq!(compiler.path(), Path::new("cc")); + assert_eq!( + compiler.cc_env(), + OsString::from("/path/to/ccache.exe cc -m32"), + ); +} + +fn more_spaces() { + let test = Test::gnu(); + test.shim("ccache"); + + env::set_var("CC", "cc -m32"); + let compiler = test.gcc().file("foo.c").get_compiler(); + assert_eq!(compiler.path(), Path::new("cc")); +} diff --git a/cc/tests/support/mod.rs b/cc/tests/support/mod.rs index bb56bf3..cae8151 100644 --- a/cc/tests/support/mod.rs +++ b/cc/tests/support/mod.rs @@ -85,7 +85,9 @@ impl Test { .unwrap() .read_to_string(&mut s) .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 { - let before_position = self.args.iter().rposition(|x| OsStr::new(x) == OsStr::new(before)); - let after_position = self.args.iter().rposition(|x| OsStr::new(x) == OsStr::new(after)); + let before_position = self.args + .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) { - (Some(b), Some(a)) if b < a => {}, - (b, a) => { panic!("{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})", before, b, after, a) }, + (Some(b), Some(a)) if b < a => {} + (b, a) => panic!( + "{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})", + before, b, after, a + ), }; self } diff --git a/cc/tests/test.rs b/cc/tests/test.rs index 7e5a28d..820072f 100644 --- a/cc/tests/test.rs +++ b/cc/tests/test.rs @@ -1,6 +1,7 @@ extern crate cc; extern crate tempdir; +use std::env; use support::Test; mod support; @@ -8,9 +9,7 @@ mod support; #[test] fn gnu_smoke() { let test = Test::gnu(); - test.gcc() - .file("foo.c") - .compile("foo"); + test.gcc().file("foo.c").compile("foo"); test.cmd(0) .must_have("-O2") @@ -25,23 +24,15 @@ fn gnu_smoke() { #[test] fn gnu_opt_level_1() { let test = Test::gnu(); - test.gcc() - .opt_level(1) - .file("foo.c") - .compile("foo"); + test.gcc().opt_level(1).file("foo.c").compile("foo"); - test.cmd(0) - .must_have("-O1") - .must_not_have("-O2"); + test.cmd(0).must_have("-O1").must_not_have("-O2"); } #[test] fn gnu_opt_level_s() { let test = Test::gnu(); - test.gcc() - .opt_level_str("s") - .file("foo.c") - .compile("foo"); + test.gcc().opt_level_str("s").file("foo.c").compile("foo"); test.cmd(0) .must_have("-Os") @@ -54,10 +45,7 @@ fn gnu_opt_level_s() { #[test] fn gnu_debug() { let test = Test::gnu(); - test.gcc() - .debug(true) - .file("foo.c") - .compile("foo"); + test.gcc().debug(true).file("foo.c").compile("foo"); test.cmd(0).must_have("-g"); } @@ -81,8 +69,33 @@ fn gnu_warnings() { .file("foo.c") .compile("foo"); - test.cmd(0).must_have("-Wall") - .must_have("-Wextra"); + test.cmd(0).must_have("-Wall").must_have("-Wextra"); +} + +#[test] +fn gnu_extra_warnings0() { + let test = Test::gnu(); + test.gcc() + .warnings(true) + .extra_warnings(false) + .flag("-Wno-missing-field-initializers") + .file("foo.c") + .compile("foo"); + + test.cmd(0).must_have("-Wall").must_not_have("-Wextra"); +} + +#[test] +fn gnu_extra_warnings1() { + let test = Test::gnu(); + test.gcc() + .warnings(false) + .extra_warnings(true) + .flag("-Wno-missing-field-initializers") + .file("foo.c") + .compile("foo"); + + test.cmd(0).must_not_have("-Wall").must_have("-Wextra"); } #[test] @@ -94,7 +107,32 @@ fn gnu_warnings_overridable() { .file("foo.c") .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] +fn gnu_no_warnings_if_cflags() { + env::set_var("CFLAGS", "-Wflag-does-not-exist"); + let test = Test::gnu(); + test.gcc() + .file("foo.c") + .compile("foo"); + + test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra"); + env::set_var("CFLAGS", ""); +} + +#[test] +fn gnu_no_warnings_if_cxxflags() { + env::set_var("CXXFLAGS", "-Wflag-does-not-exist"); + let test = Test::gnu(); + test.gcc() + .file("foo.c") + .compile("foo"); + + test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra"); + env::set_var("CXXFLAGS", ""); } #[test] @@ -108,9 +146,7 @@ fn gnu_x86_64() { .file("foo.c") .compile("foo"); - test.cmd(0) - .must_have("-fPIC") - .must_have("-m64"); + test.cmd(0).must_have("-fPIC").must_have("-m64"); } } @@ -141,8 +177,7 @@ fn gnu_i686() { .file("foo.c") .compile("foo"); - test.cmd(0) - .must_have("-m32"); + test.cmd(0).must_have("-m32"); } } @@ -176,10 +211,7 @@ fn gnu_set_stdlib() { #[test] fn gnu_include() { let test = Test::gnu(); - test.gcc() - .include("foo/bar") - .file("foo.c") - .compile("foo"); + test.gcc().include("foo/bar").file("foo.c").compile("foo"); test.cmd(0).must_have("-I").must_have("foo/bar"); } @@ -199,9 +231,7 @@ fn gnu_define() { #[test] fn gnu_compile_assembly() { let test = Test::gnu(); - test.gcc() - .file("foo.S") - .compile("foo"); + test.gcc().file("foo.S").compile("foo"); test.cmd(0).must_have("foo.S"); } @@ -214,25 +244,25 @@ fn gnu_shared() { .static_flag(false) .compile("foo"); - test.cmd(0) - .must_have("-shared") - .must_not_have("-static"); + test.cmd(0).must_have("-shared").must_not_have("-static"); } #[test] fn gnu_flag_if_supported() { if cfg!(windows) { - return + return; } let test = Test::gnu(); test.gcc() .file("foo.c") + .flag("-v") .flag_if_supported("-Wall") .flag_if_supported("-Wflag-does-not-exist") .flag_if_supported("-std=c++11") .compile("foo"); test.cmd(0) + .must_have("-v") .must_have("-Wall") .must_not_have("-Wflag-does-not-exist") .must_not_have("-std=c++11"); @@ -241,7 +271,7 @@ fn gnu_flag_if_supported() { #[test] fn gnu_flag_if_supported_cpp() { if cfg!(windows) { - return + return; } let test = Test::gnu(); test.gcc() @@ -250,8 +280,7 @@ fn gnu_flag_if_supported_cpp() { .flag_if_supported("-std=c++11") .compile("foo"); - test.cmd(0) - .must_have("-std=c++11"); + test.cmd(0).must_have("-std=c++11"); } #[test] @@ -263,17 +292,13 @@ fn gnu_static() { .static_flag(true) .compile("foo"); - test.cmd(0) - .must_have("-static") - .must_not_have("-shared"); + test.cmd(0).must_have("-static").must_not_have("-shared"); } #[test] fn msvc_smoke() { let test = Test::msvc(); - test.gcc() - .file("foo.c") - .compile("foo"); + test.gcc().file("foo.c").compile("foo"); test.cmd(0) .must_have("/O2") @@ -287,10 +312,7 @@ fn msvc_smoke() { #[test] fn msvc_opt_level_0() { let test = Test::msvc(); - test.gcc() - .opt_level(0) - .file("foo.c") - .compile("foo"); + test.gcc().opt_level(0).file("foo.c").compile("foo"); test.cmd(0).must_not_have("/O2"); } @@ -298,20 +320,14 @@ fn msvc_opt_level_0() { #[test] fn msvc_debug() { let test = Test::msvc(); - test.gcc() - .debug(true) - .file("foo.c") - .compile("foo"); + test.gcc().debug(true).file("foo.c").compile("foo"); test.cmd(0).must_have("/Z7"); } #[test] fn msvc_include() { let test = Test::msvc(); - test.gcc() - .include("foo/bar") - .file("foo.c") - .compile("foo"); + test.gcc().include("foo/bar").file("foo.c").compile("foo"); test.cmd(0).must_have("/I").must_have("foo/bar"); } @@ -331,10 +347,7 @@ fn msvc_define() { #[test] fn msvc_static_crt() { let test = Test::msvc(); - test.gcc() - .static_crt(true) - .file("foo.c") - .compile("foo"); + test.gcc().static_crt(true).file("foo.c").compile("foo"); test.cmd(0).must_have("/MT"); } @@ -342,10 +355,7 @@ fn msvc_static_crt() { #[test] fn msvc_no_static_crt() { let test = Test::msvc(); - test.gcc() - .static_crt(false) - .file("foo.c") - .compile("foo"); + test.gcc().static_crt(false).file("foo.c").compile("foo"); test.cmd(0).must_have("/MD"); } |