diff options
Diffstat (limited to 'cc/src')
| -rw-r--r-- | cc/src/com.rs | 4 | ||||
| -rw-r--r-- | cc/src/lib.rs | 293 | ||||
| -rw-r--r-- | cc/src/setup_config.rs | 22 | ||||
| -rw-r--r-- | cc/src/winapi.rs | 4 | ||||
| -rw-r--r-- | cc/src/windows_registry.rs | 112 | 
5 files changed, 288 insertions, 147 deletions
| diff --git a/cc/src/com.rs b/cc/src/com.rs index 2b16475..9b75d47 100644 --- a/cc/src/com.rs +++ b/cc/src/com.rs @@ -13,12 +13,12 @@ use std::ops::Deref;  use std::os::windows::ffi::{OsStrExt, OsStringExt};  use std::ptr::null_mut;  use std::slice::from_raw_parts; +use winapi::CoInitializeEx; +use winapi::IUnknown;  use winapi::Interface;  use winapi::BSTR; -use winapi::CoInitializeEx;  use winapi::COINIT_MULTITHREADED;  use winapi::{SysFreeString, SysStringLen}; -use winapi::IUnknown;  use winapi::{HRESULT, S_FALSE, S_OK};  pub fn initialize() -> Result<(), HRESULT> { diff --git a/cc/src/lib.rs b/cc/src/lib.rs index 5eebd07..9fef147 100644 --- a/cc/src/lib.rs +++ b/cc/src/lib.rs @@ -61,15 +61,15 @@  #[cfg(feature = "parallel")]  extern crate rayon; +use std::collections::HashMap;  use std::env;  use std::ffi::{OsStr, OsString};  use std::fs; +use std::io::{self, BufRead, BufReader, 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}; -use std::collections::HashMap;  use std::sync::{Arc, Mutex}; +use std::thread::{self, JoinHandle};  // These modules are all glue to support reading the MSVC version from  // the registry and from COM interfaces @@ -891,7 +891,7 @@ impl Build {                      return Err(Error::new(                          ErrorKind::IOError,                          "Getting object file details failed.", -                    )) +                    ));                  }              }; @@ -956,7 +956,7 @@ impl Build {      fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {          use self::rayon::prelude::*; -        if let Ok(amt) = env::var("NUM_JOBS") { +        if let Some(amt) = self.getenv("NUM_JOBS") {              if let Ok(amt) = amt.parse() {                  let _ = rayon::ThreadPoolBuilder::new()                      .num_threads(amt) @@ -1093,7 +1093,86 @@ impl Build {          let target = self.get_target()?;          let mut cmd = self.get_base_compiler()?; +        let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }); + +        // Disable default flag generation via environment variable or when +        // certain cross compiling arguments are set +        let use_defaults = self.getenv("CRATE_CC_NO_DEFAULTS").is_none(); + +        if use_defaults { +            self.add_default_flags(&mut cmd, &target, &opt_level)?; +        } else { +            println!("Info: default compiler flags are disabled"); +        } + +        for arg in envflags { +            cmd.push_cc_arg(arg.into()); +        } + +        for directory in self.include_directories.iter() { +            cmd.args.push(cmd.family.include_flag().into()); +            cmd.args.push(directory.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()); +            } +        } + +        for flag in self.flags.iter() { +            cmd.args.push(flag.into()); +        } +        for flag in self.flags_supported.iter() { +            if self.is_flag_supported(flag).unwrap_or(false) { +                cmd.push_cc_arg(flag.into()); +            } +        } + +        for &(ref key, ref value) in self.definitions.iter() { +            let lead = if let ToolFamily::Msvc { .. } = cmd.family { +                "/" +            } else { +                "-" +            }; +            if let Some(ref value) = *value { +                cmd.args.push(format!("{}D{}={}", lead, key, value).into()); +            } else { +                cmd.args.push(format!("{}D{}", lead, key).into()); +            } +        } + +        if self.warnings_into_errors { +            let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into(); +            cmd.push_cc_arg(warnings_to_errors_flag); +        } + +        Ok(cmd) +    } + +    fn add_default_flags( +        &self, +        cmd: &mut Tool, +        target: &str, +        opt_level: &str, +    ) -> Result<(), Error> {          // Non-target flags          // If the flag is not conditioned on target variable, it belongs here :)          match cmd.family { @@ -1107,8 +1186,9 @@ impl Build {                      Some(true) => "/MT",                      Some(false) => "/MD",                      None => { -                        let features = -                            env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new()); +                        let features = self +                            .getenv("CARGO_CFG_TARGET_FEATURE") +                            .unwrap_or(String::new());                          if features.contains("crt-static") {                              "/MT"                          } else { @@ -1120,9 +1200,9 @@ impl Build {                  match &opt_level[..] {                      // Msvc uses /O1 to enable all optimizations that minimize code size. -                    "z" | "s" | "1" => cmd.args.push("/O1".into()), +                    "z" | "s" | "1" => cmd.push_opt_unless_duplicate("/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()), +                    "2" | "3" => cmd.push_opt_unless_duplicate("/O2".into()),                      _ => {}                  }              } @@ -1130,9 +1210,9 @@ impl Build {                  // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does                  // not support '-Oz'                  if opt_level == "z" && cmd.family != ToolFamily::Clang { -                    cmd.args.push("-Os".into()); +                    cmd.push_opt_unless_duplicate("-Os".into());                  } else { -                    cmd.args.push(format!("-O{}", opt_level).into()); +                    cmd.push_opt_unless_duplicate(format!("-O{}", opt_level).into());                  }                  if !target.contains("-ios") { @@ -1149,9 +1229,6 @@ impl Build {                  }              }          } -        for arg in self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) { -            cmd.args.push(arg.into()); -        }          if self.get_debug() {              if self.cuda { @@ -1159,7 +1236,7 @@ impl Build {                  cmd.args.push(nvcc_debug_flag);              }              let family = cmd.family; -            family.add_debug_flags(&mut cmd); +            family.add_debug_flags(cmd);          }          // Target flags @@ -1171,11 +1248,11 @@ impl Build {                  if clang_cl {                      if target.contains("x86_64") {                          cmd.args.push("-m64".into()); -                    } else if target.contains("i586") { +                    } else if target.contains("86") {                          cmd.args.push("-m32".into());                          cmd.args.push("/arch:IA32".into());                      } else { -                        cmd.args.push("-m32".into()); +                        cmd.args.push(format!("--target={}", target).into());                      }                  } else {                      if target.contains("i586") { @@ -1193,7 +1270,8 @@ impl Build {                  // the SDK, but for all released versions of the                  // Windows SDK it is required.                  if target.contains("arm") || target.contains("thumb") { -                    cmd.args.push("/D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into()); +                    cmd.args +                        .push("/D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into());                  }              }              ToolFamily::Gnu => { @@ -1206,14 +1284,18 @@ impl Build {                  }                  if self.static_flag.is_none() { -                    let features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new()); +                    let features = self +                        .getenv("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.starts_with("thumbv7")) && target.contains("-linux-") { +                if (target.starts_with("armv7") || target.starts_with("thumbv7")) +                    && target.contains("-linux-") +                {                      cmd.args.push("-march=armv7-a".into());                  } @@ -1346,7 +1428,7 @@ impl Build {          if target.contains("-ios") {              // FIXME: potential bug. iOS is always compiled with Clang, but Gcc compiler may be              // detected instead. -            self.ios_flags(&mut cmd)?; +            self.ios_flags(cmd)?;          }          if self.static_flag.unwrap_or(false) { @@ -1372,62 +1454,17 @@ impl Build {              }          } -        for directory in self.include_directories.iter() { -            cmd.args.push(cmd.family.include_flag().into()); -            cmd.args.push(directory.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()); -            } -        } - -        for flag in self.flags.iter() { -            cmd.args.push(flag.into()); -        } - -        for flag in self.flags_supported.iter() { -            if self.is_flag_supported(flag).unwrap_or(false) { -                cmd.push_cc_arg(flag.into()); -            } -        } - -        for &(ref key, ref value) in self.definitions.iter() { -            let lead = if let ToolFamily::Msvc { .. } = cmd.family { -                "/" -            } else { -                "-" -            }; -            if let Some(ref value) = *value { -                cmd.args.push(format!("{}D{}={}", lead, key, value).into()); -            } else { -                cmd.args.push(format!("{}D{}", lead, key).into()); -            } -        } - -        if self.warnings_into_errors { -            let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into(); -            cmd.push_cc_arg(warnings_to_errors_flag); -        } - -        Ok(cmd) +        Ok(())      }      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 } +        if let Ok(_) = flags_env_var_value { +            true +        } else { +            false +        }      }      fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> { @@ -1471,12 +1508,7 @@ impl Build {          let objects: Vec<_> = objs.iter().map(|obj| obj.dst.clone()).collect();          let target = self.get_target()?;          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")), -            }; - +            let (mut cmd, program) = self.get_ar()?;              let mut out = OsString::from("/OUT:");              out.push(dst);              cmd.arg(out).arg("/nologo"); @@ -1521,7 +1553,7 @@ impl Build {              } else {                  cmd.args(&objects).args(&self.objects);              } -            run(&mut cmd, "lib.exe")?; +            run(&mut cmd, &program)?;              // 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 @@ -1537,7 +1569,7 @@ impl Build {                      return Err(Error::new(                          ErrorKind::IOError,                          "Could not copy or create a hard-link to the generated lib file.", -                    )) +                    ));                  }              };          } else { @@ -1574,7 +1606,7 @@ impl Build {                  return Err(Error::new(                      ErrorKind::ArchitectureInvalid,                      "Unknown architecture for iOS target.", -                )) +                ));              }          }; @@ -1593,7 +1625,8 @@ impl Build {          };          self.print(&format!("Detecting iOS SDK path for {}", sdk)); -        let sdk_path = self.cmd("xcrun") +        let sdk_path = self +            .cmd("xcrun")              .arg("--show-sdk-path")              .arg("--sdk")              .arg(sdk) @@ -1607,7 +1640,7 @@ impl Build {                  return Err(Error::new(                      ErrorKind::IOError,                      "Unable to determine iOS SDK path.", -                )) +                ));              }          }; @@ -1658,7 +1691,8 @@ impl Build {          let cl_exe = windows_registry::find_tool(&target, "cl.exe"); -        let tool_opt: Option<Tool> = self.env_tool(env) +        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 @@ -1717,6 +1751,10 @@ impl Build {                      }                  } else if target.contains("cloudabi") {                      format!("{}-{}", target, traditional) +                } else if target == "wasm32-wasi" || +                          target == "wasm32-unknown-wasi" || +                          target == "wasm32-unknown-unknown" { +                    "clang".to_string()                  } else if self.get_host()? != target {                      // CROSS_COMPILE is of the form: "arm-linux-gnueabi-"                      let cc_env = self.getenv("CROSS_COMPILE"); @@ -1751,6 +1789,12 @@ impl Build {                          "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),                          "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),                          "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), +                        "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"), +                        "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"), +                        "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"), +                        "mipsisa64r6el-unknown-linux-gnuabi64" => { +                            Some("mipsisa64r6el-linux-gnuabi64") +                        }                          "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),                          "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"),                          "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), @@ -1818,9 +1862,9 @@ impl Build {          // 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") +            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())); @@ -1836,7 +1880,8 @@ impl Build {          let host = self.get_host()?;          let kind = if host == target { "HOST" } else { "TARGET" };          let target_u = target.replace("-", "_"); -        let res = self.getenv(&format!("{}_{}", var_base, target)) +        let res = self +            .getenv(&format!("{}_{}", var_base, target))              .or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))              .or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))              .or_else(|| self.getenv(var_base)); @@ -1961,9 +2006,10 @@ impl Build {          if let Ok(p) = self.get_var("AR") {              return Ok((self.cmd(&p), p));          } -        let program = if self.get_target()?.contains("android") { -            format!("{}-ar", self.get_target()?.replace("armv7", "arm")) -        } else if self.get_target()?.contains("emscripten") { +        let target = self.get_target()?; +        let program = if target.contains("android") { +            format!("{}-ar", target.replace("armv7", "arm")) +        } else if target.contains("emscripten") {              // Windows use bat files so we have to be a bit more specific              if cfg!(windows) {                  let mut cmd = self.cmd("cmd"); @@ -1972,6 +2018,11 @@ impl Build {              }              "emar".to_string() +        } else if target.contains("msvc") { +            match windows_registry::find(&target, "lib.exe") { +                Some(t) => return Ok((t, "lib.exe".to_string())), +                None => "lib.exe".to_string(), +            }          } else {              "ar".to_string()          }; @@ -2021,7 +2072,7 @@ 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() +            return val.clone();          }          let r = env::var(v).ok();          self.print(&format!("{} = {:?}", v, r)); @@ -2062,10 +2113,11 @@ impl Tool {          let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {              if fname.contains("clang-cl") {                  ToolFamily::Msvc { clang_cl: true } -            } else if fname.contains("cl") && -                !fname.contains("cloudabi") && -                !fname.contains("uclibc") && -                !fname.contains("clang") { +            } 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 @@ -2104,6 +2156,42 @@ impl Tool {          self.args.push(flag);      } +    fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool { +        let flag = flag.to_str().unwrap(); +        let mut chars = flag.chars(); + +        // Only duplicate check compiler flags +        if self.is_like_msvc() { +            if chars.next() != Some('/') { +                return false; +            } +        } else if self.is_like_gnu() || self.is_like_clang() { +            if chars.next() != Some('-') { +                return false; +            } +        } + +        // Check for existing optimization flags (-O, /O) +        if chars.next() == Some('O') { +            return self +                .args() +                .iter() +                .any(|ref a| a.to_str().unwrap_or("").chars().nth(1) == Some('O')); +        } + +        // TODO Check for existing -m..., -m...=..., /arch:... flags +        return false; +    } + +    /// Don't push optimization arg if it conflicts with existing args +    fn push_opt_unless_duplicate(&mut self, flag: OsString) { +        if self.is_duplicate_opt_arg(&flag) { +            println!("Info: Ignoring duplicate arg {:?}", &flag); +        } else { +            self.push_cc_arg(flag); +        } +    } +      /// Converts this compiler into a `Command` that's ready to be run.      ///      /// This is useful for when the compiler needs to be executed and the @@ -2120,7 +2208,11 @@ impl Tool {          };          cmd.args(&self.cc_wrapper_args); -        let value = self.args.iter().filter(|a| !self.removed_args.contains(a)).collect::<Vec<_>>(); +        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() { @@ -2215,7 +2307,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {                      "Failed to wait on spawned child process, command {:?} with args {:?}.",                      cmd, program                  ), -            )) +            ));          }      };      print.join().unwrap(); @@ -2253,7 +2345,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {                      "Failed to wait on spawned child process, command {:?} with args {:?}.",                      cmd, program                  ), -            )) +            ));          }      };      print.join().unwrap(); @@ -2311,7 +2403,8 @@ fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Er  }  fn fail(s: &str) -> ! { -    panic!("\n\nInternal error occurred: {}\n\n", s) +    let _ = writeln!(io::stderr(), "\n\nerror occurred: {}\n\n", s); +    std::process::exit(1);  }  fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool, is_arm: bool) { diff --git a/cc/src/setup_config.rs b/cc/src/setup_config.rs index 7c03d38..56fe114 100644 --- a/cc/src/setup_config.rs +++ b/cc/src/setup_config.rs @@ -11,14 +11,14 @@  use std::ffi::OsString;  use std::ptr::null_mut;  use winapi::Interface; -use winapi::{LPFILETIME, ULONG}; -use winapi::S_FALSE;  use winapi::BSTR;  use winapi::LPCOLESTR; -use winapi::{CoCreateInstance, CLSCTX_ALL};  use winapi::LPSAFEARRAY; +use winapi::S_FALSE; +use winapi::{CoCreateInstance, CLSCTX_ALL};  use winapi::{IUnknown, IUnknownVtbl};  use winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG}; +use winapi::{LPFILETIME, ULONG};  use com::{BStr, ComPtr}; @@ -31,7 +31,7 @@ pub const eRegistered: InstanceState = 2;  pub const eNoRebootRequired: InstanceState = 4;  pub const eComplete: InstanceState = -1i32 as u32; -RIDL!{#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)] +RIDL! {#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)]  interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) {      fn GetInstanceId(          pbstrInstanceId: *mut BSTR, @@ -62,7 +62,7 @@ interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) {      ) -> HRESULT,  }} -RIDL!{#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)] +RIDL! {#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)]  interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtbl) {      fn GetState(          pState: *mut InstanceState, @@ -78,7 +78,7 @@ interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtb      ) -> HRESULT,  }} -RIDL!{#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)] +RIDL! {#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)]  interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) {      fn Next(          celt: ULONG, @@ -94,7 +94,7 @@ interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) {      ) -> HRESULT,  }} -RIDL!{#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)] +RIDL! {#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)]  interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) {      fn EnumInstances(          ppEnumInstances: *mut *mut IEnumSetupInstances, @@ -108,7 +108,7 @@ interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) {      ) -> HRESULT,  }} -RIDL!{#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)] +RIDL! {#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)]  interface ISetupConfiguration2(ISetupConfiguration2Vtbl):      ISetupConfiguration(ISetupConfigurationVtbl) {      fn EnumAllInstances( @@ -116,7 +116,7 @@ interface ISetupConfiguration2(ISetupConfiguration2Vtbl):      ) -> HRESULT,  }} -RIDL!{#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)] +RIDL! {#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)]  interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownVtbl) {      fn GetId(          pbstrId: *mut BSTR, @@ -141,7 +141,7 @@ interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownV      ) -> HRESULT,  }} -RIDL!{#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)] +RIDL! {#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)]  interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {      fn ParseVersion(          pwszVersion: LPCOLESTR, @@ -154,7 +154,7 @@ interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {      ) -> HRESULT,  }} -DEFINE_GUID!{CLSID_SetupConfiguration, +DEFINE_GUID! {CLSID_SetupConfiguration,  0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d}  // Safe wrapper around the COM interfaces diff --git a/cc/src/winapi.rs b/cc/src/winapi.rs index cc83963..c416325 100644 --- a/cc/src/winapi.rs +++ b/cc/src/winapi.rs @@ -115,7 +115,7 @@ macro_rules! DEFINE_GUID {              Data3: $w2,              Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],          }; -    } +    };  }  macro_rules! RIDL { @@ -207,7 +207,7 @@ macro_rules! RIDL {      );  } -RIDL!{#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)] +RIDL! {#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)]  interface IUnknown(IUnknownVtbl) {      fn QueryInterface(          riid: REFIID, diff --git a/cc/src/windows_registry.rs b/cc/src/windows_registry.rs index ca6b989..af812a7 100644 --- a/cc/src/windows_registry.rs +++ b/cc/src/windows_registry.rs @@ -17,10 +17,12 @@ use Tool;  #[cfg(windows)]  macro_rules! otry { -    ($expr:expr) => (match $expr { -        Some(val) => val, -        None => return None, -    }) +    ($expr:expr) => { +        match $expr { +            Some(val) => val, +            None => return None, +        } +    };  }  /// Attempts to find a tool within an MSVC installation using the Windows @@ -103,6 +105,8 @@ pub enum VsVers {      Vs14,      /// Visual Studio 15 (2017)      Vs15, +    /// Visual Studio 16 (2019) +    Vs16,      /// Hidden variant that should not be matched on. Callers that want to      /// handle an enumeration of `VsVers` instances should always have a default @@ -128,6 +132,7 @@ pub fn find_vs_version() -> Result<VsVers, String> {      match env::var("VisualStudioVersion") {          Ok(version) => match &version[..] { +            "16.0" => Ok(VsVers::Vs16),              "15.0" => Ok(VsVers::Vs15),              "14.0" => Ok(VsVers::Vs14),              "12.0" => Ok(VsVers::Vs12), @@ -144,7 +149,9 @@ pub fn find_vs_version() -> Result<VsVers, String> {          _ => {              // Check for the presense of a specific registry key              // that indicates visual studio is installed. -            if impl_::has_msbuild_version("15.0") { +            if impl_::has_msbuild_version("16.0") { +                Ok(VsVers::Vs16) +            } else if impl_::has_msbuild_version("15.0") {                  Ok(VsVers::Vs15)              } else if impl_::has_msbuild_version("14.0") {                  Ok(VsVers::Vs14) @@ -166,15 +173,16 @@ pub fn find_vs_version() -> Result<VsVers, String> {  #[cfg(windows)]  mod impl_ { +    use com; +    use registry::{RegistryKey, LOCAL_MACHINE}; +    use setup_config::{EnumSetupInstances, SetupConfiguration, SetupInstance};      use std::env;      use std::ffi::OsString; -    use std::mem; -    use std::path::{Path, PathBuf};      use std::fs::File;      use std::io::Read; -    use registry::{RegistryKey, LOCAL_MACHINE}; -    use com; -    use setup_config::{EnumSetupInstances, SetupConfiguration, SetupInstance}; +    use std::mem; +    use std::iter; +    use std::path::{Path, PathBuf};      use Tool; @@ -210,6 +218,41 @@ mod impl_ {          }      } +    fn vs16_instances() -> Box<Iterator<Item=PathBuf>> { +        let instances = if let Some(instances) = vs15_instances() { +            instances +        } else { +            return Box::new(iter::empty()); +        }; +        Box::new(instances.filter_map(|instance| { +            let instance = otry!(instance.ok()); +            let installation_name = otry!(instance.installation_name().ok()); +            if otry!(installation_name.to_str()).starts_with("VisualStudio/16.") { +                Some(PathBuf::from(otry!(instance.installation_path().ok()))) +            } else { +                None +            } +        })) +    } + +    fn find_tool_in_vs16_path(tool: &str, target: &str) -> Option<Tool> { +        vs16_instances().filter_map(|path| { +            let path = path.join(tool); +            if !path.is_file() { +                return None; +            } +            let mut tool = Tool::new(path); +            if target.contains("x86_64") { +                tool.env.push(("Platform".into(), "X64".into())); +            } +            Some(tool) +        }).next() +    } + +    fn find_msbuild_vs16(target: &str) -> Option<Tool> { +        find_tool_in_vs16_path(r"MSBuild\Current\Bin\MSBuild.exe", target) +    } +      // In MSVC 15 (2017) MS once again changed the scheme for locating      // the tooling.  Now we must go through some COM interfaces, which      // is super fun for Rust. @@ -251,7 +294,8 @@ mod impl_ {                      instance                          .ok()                          .and_then(|instance| instance.installation_path().ok()) -                }).map(|path| PathBuf::from(path).join(tool)) +                }) +                .map(|path| PathBuf::from(path).join(tool))                  .find(|ref path| path.is_file()),              None => None,          }; @@ -263,7 +307,7 @@ mod impl_ {                  .ok()                  .and_then(|key| key.query_str("15.0").ok())                  .map(|path| PathBuf::from(path).join(tool)) -                .filter(|ref path| path.is_file()); +                .and_then(|path| if path.is_file() { Some(path) } else { None });          }          path.map(|path| { @@ -319,13 +363,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") +        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") +        let host_dylib_path = path +            .join("bin")              .join(&format!("Host{}", host))              .join(&host.to_lowercase());          let lib_path = path.join("lib").join(&target); @@ -478,17 +524,16 @@ 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() -                    .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() -        ); +        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());          let version = max_libdir.components().last().unwrap();          let version = version.as_os_str().to_str().unwrap().to_string();          Some((root.into(), version)) @@ -512,12 +557,11 @@ mod impl_ {              .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)) @@ -637,7 +681,7 @@ mod impl_ {          for subkey in key.iter().filter_map(|k| k.ok()) {              let val = subkey                  .to_str() -                .and_then(|s| s.trim_start_matches("v").replace(".", "").parse().ok()); +                .and_then(|s| s.trim_left_matches("v").replace(".", "").parse().ok());              let val = match val {                  Some(s) => s,                  None => continue, @@ -654,6 +698,10 @@ mod impl_ {      pub fn has_msbuild_version(version: &str) -> bool {          match version { +            "16.0" => { +                find_msbuild_vs16("x86_64-pc-windows-msvc").is_some() +                    || find_msbuild_vs16("i686-pc-windows-msvc").is_some() +            }              "15.0" => {                  find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()                      || find_msbuild_vs15("i686-pc-windows-msvc").is_some() | 
