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