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 copied over from Clang in its MSVCToolChain.cpp file, but
32 //! comments can also be found below leading through the various code paths.
34 use std
::process
::Command
;
41 pub fn link_exe_cmd(sess
: &Session
) -> Command
{
43 use std
::ffi
::OsString
;
46 use std
::path
::{Path, PathBuf}
;
47 use self::registry
::{RegistryKey, LOCAL_MACHINE}
;
49 // When finding the link.exe binary the 32-bit version is at the top level
50 // but the versions to cross to other architectures are stored in
51 // sub-folders. Unknown architectures also just bail out early to return the
52 // standard `link.exe` command.
53 let extra
= match &sess
.target
.target
.arch
[..] {
57 _
=> return Command
::new("link.exe"),
60 let vs_install_dir
= get_vs_install_dir();
62 // First up, we need to find the `link.exe` binary itself, and there's a few
63 // locations that we can look. First up is the standard VCINSTALLDIR
64 // environment variable which is normally set by the vcvarsall.bat file. If
65 // an environment is set up manually by whomever's driving the compiler then
66 // we shouldn't muck with that decision and should instead respect that.
68 // Next up is looking in PATH itself. Here we look for `cl.exe` and then
69 // assume that `link.exe` is next to it if we find it. Note that we look for
70 // `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in
71 // PATH but we're not interested in finding that.
73 // Finally we read the Windows registry to discover the VS install root.
74 // From here we probe for `link.exe` just to make sure that it exists.
75 let mut cmd
= env
::var_os("VCINSTALLDIR").and_then(|dir
| {
76 let mut p
= PathBuf
::from(dir
);
80 if fs
::metadata(&p
).is_ok() {Some(p)}
else {None}
82 env
::var_os("PATH").and_then(|path
| {
83 env
::split_paths(&path
).find(|path
| {
84 fs
::metadata(&path
.join("cl.exe")).is_ok()
90 vs_install_dir
.as_ref().and_then(|p
| {
91 let mut p
= p
.join("VC/bin");
94 if fs
::metadata(&p
).is_ok() {Some(p)}
else {None}
98 }).unwrap_or_else(|| {
99 Command
::new("link.exe")
102 // The MSVC linker uses the LIB environment variable as the default lookup
103 // path for libraries. This environment variable is normally set up by the
104 // VS shells, so we only want to start adding our own pieces if it's not
107 // If we're adding our own pieces, then we need to add a few primary
108 // directories to the default search path for the linker. The first is in
109 // the VS install direcotry, the next is the Windows SDK directory, and the
110 // last is the possible UCRT installation directory.
112 // The UCRT is a recent addition to Visual Studio installs (2015 at the time
113 // of this writing), and it's in the normal windows SDK folder, but there
114 // apparently aren't registry keys pointing to it. As a result we detect the
115 // installation and then add it manually. This logic will probably need to
116 // be tweaked over time...
117 if env
::var_os("LIB").is_none() {
118 if let Some(mut vs_install_dir
) = vs_install_dir
{
119 vs_install_dir
.push("VC/lib");
120 vs_install_dir
.push(extra
);
121 let mut arg
= OsString
::from("/LIBPATH:");
122 arg
.push(&vs_install_dir
);
125 if let Some((ucrt_root
, vers
)) = ucrt_install_dir(&vs_install_dir
) {
126 if let Some(arch
) = windows_sdk_v8_subdir(sess
) {
127 let mut arg
= OsString
::from("/LIBPATH:");
128 arg
.push(ucrt_root
.join("Lib").join(vers
)
129 .join("ucrt").join(arch
));
134 if let Some(path
) = get_windows_sdk_lib_path(sess
) {
135 let mut arg
= OsString
::from("/LIBPATH:");
143 // When looking for the Visual Studio installation directory we look in a
144 // number of locations in varying degrees of precedence:
146 // 1. The Visual Studio registry keys
147 // 2. The Visual Studio Express registry keys
148 // 3. A number of somewhat standard environment variables
150 // If we find a hit from any of these keys then we strip off the IDE/Tools
151 // folders which are typically found at the end.
153 // As a final note, when we take a look at the registry keys they're
154 // typically found underneath the version of what's installed, but we don't
155 // quite know what's installed. As a result we probe all sub-keys of the two
156 // keys we're looking at to find out the maximum version of what's installed
157 // and we use that root directory.
158 fn get_vs_install_dir() -> Option
<PathBuf
> {
159 LOCAL_MACHINE
.open(r
"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_
| {
160 LOCAL_MACHINE
.open(r
"SOFTWARE\Microsoft\VCExpress".as_ref())
161 }).ok().and_then(|key
| {
162 max_version(&key
).and_then(|(_vers
, key
)| {
163 key
.query_str("InstallDir").ok()
166 env
::var_os("VS120COMNTOOLS")
168 env
::var_os("VS100COMNTOOLS")
170 env
::var_os("VS90COMNTOOLS")
172 env
::var_os("VS80COMNTOOLS")
173 }).map(PathBuf
::from
).and_then(|mut dir
| {
174 if dir
.ends_with("Common7/IDE") || dir
.ends_with("Common7/Tools") {
184 // Given a registry key, look at all the sub keys and find the one which has
185 // the maximal numeric value.
187 // Returns the name of the maximal key as well as the opened maximal key.
188 fn max_version(key
: &RegistryKey
) -> Option
<(OsString
, RegistryKey
)> {
189 let mut max_vers
= 0;
190 let mut max_key
= None
;
191 for subkey
in key
.iter().filter_map(|k
| k
.ok()) {
192 let val
= subkey
.to_str().and_then(|s
| {
193 s
.trim_left_matches("v").replace(".", "").parse().ok()
195 let val
= match val
{
200 if let Ok(k
) = key
.open(&subkey
) {
202 max_key
= Some((subkey
, k
));
209 fn get_windows_sdk_path() -> Option
<(PathBuf
, usize, Option
<OsString
>)> {
210 let key
= r
"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
211 let key
= LOCAL_MACHINE
.open(key
.as_ref());
212 let (n
, k
) = match key
.ok().as_ref().and_then(max_version
) {
216 let mut parts
= n
.to_str().unwrap().trim_left_matches("v").splitn(2, ".");
217 let major
= parts
.next().unwrap().parse
::<usize>().unwrap();
218 let _minor
= parts
.next().unwrap().parse
::<usize>().unwrap();
219 k
.query_str("InstallationFolder").ok().map(|folder
| {
220 let ver
= k
.query_str("ProductVersion");
221 (PathBuf
::from(folder
), major
, ver
.ok())
225 fn get_windows_sdk_lib_path(sess
: &Session
) -> Option
<PathBuf
> {
226 let (mut path
, major
, ver
) = match get_windows_sdk_path() {
232 // In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
233 // x64 libraries are inside, and it's not necessary to link against
234 // the SDK 7.x when targeting ARM or other architectures.
235 let x86
= match &sess
.target
.target
.arch
[..] {
240 Some(if x86 {path}
else {path.join("x64")}
)
241 } else if major
<= 8 {
242 // Windows SDK 8.x installs libraries in a folder whose names
243 // depend on the version of the OS you're targeting. By default
244 // choose the newest, which usually corresponds to the version of
245 // the OS you've installed the SDK on.
246 let extra
= match windows_sdk_v8_subdir(sess
) {
250 ["winv6.3", "win8", "win7"].iter().map(|p
| path
.join(p
)).find(|part
| {
251 fs
::metadata(part
).is_ok()
253 path
.join("um").join(extra
)
255 } else if let Some(mut ver
) = ver
{
256 // Windows SDK 10 splits the libraries into architectures the same
257 // as Windows SDK 8.x, except for the addition of arm64.
258 // Additionally, the SDK 10 is split by Windows 10 build numbers
259 // rather than the OS version like the SDK 8.x does.
260 let extra
= match windows_sdk_v10_subdir(sess
) {
264 // To get the correct directory we need to get the Windows SDK 10
265 // version, and so far it looks like the "ProductVersion" of the SDK
266 // corresponds to the folder name that the libraries are located in
267 // except that the folder contains an extra ".0". For now just
268 // append a ".0" to look for find the directory we're in. This logic
269 // will likely want to be refactored one day.
271 let p
= path
.join(ver
).join("um").join(extra
);
272 fs
::metadata(&p
).ok().map(|_
| p
)
276 fn windows_sdk_v8_subdir(sess
: &Session
) -> Option
<&'
static str> {
277 match &sess
.target
.target
.arch
[..] {
278 "x86" => Some("x86"),
279 "x86_64" => Some("x64"),
280 "arm" => Some("arm"),
285 fn windows_sdk_v10_subdir(sess
: &Session
) -> Option
<&'
static str> {
286 match &sess
.target
.target
.arch
[..] {
287 "x86" => Some("x86"),
288 "x86_64" => Some("x64"),
289 "arm" => Some("arm"),
290 "aarch64" => Some("arm64"), // FIXME - Check if aarch64 is correct
295 fn ucrt_install_dir(vs_install_dir
: &Path
) -> Option
<(PathBuf
, String
)> {
296 let is_vs_14
= vs_install_dir
.iter().filter_map(|p
| p
.to_str()).any(|s
| {
297 s
== "Microsoft Visual Studio 14.0"
302 let key
= r
"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
303 let sdk_dir
= LOCAL_MACHINE
.open(key
.as_ref()).and_then(|p
| {
304 p
.query_str("KitsRoot10")
305 }).map(PathBuf
::from
);
306 let sdk_dir
= match sdk_dir
{
308 Err(..) => return None
,
310 (move || -> io
::Result
<_
> {
312 let mut max_s
= None
;
313 for entry
in try
!(fs
::read_dir(&sdk_dir
.join("Lib"))) {
314 let entry
= try
!(entry
);
315 if let Ok(s
) = entry
.file_name().into_string() {
316 if let Ok(u
) = s
.replace(".", "").parse
::<usize>() {
324 Ok(max_s
.map(|m
| (sdk_dir
, m
)))
325 })().ok().and_then(|x
| x
)
330 pub fn link_exe_cmd(_sess
: &Session
) -> Command
{
331 Command
::new("link.exe")