1use std::any::{Any, type_name};
2use std::cell::{Cell, RefCell};
3use std::collections::BTreeSet;
4use std::fmt::{Debug, Write};
5use std::hash::Hash;
6use std::ops::Deref;
7use std::path::{Path, PathBuf};
8use std::sync::OnceLock;
9use std::time::{Duration, Instant};
10use std::{env, fs, iter};
11
12use clap::ValueEnum;
13#[cfg(feature = "tracing")]
14use tracing::instrument;
15
16pub use self::cargo::{Cargo, apply_pgo, cargo_profile_var};
17pub use crate::Compiler;
18use crate::core::build_steps::compile::{Std, StdLink, looks_like_codegen_backend};
19use crate::core::build_steps::tool::RustcPrivateCompilers;
20use crate::core::build_steps::{
21 check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
22};
23use crate::core::builder::step_stack::StepRecord;
24pub use crate::core::builder::step_stack::StepStack;
25use crate::core::config::flags::Subcommand;
26use crate::core::config::{DryRun, TargetSelection};
27use crate::utils::build_stamp::BuildStamp;
28use crate::utils::cache::Cache;
29use crate::utils::exec::{BootstrapCommand, ExecutionContext, command};
30use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t};
31use crate::utils::tracing::format_location;
32use crate::{Build, Crate, trace};
33
34mod cargo;
35mod cli_paths;
36mod step_stack;
37#[cfg(test)]
38mod tests;
39
40pub struct Builder<'a> {
43 pub build: &'a Build,
45
46 pub top_stage: u32,
50
51 pub kind: Kind,
53
54 cache: Cache,
57
58 stack: RefCell<Vec<Box<dyn AnyDebug>>>,
61
62 time_spent_on_dependencies: Cell<Duration>,
64
65 pub paths: Vec<PathBuf>,
69
70 submodule_paths_cache: OnceLock<Vec<String>>,
72
73 #[expect(clippy::type_complexity)]
77 log_cli_step_for_tests:
78 Option<Box<dyn Fn(&CommandLineStepDescription, &[PathSet], &[TargetSelection])>>,
79}
80
81impl Deref for Builder<'_> {
82 type Target = Build;
83
84 fn deref(&self) -> &Self::Target {
85 self.build
86 }
87}
88
89pub trait AnyDebug: Any + Debug {}
94impl<T: Any + Debug> AnyDebug for T {}
95impl dyn AnyDebug {
96 fn downcast_ref<T: Any>(&self) -> Option<&T> {
98 (self as &dyn Any).downcast_ref()
99 }
100
101 }
103
104pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
111 type Output: Clone;
113
114 fn run(self, builder: &Builder<'_>) -> Self::Output;
118
119 fn metadata(&self) -> Option<StepMetadata> {
121 None
122 }
123}
124
125impl<S: CommandLineStep> Step for S {
127 type Output = <S as CommandLineStep>::Output;
128
129 fn run(self, builder: &Builder<'_>) -> Self::Output {
130 <S as CommandLineStep>::run(self, builder)
131 }
132
133 fn metadata(&self) -> Option<StepMetadata> {
134 <S as CommandLineStep>::metadata(self)
135 }
136}
137
138pub trait CommandLineStep: 'static + Clone + Debug + PartialEq + Eq + Hash {
144 type Output: Clone;
146
147 const IS_HOST: bool = false;
154
155 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>;
158
159 fn is_default_step(_builder: &Builder<'_>) -> bool {
173 false
174 }
175
176 fn make_run(_run: RunConfig<'_>);
180
181 fn run(self, builder: &Builder<'_>) -> Self::Output;
183
184 fn metadata(&self) -> Option<StepMetadata> {
186 None
187 }
188}
189
190#[derive(Clone, Debug, PartialEq, Eq)]
192pub struct StepMetadata {
193 name: String,
194 kind: Kind,
195 target: TargetSelection,
196 built_by: Option<Compiler>,
197 stage: Option<u32>,
198 metadata: Option<String>,
200}
201
202impl StepMetadata {
203 pub fn build(name: &str, target: TargetSelection) -> Self {
204 Self::new(name, target, Kind::Build)
205 }
206
207 pub fn check(name: &str, target: TargetSelection) -> Self {
208 Self::new(name, target, Kind::Check)
209 }
210
211 pub fn clippy(name: &str, target: TargetSelection) -> Self {
212 Self::new(name, target, Kind::Clippy)
213 }
214
215 pub fn doc(name: &str, target: TargetSelection) -> Self {
216 Self::new(name, target, Kind::Doc)
217 }
218
219 pub fn dist(name: &str, target: TargetSelection) -> Self {
220 Self::new(name, target, Kind::Dist)
221 }
222
223 pub fn test(name: &str, target: TargetSelection) -> Self {
224 Self::new(name, target, Kind::Test)
225 }
226
227 pub fn run(name: &str, target: TargetSelection) -> Self {
228 Self::new(name, target, Kind::Run)
229 }
230
231 pub fn new(name: &str, target: TargetSelection, kind: Kind) -> Self {
232 Self { name: name.to_string(), kind, target, built_by: None, stage: None, metadata: None }
233 }
234
235 pub fn built_by(mut self, compiler: Compiler) -> Self {
236 self.built_by = Some(compiler);
237 self
238 }
239
240 pub fn stage(mut self, stage: u32) -> Self {
241 self.stage = Some(stage);
242 self
243 }
244
245 pub fn with_metadata(mut self, metadata: String) -> Self {
246 self.metadata = Some(metadata);
247 self
248 }
249
250 pub fn get_stage(&self) -> Option<u32> {
251 self.stage.or(self
252 .built_by
253 .map(|compiler| if self.name == "std" { compiler.stage } else { compiler.stage + 1 }))
256 }
257
258 pub fn get_name(&self) -> &str {
259 &self.name
260 }
261
262 pub fn get_target(&self) -> TargetSelection {
263 self.target
264 }
265}
266
267pub struct RunConfig<'a> {
268 pub builder: &'a Builder<'a>,
269 pub target: TargetSelection,
270 pub paths: Vec<PathSet>,
271}
272
273impl RunConfig<'_> {
274 pub fn build_triple(&self) -> TargetSelection {
275 self.builder.build.host_target
276 }
277
278 #[track_caller]
280 pub fn cargo_crates_in_set(&self) -> Vec<String> {
281 let mut crates = Vec::new();
282 for krate in &self.paths {
283 let path = &krate.assert_single_path().path;
284
285 let crate_name = self
286 .builder
287 .crate_paths
288 .get(path)
289 .unwrap_or_else(|| panic!("missing crate for path {}", path.display()));
290
291 crates.push(crate_name.to_string());
292 }
293 crates
294 }
295
296 pub fn make_run_crates(&self, alias: Alias) -> Vec<String> {
303 let has_alias =
304 self.paths.iter().any(|set| set.assert_single_path().path.ends_with(alias.as_str()));
305 if !has_alias {
306 return self.cargo_crates_in_set();
307 }
308
309 let crates = match alias {
310 Alias::Library => self.builder.in_tree_crates("sysroot", Some(self.target)),
311 Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)),
312 };
313
314 crates.into_iter().map(|krate| krate.name.to_string()).collect()
315 }
316}
317
318#[derive(Debug, Copy, Clone)]
319pub enum Alias {
320 Library,
321 Compiler,
322}
323
324impl Alias {
325 fn as_str(self) -> &'static str {
326 match self {
327 Alias::Library => "library",
328 Alias::Compiler => "compiler",
329 }
330 }
331}
332
333pub fn crate_description(crates: &[impl AsRef<str>]) -> String {
337 if crates.is_empty() {
338 return "".into();
339 }
340
341 let mut descr = String::from("{");
342 descr.push_str(crates[0].as_ref());
343 for krate in &crates[1..] {
344 descr.push_str(", ");
345 descr.push_str(krate.as_ref());
346 }
347 descr.push('}');
348 descr
349}
350
351struct CommandLineStepDescription {
352 is_host: bool,
353 should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>,
354 is_default_step_fn: fn(&Builder<'_>) -> bool,
355 make_run: fn(RunConfig<'_>),
356 name: &'static str,
357
358 #[cfg_attr(not(test), expect(dead_code, reason = "currently only needed by tests"))]
360 kind: Kind,
361}
362
363#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
364pub struct TaskPath {
365 pub path: PathBuf,
366}
367
368impl Debug for TaskPath {
369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370 write!(f, "{}", self.path.display())
371 }
372}
373
374#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
376pub enum PathSet {
377 Set(BTreeSet<TaskPath>),
388 Suite(TaskPath),
395}
396
397impl PathSet {
398 fn one<P: Into<PathBuf>>(path: P) -> PathSet {
399 let mut set = BTreeSet::new();
400 set.insert(TaskPath { path: path.into() });
401 PathSet::Set(set)
402 }
403
404 fn has(&self, needle: &Path) -> bool {
405 match self {
406 PathSet::Set(set) => set.iter().any(|p| Self::check(p, needle)),
407 PathSet::Suite(suite) => Self::check(suite, needle),
408 }
409 }
410
411 fn check(p: &TaskPath, needle: &Path) -> bool {
413 p.path.ends_with(needle) || p.path.starts_with(needle)
415 }
416
417 #[track_caller]
421 pub fn assert_single_path(&self) -> &TaskPath {
422 match self {
423 PathSet::Set(set) => {
424 assert_eq!(set.len(), 1, "called assert_single_path on multiple paths");
425 set.iter().next().unwrap()
426 }
427 PathSet::Suite(_) => unreachable!("called assert_single_path on a Suite path"),
428 }
429 }
430}
431
432impl CommandLineStepDescription {
433 fn from<S: CommandLineStep>(kind: Kind) -> CommandLineStepDescription {
434 CommandLineStepDescription {
435 is_host: S::IS_HOST,
436 should_run: S::should_run,
437 is_default_step_fn: S::is_default_step,
438 make_run: S::make_run,
439 name: std::any::type_name::<S>(),
440 kind,
441 }
442 }
443
444 fn maybe_run(&self, builder: &Builder<'_>, mut pathsets: Vec<PathSet>) {
445 pathsets.retain(|set| !self.is_excluded(builder, set));
446
447 if pathsets.is_empty() {
448 return;
449 }
450
451 let targets = if self.is_host { &builder.hosts } else { &builder.targets };
453
454 if let Some(ref log_cli_step) = builder.log_cli_step_for_tests {
456 log_cli_step(self, &pathsets, targets);
457 return;
459 }
460
461 for target in targets {
462 let run = RunConfig { builder, paths: pathsets.clone(), target: *target };
463 (self.make_run)(run);
464 }
465 }
466
467 fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
468 if builder.config.skip.iter().any(|e| pathset.has(e)) {
469 if !matches!(builder.config.get_dry_run(), DryRun::SelfCheck) {
470 println!("Skipping {pathset:?} because it is excluded");
471 }
472 return true;
473 }
474
475 if !builder.config.skip.is_empty()
476 && !matches!(builder.config.get_dry_run(), DryRun::SelfCheck)
477 {
478 builder.do_if_verbose(|| {
479 println!(
480 "{:?} not skipped for {:?} -- not in {:?}",
481 pathset, self.name, builder.config.skip
482 )
483 });
484 }
485 false
486 }
487}
488
489pub struct ShouldRun<'a> {
496 pub builder: &'a Builder<'a>,
497
498 paths: BTreeSet<PathSet>,
500}
501
502impl<'a> ShouldRun<'a> {
503 fn new(builder: &'a Builder<'_>) -> ShouldRun<'a> {
504 ShouldRun { builder, paths: BTreeSet::new() }
505 }
506
507 pub(crate) fn crate_or_deps(self, root_crate_name: &str) -> Self {
512 self.crate_or_deps_filtered(root_crate_name, |_: &Crate| true)
513 }
514
515 pub(crate) fn crate_or_deps_filtered(
521 mut self,
522 root_crate_name: &str,
523 crate_filter_fn: impl Fn(&Crate) -> bool,
524 ) -> Self {
525 let crates = self.builder.in_tree_crates(root_crate_name, None);
526 for krate in crates {
527 if !crate_filter_fn(krate) {
528 continue;
529 }
530
531 let path = krate.local_path(self.builder);
532 self.paths.insert(PathSet::one(path));
533 }
534 self
535 }
536
537 pub fn alias(self, alias: &str) -> Self {
539 self.assert_valid_alias(alias);
540 self.alias_without_assert(alias)
541 }
542
543 pub fn alias_without_assert(mut self, alias: &str) -> Self {
548 self.paths.insert(PathSet::Set(iter::once(TaskPath { path: alias.into() }).collect()));
549 self
550 }
551
552 fn assert_valid_alias(&self, alias: &str) {
553 assert!(
554 !self.builder.src.join(alias).exists(),
555 "use `builder.path()` for real paths: {alias}"
556 );
557 }
558
559 fn assert_valid_path(&self, path: &str) {
560 let submodules_paths = self.builder.submodule_paths();
561
562 if !submodules_paths.iter().any(|sm_p| path.contains(sm_p)) {
564 assert!(
565 self.builder.src.join(path).exists(),
566 "`should_run.path` should correspond to a real on-disk path - use `alias` if there is no relevant path: {path}"
567 );
568 }
569 }
570
571 pub fn path(mut self, path: &str) -> Self {
576 self.assert_valid_path(path);
577
578 let task = TaskPath { path: path.into() };
579 self.paths.insert(PathSet::Set(BTreeSet::from_iter([task])));
580 self
581 }
582
583 pub fn path_with_alias(mut self, path: &str, alias: &str) -> Self {
585 self.assert_valid_path(path);
586 self.assert_valid_alias(alias);
587
588 let set = [path, alias]
589 .into_iter()
590 .map(|p| TaskPath { path: PathBuf::from(p) })
591 .collect::<BTreeSet<_>>();
592 self.paths.insert(PathSet::Set(set));
593 self
594 }
595
596 pub fn multi_path(mut self, paths: &[&str]) -> Self {
598 let mut set = BTreeSet::new();
599 for path in paths {
600 self.assert_valid_path(path);
601 set.insert(TaskPath { path: (*path).into() });
602 }
603 self.paths.insert(PathSet::Set(set));
604 self
605 }
606
607 pub fn suite_path(mut self, suite: &str) -> Self {
608 self.paths.insert(PathSet::Suite(TaskPath { path: suite.into() }));
609 self
610 }
611
612 fn default_pathsets(&self) -> Vec<PathSet> {
615 self.paths.iter().cloned().collect::<Vec<_>>()
616 }
617}
618
619#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord, ValueEnum)]
620pub enum Kind {
621 #[value(alias = "b")]
622 Build,
623 #[value(alias = "c")]
624 Check,
625 Clippy,
626 Fix,
627 Format,
628 #[value(alias = "t")]
629 Test,
630 Miri,
631 MiriSetup,
632 MiriTest,
633 Bench,
634 #[value(alias = "d")]
635 Doc,
636 Clean,
637 Dist,
638 Install,
639 #[value(alias = "r")]
640 Run,
641 Setup,
642 Vendor,
643 Perf,
644}
645
646impl Kind {
647 pub fn as_str(&self) -> &'static str {
648 match self {
649 Kind::Build => "build",
650 Kind::Check => "check",
651 Kind::Clippy => "clippy",
652 Kind::Fix => "fix",
653 Kind::Format => "fmt",
654 Kind::Test => "test",
655 Kind::Miri => "miri",
656 Kind::MiriSetup => panic!("`as_str` is not supported for `Kind::MiriSetup`."),
657 Kind::MiriTest => panic!("`as_str` is not supported for `Kind::MiriTest`."),
658 Kind::Bench => "bench",
659 Kind::Doc => "doc",
660 Kind::Clean => "clean",
661 Kind::Dist => "dist",
662 Kind::Install => "install",
663 Kind::Run => "run",
664 Kind::Setup => "setup",
665 Kind::Vendor => "vendor",
666 Kind::Perf => "perf",
667 }
668 }
669
670 pub fn description(&self) -> String {
671 match self {
672 Kind::Test => "Testing",
673 Kind::Bench => "Benchmarking",
674 Kind::Doc => "Documenting",
675 Kind::Run => "Running",
676 Kind::Clippy => "Linting",
677 Kind::Perf => "Profiling & benchmarking",
678 _ => {
679 let title_letter = self.as_str()[0..1].to_ascii_uppercase();
680 return format!("{title_letter}{}ing", &self.as_str()[1..]);
681 }
682 }
683 .to_owned()
684 }
685}
686
687#[derive(Debug, Clone, Hash, PartialEq, Eq)]
688struct Libdir {
689 compiler: Compiler,
690 target: TargetSelection,
691}
692
693impl Step for Libdir {
694 type Output = PathBuf;
695
696 fn run(self, builder: &Builder<'_>) -> PathBuf {
697 let relative_sysroot_libdir = builder.sysroot_libdir_relative(self.compiler);
698 let sysroot = builder.sysroot(self.compiler).join(relative_sysroot_libdir).join("rustlib");
699
700 if !builder.config.dry_run() {
701 if !builder.download_rustc() {
704 let sysroot_target_libdir = sysroot.join(self.target).join("lib");
705 builder.do_if_verbose(|| {
706 eprintln!(
707 "Removing sysroot {} to avoid caching bugs",
708 sysroot_target_libdir.display()
709 )
710 });
711 let _ = fs::remove_dir_all(&sysroot_target_libdir);
712 t!(fs::create_dir_all(&sysroot_target_libdir));
713 }
714
715 if self.compiler.stage == 0 {
716 dist::maybe_install_llvm_target(
720 builder,
721 self.compiler.host,
722 &builder.sysroot(self.compiler),
723 );
724 }
725 }
726
727 sysroot
728 }
729}
730
731#[cfg(feature = "tracing")]
732pub const STEP_SPAN_TARGET: &str = "STEP";
733
734impl<'a> Builder<'a> {
735 fn get_step_descriptions(kind: Kind) -> Vec<CommandLineStepDescription> {
736 macro_rules! describe {
737 ($($rule:ty),+ $(,)?) => {{
738 vec![$(CommandLineStepDescription::from::<$rule>(kind)),+]
739 }};
740 }
741 match kind {
742 Kind::Build => describe!(
743 compile::Std,
744 compile::Rustc,
745 compile::Assemble,
746 compile::CraneliftCodegenBackend,
747 compile::GccCodegenBackend,
748 compile::StartupObjects,
749 tool::BuildManifest,
750 tool::Rustbook,
751 tool::ErrorIndex,
752 tool::UnstableBookGen,
753 tool::Tidy,
754 tool::Linkchecker,
755 tool::CargoTest,
756 tool::Compiletest,
757 tool::RemoteTestServer,
758 tool::RemoteTestClient,
759 tool::RustInstaller,
760 tool::FeaturesStatusDump,
761 tool::Cargo,
762 tool::RustAnalyzer,
763 tool::RustAnalyzerProcMacroSrv,
764 tool::Rustdoc,
765 tool::Clippy,
766 tool::CargoClippy,
767 llvm::Llvm,
768 gcc::Gcc,
769 llvm::Sanitizers,
770 tool::Rustfmt,
771 tool::Cargofmt,
772 tool::Miri,
773 tool::CargoMiri,
774 llvm::Lld,
775 llvm::Enzyme,
776 llvm::RustOffload,
777 llvm::CrtBeginEnd,
778 tool::RustdocGUITest,
779 tool::OptimizedDist,
780 tool::CoverageDump,
781 tool::LlvmBitcodeLinker,
782 tool::RustcPerf,
783 tool::WasmComponentLd,
784 tool::LldWrapper
785 ),
786 Kind::Clippy => describe!(
787 clippy::Std,
788 clippy::Rustc,
789 clippy::Bootstrap,
790 clippy::BuildHelper,
791 clippy::BuildManifest,
792 clippy::CargoMiri,
793 clippy::Clippy,
794 clippy::CodegenGcc,
795 clippy::CollectLicenseMetadata,
796 clippy::Compiletest,
797 clippy::CoverageDump,
798 clippy::Jsondocck,
799 clippy::Jsondoclint,
800 clippy::LintDocs,
801 clippy::LlvmBitcodeLinker,
802 clippy::Miri,
803 clippy::MiroptTestTools,
804 clippy::OptDist,
805 clippy::RemoteTestClient,
806 clippy::RemoteTestServer,
807 clippy::RustAnalyzer,
808 clippy::Rustdoc,
809 clippy::Rustfmt,
810 clippy::RustInstaller,
811 clippy::TestFloatParse,
812 clippy::Tidy,
813 clippy::CI,
814 ),
815 Kind::Check | Kind::Fix => describe!(
816 check::Rustc,
817 check::Rustdoc,
818 check::CraneliftCodegenBackend,
819 check::GccCodegenBackend,
820 check::Clippy,
821 check::Miri,
822 check::CargoMiri,
823 check::Priroda,
824 check::MiroptTestTools,
825 check::Rustfmt,
826 check::RustAnalyzer,
827 check::TestFloatParse,
828 check::Bootstrap,
829 check::RunMakeSupport,
830 check::Compiletest,
831 check::RustdocGuiTest,
832 check::FeaturesStatusDump,
833 check::CoverageDump,
834 check::Linkchecker,
835 check::BumpStage0,
836 check::Tidy,
837 check::Std,
844 ),
845 Kind::Test => describe!(
846 crate::core::build_steps::toolstate::ToolStateCheck,
847 test::Tidy,
848 test::BootstrapPy,
849 test::Bootstrap,
850 test::Ui,
851 test::Crashes,
852 test::Coverage,
853 test::CoverageModeAlias,
854 test::MirOpt,
855 test::CodegenLlvm,
856 test::CodegenUnits,
857 test::AssemblyLlvm,
858 test::Incremental,
859 test::Debuginfo,
860 test::UiFullDeps,
861 test::RustdocHtml,
862 test::CoverageRunRustdoc,
863 test::Pretty,
864 test::CodegenCranelift,
865 test::CodegenGCC,
866 test::Crate,
867 test::CrateLibrustc,
868 test::CrateRustdoc,
869 test::CrateRustdocJsonTypes,
870 test::CrateBootstrap,
871 test::RemoteTestClientTests,
872 test::Linkcheck,
873 test::TierCheck,
874 test::Cargotest,
875 test::Cargo,
876 test::RustAnalyzer,
877 test::ErrorIndex,
878 test::Distcheck,
879 test::Nomicon,
880 test::Reference,
881 test::RustdocBook,
882 test::RustByExample,
883 test::TheBook,
884 test::UnstableBook,
885 test::RustcBook,
886 test::LintDocs,
887 test::EmbeddedBook,
888 test::EditionGuide,
889 test::Rustfmt,
890 test::Miri,
891 test::CargoMiri,
892 test::Priroda,
893 test::Clippy,
894 test::CompiletestTest,
895 test::StdarchVerify,
896 test::CrateRunMakeSupport,
897 test::CrateBuildHelper,
898 test::RustdocJSStd,
899 test::RustdocJSNotStd,
900 test::RustdocGUI,
901 test::RustdocTheme,
902 test::RustdocUi,
903 test::RustdocJson,
904 test::HtmlCheck,
905 test::RustInstaller,
906 test::TestFloatParse,
907 test::CollectLicenseMetadata,
908 test::RunMake,
909 test::RunMakeCargo,
910 test::BuildStd,
911 test::StdSemverCheck,
912 test::IntrinsicTest,
913 ),
914 Kind::Miri => describe!(test::Crate),
915 Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc),
916 Kind::Doc => describe!(
917 doc::UnstableBook,
918 doc::UnstableBookGen,
919 doc::TheBook,
920 doc::Standalone,
921 doc::Std,
922 doc::Rustc,
923 doc::Rustdoc,
924 doc::Rustfmt,
925 doc::ErrorIndex,
926 doc::Nomicon,
927 doc::Reference,
928 doc::RustdocBook,
929 doc::RustByExample,
930 doc::RustcBook,
931 doc::Cargo,
932 doc::CargoBook,
933 doc::Clippy,
934 doc::ClippyBook,
935 doc::Miri,
936 doc::EmbeddedBook,
937 doc::EditionGuide,
938 doc::StyleGuide,
939 doc::Tidy,
940 doc::Bootstrap,
941 doc::Releases,
942 doc::RunMakeSupport,
943 doc::BuildHelper,
944 doc::Compiletest,
945 ),
946 Kind::Dist => describe!(
947 dist::Docs,
948 dist::RustcDocs,
949 dist::JsonDocs,
950 dist::Mingw,
951 dist::Rustc,
952 dist::CraneliftCodegenBackend,
953 dist::GccCodegenBackend,
954 dist::Std,
955 dist::RustcDev,
956 dist::Analysis,
957 dist::Src,
958 dist::Cargo,
959 dist::RustAnalyzer,
960 dist::Rustfmt,
961 dist::Clippy,
962 dist::Miri,
963 dist::LlvmTools,
964 dist::LlvmBitcodeLinker,
965 dist::RustDev,
966 dist::Enzyme,
967 dist::Offload,
968 dist::Bootstrap,
969 dist::Extended,
970 dist::PlainSourceTarball,
975 dist::PlainSourceTarballGpl,
976 dist::BuildManifest,
977 dist::ReproducibleArtifacts,
978 dist::GccDev,
979 dist::Gcc
980 ),
981 Kind::Install => describe!(
982 install::Docs,
983 install::Std,
984 install::Rustc,
989 install::RustcDev,
990 install::Cargo,
991 install::RustAnalyzer,
992 install::Rustfmt,
993 install::Clippy,
994 install::Miri,
995 install::LlvmTools,
996 install::Src,
997 install::RustcCodegenCranelift,
998 install::LlvmBitcodeLinker
999 ),
1000 Kind::Run => describe!(
1001 run::BuildManifest,
1002 run::BumpStage0,
1003 run::ReplaceVersionPlaceholder,
1004 run::Miri,
1005 run::CollectLicenseMetadata,
1006 run::GenerateCopyright,
1007 run::GenerateWindowsSys,
1008 run::GenerateCompletions,
1009 run::UnicodeTableGenerator,
1010 run::FeaturesStatusDump,
1011 run::CyclicStep,
1012 run::CoverageDump,
1013 run::Rustfmt,
1014 run::GenerateHelp,
1015 ),
1016 Kind::Setup => {
1017 describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
1018 }
1019 Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
1020 Kind::Vendor => describe!(vendor::Vendor),
1021 Kind::Format | Kind::Perf => vec![],
1023 Kind::MiriTest | Kind::MiriSetup => unreachable!(),
1024 }
1025 }
1026
1027 pub fn get_help(build: &Build, kind: Kind) -> Option<String> {
1028 let step_descriptions = Builder::get_step_descriptions(kind);
1029 if step_descriptions.is_empty() {
1030 return None;
1031 }
1032
1033 let builder = Self::new_internal(build, kind, vec![]);
1034 let builder = &builder;
1035
1036 let mut should_run = ShouldRun::new(builder);
1037 for desc in step_descriptions {
1038 should_run = (desc.should_run)(should_run);
1039 }
1040 let mut help = String::from("Available paths:\n");
1041 let mut add_path = |path: &Path| {
1042 t!(write!(help, " ./x.py {} {}\n", kind.as_str(), path.display()));
1043 };
1044 for pathset in should_run.paths {
1045 match pathset {
1046 PathSet::Set(set) => {
1047 for path in set {
1048 add_path(&path.path);
1049 }
1050 }
1051 PathSet::Suite(path) => {
1052 add_path(&path.path.join("..."));
1053 }
1054 }
1055 }
1056 Some(help)
1057 }
1058
1059 fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
1060 Builder {
1061 build,
1062 top_stage: build.config.stage,
1063 kind,
1064 cache: Cache::new(),
1065 stack: RefCell::new(Vec::new()),
1066 time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
1067 paths,
1068 submodule_paths_cache: Default::default(),
1069 log_cli_step_for_tests: None,
1070 }
1071 }
1072
1073 pub fn new(build: &Build) -> Builder<'_> {
1074 let paths = &build.config.paths;
1075 let (kind, paths) = match build.config.cmd {
1076 Subcommand::Build { .. } => (Kind::Build, &paths[..]),
1077 Subcommand::Check { .. } => (Kind::Check, &paths[..]),
1078 Subcommand::Clippy { .. } => (Kind::Clippy, &paths[..]),
1079 Subcommand::Fix => (Kind::Fix, &paths[..]),
1080 Subcommand::Doc { .. } => (Kind::Doc, &paths[..]),
1081 Subcommand::Test { .. } => (Kind::Test, &paths[..]),
1082 Subcommand::Miri { .. } => (Kind::Miri, &paths[..]),
1083 Subcommand::Bench { .. } => (Kind::Bench, &paths[..]),
1084 Subcommand::Dist => (Kind::Dist, &paths[..]),
1085 Subcommand::Install => (Kind::Install, &paths[..]),
1086 Subcommand::Run { .. } => (Kind::Run, &paths[..]),
1087 Subcommand::Clean { .. } => (Kind::Clean, &paths[..]),
1088 Subcommand::Format { .. } => (Kind::Format, &[][..]),
1089 Subcommand::Setup { profile: ref path } => (
1090 Kind::Setup,
1091 path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
1092 ),
1093 Subcommand::Vendor { .. } => (Kind::Vendor, &paths[..]),
1094 Subcommand::Perf { .. } => (Kind::Perf, &paths[..]),
1095 };
1096
1097 StepStack::with_current(|stack| stack.clear());
1098 Self::new_internal(build, kind, paths.to_owned())
1099 }
1100
1101 pub fn execute_cli(&self) {
1102 self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
1103 }
1104
1105 pub fn run_default_doc_steps(&self) {
1107 for desc in &Builder::get_step_descriptions(Kind::Doc) {
1116 if !(desc.is_default_step_fn)(self) {
1117 continue;
1118 }
1119
1120 let should_run = (desc.should_run)(ShouldRun::new(self));
1121 let default_pathsets = should_run.default_pathsets();
1122
1123 let targets = if desc.is_host { &self.hosts } else { &self.targets };
1124 for &target in targets {
1125 let run = RunConfig { builder: self, target, paths: default_pathsets.clone() };
1126 (desc.make_run)(run);
1127 }
1128 }
1129 }
1130
1131 pub fn doc_rust_lang_org_channel(&self) -> String {
1132 let channel = match &*self.config.channel {
1133 "stable" => &self.version,
1134 "beta" => "beta",
1135 "nightly" | "dev" => "nightly",
1136 _ => "stable",
1138 };
1139
1140 format!("https://doc.rust-lang.org/{channel}")
1141 }
1142
1143 fn run_step_descriptions(&self, v: &[CommandLineStepDescription], paths: &[PathBuf]) {
1144 cli_paths::match_paths_to_steps_and_run(self, v, paths);
1145 }
1146
1147 pub fn link_std_into_rustc_driver(&self, target: TargetSelection) -> bool {
1150 !target.triple.ends_with("-windows-gnu")
1151 }
1152
1153 #[track_caller]
1158 #[cfg_attr(
1159 feature = "tracing",
1160 instrument(
1161 level = "trace",
1162 name = "Builder::compiler",
1163 target = "COMPILER",
1164 skip_all,
1165 fields(
1166 stage = stage,
1167 host = ?host,
1168 ),
1169 ),
1170 )]
1171 pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler {
1172 self.ensure(compile::Assemble { target_compiler: Compiler::new(stage, host) })
1173 }
1174
1175 #[track_caller]
1192 pub fn compiler_for_std(&self, stage: u32) -> Compiler {
1193 if compile::Std::should_be_uplifted_from_stage_1(self, stage) {
1194 self.compiler(1, self.host_target)
1195 } else {
1196 self.compiler(stage, self.host_target)
1197 }
1198 }
1199
1200 #[track_caller]
1212 #[cfg_attr(
1213 feature = "tracing",
1214 instrument(
1215 level = "trace",
1216 name = "Builder::compiler_for",
1217 target = "COMPILER_FOR",
1218 skip_all,
1219 fields(
1220 stage = stage,
1221 host = ?host,
1222 target = ?target,
1223 ),
1224 ),
1225 )]
1226 pub fn compiler_for(
1229 &self,
1230 stage: u32,
1231 host: TargetSelection,
1232 target: TargetSelection,
1233 ) -> Compiler {
1234 let mut resolved_compiler = if self.build.force_use_stage2(stage) {
1235 trace!(target: "COMPILER_FOR", ?stage, "force_use_stage2");
1236 self.compiler(2, self.config.host_target)
1237 } else if self.build.force_use_stage1(stage, target) {
1238 trace!(target: "COMPILER_FOR", ?stage, "force_use_stage1");
1239 self.compiler(1, self.config.host_target)
1240 } else {
1241 trace!(target: "COMPILER_FOR", ?stage, ?host, "no force, fallback to `compiler()`");
1242 self.compiler(stage, host)
1243 };
1244
1245 if stage != resolved_compiler.stage {
1246 resolved_compiler.forced_compiler(true);
1247 }
1248
1249 trace!(target: "COMPILER_FOR", ?resolved_compiler);
1250 resolved_compiler
1251 }
1252
1253 #[track_caller]
1260 #[cfg_attr(
1261 feature = "tracing",
1262 instrument(
1263 level = "trace",
1264 name = "Builder::std",
1265 target = "STD",
1266 skip_all,
1267 fields(
1268 compiler = ?compiler,
1269 target = ?target,
1270 ),
1271 ),
1272 )]
1273 pub fn std(&self, compiler: Compiler, target: TargetSelection) -> Option<BuildStamp> {
1274 if compiler.stage == 0 {
1284 if target != compiler.host {
1285 if self.local_rebuild {
1286 self.ensure(Std::new(compiler, target))
1287 } else {
1288 panic!(
1289 r"It is not possible to build the standard library for `{target}` using the stage0 compiler.
1290You have to build a stage1 compiler for `{}` first, and then use it to build a standard library for `{target}`.
1291Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler built from in-tree sources.
1292",
1293 compiler.host
1294 )
1295 }
1296 } else {
1297 self.ensure(StdLink::from_std(Std::new(compiler, target), compiler));
1299 None
1300 }
1301 } else {
1302 self.ensure(Std::new(compiler, target))
1305 }
1306 }
1307
1308 #[track_caller]
1309 pub fn sysroot(&self, compiler: Compiler) -> PathBuf {
1310 self.ensure(compile::Sysroot::new(compiler))
1311 }
1312
1313 #[track_caller]
1315 pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf {
1316 self.ensure(Libdir { compiler, target }).join(target).join("bin")
1317 }
1318
1319 #[track_caller]
1322 pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf {
1323 self.ensure(Libdir { compiler, target }).join(target).join("lib")
1324 }
1325
1326 pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf {
1327 self.sysroot_target_libdir(compiler, compiler.host).with_file_name("codegen-backends")
1328 }
1329
1330 pub fn rustc_libdir(&self, compiler: Compiler) -> PathBuf {
1336 if compiler.is_snapshot(self) {
1337 self.rustc_snapshot_libdir()
1338 } else {
1339 match self.config.libdir_relative() {
1340 Some(relative_libdir) if compiler.stage >= 1 => {
1341 self.sysroot(compiler).join(relative_libdir)
1342 }
1343 _ => self.sysroot(compiler).join(libdir(compiler.host)),
1344 }
1345 }
1346 }
1347
1348 pub fn libdir_relative(&self, compiler: Compiler) -> &Path {
1354 if compiler.is_snapshot(self) {
1355 libdir(self.config.host_target).as_ref()
1356 } else {
1357 match self.config.libdir_relative() {
1358 Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1359 _ => libdir(compiler.host).as_ref(),
1360 }
1361 }
1362 }
1363
1364 pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path {
1369 match self.config.libdir_relative() {
1370 Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1371 _ if compiler.stage == 0 => &self.build.initial_relative_libdir,
1372 _ => Path::new("lib"),
1373 }
1374 }
1375
1376 pub fn rustc_lib_paths(&self, compiler: Compiler) -> Vec<PathBuf> {
1377 let mut dylib_dirs = vec![self.rustc_libdir(compiler)];
1378
1379 if self.config.llvm_from_ci {
1381 let ci_llvm_lib = self.out.join(compiler.host).join("ci-llvm").join("lib");
1382 dylib_dirs.push(ci_llvm_lib);
1383 }
1384
1385 dylib_dirs
1386 }
1387
1388 pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut BootstrapCommand) {
1391 if cfg!(any(windows, target_os = "cygwin")) {
1395 return;
1396 }
1397
1398 add_dylib_path(self.rustc_lib_paths(compiler), cmd);
1399 }
1400
1401 pub fn rustc(&self, compiler: Compiler) -> PathBuf {
1403 if compiler.is_snapshot(self) {
1404 self.initial_rustc.clone()
1405 } else {
1406 self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
1407 }
1408 }
1409
1410 pub fn rustc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
1413 let mut cmd = command(self.rustc(compiler));
1414 self.add_rustc_lib_path(compiler, &mut cmd);
1415 cmd
1416 }
1417
1418 fn codegen_backends(&self, compiler: Compiler) -> impl Iterator<Item = PathBuf> {
1420 fs::read_dir(self.sysroot_codegen_backends(compiler))
1421 .into_iter()
1422 .flatten()
1423 .filter_map(Result::ok)
1424 .filter(|path| looks_like_codegen_backend(&path.path()))
1425 .map(|entry| entry.path())
1426 }
1427
1428 #[track_caller]
1432 pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf {
1433 self.ensure(tool::Rustdoc { target_compiler })
1434 }
1435
1436 pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
1437 assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
1438
1439 let compilers =
1440 RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target);
1441 assert_eq!(run_compiler, compilers.target_compiler());
1442
1443 let miri = self.ensure(tool::Miri::from_compilers(compilers));
1445 let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers));
1446 let mut cmd = command(cargo_miri.tool_path);
1448 cmd.env("MIRI", &miri.tool_path);
1449 cmd.env("CARGO", &self.initial_cargo);
1450 add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd);
1459 cmd
1460 }
1461
1462 pub fn cargo_clippy_cmd(&self, build_compiler: Compiler) -> BootstrapCommand {
1465 if build_compiler.stage == 0 {
1466 let cargo_clippy = self
1467 .config
1468 .initial_cargo_clippy
1469 .clone()
1470 .unwrap_or_else(|| self.build.config.download_clippy());
1471
1472 let mut cmd = command(cargo_clippy);
1473 cmd.env("CARGO", &self.initial_cargo);
1474 return cmd;
1475 }
1476
1477 let compilers = RustcPrivateCompilers::from_target_compiler(self, build_compiler);
1481
1482 let _ = self.ensure(tool::Clippy::from_compilers(compilers));
1483 let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers));
1484 let mut dylib_path = helpers::dylib_path();
1485 dylib_path.insert(0, self.sysroot(build_compiler).join("lib"));
1486
1487 let mut cmd = command(cargo_clippy.tool_path);
1488 cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
1489 cmd.env("CARGO", &self.initial_cargo);
1490 cmd
1491 }
1492
1493 pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
1494 let mut cmd = command(self.bootstrap_out.join("rustdoc"));
1495 cmd.env("RUSTC_STAGE", compiler.stage.to_string())
1496 .env("RUSTC_SYSROOT", self.sysroot(compiler))
1497 .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler))
1500 .env("CFG_RELEASE_CHANNEL", &self.config.channel)
1501 .env("RUSTDOC_REAL", self.rustdoc_for_compiler(compiler))
1502 .env("RUSTC_BOOTSTRAP", "1");
1503
1504 cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
1505
1506 if self.config.deny_warnings {
1507 cmd.arg("-Dwarnings");
1508 }
1509 cmd.arg("-Znormalize-docs");
1510 cmd.args(linker_args(self, compiler.host, LldThreads::Yes));
1511 cmd
1512 }
1513
1514 pub fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
1523 if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() {
1524 let llvm::LlvmOutput { host_llvm_config, .. } = self.ensure(llvm::Llvm { target });
1525 if host_llvm_config.is_file() {
1526 return Some(host_llvm_config);
1527 }
1528 }
1529 None
1530 }
1531
1532 pub fn require_and_update_all_submodules(&self) {
1535 for submodule in self.submodule_paths() {
1536 self.require_submodule(submodule, None);
1537 }
1538 }
1539
1540 pub fn submodule_paths(&self) -> &[String] {
1542 self.submodule_paths_cache.get_or_init(|| build_helper::util::parse_gitmodules(&self.src))
1543 }
1544
1545 #[track_caller]
1549 pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
1550 {
1551 let mut stack = self.stack.borrow_mut();
1552 for stack_step in stack.iter() {
1553 if stack_step.downcast_ref::<S>().is_none_or(|stack_step| *stack_step != step) {
1555 continue;
1556 }
1557 let mut out = String::new();
1558 out += &format!("\n\nCycle in build detected when adding {step:?}\n");
1559 for el in stack.iter().rev() {
1560 out += &format!("\t{el:?}\n");
1561 }
1562 panic!("{}", out);
1563 }
1564 if let Some(out) = self.cache.get(&step) {
1565 #[cfg(feature = "tracing")]
1566 {
1567 if let Some(parent) = stack.last() {
1568 let mut graph = self.build.step_graph.borrow_mut();
1569 graph.register_cached_step(&step, parent, self.config.dry_run());
1570 }
1571 }
1572 return out;
1573 }
1574
1575 #[cfg(feature = "tracing")]
1576 {
1577 let parent = stack.last();
1578 let mut graph = self.build.step_graph.borrow_mut();
1579 graph.register_step_execution(&step, parent, self.config.dry_run());
1580 }
1581
1582 let location = format_location(*std::panic::Location::caller());
1585 StepStack::with_current(|stack| {
1586 stack.push(StepRecord { info: pretty_print_step(&step), location });
1587 });
1588 stack.push(Box::new(step.clone()));
1589 }
1590
1591 #[cfg(feature = "build-metrics")]
1592 self.metrics.enter_step(&step, self);
1593
1594 if self.config.print_step_timings && !self.config.dry_run() {
1595 println!("[TIMING:start] {}", pretty_print_step(&step));
1596 }
1597
1598 let (out, dur) = {
1599 let start = Instant::now();
1600 let zero = Duration::new(0, 0);
1601 let parent = self.time_spent_on_dependencies.replace(zero);
1602
1603 #[cfg(feature = "tracing")]
1604 let _span = {
1605 let span = tracing::info_span!(
1607 target: STEP_SPAN_TARGET,
1608 "step",
1611 step_name = pretty_step_name::<S>(),
1612 args = step_debug_args(&step),
1613 location = format_location(*std::panic::Location::caller())
1614 );
1615 span.entered()
1616 };
1617
1618 let out = step.clone().run(self);
1619 let dur = start.elapsed();
1620 let deps = self.time_spent_on_dependencies.replace(parent + dur);
1621 (out, dur.saturating_sub(deps))
1622 };
1623
1624 if self.config.print_step_timings && !self.config.dry_run() {
1625 println!(
1626 "[TIMING:end] {} -- {}.{:03}",
1627 pretty_print_step(&step),
1628 dur.as_secs(),
1629 dur.subsec_millis()
1630 );
1631 }
1632
1633 #[cfg(feature = "build-metrics")]
1634 self.metrics.exit_step(self);
1635
1636 {
1637 let mut stack = self.stack.borrow_mut();
1638 let cur_step = stack.pop().expect("step stack empty");
1639 assert_eq!(cur_step.downcast_ref(), Some(&step));
1640
1641 StepStack::with_current(|stack| {
1642 stack.pop();
1643 });
1644 }
1645 self.cache.put(step, out.clone());
1646 out
1647 }
1648
1649 pub(crate) fn ensure_if_default<T, S: CommandLineStep<Output = T>>(
1653 &'a self,
1654 step: S,
1655 kind: Kind,
1656 ) -> Option<S::Output> {
1657 let desc = CommandLineStepDescription::from::<S>(kind);
1658 let should_run = (desc.should_run)(ShouldRun::new(self));
1659
1660 for pathset in &should_run.paths {
1662 if desc.is_excluded(self, pathset) {
1663 return None;
1664 }
1665 }
1666
1667 if (desc.is_default_step_fn)(self) { Some(self.ensure(step)) } else { None }
1669 }
1670
1671 pub(crate) fn was_invoked_explicitly<S: CommandLineStep>(&'a self, kind: Kind) -> bool {
1673 let desc = CommandLineStepDescription::from::<S>(kind);
1674 let should_run = (desc.should_run)(ShouldRun::new(self));
1675
1676 for path in &self.paths {
1677 if should_run.paths.iter().any(|s| s.has(path))
1678 && !desc.is_excluded(self, &PathSet::Suite(TaskPath { path: path.clone() }))
1679 {
1680 return true;
1681 }
1682 }
1683
1684 false
1685 }
1686
1687 pub(crate) fn maybe_open_in_browser<S: CommandLineStep>(&self, path: impl AsRef<Path>) {
1688 if self.was_invoked_explicitly::<S>(Kind::Doc) {
1689 self.open_in_browser(path);
1690 } else {
1691 self.info(&format!("Doc path: {}", path.as_ref().display()));
1692 }
1693 }
1694
1695 pub(crate) fn open_in_browser(&self, path: impl AsRef<Path>) {
1696 let path = path.as_ref();
1697
1698 if self.config.dry_run() || !self.config.cmd.open() {
1699 self.info(&format!("Doc path: {}", path.display()));
1700 return;
1701 }
1702
1703 self.info(&format!("Opening doc {}", path.display()));
1704 if let Err(err) = opener::open(path) {
1705 self.info(&format!("{err}\n"));
1706 }
1707 }
1708
1709 pub fn exec_ctx(&self) -> &ExecutionContext {
1710 &self.config.exec_ctx
1711 }
1712}
1713
1714pub fn pretty_step_name<S: Step>() -> String {
1716 let path = type_name::<S>().rsplit("::").take(2).collect::<Vec<_>>();
1718 path.into_iter().rev().collect::<Vec<_>>().join("::")
1719}
1720
1721fn step_debug_args<S: Step>(step: &S) -> String {
1723 let step_dbg_repr = format!("{step:?}");
1724
1725 match (step_dbg_repr.find('{'), step_dbg_repr.rfind('}')) {
1727 (Some(brace_start), Some(brace_end)) => {
1728 step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string()
1729 }
1730 _ => String::new(),
1731 }
1732}
1733
1734fn pretty_print_step<S: Step>(step: &S) -> String {
1735 format!("{} {{ {} }}", pretty_step_name::<S>(), step_debug_args(step))
1736}
1737
1738impl<'a> AsRef<ExecutionContext> for Builder<'a> {
1739 fn as_ref(&self) -> &ExecutionContext {
1740 self.exec_ctx()
1741 }
1742}