]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/back/msvc/mod.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / librustc_trans / back / msvc / mod.rs
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.
4 //
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.
10
11 //! MSVC-specific logic for linkers and such.
12 //!
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.
16 //!
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.
23 //!
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.
29 //!
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.
33
34 use std::process::Command;
35 use session::Session;
36
37 #[cfg(windows)]
38 mod registry;
39
40 #[cfg(windows)]
41 pub fn link_exe_cmd(sess: &Session) -> Command {
42 use std::env;
43 use std::ffi::OsString;
44 use std::fs;
45 use std::io;
46 use std::path::{Path, PathBuf};
47 use self::registry::{RegistryKey, LOCAL_MACHINE};
48
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[..] {
54 "x86" => "",
55 "x86_64" => "amd64",
56 "arm" => "arm",
57 _ => return Command::new("link.exe"),
58 };
59
60 let vs_install_dir = get_vs_install_dir();
61
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.
67 //
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.
72 //
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);
77 p.push("bin");
78 p.push(extra);
79 p.push("link.exe");
80 if fs::metadata(&p).is_ok() {Some(p)} else {None}
81 }).or_else(|| {
82 env::var_os("PATH").and_then(|path| {
83 env::split_paths(&path).find(|path| {
84 fs::metadata(&path.join("cl.exe")).is_ok()
85 }).map(|p| {
86 p.join("link.exe")
87 })
88 })
89 }).or_else(|| {
90 vs_install_dir.as_ref().and_then(|p| {
91 let mut p = p.join("VC/bin");
92 p.push(extra);
93 p.push("link.exe");
94 if fs::metadata(&p).is_ok() {Some(p)} else {None}
95 })
96 }).map(|linker| {
97 Command::new(linker)
98 }).unwrap_or_else(|| {
99 Command::new("link.exe")
100 });
101
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
105 // set.
106 //
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.
111 //
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);
123 cmd.arg(arg);
124
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));
130 cmd.arg(arg);
131 }
132 }
133 }
134 if let Some(path) = get_windows_sdk_lib_path(sess) {
135 let mut arg = OsString::from("/LIBPATH:");
136 arg.push(&path);
137 cmd.arg(arg);
138 }
139 }
140
141 return cmd;
142
143 // When looking for the Visual Studio installation directory we look in a
144 // number of locations in varying degrees of precedence:
145 //
146 // 1. The Visual Studio registry keys
147 // 2. The Visual Studio Express registry keys
148 // 3. A number of somewhat standard environment variables
149 //
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.
152 //
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()
164 })
165 }).or_else(|| {
166 env::var_os("VS120COMNTOOLS")
167 }).or_else(|| {
168 env::var_os("VS100COMNTOOLS")
169 }).or_else(|| {
170 env::var_os("VS90COMNTOOLS")
171 }).or_else(|| {
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") {
175 dir.pop();
176 dir.pop();
177 Some(dir)
178 } else {
179 None
180 }
181 })
182 }
183
184 // Given a registry key, look at all the sub keys and find the one which has
185 // the maximal numeric value.
186 //
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()
194 });
195 let val = match val {
196 Some(s) => s,
197 None => continue,
198 };
199 if val > max_vers {
200 if let Ok(k) = key.open(&subkey) {
201 max_vers = val;
202 max_key = Some((subkey, k));
203 }
204 }
205 }
206 return max_key
207 }
208
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) {
213 Some(p) => p,
214 None => return None,
215 };
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())
222 })
223 }
224
225 fn get_windows_sdk_lib_path(sess: &Session) -> Option<PathBuf> {
226 let (mut path, major, ver) = match get_windows_sdk_path() {
227 Some(p) => p,
228 None => return None,
229 };
230 path.push("Lib");
231 if major <= 7 {
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[..] {
236 "x86" => true,
237 "x86_64" => false,
238 _ => return None,
239 };
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) {
247 Some(e) => e,
248 None => return None,
249 };
250 ["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| {
251 fs::metadata(part).is_ok()
252 }).map(|path| {
253 path.join("um").join(extra)
254 })
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) {
261 Some(e) => e,
262 None => return None,
263 };
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.
270 ver.push(".0");
271 let p = path.join(ver).join("um").join(extra);
272 fs::metadata(&p).ok().map(|_| p)
273 } else { None }
274 }
275
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"),
281 _ => return None,
282 }
283 }
284
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
291 _ => return None,
292 }
293 }
294
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"
298 });
299 if !is_vs_14 {
300 return None
301 }
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 {
307 Ok(p) => p,
308 Err(..) => return None,
309 };
310 (move || -> io::Result<_> {
311 let mut max = None;
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>() {
317 if Some(u) > max {
318 max = Some(u);
319 max_s = Some(s);
320 }
321 }
322 }
323 }
324 Ok(max_s.map(|m| (sdk_dir, m)))
325 })().ok().and_then(|x| x)
326 }
327 }
328
329 #[cfg(not(windows))]
330 pub fn link_exe_cmd(_sess: &Session) -> Command {
331 Command::new("link.exe")
332 }