]>
Commit | Line | Data |
---|---|---|
3b2f2976 XL |
1 | // Copyright 2017 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 | use std::fs; | |
12 | use std::env; | |
83c7162d | 13 | use std::iter; |
3b2f2976 | 14 | use std::path::PathBuf; |
ff7c6d11 | 15 | use std::process::{Command, exit}; |
8faf50e0 | 16 | use std::collections::HashSet; |
3b2f2976 XL |
17 | |
18 | use Mode; | |
19 | use Compiler; | |
20 | use builder::{Step, RunConfig, ShouldRun, Builder}; | |
83c7162d | 21 | use util::{exe, add_lib_path}; |
b7449926 | 22 | use compile; |
3b2f2976 XL |
23 | use native; |
24 | use channel::GitInfo; | |
13cf67c4 | 25 | use channel; |
3b2f2976 | 26 | use cache::Interned; |
ea8adc8c | 27 | use toolstate::ToolState; |
3b2f2976 | 28 | |
8faf50e0 XL |
29 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
30 | pub enum SourceType { | |
31 | InTree, | |
32 | Submodule, | |
33 | } | |
34 | ||
0531ce1d | 35 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
3b2f2976 XL |
36 | struct ToolBuild { |
37 | compiler: Compiler, | |
38 | target: Interned<String>, | |
39 | tool: &'static str, | |
ea8adc8c | 40 | path: &'static str, |
3b2f2976 | 41 | mode: Mode, |
8faf50e0 XL |
42 | is_optional_tool: bool, |
43 | source_type: SourceType, | |
0531ce1d | 44 | extra_features: Vec<String>, |
3b2f2976 XL |
45 | } |
46 | ||
47 | impl Step for ToolBuild { | |
abe05a73 | 48 | type Output = Option<PathBuf>; |
3b2f2976 XL |
49 | |
50 | fn should_run(run: ShouldRun) -> ShouldRun { | |
51 | run.never() | |
52 | } | |
53 | ||
54 | /// Build a tool in `src/tools` | |
55 | /// | |
56 | /// This will build the specified tool with the specified `host` compiler in | |
57 | /// `stage` into the normal cargo output directory. | |
abe05a73 | 58 | fn run(self, builder: &Builder) -> Option<PathBuf> { |
3b2f2976 XL |
59 | let compiler = self.compiler; |
60 | let target = self.target; | |
61 | let tool = self.tool; | |
ea8adc8c | 62 | let path = self.path; |
8faf50e0 | 63 | let is_optional_tool = self.is_optional_tool; |
3b2f2976 XL |
64 | |
65 | match self.mode { | |
8faf50e0 XL |
66 | Mode::ToolRustc => { |
67 | builder.ensure(compile::Rustc { compiler, target }) | |
68 | } | |
69 | Mode::ToolStd => { | |
70 | builder.ensure(compile::Std { compiler, target }) | |
71 | } | |
72 | Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs | |
94b46f34 | 73 | _ => panic!("unexpected Mode for tool build") |
3b2f2976 XL |
74 | } |
75 | ||
8faf50e0 XL |
76 | let mut cargo = prepare_tool_cargo( |
77 | builder, | |
78 | compiler, | |
79 | self.mode, | |
80 | target, | |
81 | "build", | |
82 | path, | |
83 | self.source_type, | |
0bf4aa26 | 84 | &self.extra_features, |
8faf50e0 | 85 | ); |
0531ce1d | 86 | |
83c7162d XL |
87 | let _folder = builder.fold_output(|| format!("stage{}-{}", compiler.stage, tool)); |
88 | builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target)); | |
0531ce1d | 89 | let mut duplicates = Vec::new(); |
b7449926 | 90 | let is_expected = compile::stream_cargo(builder, &mut cargo, vec![], &mut |msg| { |
0531ce1d | 91 | // Only care about big things like the RLS/Cargo for now |
8faf50e0 XL |
92 | match tool { |
93 | | "rls" | |
94 | | "cargo" | |
95 | | "clippy-driver" | |
96 | => {} | |
97 | ||
98 | _ => return, | |
0531ce1d XL |
99 | } |
100 | let (id, features, filenames) = match msg { | |
101 | compile::CargoMessage::CompilerArtifact { | |
102 | package_id, | |
103 | features, | |
104 | filenames | |
105 | } => { | |
106 | (package_id, features, filenames) | |
107 | } | |
108 | _ => return, | |
109 | }; | |
110 | let features = features.iter().map(|s| s.to_string()).collect::<Vec<_>>(); | |
111 | ||
112 | for path in filenames { | |
113 | let val = (tool, PathBuf::from(&*path), features.clone()); | |
114 | // we're only interested in deduplicating rlibs for now | |
115 | if val.1.extension().and_then(|s| s.to_str()) != Some("rlib") { | |
116 | continue | |
117 | } | |
118 | ||
119 | // Don't worry about libs that turn out to be host dependencies | |
120 | // or build scripts, we only care about target dependencies that | |
121 | // are in `deps`. | |
122 | if let Some(maybe_target) = val.1 | |
123 | .parent() // chop off file name | |
124 | .and_then(|p| p.parent()) // chop off `deps` | |
125 | .and_then(|p| p.parent()) // chop off `release` | |
126 | .and_then(|p| p.file_name()) | |
127 | .and_then(|p| p.to_str()) | |
128 | { | |
129 | if maybe_target != &*target { | |
130 | continue | |
131 | } | |
132 | } | |
133 | ||
83c7162d | 134 | let mut artifacts = builder.tool_artifacts.borrow_mut(); |
0531ce1d XL |
135 | let prev_artifacts = artifacts |
136 | .entry(target) | |
b7449926 | 137 | .or_default(); |
0531ce1d XL |
138 | if let Some(prev) = prev_artifacts.get(&*id) { |
139 | if prev.1 != val.1 { | |
140 | duplicates.push(( | |
141 | id.to_string(), | |
142 | val, | |
143 | prev.clone(), | |
144 | )); | |
145 | } | |
146 | return | |
147 | } | |
148 | prev_artifacts.insert(id.to_string(), val); | |
149 | } | |
150 | }); | |
151 | ||
152 | if is_expected && duplicates.len() != 0 { | |
153 | println!("duplicate artfacts found when compiling a tool, this \ | |
154 | typically means that something was recompiled because \ | |
155 | a transitive dependency has different features activated \ | |
156 | than in a previous build:\n"); | |
8faf50e0 XL |
157 | println!("the following dependencies are duplicated although they \ |
158 | have the same features enabled:"); | |
159 | for (id, cur, prev) in duplicates.drain_filter(|(_, cur, prev)| cur.2 == prev.2) { | |
160 | println!(" {}", id); | |
161 | // same features | |
162 | println!(" `{}` ({:?})\n `{}` ({:?})", cur.0, cur.1, prev.0, prev.1); | |
163 | } | |
164 | println!("the following dependencies have different features:"); | |
0531ce1d XL |
165 | for (id, cur, prev) in duplicates { |
166 | println!(" {}", id); | |
8faf50e0 XL |
167 | let cur_features: HashSet<_> = cur.2.into_iter().collect(); |
168 | let prev_features: HashSet<_> = prev.2.into_iter().collect(); | |
169 | println!(" `{}` additionally enabled features {:?} at {:?}", | |
170 | cur.0, &cur_features - &prev_features, cur.1); | |
171 | println!(" `{}` additionally enabled features {:?} at {:?}", | |
172 | prev.0, &prev_features - &cur_features, prev.1); | |
0531ce1d XL |
173 | } |
174 | println!(""); | |
b7449926 XL |
175 | println!("to fix this you will probably want to edit the local \ |
176 | src/tools/rustc-workspace-hack/Cargo.toml crate, as \ | |
177 | that will update the dependency graph to ensure that \ | |
178 | these crates all share the same feature set"); | |
0531ce1d XL |
179 | panic!("tools should not compile multiple copies of the same crate"); |
180 | } | |
3b2f2976 | 181 | |
83c7162d | 182 | builder.save_toolstate(tool, if is_expected { |
ff7c6d11 XL |
183 | ToolState::TestFail |
184 | } else { | |
185 | ToolState::BuildFail | |
186 | }); | |
187 | ||
188 | if !is_expected { | |
8faf50e0 | 189 | if !is_optional_tool { |
ff7c6d11 XL |
190 | exit(1); |
191 | } else { | |
192 | return None; | |
193 | } | |
194 | } else { | |
94b46f34 | 195 | let cargo_out = builder.cargo_out(compiler, self.mode, target) |
abe05a73 | 196 | .join(exe(tool, &compiler.host)); |
83c7162d XL |
197 | let bin = builder.tools_dir(compiler).join(exe(tool, &compiler.host)); |
198 | builder.copy(&cargo_out, &bin); | |
abe05a73 | 199 | Some(bin) |
abe05a73 | 200 | } |
3b2f2976 XL |
201 | } |
202 | } | |
203 | ||
ea8adc8c | 204 | pub fn prepare_tool_cargo( |
3b2f2976 XL |
205 | builder: &Builder, |
206 | compiler: Compiler, | |
94b46f34 | 207 | mode: Mode, |
3b2f2976 | 208 | target: Interned<String>, |
ea8adc8c XL |
209 | command: &'static str, |
210 | path: &'static str, | |
8faf50e0 | 211 | source_type: SourceType, |
0bf4aa26 | 212 | extra_features: &[String], |
3b2f2976 | 213 | ) -> Command { |
94b46f34 | 214 | let mut cargo = builder.cargo(compiler, mode, target, command); |
83c7162d | 215 | let dir = builder.src.join(path); |
3b2f2976 XL |
216 | cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); |
217 | ||
218 | // We don't want to build tools dynamically as they'll be running across | |
219 | // stages and such and it's just easier if they're not dynamically linked. | |
220 | cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); | |
221 | ||
8faf50e0 XL |
222 | if source_type == SourceType::Submodule { |
223 | cargo.env("RUSTC_EXTERNAL_TOOL", "1"); | |
224 | } | |
225 | ||
0bf4aa26 XL |
226 | let mut features = extra_features.iter().cloned().collect::<Vec<_>>(); |
227 | if builder.build.config.cargo_native_static { | |
228 | if path.ends_with("cargo") || | |
229 | path.ends_with("rls") || | |
230 | path.ends_with("clippy") || | |
231 | path.ends_with("rustfmt") | |
232 | { | |
233 | cargo.env("LIBZ_SYS_STATIC", "1"); | |
234 | features.push("rustc-workspace-hack/all-static".to_string()); | |
235 | } | |
3b2f2976 XL |
236 | } |
237 | ||
ea8adc8c XL |
238 | // if tools are using lzma we want to force the build script to build its |
239 | // own copy | |
240 | cargo.env("LZMA_API_STATIC", "1"); | |
241 | ||
83c7162d XL |
242 | cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel); |
243 | cargo.env("CFG_VERSION", builder.rust_version()); | |
13cf67c4 | 244 | cargo.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM); |
3b2f2976 | 245 | |
83c7162d | 246 | let info = GitInfo::new(&builder.config, &dir); |
3b2f2976 XL |
247 | if let Some(sha) = info.sha() { |
248 | cargo.env("CFG_COMMIT_HASH", sha); | |
249 | } | |
250 | if let Some(sha_short) = info.sha_short() { | |
251 | cargo.env("CFG_SHORT_COMMIT_HASH", sha_short); | |
252 | } | |
253 | if let Some(date) = info.commit_date() { | |
254 | cargo.env("CFG_COMMIT_DATE", date); | |
255 | } | |
0bf4aa26 XL |
256 | if features.len() > 0 { |
257 | cargo.arg("--features").arg(&features.join(", ")); | |
258 | } | |
3b2f2976 XL |
259 | cargo |
260 | } | |
261 | ||
262 | macro_rules! tool { | |
8faf50e0 XL |
263 | ($($name:ident, $path:expr, $tool_name:expr, $mode:expr |
264 | $(,llvm_tools = $llvm:expr)* $(,is_external_tool = $external:expr)*;)+) => { | |
265 | #[derive(Copy, PartialEq, Eq, Clone)] | |
3b2f2976 XL |
266 | pub enum Tool { |
267 | $( | |
268 | $name, | |
269 | )+ | |
270 | } | |
271 | ||
94b46f34 XL |
272 | impl Tool { |
273 | pub fn get_mode(&self) -> Mode { | |
274 | let mode = match self { | |
275 | $(Tool::$name => $mode,)+ | |
276 | }; | |
277 | mode | |
278 | } | |
279 | ||
280 | /// Whether this tool requires LLVM to run | |
281 | pub fn uses_llvm_tools(&self) -> bool { | |
282 | match self { | |
283 | $(Tool::$name => false $(|| $llvm)*,)+ | |
284 | } | |
285 | } | |
286 | } | |
287 | ||
3b2f2976 XL |
288 | impl<'a> Builder<'a> { |
289 | pub fn tool_exe(&self, tool: Tool) -> PathBuf { | |
ea8adc8c | 290 | let stage = self.tool_default_stage(tool); |
3b2f2976 XL |
291 | match tool { |
292 | $(Tool::$name => | |
293 | self.ensure($name { | |
83c7162d XL |
294 | compiler: self.compiler(stage, self.config.build), |
295 | target: self.config.build, | |
3b2f2976 XL |
296 | }), |
297 | )+ | |
298 | } | |
299 | } | |
ea8adc8c XL |
300 | |
301 | pub fn tool_default_stage(&self, tool: Tool) -> u32 { | |
abe05a73 XL |
302 | // Compile the error-index in the same stage as rustdoc to avoid |
303 | // recompiling rustdoc twice if we can. Otherwise compile | |
304 | // everything else in stage0 as there's no need to rebootstrap | |
305 | // everything. | |
ea8adc8c | 306 | match tool { |
abe05a73 | 307 | Tool::ErrorIndex if self.top_stage >= 2 => self.top_stage, |
ea8adc8c XL |
308 | _ => 0, |
309 | } | |
310 | } | |
3b2f2976 XL |
311 | } |
312 | ||
313 | $( | |
314 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
315 | pub struct $name { | |
316 | pub compiler: Compiler, | |
317 | pub target: Interned<String>, | |
318 | } | |
319 | ||
320 | impl Step for $name { | |
321 | type Output = PathBuf; | |
322 | ||
323 | fn should_run(run: ShouldRun) -> ShouldRun { | |
324 | run.path($path) | |
325 | } | |
326 | ||
327 | fn make_run(run: RunConfig) { | |
328 | run.builder.ensure($name { | |
83c7162d | 329 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
3b2f2976 XL |
330 | target: run.target, |
331 | }); | |
332 | } | |
333 | ||
334 | fn run(self, builder: &Builder) -> PathBuf { | |
335 | builder.ensure(ToolBuild { | |
336 | compiler: self.compiler, | |
337 | target: self.target, | |
338 | tool: $tool_name, | |
339 | mode: $mode, | |
ea8adc8c | 340 | path: $path, |
8faf50e0 XL |
341 | is_optional_tool: false, |
342 | source_type: if false $(|| $external)* { | |
343 | SourceType::Submodule | |
344 | } else { | |
345 | SourceType::InTree | |
346 | }, | |
0531ce1d | 347 | extra_features: Vec::new(), |
ff7c6d11 | 348 | }).expect("expected to build -- essential tool") |
3b2f2976 XL |
349 | } |
350 | } | |
351 | )+ | |
352 | } | |
353 | } | |
354 | ||
355 | tool!( | |
8faf50e0 | 356 | Rustbook, "src/tools/rustbook", "rustbook", Mode::ToolBootstrap; |
94b46f34 | 357 | ErrorIndex, "src/tools/error_index_generator", "error_index_generator", Mode::ToolRustc; |
8faf50e0 XL |
358 | UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen", Mode::ToolBootstrap; |
359 | Tidy, "src/tools/tidy", "tidy", Mode::ToolBootstrap; | |
360 | Linkchecker, "src/tools/linkchecker", "linkchecker", Mode::ToolBootstrap; | |
361 | CargoTest, "src/tools/cargotest", "cargotest", Mode::ToolBootstrap; | |
362 | Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolBootstrap, llvm_tools = true; | |
363 | BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolBootstrap; | |
364 | RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolBootstrap; | |
365 | RustInstaller, "src/tools/rust-installer", "fabricate", Mode::ToolBootstrap, | |
366 | is_external_tool = true; | |
367 | RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes", Mode::ToolBootstrap; | |
3b2f2976 XL |
368 | ); |
369 | ||
370 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
371 | pub struct RemoteTestServer { | |
372 | pub compiler: Compiler, | |
373 | pub target: Interned<String>, | |
374 | } | |
375 | ||
376 | impl Step for RemoteTestServer { | |
377 | type Output = PathBuf; | |
378 | ||
379 | fn should_run(run: ShouldRun) -> ShouldRun { | |
380 | run.path("src/tools/remote-test-server") | |
381 | } | |
382 | ||
383 | fn make_run(run: RunConfig) { | |
384 | run.builder.ensure(RemoteTestServer { | |
83c7162d | 385 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
3b2f2976 XL |
386 | target: run.target, |
387 | }); | |
388 | } | |
389 | ||
390 | fn run(self, builder: &Builder) -> PathBuf { | |
391 | builder.ensure(ToolBuild { | |
392 | compiler: self.compiler, | |
393 | target: self.target, | |
394 | tool: "remote-test-server", | |
94b46f34 | 395 | mode: Mode::ToolStd, |
ea8adc8c | 396 | path: "src/tools/remote-test-server", |
8faf50e0 XL |
397 | is_optional_tool: false, |
398 | source_type: SourceType::InTree, | |
0531ce1d | 399 | extra_features: Vec::new(), |
ff7c6d11 | 400 | }).expect("expected to build -- essential tool") |
3b2f2976 XL |
401 | } |
402 | } | |
403 | ||
404 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
405 | pub struct Rustdoc { | |
406 | pub host: Interned<String>, | |
407 | } | |
408 | ||
409 | impl Step for Rustdoc { | |
410 | type Output = PathBuf; | |
411 | const DEFAULT: bool = true; | |
412 | const ONLY_HOSTS: bool = true; | |
413 | ||
414 | fn should_run(run: ShouldRun) -> ShouldRun { | |
415 | run.path("src/tools/rustdoc") | |
416 | } | |
417 | ||
418 | fn make_run(run: RunConfig) { | |
419 | run.builder.ensure(Rustdoc { | |
420 | host: run.host, | |
421 | }); | |
422 | } | |
423 | ||
424 | fn run(self, builder: &Builder) -> PathBuf { | |
3b2f2976 XL |
425 | let target_compiler = builder.compiler(builder.top_stage, self.host); |
426 | let target = target_compiler.host; | |
427 | let build_compiler = if target_compiler.stage == 0 { | |
83c7162d | 428 | builder.compiler(0, builder.config.build) |
3b2f2976 XL |
429 | } else if target_compiler.stage >= 2 { |
430 | // Past stage 2, we consider the compiler to be ABI-compatible and hence capable of | |
431 | // building rustdoc itself. | |
83c7162d | 432 | builder.compiler(target_compiler.stage, builder.config.build) |
3b2f2976 XL |
433 | } else { |
434 | // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise | |
435 | // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage | |
436 | // compilers, which isn't what we want. | |
83c7162d | 437 | builder.compiler(target_compiler.stage - 1, builder.config.build) |
3b2f2976 XL |
438 | }; |
439 | ||
440 | builder.ensure(compile::Rustc { compiler: build_compiler, target }); | |
0531ce1d XL |
441 | builder.ensure(compile::Rustc { |
442 | compiler: build_compiler, | |
83c7162d | 443 | target: builder.config.build, |
0531ce1d | 444 | }); |
3b2f2976 | 445 | |
8faf50e0 XL |
446 | let mut cargo = prepare_tool_cargo( |
447 | builder, | |
448 | build_compiler, | |
449 | Mode::ToolRustc, | |
450 | target, | |
451 | "build", | |
452 | "src/tools/rustdoc", | |
453 | SourceType::InTree, | |
0bf4aa26 | 454 | &[], |
8faf50e0 | 455 | ); |
ea8adc8c XL |
456 | |
457 | // Most tools don't get debuginfo, but rustdoc should. | |
458 | cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string()) | |
459 | .env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string()); | |
460 | ||
83c7162d XL |
461 | let _folder = builder.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage)); |
462 | builder.info(&format!("Building rustdoc for stage{} ({})", | |
463 | target_compiler.stage, target_compiler.host)); | |
464 | builder.run(&mut cargo); | |
0531ce1d | 465 | |
3b2f2976 XL |
466 | // Cargo adds a number of paths to the dylib search path on windows, which results in |
467 | // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" | |
468 | // rustdoc a different name. | |
94b46f34 XL |
469 | let tool_rustdoc = builder.cargo_out(build_compiler, Mode::ToolRustc, target) |
470 | .join(exe("rustdoc_tool_binary", &target_compiler.host)); | |
3b2f2976 XL |
471 | |
472 | // don't create a stage0-sysroot/bin directory. | |
473 | if target_compiler.stage > 0 { | |
474 | let sysroot = builder.sysroot(target_compiler); | |
475 | let bindir = sysroot.join("bin"); | |
476 | t!(fs::create_dir_all(&bindir)); | |
477 | let bin_rustdoc = bindir.join(exe("rustdoc", &*target_compiler.host)); | |
478 | let _ = fs::remove_file(&bin_rustdoc); | |
83c7162d | 479 | builder.copy(&tool_rustdoc, &bin_rustdoc); |
3b2f2976 XL |
480 | bin_rustdoc |
481 | } else { | |
482 | tool_rustdoc | |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
488 | pub struct Cargo { | |
489 | pub compiler: Compiler, | |
490 | pub target: Interned<String>, | |
491 | } | |
492 | ||
493 | impl Step for Cargo { | |
494 | type Output = PathBuf; | |
495 | const DEFAULT: bool = true; | |
496 | const ONLY_HOSTS: bool = true; | |
497 | ||
498 | fn should_run(run: ShouldRun) -> ShouldRun { | |
499 | let builder = run.builder; | |
83c7162d | 500 | run.path("src/tools/cargo").default_condition(builder.config.extended) |
3b2f2976 XL |
501 | } |
502 | ||
503 | fn make_run(run: RunConfig) { | |
504 | run.builder.ensure(Cargo { | |
83c7162d | 505 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
3b2f2976 XL |
506 | target: run.target, |
507 | }); | |
508 | } | |
509 | ||
510 | fn run(self, builder: &Builder) -> PathBuf { | |
3b2f2976 XL |
511 | // Cargo depends on procedural macros, which requires a full host |
512 | // compiler to be available, so we need to depend on that. | |
513 | builder.ensure(compile::Rustc { | |
514 | compiler: self.compiler, | |
83c7162d | 515 | target: builder.config.build, |
3b2f2976 XL |
516 | }); |
517 | builder.ensure(ToolBuild { | |
518 | compiler: self.compiler, | |
519 | target: self.target, | |
520 | tool: "cargo", | |
94b46f34 | 521 | mode: Mode::ToolRustc, |
ea8adc8c | 522 | path: "src/tools/cargo", |
8faf50e0 XL |
523 | is_optional_tool: false, |
524 | source_type: SourceType::Submodule, | |
0531ce1d | 525 | extra_features: Vec::new(), |
ff7c6d11 | 526 | }).expect("expected to build -- essential tool") |
ea8adc8c XL |
527 | } |
528 | } | |
529 | ||
ff7c6d11 XL |
530 | macro_rules! tool_extended { |
531 | (($sel:ident, $builder:ident), | |
532 | $($name:ident, | |
533 | $toolstate:ident, | |
534 | $path:expr, | |
535 | $tool_name:expr, | |
536 | $extra_deps:block;)+) => { | |
537 | $( | |
0531ce1d | 538 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
ff7c6d11 XL |
539 | pub struct $name { |
540 | pub compiler: Compiler, | |
541 | pub target: Interned<String>, | |
0531ce1d | 542 | pub extra_features: Vec<String>, |
ff7c6d11 | 543 | } |
ea8adc8c | 544 | |
ff7c6d11 XL |
545 | impl Step for $name { |
546 | type Output = Option<PathBuf>; | |
547 | const DEFAULT: bool = true; | |
548 | const ONLY_HOSTS: bool = true; | |
ea8adc8c | 549 | |
ff7c6d11 XL |
550 | fn should_run(run: ShouldRun) -> ShouldRun { |
551 | let builder = run.builder; | |
83c7162d | 552 | run.path($path).default_condition(builder.config.extended) |
ff7c6d11 | 553 | } |
ea8adc8c | 554 | |
ff7c6d11 XL |
555 | fn make_run(run: RunConfig) { |
556 | run.builder.ensure($name { | |
83c7162d | 557 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
ff7c6d11 | 558 | target: run.target, |
0531ce1d | 559 | extra_features: Vec::new(), |
ff7c6d11 XL |
560 | }); |
561 | } | |
562 | ||
0531ce1d XL |
563 | #[allow(unused_mut)] |
564 | fn run(mut $sel, $builder: &Builder) -> Option<PathBuf> { | |
ff7c6d11 XL |
565 | $extra_deps |
566 | $builder.ensure(ToolBuild { | |
567 | compiler: $sel.compiler, | |
568 | target: $sel.target, | |
569 | tool: $tool_name, | |
94b46f34 | 570 | mode: Mode::ToolRustc, |
ff7c6d11 | 571 | path: $path, |
0531ce1d | 572 | extra_features: $sel.extra_features, |
8faf50e0 XL |
573 | is_optional_tool: true, |
574 | source_type: SourceType::Submodule, | |
ff7c6d11 XL |
575 | }) |
576 | } | |
577 | } | |
578 | )+ | |
ea8adc8c | 579 | } |
ff7c6d11 | 580 | } |
ea8adc8c | 581 | |
ff7c6d11 XL |
582 | tool_extended!((self, builder), |
583 | Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {}; | |
8faf50e0 XL |
584 | CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", { |
585 | // Clippy depends on procedural macros (serde), which requires a full host | |
586 | // compiler to be available, so we need to depend on that. | |
587 | builder.ensure(compile::Rustc { | |
588 | compiler: self.compiler, | |
589 | target: builder.config.build, | |
590 | }); | |
591 | }; | |
ff7c6d11 | 592 | Clippy, clippy, "src/tools/clippy", "clippy-driver", { |
ea8adc8c XL |
593 | // Clippy depends on procedural macros (serde), which requires a full host |
594 | // compiler to be available, so we need to depend on that. | |
595 | builder.ensure(compile::Rustc { | |
596 | compiler: self.compiler, | |
83c7162d | 597 | target: builder.config.build, |
ea8adc8c | 598 | }); |
ff7c6d11 XL |
599 | }; |
600 | Miri, miri, "src/tools/miri", "miri", {}; | |
601 | Rls, rls, "src/tools/rls", "rls", { | |
8faf50e0 XL |
602 | let clippy = builder.ensure(Clippy { |
603 | compiler: self.compiler, | |
604 | target: self.target, | |
605 | extra_features: Vec::new(), | |
606 | }); | |
607 | if clippy.is_some() { | |
608 | self.extra_features.push("clippy".to_owned()); | |
609 | } | |
3b2f2976 XL |
610 | // RLS depends on procedural macros, which requires a full host |
611 | // compiler to be available, so we need to depend on that. | |
612 | builder.ensure(compile::Rustc { | |
613 | compiler: self.compiler, | |
83c7162d | 614 | target: builder.config.build, |
3b2f2976 | 615 | }); |
ff7c6d11 XL |
616 | }; |
617 | Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {}; | |
618 | ); | |
3b2f2976 XL |
619 | |
620 | impl<'a> Builder<'a> { | |
621 | /// Get a `Command` which is ready to run `tool` in `stage` built for | |
622 | /// `host`. | |
623 | pub fn tool_cmd(&self, tool: Tool) -> Command { | |
624 | let mut cmd = Command::new(self.tool_exe(tool)); | |
83c7162d | 625 | let compiler = self.compiler(self.tool_default_stage(tool), self.config.build); |
94b46f34 | 626 | self.prepare_tool_cmd(compiler, tool, &mut cmd); |
3b2f2976 XL |
627 | cmd |
628 | } | |
629 | ||
630 | /// Prepares the `cmd` provided to be able to run the `compiler` provided. | |
631 | /// | |
632 | /// Notably this munges the dynamic library lookup path to point to the | |
633 | /// right location to run `compiler`. | |
94b46f34 | 634 | fn prepare_tool_cmd(&self, compiler: Compiler, tool: Tool, cmd: &mut Command) { |
3b2f2976 | 635 | let host = &compiler.host; |
83c7162d | 636 | let mut lib_paths: Vec<PathBuf> = vec![ |
8faf50e0 XL |
637 | if compiler.stage == 0 && tool != Tool::ErrorIndex { |
638 | self.build.rustc_snapshot_libdir() | |
639 | } else { | |
640 | PathBuf::from(&self.sysroot_libdir(compiler, compiler.host)) | |
641 | }, | |
94b46f34 | 642 | self.cargo_out(compiler, tool.get_mode(), *host).join("deps"), |
3b2f2976 XL |
643 | ]; |
644 | ||
645 | // On MSVC a tool may invoke a C compiler (e.g. compiletest in run-make | |
646 | // mode) and that C compiler may need some extra PATH modification. Do | |
647 | // so here. | |
648 | if compiler.host.contains("msvc") { | |
649 | let curpaths = env::var_os("PATH").unwrap_or_default(); | |
650 | let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>(); | |
abe05a73 | 651 | for &(ref k, ref v) in self.cc[&compiler.host].env() { |
3b2f2976 XL |
652 | if k != "PATH" { |
653 | continue | |
654 | } | |
655 | for path in env::split_paths(v) { | |
656 | if !curpaths.contains(&path) { | |
83c7162d | 657 | lib_paths.push(path); |
3b2f2976 XL |
658 | } |
659 | } | |
660 | } | |
661 | } | |
83c7162d XL |
662 | |
663 | // Add the llvm/bin directory to PATH since it contains lots of | |
664 | // useful, platform-independent tools | |
94b46f34 XL |
665 | if tool.uses_llvm_tools() { |
666 | if let Some(llvm_bin_path) = self.llvm_bin_path() { | |
667 | if host.contains("windows") { | |
668 | // On Windows, PATH and the dynamic library path are the same, | |
669 | // so we just add the LLVM bin path to lib_path | |
670 | lib_paths.push(llvm_bin_path); | |
671 | } else { | |
672 | let old_path = env::var_os("PATH").unwrap_or_default(); | |
673 | let new_path = env::join_paths(iter::once(llvm_bin_path) | |
674 | .chain(env::split_paths(&old_path))) | |
675 | .expect("Could not add LLVM bin path to PATH"); | |
676 | cmd.env("PATH", new_path); | |
677 | } | |
83c7162d XL |
678 | } |
679 | } | |
680 | ||
681 | add_lib_path(lib_paths, cmd); | |
682 | } | |
683 | ||
684 | fn llvm_bin_path(&self) -> Option<PathBuf> { | |
685 | if self.config.llvm_enabled && !self.config.dry_run { | |
686 | let llvm_config = self.ensure(native::Llvm { | |
687 | target: self.config.build, | |
688 | emscripten: false, | |
689 | }); | |
690 | ||
691 | // Add the llvm/bin directory to PATH since it contains lots of | |
692 | // useful, platform-independent tools | |
693 | let llvm_bin_path = llvm_config.parent() | |
694 | .expect("Expected llvm-config to be contained in directory"); | |
695 | assert!(llvm_bin_path.is_dir()); | |
696 | Some(llvm_bin_path.to_path_buf()) | |
697 | } else { | |
698 | None | |
699 | } | |
3b2f2976 XL |
700 | } |
701 | } |