]>
Commit | Line | Data |
---|---|---|
7453a54e SL |
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 | ||
a7813a04 XL |
11 | //! Sanity checking performed by rustbuild before actually executing anything. |
12 | //! | |
13 | //! This module contains the implementation of ensuring that the build | |
14 | //! environment looks reasonable before progressing. This will verify that | |
15 | //! various programs like git and python exist, along with ensuring that all C | |
16 | //! compilers for cross-compiling are found. | |
17 | //! | |
18 | //! In theory if we get past this phase it's a bug if a build fails, but in | |
19 | //! practice that's likely not true! | |
20 | ||
041b39d2 | 21 | use std::collections::HashMap; |
7453a54e | 22 | use std::env; |
041b39d2 | 23 | use std::ffi::{OsString, OsStr}; |
7453a54e SL |
24 | use std::fs; |
25 | use std::process::Command; | |
041b39d2 | 26 | use std::path::PathBuf; |
7453a54e SL |
27 | |
28 | use build_helper::output; | |
29 | ||
5bcae85e | 30 | use Build; |
7453a54e | 31 | |
041b39d2 XL |
32 | struct Finder { |
33 | cache: HashMap<OsString, Option<PathBuf>>, | |
34 | path: OsString, | |
35 | } | |
36 | ||
37 | impl Finder { | |
38 | fn new() -> Self { | |
39 | Self { | |
40 | cache: HashMap::new(), | |
41 | path: env::var_os("PATH").unwrap_or_default() | |
42 | } | |
43 | } | |
44 | ||
45 | fn maybe_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> Option<PathBuf> { | |
46 | let cmd: OsString = cmd.as_ref().into(); | |
47 | let path = self.path.clone(); | |
48 | self.cache.entry(cmd.clone()).or_insert_with(|| { | |
49 | for path in env::split_paths(&path) { | |
50 | let target = path.join(&cmd); | |
51 | let mut cmd_alt = cmd.clone(); | |
52 | cmd_alt.push(".exe"); | |
53 | if target.is_file() || // some/path/git | |
54 | target.with_extension("exe").exists() || // some/path/git.exe | |
55 | target.join(&cmd_alt).exists() { // some/path/git/git.exe | |
56 | return Some(target); | |
57 | } | |
58 | } | |
59 | None | |
60 | }).clone() | |
61 | } | |
62 | ||
63 | fn must_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> PathBuf { | |
64 | self.maybe_have(&cmd).unwrap_or_else(|| { | |
65 | panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref()); | |
66 | }) | |
67 | } | |
68 | } | |
69 | ||
7453a54e | 70 | pub fn check(build: &mut Build) { |
041b39d2 | 71 | let path = env::var_os("PATH").unwrap_or_default(); |
5bcae85e SL |
72 | // On Windows, quotes are invalid characters for filename paths, and if |
73 | // one is present as part of the PATH then that can lead to the system | |
74 | // being unable to identify the files properly. See | |
75 | // https://github.com/rust-lang/rust/issues/34959 for more details. | |
041b39d2 XL |
76 | if cfg!(windows) && path.to_string_lossy().contains("\"") { |
77 | panic!("PATH contains invalid character '\"'"); | |
5bcae85e | 78 | } |
7453a54e | 79 | |
041b39d2 | 80 | let mut cmd_finder = Finder::new(); |
7453a54e SL |
81 | // If we've got a git directory we're gona need git to update |
82 | // submodules and learn about various other aspects. | |
041b39d2 XL |
83 | if build.rust_info.is_git() { |
84 | cmd_finder.must_have("git"); | |
7453a54e SL |
85 | } |
86 | ||
7cac9316 | 87 | // We need cmake, but only if we're actually building LLVM or sanitizers. |
3b2f2976 | 88 | let building_llvm = build.hosts.iter() |
7cac9316 XL |
89 | .filter_map(|host| build.config.target_config.get(host)) |
90 | .any(|config| config.llvm_config.is_none()); | |
91 | if building_llvm || build.config.sanitizers { | |
041b39d2 | 92 | cmd_finder.must_have("cmake"); |
7cac9316 XL |
93 | } |
94 | ||
95 | // Ninja is currently only used for LLVM itself. | |
3b2f2976 XL |
96 | if building_llvm { |
97 | if build.config.ninja { | |
98 | // Some Linux distros rename `ninja` to `ninja-build`. | |
99 | // CMake can work with either binary name. | |
100 | if cmd_finder.maybe_have("ninja-build").is_none() { | |
101 | cmd_finder.must_have("ninja"); | |
102 | } | |
103 | } | |
104 | ||
105 | // If ninja isn't enabled but we're building for MSVC then we try | |
106 | // doubly hard to enable it. It was realized in #43767 that the msbuild | |
107 | // CMake generator for MSVC doesn't respect configuration options like | |
108 | // disabling LLVM assertions, which can often be quite important! | |
109 | // | |
110 | // In these cases we automatically enable Ninja if we find it in the | |
111 | // environment. | |
112 | if !build.config.ninja && build.config.build.contains("msvc") { | |
113 | if cmd_finder.maybe_have("ninja").is_some() { | |
114 | build.config.ninja = true; | |
115 | } | |
116 | } | |
7453a54e SL |
117 | } |
118 | ||
041b39d2 XL |
119 | build.config.python = build.config.python.take().map(|p| cmd_finder.must_have(p)) |
120 | .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py | |
121 | .or_else(|| cmd_finder.maybe_have("python2.7")) | |
122 | .or_else(|| cmd_finder.maybe_have("python2")) | |
123 | .or_else(|| Some(cmd_finder.must_have("python"))); | |
c30ab7b3 | 124 | |
041b39d2 XL |
125 | build.config.nodejs = build.config.nodejs.take().map(|p| cmd_finder.must_have(p)) |
126 | .or_else(|| cmd_finder.maybe_have("node")) | |
127 | .or_else(|| cmd_finder.maybe_have("nodejs")); | |
9e0c209e | 128 | |
041b39d2 XL |
129 | build.config.gdb = build.config.gdb.take().map(|p| cmd_finder.must_have(p)) |
130 | .or_else(|| cmd_finder.maybe_have("gdb")); | |
c30ab7b3 | 131 | |
7453a54e SL |
132 | // We're gonna build some custom C code here and there, host triples |
133 | // also build some C++ shims for LLVM so we need a C++ compiler. | |
3b2f2976 | 134 | for target in &build.targets { |
c30ab7b3 SL |
135 | // On emscripten we don't actually need the C compiler to just |
136 | // build the target artifacts, only for testing. For the sake | |
137 | // of easier bot configuration, just skip detection. | |
138 | if target.contains("emscripten") { | |
139 | continue; | |
140 | } | |
141 | ||
3b2f2976 XL |
142 | cmd_finder.must_have(build.cc(*target)); |
143 | if let Some(ar) = build.ar(*target) { | |
041b39d2 | 144 | cmd_finder.must_have(ar); |
3157f602 | 145 | } |
7453a54e | 146 | } |
7453a54e | 147 | |
3b2f2976 XL |
148 | for host in &build.hosts { |
149 | cmd_finder.must_have(build.cxx(*host).unwrap()); | |
041b39d2 XL |
150 | |
151 | // The msvc hosts don't use jemalloc, turn it off globally to | |
152 | // avoid packaging the dummy liballoc_jemalloc on that platform. | |
c30ab7b3 SL |
153 | if host.contains("msvc") { |
154 | build.config.use_jemalloc = false; | |
155 | } | |
156 | } | |
157 | ||
a7813a04 | 158 | // Externally configured LLVM requires FileCheck to exist |
3b2f2976 | 159 | let filecheck = build.llvm_filecheck(build.build); |
9e0c209e | 160 | if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { |
32a655c1 | 161 | panic!("FileCheck executable {:?} does not exist", filecheck); |
a7813a04 XL |
162 | } |
163 | ||
3b2f2976 | 164 | for target in &build.targets { |
cc61c64b | 165 | // Can't compile for iOS unless we're on macOS |
7453a54e | 166 | if target.contains("apple-ios") && |
041b39d2 | 167 | !build.build.contains("apple-darwin") { |
cc61c64b | 168 | panic!("the iOS target is only supported on macOS"); |
7453a54e SL |
169 | } |
170 | ||
3b2f2976 | 171 | // Make sure musl-root is valid |
5bcae85e | 172 | if target.contains("musl") && !target.contains("mips") { |
3b2f2976 XL |
173 | // If this is a native target (host is also musl) and no musl-root is given, |
174 | // fall back to the system toolchain in /usr before giving up | |
175 | if build.musl_root(*target).is_none() && build.config.build == *target { | |
176 | let target = build.config.target_config.entry(target.clone()) | |
177 | .or_insert(Default::default()); | |
178 | target.musl_root = Some("/usr".into()); | |
179 | } | |
180 | match build.musl_root(*target) { | |
9e0c209e | 181 | Some(root) => { |
7453a54e SL |
182 | if fs::metadata(root.join("lib/libc.a")).is_err() { |
183 | panic!("couldn't find libc.a in musl dir: {}", | |
184 | root.join("lib").display()); | |
185 | } | |
186 | if fs::metadata(root.join("lib/libunwind.a")).is_err() { | |
187 | panic!("couldn't find libunwind.a in musl dir: {}", | |
188 | root.join("lib").display()); | |
189 | } | |
190 | } | |
191 | None => { | |
c30ab7b3 SL |
192 | panic!("when targeting MUSL either the rust.musl-root \ |
193 | option or the target.$TARGET.musl-root option must \ | |
9e0c209e | 194 | be specified in config.toml") |
7453a54e SL |
195 | } |
196 | } | |
197 | } | |
198 | ||
199 | if target.contains("msvc") { | |
200 | // There are three builds of cmake on windows: MSVC, MinGW, and | |
201 | // Cygwin. The Cygwin build does not have generators for Visual | |
202 | // Studio, so detect that here and error. | |
203 | let out = output(Command::new("cmake").arg("--help")); | |
204 | if !out.contains("Visual Studio") { | |
205 | panic!(" | |
206 | cmake does not support Visual Studio generators. | |
207 | ||
208 | This is likely due to it being an msys/cygwin build of cmake, | |
209 | rather than the required windows version, built using MinGW | |
210 | or Visual Studio. | |
211 | ||
212 | If you are building under msys2 try installing the mingw-w64-x86_64-cmake | |
213 | package instead of cmake: | |
214 | ||
215 | $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake | |
216 | "); | |
217 | } | |
218 | } | |
219 | } | |
54a0048b | 220 | |
a7813a04 XL |
221 | let run = |cmd: &mut Command| { |
222 | cmd.output().map(|output| { | |
223 | String::from_utf8_lossy(&output.stdout) | |
224 | .lines().next().unwrap() | |
225 | .to_string() | |
226 | }) | |
227 | }; | |
a7813a04 XL |
228 | build.lldb_version = run(Command::new("lldb").arg("--version")).ok(); |
229 | if build.lldb_version.is_some() { | |
230 | build.lldb_python_dir = run(Command::new("lldb").arg("-P")).ok(); | |
231 | } | |
476ff2be SL |
232 | |
233 | if let Some(ref s) = build.config.ccache { | |
041b39d2 | 234 | cmd_finder.must_have(s); |
476ff2be | 235 | } |
7453a54e | 236 | } |