Skip to content

Add host toolchain DLLs to PATH when executing link.exe #31064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ fn command_path(sess: &Session) -> OsString {
if let Some(path) = env::var_os("PATH") {
new_path.extend(env::split_paths(&path));
}
if sess.target.target.options.is_like_msvc {
new_path.extend(msvc::host_dll_path());
}
env::join_paths(new_path).unwrap()
}

Expand Down
186 changes: 103 additions & 83 deletions src/librustc_trans/back/msvc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,98 +31,106 @@
//! paths/files is based on Microsoft's logic in their vcvars bat files, 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 {
mod platform {
use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
use self::registry::{LOCAL_MACHINE};

let arch = &sess.target.target.arch;
let (binsub, libsub, vclibsub) =
match (bin_subdir(arch), lib_subdir(arch), vc_lib_subdir(arch)) {
(Some(x), Some(y), Some(z)) => (x, y, z),
_ => return Command::new("link.exe"),
};
use std::process::Command;
use session::Session;
use super::registry::{LOCAL_MACHINE};

// First we need to figure out whether the environment is already correctly
// configured by vcvars. We do this by looking at the environment variable
// `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set
// otherwise. If it is defined, then we derive the path to `link.exe` from
// that and trust that everything else is configured correctly.
//
// If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where it
// claimed it should be), then we resort to finding everything ourselves.
// First we find where the latest version of MSVC is installed and what
// version it is. Then based on the version we find the appropriate SDKs.
//
// For MSVC 14 (VS 2015) we look for the Win10 SDK and failing that we look
// for the Win8.1 SDK. We also look for the Universal CRT.
//
// For MSVC 12 (VS 2013) we look for the Win8.1 SDK.
//
// For MSVC 11 (VS 2012) we look for the Win8 SDK.
//
// For all other versions the user has to execute the appropriate vcvars bat
// file themselves to configure the environment.
//
// If despite our best efforts we are still unable to find MSVC then we just
// blindly call `link.exe` and hope for the best.
return env::var_os("VCINSTALLDIR").and_then(|dir| {
debug!("Environment already configured by user. Assuming it works.");
let mut p = PathBuf::from(dir);
p.push("bin");
p.push(binsub);
p.push("link.exe");
if !p.is_file() { return None }
Some(Command::new(p))
}).or_else(|| {
get_vc_dir().and_then(|(ver, vcdir)| {
debug!("Found VC installation directory {:?}", vcdir);
let mut linker = vcdir.clone();
linker.push("bin");
linker.push(binsub);
linker.push("link.exe");
if !linker.is_file() { return None }
let mut cmd = Command::new(linker);
add_lib(&mut cmd, &vcdir.join("lib").join(vclibsub));
if ver == "14.0" {
if let Some(dir) = get_ucrt_dir() {
debug!("Found Universal CRT {:?}", dir);
add_lib(&mut cmd, &dir.join("ucrt").join(libsub));
}
if let Some(dir) = get_sdk10_dir() {
debug!("Found Win10 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
} else if let Some(dir) = get_sdk81_dir() {
debug!("Found Win8.1 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
}
} else if ver == "12.0" {
if let Some(dir) = get_sdk81_dir() {
debug!("Found Win8.1 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
}
} else { // ver == "11.0"
if let Some(dir) = get_sdk8_dir() {
debug!("Found Win8 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
}
}
Some(cmd)
// Cross toolchains depend on dlls from the host toolchain
// We can't just add it to the Command's PATH in `link_exe_cmd` because it
// is later overridden so we publicly expose it here instead
pub fn host_dll_path() -> Option<PathBuf> {
get_vc_dir().and_then(|(_, vcdir)| {
host_dll_subdir().map(|sub| {
vcdir.join("bin").join(sub)
})
})
}).unwrap_or_else(|| {
debug!("Failed to locate linker.");
Command::new("link.exe")
});
}

pub fn link_exe_cmd(sess: &Session) -> Command {
let arch = &sess.target.target.arch;
let (binsub, libsub, vclibsub) =
match (bin_subdir(arch), lib_subdir(arch), vc_lib_subdir(arch)) {
(Some(x), Some(y), Some(z)) => (x, y, z),
_ => return Command::new("link.exe"),
};

// First we need to figure out whether the environment is already correctly
// configured by vcvars. We do this by looking at the environment variable
// `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set
// otherwise. If it is defined, then we derive the path to `link.exe` from
// that and trust that everything else is configured correctly.
//
// If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where it
// claimed it should be), then we resort to finding everything ourselves.
// First we find where the latest version of MSVC is installed and what
// version it is. Then based on the version we find the appropriate SDKs.
//
// For MSVC 14 (VS 2015) we look for the Win10 SDK and failing that we look
// for the Win8.1 SDK. We also look for the Universal CRT.
//
// For MSVC 12 (VS 2013) we look for the Win8.1 SDK.
//
// For MSVC 11 (VS 2012) we look for the Win8 SDK.
//
// For all other versions the user has to execute the appropriate vcvars bat
// file themselves to configure the environment.
//
// If despite our best efforts we are still unable to find MSVC then we just
// blindly call `link.exe` and hope for the best.
return env::var_os("VCINSTALLDIR").and_then(|dir| {
debug!("Environment already configured by user. Assuming it works.");
let mut p = PathBuf::from(dir);
p.push("bin");
p.push(binsub);
p.push("link.exe");
if !p.is_file() { return None }
Some(Command::new(p))
}).or_else(|| {
get_vc_dir().and_then(|(ver, vcdir)| {
debug!("Found VC installation directory {:?}", vcdir);
let linker = vcdir.clone().join("bin").join(binsub).join("link.exe");
if !linker.is_file() { return None }
let mut cmd = Command::new(linker);
add_lib(&mut cmd, &vcdir.join("lib").join(vclibsub));
if ver == "14.0" {
if let Some(dir) = get_ucrt_dir() {
debug!("Found Universal CRT {:?}", dir);
add_lib(&mut cmd, &dir.join("ucrt").join(libsub));
}
if let Some(dir) = get_sdk10_dir() {
debug!("Found Win10 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
} else if let Some(dir) = get_sdk81_dir() {
debug!("Found Win8.1 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
}
} else if ver == "12.0" {
if let Some(dir) = get_sdk81_dir() {
debug!("Found Win8.1 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
}
} else { // ver == "11.0"
if let Some(dir) = get_sdk8_dir() {
debug!("Found Win8 SDK {:?}", dir);
add_lib(&mut cmd, &dir.join("um").join(libsub));
}
}
Some(cmd)
})
}).unwrap_or_else(|| {
debug!("Failed to locate linker.");
Command::new("link.exe")
});
}
// A convenience function to make the above code simpler
fn add_lib(cmd: &mut Command, lib: &Path) {
let mut arg: OsString = "/LIBPATH:".into();
Expand Down Expand Up @@ -257,11 +265,23 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
_ => None,
}
}
fn host_dll_subdir() -> Option<&'static str> {
if cfg!(target_arch = "x86_64") { Some("amd64") }
else if cfg!(target_arch = "x86") { Some("") }
else { None }
}
}

// If we're not on Windows, then there's no registry to search through and MSVC
// wouldn't be able to run, so we just call `link.exe` and hope for the best.
#[cfg(not(windows))]
pub fn link_exe_cmd(_sess: &Session) -> Command {
Command::new("link.exe")
mod platform {
use std::path::PathBuf;
use std::process::Command;
use session::Session;
pub fn link_exe_cmd(_sess: &Session) -> Command {
Command::new("link.exe")
}
pub fn host_dll_path() -> Option<PathBuf> { None }
}
pub use self::platform::*;