@@ -24,7 +24,7 @@ use std::{cmp, env, fs};
2424
2525use build_helper:: ci:: CiEnv ;
2626use build_helper:: exit;
27- use build_helper:: git:: { GitConfig , PathFreshness , check_path_modifications, output_result } ;
27+ use build_helper:: git:: { GitConfig , PathFreshness , check_path_modifications} ;
2828use serde:: Deserialize ;
2929#[ cfg( feature = "tracing" ) ]
3030use tracing:: { instrument, span} ;
@@ -47,8 +47,10 @@ use crate::core::config::{
4747} ;
4848use crate :: core:: download:: is_download_ci_available;
4949use crate :: utils:: channel;
50+ use crate :: utils:: exec:: command;
51+ use crate :: utils:: execution_context:: ExecutionContext ;
5052use crate :: utils:: helpers:: exe;
51- use crate :: { Command , GitInfo , OnceLock , TargetSelection , check_ci_llvm, helpers, output , t} ;
53+ use crate :: { GitInfo , OnceLock , TargetSelection , check_ci_llvm, helpers, t} ;
5254
5355/// Each path from this function is considered "allowed" in the `download-rustc="if-unchanged"` logic.
5456/// This means they can be modified and changes to these paths should never trigger a compiler build
@@ -142,7 +144,6 @@ pub struct Config {
142144 pub jobs : Option < u32 > ,
143145 pub cmd : Subcommand ,
144146 pub incremental : bool ,
145- pub dry_run : DryRun ,
146147 pub dump_bootstrap_shims : bool ,
147148 /// Arguments appearing after `--` to be forwarded to tools,
148149 /// e.g. `--fix-broken` or test arguments.
@@ -317,6 +318,8 @@ pub struct Config {
317318 /// This is mostly for RA as building the stage1 compiler to check the library tree
318319 /// on each code change might be too much for some computers.
319320 pub skip_std_check_if_no_download_rustc : bool ,
321+
322+ pub exec_ctx : ExecutionContext ,
320323}
321324
322325impl Config {
@@ -373,6 +376,14 @@ impl Config {
373376 }
374377 }
375378
379+ pub fn set_dry_run ( & mut self , dry_run : DryRun ) {
380+ self . exec_ctx . set_dry_run ( dry_run) ;
381+ }
382+
383+ pub fn get_dry_run ( & self ) -> & DryRun {
384+ self . exec_ctx . get_dry_run ( )
385+ }
386+
376387 #[ cfg_attr(
377388 feature = "tracing" ,
378389 instrument( target = "CONFIG_HANDLING" , level = "trace" , name = "Config::parse" , skip_all)
@@ -395,6 +406,11 @@ impl Config {
395406 get_toml : impl Fn ( & Path ) -> Result < TomlConfig , toml:: de:: Error > ,
396407 ) -> Config {
397408 let mut config = Config :: default_opts ( ) ;
409+ let mut exec_ctx = ExecutionContext :: new ( ) ;
410+ exec_ctx. set_verbose ( flags. verbose ) ;
411+ exec_ctx. set_fail_fast ( flags. cmd . fail_fast ( ) ) ;
412+
413+ config. exec_ctx = exec_ctx;
398414
399415 // Set flags.
400416 config. paths = std:: mem:: take ( & mut flags. paths ) ;
@@ -423,7 +439,7 @@ impl Config {
423439 config. on_fail = flags. on_fail ;
424440 config. cmd = flags. cmd ;
425441 config. incremental = flags. incremental ;
426- config. dry_run = if flags. dry_run { DryRun :: UserSelected } else { DryRun :: Disabled } ;
442+ config. set_dry_run ( if flags. dry_run { DryRun :: UserSelected } else { DryRun :: Disabled } ) ;
427443 config. dump_bootstrap_shims = flags. dump_bootstrap_shims ;
428444 config. keep_stage = flags. keep_stage ;
429445 config. keep_stage_std = flags. keep_stage_std ;
@@ -453,14 +469,9 @@ impl Config {
453469 // has already been (kinda-cross-)compiled to Windows land, we require a normal Windows path.
454470 cmd. arg ( "rev-parse" ) . arg ( "--show-cdup" ) ;
455471 // Discard stderr because we expect this to fail when building from a tarball.
456- let output = cmd
457- . as_command_mut ( )
458- . stderr ( std:: process:: Stdio :: null ( ) )
459- . output ( )
460- . ok ( )
461- . and_then ( |output| if output. status . success ( ) { Some ( output) } else { None } ) ;
462- if let Some ( output) = output {
463- let git_root_relative = String :: from_utf8 ( output. stdout ) . unwrap ( ) ;
472+ let output = cmd. allow_failure ( ) . run_capture_stdout ( & config) ;
473+ if output. is_success ( ) {
474+ let git_root_relative = output. stdout ( ) ;
464475 // We need to canonicalize this path to make sure it uses backslashes instead of forward slashes,
465476 // and to resolve any relative components.
466477 let git_root = env:: current_dir ( )
@@ -555,7 +566,7 @@ impl Config {
555566 build. cargo = build. cargo . take ( ) . or ( std:: env:: var_os ( "CARGO" ) . map ( |p| p. into ( ) ) ) ;
556567 }
557568
558- if GitInfo :: new ( false , & config. src ) . is_from_tarball ( ) && toml. profile . is_none ( ) {
569+ if config . git_info ( false , & config. src ) . is_from_tarball ( ) && toml. profile . is_none ( ) {
559570 toml. profile = Some ( "dist" . into ( ) ) ;
560571 }
561572
@@ -762,7 +773,12 @@ impl Config {
762773 } ;
763774
764775 config. initial_sysroot = t ! ( PathBuf :: from_str(
765- output( Command :: new( & config. initial_rustc) . args( [ "--print" , "sysroot" ] ) ) . trim( )
776+ command( & config. initial_rustc)
777+ . args( [ "--print" , "sysroot" ] )
778+ . run_always( )
779+ . run_capture_stdout( & config)
780+ . stdout( )
781+ . trim( )
766782 ) ) ;
767783
768784 config. initial_cargo_clippy = cargo_clippy;
@@ -858,19 +874,21 @@ impl Config {
858874 let default = config. channel == "dev" ;
859875 config. omit_git_hash = toml. rust . as_ref ( ) . and_then ( |r| r. omit_git_hash ) . unwrap_or ( default) ;
860876
861- config. rust_info = GitInfo :: new ( config. omit_git_hash , & config. src ) ;
862- config. cargo_info = GitInfo :: new ( config. omit_git_hash , & config. src . join ( "src/tools/cargo" ) ) ;
877+ config. rust_info = config. git_info ( config. omit_git_hash , & config. src ) ;
878+ config. cargo_info =
879+ config. git_info ( config. omit_git_hash , & config. src . join ( "src/tools/cargo" ) ) ;
863880 config. rust_analyzer_info =
864- GitInfo :: new ( config. omit_git_hash , & config. src . join ( "src/tools/rust-analyzer" ) ) ;
881+ config . git_info ( config. omit_git_hash , & config. src . join ( "src/tools/rust-analyzer" ) ) ;
865882 config. clippy_info =
866- GitInfo :: new ( config. omit_git_hash , & config. src . join ( "src/tools/clippy" ) ) ;
867- config. miri_info = GitInfo :: new ( config. omit_git_hash , & config. src . join ( "src/tools/miri" ) ) ;
883+ config. git_info ( config. omit_git_hash , & config. src . join ( "src/tools/clippy" ) ) ;
884+ config. miri_info =
885+ config. git_info ( config. omit_git_hash , & config. src . join ( "src/tools/miri" ) ) ;
868886 config. rustfmt_info =
869- GitInfo :: new ( config. omit_git_hash , & config. src . join ( "src/tools/rustfmt" ) ) ;
887+ config . git_info ( config. omit_git_hash , & config. src . join ( "src/tools/rustfmt" ) ) ;
870888 config. enzyme_info =
871- GitInfo :: new ( config. omit_git_hash , & config. src . join ( "src/tools/enzyme" ) ) ;
872- config. in_tree_llvm_info = GitInfo :: new ( false , & config. src . join ( "src/llvm-project" ) ) ;
873- config. in_tree_gcc_info = GitInfo :: new ( false , & config. src . join ( "src/gcc" ) ) ;
889+ config . git_info ( config. omit_git_hash , & config. src . join ( "src/tools/enzyme" ) ) ;
890+ config. in_tree_llvm_info = config . git_info ( false , & config. src . join ( "src/llvm-project" ) ) ;
891+ config. in_tree_gcc_info = config . git_info ( false , & config. src . join ( "src/gcc" ) ) ;
874892
875893 config. vendor = vendor. unwrap_or (
876894 config. rust_info . is_from_tarball ( )
@@ -1030,28 +1048,13 @@ impl Config {
10301048 }
10311049
10321050 pub fn dry_run ( & self ) -> bool {
1033- match self . dry_run {
1034- DryRun :: Disabled => false ,
1035- DryRun :: SelfCheck | DryRun :: UserSelected => true ,
1036- }
1051+ self . exec_ctx . dry_run ( )
10371052 }
10381053
10391054 pub fn is_explicit_stage ( & self ) -> bool {
10401055 self . explicit_stage_from_cli || self . explicit_stage_from_config
10411056 }
10421057
1043- /// Runs a command, printing out nice contextual information if it fails.
1044- /// Exits if the command failed to execute at all, otherwise returns its
1045- /// `status.success()`.
1046- #[ deprecated = "use `Builder::try_run` instead where possible" ]
1047- pub ( crate ) fn try_run ( & self , cmd : & mut Command ) -> Result < ( ) , ( ) > {
1048- if self . dry_run ( ) {
1049- return Ok ( ( ) ) ;
1050- }
1051- self . verbose ( || println ! ( "running: {cmd:?}" ) ) ;
1052- build_helper:: util:: try_run ( cmd, self . is_verbose ( ) )
1053- }
1054-
10551058 pub ( crate ) fn test_args ( & self ) -> Vec < & str > {
10561059 let mut test_args = match self . cmd {
10571060 Subcommand :: Test { ref test_args, .. }
@@ -1085,7 +1088,7 @@ impl Config {
10851088
10861089 let mut git = helpers:: git ( Some ( & self . src ) ) ;
10871090 git. arg ( "show" ) . arg ( format ! ( "{commit}:{}" , file. to_str( ) . unwrap( ) ) ) ;
1088- output ( git. as_command_mut ( ) )
1091+ git. run_capture_stdout ( self ) . stdout ( )
10891092 }
10901093
10911094 /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI.
@@ -1273,9 +1276,7 @@ impl Config {
12731276
12741277 /// Runs a function if verbosity is greater than 0
12751278 pub fn verbose ( & self , f : impl Fn ( ) ) {
1276- if self . is_verbose ( ) {
1277- f ( )
1278- }
1279+ self . exec_ctx . verbose ( f) ;
12791280 }
12801281
12811282 pub fn any_sanitizers_to_build ( & self ) -> bool {
@@ -1337,7 +1338,7 @@ impl Config {
13371338
13381339 // NOTE: The check for the empty directory is here because when running x.py the first time,
13391340 // the submodule won't be checked out. Check it out now so we can build it.
1340- if !GitInfo :: new ( false , & absolute_path) . is_managed_git_subrepository ( )
1341+ if !self . git_info ( false , & absolute_path) . is_managed_git_subrepository ( )
13411342 && !helpers:: dir_is_empty ( & absolute_path)
13421343 {
13431344 return ;
@@ -1356,16 +1357,16 @@ impl Config {
13561357 } ;
13571358
13581359 // Determine commit checked out in submodule.
1359- let checked_out_hash = output ( submodule_git ( ) . args ( [ "rev-parse" , "HEAD" ] ) . as_command_mut ( ) ) ;
1360+ let checked_out_hash =
1361+ submodule_git ( ) . args ( [ "rev-parse" , "HEAD" ] ) . run_capture_stdout ( self ) . stdout ( ) ;
13601362 let checked_out_hash = checked_out_hash. trim_end ( ) ;
13611363 // Determine commit that the submodule *should* have.
1362- let recorded = output (
1363- helpers:: git ( Some ( & self . src ) )
1364- . run_always ( )
1365- . args ( [ "ls-tree" , "HEAD" ] )
1366- . arg ( relative_path)
1367- . as_command_mut ( ) ,
1368- ) ;
1364+ let recorded = helpers:: git ( Some ( & self . src ) )
1365+ . run_always ( )
1366+ . args ( [ "ls-tree" , "HEAD" ] )
1367+ . arg ( relative_path)
1368+ . run_capture_stdout ( self )
1369+ . stdout ( ) ;
13691370
13701371 let actual_hash = recorded
13711372 . split_whitespace ( )
@@ -1389,20 +1390,18 @@ impl Config {
13891390 let update = |progress : bool | {
13901391 // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
13911392 // even though that has no relation to the upstream for the submodule.
1392- let current_branch = output_result (
1393- helpers:: git ( Some ( & self . src ) )
1394- . allow_failure ( )
1395- . run_always ( )
1396- . args ( [ "symbolic-ref" , "--short" , "HEAD" ] )
1397- . as_command_mut ( ) ,
1398- )
1399- . map ( |b| b. trim ( ) . to_owned ( ) ) ;
1393+ let current_branch = helpers:: git ( Some ( & self . src ) )
1394+ . allow_failure ( )
1395+ . run_always ( )
1396+ . args ( [ "symbolic-ref" , "--short" , "HEAD" ] )
1397+ . run_capture ( self ) ;
14001398
14011399 let mut git = helpers:: git ( Some ( & self . src ) ) . allow_failure ( ) ;
14021400 git. run_always ( ) ;
1403- if let Ok ( branch ) = current_branch {
1401+ if current_branch . is_success ( ) {
14041402 // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
14051403 // This syntax isn't accepted by `branch.{branch}`. Strip it.
1404+ let branch = current_branch. stdout ( ) ;
14061405 let branch = branch. strip_prefix ( "heads/" ) . unwrap_or ( & branch) ;
14071406 git. arg ( "-c" ) . arg ( format ! ( "branch.{branch}.remote=origin" ) ) ;
14081407 }
@@ -1448,7 +1447,8 @@ impl Config {
14481447 return ;
14491448 }
14501449
1451- let stage0_output = output ( Command :: new ( program_path) . arg ( "--version" ) ) ;
1450+ let stage0_output =
1451+ command ( program_path) . arg ( "--version" ) . run_capture_stdout ( self ) . stdout ( ) ;
14521452 let mut stage0_output = stage0_output. lines ( ) . next ( ) . unwrap ( ) . split ( ' ' ) ;
14531453
14541454 let stage0_name = stage0_output. next ( ) . unwrap ( ) ;
@@ -1754,4 +1754,18 @@ impl Config {
17541754 _ => !self . is_system_llvm ( target) ,
17551755 }
17561756 }
1757+
1758+ pub fn exec_ctx ( & self ) -> & ExecutionContext {
1759+ & self . exec_ctx
1760+ }
1761+
1762+ pub fn git_info ( & self , omit_git_hash : bool , dir : & Path ) -> GitInfo {
1763+ GitInfo :: new ( omit_git_hash, dir, self )
1764+ }
1765+ }
1766+
1767+ impl AsRef < ExecutionContext > for Config {
1768+ fn as_ref ( & self ) -> & ExecutionContext {
1769+ & self . exec_ctx
1770+ }
17571771}
0 commit comments