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");  }  | 
