1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! MSVC-specific logic for linkers and such.
13 //! This module contains a cross-platform interface but has a blank unix
14 //! implementation. The Windows implementation builds on top of Windows native
15 //! libraries (reading registry keys), so it otherwise wouldn't link on unix.
17 //! Note that we don't have much special logic for finding the system linker on
18 //! any other platforms, so it may seem a little odd to single out MSVC to have
19 //! a good deal of code just to find the linker. Unlike Unix systems, however,
20 //! the MSVC linker is not in the system PATH by default. It also additionally
21 //! needs a few environment variables or command line flags to be able to link
22 //! against system libraries.
24 //! In order to have a nice smooth experience on Windows, the logic in this file
25 //! is here to find the MSVC linker and set it up in the default configuration
26 //! one would need to set up anyway. This means that the Rust compiler can be
27 //! run not only in the developer shells of MSVC but also the standard cmd.exe
28 //! shell or MSYS shells.
30 //! As a high-level note, all logic in this module for looking up various
31 //! paths/files is based on Microsoft's logic in their vcvars bat files, but
32 //! comments can also be found below leading through the various code paths.
34 // A simple macro to make this option mess easier to read
37 ($expr:expr) => (match $expr {
51 use std::ffi::OsString;
53 use std::path::{Path, PathBuf};
54 use std::process::Command;
56 use super::arch::{host_arch, Arch};
57 use super::registry::LOCAL_MACHINE;
59 // First we need to figure out whether the environment is already correctly
60 // configured by vcvars. We do this by looking at the environment variable
61 // `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set
62 // otherwise. If it is defined, then we find `link.exe` in `PATH and trust
63 // that everything else is configured correctly.
65 // If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where
66 // it claimed it should be), then we resort to finding everything
67 // ourselves. First we find where the latest version of MSVC is installed
68 // and what version it is. Then based on the version we find the
71 // If despite our best efforts we are still unable to find MSVC then we
72 // just blindly call `link.exe` and hope for the best.
74 // This code only supports VC 11 through 15. For versions older than that
75 // the user will need to manually execute the appropriate vcvars bat file
76 // and it should hopefully work.
78 // The second member of the tuple we return is the directory for the host
79 // linker toolchain, which is necessary when using the cross linkers.
80 pub fn link_exe_cmd(sess: &Session) -> (Command, Option<PathBuf>) {
81 let arch = &sess.target.target.arch;
82 env::var_os("VCINSTALLDIR").and_then(|_| {
83 debug!("Detected that vcvars was already run.");
84 let path = otry!(env::var_os("PATH"));
85 // Mingw has its own link which is not the link we want so we
86 // look for `cl.exe` too as a precaution.
87 env::split_paths(&path).find(|path| {
88 path.join("cl.exe").is_file()
89 && path.join("link.exe").is_file()
91 (Command::new(path.join("link.exe")), None)
95 find_msvc_latest(arch, "15.0")
97 find_msvc_latest(arch, "14.0")
102 }).map(|(cmd, path)| (cmd, Some(path)))
103 }).unwrap_or_else(|| {
104 debug!("Failed to locate linker.");
105 (Command::new("link.exe"), None)
109 // For MSVC 14 or newer we need to find the Universal CRT as well as either
110 // the Windows 10 SDK or Windows 8.1 SDK.
111 fn find_msvc_latest(arch: &str, ver: &str) -> Option<(Command, PathBuf)> {
112 let vcdir = otry!(get_vc_dir(ver));
113 let (mut cmd, host) = otry!(get_linker(&vcdir, arch));
114 let sub = otry!(lib_subdir(arch));
115 let ucrt = otry!(get_ucrt_dir());
116 debug!("Found Universal CRT {:?}", ucrt);
117 add_lib(&mut cmd, &ucrt.join("ucrt").join(sub));
118 if let Some(dir) = get_sdk10_dir() {
119 debug!("Found Win10 SDK {:?}", dir);
120 add_lib(&mut cmd, &dir.join("um").join(sub));
121 } else if let Some(dir) = get_sdk81_dir() {
122 debug!("Found Win8.1 SDK {:?}", dir);
123 add_lib(&mut cmd, &dir.join("um").join(sub));
130 // For MSVC 12 we need to find the Windows 8.1 SDK.
131 fn find_msvc_12(arch: &str) -> Option<(Command, PathBuf)> {
132 let vcdir = otry!(get_vc_dir("12.0"));
133 let (mut cmd, host) = otry!(get_linker(&vcdir, arch));
134 let sub = otry!(lib_subdir(arch));
135 let sdk81 = otry!(get_sdk81_dir());
136 debug!("Found Win8.1 SDK {:?}", sdk81);
137 add_lib(&mut cmd, &sdk81.join("um").join(sub));
141 // For MSVC 11 we need to find the Windows 8 SDK.
142 fn find_msvc_11(arch: &str) -> Option<(Command, PathBuf)> {
143 let vcdir = otry!(get_vc_dir("11.0"));
144 let (mut cmd, host) = otry!(get_linker(&vcdir, arch));
145 let sub = otry!(lib_subdir(arch));
146 let sdk8 = otry!(get_sdk8_dir());
147 debug!("Found Win8 SDK {:?}", sdk8);
148 add_lib(&mut cmd, &sdk8.join("um").join(sub));
152 // A convenience function to append library paths.
153 fn add_lib(cmd: &mut Command, lib: &Path) {
154 let mut arg: OsString = "/LIBPATH:".into();
159 // Given a possible MSVC installation directory, we look for the linker and
160 // then add the MSVC library path.
161 fn get_linker(path: &Path, arch: &str) -> Option<(Command, PathBuf)> {
162 debug!("Looking for linker in {:?}", path);
163 bin_subdir(arch).into_iter().map(|(sub, host)| {
164 (path.join("bin").join(sub).join("link.exe"),
165 path.join("bin").join(host))
166 }).filter(|&(ref path, _)| {
168 }).map(|(path, host)| {
169 (Command::new(path), host)
170 }).filter_map(|(mut cmd, host)| {
171 let sub = otry!(vc_lib_subdir(arch));
172 add_lib(&mut cmd, &path.join("lib").join(sub));
177 // To find MSVC we look in a specific registry key for the version we are
179 fn get_vc_dir(ver: &str) -> Option<PathBuf> {
180 let key = otry!(LOCAL_MACHINE
181 .open(r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7".as_ref()).ok());
182 let path = otry!(key.query_str(ver).ok());
186 // To find the Universal CRT we look in a specific registry key for where
187 // all the Universal CRTs are located and then sort them asciibetically to
188 // find the newest version. While this sort of sorting isn't ideal, it is
189 // what vcvars does so that's good enough for us.
190 fn get_ucrt_dir() -> Option<PathBuf> {
191 let key = otry!(LOCAL_MACHINE
192 .open(r"SOFTWARE\Microsoft\Windows Kits\Installed Roots".as_ref()).ok());
193 let root = otry!(key.query_str("KitsRoot10").ok());
194 let readdir = otry!(fs::read_dir(Path::new(&root).join("lib")).ok());
195 readdir.filter_map(|dir| {
200 dir.components().last().and_then(|c| {
201 c.as_os_str().to_str()
203 c.starts_with("10.") && dir.join("ucrt").is_dir()
208 // Vcvars finds the correct version of the Windows 10 SDK by looking
209 // for the include `um\Windows.h` because sometimes a given version will
210 // only have UCRT bits without the rest of the SDK. Since we only care about
211 // libraries and not includes, we instead look for `um\x64\kernel32.lib`.
212 // Since the 32-bit and 64-bit libraries are always installed together we
213 // only need to bother checking x64, making this code a tiny bit simpler.
214 // Like we do for the Universal CRT, we sort the possibilities
215 // asciibetically to find the newest one as that is what vcvars does.
216 fn get_sdk10_dir() -> Option<PathBuf> {
217 let key = otry!(LOCAL_MACHINE
218 .open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0".as_ref()).ok());
219 let root = otry!(key.query_str("InstallationFolder").ok());
220 let readdir = otry!(fs::read_dir(Path::new(&root).join("lib")).ok());
221 let mut dirs: Vec<_> = readdir.filter_map(|dir| dir.ok())
222 .map(|dir| dir.path()).collect();
224 dirs.into_iter().rev().filter(|dir| {
225 dir.join("um").join("x64").join("kernel32.lib").is_file()
229 // Interestingly there are several subdirectories, `win7` `win8` and
230 // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
231 // applies to us. Note that if we were targetting kernel mode drivers
232 // instead of user mode applications, we would care.
233 fn get_sdk81_dir() -> Option<PathBuf> {
234 let key = otry!(LOCAL_MACHINE
235 .open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1".as_ref()).ok());
236 let root = otry!(key.query_str("InstallationFolder").ok());
237 Some(Path::new(&root).join("lib").join("winv6.3"))
240 fn get_sdk8_dir() -> Option<PathBuf> {
241 let key = otry!(LOCAL_MACHINE
242 .open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0".as_ref()).ok());
243 let root = otry!(key.query_str("InstallationFolder").ok());
244 Some(Path::new(&root).join("lib").join("win8"))
247 // When choosing the linker toolchain to use, we have to choose the one
248 // which matches the host architecture. Otherwise we end up in situations
249 // where someone on 32-bit Windows is trying to cross compile to 64-bit and
250 // it tries to invoke the native 64-bit linker which won't work.
252 // For the return value of this function, the first member of the tuple is
253 // the folder of the linker we will be invoking, while the second member
254 // is the folder of the host toolchain for that linker which is essential
255 // when using a cross linker. We return a Vec since on x64 there are often
256 // two linkers that can target the architecture we desire. The 64-bit host
257 // linker is preferred, and hence first, due to 64-bit allowing it more
258 // address space to work with and potentially being faster.
260 // FIXME - Figure out what happens when the host architecture is arm.
261 fn bin_subdir(arch: &str) -> Vec<(&'static str, &'static str)> {
262 match (arch, host_arch()) {
263 ("x86", Some(Arch::X86)) => vec![("", "")],
264 ("x86", Some(Arch::Amd64)) => vec![("amd64_x86", "amd64"), ("", "")],
265 ("x86_64", Some(Arch::X86)) => vec![("x86_amd64", "")],
266 ("x86_64", Some(Arch::Amd64)) => vec![("amd64", "amd64"), ("x86_amd64", "")],
267 ("arm", Some(Arch::X86)) => vec![("x86_arm", "")],
268 ("arm", Some(Arch::Amd64)) => vec![("amd64_arm", "amd64"), ("x86_arm", "")],
273 fn lib_subdir(arch: &str) -> Option<&'static str> {
275 "x86" => Some("x86"),
276 "x86_64" => Some("x64"),
277 "arm" => Some("arm"),
282 // MSVC's x86 libraries are not in a subfolder
283 fn vc_lib_subdir(arch: &str) -> Option<&'static str> {
286 "x86_64" => Some("amd64"),
287 "arm" => Some("arm"),
293 // If we're not on Windows, then there's no registry to search through and MSVC
294 // wouldn't be able to run, so we just call `link.exe` and hope for the best.
297 use std::path::PathBuf;
298 use std::process::Command;
299 use session::Session;
300 pub fn link_exe_cmd(_sess: &Session) -> (Command, Option<PathBuf>) {
301 (Command::new("link.exe"), None)
305 pub use self::platform::*;