1use std::any::{Any, type_name};
2use std::cell::{Cell, RefCell};
3use std::collections::BTreeSet;
4use std::fmt::{Debug, Write};
5use std::hash::Hash;
6use std::ops::Deref;
7use std::path::{Path, PathBuf};
8use std::sync::OnceLock;
9use std::time::{Duration, Instant};
10use std::{env, fs, iter};
11
12use clap::ValueEnum;
13#[cfg(feature = "tracing")]
14use tracing::instrument;
15
16pub use self::cargo::{Cargo, apply_pgo, cargo_profile_var};
17pub use crate::Compiler;
18use crate::core::build_steps::compile::{Std, StdLink, looks_like_codegen_backend};
19use crate::core::build_steps::tool::RustcPrivateCompilers;
20use crate::core::build_steps::{
21 check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
22};
23use crate::core::builder::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:
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 trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
112 type Output: Clone;
114
115 fn run(self, builder: &Builder<'_>) -> Self::Output;
119
120 fn metadata(&self) -> Option<StepMetadata> {
122 None
123 }
124}
125
126impl<S: CommandLineStep> Step for S {
128 type Output = <S as CommandLineStep>::Output;
129
130 fn run(self, builder: &Builder<'_>) -> Self::Output {
131 <S as CommandLineStep>::run(self, builder)
132 }
133
134 fn metadata(&self) -> Option<StepMetadata> {
135 <S as CommandLineStep>::metadata(self)
136 }
137}
138
139pub trait CommandLineStep: 'static + Clone + Debug + PartialEq + Eq + Hash {
145 type Output: Clone;
147
148 const IS_HOST: bool = false;
155
156 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>;
159
160 fn is_default_step(_builder: &Builder<'_>) -> bool {
174 false
175 }
176
177 fn make_run(_run: RunConfig<'_>);
181
182 fn run(self, builder: &Builder<'_>) -> Self::Output;
184
185 fn metadata(&self) -> Option<StepMetadata> {
187 None
188 }
189}
190
191#[derive(Clone, Debug, PartialEq, Eq)]
193pub struct StepMetadata {
194 name: String,
195 kind: Kind,
196 target: TargetSelection,
197 built_by: Option<Compiler>,
198 stage: Option<u32>,
199 metadata: Option<String>,
201}
202
203impl StepMetadata {
204 pub fn build(name: &str, target: TargetSelection) -> Self {
205 Self::new(name, target, Kind::Build)
206 }
207
208 pub fn check(name: &str, target: TargetSelection) -> Self {
209 Self::new(name, target, Kind::Check)
210 }
211
212 pub fn clippy(name: &str, target: TargetSelection) -> Self {
213 Self::new(name, target, Kind::Clippy)
214 }
215
216 pub fn doc(name: &str, target: TargetSelection) -> Self {
217 Self::new(name, target, Kind::Doc)
218 }
219
220 pub fn dist(name: &str, target: TargetSelection) -> Self {
221 Self::new(name, target, Kind::Dist)
222 }
223
224 pub fn test(name: &str, target: TargetSelection) -> Self {
225 Self::new(name, target, Kind::Test)
226 }
227
228 pub fn run(name: &str, target: TargetSelection) -> Self {
229 Self::new(name, target, Kind::Run)
230 }
231
232 pub fn new(name: &str, target: TargetSelection, kind: Kind) -> Self {
233 Self { name: name.to_string(), kind, target, built_by: None, stage: None, metadata: None }
234 }
235
236 pub fn built_by(mut self, compiler: Compiler) -> Self {
237 self.built_by = Some(compiler);
238 self
239 }
240
241 pub fn stage(mut self, stage: u32) -> Self {
242 self.stage = Some(stage);
243 self
244 }
245
246 pub fn with_metadata(mut self, metadata: String) -> Self {
247 self.metadata = Some(metadata);
248 self
249 }
250
251 pub fn get_stage(&self) -> Option<u32> {
252 self.stage.or(self
253 .built_by
254 .map(|compiler| if self.name == "std" { compiler.stage } else { compiler.stage + 1 }))
257 }
258
259 pub fn get_name(&self) -> &str {
260 &self.name
261 }
262
263 pub fn get_target(&self) -> TargetSelection {
264 self.target
265 }
266}
267
268pub struct RunConfig<'a> {
269 pub builder: &'a Builder<'a>,
270 pub target: TargetSelection,
271 pub paths: Vec<PathSet>,
272}
273
274impl RunConfig<'_> {
275 pub fn build_triple(&self) -> TargetSelection {
276 self.builder.build.host_target
277 }
278
279 #[track_caller]
281 pub fn cargo_crates_in_set(&self) -> Vec<String> {
282 let mut crates = Vec::new();
283 for krate in &self.paths {
284 let path = &krate.assert_single_path().path;
285
286 let crate_name = self
287 .builder
288 .crate_paths
289 .get(path)
290 .unwrap_or_else(|| panic!("missing crate for path {}", path.display()));
291
292 crates.push(crate_name.to_string());
293 }
294 crates
295 }
296
297 pub fn make_run_crates(&self, alias: Alias) -> Vec<String> {
304 let has_alias =
305 self.paths.iter().any(|set| set.assert_single_path().path.ends_with(alias.as_str()));
306 if !has_alias {
307 return self.cargo_crates_in_set();
308 }
309
310 let crates = match alias {
311 Alias::Library => self.builder.in_tree_crates("sysroot", Some(self.target)),
312 Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)),
313 };
314
315 crates.into_iter().map(|krate| krate.name.to_string()).collect()
316 }
317}
318
319#[derive(Debug, Copy, Clone)]
320pub enum Alias {
321 Library,
322 Compiler,
323}
324
325impl Alias {
326 fn as_str(self) -> &'static str {
327 match self {
328 Alias::Library => "library",
329 Alias::Compiler => "compiler",
330 }
331 }
332}
333
334pub fn crate_description(crates: &[impl AsRef<str>]) -> String {
338 if crates.is_empty() {
339 return "".into();
340 }
341
342 let mut descr = String::from("{");
343 descr.push_str(crates[0].as_ref());
344 for krate in &crates[1..] {
345 descr.push_str(", ");
346 descr.push_str(krate.as_ref());
347 }
348 descr.push('}');
349 descr
350}
351
352struct CommandLineStepDescription {
353 is_host: bool,
354 should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>,
355 is_default_step_fn: fn(&Builder<'_>) -> bool,
356 make_run: fn(RunConfig<'_>),
357 name: &'static str,
358
359 #[cfg_attr(not(test), expect(dead_code, reason = "currently only needed by tests"))]
361 kind: Kind,
362}
363
364#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
365pub struct TaskPath {
366 pub path: PathBuf,
367}
368
369impl Debug for TaskPath {
370 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
371 write!(f, "{}", self.path.display())
372 }
373}
374
375#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
377pub enum PathSet {
378 Set(BTreeSet<TaskPath>),
389 Suite(TaskPath),
396}
397
398impl PathSet {
399 fn one<P: Into<PathBuf>>(path: P) -> PathSet {
400 let mut set = BTreeSet::new();
401 set.insert(TaskPath { path: path.into() });
402 PathSet::Set(set)
403 }
404
405 fn has(&self, needle: &Path) -> bool {
406 match self {
407 PathSet::Set(set) => set.iter().any(|p| Self::check(p, needle)),
408 PathSet::Suite(suite) => Self::check(suite, needle),
409 }
410 }
411
412 fn check(p: &TaskPath, needle: &Path) -> bool {
414 p.path.ends_with(needle) || p.path.starts_with(needle)
416 }
417
418 fn match_and_flag_selectors(&self, selectors: &mut [CLIStepPath]) -> bool {
421 let mut check_and_flag = |p| {
422 let mut result = false;
423 for selector in selectors.iter_mut() {
424 let matched = Self::check(p, &selector.path);
425 if matched {
426 selector.will_be_executed = true;
427 result = true;
428 }
429 }
430 result
431 };
432
433 match self {
434 PathSet::Set(set) => {
435 let mut matched = false;
437 for p in set {
438 matched |= check_and_flag(p);
439 }
440 matched
441 }
442 PathSet::Suite(suite) => check_and_flag(suite),
443 }
444 }
445
446 #[track_caller]
450 pub fn assert_single_path(&self) -> &TaskPath {
451 match self {
452 PathSet::Set(set) => {
453 assert_eq!(set.len(), 1, "called assert_single_path on multiple paths");
454 set.iter().next().unwrap()
455 }
456 PathSet::Suite(_) => unreachable!("called assert_single_path on a Suite path"),
457 }
458 }
459}
460
461impl CommandLineStepDescription {
462 fn from<S: CommandLineStep>(kind: Kind) -> CommandLineStepDescription {
463 CommandLineStepDescription {
464 is_host: S::IS_HOST,
465 should_run: S::should_run,
466 is_default_step_fn: S::is_default_step,
467 make_run: S::make_run,
468 name: std::any::type_name::<S>(),
469 kind,
470 }
471 }
472
473 fn maybe_run(&self, builder: &Builder<'_>, mut pathsets: Vec<PathSet>) {
474 pathsets.retain(|set| !self.is_excluded(builder, set));
475
476 if pathsets.is_empty() {
477 return;
478 }
479
480 let targets = if self.is_host { &builder.hosts } else { &builder.targets };
482
483 if let Some(ref log_cli_step) = builder.log_cli_step_for_tests {
485 log_cli_step(self, &pathsets, targets);
486 return;
488 }
489
490 for target in targets {
491 let run = RunConfig { builder, paths: pathsets.clone(), target: *target };
492 (self.make_run)(run);
493 }
494 }
495
496 fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
497 if builder.config.skip.iter().any(|e| pathset.has(e)) {
498 if !matches!(builder.config.get_dry_run(), DryRun::SelfCheck) {
499 println!("Skipping {pathset:?} because it is excluded");
500 }
501 return true;
502 }
503
504 if !builder.config.skip.is_empty()
505 && !matches!(builder.config.get_dry_run(), DryRun::SelfCheck)
506 {
507 builder.do_if_verbose(|| {
508 println!(
509 "{:?} not skipped for {:?} -- not in {:?}",
510 pathset, self.name, builder.config.skip
511 )
512 });
513 }
514 false
515 }
516}
517
518pub struct ShouldRun<'a> {
525 pub builder: &'a Builder<'a>,
526
527 paths: BTreeSet<PathSet>,
529}
530
531impl<'a> ShouldRun<'a> {
532 fn new(builder: &'a Builder<'_>) -> ShouldRun<'a> {
533 ShouldRun { builder, paths: BTreeSet::new() }
534 }
535
536 pub(crate) fn crate_or_deps(self, root_crate_name: &str) -> Self {
541 self.crate_or_deps_filtered(root_crate_name, |_: &Crate| true)
542 }
543
544 pub(crate) fn crate_or_deps_filtered(
550 mut self,
551 root_crate_name: &str,
552 crate_filter_fn: impl Fn(&Crate) -> bool,
553 ) -> Self {
554 let crates = self.builder.in_tree_crates(root_crate_name, None);
555 for krate in crates {
556 if !crate_filter_fn(krate) {
557 continue;
558 }
559
560 let path = krate.local_path(self.builder);
561 self.paths.insert(PathSet::one(path));
562 }
563 self
564 }
565
566 pub fn alias(self, alias: &str) -> Self {
568 self.assert_valid_alias(alias);
569 self.alias_without_assert(alias)
570 }
571
572 pub fn alias_without_assert(mut self, alias: &str) -> Self {
577 self.paths.insert(PathSet::Set(iter::once(TaskPath { path: alias.into() }).collect()));
578 self
579 }
580
581 fn assert_valid_alias(&self, alias: &str) {
582 assert!(
583 !self.builder.src.join(alias).exists(),
584 "use `builder.path()` for real paths: {alias}"
585 );
586 }
587
588 fn assert_valid_path(&self, path: &str) {
589 let submodules_paths = self.builder.submodule_paths();
590
591 if !submodules_paths.iter().any(|sm_p| path.contains(sm_p)) {
593 assert!(
594 self.builder.src.join(path).exists(),
595 "`should_run.path` should correspond to a real on-disk path - use `alias` if there is no relevant path: {path}"
596 );
597 }
598 }
599
600 pub fn path(mut self, path: &str) -> Self {
605 self.assert_valid_path(path);
606
607 let task = TaskPath { path: path.into() };
608 self.paths.insert(PathSet::Set(BTreeSet::from_iter([task])));
609 self
610 }
611
612 pub fn path_with_alias(mut self, path: &str, alias: &str) -> Self {
614 self.assert_valid_path(path);
615 self.assert_valid_alias(alias);
616
617 let set = [path, alias]
618 .into_iter()
619 .map(|p| TaskPath { path: PathBuf::from(p) })
620 .collect::<BTreeSet<_>>();
621 self.paths.insert(PathSet::Set(set));
622 self
623 }
624
625 pub fn multi_path(mut self, paths: &[&str]) -> Self {
627 let mut set = BTreeSet::new();
628 for path in paths {
629 self.assert_valid_path(path);
630 set.insert(TaskPath { path: (*path).into() });
631 }
632 self.paths.insert(PathSet::Set(set));
633 self
634 }
635
636 fn is_suite_path(&self, requested_path: &Path) -> Option<&PathSet> {
638 self.paths.iter().find(|pathset| match pathset {
639 PathSet::Suite(suite) => requested_path.starts_with(&suite.path),
640 PathSet::Set(_) => false,
641 })
642 }
643
644 pub fn suite_path(mut self, suite: &str) -> Self {
645 self.paths.insert(PathSet::Suite(TaskPath { path: suite.into() }));
646 self
647 }
648
649 fn pathsets_for_paths_flagging_matches(&self, paths: &mut [CLIStepPath]) -> Vec<PathSet> {
659 let mut sets = vec![];
660 for pathset in &self.paths {
661 if pathset.match_and_flag_selectors(paths) {
662 sets.push(pathset.clone());
663 }
664 }
665 sets
666 }
667
668 fn default_pathsets(&self) -> Vec<PathSet> {
671 self.paths.iter().cloned().collect::<Vec<_>>()
672 }
673}
674
675#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord, ValueEnum)]
676pub enum Kind {
677 #[value(alias = "b")]
678 Build,
679 #[value(alias = "c")]
680 Check,
681 Clippy,
682 Fix,
683 Format,
684 #[value(alias = "t")]
685 Test,
686 Miri,
687 MiriSetup,
688 MiriTest,
689 Bench,
690 #[value(alias = "d")]
691 Doc,
692 Clean,
693 Dist,
694 Install,
695 #[value(alias = "r")]
696 Run,
697 Setup,
698 Vendor,
699 Perf,
700}
701
702impl Kind {
703 pub fn as_str(&self) -> &'static str {
704 match self {
705 Kind::Build => "build",
706 Kind::Check => "check",
707 Kind::Clippy => "clippy",
708 Kind::Fix => "fix",
709 Kind::Format => "fmt",
710 Kind::Test => "test",
711 Kind::Miri => "miri",
712 Kind::MiriSetup => panic!("`as_str` is not supported for `Kind::MiriSetup`."),
713 Kind::MiriTest => panic!("`as_str` is not supported for `Kind::MiriTest`."),
714 Kind::Bench => "bench",
715 Kind::Doc => "doc",
716 Kind::Clean => "clean",
717 Kind::Dist => "dist",
718 Kind::Install => "install",
719 Kind::Run => "run",
720 Kind::Setup => "setup",
721 Kind::Vendor => "vendor",
722 Kind::Perf => "perf",
723 }
724 }
725
726 pub fn description(&self) -> String {
727 match self {
728 Kind::Test => "Testing",
729 Kind::Bench => "Benchmarking",
730 Kind::Doc => "Documenting",
731 Kind::Run => "Running",
732 Kind::Clippy => "Linting",
733 Kind::Perf => "Profiling & benchmarking",
734 _ => {
735 let title_letter = self.as_str()[0..1].to_ascii_uppercase();
736 return format!("{title_letter}{}ing", &self.as_str()[1..]);
737 }
738 }
739 .to_owned()
740 }
741}
742
743#[derive(Debug, Clone, Hash, PartialEq, Eq)]
744struct Libdir {
745 compiler: Compiler,
746 target: TargetSelection,
747}
748
749impl Step for Libdir {
750 type Output = PathBuf;
751
752 fn run(self, builder: &Builder<'_>) -> PathBuf {
753 let relative_sysroot_libdir = builder.sysroot_libdir_relative(self.compiler);
754 let sysroot = builder.sysroot(self.compiler).join(relative_sysroot_libdir).join("rustlib");
755
756 if !builder.config.dry_run() {
757 if !builder.download_rustc() {
760 let sysroot_target_libdir = sysroot.join(self.target).join("lib");
761 builder.do_if_verbose(|| {
762 eprintln!(
763 "Removing sysroot {} to avoid caching bugs",
764 sysroot_target_libdir.display()
765 )
766 });
767 let _ = fs::remove_dir_all(&sysroot_target_libdir);
768 t!(fs::create_dir_all(&sysroot_target_libdir));
769 }
770
771 if self.compiler.stage == 0 {
772 dist::maybe_install_llvm_target(
776 builder,
777 self.compiler.host,
778 &builder.sysroot(self.compiler),
779 );
780 }
781 }
782
783 sysroot
784 }
785}
786
787#[cfg(feature = "tracing")]
788pub const STEP_SPAN_TARGET: &str = "STEP";
789
790impl<'a> Builder<'a> {
791 fn get_step_descriptions(kind: Kind) -> Vec<CommandLineStepDescription> {
792 macro_rules! describe {
793 ($($rule:ty),+ $(,)?) => {{
794 vec![$(CommandLineStepDescription::from::<$rule>(kind)),+]
795 }};
796 }
797 match kind {
798 Kind::Build => describe!(
799 compile::Std,
800 compile::Rustc,
801 compile::Assemble,
802 compile::CraneliftCodegenBackend,
803 compile::GccCodegenBackend,
804 compile::StartupObjects,
805 tool::BuildManifest,
806 tool::Rustbook,
807 tool::ErrorIndex,
808 tool::UnstableBookGen,
809 tool::Tidy,
810 tool::Linkchecker,
811 tool::CargoTest,
812 tool::Compiletest,
813 tool::RemoteTestServer,
814 tool::RemoteTestClient,
815 tool::RustInstaller,
816 tool::FeaturesStatusDump,
817 tool::Cargo,
818 tool::RustAnalyzer,
819 tool::RustAnalyzerProcMacroSrv,
820 tool::Rustdoc,
821 tool::Clippy,
822 tool::CargoClippy,
823 llvm::Llvm,
824 gcc::Gcc,
825 llvm::Sanitizers,
826 tool::Rustfmt,
827 tool::Cargofmt,
828 tool::Miri,
829 tool::CargoMiri,
830 llvm::Lld,
831 llvm::Enzyme,
832 llvm::RustOffload,
833 llvm::CrtBeginEnd,
834 tool::RustdocGUITest,
835 tool::OptimizedDist,
836 tool::CoverageDump,
837 tool::LlvmBitcodeLinker,
838 tool::RustcPerf,
839 tool::WasmComponentLd,
840 tool::LldWrapper
841 ),
842 Kind::Clippy => describe!(
843 clippy::Std,
844 clippy::Rustc,
845 clippy::Bootstrap,
846 clippy::BuildHelper,
847 clippy::BuildManifest,
848 clippy::CargoMiri,
849 clippy::Clippy,
850 clippy::CodegenGcc,
851 clippy::CollectLicenseMetadata,
852 clippy::Compiletest,
853 clippy::CoverageDump,
854 clippy::Jsondocck,
855 clippy::Jsondoclint,
856 clippy::LintDocs,
857 clippy::LlvmBitcodeLinker,
858 clippy::Miri,
859 clippy::MiroptTestTools,
860 clippy::OptDist,
861 clippy::RemoteTestClient,
862 clippy::RemoteTestServer,
863 clippy::RustAnalyzer,
864 clippy::Rustdoc,
865 clippy::Rustfmt,
866 clippy::RustInstaller,
867 clippy::TestFloatParse,
868 clippy::Tidy,
869 clippy::CI,
870 ),
871 Kind::Check | Kind::Fix => describe!(
872 check::Rustc,
873 check::Rustdoc,
874 check::CraneliftCodegenBackend,
875 check::GccCodegenBackend,
876 check::Clippy,
877 check::Miri,
878 check::CargoMiri,
879 check::Priroda,
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::CoverageModeAlias,
910 test::MirOpt,
911 test::CodegenLlvm,
912 test::CodegenUnits,
913 test::AssemblyLlvm,
914 test::Incremental,
915 test::Debuginfo,
916 test::UiFullDeps,
917 test::RustdocHtml,
918 test::CoverageRunRustdoc,
919 test::Pretty,
920 test::CodegenCranelift,
921 test::CodegenGCC,
922 test::Crate,
923 test::CrateLibrustc,
924 test::CrateRustdoc,
925 test::CrateRustdocJsonTypes,
926 test::CrateBootstrap,
927 test::RemoteTestClientTests,
928 test::Linkcheck,
929 test::TierCheck,
930 test::Cargotest,
931 test::Cargo,
932 test::RustAnalyzer,
933 test::ErrorIndex,
934 test::Distcheck,
935 test::Nomicon,
936 test::Reference,
937 test::RustdocBook,
938 test::RustByExample,
939 test::TheBook,
940 test::UnstableBook,
941 test::RustcBook,
942 test::LintDocs,
943 test::EmbeddedBook,
944 test::EditionGuide,
945 test::Rustfmt,
946 test::Miri,
947 test::CargoMiri,
948 test::Priroda,
949 test::Clippy,
950 test::CompiletestTest,
951 test::StdarchVerify,
952 test::CrateRunMakeSupport,
953 test::CrateBuildHelper,
954 test::RustdocJSStd,
955 test::RustdocJSNotStd,
956 test::RustdocGUI,
957 test::RustdocTheme,
958 test::RustdocUi,
959 test::RustdocJson,
960 test::HtmlCheck,
961 test::RustInstaller,
962 test::TestFloatParse,
963 test::CollectLicenseMetadata,
964 test::RunMake,
965 test::RunMakeCargo,
966 test::BuildStd,
967 test::StdSemverCheck,
968 test::IntrinsicTest,
969 ),
970 Kind::Miri => describe!(test::Crate),
971 Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc),
972 Kind::Doc => describe!(
973 doc::UnstableBook,
974 doc::UnstableBookGen,
975 doc::TheBook,
976 doc::Standalone,
977 doc::Std,
978 doc::Rustc,
979 doc::Rustdoc,
980 doc::Rustfmt,
981 doc::ErrorIndex,
982 doc::Nomicon,
983 doc::Reference,
984 doc::RustdocBook,
985 doc::RustByExample,
986 doc::RustcBook,
987 doc::Cargo,
988 doc::CargoBook,
989 doc::Clippy,
990 doc::ClippyBook,
991 doc::Miri,
992 doc::EmbeddedBook,
993 doc::EditionGuide,
994 doc::StyleGuide,
995 doc::Tidy,
996 doc::Bootstrap,
997 doc::Releases,
998 doc::RunMakeSupport,
999 doc::BuildHelper,
1000 doc::Compiletest,
1001 ),
1002 Kind::Dist => describe!(
1003 dist::Docs,
1004 dist::RustcDocs,
1005 dist::JsonDocs,
1006 dist::Mingw,
1007 dist::Rustc,
1008 dist::CraneliftCodegenBackend,
1009 dist::GccCodegenBackend,
1010 dist::Std,
1011 dist::RustcDev,
1012 dist::Analysis,
1013 dist::Src,
1014 dist::Cargo,
1015 dist::RustAnalyzer,
1016 dist::Rustfmt,
1017 dist::Clippy,
1018 dist::Miri,
1019 dist::LlvmTools,
1020 dist::LlvmBitcodeLinker,
1021 dist::RustDev,
1022 dist::Enzyme,
1023 dist::Offload,
1024 dist::Bootstrap,
1025 dist::Extended,
1026 dist::PlainSourceTarball,
1031 dist::PlainSourceTarballGpl,
1032 dist::BuildManifest,
1033 dist::ReproducibleArtifacts,
1034 dist::GccDev,
1035 dist::Gcc
1036 ),
1037 Kind::Install => describe!(
1038 install::Docs,
1039 install::Std,
1040 install::Rustc,
1045 install::RustcDev,
1046 install::Cargo,
1047 install::RustAnalyzer,
1048 install::Rustfmt,
1049 install::Clippy,
1050 install::Miri,
1051 install::LlvmTools,
1052 install::Src,
1053 install::RustcCodegenCranelift,
1054 install::LlvmBitcodeLinker
1055 ),
1056 Kind::Run => describe!(
1057 run::BuildManifest,
1058 run::BumpStage0,
1059 run::ReplaceVersionPlaceholder,
1060 run::Miri,
1061 run::CollectLicenseMetadata,
1062 run::GenerateCopyright,
1063 run::GenerateWindowsSys,
1064 run::GenerateCompletions,
1065 run::UnicodeTableGenerator,
1066 run::FeaturesStatusDump,
1067 run::CyclicStep,
1068 run::CoverageDump,
1069 run::Rustfmt,
1070 run::GenerateHelp,
1071 ),
1072 Kind::Setup => {
1073 describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
1074 }
1075 Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
1076 Kind::Vendor => describe!(vendor::Vendor),
1077 Kind::Format | Kind::Perf => vec![],
1079 Kind::MiriTest | Kind::MiriSetup => unreachable!(),
1080 }
1081 }
1082
1083 pub fn get_help(build: &Build, kind: Kind) -> Option<String> {
1084 let step_descriptions = Builder::get_step_descriptions(kind);
1085 if step_descriptions.is_empty() {
1086 return None;
1087 }
1088
1089 let builder = Self::new_internal(build, kind, vec![]);
1090 let builder = &builder;
1091
1092 let mut should_run = ShouldRun::new(builder);
1093 for desc in step_descriptions {
1094 should_run = (desc.should_run)(should_run);
1095 }
1096 let mut help = String::from("Available paths:\n");
1097 let mut add_path = |path: &Path| {
1098 t!(write!(help, " ./x.py {} {}\n", kind.as_str(), path.display()));
1099 };
1100 for pathset in should_run.paths {
1101 match pathset {
1102 PathSet::Set(set) => {
1103 for path in set {
1104 add_path(&path.path);
1105 }
1106 }
1107 PathSet::Suite(path) => {
1108 add_path(&path.path.join("..."));
1109 }
1110 }
1111 }
1112 Some(help)
1113 }
1114
1115 fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
1116 Builder {
1117 build,
1118 top_stage: build.config.stage,
1119 kind,
1120 cache: Cache::new(),
1121 stack: RefCell::new(Vec::new()),
1122 time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
1123 paths,
1124 submodule_paths_cache: Default::default(),
1125 log_cli_step_for_tests: None,
1126 }
1127 }
1128
1129 pub fn new(build: &Build) -> Builder<'_> {
1130 let paths = &build.config.paths;
1131 let (kind, paths) = match build.config.cmd {
1132 Subcommand::Build { .. } => (Kind::Build, &paths[..]),
1133 Subcommand::Check { .. } => (Kind::Check, &paths[..]),
1134 Subcommand::Clippy { .. } => (Kind::Clippy, &paths[..]),
1135 Subcommand::Fix => (Kind::Fix, &paths[..]),
1136 Subcommand::Doc { .. } => (Kind::Doc, &paths[..]),
1137 Subcommand::Test { .. } => (Kind::Test, &paths[..]),
1138 Subcommand::Miri { .. } => (Kind::Miri, &paths[..]),
1139 Subcommand::Bench { .. } => (Kind::Bench, &paths[..]),
1140 Subcommand::Dist => (Kind::Dist, &paths[..]),
1141 Subcommand::Install => (Kind::Install, &paths[..]),
1142 Subcommand::Run { .. } => (Kind::Run, &paths[..]),
1143 Subcommand::Clean { .. } => (Kind::Clean, &paths[..]),
1144 Subcommand::Format { .. } => (Kind::Format, &[][..]),
1145 Subcommand::Setup { profile: ref path } => (
1146 Kind::Setup,
1147 path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
1148 ),
1149 Subcommand::Vendor { .. } => (Kind::Vendor, &paths[..]),
1150 Subcommand::Perf { .. } => (Kind::Perf, &paths[..]),
1151 };
1152
1153 StepStack::with_current(|stack| stack.clear());
1154 Self::new_internal(build, kind, paths.to_owned())
1155 }
1156
1157 pub fn execute_cli(&self) {
1158 self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
1159 }
1160
1161 pub fn run_default_doc_steps(&self) {
1163 for desc in &Builder::get_step_descriptions(Kind::Doc) {
1172 if !(desc.is_default_step_fn)(self) {
1173 continue;
1174 }
1175
1176 let should_run = (desc.should_run)(ShouldRun::new(self));
1177 let default_pathsets = should_run.default_pathsets();
1178
1179 let targets = if desc.is_host { &self.hosts } else { &self.targets };
1180 for &target in targets {
1181 let run = RunConfig { builder: self, target, paths: default_pathsets.clone() };
1182 (desc.make_run)(run);
1183 }
1184 }
1185 }
1186
1187 pub fn doc_rust_lang_org_channel(&self) -> String {
1188 let channel = match &*self.config.channel {
1189 "stable" => &self.version,
1190 "beta" => "beta",
1191 "nightly" | "dev" => "nightly",
1192 _ => "stable",
1194 };
1195
1196 format!("https://doc.rust-lang.org/{channel}")
1197 }
1198
1199 fn run_step_descriptions(&self, v: &[CommandLineStepDescription], paths: &[PathBuf]) {
1200 cli_paths::match_paths_to_steps_and_run(self, v, paths);
1201 }
1202
1203 pub fn link_std_into_rustc_driver(&self, target: TargetSelection) -> bool {
1206 !target.triple.ends_with("-windows-gnu")
1207 }
1208
1209 #[track_caller]
1214 #[cfg_attr(
1215 feature = "tracing",
1216 instrument(
1217 level = "trace",
1218 name = "Builder::compiler",
1219 target = "COMPILER",
1220 skip_all,
1221 fields(
1222 stage = stage,
1223 host = ?host,
1224 ),
1225 ),
1226 )]
1227 pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler {
1228 self.ensure(compile::Assemble { target_compiler: Compiler::new(stage, host) })
1229 }
1230
1231 #[track_caller]
1248 pub fn compiler_for_std(&self, stage: u32) -> Compiler {
1249 if compile::Std::should_be_uplifted_from_stage_1(self, stage) {
1250 self.compiler(1, self.host_target)
1251 } else {
1252 self.compiler(stage, self.host_target)
1253 }
1254 }
1255
1256 #[track_caller]
1268 #[cfg_attr(
1269 feature = "tracing",
1270 instrument(
1271 level = "trace",
1272 name = "Builder::compiler_for",
1273 target = "COMPILER_FOR",
1274 skip_all,
1275 fields(
1276 stage = stage,
1277 host = ?host,
1278 target = ?target,
1279 ),
1280 ),
1281 )]
1282 pub fn compiler_for(
1285 &self,
1286 stage: u32,
1287 host: TargetSelection,
1288 target: TargetSelection,
1289 ) -> Compiler {
1290 let mut resolved_compiler = if self.build.force_use_stage2(stage) {
1291 trace!(target: "COMPILER_FOR", ?stage, "force_use_stage2");
1292 self.compiler(2, self.config.host_target)
1293 } else if self.build.force_use_stage1(stage, target) {
1294 trace!(target: "COMPILER_FOR", ?stage, "force_use_stage1");
1295 self.compiler(1, self.config.host_target)
1296 } else {
1297 trace!(target: "COMPILER_FOR", ?stage, ?host, "no force, fallback to `compiler()`");
1298 self.compiler(stage, host)
1299 };
1300
1301 if stage != resolved_compiler.stage {
1302 resolved_compiler.forced_compiler(true);
1303 }
1304
1305 trace!(target: "COMPILER_FOR", ?resolved_compiler);
1306 resolved_compiler
1307 }
1308
1309 #[track_caller]
1316 #[cfg_attr(
1317 feature = "tracing",
1318 instrument(
1319 level = "trace",
1320 name = "Builder::std",
1321 target = "STD",
1322 skip_all,
1323 fields(
1324 compiler = ?compiler,
1325 target = ?target,
1326 ),
1327 ),
1328 )]
1329 pub fn std(&self, compiler: Compiler, target: TargetSelection) -> Option<BuildStamp> {
1330 if compiler.stage == 0 {
1340 if target != compiler.host {
1341 if self.local_rebuild {
1342 self.ensure(Std::new(compiler, target))
1343 } else {
1344 panic!(
1345 r"It is not possible to build the standard library for `{target}` using the stage0 compiler.
1346You have to build a stage1 compiler for `{}` first, and then use it to build a standard library for `{target}`.
1347Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler built from in-tree sources.
1348",
1349 compiler.host
1350 )
1351 }
1352 } else {
1353 self.ensure(StdLink::from_std(Std::new(compiler, target), compiler));
1355 None
1356 }
1357 } else {
1358 self.ensure(Std::new(compiler, target))
1361 }
1362 }
1363
1364 #[track_caller]
1365 pub fn sysroot(&self, compiler: Compiler) -> PathBuf {
1366 self.ensure(compile::Sysroot::new(compiler))
1367 }
1368
1369 #[track_caller]
1371 pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf {
1372 self.ensure(Libdir { compiler, target }).join(target).join("bin")
1373 }
1374
1375 #[track_caller]
1378 pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf {
1379 self.ensure(Libdir { compiler, target }).join(target).join("lib")
1380 }
1381
1382 pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf {
1383 self.sysroot_target_libdir(compiler, compiler.host).with_file_name("codegen-backends")
1384 }
1385
1386 pub fn rustc_libdir(&self, compiler: Compiler) -> PathBuf {
1392 if compiler.is_snapshot(self) {
1393 self.rustc_snapshot_libdir()
1394 } else {
1395 match self.config.libdir_relative() {
1396 Some(relative_libdir) if compiler.stage >= 1 => {
1397 self.sysroot(compiler).join(relative_libdir)
1398 }
1399 _ => self.sysroot(compiler).join(libdir(compiler.host)),
1400 }
1401 }
1402 }
1403
1404 pub fn libdir_relative(&self, compiler: Compiler) -> &Path {
1410 if compiler.is_snapshot(self) {
1411 libdir(self.config.host_target).as_ref()
1412 } else {
1413 match self.config.libdir_relative() {
1414 Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1415 _ => libdir(compiler.host).as_ref(),
1416 }
1417 }
1418 }
1419
1420 pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path {
1425 match self.config.libdir_relative() {
1426 Some(relative_libdir) if compiler.stage >= 1 => relative_libdir,
1427 _ if compiler.stage == 0 => &self.build.initial_relative_libdir,
1428 _ => Path::new("lib"),
1429 }
1430 }
1431
1432 pub fn rustc_lib_paths(&self, compiler: Compiler) -> Vec<PathBuf> {
1433 let mut dylib_dirs = vec![self.rustc_libdir(compiler)];
1434
1435 if self.config.llvm_from_ci {
1437 let ci_llvm_lib = self.out.join(compiler.host).join("ci-llvm").join("lib");
1438 dylib_dirs.push(ci_llvm_lib);
1439 }
1440
1441 dylib_dirs
1442 }
1443
1444 pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut BootstrapCommand) {
1447 if cfg!(any(windows, target_os = "cygwin")) {
1451 return;
1452 }
1453
1454 add_dylib_path(self.rustc_lib_paths(compiler), cmd);
1455 }
1456
1457 pub fn rustc(&self, compiler: Compiler) -> PathBuf {
1459 if compiler.is_snapshot(self) {
1460 self.initial_rustc.clone()
1461 } else {
1462 self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
1463 }
1464 }
1465
1466 pub fn rustc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
1469 let mut cmd = command(self.rustc(compiler));
1470 self.add_rustc_lib_path(compiler, &mut cmd);
1471 cmd
1472 }
1473
1474 fn codegen_backends(&self, compiler: Compiler) -> impl Iterator<Item = PathBuf> {
1476 fs::read_dir(self.sysroot_codegen_backends(compiler))
1477 .into_iter()
1478 .flatten()
1479 .filter_map(Result::ok)
1480 .filter(|path| looks_like_codegen_backend(&path.path()))
1481 .map(|entry| entry.path())
1482 }
1483
1484 #[track_caller]
1488 pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf {
1489 self.ensure(tool::Rustdoc { target_compiler })
1490 }
1491
1492 pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
1493 assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
1494
1495 let compilers =
1496 RustcPrivateCompilers::new(self, run_compiler.stage, self.build.host_target);
1497 assert_eq!(run_compiler, compilers.target_compiler());
1498
1499 let miri = self.ensure(tool::Miri::from_compilers(compilers));
1501 let cargo_miri = self.ensure(tool::CargoMiri::from_compilers(compilers));
1502 let mut cmd = command(cargo_miri.tool_path);
1504 cmd.env("MIRI", &miri.tool_path);
1505 cmd.env("CARGO", &self.initial_cargo);
1506 add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd);
1515 cmd
1516 }
1517
1518 pub fn cargo_clippy_cmd(&self, build_compiler: Compiler) -> BootstrapCommand {
1521 if build_compiler.stage == 0 {
1522 let cargo_clippy = self
1523 .config
1524 .initial_cargo_clippy
1525 .clone()
1526 .unwrap_or_else(|| self.build.config.download_clippy());
1527
1528 let mut cmd = command(cargo_clippy);
1529 cmd.env("CARGO", &self.initial_cargo);
1530 return cmd;
1531 }
1532
1533 let compilers = RustcPrivateCompilers::from_target_compiler(self, build_compiler);
1537
1538 let _ = self.ensure(tool::Clippy::from_compilers(compilers));
1539 let cargo_clippy = self.ensure(tool::CargoClippy::from_compilers(compilers));
1540 let mut dylib_path = helpers::dylib_path();
1541 dylib_path.insert(0, self.sysroot(build_compiler).join("lib"));
1542
1543 let mut cmd = command(cargo_clippy.tool_path);
1544 cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
1545 cmd.env("CARGO", &self.initial_cargo);
1546 cmd
1547 }
1548
1549 pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
1550 let mut cmd = command(self.bootstrap_out.join("rustdoc"));
1551 cmd.env("RUSTC_STAGE", compiler.stage.to_string())
1552 .env("RUSTC_SYSROOT", self.sysroot(compiler))
1553 .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler))
1556 .env("CFG_RELEASE_CHANNEL", &self.config.channel)
1557 .env("RUSTDOC_REAL", self.rustdoc_for_compiler(compiler))
1558 .env("RUSTC_BOOTSTRAP", "1");
1559
1560 cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
1561
1562 if self.config.deny_warnings {
1563 cmd.arg("-Dwarnings");
1564 }
1565 cmd.arg("-Znormalize-docs");
1566 cmd.args(linker_args(self, compiler.host, LldThreads::Yes));
1567 cmd
1568 }
1569
1570 pub fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
1579 if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() {
1580 let llvm::LlvmOutput { host_llvm_config, .. } = self.ensure(llvm::Llvm { target });
1581 if host_llvm_config.is_file() {
1582 return Some(host_llvm_config);
1583 }
1584 }
1585 None
1586 }
1587
1588 pub fn require_and_update_all_submodules(&self) {
1591 for submodule in self.submodule_paths() {
1592 self.require_submodule(submodule, None);
1593 }
1594 }
1595
1596 pub fn submodule_paths(&self) -> &[String] {
1598 self.submodule_paths_cache.get_or_init(|| build_helper::util::parse_gitmodules(&self.src))
1599 }
1600
1601 #[track_caller]
1605 pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
1606 {
1607 let mut stack = self.stack.borrow_mut();
1608 for stack_step in stack.iter() {
1609 if stack_step.downcast_ref::<S>().is_none_or(|stack_step| *stack_step != step) {
1611 continue;
1612 }
1613 let mut out = String::new();
1614 out += &format!("\n\nCycle in build detected when adding {step:?}\n");
1615 for el in stack.iter().rev() {
1616 out += &format!("\t{el:?}\n");
1617 }
1618 panic!("{}", out);
1619 }
1620 if let Some(out) = self.cache.get(&step) {
1621 #[cfg(feature = "tracing")]
1622 {
1623 if let Some(parent) = stack.last() {
1624 let mut graph = self.build.step_graph.borrow_mut();
1625 graph.register_cached_step(&step, parent, self.config.dry_run());
1626 }
1627 }
1628 return out;
1629 }
1630
1631 #[cfg(feature = "tracing")]
1632 {
1633 let parent = stack.last();
1634 let mut graph = self.build.step_graph.borrow_mut();
1635 graph.register_step_execution(&step, parent, self.config.dry_run());
1636 }
1637
1638 let location = format_location(*std::panic::Location::caller());
1641 StepStack::with_current(|stack| {
1642 stack.push(StepRecord { info: pretty_print_step(&step), location });
1643 });
1644 stack.push(Box::new(step.clone()));
1645 }
1646
1647 #[cfg(feature = "build-metrics")]
1648 self.metrics.enter_step(&step, self);
1649
1650 if self.config.print_step_timings && !self.config.dry_run() {
1651 println!("[TIMING:start] {}", pretty_print_step(&step));
1652 }
1653
1654 let (out, dur) = {
1655 let start = Instant::now();
1656 let zero = Duration::new(0, 0);
1657 let parent = self.time_spent_on_dependencies.replace(zero);
1658
1659 #[cfg(feature = "tracing")]
1660 let _span = {
1661 let span = tracing::info_span!(
1663 target: STEP_SPAN_TARGET,
1664 "step",
1667 step_name = pretty_step_name::<S>(),
1668 args = step_debug_args(&step),
1669 location = format_location(*std::panic::Location::caller())
1670 );
1671 span.entered()
1672 };
1673
1674 let out = step.clone().run(self);
1675 let dur = start.elapsed();
1676 let deps = self.time_spent_on_dependencies.replace(parent + dur);
1677 (out, dur.saturating_sub(deps))
1678 };
1679
1680 if self.config.print_step_timings && !self.config.dry_run() {
1681 println!(
1682 "[TIMING:end] {} -- {}.{:03}",
1683 pretty_print_step(&step),
1684 dur.as_secs(),
1685 dur.subsec_millis()
1686 );
1687 }
1688
1689 #[cfg(feature = "build-metrics")]
1690 self.metrics.exit_step(self);
1691
1692 {
1693 let mut stack = self.stack.borrow_mut();
1694 let cur_step = stack.pop().expect("step stack empty");
1695 assert_eq!(cur_step.downcast_ref(), Some(&step));
1696
1697 StepStack::with_current(|stack| {
1698 stack.pop();
1699 });
1700 }
1701 self.cache.put(step, out.clone());
1702 out
1703 }
1704
1705 pub(crate) fn ensure_if_default<T, S: CommandLineStep<Output = T>>(
1709 &'a self,
1710 step: S,
1711 kind: Kind,
1712 ) -> Option<S::Output> {
1713 let desc = CommandLineStepDescription::from::<S>(kind);
1714 let should_run = (desc.should_run)(ShouldRun::new(self));
1715
1716 for pathset in &should_run.paths {
1718 if desc.is_excluded(self, pathset) {
1719 return None;
1720 }
1721 }
1722
1723 if (desc.is_default_step_fn)(self) { Some(self.ensure(step)) } else { None }
1725 }
1726
1727 pub(crate) fn was_invoked_explicitly<S: CommandLineStep>(&'a self, kind: Kind) -> bool {
1729 let desc = CommandLineStepDescription::from::<S>(kind);
1730 let should_run = (desc.should_run)(ShouldRun::new(self));
1731
1732 for path in &self.paths {
1733 if should_run.paths.iter().any(|s| s.has(path))
1734 && !desc.is_excluded(self, &PathSet::Suite(TaskPath { path: path.clone() }))
1735 {
1736 return true;
1737 }
1738 }
1739
1740 false
1741 }
1742
1743 pub(crate) fn maybe_open_in_browser<S: CommandLineStep>(&self, path: impl AsRef<Path>) {
1744 if self.was_invoked_explicitly::<S>(Kind::Doc) {
1745 self.open_in_browser(path);
1746 } else {
1747 self.info(&format!("Doc path: {}", path.as_ref().display()));
1748 }
1749 }
1750
1751 pub(crate) fn open_in_browser(&self, path: impl AsRef<Path>) {
1752 let path = path.as_ref();
1753
1754 if self.config.dry_run() || !self.config.cmd.open() {
1755 self.info(&format!("Doc path: {}", path.display()));
1756 return;
1757 }
1758
1759 self.info(&format!("Opening doc {}", path.display()));
1760 if let Err(err) = opener::open(path) {
1761 self.info(&format!("{err}\n"));
1762 }
1763 }
1764
1765 pub fn exec_ctx(&self) -> &ExecutionContext {
1766 &self.config.exec_ctx
1767 }
1768}
1769
1770pub fn pretty_step_name<S: Step>() -> String {
1772 let path = type_name::<S>().rsplit("::").take(2).collect::<Vec<_>>();
1774 path.into_iter().rev().collect::<Vec<_>>().join("::")
1775}
1776
1777fn step_debug_args<S: Step>(step: &S) -> String {
1779 let step_dbg_repr = format!("{step:?}");
1780
1781 match (step_dbg_repr.find('{'), step_dbg_repr.rfind('}')) {
1783 (Some(brace_start), Some(brace_end)) => {
1784 step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string()
1785 }
1786 _ => String::new(),
1787 }
1788}
1789
1790fn pretty_print_step<S: Step>(step: &S) -> String {
1791 format!("{} {{ {} }}", pretty_step_name::<S>(), step_debug_args(step))
1792}
1793
1794impl<'a> AsRef<ExecutionContext> for Builder<'a> {
1795 fn as_ref(&self) -> &ExecutionContext {
1796 self.exec_ctx()
1797 }
1798}