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};
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};
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::cli_paths::CLIStepPath;
24use crate::core::builder::step_stack::StepRecord;
25pub use crate::core::builder::step_stack::StepStack;
26use crate::core::config::flags::Subcommand;
27use crate::core::config::{DryRun, TargetSelection};
28use crate::utils::build_stamp::BuildStamp;
29use crate::utils::cache::Cache;
30use crate::utils::exec::{BootstrapCommand, ExecutionContext, command};
31use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t};
32use crate::utils::tracing::format_location;
33use crate::{Build, Crate, trace};
34
35mod cargo;
36mod cli_paths;
37mod step_stack;
38#[cfg(test)]
39mod tests;
40
41pub struct Builder<'a> {
44 pub build: &'a Build,
46
47 pub top_stage: u32,
51
52 pub kind: Kind,
54
55 cache: Cache,
58
59 stack: RefCell<Vec<Box<dyn AnyDebug>>>,
62
63 time_spent_on_dependencies: Cell<Duration>,
65
66 pub paths: Vec<PathBuf>,
70
71 submodule_paths_cache: OnceLock<Vec<String>>,
73
74 #[expect(clippy::type_complexity)]
78 log_cli_step_for_tests: Option<Box<dyn Fn(&StepDescription, &[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 {
105 type Output: Clone;
107
108 const IS_HOST: bool = false;
115
116 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>;
119
120 fn is_default_step(_builder: &Builder<'_>) -> bool {
134 false
135 }
136
137 fn run(self, builder: &Builder<'_>) -> Self::Output;
151
152 fn make_run(_run: RunConfig<'_>) {
156 unimplemented!()
161 }
162
163 fn metadata(&self) -> Option<StepMetadata> {
165 None
166 }
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
171pub struct StepMetadata {
172 name: String,
173 kind: Kind,
174 target: TargetSelection,
175 built_by: Option<Compiler>,
176 stage: Option<u32>,
177 metadata: Option<String>,
179}
180
181impl StepMetadata {
182 pub fn build(name: &str, target: TargetSelection) -> Self {
183 Self::new(name, target, Kind::Build)
184 }
185
186 pub fn check(name: &str, target: TargetSelection) -> Self {
187 Self::new(name, target, Kind::Check)
188 }
189
190 pub fn clippy(name: &str, target: TargetSelection) -> Self {
191 Self::new(name, target, Kind::Clippy)
192 }
193
194 pub fn doc(name: &str, target: TargetSelection) -> Self {
195 Self::new(name, target, Kind::Doc)
196 }
197
198 pub fn dist(name: &str, target: TargetSelection) -> Self {
199 Self::new(name, target, Kind::Dist)
200 }
201
202 pub fn test(name: &str, target: TargetSelection) -> Self {
203 Self::new(name, target, Kind::Test)
204 }
205
206 pub fn run(name: &str, target: TargetSelection) -> Self {
207 Self::new(name, target, Kind::Run)
208 }
209
210 fn new(name: &str, target: TargetSelection, kind: Kind) -> Self {
211 Self { name: name.to_string(), kind, target, built_by: None, stage: None, metadata: None }
212 }
213
214 pub fn built_by(mut self, compiler: Compiler) -> Self {
215 self.built_by = Some(compiler);
216 self
217 }
218
219 pub fn stage(mut self, stage: u32) -> Self {
220 self.stage = Some(stage);
221 self
222 }
223
224 pub fn with_metadata(mut self, metadata: String) -> Self {
225 self.metadata = Some(metadata);
226 self
227 }
228
229 pub fn get_stage(&self) -> Option<u32> {
230 self.stage.or(self
231 .built_by
232 .map(|compiler| if self.name == "std" { compiler.stage } else { compiler.stage + 1 }))
235 }
236
237 pub fn get_name(&self) -> &str {
238 &self.name
239 }
240
241 pub fn get_target(&self) -> TargetSelection {
242 self.target
243 }
244}
245
246pub struct RunConfig<'a> {
247 pub builder: &'a Builder<'a>,
248 pub target: TargetSelection,
249 pub paths: Vec<PathSet>,
250}
251
252impl RunConfig<'_> {
253 pub fn build_triple(&self) -> TargetSelection {
254 self.builder.build.host_target
255 }
256
257 #[track_caller]
259 pub fn cargo_crates_in_set(&self) -> Vec<String> {
260 let mut crates = Vec::new();
261 for krate in &self.paths {
262 let path = &krate.assert_single_path().path;
263
264 let crate_name = self
265 .builder
266 .crate_paths
267 .get(path)
268 .unwrap_or_else(|| panic!("missing crate for path {}", path.display()));
269
270 crates.push(crate_name.to_string());
271 }
272 crates
273 }
274
275 pub fn make_run_crates(&self, alias: Alias) -> Vec<String> {
282 let has_alias =
283 self.paths.iter().any(|set| set.assert_single_path().path.ends_with(alias.as_str()));
284 if !has_alias {
285 return self.cargo_crates_in_set();
286 }
287
288 let crates = match alias {
289 Alias::Library => self.builder.in_tree_crates("sysroot", Some(self.target)),
290 Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)),
291 };
292
293 crates.into_iter().map(|krate| krate.name.to_string()).collect()
294 }
295}
296
297#[derive(Debug, Copy, Clone)]
298pub enum Alias {
299 Library,
300 Compiler,
301}
302
303impl Alias {
304 fn as_str(self) -> &'static str {
305 match self {
306 Alias::Library => "library",
307 Alias::Compiler => "compiler",
308 }
309 }
310}
311
312pub fn crate_description(crates: &[impl AsRef<str>]) -> String {
316 if crates.is_empty() {
317 return "".into();
318 }
319
320 let mut descr = String::from("{");
321 descr.push_str(crates[0].as_ref());
322 for krate in &crates[1..] {
323 descr.push_str(", ");
324 descr.push_str(krate.as_ref());
325 }
326 descr.push('}');
327 descr
328}
329
330struct StepDescription {
331 is_host: bool,
332 should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>,
333 is_default_step_fn: fn(&Builder<'_>) -> bool,
334 make_run: fn(RunConfig<'_>),
335 name: &'static str,
336 kind: Kind,
337}
338
339#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
340pub struct TaskPath {
341 pub path: PathBuf,
342 pub kind: Option<Kind>,
343}
344
345impl Debug for TaskPath {
346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347 if let Some(kind) = &self.kind {
348 write!(f, "{}::", kind.as_str())?;
349 }
350 write!(f, "{}", self.path.display())
351 }
352}
353
354#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
356pub enum PathSet {
357 Set(BTreeSet<TaskPath>),
368 Suite(TaskPath),
375}
376
377impl PathSet {
378 fn empty() -> PathSet {
379 PathSet::Set(BTreeSet::new())
380 }
381
382 fn one<P: Into<PathBuf>>(path: P, kind: Kind) -> PathSet {
383 let mut set = BTreeSet::new();
384 set.insert(TaskPath { path: path.into(), kind: Some(kind) });
385 PathSet::Set(set)
386 }
387
388 fn has(&self, needle: &Path, module: Kind) -> bool {
389 match self {
390 PathSet::Set(set) => set.iter().any(|p| Self::check(p, needle, module)),
391 PathSet::Suite(suite) => Self::check(suite, needle, module),
392 }
393 }
394
395 fn check(p: &TaskPath, needle: &Path, module: Kind) -> bool {
397 let check_path = || {
398 p.path.ends_with(needle) || p.path.starts_with(needle)
400 };
401 if let Some(p_kind) = &p.kind { check_path() && *p_kind == module } else { check_path() }
402 }
403
404 fn intersection_removing_matches(&self, needles: &mut [CLIStepPath], module: Kind) -> PathSet {
411 let mut check = |p| {
412 let mut result = false;
413 for n in needles.iter_mut() {
414 let matched = Self::check(p, &n.path, module);
415 if matched {
416 n.will_be_executed = true;
417 result = true;
418 }
419 }
420 result
421 };
422 match self {
423 PathSet::Set(set) => PathSet::Set(set.iter().filter(|&p| check(p)).cloned().collect()),
424 PathSet::Suite(suite) => {
425 if check(suite) {
426 self.clone()
427 } else {
428 PathSet::empty()
429 }
430 }
431 }
432 }
433
434 #[track_caller]
438 pub fn assert_single_path(&self) -> &TaskPath {
439 match self {
440 PathSet::Set(set) => {
441 assert_eq!(set.len(), 1, "called assert_single_path on multiple paths");
442 set.iter().next().unwrap()
443 }
444 PathSet::Suite(_) => unreachable!("called assert_single_path on a Suite path"),
445 }
446 }
447}
448
449impl StepDescription {
450 fn from<S: Step>(kind: Kind) -> StepDescription {
451 StepDescription {
452 is_host: S::IS_HOST,
453 should_run: S::should_run,
454 is_default_step_fn: S::is_default_step,
455 make_run: S::make_run,
456 name: std::any::type_name::<S>(),
457 kind,
458 }
459 }
460
461 fn maybe_run(&self, builder: &Builder<'_>, mut pathsets: Vec<PathSet>) {
462 pathsets.retain(|set| !self.is_excluded(builder, set));
463
464 if pathsets.is_empty() {
465 return;
466 }
467
468 let targets = if self.is_host { &builder.hosts } else { &builder.targets };
470
471 if let Some(ref log_cli_step) = builder.log_cli_step_for_tests {
473 log_cli_step(self, &pathsets, targets);
474 return;
476 }
477
478 for target in targets {
479 let run = RunConfig { builder, paths: pathsets.clone(), target: *target };
480 (self.make_run)(run);
481 }
482 }
483
484 fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
485 if builder.config.skip.iter().any(|e| pathset.has(e, builder.kind)) {
486 if !matches!(builder.config.get_dry_run(), DryRun::SelfCheck) {
487 println!("Skipping {pathset:?} because it is excluded");
488 }
489 return true;
490 }
491
492 if !builder.config.skip.is_empty()
493 && !matches!(builder.config.get_dry_run(), DryRun::SelfCheck)
494 {
495 builder.do_if_verbose(|| {
496 println!(
497 "{:?} not skipped for {:?} -- not in {:?}",
498 pathset, self.name, builder.config.skip
499 )
500 });
501 }
502 false
503 }
504}
505
506pub struct ShouldRun<'a> {
513 pub builder: &'a Builder<'a>,
514 kind: Kind,
515
516 paths: BTreeSet<PathSet>,
518
519 default_to_suites_only: bool,
520}
521
522impl<'a> ShouldRun<'a> {
523 fn new(builder: &'a Builder<'_>, kind: Kind) -> ShouldRun<'a> {
524 ShouldRun { builder, kind, paths: BTreeSet::new(), default_to_suites_only: false }
525 }
526
527 pub(crate) fn crate_or_deps(self, root_crate_name: &str) -> Self {
532 self.crate_or_deps_filtered(root_crate_name, |_: &Crate| true)
533 }
534
535 pub(crate) fn crate_or_deps_filtered(
541 mut self,
542 root_crate_name: &str,
543 crate_filter_fn: impl Fn(&Crate) -> bool,
544 ) -> Self {
545 let crates = self.builder.in_tree_crates(root_crate_name, None);
546 for krate in crates {
547 if !crate_filter_fn(krate) {
548 continue;
549 }
550
551 let path = krate.local_path(self.builder);
552 self.paths.insert(PathSet::one(path, self.kind));
553 }
554 self
555 }
556
557 pub fn alias(mut self, alias: &str) -> Self {
559 assert!(
563 self.kind == Kind::Setup || !self.builder.src.join(alias).exists(),
564 "use `builder.path()` for real paths: {alias}"
565 );
566 self.paths.insert(PathSet::Set(
567 std::iter::once(TaskPath { path: alias.into(), kind: Some(self.kind) }).collect(),
568 ));
569 self
570 }
571
572 fn assert_valid_path(&self, path: &str) {
573 let submodules_paths = self.builder.submodule_paths();
574
575 if !submodules_paths.iter().any(|sm_p| path.contains(sm_p)) {
577 assert!(
578 self.builder.src.join(path).exists(),
579 "`should_run.path` should correspond to a real on-disk path - use `alias` if there is no relevant path: {path}"
580 );
581 }
582 }
583
584 pub fn path(mut self, path: &str) -> Self {
589 self.assert_valid_path(path);
590
591 let task = TaskPath { path: path.into(), kind: Some(self.kind) };
592 self.paths.insert(PathSet::Set(BTreeSet::from_iter([task])));
593 self
594 }
595
596 pub fn selectors(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(), kind: Some(self.kind) });
602 }
603 self.paths.insert(PathSet::Set(set));
604 self
605 }
606
607 fn is_suite_path(&self, requested_path: &Path) -> Option<&PathSet> {
609 self.paths.iter().find(|pathset| match pathset {
610 PathSet::Suite(suite) => requested_path.starts_with(&suite.path),
611 PathSet::Set(_) => false,
612 })
613 }
614
615 pub fn suite_path(mut self, suite: &str) -> Self {
616 self.paths.insert(PathSet::Suite(TaskPath { path: suite.into(), kind: Some(self.kind) }));
617 self
618 }
619
620 pub fn never(mut self) -> ShouldRun<'a> {
622 self.paths.insert(PathSet::empty());
623 self
624 }
625
626 fn pathset_for_paths_removing_matches(
636 &self,
637 paths: &mut [CLIStepPath],
638 kind: Kind,
639 ) -> Vec<PathSet> {
640 let mut sets = vec![];
641 for pathset in &self.paths {
642 let subset = pathset.intersection_removing_matches(paths, kind);
643 if subset != PathSet::empty() {
644 sets.push(subset);
645 }
646 }
647 sets
648 }
649
650 pub(crate) fn default_to_suites_only(mut self) -> Self {
658 self.default_to_suites_only = true;
659 self
660 }
661
662 fn default_pathsets(&self) -> Vec<PathSet> {
665 let mut default_pathsets = self.paths.iter().cloned().collect::<Vec<_>>();
666 if self.default_to_suites_only {
667 default_pathsets.retain(|p| matches!(p, PathSet::Suite(_)));
668 }
669 default_pathsets
670 }
671}
672
673#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord, ValueEnum)]
674pub enum Kind {
675 #[value(alias = "b")]
676 Build,
677 #[value(alias = "c")]
678 Check,
679 Clippy,
680 Fix,
681 Format,
682 #[value(alias = "t")]
683 Test,
684 Miri,
685 MiriSetup,
686 MiriTest,
687 Bench,
688 #[value(alias = "d")]
689 Doc,
690 Clean,
691 Dist,
692 Install,
693 #[value(alias = "r")]
694 Run,
695 Setup,
696 Vendor,
697 Perf,
698}
699
700impl Kind {
701 pub fn as_str(&self) -> &'static str {
702 match self {
703 Kind::Build => "build",
704 Kind::Check => "check",
705 Kind::Clippy => "clippy",
706 Kind::Fix => "fix",
707 Kind::Format => "fmt",
708 Kind::Test => "test",
709 Kind::Miri => "miri",
710 Kind::MiriSetup => panic!("`as_str` is not supported for `Kind::MiriSetup`."),
711 Kind::MiriTest => panic!("`as_str` is not supported for `Kind::MiriTest`."),
712 Kind::Bench => "bench",
713 Kind::Doc => "doc",
714 Kind::Clean => "clean",
715 Kind::Dist => "dist",
716 Kind::Install => "install",
717 Kind::Run => "run",
718 Kind::Setup => "setup",
719 Kind::Vendor => "vendor",
720 Kind::Perf => "perf",
721 }
722 }
723
724 pub fn description(&self) -> String {
725 match self {
726 Kind::Test => "Testing",
727 Kind::Bench => "Benchmarking",
728 Kind::Doc => "Documenting",
729 Kind::Run => "Running",
730 Kind::Clippy => "Linting",
731 Kind::Perf => "Profiling & benchmarking",
732 _ => {
733 let title_letter = self.as_str()[0..1].to_ascii_uppercase();
734 return format!("{title_letter}{}ing", &self.as_str()[1..]);
735 }
736 }
737 .to_owned()
738 }
739}
740
741#[derive(Debug, Clone, Hash, PartialEq, Eq)]
742struct Libdir {
743 compiler: Compiler,
744 target: TargetSelection,
745}
746
747impl Step for Libdir {
748 type Output = PathBuf;
749
750 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
751 run.never()
752 }
753
754 fn run(self, builder: &Builder<'_>) -> PathBuf {
755 let relative_sysroot_libdir = builder.sysroot_libdir_relative(self.compiler);
756 let sysroot = builder.sysroot(self.compiler).join(relative_sysroot_libdir).join("rustlib");
757
758 if !builder.config.dry_run() {
759 if !builder.download_rustc() {
762 let sysroot_target_libdir = sysroot.join(self.target).join("lib");
763 builder.do_if_verbose(|| {
764 eprintln!(
765 "Removing sysroot {} to avoid caching bugs",
766 sysroot_target_libdir.display()
767 )
768 });
769 let _ = fs::remove_dir_all(&sysroot_target_libdir);
770 t!(fs::create_dir_all(&sysroot_target_libdir));
771 }
772
773 if self.compiler.stage == 0 {
774 dist::maybe_install_llvm_target(
778 builder,
779 self.compiler.host,
780 &builder.sysroot(self.compiler),
781 );
782 }
783 }
784
785 sysroot
786 }
787}
788
789#[cfg(feature = "tracing")]
790pub const STEP_SPAN_TARGET: &str = "STEP";
791
792impl<'a> Builder<'a> {
793 fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
794 macro_rules! describe {
795 ($($rule:ty),+ $(,)?) => {{
796 vec![$(StepDescription::from::<$rule>(kind)),+]
797 }};
798 }
799 match kind {
800 Kind::Build => describe!(
801 compile::Std,
802 compile::Rustc,
803 compile::Assemble,
804 compile::CraneliftCodegenBackend,
805 compile::GccCodegenBackend,
806 compile::StartupObjects,
807 tool::BuildManifest,
808 tool::Rustbook,
809 tool::ErrorIndex,
810 tool::UnstableBookGen,
811 tool::Tidy,
812 tool::Linkchecker,
813 tool::CargoTest,
814 tool::Compiletest,
815 tool::RemoteTestServer,
816 tool::RemoteTestClient,
817 tool::RustInstaller,
818 tool::FeaturesStatusDump,
819 tool::Cargo,
820 tool::RustAnalyzer,
821 tool::RustAnalyzerProcMacroSrv,
822 tool::Rustdoc,
823 tool::Clippy,
824 tool::CargoClippy,
825 llvm::Llvm,
826 gcc::Gcc,
827 llvm::Sanitizers,
828 tool::Rustfmt,
829 tool::Cargofmt,
830 tool::Miri,
831 tool::CargoMiri,
832 llvm::Lld,
833 llvm::Enzyme,
834 llvm::CrtBeginEnd,
835 tool::RustdocGUITest,
836 tool::OptimizedDist,
837 tool::CoverageDump,
838 tool::LlvmBitcodeLinker,
839 tool::RustcPerf,
840 tool::WasmComponentLd,
841 tool::LldWrapper
842 ),
843 Kind::Clippy => describe!(
844 clippy::Std,
845 clippy::Rustc,
846 clippy::Bootstrap,
847 clippy::BuildHelper,
848 clippy::BuildManifest,
849 clippy::CargoMiri,
850 clippy::Clippy,
851 clippy::CodegenGcc,
852 clippy::CollectLicenseMetadata,
853 clippy::Compiletest,
854 clippy::CoverageDump,
855 clippy::Jsondocck,
856 clippy::Jsondoclint,
857 clippy::LintDocs,
858 clippy::LlvmBitcodeLinker,
859 clippy::Miri,
860 clippy::MiroptTestTools,
861 clippy::OptDist,
862 clippy::RemoteTestClient,
863 clippy::RemoteTestServer,
864 clippy::RustAnalyzer,
865 clippy::Rustdoc,
866 clippy::Rustfmt,
867 clippy::RustInstaller,
868 clippy::TestFloatParse,
869 clippy::Tidy,
870 clippy::CI,
871 ),
872 Kind::Check | Kind::Fix => describe!(
873 check::Rustc,
874 check::Rustdoc,
875 check::CraneliftCodegenBackend,
876 check::GccCodegenBackend,
877 check::Clippy,
878 check::Miri,
879 check::CargoMiri,
880 check::MiroptTestTools,
881 check::Rustfmt,
882 check::RustAnalyzer,
883 check::TestFloatParse,
884 check::Bootstrap,
885 check::RunMakeSupport,
886 check::Compiletest,
887 check::RustdocGuiTest,
888 check::FeaturesStatusDump,
889 check::CoverageDump,
890 check::Linkchecker,
891 check::BumpStage0,
892 check::Tidy,
893 check::Std,
900 ),
901 Kind::Test => describe!(
902 crate::core::build_steps::toolstate::ToolStateCheck,
903 test::Tidy,
904 test::BootstrapPy,
905 test::Bootstrap,
906 test::Ui,
907 test::Crashes,
908 test::Coverage,
909 test::MirOpt,
910 test::CodegenLlvm,
911 test::CodegenUnits,
912 test::AssemblyLlvm,
913 test::Incremental,
914 test::Debuginfo,
915 test::UiFullDeps,
916 test::RustdocHtml,
917 test::CoverageRunRustdoc,
918 test::Pretty,
919 test::CodegenCranelift,
920 test::CodegenGCC,
921 test::Crate,
922 test::CrateLibrustc,
923 test::CrateRustdoc,
924 test::CrateRustdocJsonTypes,
925 test::CrateBootstrap,
926 test::RemoteTestClientTests,
927 test::Linkcheck,
928 test::TierCheck,
929 test::Cargotest,
930 test::Cargo,
931 test::RustAnalyzer,
932 test::ErrorIndex,
933 test::Distcheck,
934 test::Nomicon,
935 test::Reference,
936 test::RustdocBook,
937 test::RustByExample,
938 test::TheBook,
939 test::UnstableBook,
940 test::RustcBook,
941 test::LintDocs,
942 test::EmbeddedBook,
943 test::EditionGuide,
944 test::Rustfmt,
945 test::Miri,
946 test::CargoMiri,
947 test::Clippy,
948 test::CompiletestTest,
949 test::StdarchVerify,
950 test::IntrinsicTest,
951 test::CrateRunMakeSupport,
952 test::CrateBuildHelper,
953 test::RustdocJSStd,
954 test::RustdocJSNotStd,
955 test::RustdocGUI,
956 test::RustdocTheme,
957 test::RustdocUi,
958 test::RustdocJson,
959 test::HtmlCheck,
960 test::RustInstaller,
961 test::TestFloatParse,
962 test::CollectLicenseMetadata,
963 test::RunMake,
964 test::RunMakeCargo,
965 test::BuildStd,
966 ),
967 Kind::Miri => describe!(test::Crate),
968 Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc),
969 Kind::Doc => describe!(
970 doc::UnstableBook,
971 doc::UnstableBookGen,
972 doc::TheBook,
973 doc::Standalone,
974 doc::Std,
975 doc::Rustc,
976 doc::Rustdoc,
977 doc::Rustfmt,
978 doc::ErrorIndex,
979 doc::Nomicon,
980 doc::Reference,
981 doc::RustdocBook,
982 doc::RustByExample,
983 doc::RustcBook,
984 doc::Cargo,
985 doc::CargoBook,
986 doc::Clippy,
987 doc::ClippyBook,
988 doc::Miri,
989 doc::EmbeddedBook,
990 doc::EditionGuide,
991 doc::StyleGuide,
992 doc::Tidy,
993 doc::Bootstrap,
994 doc::Releases,
995 doc::RunMakeSupport,
996 doc::BuildHelper,
997 doc::Compiletest,
998 ),
999 Kind::Dist => describe!(
1000 dist::Docs,
1001 dist::RustcDocs,
1002 dist::JsonDocs,
1003 dist::Mingw,
1004 dist::Rustc,
1005 dist::CraneliftCodegenBackend,
1006 dist::GccCodegenBackend,
1007 dist::Std,
1008 dist::RustcDev,
1009 dist::Analysis,
1010 dist::Src,
1011 dist::Cargo,
1012 dist::RustAnalyzer,
1013 dist::Rustfmt,
1014 dist::Clippy,
1015 dist::Miri,
1016 dist::LlvmTools,
1017 dist::LlvmBitcodeLinker,
1018 dist::RustDev,
1019 dist::Enzyme,
1020 dist::Bootstrap,
1021 dist::Extended,
1022 dist::PlainSourceTarball,
1027 dist::PlainSourceTarballGpl,
1028 dist::BuildManifest,
1029 dist::ReproducibleArtifacts,
1030 dist::GccDev,
1031 dist::Gcc
1032 ),
1033 Kind::Install => describe!(
1034 install::Docs,
1035 install::Std,
1036 install::Rustc,
1041 install::RustcDev,
1042 install::Cargo,
1043 install::RustAnalyzer,
1044 install::Rustfmt,
1045 install::Clippy,
1046 install::Miri,
1047 install::LlvmTools,
1048 install::Src,
1049 install::RustcCodegenCranelift,
1050 install::LlvmBitcodeLinker
1051 ),
1052 Kind::Run => describe!(
1053 run::BuildManifest,
1054 run::BumpStage0,
1055 run::ReplaceVersionPlaceholder,
1056 run::Miri,
1057 run::CollectLicenseMetadata,
1058 run::GenerateCopyright,
1059 run::GenerateWindowsSys,
1060 run::GenerateCompletions,
1061 run::UnicodeTableGenerator,
1062 run::FeaturesStatusDump,
1063 run::CyclicStep,
1064 run::CoverageDump,
1065 run::Rustfmt,
1066 run::GenerateHelp,
1067 ),
1068 Kind::Setup => {
1069 describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
1070 }
1071 Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
1072 Kind::Vendor => describe!(vendor::Vendor),
1073 Kind::Format | Kind::Perf => vec![],
1075 Kind::MiriTest | Kind::MiriSetup => unreachable!(),
1076 }
1077 }
1078
1079 pub fn get_help(build: &Build, kind: Kind) -> Option<String> {
1080 let step_descriptions = Builder::get_step_descriptions(kind);
1081 if step_descriptions.is_empty() {
1082 return None;
1083 }
1084
1085 let builder = Self::new_internal(build, kind, vec![]);
1086 let builder = &builder;
1087 let mut should_run = ShouldRun::new(builder, Kind::Build);
1090 for desc in step_descriptions {
1091 should_run.kind = desc.kind;
1092 should_run = (desc.should_run)(should_run);
1093 }
1094 let mut help = String::from("Available paths:\n");
1095 let mut add_path = |path: &Path| {
1096 t!(write!(help, " ./x.py {} {}\n", kind.as_str(), path.display()));
1097 };
1098 for pathset in should_run.paths {
1099 match pathset {
1100 PathSet::Set(set) => {
1101 for path in set {
1102 add_path(&path.path);
1103 }
1104 }
1105 PathSet::Suite(path) => {
1106 add_path(&path.path.join("..."));
1107 }
1108 }
1109 }
1110 Some(help)
1111 }
1112
1113 fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
1114 Builder {
1115 build,
1116 top_stage: build.config.stage,
1117 kind,
1118 cache: Cache::new(),
1119 stack: RefCell::new(Vec::new()),
1120 time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
1121 paths,
1122 submodule_paths_cache: Default::default(),
1123 log_cli_step_for_tests: None,
1124 }
1125 }
1126
1127 pub fn new(build: &Build) -> Builder<'_> {
1128 let paths = &build.config.paths;
1129 let (kind, paths) = match build.config.cmd {
1130 Subcommand::Build { .. } => (Kind::Build, &paths[..]),
1131 Subcommand::Check { .. } => (Kind::Check, &paths[..]),
1132 Subcommand::Clippy { .. } => (Kind::Clippy, &paths[..]),
1133 Subcommand::Fix => (Kind::Fix, &paths[..]),
1134 Subcommand::Doc { .. } => (Kind::Doc, &paths[..]),
1135 Subcommand::Test { .. } => (Kind::Test, &paths[..]),
1136 Subcommand::Miri { .. } => (Kind::Miri, &paths[..]),
1137 Subcommand::Bench { .. } => (Kind::Bench, &paths[..]),
1138 Subcommand::Dist => (Kind::Dist, &paths[..]),
1139 Subcommand::Install => (Kind::Install, &paths[..]),
1140 Subcommand::Run { .. } => (Kind::Run, &paths[..]),
1141 Subcommand::Clean { .. } => (Kind::Clean, &paths[..]),
1142 Subcommand::Format { .. } => (Kind::Format, &[][..]),
1143 Subcommand::Setup { profile: ref path } => (
1144 Kind::Setup,
1145 path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
1146 ),
1147 Subcommand::Vendor { .. } => (Kind::Vendor, &paths[..]),
1148 Subcommand::Perf { .. } => (Kind::Perf, &paths[..]),
1149 };
1150
1151 StepStack::with_current(|stack| stack.clear());
1152 Self::new_internal(build, kind, paths.to_owned())
1153 }
1154
1155 pub fn execute_cli(&self) {
1156 self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
1157 }
1158
1159 pub fn run_default_doc_steps(&self) {
1161 self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
1162 }
1163
1164 pub fn doc_rust_lang_org_channel(&self) -> String {
1165 let channel = match &*self.config.channel {
1166 "stable" => &self.version,
1167 "beta" => "beta",
1168 "nightly" | "dev" => "nightly",
1169 _ => "stable",
1171 };
1172
1173 format!("https://doc.rust-lang.org/{channel}")
1174 }
1175
1176 fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) {
1177 cli_paths::match_paths_to_steps_and_run(self, v, paths);
1178 }
1179
1180 pub fn link_std_into_rustc_driver(&self, target: TargetSelection) -> bool {
1183 !target.triple.ends_with("-windows-gnu")
1184 }
1185
1186 #[track_caller]
1191 #[cfg_attr(
1192 feature = "tracing",
1193 instrument(
1194 level = "trace",
1195 name = "Builder::compiler",
1196 target = "COMPILER",
1197 skip_all,
1198 fields(
1199 stage = stage,
1200 host = ?host,
1201 ),
1202 ),
1203 )]
1204 pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler {
1205 self.ensure(compile::Assemble { target_compiler: Compiler::new(stage, host) })
1206 }
1207
1208 #[track_caller]
1225 pub fn compiler_for_std(&self, stage: u32) -> Compiler {
1226 if compile::Std::should_be_uplifted_from_stage_1(self, stage) {
1227 self.compiler(1, self.host_target)
1228 } else {
1229 self.compiler(stage, self.host_target)
1230 }
1231 }
1232
1233 #[track_caller]
1245 #[cfg_attr(
1246 feature = "tracing",
1247 instrument(
1248 level = "trace",
1249 name = "Builder::compiler_for",
1250 target = "COMPILER_FOR",
1251 skip_all,
1252 fields(
1253 stage = stage,
1254 host = ?host,
1255 target = ?target,
1256 ),
1257 ),
1258 )]
1259 pub fn compiler_for(
1262 &self,
1263 stage: u32,
1264 host: TargetSelection,
1265 target: TargetSelection,
1266 ) -> Compiler {
1267 let mut resolved_compiler = if self.build.force_use_stage2(stage) {
1268 trace!(target: "COMPILER_FOR", ?stage, "force_use_stage2");
1269 self.compiler(2, self.config.host_target)
1270 } else if self.build.force_use_stage1(stage, target) {
1271 trace!(target: "COMPILER_FOR", ?stage, "force_use_stage1");
1272 self.compiler(1, self.config.host_target)
1273 } else {
1274 trace!(target: "COMPILER_FOR", ?stage, ?host, "no force, fallback to `compiler()`");
1275 self.compiler(stage, host)
1276 };
1277
1278 if stage != resolved_compiler.stage {
1279 resolved_compiler.forced_compiler(true);
1280 }
1281
1282 trace!(target: "COMPILER_FOR", ?resolved_compiler);
1283 resolved_compiler
1284 }
1285
1286 #[track_caller]
1293 #[cfg_attr(
1294 feature = "tracing",
1295 instrument(
1296 level = "trace",
1297 name = "Builder::std",
1298 target = "STD",
1299 skip_all,
1300 fields(
1301 compiler = ?compiler,
1302 target = ?target,
1303 ),
1304 ),
1305 )]
1306 pub fn std(&self, compiler: Compiler, target: TargetSelection) -> Option<BuildStamp> {
1307 if compiler.stage == 0 {
1317 if target != compiler.host {
1318 if self.local_rebuild {
1319 self.ensure(Std::new(compiler, target))
1320 } else {
1321 panic!(
1322 r"It is not possible to build the standard library for `{target}` using the stage0 compiler.
1323You have to build a stage1 compiler for `{}` first, and then use it to build a standard library for `{target}`.
1324Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler built from in-tree sources.
1325",
1326 compiler.host
1327 )
1328 }
1329 } else {
1330 self.ensure(StdLink::from_std(Std::new(compiler, target), compiler));
1332 None
1333 }
1334 } else {
1335 self.ensure(Std::new(compiler, target))
1338 }
1339 }
1340
1341 #[track_caller]
1342 pub fn sysroot(&self, compiler: Compiler) -> PathBuf {
1343 self.ensure(compile::Sysroot::new(compiler))
1344 }
1345
1346 #[track_caller]
1348 pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf {
1349 self.ensure(Libdir { compiler, target }).join(target).join("bin")
1350 }
1351
1352 #[track_caller]
1355 pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf {
1356 self.ensure(Libdir { compiler, target }).join(target).join("lib")
1357 }
1358
1359 pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf {
1360 self.sysroot_target_libdir(compiler, compiler.host).with_file_name("codegen-backends")
1361 }
1362
1363 pub fn rustc_libdir(&self, compiler: Compiler) -> PathBuf {
1369 if compiler.is_snapshot(self) {
1370 self.rustc_snapshot_libdir()
1371 } else {
1372 match self.config.libdir_relative() {
1373 Some(relative_libdir) if compiler.stage >= 1 => {
1374 self.sysroot(compiler).join(relative_libdir)
1375 }
1376 _ => self.sysroot(compiler).join(libdir(compiler.host)),
1377 }
1378 }
1379 }
1380
1381 pub fn libdir_relative(&self, compiler: Compiler) -> &Path {
1387 if compiler.is_snapshot(self) {
1388 libdir(self.config.host_target).as_ref()
1389 } else {
1390 match self.config.libdir_relative() {
1391 Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1392 _ => libdir(compiler.host).as_ref(),
1393 }
1394 }
1395 }
1396
1397 pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path {
1402 match self.config.libdir_relative() {
1403 Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1404 _ if compiler.stage == 0 => &self.build.initial_relative_libdir,
1405 _ => Path::new("lib"),
1406 }
1407 }
1408
1409 pub fn rustc_lib_paths(&self, compiler: Compiler) -> Vec<PathBuf> {
1410 let mut dylib_dirs = vec![self.rustc_libdir(compiler)];
1411
1412 if self.config.llvm_from_ci {
1414 let ci_llvm_lib = self.out.join(compiler.host).join("ci-llvm").join("lib");
1415 dylib_dirs.push(ci_llvm_lib);
1416 }
1417
1418 dylib_dirs
1419 }
1420
1421 pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut BootstrapCommand) {
1424 if cfg!(any(windows, target_os = "cygwin")) {
1428 return;
1429 }
1430
1431 add_dylib_path(self.rustc_lib_paths(compiler), cmd);
1432 }
1433
1434 pub fn rustc(&self, compiler: Compiler) -> PathBuf {
1436 if compiler.is_snapshot(self) {
1437 self.initial_rustc.clone()
1438 } else {
1439 self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
1440 }
1441 }
1442
1443 pub fn rustc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
1446 let mut cmd = command(self.rustc(compiler));
1447 self.add_rustc_lib_path(compiler, &mut cmd);
1448 cmd
1449 }
1450
1451 fn codegen_backends(&self, compiler: Compiler) -> impl Iterator<Item = PathBuf> {
1453 fs::read_dir(self.sysroot_codegen_backends(compiler))
1454 .into_iter()
1455 .flatten()
1456 .filter_map(Result::ok)
1457 .map(|entry| entry.path())
1458 }
1459
1460 #[track_caller]
1464 pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf {
1465 self.ensure(tool::Rustdoc { target_compiler })
1466 }
1467
1468 pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
1469 assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
1470
1471 let compilers =
1472 RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target);
1473 assert_eq!(run_compiler, compilers.target_compiler());
1474
1475 let miri = self.ensure(tool::Miri::from_compilers(compilers));
1477 let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers));
1478 let mut cmd = command(cargo_miri.tool_path);
1480 cmd.env("MIRI", &miri.tool_path);
1481 cmd.env("CARGO", &self.initial_cargo);
1482 add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd);
1491 cmd
1492 }
1493
1494 pub fn cargo_clippy_cmd(&self, build_compiler: Compiler) -> BootstrapCommand {
1497 if build_compiler.stage == 0 {
1498 let cargo_clippy = self
1499 .config
1500 .initial_cargo_clippy
1501 .clone()
1502 .unwrap_or_else(|| self.build.config.download_clippy());
1503
1504 let mut cmd = command(cargo_clippy);
1505 cmd.env("CARGO", &self.initial_cargo);
1506 return cmd;
1507 }
1508
1509 let compilers = RustcPrivateCompilers::from_target_compiler(self, build_compiler);
1513
1514 let _ = self.ensure(tool::Clippy::from_compilers(compilers));
1515 let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers));
1516 let mut dylib_path = helpers::dylib_path();
1517 dylib_path.insert(0, self.sysroot(build_compiler).join("lib"));
1518
1519 let mut cmd = command(cargo_clippy.tool_path);
1520 cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
1521 cmd.env("CARGO", &self.initial_cargo);
1522 cmd
1523 }
1524
1525 pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
1526 let mut cmd = command(self.bootstrap_out.join("rustdoc"));
1527 cmd.env("RUSTC_STAGE", compiler.stage.to_string())
1528 .env("RUSTC_SYSROOT", self.sysroot(compiler))
1529 .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler))
1532 .env("CFG_RELEASE_CHANNEL", &self.config.channel)
1533 .env("RUSTDOC_REAL", self.rustdoc_for_compiler(compiler))
1534 .env("RUSTC_BOOTSTRAP", "1");
1535
1536 cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
1537
1538 if self.config.deny_warnings {
1539 cmd.arg("-Dwarnings");
1540 }
1541 cmd.arg("-Znormalize-docs");
1542 cmd.args(linker_args(self, compiler.host, LldThreads::Yes));
1543 cmd
1544 }
1545
1546 pub fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
1555 if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() {
1556 let llvm::LlvmResult { host_llvm_config, .. } = self.ensure(llvm::Llvm { target });
1557 if host_llvm_config.is_file() {
1558 return Some(host_llvm_config);
1559 }
1560 }
1561 None
1562 }
1563
1564 pub fn require_and_update_all_submodules(&self) {
1567 for submodule in self.submodule_paths() {
1568 self.require_submodule(submodule, None);
1569 }
1570 }
1571
1572 pub fn submodule_paths(&self) -> &[String] {
1574 self.submodule_paths_cache.get_or_init(|| build_helper::util::parse_gitmodules(&self.src))
1575 }
1576
1577 #[track_caller]
1581 pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
1582 {
1583 let mut stack = self.stack.borrow_mut();
1584 for stack_step in stack.iter() {
1585 if stack_step.downcast_ref::<S>().is_none_or(|stack_step| *stack_step != step) {
1587 continue;
1588 }
1589 let mut out = String::new();
1590 out += &format!("\n\nCycle in build detected when adding {step:?}\n");
1591 for el in stack.iter().rev() {
1592 out += &format!("\t{el:?}\n");
1593 }
1594 panic!("{}", out);
1595 }
1596 if let Some(out) = self.cache.get(&step) {
1597 #[cfg(feature = "tracing")]
1598 {
1599 if let Some(parent) = stack.last() {
1600 let mut graph = self.build.step_graph.borrow_mut();
1601 graph.register_cached_step(&step, parent, self.config.dry_run());
1602 }
1603 }
1604 return out;
1605 }
1606
1607 #[cfg(feature = "tracing")]
1608 {
1609 let parent = stack.last();
1610 let mut graph = self.build.step_graph.borrow_mut();
1611 graph.register_step_execution(&step, parent, self.config.dry_run());
1612 }
1613
1614 let location = format_location(*std::panic::Location::caller());
1617 StepStack::with_current(|stack| {
1618 stack.push(StepRecord { info: pretty_print_step(&step), location });
1619 });
1620 stack.push(Box::new(step.clone()));
1621 }
1622
1623 #[cfg(feature = "build-metrics")]
1624 self.metrics.enter_step(&step, self);
1625
1626 if self.config.print_step_timings && !self.config.dry_run() {
1627 println!("[TIMING:start] {}", pretty_print_step(&step));
1628 }
1629
1630 let (out, dur) = {
1631 let start = Instant::now();
1632 let zero = Duration::new(0, 0);
1633 let parent = self.time_spent_on_dependencies.replace(zero);
1634
1635 #[cfg(feature = "tracing")]
1636 let _span = {
1637 let span = tracing::info_span!(
1639 target: STEP_SPAN_TARGET,
1640 "step",
1643 step_name = pretty_step_name::<S>(),
1644 args = step_debug_args(&step),
1645 location = format_location(*std::panic::Location::caller())
1646 );
1647 span.entered()
1648 };
1649
1650 let out = step.clone().run(self);
1651 let dur = start.elapsed();
1652 let deps = self.time_spent_on_dependencies.replace(parent + dur);
1653 (out, dur.saturating_sub(deps))
1654 };
1655
1656 if self.config.print_step_timings && !self.config.dry_run() {
1657 println!(
1658 "[TIMING:end] {} -- {}.{:03}",
1659 pretty_print_step(&step),
1660 dur.as_secs(),
1661 dur.subsec_millis()
1662 );
1663 }
1664
1665 #[cfg(feature = "build-metrics")]
1666 self.metrics.exit_step(self);
1667
1668 {
1669 let mut stack = self.stack.borrow_mut();
1670 let cur_step = stack.pop().expect("step stack empty");
1671 assert_eq!(cur_step.downcast_ref(), Some(&step));
1672
1673 StepStack::with_current(|stack| {
1674 stack.pop();
1675 });
1676 }
1677 self.cache.put(step, out.clone());
1678 out
1679 }
1680
1681 pub(crate) fn ensure_if_default<T, S: Step<Output = T>>(
1685 &'a self,
1686 step: S,
1687 kind: Kind,
1688 ) -> Option<S::Output> {
1689 let desc = StepDescription::from::<S>(kind);
1690 let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
1691
1692 for pathset in &should_run.paths {
1694 if desc.is_excluded(self, pathset) {
1695 return None;
1696 }
1697 }
1698
1699 if (desc.is_default_step_fn)(self) { Some(self.ensure(step)) } else { None }
1701 }
1702
1703 pub(crate) fn was_invoked_explicitly<S: Step>(&'a self, kind: Kind) -> bool {
1705 let desc = StepDescription::from::<S>(kind);
1706 let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind));
1707
1708 for path in &self.paths {
1709 if should_run.paths.iter().any(|s| s.has(path, desc.kind))
1710 && !desc.is_excluded(
1711 self,
1712 &PathSet::Suite(TaskPath { path: path.clone(), kind: Some(desc.kind) }),
1713 )
1714 {
1715 return true;
1716 }
1717 }
1718
1719 false
1720 }
1721
1722 pub(crate) fn maybe_open_in_browser<S: Step>(&self, path: impl AsRef<Path>) {
1723 if self.was_invoked_explicitly::<S>(Kind::Doc) {
1724 self.open_in_browser(path);
1725 } else {
1726 self.info(&format!("Doc path: {}", path.as_ref().display()));
1727 }
1728 }
1729
1730 pub(crate) fn open_in_browser(&self, path: impl AsRef<Path>) {
1731 let path = path.as_ref();
1732
1733 if self.config.dry_run() || !self.config.cmd.open() {
1734 self.info(&format!("Doc path: {}", path.display()));
1735 return;
1736 }
1737
1738 self.info(&format!("Opening doc {}", path.display()));
1739 if let Err(err) = opener::open(path) {
1740 self.info(&format!("{err}\n"));
1741 }
1742 }
1743
1744 pub fn exec_ctx(&self) -> &ExecutionContext {
1745 &self.config.exec_ctx
1746 }
1747}
1748
1749pub fn pretty_step_name<S: Step>() -> String {
1751 let path = type_name::<S>().rsplit("::").take(2).collect::<Vec<_>>();
1753 path.into_iter().rev().collect::<Vec<_>>().join("::")
1754}
1755
1756fn step_debug_args<S: Step>(step: &S) -> String {
1758 let step_dbg_repr = format!("{step:?}");
1759
1760 match (step_dbg_repr.find('{'), step_dbg_repr.rfind('}')) {
1762 (Some(brace_start), Some(brace_end)) => {
1763 step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string()
1764 }
1765 _ => String::new(),
1766 }
1767}
1768
1769fn pretty_print_step<S: Step>(step: &S) -> String {
1770 format!("{} {{ {} }}", pretty_step_name::<S>(), step_debug_args(step))
1771}
1772
1773impl<'a> AsRef<ExecutionContext> for Builder<'a> {
1774 fn as_ref(&self) -> &ExecutionContext {
1775 self.exec_ctx()
1776 }
1777}