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