]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | //! Sanity checking performed by rustbuild before actually executing anything. |
2 | //! | |
3 | //! This module contains the implementation of ensuring that the build | |
4 | //! environment looks reasonable before progressing. This will verify that | |
5 | //! various programs like git and python exist, along with ensuring that all C | |
6 | //! compilers for cross-compiling are found. | |
7 | //! | |
8 | //! In theory if we get past this phase it's a bug if a build fails, but in | |
9 | //! practice that's likely not true! | |
10 | ||
041b39d2 | 11 | use std::collections::HashMap; |
7453a54e | 12 | use std::env; |
dfeec247 | 13 | use std::ffi::{OsStr, OsString}; |
0731742a | 14 | use std::fs; |
041b39d2 | 15 | use std::path::PathBuf; |
2c00a5a8 | 16 | use std::process::Command; |
7453a54e | 17 | |
fc512014 | 18 | use crate::cache::INTERNER; |
74b04a01 | 19 | use crate::config::Target; |
5e7ed085 | 20 | use crate::util::output; |
0731742a | 21 | use crate::Build; |
7453a54e | 22 | |
1b1a35ee | 23 | pub struct Finder { |
041b39d2 XL |
24 | cache: HashMap<OsString, Option<PathBuf>>, |
25 | path: OsString, | |
26 | } | |
27 | ||
28 | impl Finder { | |
1b1a35ee | 29 | pub fn new() -> Self { |
dfeec247 | 30 | Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() } |
041b39d2 XL |
31 | } |
32 | ||
1b1a35ee XL |
33 | pub fn maybe_have<S: Into<OsString>>(&mut self, cmd: S) -> Option<PathBuf> { |
34 | let cmd: OsString = cmd.into(); | |
532ac7d7 | 35 | let path = &self.path; |
dfeec247 XL |
36 | self.cache |
37 | .entry(cmd.clone()) | |
38 | .or_insert_with(|| { | |
39 | for path in env::split_paths(path) { | |
40 | let target = path.join(&cmd); | |
41 | let mut cmd_exe = cmd.clone(); | |
42 | cmd_exe.push(".exe"); | |
43 | ||
44 | if target.is_file() // some/path/git | |
532ac7d7 | 45 | || path.join(&cmd_exe).exists() // some/path/git.exe |
dfeec247 XL |
46 | || target.join(&cmd_exe).exists() |
47 | // some/path/git/git.exe | |
48 | { | |
49 | return Some(target); | |
50 | } | |
041b39d2 | 51 | } |
dfeec247 XL |
52 | None |
53 | }) | |
54 | .clone() | |
041b39d2 XL |
55 | } |
56 | ||
1b1a35ee | 57 | pub fn must_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> PathBuf { |
041b39d2 XL |
58 | self.maybe_have(&cmd).unwrap_or_else(|| { |
59 | panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref()); | |
60 | }) | |
61 | } | |
62 | } | |
63 | ||
7453a54e | 64 | pub fn check(build: &mut Build) { |
041b39d2 | 65 | let path = env::var_os("PATH").unwrap_or_default(); |
5bcae85e SL |
66 | // On Windows, quotes are invalid characters for filename paths, and if |
67 | // one is present as part of the PATH then that can lead to the system | |
68 | // being unable to identify the files properly. See | |
69 | // https://github.com/rust-lang/rust/issues/34959 for more details. | |
a1dfa0c6 | 70 | if cfg!(windows) && path.to_string_lossy().contains('\"') { |
041b39d2 | 71 | panic!("PATH contains invalid character '\"'"); |
5bcae85e | 72 | } |
7453a54e | 73 | |
041b39d2 | 74 | let mut cmd_finder = Finder::new(); |
ff7c6d11 | 75 | // If we've got a git directory we're gonna need git to update |
7453a54e | 76 | // submodules and learn about various other aspects. |
2b03887a | 77 | if build.rust_info.is_managed_git_subrepository() { |
041b39d2 | 78 | cmd_finder.must_have("git"); |
7453a54e SL |
79 | } |
80 | ||
7cac9316 | 81 | // We need cmake, but only if we're actually building LLVM or sanitizers. |
fc512014 XL |
82 | let building_llvm = build.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) |
83 | && build | |
84 | .hosts | |
85 | .iter() | |
86 | .map(|host| { | |
87 | build | |
88 | .config | |
89 | .target_config | |
90 | .get(host) | |
91 | .map(|config| config.llvm_config.is_none()) | |
92 | .unwrap_or(true) | |
93 | }) | |
94 | .any(|build_llvm_ourselves| build_llvm_ourselves); | |
17df50a5 XL |
95 | let need_cmake = building_llvm || build.config.any_sanitizers_enabled(); |
96 | if need_cmake { | |
04454e1e FG |
97 | if cmd_finder.maybe_have("cmake").is_none() { |
98 | eprintln!( | |
99 | " | |
100 | Couldn't find required command: cmake | |
101 | ||
102 | You should install cmake, or set `download-ci-llvm = true` in the | |
103 | `[llvm]` section section of `config.toml` to download LLVM rather | |
104 | than building it. | |
105 | " | |
106 | ); | |
064997fb | 107 | crate::detail_exit(1); |
04454e1e | 108 | } |
7cac9316 XL |
109 | } |
110 | ||
dfeec247 XL |
111 | build.config.python = build |
112 | .config | |
113 | .python | |
114 | .take() | |
115 | .map(|p| cmd_finder.must_have(p)) | |
532ac7d7 | 116 | .or_else(|| env::var_os("BOOTSTRAP_PYTHON").map(PathBuf::from)) // set by bootstrap.py |
04454e1e FG |
117 | .or_else(|| cmd_finder.maybe_have("python")) |
118 | .or_else(|| cmd_finder.maybe_have("python3")) | |
119 | .or_else(|| cmd_finder.maybe_have("python2")); | |
c30ab7b3 | 120 | |
dfeec247 XL |
121 | build.config.nodejs = build |
122 | .config | |
123 | .nodejs | |
124 | .take() | |
125 | .map(|p| cmd_finder.must_have(p)) | |
041b39d2 XL |
126 | .or_else(|| cmd_finder.maybe_have("node")) |
127 | .or_else(|| cmd_finder.maybe_have("nodejs")); | |
9e0c209e | 128 | |
6a06907d XL |
129 | build.config.npm = build |
130 | .config | |
131 | .npm | |
132 | .take() | |
133 | .map(|p| cmd_finder.must_have(p)) | |
134 | .or_else(|| cmd_finder.maybe_have("npm")); | |
135 | ||
dfeec247 XL |
136 | build.config.gdb = build |
137 | .config | |
138 | .gdb | |
139 | .take() | |
140 | .map(|p| cmd_finder.must_have(p)) | |
041b39d2 | 141 | .or_else(|| cmd_finder.maybe_have("gdb")); |
c30ab7b3 | 142 | |
7453a54e SL |
143 | // We're gonna build some custom C code here and there, host triples |
144 | // also build some C++ shims for LLVM so we need a C++ compiler. | |
3b2f2976 | 145 | for target in &build.targets { |
c30ab7b3 SL |
146 | // On emscripten we don't actually need the C compiler to just |
147 | // build the target artifacts, only for testing. For the sake | |
148 | // of easier bot configuration, just skip detection. | |
149 | if target.contains("emscripten") { | |
150 | continue; | |
151 | } | |
152 | ||
48663c56 XL |
153 | // We don't use a C compiler on wasm32 |
154 | if target.contains("wasm32") { | |
155 | continue; | |
156 | } | |
157 | ||
83c7162d XL |
158 | if !build.config.dry_run { |
159 | cmd_finder.must_have(build.cc(*target)); | |
160 | if let Some(ar) = build.ar(*target) { | |
161 | cmd_finder.must_have(ar); | |
162 | } | |
3157f602 | 163 | } |
7453a54e | 164 | } |
7453a54e | 165 | |
3b2f2976 | 166 | for host in &build.hosts { |
83c7162d XL |
167 | if !build.config.dry_run { |
168 | cmd_finder.must_have(build.cxx(*host).unwrap()); | |
169 | } | |
c30ab7b3 SL |
170 | } |
171 | ||
fc512014 XL |
172 | if build.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) { |
173 | // Externally configured LLVM requires FileCheck to exist | |
174 | let filecheck = build.llvm_filecheck(build.build); | |
175 | if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { | |
176 | panic!("FileCheck executable {:?} does not exist", filecheck); | |
177 | } | |
a7813a04 XL |
178 | } |
179 | ||
3b2f2976 | 180 | for target in &build.targets { |
5869c6ff XL |
181 | build |
182 | .config | |
183 | .target_config | |
184 | .entry(*target) | |
185 | .or_insert_with(|| Target::from_triple(&target.triple)); | |
83c7162d | 186 | |
74b04a01 | 187 | if target.contains("-none-") || target.contains("nvptx") { |
83c7162d | 188 | if build.no_std(*target) == Some(false) { |
9fa01778 | 189 | panic!("All the *-none-* and nvptx* targets are no-std targets") |
83c7162d XL |
190 | } |
191 | } | |
192 | ||
3b2f2976 | 193 | // Make sure musl-root is valid |
2c00a5a8 | 194 | if target.contains("musl") { |
3b2f2976 XL |
195 | // If this is a native target (host is also musl) and no musl-root is given, |
196 | // fall back to the system toolchain in /usr before giving up | |
197 | if build.musl_root(*target).is_none() && build.config.build == *target { | |
fc512014 | 198 | let target = build.config.target_config.entry(*target).or_default(); |
3b2f2976 XL |
199 | target.musl_root = Some("/usr".into()); |
200 | } | |
f035d41b XL |
201 | match build.musl_libdir(*target) { |
202 | Some(libdir) => { | |
203 | if fs::metadata(libdir.join("libc.a")).is_err() { | |
204 | panic!("couldn't find libc.a in musl libdir: {}", libdir.display()); | |
7453a54e | 205 | } |
7453a54e | 206 | } |
dfeec247 XL |
207 | None => panic!( |
208 | "when targeting MUSL either the rust.musl-root \ | |
c30ab7b3 | 209 | option or the target.$TARGET.musl-root option must \ |
dfeec247 XL |
210 | be specified in config.toml" |
211 | ), | |
7453a54e SL |
212 | } |
213 | } | |
214 | ||
17df50a5 | 215 | if need_cmake && target.contains("msvc") { |
7453a54e SL |
216 | // There are three builds of cmake on windows: MSVC, MinGW, and |
217 | // Cygwin. The Cygwin build does not have generators for Visual | |
218 | // Studio, so detect that here and error. | |
219 | let out = output(Command::new("cmake").arg("--help")); | |
220 | if !out.contains("Visual Studio") { | |
dfeec247 XL |
221 | panic!( |
222 | " | |
7453a54e SL |
223 | cmake does not support Visual Studio generators. |
224 | ||
225 | This is likely due to it being an msys/cygwin build of cmake, | |
226 | rather than the required windows version, built using MinGW | |
227 | or Visual Studio. | |
228 | ||
229 | If you are building under msys2 try installing the mingw-w64-x86_64-cmake | |
230 | package instead of cmake: | |
231 | ||
232 | $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake | |
dfeec247 XL |
233 | " |
234 | ); | |
7453a54e SL |
235 | } |
236 | } | |
237 | } | |
54a0048b | 238 | |
476ff2be | 239 | if let Some(ref s) = build.config.ccache { |
041b39d2 | 240 | cmd_finder.must_have(s); |
476ff2be | 241 | } |
7453a54e | 242 | } |