$(foreach host,$(CFG_HOST), \
$(foreach target,$(CFG_TARGET), \
- $(foreach stage,$(STAGES), \
- $(foreach crate,$(CRATES), \
- $(eval $(call SETUP_LIB_MSVC_ENV_VARS,$(stage),$(target),$(host),$(crate)))))))
+ $(foreach crate,$(CRATES), \
+ $(eval $(call SETUP_LIB_MSVC_ENV_VARS,0,$(target),$(host),$(crate))))))
$(foreach host,$(CFG_HOST), \
$(foreach target,$(CFG_TARGET), \
- $(foreach stage,$(STAGES), \
- $(foreach tool,$(TOOLS), \
- $(eval $(call SETUP_TOOL_MSVC_ENV_VARS,$(stage),$(target),$(host),$(tool)))))))
+ $(foreach tool,$(TOOLS), \
+ $(eval $(call SETUP_TOOL_MSVC_ENV_VARS,0,$(target),$(host),$(tool))))))
use super::linker::{Linker, GnuLinker, MsvcLinker};
use super::rpath::RPathConfig;
use super::rpath;
+use super::msvc;
use super::svh::Svh;
use session::config;
use session::config::NoDebugInfo;
mangle(path.chain(Some(gensym_name(flav))), None)
}
-pub fn get_cc_prog(sess: &Session) -> String {
- match sess.opts.cg.linker {
- Some(ref linker) => return linker.to_string(),
- None => sess.target.target.options.linker.clone(),
+pub fn get_linker(sess: &Session) -> (String, Command) {
+ if let Some(ref linker) = sess.opts.cg.linker {
+ (linker.clone(), Command::new(linker))
+ } else if sess.target.target.options.is_like_msvc {
+ ("link.exe".to_string(), msvc::link_exe_cmd(sess))
+ } else {
+ (sess.target.target.options.linker.clone(),
+ Command::new(&sess.target.target.options.linker))
}
}
let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
// The invocations of cc share some flags across platforms
- let pname = get_cc_prog(sess);
- let mut cmd = Command::new(&pname);
+ let (pname, mut cmd) = get_linker(sess);
cmd.env("PATH", command_path(sess));
let root = sess.target_filesearch(PathKind::Native).get_lib_path();
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! MSVC-specific logic for linkers and such.
+//!
+//! This module contains a cross-platform interface but has a blank unix
+//! implementation. The Windows implementation builds on top of Windows native
+//! libraries (reading registry keys), so it otherwise wouldn't link on unix.
+//!
+//! Note that we don't have much special logic for finding the system linker on
+//! any other platforms, so it may seem a little odd to single out MSVC to have
+//! a good deal of code just to find the linker. Unlike Unix systems, however,
+//! the MSVC linker is not in the system PATH by default. It also additionally
+//! needs a few environment variables or command line flags to be able to link
+//! against system libraries.
+//!
+//! In order to have a nice smooth experience on Windows, the logic in this file
+//! is here to find the MSVC linker and set it up in the default configuration
+//! one would need to set up anyway. This means that the Rust compiler can be
+//! run not only in the developer shells of MSVC but also the standard cmd.exe
+//! shell or MSYS shells.
+//!
+//! As a high-level note, all logic in this module for looking up various
+//! paths/files is copied over from Clang in its MSVCToolChain.cpp file, but
+//! comments can also be found below leading through the various code paths.
+
+use std::process::Command;
+use session::Session;
+
+#[cfg(windows)]
+mod registry;
+
+#[cfg(windows)]
+pub fn link_exe_cmd(sess: &Session) -> Command {
+ use std::env;
+ use std::ffi::OsString;
+ use std::fs;
+ use std::path::PathBuf;
+ use self::registry::{RegistryKey, LOCAL_MACHINE};
+
+ // When finding the link.exe binary the 32-bit version is at the top level
+ // but the versions to cross to other architectures are stored in
+ // sub-folders. Unknown architectures also just bail out early to return the
+ // standard `link.exe` command.
+ let extra = match &sess.target.target.arch[..] {
+ "x86" => "",
+ "x86_64" => "amd64",
+ "arm" => "arm",
+ _ => return Command::new("link.exe"),
+ };
+
+ let vs_install_dir = get_vs_install_dir();
+
+ // First up, we need to find the `link.exe` binary itself, and there's a few
+ // locations that we can look. First up is the standard VCINSTALLDIR
+ // environment variable which is normally set by the vcvarsall.bat file. If
+ // an environment is set up manually by whomever's driving the compiler then
+ // we shouldn't muck with that decision and should instead respect that.
+ //
+ // Next up is looking in PATH itself. Here we look for `cl.exe` and then
+ // assume that `link.exe` is next to it if we find it. Note that we look for
+ // `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in
+ // PATH but we're not interested in finding that.
+ //
+ // Finally we read the Windows registry to discover the VS install root.
+ // From here we probe for `link.exe` just to make sure that it exists.
+ let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| {
+ let mut p = PathBuf::from(dir);
+ p.push("bin");
+ p.push(extra);
+ p.push("link.exe");
+ if fs::metadata(&p).is_ok() {Some(p)} else {None}
+ }).or_else(|| {
+ env::var_os("PATH").and_then(|path| {
+ env::split_paths(&path).find(|path| {
+ fs::metadata(&path.join("cl.exe")).is_ok()
+ }).map(|p| {
+ p.join("link.exe")
+ })
+ })
+ }).or_else(|| {
+ vs_install_dir.as_ref().and_then(|p| {
+ let mut p = p.join("VC/bin");
+ p.push(extra);
+ p.push("link.exe");
+ if fs::metadata(&p).is_ok() {Some(p)} else {None}
+ })
+ }).map(|linker| {
+ Command::new(linker)
+ }).unwrap_or_else(|| {
+ Command::new("link.exe")
+ });
+
+ // The MSVC linker uses the LIB environment variable as the default lookup
+ // path for libraries. This environment variable is normally set up by the
+ // VS shells, so we only want to start adding our own pieces if it's not
+ // set.
+ //
+ // If we're adding our own pieces, then we need to add two primary
+ // directories to the default search path for the linker. The first is in
+ // the VS install direcotry and the next is the Windows SDK directory.
+ if env::var_os("LIB").is_none() {
+ if let Some(mut vs_install_dir) = vs_install_dir {
+ vs_install_dir.push("VC/lib");
+ vs_install_dir.push(extra);
+ let mut arg = OsString::from("/LIBPATH:");
+ arg.push(&vs_install_dir);
+ cmd.arg(arg);
+ }
+ if let Some(path) = get_windows_sdk_lib_path(sess) {
+ let mut arg = OsString::from("/LIBPATH:");
+ arg.push(&path);
+ cmd.arg(arg);
+ }
+ }
+
+ return cmd;
+
+ // When looking for the Visual Studio installation directory we look in a
+ // number of locations in varying degrees of precedence:
+ //
+ // 1. The Visual Studio registry keys
+ // 2. The Visual Studio Express registry keys
+ // 3. A number of somewhat standard environment variables
+ //
+ // If we find a hit from any of these keys then we strip off the IDE/Tools
+ // folders which are typically found at the end.
+ //
+ // As a final note, when we take a look at the registry keys they're
+ // typically found underneath the version of what's installed, but we don't
+ // quite know what's installed. As a result we probe all sub-keys of the two
+ // keys we're looking at to find out the maximum version of what's installed
+ // and we use that root directory.
+ fn get_vs_install_dir() -> Option<PathBuf> {
+ LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| {
+ LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref())
+ }).ok().and_then(|key| {
+ max_version(&key).and_then(|(_vers, key)| {
+ key.query_str("InstallDir").ok()
+ })
+ }).or_else(|| {
+ env::var_os("VS120COMNTOOLS")
+ }).or_else(|| {
+ env::var_os("VS100COMNTOOLS")
+ }).or_else(|| {
+ env::var_os("VS90COMNTOOLS")
+ }).or_else(|| {
+ env::var_os("VS80COMNTOOLS")
+ }).map(PathBuf::from).and_then(|mut dir| {
+ if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") {
+ dir.pop();
+ dir.pop();
+ Some(dir)
+ } else {
+ None
+ }
+ })
+ }
+
+ // Given a registry key, look at all the sub keys and find the one which has
+ // the maximal numeric value.
+ //
+ // Returns the name of the maximal key as well as the opened maximal key.
+ fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
+ 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().and_then(|s| {
+ s.trim_left_matches("v").replace(".", "").parse().ok()
+ });
+ let val = match val {
+ Some(s) => s,
+ None => continue,
+ };
+ if val > max_vers {
+ if let Ok(k) = key.open(&subkey) {
+ max_vers = val;
+ max_key = Some((subkey, k));
+ }
+ }
+ }
+ return max_key
+ }
+
+ fn get_windows_sdk_lib_path(sess: &Session) -> Option<PathBuf> {
+ let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
+ let key = LOCAL_MACHINE.open(key.as_ref());
+ let (n, k) = match key.ok().as_ref().and_then(max_version) {
+ Some(p) => p,
+ None => return None,
+ };
+ let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, ".");
+ let major = parts.next().unwrap().parse::<usize>().unwrap();
+ let _minor = parts.next().unwrap().parse::<usize>().unwrap();
+ let path = match k.query_str("InstallationFolder") {
+ Ok(p) => PathBuf::from(p).join("Lib"),
+ Err(..) => return None,
+ };
+ if major <= 7 {
+ // In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
+ // x64 libraries are inside, and it's not necessary to link agains
+ // the SDK 7.x when targeting ARM or other architectures.
+ let x86 = match &sess.target.target.arch[..] {
+ "x86" => true,
+ "x86_64" => false,
+ _ => return None,
+ };
+ Some(if x86 {path} else {path.join("x64")})
+ } else {
+ // Windows SDK 8.x installes libraries in a folder whose names
+ // depend on the version of the OS you're targeting. By default
+ // choose the newest, which usually corresponds to the version of
+ // the OS you've installed the SDK on.
+ let extra = match &sess.target.target.arch[..] {
+ "x86" => "x86",
+ "x86_64" => "x64",
+ "arm" => "arm",
+ _ => return None,
+ };
+ ["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| {
+ fs::metadata(part).is_ok()
+ }).map(|path| {
+ path.join("um").join(extra)
+ })
+ }
+ }
+}
+
+#[cfg(not(windows))]
+pub fn link_exe_cmd(_sess: &Session) -> Command {
+ Command::new("link.exe")
+}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::io;
+use std::ffi::{OsString, OsStr};
+use std::os::windows::prelude::*;
+use std::ops::RangeFrom;
+use libc::{DWORD, LPCWSTR, LONG, LPDWORD, LPBYTE, ERROR_SUCCESS};
+
+const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY;
+const KEY_WOW64_32KEY: REGSAM = 0x0200;
+const KEY_READ: REGSAM = (STANDARD_RIGTS_READ | KEY_QUERY_VALUE |
+ KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & !SYNCHRONIZE;
+const STANDARD_RIGTS_READ: REGSAM = READ_CONTROL;
+const READ_CONTROL: REGSAM = 0x00020000;
+const KEY_QUERY_VALUE: REGSAM = 0x0001;
+const KEY_ENUMERATE_SUB_KEYS: REGSAM = 0x0008;
+const KEY_NOTIFY: REGSAM = 0x0010;
+const SYNCHRONIZE: REGSAM = 0x00100000;
+const REG_SZ: DWORD = 1;
+const ERROR_NO_MORE_ITEMS: DWORD = 259;
+
+enum __HKEY__ {}
+pub type HKEY = *mut __HKEY__;
+pub type PHKEY = *mut HKEY;
+pub type REGSAM = DWORD;
+pub type LPWSTR = *mut u16;
+pub type PFILETIME = *mut ();
+
+#[link(name = "advapi32")]
+extern "system" {
+ fn RegOpenKeyExW(hKey: HKEY,
+ lpSubKey: LPCWSTR,
+ ulOptions: DWORD,
+ samDesired: REGSAM,
+ phkResult: PHKEY) -> LONG;
+ fn RegQueryValueExW(hKey: HKEY,
+ lpValueName: LPCWSTR,
+ lpReserved: LPDWORD,
+ lpType: LPDWORD,
+ lpData: LPBYTE,
+ lpcbData: LPDWORD) -> LONG;
+ fn RegEnumKeyExW(hKey: HKEY,
+ dwIndex: DWORD,
+ lpName: LPWSTR,
+ lpcName: LPDWORD,
+ lpReserved: LPDWORD,
+ lpClass: LPWSTR,
+ lpcClass: LPDWORD,
+ lpftLastWriteTime: PFILETIME) -> LONG;
+ fn RegCloseKey(hKey: HKEY) -> LONG;
+}
+
+pub struct RegistryKey(Repr);
+
+struct OwnedKey(HKEY);
+
+enum Repr {
+ Const(HKEY),
+ Owned(OwnedKey),
+}
+
+pub struct Iter<'a> {
+ idx: RangeFrom<DWORD>,
+ key: &'a RegistryKey,
+}
+
+unsafe impl Sync for RegistryKey {}
+unsafe impl Send for RegistryKey {}
+
+pub static LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE));
+
+impl RegistryKey {
+ fn raw(&self) -> HKEY {
+ match self.0 {
+ Repr::Const(val) => val,
+ Repr::Owned(ref val) => val.0,
+ }
+ }
+
+ pub fn open(&self, key: &OsStr) -> io::Result<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)
+ };
+ if err == ERROR_SUCCESS {
+ Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
+ } else {
+ Err(io::Error::from_raw_os_error(err as i32))
+ }
+ }
+
+ pub fn iter(&self) -> Iter {
+ Iter { idx: 0.., key: self }
+ }
+
+ pub fn query_str(&self, name: &str) -> io::Result<OsString> {
+ let name: &OsStr = name.as_ref();
+ let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
+ 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);
+ if err != ERROR_SUCCESS {
+ 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"))
+ }
+
+ // 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);
+ if err != ERROR_SUCCESS {
+ return Err(io::Error::from_raw_os_error(err as i32))
+ }
+ v.set_len(len as usize / 2);
+
+ // Some registry keys may have a terminating nul character, but
+ // we're not interested in that, so chop it off if it's there.
+ if v[v.len() - 1] == 0 {
+ v.pop();
+ }
+ Ok(OsString::from_wide(&v))
+ }
+ }
+}
+
+impl Drop for OwnedKey {
+ fn drop(&mut self) {
+ unsafe { RegCloseKey(self.0); }
+ }
+}
+
+impl<'a> Iterator for Iter<'a> {
+ type Item = io::Result<OsString>;
+
+ fn next(&mut self) -> Option<io::Result<OsString>> {
+ 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 _);
+ if ret == ERROR_NO_MORE_ITEMS as LONG {
+ None
+ } else if ret != ERROR_SUCCESS {
+ Some(Err(io::Error::from_raw_os_error(ret as i32)))
+ } else {
+ v.set_len(len as usize);
+ Some(Ok(OsString::from_wide(&v)))
+ }
+ })
+ }
+}
// except according to those terms.
use back::lto;
-use back::link::{get_cc_prog, remove};
+use back::link::{get_linker, remove};
use session::config::{OutputFilenames, Passes, SomePasses, AllPasses};
use session::Session;
use session::config;
use std::fs;
use std::mem;
use std::path::Path;
-use std::process::{Command, Stdio};
+use std::process::Stdio;
use std::ptr;
use std::str;
use std::sync::{Arc, Mutex};
None
};
- let pname = get_cc_prog(sess);
- let mut cmd = Command::new(&pname[..]);
+ let (pname, mut cmd) = get_linker(sess);
cmd.args(&sess.target.target.options.pre_link_args);
cmd.arg("-nostdlib");
},
Err(e) => {
sess.err(&format!("could not exec the linker `{}`: {}",
- pname,
- e));
+ pname, e));
sess.abort_if_errors();
},
}
}
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
- let pname = get_cc_prog(sess);
- let mut cmd = Command::new(&pname[..]);
+ let (pname, mut cmd) = get_linker(sess);
cmd.arg("-c").arg("-o").arg(&outputs.path(config::OutputTypeObject))
.arg(&outputs.temp_path(config::OutputTypeAssembly));
}
},
Err(e) => {
- sess.err(&format!("could not exec the linker `{}`: {}",
- pname,
- e));
+ sess.err(&format!("could not exec the linker `{}`: {}", pname, e));
sess.abort_if_errors();
}
}
pub mod link;
pub mod lto;
pub mod write;
-
+ pub mod msvc;
}
pub mod trans;