]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use std::collections::HashSet; |
3b2f2976 | 2 | use std::env; |
dfeec247 | 3 | use std::fs; |
3b2f2976 | 4 | use std::path::PathBuf; |
dfeec247 | 5 | use std::process::{exit, Command}; |
3b2f2976 | 6 | |
48663c56 XL |
7 | use build_helper::t; |
8 | ||
dfeec247 | 9 | use crate::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; |
dfeec247 XL |
10 | use crate::channel::GitInfo; |
11 | use crate::compile; | |
3dfed10e | 12 | use crate::config::TargetSelection; |
0731742a | 13 | use crate::toolstate::ToolState; |
f035d41b | 14 | use crate::util::{add_dylib_path, exe}; |
dfeec247 XL |
15 | use crate::Compiler; |
16 | use crate::Mode; | |
3b2f2976 | 17 | |
f035d41b | 18 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] |
8faf50e0 XL |
19 | pub enum SourceType { |
20 | InTree, | |
21 | Submodule, | |
22 | } | |
23 | ||
0531ce1d | 24 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
3b2f2976 XL |
25 | struct ToolBuild { |
26 | compiler: Compiler, | |
3dfed10e | 27 | target: TargetSelection, |
3b2f2976 | 28 | tool: &'static str, |
ea8adc8c | 29 | path: &'static str, |
3b2f2976 | 30 | mode: Mode, |
8faf50e0 XL |
31 | is_optional_tool: bool, |
32 | source_type: SourceType, | |
0531ce1d | 33 | extra_features: Vec<String>, |
3b2f2976 XL |
34 | } |
35 | ||
36 | impl Step for ToolBuild { | |
abe05a73 | 37 | type Output = Option<PathBuf>; |
3b2f2976 | 38 | |
9fa01778 | 39 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 XL |
40 | run.never() |
41 | } | |
42 | ||
9fa01778 | 43 | /// Builds a tool in `src/tools` |
3b2f2976 XL |
44 | /// |
45 | /// This will build the specified tool with the specified `host` compiler in | |
46 | /// `stage` into the normal cargo output directory. | |
9fa01778 | 47 | fn run(self, builder: &Builder<'_>) -> Option<PathBuf> { |
3b2f2976 XL |
48 | let compiler = self.compiler; |
49 | let target = self.target; | |
6a06907d | 50 | let mut tool = self.tool; |
ea8adc8c | 51 | let path = self.path; |
8faf50e0 | 52 | let is_optional_tool = self.is_optional_tool; |
3b2f2976 XL |
53 | |
54 | match self.mode { | |
dfeec247 XL |
55 | Mode::ToolRustc => builder.ensure(compile::Rustc { compiler, target }), |
56 | Mode::ToolStd => builder.ensure(compile::Std { compiler, target }), | |
8faf50e0 | 57 | Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs |
dfeec247 | 58 | _ => panic!("unexpected Mode for tool build"), |
3b2f2976 XL |
59 | } |
60 | ||
e1599b0c | 61 | let cargo = prepare_tool_cargo( |
8faf50e0 XL |
62 | builder, |
63 | compiler, | |
64 | self.mode, | |
65 | target, | |
66 | "build", | |
67 | path, | |
68 | self.source_type, | |
0bf4aa26 | 69 | &self.extra_features, |
8faf50e0 | 70 | ); |
0531ce1d | 71 | |
83c7162d | 72 | builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target)); |
0531ce1d | 73 | let mut duplicates = Vec::new(); |
e1599b0c | 74 | let is_expected = compile::stream_cargo(builder, cargo, vec![], &mut |msg| { |
0531ce1d | 75 | // Only care about big things like the RLS/Cargo for now |
8faf50e0 | 76 | match tool { |
dfeec247 | 77 | "rls" | "cargo" | "clippy-driver" | "miri" | "rustfmt" => {} |
8faf50e0 XL |
78 | |
79 | _ => return, | |
0531ce1d XL |
80 | } |
81 | let (id, features, filenames) = match msg { | |
82 | compile::CargoMessage::CompilerArtifact { | |
83 | package_id, | |
84 | features, | |
532ac7d7 XL |
85 | filenames, |
86 | target: _, | |
dfeec247 | 87 | } => (package_id, features, filenames), |
0531ce1d XL |
88 | _ => return, |
89 | }; | |
90 | let features = features.iter().map(|s| s.to_string()).collect::<Vec<_>>(); | |
91 | ||
92 | for path in filenames { | |
93 | let val = (tool, PathBuf::from(&*path), features.clone()); | |
94 | // we're only interested in deduplicating rlibs for now | |
95 | if val.1.extension().and_then(|s| s.to_str()) != Some("rlib") { | |
dfeec247 | 96 | continue; |
0531ce1d XL |
97 | } |
98 | ||
416331ca XL |
99 | // Don't worry about compiles that turn out to be host |
100 | // dependencies or build scripts. To skip these we look for | |
101 | // anything that goes in `.../release/deps` but *doesn't* go in | |
102 | // `$target/release/deps`. This ensure that outputs in | |
103 | // `$target/release` are still considered candidates for | |
104 | // deduplication. | |
105 | if let Some(parent) = val.1.parent() { | |
106 | if parent.ends_with("release/deps") { | |
107 | let maybe_target = parent | |
108 | .parent() | |
109 | .and_then(|p| p.parent()) | |
110 | .and_then(|p| p.file_name()) | |
111 | .and_then(|p| p.to_str()) | |
112 | .unwrap(); | |
3dfed10e | 113 | if maybe_target != &*target.triple { |
416331ca XL |
114 | continue; |
115 | } | |
0531ce1d XL |
116 | } |
117 | } | |
118 | ||
416331ca XL |
119 | // Record that we've built an artifact for `id`, and if one was |
120 | // already listed then we need to see if we reused the same | |
121 | // artifact or produced a duplicate. | |
83c7162d | 122 | let mut artifacts = builder.tool_artifacts.borrow_mut(); |
dfeec247 | 123 | let prev_artifacts = artifacts.entry(target).or_default(); |
416331ca XL |
124 | let prev = match prev_artifacts.get(&*id) { |
125 | Some(prev) => prev, | |
126 | None => { | |
127 | prev_artifacts.insert(id.to_string(), val); | |
128 | continue; | |
0531ce1d | 129 | } |
416331ca XL |
130 | }; |
131 | if prev.1 == val.1 { | |
132 | return; // same path, same artifact | |
0531ce1d | 133 | } |
416331ca XL |
134 | |
135 | // If the paths are different and one of them *isn't* inside of | |
136 | // `release/deps`, then it means it's probably in | |
137 | // `$target/release`, or it's some final artifact like | |
138 | // `libcargo.rlib`. In these situations Cargo probably just | |
139 | // copied it up from `$target/release/deps/libcargo-xxxx.rlib`, | |
140 | // so if the features are equal we can just skip it. | |
141 | let prev_no_hash = prev.1.parent().unwrap().ends_with("release/deps"); | |
142 | let val_no_hash = val.1.parent().unwrap().ends_with("release/deps"); | |
143 | if prev.2 == val.2 || !prev_no_hash || !val_no_hash { | |
144 | return; | |
145 | } | |
146 | ||
147 | // ... and otherwise this looks like we duplicated some sort of | |
148 | // compilation, so record it to generate an error later. | |
dfeec247 | 149 | duplicates.push((id.to_string(), val, prev.clone())); |
0531ce1d XL |
150 | } |
151 | }); | |
152 | ||
a1dfa0c6 | 153 | if is_expected && !duplicates.is_empty() { |
dfeec247 XL |
154 | println!( |
155 | "duplicate artifacts found when compiling a tool, this \ | |
0531ce1d XL |
156 | typically means that something was recompiled because \ |
157 | a transitive dependency has different features activated \ | |
dfeec247 XL |
158 | than in a previous build:\n" |
159 | ); | |
160 | println!( | |
161 | "the following dependencies are duplicated although they \ | |
162 | have the same features enabled:" | |
163 | ); | |
1b1a35ee XL |
164 | let (same, different): (Vec<_>, Vec<_>) = |
165 | duplicates.into_iter().partition(|(_, cur, prev)| cur.2 == prev.2); | |
166 | for (id, cur, prev) in same { | |
8faf50e0 XL |
167 | println!(" {}", id); |
168 | // same features | |
169 | println!(" `{}` ({:?})\n `{}` ({:?})", cur.0, cur.1, prev.0, prev.1); | |
170 | } | |
171 | println!("the following dependencies have different features:"); | |
1b1a35ee | 172 | for (id, cur, prev) in different { |
0531ce1d | 173 | println!(" {}", id); |
8faf50e0 XL |
174 | let cur_features: HashSet<_> = cur.2.into_iter().collect(); |
175 | let prev_features: HashSet<_> = prev.2.into_iter().collect(); | |
dfeec247 XL |
176 | println!( |
177 | " `{}` additionally enabled features {:?} at {:?}", | |
178 | cur.0, | |
179 | &cur_features - &prev_features, | |
180 | cur.1 | |
181 | ); | |
182 | println!( | |
183 | " `{}` additionally enabled features {:?} at {:?}", | |
184 | prev.0, | |
185 | &prev_features - &cur_features, | |
186 | prev.1 | |
187 | ); | |
0531ce1d | 188 | } |
a1dfa0c6 | 189 | println!(); |
dfeec247 XL |
190 | println!( |
191 | "to fix this you will probably want to edit the local \ | |
b7449926 XL |
192 | src/tools/rustc-workspace-hack/Cargo.toml crate, as \ |
193 | that will update the dependency graph to ensure that \ | |
dfeec247 XL |
194 | these crates all share the same feature set" |
195 | ); | |
0531ce1d XL |
196 | panic!("tools should not compile multiple copies of the same crate"); |
197 | } | |
3b2f2976 | 198 | |
dfeec247 XL |
199 | builder.save_toolstate( |
200 | tool, | |
201 | if is_expected { ToolState::TestFail } else { ToolState::BuildFail }, | |
202 | ); | |
ff7c6d11 XL |
203 | |
204 | if !is_expected { | |
8faf50e0 | 205 | if !is_optional_tool { |
ff7c6d11 XL |
206 | exit(1); |
207 | } else { | |
a1dfa0c6 | 208 | None |
ff7c6d11 XL |
209 | } |
210 | } else { | |
6a06907d XL |
211 | // HACK(#82501): on Windows, the tools directory gets added to PATH when running tests, and |
212 | // compiletest confuses HTML tidy with the in-tree tidy. Name the in-tree tidy something | |
213 | // different so the problem doesn't come up. | |
214 | if tool == "tidy" { | |
215 | tool = "rust-tidy"; | |
216 | } | |
dfeec247 | 217 | let cargo_out = |
3dfed10e XL |
218 | builder.cargo_out(compiler, self.mode, target).join(exe(tool, compiler.host)); |
219 | let bin = builder.tools_dir(compiler).join(exe(tool, compiler.host)); | |
83c7162d | 220 | builder.copy(&cargo_out, &bin); |
abe05a73 | 221 | Some(bin) |
abe05a73 | 222 | } |
3b2f2976 XL |
223 | } |
224 | } | |
225 | ||
ea8adc8c | 226 | pub fn prepare_tool_cargo( |
9fa01778 | 227 | builder: &Builder<'_>, |
3b2f2976 | 228 | compiler: Compiler, |
94b46f34 | 229 | mode: Mode, |
3dfed10e | 230 | target: TargetSelection, |
ea8adc8c XL |
231 | command: &'static str, |
232 | path: &'static str, | |
8faf50e0 | 233 | source_type: SourceType, |
0bf4aa26 | 234 | extra_features: &[String], |
e1599b0c | 235 | ) -> CargoCommand { |
f035d41b | 236 | let mut cargo = builder.cargo(compiler, mode, source_type, target, command); |
83c7162d | 237 | let dir = builder.src.join(path); |
3b2f2976 XL |
238 | cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); |
239 | ||
74b04a01 | 240 | let mut features = extra_features.to_vec(); |
0bf4aa26 | 241 | if builder.build.config.cargo_native_static { |
dfeec247 XL |
242 | if path.ends_with("cargo") |
243 | || path.ends_with("rls") | |
244 | || path.ends_with("clippy") | |
245 | || path.ends_with("miri") | |
dfeec247 | 246 | || path.ends_with("rustfmt") |
0bf4aa26 XL |
247 | { |
248 | cargo.env("LIBZ_SYS_STATIC", "1"); | |
249 | features.push("rustc-workspace-hack/all-static".to_string()); | |
250 | } | |
3b2f2976 XL |
251 | } |
252 | ||
ea8adc8c XL |
253 | // if tools are using lzma we want to force the build script to build its |
254 | // own copy | |
255 | cargo.env("LZMA_API_STATIC", "1"); | |
256 | ||
f9f354fc XL |
257 | // CFG_RELEASE is needed by rustfmt (and possibly other tools) which |
258 | // import rustc-ap-rustc_attr which requires this to be set for the | |
259 | // `#[cfg(version(...))]` attribute. | |
260 | cargo.env("CFG_RELEASE", builder.rust_release()); | |
83c7162d XL |
261 | cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel); |
262 | cargo.env("CFG_VERSION", builder.rust_version()); | |
1b1a35ee | 263 | cargo.env("CFG_RELEASE_NUM", &builder.version); |
3b2f2976 | 264 | |
532ac7d7 | 265 | let info = GitInfo::new(builder.config.ignore_git, &dir); |
3b2f2976 XL |
266 | if let Some(sha) = info.sha() { |
267 | cargo.env("CFG_COMMIT_HASH", sha); | |
268 | } | |
269 | if let Some(sha_short) = info.sha_short() { | |
270 | cargo.env("CFG_SHORT_COMMIT_HASH", sha_short); | |
271 | } | |
272 | if let Some(date) = info.commit_date() { | |
273 | cargo.env("CFG_COMMIT_DATE", date); | |
274 | } | |
a1dfa0c6 | 275 | if !features.is_empty() { |
0bf4aa26 XL |
276 | cargo.arg("--features").arg(&features.join(", ")); |
277 | } | |
3b2f2976 XL |
278 | cargo |
279 | } | |
280 | ||
532ac7d7 | 281 | macro_rules! bootstrap_tool { |
a1dfa0c6 | 282 | ($( |
532ac7d7 | 283 | $name:ident, $path:expr, $tool_name:expr |
a1dfa0c6 | 284 | $(,is_external_tool = $external:expr)* |
dfeec247 | 285 | $(,is_unstable_tool = $unstable:expr)* |
416331ca | 286 | $(,features = $features:expr)* |
a1dfa0c6 XL |
287 | ; |
288 | )+) => { | |
8faf50e0 | 289 | #[derive(Copy, PartialEq, Eq, Clone)] |
3b2f2976 XL |
290 | pub enum Tool { |
291 | $( | |
292 | $name, | |
293 | )+ | |
294 | } | |
295 | ||
296 | impl<'a> Builder<'a> { | |
297 | pub fn tool_exe(&self, tool: Tool) -> PathBuf { | |
298 | match tool { | |
299 | $(Tool::$name => | |
300 | self.ensure($name { | |
532ac7d7 | 301 | compiler: self.compiler(0, self.config.build), |
83c7162d | 302 | target: self.config.build, |
3b2f2976 XL |
303 | }), |
304 | )+ | |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | $( | |
310 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
311 | pub struct $name { | |
312 | pub compiler: Compiler, | |
3dfed10e | 313 | pub target: TargetSelection, |
3b2f2976 XL |
314 | } |
315 | ||
316 | impl Step for $name { | |
317 | type Output = PathBuf; | |
318 | ||
9fa01778 | 319 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 XL |
320 | run.path($path) |
321 | } | |
322 | ||
9fa01778 | 323 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 324 | run.builder.ensure($name { |
532ac7d7 XL |
325 | // snapshot compiler |
326 | compiler: run.builder.compiler(0, run.builder.config.build), | |
3b2f2976 XL |
327 | target: run.target, |
328 | }); | |
329 | } | |
330 | ||
9fa01778 | 331 | fn run(self, builder: &Builder<'_>) -> PathBuf { |
3b2f2976 XL |
332 | builder.ensure(ToolBuild { |
333 | compiler: self.compiler, | |
334 | target: self.target, | |
335 | tool: $tool_name, | |
dfeec247 XL |
336 | mode: if false $(|| $unstable)* { |
337 | // use in-tree libraries for unstable features | |
338 | Mode::ToolStd | |
339 | } else { | |
340 | Mode::ToolBootstrap | |
341 | }, | |
ea8adc8c | 342 | path: $path, |
8faf50e0 XL |
343 | is_optional_tool: false, |
344 | source_type: if false $(|| $external)* { | |
345 | SourceType::Submodule | |
346 | } else { | |
347 | SourceType::InTree | |
348 | }, | |
416331ca XL |
349 | extra_features: { |
350 | // FIXME(#60643): avoid this lint by using `_` | |
351 | let mut _tmp = Vec::new(); | |
352 | $(_tmp.extend($features);)* | |
353 | _tmp | |
354 | }, | |
ff7c6d11 | 355 | }).expect("expected to build -- essential tool") |
3b2f2976 XL |
356 | } |
357 | } | |
358 | )+ | |
359 | } | |
360 | } | |
361 | ||
532ac7d7 | 362 | bootstrap_tool!( |
f035d41b | 363 | Rustbook, "src/tools/rustbook", "rustbook"; |
532ac7d7 XL |
364 | UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen"; |
365 | Tidy, "src/tools/tidy", "tidy"; | |
366 | Linkchecker, "src/tools/linkchecker", "linkchecker"; | |
367 | CargoTest, "src/tools/cargotest", "cargotest"; | |
dfeec247 | 368 | Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true; |
532ac7d7 XL |
369 | BuildManifest, "src/tools/build-manifest", "build-manifest"; |
370 | RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; | |
3dfed10e | 371 | RustDemangler, "src/tools/rust-demangler", "rust-demangler"; |
532ac7d7 XL |
372 | RustInstaller, "src/tools/rust-installer", "fabricate", is_external_tool = true; |
373 | RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; | |
ba9703b0 | 374 | ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; |
1b1a35ee | 375 | LintDocs, "src/tools/lint-docs", "lint-docs"; |
5869c6ff | 376 | JsonDocCk, "src/tools/jsondocck", "jsondocck"; |
3b2f2976 XL |
377 | ); |
378 | ||
f035d41b | 379 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] |
532ac7d7 XL |
380 | pub struct ErrorIndex { |
381 | pub compiler: Compiler, | |
382 | } | |
383 | ||
384 | impl ErrorIndex { | |
5869c6ff XL |
385 | pub fn command(builder: &Builder<'_>) -> Command { |
386 | // This uses stage-1 to match the behavior of building rustdoc. | |
387 | // Error-index-generator links with the rustdoc library, so we want to | |
388 | // use the same librustdoc to avoid building rustdoc twice (and to | |
389 | // avoid building the compiler an extra time). This uses | |
390 | // saturating_sub to deal with building with stage 0. (Using stage 0 | |
391 | // isn't recommended, since it will fail if any new error index tests | |
392 | // use new syntax, but it should work otherwise.) | |
393 | let compiler = builder.compiler(builder.top_stage.saturating_sub(1), builder.config.build); | |
dfeec247 | 394 | let mut cmd = Command::new(builder.ensure(ErrorIndex { compiler })); |
ba9703b0 | 395 | add_dylib_path( |
532ac7d7 XL |
396 | vec![PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host))], |
397 | &mut cmd, | |
398 | ); | |
399 | cmd | |
400 | } | |
401 | } | |
402 | ||
403 | impl Step for ErrorIndex { | |
404 | type Output = PathBuf; | |
405 | ||
406 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
407 | run.path("src/tools/error_index_generator") | |
408 | } | |
409 | ||
410 | fn make_run(run: RunConfig<'_>) { | |
411 | // Compile the error-index in the same stage as rustdoc to avoid | |
412 | // recompiling rustdoc twice if we can. | |
5869c6ff XL |
413 | // |
414 | // NOTE: This `make_run` isn't used in normal situations, only if you | |
415 | // manually build the tool with `x.py build | |
416 | // src/tools/error-index-generator` which almost nobody does. | |
417 | // Normally, `x.py test` or `x.py doc` will use the | |
418 | // `ErrorIndex::command` function instead. | |
419 | let compiler = | |
420 | run.builder.compiler(run.builder.top_stage.saturating_sub(1), run.builder.config.build); | |
f035d41b | 421 | run.builder.ensure(ErrorIndex { compiler }); |
532ac7d7 XL |
422 | } |
423 | ||
424 | fn run(self, builder: &Builder<'_>) -> PathBuf { | |
dfeec247 XL |
425 | builder |
426 | .ensure(ToolBuild { | |
427 | compiler: self.compiler, | |
428 | target: self.compiler.host, | |
429 | tool: "error_index_generator", | |
430 | mode: Mode::ToolRustc, | |
431 | path: "src/tools/error_index_generator", | |
432 | is_optional_tool: false, | |
433 | source_type: SourceType::InTree, | |
434 | extra_features: Vec::new(), | |
435 | }) | |
436 | .expect("expected to build -- essential tool") | |
532ac7d7 XL |
437 | } |
438 | } | |
439 | ||
3b2f2976 XL |
440 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] |
441 | pub struct RemoteTestServer { | |
442 | pub compiler: Compiler, | |
3dfed10e | 443 | pub target: TargetSelection, |
3b2f2976 XL |
444 | } |
445 | ||
446 | impl Step for RemoteTestServer { | |
447 | type Output = PathBuf; | |
448 | ||
9fa01778 | 449 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 XL |
450 | run.path("src/tools/remote-test-server") |
451 | } | |
452 | ||
9fa01778 | 453 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 454 | run.builder.ensure(RemoteTestServer { |
83c7162d | 455 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
3b2f2976 XL |
456 | target: run.target, |
457 | }); | |
458 | } | |
459 | ||
9fa01778 | 460 | fn run(self, builder: &Builder<'_>) -> PathBuf { |
dfeec247 XL |
461 | builder |
462 | .ensure(ToolBuild { | |
463 | compiler: self.compiler, | |
464 | target: self.target, | |
465 | tool: "remote-test-server", | |
466 | mode: Mode::ToolStd, | |
467 | path: "src/tools/remote-test-server", | |
468 | is_optional_tool: false, | |
469 | source_type: SourceType::InTree, | |
470 | extra_features: Vec::new(), | |
471 | }) | |
472 | .expect("expected to build -- essential tool") | |
3b2f2976 XL |
473 | } |
474 | } | |
475 | ||
f035d41b | 476 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] |
3b2f2976 | 477 | pub struct Rustdoc { |
532ac7d7 XL |
478 | /// This should only ever be 0 or 2. |
479 | /// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here. | |
480 | pub compiler: Compiler, | |
3b2f2976 XL |
481 | } |
482 | ||
483 | impl Step for Rustdoc { | |
484 | type Output = PathBuf; | |
485 | const DEFAULT: bool = true; | |
486 | const ONLY_HOSTS: bool = true; | |
487 | ||
9fa01778 | 488 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
f035d41b | 489 | run.path("src/tools/rustdoc").path("src/librustdoc") |
3b2f2976 XL |
490 | } |
491 | ||
9fa01778 | 492 | fn make_run(run: RunConfig<'_>) { |
1b1a35ee XL |
493 | run.builder.ensure(Rustdoc { |
494 | // Note: this is somewhat unique in that we actually want a *target* | |
495 | // compiler here, because rustdoc *is* a compiler. We won't be using | |
496 | // this as the compiler to build with, but rather this is "what | |
497 | // compiler are we producing"? | |
498 | compiler: run.builder.compiler(run.builder.top_stage, run.target), | |
499 | }); | |
3b2f2976 XL |
500 | } |
501 | ||
9fa01778 | 502 | fn run(self, builder: &Builder<'_>) -> PathBuf { |
532ac7d7 | 503 | let target_compiler = self.compiler; |
9fa01778 XL |
504 | if target_compiler.stage == 0 { |
505 | if !target_compiler.is_snapshot(builder) { | |
506 | panic!("rustdoc in stage 0 must be snapshot rustdoc"); | |
507 | } | |
3dfed10e | 508 | return builder.initial_rustc.with_file_name(exe("rustdoc", target_compiler.host)); |
9fa01778 | 509 | } |
3b2f2976 | 510 | let target = target_compiler.host; |
9fa01778 XL |
511 | // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise |
512 | // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage | |
513 | // compilers, which isn't what we want. Rustdoc should be linked in the same way as the | |
514 | // rustc compiler it's paired with, so it must be built with the previous stage compiler. | |
515 | let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build); | |
516 | ||
517 | // The presence of `target_compiler` ensures that the necessary libraries (codegen backends, | |
518 | // compiler libraries, ...) are built. Rustdoc does not require the presence of any | |
519 | // libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since | |
520 | // they'll be linked to those libraries). As such, don't explicitly `ensure` any additional | |
521 | // libraries here. The intuition here is that If we've built a compiler, we should be able | |
522 | // to build rustdoc. | |
3b2f2976 | 523 | |
e1599b0c | 524 | let cargo = prepare_tool_cargo( |
8faf50e0 XL |
525 | builder, |
526 | build_compiler, | |
527 | Mode::ToolRustc, | |
528 | target, | |
529 | "build", | |
530 | "src/tools/rustdoc", | |
531 | SourceType::InTree, | |
0bf4aa26 | 532 | &[], |
8faf50e0 | 533 | ); |
ea8adc8c | 534 | |
dfeec247 XL |
535 | builder.info(&format!( |
536 | "Building rustdoc for stage{} ({})", | |
537 | target_compiler.stage, target_compiler.host | |
538 | )); | |
e1599b0c | 539 | builder.run(&mut cargo.into()); |
0531ce1d | 540 | |
3b2f2976 XL |
541 | // Cargo adds a number of paths to the dylib search path on windows, which results in |
542 | // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" | |
543 | // rustdoc a different name. | |
dfeec247 XL |
544 | let tool_rustdoc = builder |
545 | .cargo_out(build_compiler, Mode::ToolRustc, target) | |
3dfed10e | 546 | .join(exe("rustdoc_tool_binary", target_compiler.host)); |
3b2f2976 XL |
547 | |
548 | // don't create a stage0-sysroot/bin directory. | |
549 | if target_compiler.stage > 0 { | |
550 | let sysroot = builder.sysroot(target_compiler); | |
551 | let bindir = sysroot.join("bin"); | |
552 | t!(fs::create_dir_all(&bindir)); | |
3dfed10e | 553 | let bin_rustdoc = bindir.join(exe("rustdoc", target_compiler.host)); |
3b2f2976 | 554 | let _ = fs::remove_file(&bin_rustdoc); |
83c7162d | 555 | builder.copy(&tool_rustdoc, &bin_rustdoc); |
3b2f2976 XL |
556 | bin_rustdoc |
557 | } else { | |
558 | tool_rustdoc | |
559 | } | |
560 | } | |
561 | } | |
562 | ||
563 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
564 | pub struct Cargo { | |
565 | pub compiler: Compiler, | |
3dfed10e | 566 | pub target: TargetSelection, |
3b2f2976 XL |
567 | } |
568 | ||
569 | impl Step for Cargo { | |
570 | type Output = PathBuf; | |
571 | const DEFAULT: bool = true; | |
572 | const ONLY_HOSTS: bool = true; | |
573 | ||
9fa01778 | 574 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 | 575 | let builder = run.builder; |
83c7162d | 576 | run.path("src/tools/cargo").default_condition(builder.config.extended) |
3b2f2976 XL |
577 | } |
578 | ||
9fa01778 | 579 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 580 | run.builder.ensure(Cargo { |
83c7162d | 581 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
3b2f2976 XL |
582 | target: run.target, |
583 | }); | |
584 | } | |
585 | ||
9fa01778 | 586 | fn run(self, builder: &Builder<'_>) -> PathBuf { |
fc512014 | 587 | let cargo_bin_path = builder |
dfeec247 XL |
588 | .ensure(ToolBuild { |
589 | compiler: self.compiler, | |
590 | target: self.target, | |
591 | tool: "cargo", | |
592 | mode: Mode::ToolRustc, | |
593 | path: "src/tools/cargo", | |
594 | is_optional_tool: false, | |
595 | source_type: SourceType::Submodule, | |
596 | extra_features: Vec::new(), | |
597 | }) | |
fc512014 XL |
598 | .expect("expected to build -- essential tool"); |
599 | ||
600 | let build_cred = |name, path| { | |
601 | // These credential helpers are currently experimental. | |
602 | // Any build failures will be ignored. | |
603 | let _ = builder.ensure(ToolBuild { | |
604 | compiler: self.compiler, | |
605 | target: self.target, | |
606 | tool: name, | |
607 | mode: Mode::ToolRustc, | |
608 | path, | |
609 | is_optional_tool: true, | |
610 | source_type: SourceType::Submodule, | |
611 | extra_features: Vec::new(), | |
612 | }); | |
613 | }; | |
614 | ||
615 | if self.target.contains("windows") { | |
616 | build_cred( | |
617 | "cargo-credential-wincred", | |
618 | "src/tools/cargo/crates/credential/cargo-credential-wincred", | |
619 | ); | |
620 | } | |
621 | if self.target.contains("apple-darwin") { | |
622 | build_cred( | |
623 | "cargo-credential-macos-keychain", | |
624 | "src/tools/cargo/crates/credential/cargo-credential-macos-keychain", | |
625 | ); | |
626 | } | |
627 | build_cred( | |
628 | "cargo-credential-1password", | |
629 | "src/tools/cargo/crates/credential/cargo-credential-1password", | |
630 | ); | |
631 | cargo_bin_path | |
ea8adc8c XL |
632 | } |
633 | } | |
634 | ||
ff7c6d11 XL |
635 | macro_rules! tool_extended { |
636 | (($sel:ident, $builder:ident), | |
637 | $($name:ident, | |
638 | $toolstate:ident, | |
639 | $path:expr, | |
640 | $tool_name:expr, | |
f035d41b XL |
641 | stable = $stable:expr, |
642 | $(in_tree = $in_tree:expr,)* | |
ff7c6d11 XL |
643 | $extra_deps:block;)+) => { |
644 | $( | |
0531ce1d | 645 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
ff7c6d11 XL |
646 | pub struct $name { |
647 | pub compiler: Compiler, | |
3dfed10e | 648 | pub target: TargetSelection, |
0531ce1d | 649 | pub extra_features: Vec<String>, |
ff7c6d11 | 650 | } |
ea8adc8c | 651 | |
ff7c6d11 XL |
652 | impl Step for $name { |
653 | type Output = Option<PathBuf>; | |
f035d41b | 654 | const DEFAULT: bool = true; // Overwritten below |
ff7c6d11 | 655 | const ONLY_HOSTS: bool = true; |
ea8adc8c | 656 | |
9fa01778 | 657 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
ff7c6d11 | 658 | let builder = run.builder; |
ba9703b0 XL |
659 | run.path($path).default_condition( |
660 | builder.config.extended | |
f035d41b XL |
661 | && builder.config.tools.as_ref().map_or( |
662 | // By default, on nightly/dev enable all tools, else only | |
663 | // build stable tools. | |
664 | $stable || builder.build.unstable_features(), | |
665 | // If `tools` is set, search list for this tool. | |
666 | |tools| { | |
667 | tools.iter().any(|tool| match tool.as_ref() { | |
668 | "clippy" => $tool_name == "clippy-driver", | |
669 | x => $tool_name == x, | |
ba9703b0 XL |
670 | }) |
671 | }), | |
672 | ) | |
ff7c6d11 | 673 | } |
ea8adc8c | 674 | |
9fa01778 | 675 | fn make_run(run: RunConfig<'_>) { |
ff7c6d11 | 676 | run.builder.ensure($name { |
83c7162d | 677 | compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), |
ff7c6d11 | 678 | target: run.target, |
0531ce1d | 679 | extra_features: Vec::new(), |
ff7c6d11 XL |
680 | }); |
681 | } | |
682 | ||
0531ce1d | 683 | #[allow(unused_mut)] |
9fa01778 | 684 | fn run(mut $sel, $builder: &Builder<'_>) -> Option<PathBuf> { |
ff7c6d11 XL |
685 | $extra_deps |
686 | $builder.ensure(ToolBuild { | |
687 | compiler: $sel.compiler, | |
688 | target: $sel.target, | |
689 | tool: $tool_name, | |
94b46f34 | 690 | mode: Mode::ToolRustc, |
ff7c6d11 | 691 | path: $path, |
0531ce1d | 692 | extra_features: $sel.extra_features, |
8faf50e0 | 693 | is_optional_tool: true, |
f035d41b XL |
694 | source_type: if false $(|| $in_tree)* { |
695 | SourceType::InTree | |
696 | } else { | |
697 | SourceType::Submodule | |
698 | }, | |
ff7c6d11 XL |
699 | }) |
700 | } | |
701 | } | |
702 | )+ | |
ea8adc8c | 703 | } |
ff7c6d11 | 704 | } |
ea8adc8c | 705 | |
f035d41b | 706 | // Note: tools need to be also added to `Builder::get_step_descriptions` in `builder.rs` |
f9f354fc | 707 | // to make `./x.py build <tool>` work. |
ff7c6d11 | 708 | tool_extended!((self, builder), |
f035d41b XL |
709 | Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", stable=true, {}; |
710 | CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", stable=true, in_tree=true, {}; | |
711 | Clippy, clippy, "src/tools/clippy", "clippy-driver", stable=true, in_tree=true, {}; | |
712 | Miri, miri, "src/tools/miri", "miri", stable=false, {}; | |
713 | CargoMiri, miri, "src/tools/miri/cargo-miri", "cargo-miri", stable=false, {}; | |
714 | Rls, rls, "src/tools/rls", "rls", stable=true, { | |
f9f354fc | 715 | builder.ensure(Clippy { |
8faf50e0 XL |
716 | compiler: self.compiler, |
717 | target: self.target, | |
718 | extra_features: Vec::new(), | |
719 | }); | |
f9f354fc | 720 | self.extra_features.push("clippy".to_owned()); |
ff7c6d11 | 721 | }; |
f035d41b XL |
722 | Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, {}; |
723 | RustAnalyzer, rust_analyzer, "src/tools/rust-analyzer/crates/rust-analyzer", "rust-analyzer", stable=false, {}; | |
ff7c6d11 | 724 | ); |
3b2f2976 XL |
725 | |
726 | impl<'a> Builder<'a> { | |
9fa01778 | 727 | /// Gets a `Command` which is ready to run `tool` in `stage` built for |
3b2f2976 XL |
728 | /// `host`. |
729 | pub fn tool_cmd(&self, tool: Tool) -> Command { | |
730 | let mut cmd = Command::new(self.tool_exe(tool)); | |
532ac7d7 | 731 | let compiler = self.compiler(0, self.config.build); |
3b2f2976 | 732 | let host = &compiler.host; |
dc9dc135 XL |
733 | // Prepares the `cmd` provided to be able to run the `compiler` provided. |
734 | // | |
735 | // Notably this munges the dynamic library lookup path to point to the | |
736 | // right location to run `compiler`. | |
83c7162d | 737 | let mut lib_paths: Vec<PathBuf> = vec![ |
dc9dc135 XL |
738 | self.build.rustc_snapshot_libdir(), |
739 | self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("deps"), | |
3b2f2976 XL |
740 | ]; |
741 | ||
0731742a | 742 | // On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make |
3b2f2976 XL |
743 | // mode) and that C compiler may need some extra PATH modification. Do |
744 | // so here. | |
745 | if compiler.host.contains("msvc") { | |
746 | let curpaths = env::var_os("PATH").unwrap_or_default(); | |
747 | let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>(); | |
abe05a73 | 748 | for &(ref k, ref v) in self.cc[&compiler.host].env() { |
3b2f2976 | 749 | if k != "PATH" { |
dfeec247 | 750 | continue; |
3b2f2976 XL |
751 | } |
752 | for path in env::split_paths(v) { | |
753 | if !curpaths.contains(&path) { | |
83c7162d | 754 | lib_paths.push(path); |
3b2f2976 XL |
755 | } |
756 | } | |
757 | } | |
758 | } | |
83c7162d | 759 | |
ba9703b0 | 760 | add_dylib_path(lib_paths, &mut cmd); |
1b1a35ee XL |
761 | |
762 | // Provide a RUSTC for this command to use. | |
763 | cmd.env("RUSTC", &self.initial_rustc); | |
764 | ||
dc9dc135 | 765 | cmd |
83c7162d | 766 | } |
3b2f2976 | 767 | } |