• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::env;
2 use std::fs;
3 use std::path::PathBuf;
4 use std::process::Command;
5 
6 use crate::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
7 use crate::channel::GitInfo;
8 use crate::compile;
9 use crate::config::TargetSelection;
10 use crate::toolstate::ToolState;
11 use crate::util::{add_dylib_path, exe, t};
12 use crate::Compiler;
13 use crate::Mode;
14 use crate::{gha, Kind};
15 
16 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
17 pub enum SourceType {
18     InTree,
19     Submodule,
20 }
21 
22 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
23 struct ToolBuild {
24     compiler: Compiler,
25     target: TargetSelection,
26     tool: &'static str,
27     path: &'static str,
28     mode: Mode,
29     is_optional_tool: bool,
30     source_type: SourceType,
31     extra_features: Vec<String>,
32     /// Nightly-only features that are allowed (comma-separated list).
33     allow_features: &'static str,
34 }
35 
36 impl Builder<'_> {
msg_tool( &self, mode: Mode, tool: &str, build_stage: u32, host: &TargetSelection, target: &TargetSelection, ) -> Option<gha::Group>37     fn msg_tool(
38         &self,
39         mode: Mode,
40         tool: &str,
41         build_stage: u32,
42         host: &TargetSelection,
43         target: &TargetSelection,
44     ) -> Option<gha::Group> {
45         match mode {
46             // depends on compiler stage, different to host compiler
47             Mode::ToolRustc => self.msg_sysroot_tool(
48                 Kind::Build,
49                 build_stage,
50                 format_args!("tool {tool}"),
51                 *host,
52                 *target,
53             ),
54             // doesn't depend on compiler, same as host compiler
55             _ => self.msg(Kind::Build, build_stage, format_args!("tool {tool}"), *host, *target),
56         }
57     }
58 }
59 
60 impl Step for ToolBuild {
61     type Output = Option<PathBuf>;
62 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>63     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
64         run.never()
65     }
66 
67     /// Builds a tool in `src/tools`
68     ///
69     /// This will build the specified tool with the specified `host` compiler in
70     /// `stage` into the normal cargo output directory.
run(self, builder: &Builder<'_>) -> Option<PathBuf>71     fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
72         let compiler = self.compiler;
73         let target = self.target;
74         let mut tool = self.tool;
75         let path = self.path;
76         let is_optional_tool = self.is_optional_tool;
77 
78         match self.mode {
79             Mode::ToolRustc => {
80                 builder.ensure(compile::Std::new(compiler, compiler.host));
81                 builder.ensure(compile::Rustc::new(compiler, target));
82             }
83             Mode::ToolStd => builder.ensure(compile::Std::new(compiler, target)),
84             Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs
85             _ => panic!("unexpected Mode for tool build"),
86         }
87 
88         let mut cargo = prepare_tool_cargo(
89             builder,
90             compiler,
91             self.mode,
92             target,
93             "build",
94             path,
95             self.source_type,
96             &self.extra_features,
97         );
98         if !self.allow_features.is_empty() {
99             cargo.allow_features(self.allow_features);
100         }
101         let _guard = builder.msg_tool(
102             self.mode,
103             self.tool,
104             self.compiler.stage,
105             &self.compiler.host,
106             &self.target,
107         );
108 
109         let mut cargo = Command::from(cargo);
110         let is_expected = builder.try_run(&mut cargo).is_ok();
111 
112         builder.save_toolstate(
113             tool,
114             if is_expected { ToolState::TestFail } else { ToolState::BuildFail },
115         );
116 
117         if !is_expected {
118             if !is_optional_tool {
119                 crate::detail_exit_macro!(1);
120             } else {
121                 None
122             }
123         } else {
124             // HACK(#82501): on Windows, the tools directory gets added to PATH when running tests, and
125             // compiletest confuses HTML tidy with the in-tree tidy. Name the in-tree tidy something
126             // different so the problem doesn't come up.
127             if tool == "tidy" {
128                 tool = "rust-tidy";
129             }
130             let cargo_out = builder.cargo_out(compiler, self.mode, target).join(exe(tool, target));
131             let bin = builder.tools_dir(compiler).join(exe(tool, target));
132             builder.copy(&cargo_out, &bin);
133             Some(bin)
134         }
135     }
136 }
137 
prepare_tool_cargo( builder: &Builder<'_>, compiler: Compiler, mode: Mode, target: TargetSelection, command: &'static str, path: &str, source_type: SourceType, extra_features: &[String], ) -> CargoCommand138 pub fn prepare_tool_cargo(
139     builder: &Builder<'_>,
140     compiler: Compiler,
141     mode: Mode,
142     target: TargetSelection,
143     command: &'static str,
144     path: &str,
145     source_type: SourceType,
146     extra_features: &[String],
147 ) -> CargoCommand {
148     let mut cargo = builder.cargo(compiler, mode, source_type, target, command);
149     let dir = builder.src.join(path);
150     cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
151 
152     let mut features = extra_features.to_vec();
153     if builder.build.config.cargo_native_static {
154         if path.ends_with("cargo")
155             || path.ends_with("rls")
156             || path.ends_with("clippy")
157             || path.ends_with("miri")
158             || path.ends_with("rustfmt")
159         {
160             cargo.env("LIBZ_SYS_STATIC", "1");
161         }
162         if path.ends_with("cargo") {
163             features.push("all-static".to_string());
164         }
165     }
166 
167     // clippy tests need to know about the stage sysroot. Set them consistently while building to
168     // avoid rebuilding when running tests.
169     cargo.env("SYSROOT", builder.sysroot(compiler));
170 
171     // if tools are using lzma we want to force the build script to build its
172     // own copy
173     cargo.env("LZMA_API_STATIC", "1");
174 
175     // CFG_RELEASE is needed by rustfmt (and possibly other tools) which
176     // import rustc-ap-rustc_attr which requires this to be set for the
177     // `#[cfg(version(...))]` attribute.
178     cargo.env("CFG_RELEASE", builder.rust_release());
179     cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel);
180     cargo.env("CFG_VERSION", builder.rust_version());
181     cargo.env("CFG_RELEASE_NUM", &builder.version);
182     cargo.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
183     if let Some(ref ver_date) = builder.rust_info().commit_date() {
184         cargo.env("CFG_VER_DATE", ver_date);
185     }
186     if let Some(ref ver_hash) = builder.rust_info().sha() {
187         cargo.env("CFG_VER_HASH", ver_hash);
188     }
189 
190     let info = GitInfo::new(builder.config.omit_git_hash, &dir);
191     if let Some(sha) = info.sha() {
192         cargo.env("CFG_COMMIT_HASH", sha);
193     }
194     if let Some(sha_short) = info.sha_short() {
195         cargo.env("CFG_SHORT_COMMIT_HASH", sha_short);
196     }
197     if let Some(date) = info.commit_date() {
198         cargo.env("CFG_COMMIT_DATE", date);
199     }
200     if !features.is_empty() {
201         cargo.arg("--features").arg(&features.join(", "));
202     }
203     cargo
204 }
205 
206 macro_rules! bootstrap_tool {
207     ($(
208         $name:ident, $path:expr, $tool_name:expr
209         $(,is_external_tool = $external:expr)*
210         $(,is_unstable_tool = $unstable:expr)*
211         $(,allow_features = $allow_features:expr)?
212         ;
213     )+) => {
214         #[derive(Copy, PartialEq, Eq, Clone)]
215         pub enum Tool {
216             $(
217                 $name,
218             )+
219         }
220 
221         impl<'a> Builder<'a> {
222             pub fn tool_exe(&self, tool: Tool) -> PathBuf {
223                 match tool {
224                     $(Tool::$name =>
225                         self.ensure($name {
226                             compiler: self.compiler(0, self.config.build),
227                             target: self.config.build,
228                         }),
229                     )+
230                 }
231             }
232         }
233 
234         $(
235             #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
236         pub struct $name {
237             pub compiler: Compiler,
238             pub target: TargetSelection,
239         }
240 
241         impl Step for $name {
242             type Output = PathBuf;
243 
244             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
245                 run.path($path)
246             }
247 
248             fn make_run(run: RunConfig<'_>) {
249                 run.builder.ensure($name {
250                     // snapshot compiler
251                     compiler: run.builder.compiler(0, run.builder.config.build),
252                     target: run.target,
253                 });
254             }
255 
256             fn run(self, builder: &Builder<'_>) -> PathBuf {
257                 builder.ensure(ToolBuild {
258                     compiler: self.compiler,
259                     target: self.target,
260                     tool: $tool_name,
261                     mode: if false $(|| $unstable)* {
262                         // use in-tree libraries for unstable features
263                         Mode::ToolStd
264                     } else {
265                         Mode::ToolBootstrap
266                     },
267                     path: $path,
268                     is_optional_tool: false,
269                     source_type: if false $(|| $external)* {
270                         SourceType::Submodule
271                     } else {
272                         SourceType::InTree
273                     },
274                     extra_features: vec![],
275                     allow_features: concat!($($allow_features)*),
276                 }).expect("expected to build -- essential tool")
277             }
278         }
279         )+
280     }
281 }
282 
283 bootstrap_tool!(
284     Rustbook, "src/tools/rustbook", "rustbook";
285     UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen";
286     Tidy, "src/tools/tidy", "tidy";
287     Linkchecker, "src/tools/linkchecker", "linkchecker";
288     CargoTest, "src/tools/cargotest", "cargotest";
289     Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = "test";
290     BuildManifest, "src/tools/build-manifest", "build-manifest";
291     RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
292     RustInstaller, "src/tools/rust-installer", "rust-installer";
293     RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes";
294     ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors";
295     LintDocs, "src/tools/lint-docs", "lint-docs";
296     JsonDocCk, "src/tools/jsondocck", "jsondocck";
297     JsonDocLint, "src/tools/jsondoclint", "jsondoclint";
298     HtmlChecker, "src/tools/html-checker", "html-checker";
299     BumpStage0, "src/tools/bump-stage0", "bump-stage0";
300     ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
301     CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
302     GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
303     SuggestTests, "src/tools/suggest-tests", "suggest-tests";
304     GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
305     RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test";
306 );
307 
308 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
309 pub struct ErrorIndex {
310     pub compiler: Compiler,
311 }
312 
313 impl ErrorIndex {
command(builder: &Builder<'_>) -> Command314     pub fn command(builder: &Builder<'_>) -> Command {
315         // Error-index-generator links with the rustdoc library, so we need to add `rustc_lib_paths`
316         // for rustc_private and libLLVM.so, and `sysroot_lib` for libstd, etc.
317         let host = builder.config.build;
318         let compiler = builder.compiler_for(builder.top_stage, host, host);
319         let mut cmd = Command::new(builder.ensure(ErrorIndex { compiler }));
320         let mut dylib_paths = builder.rustc_lib_paths(compiler);
321         dylib_paths.push(PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host)));
322         add_dylib_path(dylib_paths, &mut cmd);
323         cmd
324     }
325 }
326 
327 impl Step for ErrorIndex {
328     type Output = PathBuf;
329 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>330     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
331         run.path("src/tools/error_index_generator")
332     }
333 
make_run(run: RunConfig<'_>)334     fn make_run(run: RunConfig<'_>) {
335         // Compile the error-index in the same stage as rustdoc to avoid
336         // recompiling rustdoc twice if we can.
337         //
338         // NOTE: This `make_run` isn't used in normal situations, only if you
339         // manually build the tool with `x.py build
340         // src/tools/error-index-generator` which almost nobody does.
341         // Normally, `x.py test` or `x.py doc` will use the
342         // `ErrorIndex::command` function instead.
343         let compiler =
344             run.builder.compiler(run.builder.top_stage.saturating_sub(1), run.builder.config.build);
345         run.builder.ensure(ErrorIndex { compiler });
346     }
347 
run(self, builder: &Builder<'_>) -> PathBuf348     fn run(self, builder: &Builder<'_>) -> PathBuf {
349         builder
350             .ensure(ToolBuild {
351                 compiler: self.compiler,
352                 target: self.compiler.host,
353                 tool: "error_index_generator",
354                 mode: Mode::ToolRustc,
355                 path: "src/tools/error_index_generator",
356                 is_optional_tool: false,
357                 source_type: SourceType::InTree,
358                 extra_features: Vec::new(),
359                 allow_features: "",
360             })
361             .expect("expected to build -- essential tool")
362     }
363 }
364 
365 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
366 pub struct RemoteTestServer {
367     pub compiler: Compiler,
368     pub target: TargetSelection,
369 }
370 
371 impl Step for RemoteTestServer {
372     type Output = PathBuf;
373 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>374     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
375         run.path("src/tools/remote-test-server")
376     }
377 
make_run(run: RunConfig<'_>)378     fn make_run(run: RunConfig<'_>) {
379         run.builder.ensure(RemoteTestServer {
380             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
381             target: run.target,
382         });
383     }
384 
run(self, builder: &Builder<'_>) -> PathBuf385     fn run(self, builder: &Builder<'_>) -> PathBuf {
386         builder
387             .ensure(ToolBuild {
388                 compiler: self.compiler,
389                 target: self.target,
390                 tool: "remote-test-server",
391                 mode: Mode::ToolStd,
392                 path: "src/tools/remote-test-server",
393                 is_optional_tool: false,
394                 source_type: SourceType::InTree,
395                 extra_features: Vec::new(),
396                 allow_features: "",
397             })
398             .expect("expected to build -- essential tool")
399     }
400 }
401 
402 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
403 pub struct Rustdoc {
404     /// This should only ever be 0 or 2.
405     /// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here.
406     pub compiler: Compiler,
407 }
408 
409 impl Step for Rustdoc {
410     type Output = PathBuf;
411     const DEFAULT: bool = true;
412     const ONLY_HOSTS: bool = true;
413 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>414     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
415         run.path("src/tools/rustdoc").path("src/librustdoc")
416     }
417 
make_run(run: RunConfig<'_>)418     fn make_run(run: RunConfig<'_>) {
419         run.builder.ensure(Rustdoc {
420             // Note: this is somewhat unique in that we actually want a *target*
421             // compiler here, because rustdoc *is* a compiler. We won't be using
422             // this as the compiler to build with, but rather this is "what
423             // compiler are we producing"?
424             compiler: run.builder.compiler(run.builder.top_stage, run.target),
425         });
426     }
427 
run(self, builder: &Builder<'_>) -> PathBuf428     fn run(self, builder: &Builder<'_>) -> PathBuf {
429         let target_compiler = self.compiler;
430         if target_compiler.stage == 0 {
431             if !target_compiler.is_snapshot(builder) {
432                 panic!("rustdoc in stage 0 must be snapshot rustdoc");
433             }
434             return builder.initial_rustc.with_file_name(exe("rustdoc", target_compiler.host));
435         }
436         let target = target_compiler.host;
437         // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
438         // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
439         // compilers, which isn't what we want. Rustdoc should be linked in the same way as the
440         // rustc compiler it's paired with, so it must be built with the previous stage compiler.
441         let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build);
442 
443         // When using `download-rustc` and a stage0 build_compiler, copying rustc doesn't actually
444         // build stage0 libstd (because the libstd in sysroot has the wrong ABI). Explicitly build
445         // it.
446         builder.ensure(compile::Std::new(build_compiler, target_compiler.host));
447         builder.ensure(compile::Rustc::new(build_compiler, target_compiler.host));
448         // NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0
449         // compiler, since you do just as much work.
450         if !builder.config.dry_run() && builder.download_rustc() && build_compiler.stage == 0 {
451             println!(
452                 "warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead"
453             );
454         }
455 
456         // The presence of `target_compiler` ensures that the necessary libraries (codegen backends,
457         // compiler libraries, ...) are built. Rustdoc does not require the presence of any
458         // libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since
459         // they'll be linked to those libraries). As such, don't explicitly `ensure` any additional
460         // libraries here. The intuition here is that If we've built a compiler, we should be able
461         // to build rustdoc.
462         //
463         let mut features = Vec::new();
464         if builder.config.jemalloc {
465             features.push("jemalloc".to_string());
466         }
467 
468         let mut cargo = prepare_tool_cargo(
469             builder,
470             build_compiler,
471             Mode::ToolRustc,
472             target,
473             "build",
474             "src/tools/rustdoc",
475             SourceType::InTree,
476             features.as_slice(),
477         );
478 
479         if builder.config.rustc_parallel {
480             cargo.rustflag("--cfg=parallel_compiler");
481         }
482 
483         let _guard = builder.msg_tool(
484             Mode::ToolRustc,
485             "rustdoc",
486             build_compiler.stage,
487             &self.compiler.host,
488             &target,
489         );
490         builder.run(&mut cargo.into());
491 
492         // Cargo adds a number of paths to the dylib search path on windows, which results in
493         // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
494         // rustdoc a different name.
495         let tool_rustdoc = builder
496             .cargo_out(build_compiler, Mode::ToolRustc, target)
497             .join(exe("rustdoc_tool_binary", target_compiler.host));
498 
499         // don't create a stage0-sysroot/bin directory.
500         if target_compiler.stage > 0 {
501             let sysroot = builder.sysroot(target_compiler);
502             let bindir = sysroot.join("bin");
503             t!(fs::create_dir_all(&bindir));
504             let bin_rustdoc = bindir.join(exe("rustdoc", target_compiler.host));
505             let _ = fs::remove_file(&bin_rustdoc);
506             builder.copy(&tool_rustdoc, &bin_rustdoc);
507             bin_rustdoc
508         } else {
509             tool_rustdoc
510         }
511     }
512 }
513 
514 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
515 pub struct Cargo {
516     pub compiler: Compiler,
517     pub target: TargetSelection,
518 }
519 
520 impl Step for Cargo {
521     type Output = PathBuf;
522     const DEFAULT: bool = true;
523     const ONLY_HOSTS: bool = true;
524 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>525     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
526         let builder = run.builder;
527         run.path("src/tools/cargo").default_condition(
528             builder.config.extended
529                 && builder.config.tools.as_ref().map_or(
530                     true,
531                     // If `tools` is set, search list for this tool.
532                     |tools| tools.iter().any(|tool| tool == "cargo"),
533                 ),
534         )
535     }
536 
make_run(run: RunConfig<'_>)537     fn make_run(run: RunConfig<'_>) {
538         run.builder.ensure(Cargo {
539             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
540             target: run.target,
541         });
542     }
543 
run(self, builder: &Builder<'_>) -> PathBuf544     fn run(self, builder: &Builder<'_>) -> PathBuf {
545         let cargo_bin_path = builder
546             .ensure(ToolBuild {
547                 compiler: self.compiler,
548                 target: self.target,
549                 tool: "cargo",
550                 mode: Mode::ToolRustc,
551                 path: "src/tools/cargo",
552                 is_optional_tool: false,
553                 source_type: SourceType::Submodule,
554                 extra_features: Vec::new(),
555                 allow_features: "",
556             })
557             .expect("expected to build -- essential tool");
558 
559         let build_cred = |name, path| {
560             // These credential helpers are currently experimental.
561             // Any build failures will be ignored.
562             let _ = builder.ensure(ToolBuild {
563                 compiler: self.compiler,
564                 target: self.target,
565                 tool: name,
566                 mode: Mode::ToolRustc,
567                 path,
568                 is_optional_tool: true,
569                 source_type: SourceType::Submodule,
570                 extra_features: Vec::new(),
571                 allow_features: "",
572             });
573         };
574 
575         if self.target.contains("windows") {
576             build_cred(
577                 "cargo-credential-wincred",
578                 "src/tools/cargo/credential/cargo-credential-wincred",
579             );
580         }
581         if self.target.contains("apple-darwin") {
582             build_cred(
583                 "cargo-credential-macos-keychain",
584                 "src/tools/cargo/credential/cargo-credential-macos-keychain",
585             );
586         }
587         build_cred(
588             "cargo-credential-1password",
589             "src/tools/cargo/credential/cargo-credential-1password",
590         );
591         cargo_bin_path
592     }
593 }
594 
595 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
596 pub struct LldWrapper {
597     pub compiler: Compiler,
598     pub target: TargetSelection,
599 }
600 
601 impl Step for LldWrapper {
602     type Output = PathBuf;
603 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>604     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
605         run.never()
606     }
607 
run(self, builder: &Builder<'_>) -> PathBuf608     fn run(self, builder: &Builder<'_>) -> PathBuf {
609         let src_exe = builder
610             .ensure(ToolBuild {
611                 compiler: self.compiler,
612                 target: self.target,
613                 tool: "lld-wrapper",
614                 mode: Mode::ToolStd,
615                 path: "src/tools/lld-wrapper",
616                 is_optional_tool: false,
617                 source_type: SourceType::InTree,
618                 extra_features: Vec::new(),
619                 allow_features: "",
620             })
621             .expect("expected to build -- essential tool");
622 
623         src_exe
624     }
625 }
626 
627 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
628 pub struct RustAnalyzer {
629     pub compiler: Compiler,
630     pub target: TargetSelection,
631 }
632 
633 impl RustAnalyzer {
634     pub const ALLOW_FEATURES: &str =
635         "proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink";
636 }
637 
638 impl Step for RustAnalyzer {
639     type Output = Option<PathBuf>;
640     const DEFAULT: bool = true;
641     const ONLY_HOSTS: bool = true;
642 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>643     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
644         let builder = run.builder;
645         run.path("src/tools/rust-analyzer").default_condition(
646             builder.config.extended
647                 && builder
648                     .config
649                     .tools
650                     .as_ref()
651                     .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")),
652         )
653     }
654 
make_run(run: RunConfig<'_>)655     fn make_run(run: RunConfig<'_>) {
656         run.builder.ensure(RustAnalyzer {
657             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
658             target: run.target,
659         });
660     }
661 
run(self, builder: &Builder<'_>) -> Option<PathBuf>662     fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
663         builder.ensure(ToolBuild {
664             compiler: self.compiler,
665             target: self.target,
666             tool: "rust-analyzer",
667             mode: Mode::ToolStd,
668             path: "src/tools/rust-analyzer",
669             extra_features: vec!["rust-analyzer/in-rust-tree".to_owned()],
670             is_optional_tool: false,
671             source_type: SourceType::InTree,
672             allow_features: RustAnalyzer::ALLOW_FEATURES,
673         })
674     }
675 }
676 
677 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
678 pub struct RustAnalyzerProcMacroSrv {
679     pub compiler: Compiler,
680     pub target: TargetSelection,
681 }
682 
683 impl Step for RustAnalyzerProcMacroSrv {
684     type Output = Option<PathBuf>;
685     const DEFAULT: bool = true;
686     const ONLY_HOSTS: bool = true;
687 
should_run(run: ShouldRun<'_>) -> ShouldRun<'_>688     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
689         let builder = run.builder;
690         // Allow building `rust-analyzer-proc-macro-srv` both as part of the `rust-analyzer` and as a stand-alone tool.
691         run.path("src/tools/rust-analyzer")
692             .path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
693             .default_condition(builder.config.tools.as_ref().map_or(true, |tools| {
694                 tools
695                     .iter()
696                     .any(|tool| tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv")
697             }))
698     }
699 
make_run(run: RunConfig<'_>)700     fn make_run(run: RunConfig<'_>) {
701         run.builder.ensure(RustAnalyzerProcMacroSrv {
702             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
703             target: run.target,
704         });
705     }
706 
run(self, builder: &Builder<'_>) -> Option<PathBuf>707     fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
708         let path = builder.ensure(ToolBuild {
709             compiler: self.compiler,
710             target: self.target,
711             tool: "rust-analyzer-proc-macro-srv",
712             mode: Mode::ToolStd,
713             path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli",
714             extra_features: vec!["sysroot-abi".to_owned()],
715             is_optional_tool: false,
716             source_type: SourceType::InTree,
717             allow_features: RustAnalyzer::ALLOW_FEATURES,
718         })?;
719 
720         // Copy `rust-analyzer-proc-macro-srv` to `<sysroot>/libexec/`
721         // so that r-a can use it.
722         let libexec_path = builder.sysroot(self.compiler).join("libexec");
723         t!(fs::create_dir_all(&libexec_path));
724         builder.copy(&path, &libexec_path.join("rust-analyzer-proc-macro-srv"));
725 
726         Some(path)
727     }
728 }
729 
730 macro_rules! tool_extended {
731     (($sel:ident, $builder:ident),
732        $($name:ident,
733        $path:expr,
734        $tool_name:expr,
735        stable = $stable:expr
736        $(,tool_std = $tool_std:literal)?
737        $(,allow_features = $allow_features:expr)?
738        $(,add_bins_to_sysroot = $add_bins_to_sysroot:expr)?
739        ;)+) => {
740         $(
741             #[derive(Debug, Clone, Hash, PartialEq, Eq)]
742         pub struct $name {
743             pub compiler: Compiler,
744             pub target: TargetSelection,
745             pub extra_features: Vec<String>,
746         }
747 
748         impl Step for $name {
749             type Output = Option<PathBuf>;
750             const DEFAULT: bool = true; // Overwritten below
751             const ONLY_HOSTS: bool = true;
752 
753             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
754                 let builder = run.builder;
755                 run.path($path).default_condition(
756                     builder.config.extended
757                         && builder.config.tools.as_ref().map_or(
758                             // By default, on nightly/dev enable all tools, else only
759                             // build stable tools.
760                             $stable || builder.build.unstable_features(),
761                             // If `tools` is set, search list for this tool.
762                             |tools| {
763                                 tools.iter().any(|tool| match tool.as_ref() {
764                                     "clippy" => $tool_name == "clippy-driver",
765                                     x => $tool_name == x,
766                             })
767                         }),
768                 )
769             }
770 
771             fn make_run(run: RunConfig<'_>) {
772                 run.builder.ensure($name {
773                     compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
774                     target: run.target,
775                     extra_features: Vec::new(),
776                 });
777             }
778 
779             #[allow(unused_mut)]
780             fn run(mut $sel, $builder: &Builder<'_>) -> Option<PathBuf> {
781                 let tool = $builder.ensure(ToolBuild {
782                     compiler: $sel.compiler,
783                     target: $sel.target,
784                     tool: $tool_name,
785                     mode: if false $(|| $tool_std)? { Mode::ToolStd } else { Mode::ToolRustc },
786                     path: $path,
787                     extra_features: $sel.extra_features,
788                     is_optional_tool: true,
789                     source_type: SourceType::InTree,
790                     allow_features: concat!($($allow_features)*),
791                 })?;
792 
793                 if (false $(|| !$add_bins_to_sysroot.is_empty())?) && $sel.compiler.stage > 0 {
794                     let bindir = $builder.sysroot($sel.compiler).join("bin");
795                     t!(fs::create_dir_all(&bindir));
796 
797                     #[allow(unused_variables)]
798                     let tools_out = $builder
799                         .cargo_out($sel.compiler, Mode::ToolRustc, $sel.target);
800 
801                     $(for add_bin in $add_bins_to_sysroot {
802                         let bin_source = tools_out.join(exe(add_bin, $sel.target));
803                         let bin_destination = bindir.join(exe(add_bin, $sel.compiler.host));
804                         $builder.copy(&bin_source, &bin_destination);
805                     })?
806 
807                     let tool = bindir.join(exe($tool_name, $sel.compiler.host));
808                     Some(tool)
809                 } else {
810                     Some(tool)
811                 }
812             }
813         }
814         )+
815     }
816 }
817 
818 // Note: tools need to be also added to `Builder::get_step_descriptions` in `builder.rs`
819 // to make `./x.py build <tool>` work.
820 // Note: Most submodule updates for tools are handled by bootstrap.py, since they're needed just to
821 // invoke Cargo to build bootstrap. See the comment there for more details.
822 tool_extended!((self, builder),
823     Cargofmt, "src/tools/rustfmt", "cargo-fmt", stable=true;
824     CargoClippy, "src/tools/clippy", "cargo-clippy", stable=true;
825     Clippy, "src/tools/clippy", "clippy-driver", stable=true, add_bins_to_sysroot = ["clippy-driver", "cargo-clippy"];
826     Miri, "src/tools/miri", "miri", stable=false, add_bins_to_sysroot = ["miri"];
827     CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", stable=true, add_bins_to_sysroot = ["cargo-miri"];
828     // FIXME: tool_std is not quite right, we shouldn't allow nightly features.
829     // But `builder.cargo` doesn't know how to handle ToolBootstrap in stages other than 0,
830     // and this is close enough for now.
831     Rls, "src/tools/rls", "rls", stable=true, tool_std=true;
832     RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true;
833     Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"];
834 );
835 
836 impl<'a> Builder<'a> {
837     /// Gets a `Command` which is ready to run `tool` in `stage` built for
838     /// `host`.
tool_cmd(&self, tool: Tool) -> Command839     pub fn tool_cmd(&self, tool: Tool) -> Command {
840         let mut cmd = Command::new(self.tool_exe(tool));
841         let compiler = self.compiler(0, self.config.build);
842         let host = &compiler.host;
843         // Prepares the `cmd` provided to be able to run the `compiler` provided.
844         //
845         // Notably this munges the dynamic library lookup path to point to the
846         // right location to run `compiler`.
847         let mut lib_paths: Vec<PathBuf> = vec![
848             self.build.rustc_snapshot_libdir(),
849             self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("deps"),
850         ];
851 
852         // On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make
853         // mode) and that C compiler may need some extra PATH modification. Do
854         // so here.
855         if compiler.host.contains("msvc") {
856             let curpaths = env::var_os("PATH").unwrap_or_default();
857             let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
858             for &(ref k, ref v) in self.cc.borrow()[&compiler.host].env() {
859                 if k != "PATH" {
860                     continue;
861                 }
862                 for path in env::split_paths(v) {
863                     if !curpaths.contains(&path) {
864                         lib_paths.push(path);
865                     }
866                 }
867             }
868         }
869 
870         add_dylib_path(lib_paths, &mut cmd);
871 
872         // Provide a RUSTC for this command to use.
873         cmd.env("RUSTC", &self.initial_rustc);
874 
875         cmd
876     }
877 }
878