]> git.lizzy.rs Git - rust.git/blob - src/vendor/gcc/src/windows_registry.rs
Unignore u128 test for stage 0,1
[rust.git] / src / vendor / gcc / src / windows_registry.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 //! A helper module to probe the Windows Registry when looking for
12 //! windows-specific tools.
13
14 use std::process::Command;
15
16 use Tool;
17
18 macro_rules! otry {
19     ($expr:expr) => (match $expr {
20         Some(val) => val,
21         None => return None,
22     })
23 }
24
25 /// Attempts to find a tool within an MSVC installation using the Windows
26 /// registry as a point to search from.
27 ///
28 /// The `target` argument is the target that the tool should work for (e.g.
29 /// compile or link for) and the `tool` argument is the tool to find (e.g.
30 /// `cl.exe` or `link.exe`).
31 ///
32 /// This function will return `None` if the tool could not be found, or it will
33 /// return `Some(cmd)` which represents a command that's ready to execute the
34 /// tool with the appropriate environment variables set.
35 ///
36 /// Note that this function always returns `None` for non-MSVC targets.
37 pub fn find(target: &str, tool: &str) -> Option<Command> {
38     find_tool(target, tool).map(|c| c.to_command())
39 }
40
41 /// Similar to the `find` function above, this function will attempt the same
42 /// operation (finding a MSVC tool in a local install) but instead returns a
43 /// `Tool` which may be introspected.
44 #[cfg(not(windows))]
45 pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
46     None
47 }
48
49 /// Documented above.
50 #[cfg(windows)]
51 pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
52     use std::env;
53     use std::ffi::OsString;
54     use std::mem;
55     use std::path::{Path, PathBuf};
56     use registry::{RegistryKey, LOCAL_MACHINE};
57
58     struct MsvcTool {
59         tool: PathBuf,
60         libs: Vec<PathBuf>,
61         path: Vec<PathBuf>,
62         include: Vec<PathBuf>,
63     }
64
65     impl MsvcTool {
66         fn new(tool: PathBuf) -> MsvcTool {
67             MsvcTool {
68                 tool: tool,
69                 libs: Vec::new(),
70                 path: Vec::new(),
71                 include: Vec::new(),
72             }
73         }
74
75         fn into_tool(self) -> Tool {
76             let MsvcTool { tool, libs, path, include } = self;
77             let mut tool = Tool::new(tool.into());
78             add_env(&mut tool, "LIB", libs);
79             add_env(&mut tool, "PATH", path);
80             add_env(&mut tool, "INCLUDE", include);
81             return tool
82         }
83     }
84
85     // This logic is all tailored for MSVC, if we're not that then bail out
86     // early.
87     if !target.contains("msvc") {
88         return None
89     }
90
91     // Looks like msbuild isn't located in the same location as other tools like
92     // cl.exe and lib.exe. To handle this we probe for it manually with
93     // dedicated registry keys.
94     if tool.contains("msbuild") {
95         return find_msbuild(target)
96     }
97
98     // If VCINSTALLDIR is set, then someone's probably already run vcvars and we
99     // should just find whatever that indicates.
100     if env::var_os("VCINSTALLDIR").is_some() {
101         return env::var_os("PATH").and_then(|path| {
102             env::split_paths(&path).map(|p| p.join(tool)).find(|p| p.exists())
103         }).map(|path| {
104             Tool::new(path.into())
105         })
106     }
107
108     // Ok, if we're here, now comes the fun part of the probing. Default shells
109     // or shells like MSYS aren't really configured to execute `cl.exe` and the
110     // various compiler tools shipped as part of Visual Studio. Here we try to
111     // first find the relevant tool, then we also have to be sure to fill in
112     // environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
113     // the tool is actually usable.
114
115     return find_msvc_latest(tool, target, "15.0").or_else(|| {
116         find_msvc_latest(tool, target, "14.0")
117     }).or_else(|| {
118         find_msvc_12(tool, target)
119     }).or_else(|| {
120         find_msvc_11(tool, target)
121     });
122
123     // For MSVC 14 or newer we need to find the Universal CRT as well as either
124     // the Windows 10 SDK or Windows 8.1 SDK.
125     fn find_msvc_latest(tool: &str, target: &str, ver: &str) -> Option<Tool> {
126         let vcdir = otry!(get_vc_dir(ver));
127         let mut tool = otry!(get_tool(tool, &vcdir, target));
128         let sub = otry!(lib_subdir(target));
129         let (ucrt, ucrt_version) = otry!(get_ucrt_dir());
130
131         let ucrt_include = ucrt.join("include").join(&ucrt_version);
132         tool.include.push(ucrt_include.join("ucrt"));
133
134         let ucrt_lib = ucrt.join("lib").join(&ucrt_version);
135         tool.libs.push(ucrt_lib.join("ucrt").join(sub));
136
137         if let Some((sdk, version)) = get_sdk10_dir() {
138             tool.path.push(sdk.join("bin").join(sub));
139             let sdk_lib = sdk.join("lib").join(&version);
140             tool.libs.push(sdk_lib.join("um").join(sub));
141             let sdk_include = sdk.join("include").join(&version);
142             tool.include.push(sdk_include.join("um"));
143             tool.include.push(sdk_include.join("winrt"));
144             tool.include.push(sdk_include.join("shared"));
145         } else if let Some(sdk) = get_sdk81_dir() {
146             tool.path.push(sdk.join("bin").join(sub));
147             let sdk_lib = sdk.join("lib").join("winv6.3");
148             tool.libs.push(sdk_lib.join("um").join(sub));
149             let sdk_include = sdk.join("include");
150             tool.include.push(sdk_include.join("um"));
151             tool.include.push(sdk_include.join("winrt"));
152             tool.include.push(sdk_include.join("shared"));
153         } else {
154             return None
155         }
156         Some(tool.into_tool())
157     }
158
159     // For MSVC 12 we need to find the Windows 8.1 SDK.
160     fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
161         let vcdir = otry!(get_vc_dir("12.0"));
162         let mut tool = otry!(get_tool(tool, &vcdir, target));
163         let sub = otry!(lib_subdir(target));
164         let sdk81 = otry!(get_sdk81_dir());
165         tool.path.push(sdk81.join("bin").join(sub));
166         let sdk_lib = sdk81.join("lib").join("winv6.3");
167         tool.libs.push(sdk_lib.join("um").join(sub));
168         let sdk_include = sdk81.join("include");
169         tool.include.push(sdk_include.join("shared"));
170         tool.include.push(sdk_include.join("um"));
171         tool.include.push(sdk_include.join("winrt"));
172         Some(tool.into_tool())
173     }
174
175     // For MSVC 11 we need to find the Windows 8 SDK.
176     fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
177         let vcdir = otry!(get_vc_dir("11.0"));
178         let mut tool = otry!(get_tool(tool, &vcdir, target));
179         let sub = otry!(lib_subdir(target));
180         let sdk8 = otry!(get_sdk8_dir());
181         tool.path.push(sdk8.join("bin").join(sub));
182         let sdk_lib = sdk8.join("lib").join("win8");
183         tool.libs.push(sdk_lib.join("um").join(sub));
184         let sdk_include = sdk8.join("include");
185         tool.include.push(sdk_include.join("shared"));
186         tool.include.push(sdk_include.join("um"));
187         tool.include.push(sdk_include.join("winrt"));
188         Some(tool.into_tool())
189     }
190
191     fn add_env(tool: &mut Tool, env: &str, paths: Vec<PathBuf>) {
192         let prev = env::var_os(env).unwrap_or(OsString::new());
193         let prev = env::split_paths(&prev);
194         let new = paths.into_iter().chain(prev);
195         tool.env.push((env.to_string().into(), env::join_paths(new).unwrap()));
196     }
197
198     // Given a possible MSVC installation directory, we look for the linker and
199     // then add the MSVC library path.
200     fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> {
201         bin_subdir(target).into_iter().map(|(sub, host)| {
202             (path.join("bin").join(sub).join(tool),
203              path.join("bin").join(host))
204         }).filter(|&(ref path, _)| {
205             path.is_file()
206         }).map(|(path, host)| {
207             let mut tool = MsvcTool::new(path);
208             tool.path.push(host);
209             tool
210         }).filter_map(|mut tool| {
211             let sub = otry!(vc_lib_subdir(target));
212             tool.libs.push(path.join("lib").join(sub));
213             tool.include.push(path.join("include"));
214             let atlmfc_path = path.join("atlmfc");
215             if atlmfc_path.exists() {
216                 tool.libs.push(atlmfc_path.join("lib").join(sub));
217                 tool.include.push(atlmfc_path.join("include"));
218             }
219             Some(tool)
220         }).next()
221     }
222
223     // To find MSVC we look in a specific registry key for the version we are
224     // trying to find.
225     fn get_vc_dir(ver: &str) -> Option<PathBuf> {
226         let key = r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7";
227         let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
228         let path = otry!(key.query_str(ver).ok());
229         Some(path.into())
230     }
231
232     // To find the Universal CRT we look in a specific registry key for where
233     // all the Universal CRTs are located and then sort them asciibetically to
234     // find the newest version. While this sort of sorting isn't ideal,  it is
235     // what vcvars does so that's good enough for us.
236     //
237     // Returns a pair of (root, version) for the ucrt dir if found
238     fn get_ucrt_dir() -> Option<(PathBuf, String)> {
239         let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
240         let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
241         let root = otry!(key.query_str("KitsRoot10").ok());
242         let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
243         let max_libdir = otry!(readdir.filter_map(|dir| {
244             dir.ok()
245         }).map(|dir| {
246             dir.path()
247         }).filter(|dir| {
248             dir.components().last().and_then(|c| {
249                 c.as_os_str().to_str()
250             }).map(|c| {
251                 c.starts_with("10.") && dir.join("ucrt").is_dir()
252             }).unwrap_or(false)
253         }).max());
254         let version = max_libdir.components().last().unwrap();
255         let version = version.as_os_str().to_str().unwrap().to_string();
256         Some((root.into(), version))
257     }
258
259     // Vcvars finds the correct version of the Windows 10 SDK by looking
260     // for the include `um\Windows.h` because sometimes a given version will
261     // only have UCRT bits without the rest of the SDK. Since we only care about
262     // libraries and not includes, we instead look for `um\x64\kernel32.lib`.
263     // Since the 32-bit and 64-bit libraries are always installed together we
264     // only need to bother checking x64, making this code a tiny bit simpler.
265     // Like we do for the Universal CRT, we sort the possibilities
266     // asciibetically to find the newest one as that is what vcvars does.
267     fn get_sdk10_dir() -> Option<(PathBuf, String)> {
268         let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
269         let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
270         let root = otry!(key.query_str("InstallationFolder").ok());
271         let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
272         let mut dirs = readdir.filter_map(|dir| dir.ok())
273                               .map(|dir| dir.path())
274                               .collect::<Vec<_>>();
275         dirs.sort();
276         let dir = otry!(dirs.into_iter().rev().filter(|dir| {
277             dir.join("um").join("x64").join("kernel32.lib").is_file()
278         }).next());
279         let version = dir.components().last().unwrap();
280         let version = version.as_os_str().to_str().unwrap().to_string();
281         Some((root.into(), version))
282     }
283
284     // Interestingly there are several subdirectories, `win7` `win8` and
285     // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
286     // applies to us. Note that if we were targetting kernel mode drivers
287     // instead of user mode applications, we would care.
288     fn get_sdk81_dir() -> Option<PathBuf> {
289         let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
290         let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
291         let root = otry!(key.query_str("InstallationFolder").ok());
292         Some(root.into())
293     }
294
295     fn get_sdk8_dir() -> Option<PathBuf> {
296         let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0";
297         let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
298         let root = otry!(key.query_str("InstallationFolder").ok());
299         Some(root.into())
300     }
301
302     const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0;
303     const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9;
304     const X86: u16 = PROCESSOR_ARCHITECTURE_INTEL;
305     const X86_64: u16 = PROCESSOR_ARCHITECTURE_AMD64;
306
307     // When choosing the tool to use, we have to choose the one which matches
308     // the target architecture. Otherwise we end up in situations where someone
309     // on 32-bit Windows is trying to cross compile to 64-bit and it tries to
310     // invoke the native 64-bit compiler which won't work.
311     //
312     // For the return value of this function, the first member of the tuple is
313     // the folder of the tool we will be invoking, while the second member is
314     // the folder of the host toolchain for that tool which is essential when
315     // using a cross linker. We return a Vec since on x64 there are often two
316     // linkers that can target the architecture we desire. The 64-bit host
317     // linker is preferred, and hence first, due to 64-bit allowing it more
318     // address space to work with and potentially being faster.
319     fn bin_subdir(target: &str) -> Vec<(&'static str, &'static str)> {
320         let arch = target.split('-').next().unwrap();
321         match (arch, host_arch()) {
322             ("i586", X86) |
323             ("i686", X86) => vec![("", "")],
324             ("i586", X86_64) |
325             ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
326             ("x86_64", X86) => vec![("x86_amd64", "")],
327             ("x86_64", X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")],
328             ("arm", X86) => vec![("x86_arm", "")],
329             ("arm", X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")],
330             _ => vec![],
331         }
332     }
333
334     fn lib_subdir(target: &str) -> Option<&'static str> {
335         let arch = target.split('-').next().unwrap();
336         match arch {
337             "i586" | "i686" => Some("x86"),
338             "x86_64" => Some("x64"),
339             "arm" => Some("arm"),
340             _ => None,
341         }
342     }
343
344     // MSVC's x86 libraries are not in a subfolder
345     fn vc_lib_subdir(target: &str) -> Option<&'static str> {
346         let arch = target.split('-').next().unwrap();
347         match arch {
348             "i586" | "i686" => Some(""),
349             "x86_64" => Some("amd64"),
350             "arm" => Some("arm"),
351             _ => None,
352         }
353     }
354
355     #[allow(bad_style)]
356     fn host_arch() -> u16 {
357         type DWORD = u32;
358         type WORD = u16;
359         type LPVOID = *mut u8;
360         type DWORD_PTR = usize;
361
362         #[repr(C)]
363         struct SYSTEM_INFO {
364             wProcessorArchitecture: WORD,
365             _wReserved: WORD,
366             _dwPageSize: DWORD,
367             _lpMinimumApplicationAddress: LPVOID,
368             _lpMaximumApplicationAddress: LPVOID,
369             _dwActiveProcessorMask: DWORD_PTR,
370             _dwNumberOfProcessors: DWORD,
371             _dwProcessorType: DWORD,
372             _dwAllocationGranularity: DWORD,
373             _wProcessorLevel: WORD,
374             _wProcessorRevision: WORD,
375         }
376
377         extern "system" {
378             fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
379         }
380
381         unsafe {
382             let mut info = mem::zeroed();
383             GetNativeSystemInfo(&mut info);
384             info.wProcessorArchitecture
385         }
386     }
387
388     // Given a registry key, look at all the sub keys and find the one which has
389     // the maximal numeric value.
390     //
391     // Returns the name of the maximal key as well as the opened maximal key.
392     fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
393         let mut max_vers = 0;
394         let mut max_key = None;
395         for subkey in key.iter().filter_map(|k| k.ok()) {
396             let val = subkey.to_str().and_then(|s| {
397                 s.trim_left_matches("v").replace(".", "").parse().ok()
398             });
399             let val = match val {
400                 Some(s) => s,
401                 None => continue,
402             };
403             if val > max_vers {
404                 if let Ok(k) = key.open(&subkey) {
405                     max_vers = val;
406                     max_key = Some((subkey, k));
407                 }
408             }
409         }
410         return max_key
411     }
412
413     // see http://stackoverflow.com/questions/328017/path-to-msbuild
414     fn find_msbuild(target: &str) -> Option<Tool> {
415         let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
416         LOCAL_MACHINE.open(key.as_ref()).ok().and_then(|key| {
417             max_version(&key).and_then(|(_vers, key)| {
418                 key.query_str("MSBuildToolsPath").ok()
419             })
420         }).map(|path| {
421             let mut path = PathBuf::from(path);
422             path.push("MSBuild.exe");
423             let mut tool = Tool::new(path);
424             if target.contains("x86_64") {
425                 tool.env.push(("Platform".into(), "X64".into()));
426             }
427             tool
428         })
429     }
430 }