@@ -80,11 +80,21 @@ impl CommandFingerprint {
8080    /// Helper method to format both Command and BootstrapCommand as a short execution line, 
8181/// without all the other details (e.g. environment variables). 
8282pub  fn  format_short_cmd ( & self )  -> String  { 
83-         let  program = Path :: new ( & self . program ) ; 
84-         let  mut  line = vec ! [ program. file_name( ) . unwrap( ) . to_str( ) . unwrap( ) . to_owned( ) ] ; 
85-         line. extend ( self . args . iter ( ) . map ( |arg| arg. to_string_lossy ( ) . into_owned ( ) ) ) ; 
86-         line. extend ( self . cwd . iter ( ) . map ( |p| p. to_string_lossy ( ) . into_owned ( ) ) ) ; 
87-         line. join ( " " ) 
83+         use  std:: fmt:: Write ; 
84+ 
85+         let  mut  cmd = self . program . to_string_lossy ( ) . to_string ( ) ; 
86+         for  arg in  & self . args  { 
87+             let  arg = arg. to_string_lossy ( ) ; 
88+             if  arg. contains ( ' ' )  { 
89+                 write ! ( cmd,  " '{arg}'" ) . unwrap ( ) ; 
90+             }  else  { 
91+                 write ! ( cmd,  " {arg}" ) . unwrap ( ) ; 
92+             } 
93+         } 
94+         if  let  Some ( cwd)  = & self . cwd  { 
95+             write ! ( cmd,  " [workdir={}]" ,  cwd. to_string_lossy( ) ) . unwrap ( ) ; 
96+         } 
97+         cmd
8898    } 
8999} 
90100
@@ -434,8 +444,8 @@ impl From<Command> for BootstrapCommand {
434444enum  CommandStatus  { 
435445    /// The command has started and finished with some status. 
436446Finished ( ExitStatus ) , 
437-     /// It was not even possible to start the command. 
438- DidNotStart , 
447+     /// It was not even possible to start the command or wait for it to finish . 
448+ DidNotStartOrFinish , 
439449} 
440450
441451/// Create a new BootstrapCommand. This is a helper function to make command creation 
@@ -456,9 +466,9 @@ pub struct CommandOutput {
456466
457467impl  CommandOutput  { 
458468    #[ must_use]  
459-     pub  fn  did_not_start ( stdout :  OutputMode ,  stderr :  OutputMode )  -> Self  { 
469+     pub  fn  not_finished ( stdout :  OutputMode ,  stderr :  OutputMode )  -> Self  { 
460470        Self  { 
461-             status :  CommandStatus :: DidNotStart , 
471+             status :  CommandStatus :: DidNotStartOrFinish , 
462472            stdout :  match  stdout { 
463473                OutputMode :: Print  => None , 
464474                OutputMode :: Capture  => Some ( vec ! [ ] ) , 
@@ -489,7 +499,7 @@ impl CommandOutput {
489499    pub  fn  is_success ( & self )  -> bool  { 
490500        match  self . status  { 
491501            CommandStatus :: Finished ( status)  => status. success ( ) , 
492-             CommandStatus :: DidNotStart  => false , 
502+             CommandStatus :: DidNotStartOrFinish  => false , 
493503        } 
494504    } 
495505
@@ -501,7 +511,7 @@ impl CommandOutput {
501511    pub  fn  status ( & self )  -> Option < ExitStatus >  { 
502512        match  self . status  { 
503513            CommandStatus :: Finished ( status)  => Some ( status) , 
504-             CommandStatus :: DidNotStart  => None , 
514+             CommandStatus :: DidNotStartOrFinish  => None , 
505515        } 
506516    } 
507517
@@ -745,25 +755,11 @@ impl ExecutionContext {
745755        self . start ( command,  stdout,  stderr) . wait_for_output ( self ) 
746756    } 
747757
748-     fn  fail ( & self ,  message :  & str ,  output :  CommandOutput )  -> ! { 
749-         if  self . is_verbose ( )  { 
750-             println ! ( "{message}" ) ; 
751-         }  else  { 
752-             let  ( stdout,  stderr)  = ( output. stdout_if_present ( ) ,  output. stderr_if_present ( ) ) ; 
753-             // If the command captures output, the user would not see any indication that 
754-             // it has failed. In this case, print a more verbose error, since to provide more 
755-             // context. 
756-             if  stdout. is_some ( )  || stderr. is_some ( )  { 
757-                 if  let  Some ( stdout)  = output. stdout_if_present ( ) . take_if ( |s| !s. trim ( ) . is_empty ( ) )  { 
758-                     println ! ( "STDOUT:\n {stdout}\n " ) ; 
759-                 } 
760-                 if  let  Some ( stderr)  = output. stderr_if_present ( ) . take_if ( |s| !s. trim ( ) . is_empty ( ) )  { 
761-                     println ! ( "STDERR:\n {stderr}\n " ) ; 
762-                 } 
763-                 println ! ( "Command has failed. Rerun with -v to see more details." ) ; 
764-             }  else  { 
765-                 println ! ( "Command has failed. Rerun with -v to see more details." ) ; 
766-             } 
758+     fn  fail ( & self ,  message :  & str )  -> ! { 
759+         println ! ( "{message}" ) ; 
760+ 
761+         if  !self . is_verbose ( )  { 
762+             println ! ( "Command has failed. Rerun with -v to see more details." ) ; 
767763        } 
768764        exit ! ( 1 ) ; 
769765    } 
@@ -856,7 +852,7 @@ impl<'a> DeferredCommand<'a> {
856852                    && command. should_cache 
857853                { 
858854                    exec_ctx. command_cache . insert ( fingerprint. clone ( ) ,  output. clone ( ) ) ; 
859-                     exec_ctx. profiler . record_execution ( fingerprint. clone ( ) ,  start_time) ; 
855+                     exec_ctx. profiler . record_execution ( fingerprint,  start_time) ; 
860856                } 
861857
862858                output
@@ -872,6 +868,8 @@ impl<'a> DeferredCommand<'a> {
872868        executed_at :  & ' a  std:: panic:: Location < ' a > , 
873869        exec_ctx :  & ExecutionContext , 
874870    )  -> CommandOutput  { 
871+         use  std:: fmt:: Write ; 
872+ 
875873        command. mark_as_executed ( ) ; 
876874
877875        let  process = match  process. take ( )  { 
@@ -881,79 +879,82 @@ impl<'a> DeferredCommand<'a> {
881879
882880        let  created_at = command. get_created_location ( ) ; 
883881
884-         let  mut  message = String :: new ( ) ; 
882+         #[ allow( clippy:: enum_variant_names) ]  
883+         enum  FailureReason  { 
884+             FailedAtRuntime ( ExitStatus ) , 
885+             FailedToFinish ( std:: io:: Error ) , 
886+             FailedToStart ( std:: io:: Error ) , 
887+         } 
885888
886-         let  output = match  process { 
889+         let  ( output,  fail_reason )  = match  process { 
887890            Ok ( child)  => match  child. wait_with_output ( )  { 
888-                 Ok ( result )  if  result . status . success ( )  => { 
891+                 Ok ( output )  if  output . status . success ( )  => { 
889892                    // Successful execution 
890-                     CommandOutput :: from_output ( result ,  stdout,  stderr) 
893+                     ( CommandOutput :: from_output ( output ,  stdout,  stderr) ,   None ) 
891894                } 
892-                 Ok ( result)  => { 
893-                     // Command ran but failed 
894-                     use  std:: fmt:: Write ; 
895- 
896-                     writeln ! ( 
897-                         message, 
898-                         r#" 
899- Command {command:?} did not execute successfully. 
900- Expected success, got {} 
901- Created at: {created_at} 
902- Executed at: {executed_at}"# , 
903-                         result. status, 
895+                 Ok ( output)  => { 
896+                     // Command started, but then it failed 
897+                     let  status = output. status ; 
898+                     ( 
899+                         CommandOutput :: from_output ( output,  stdout,  stderr) , 
900+                         Some ( FailureReason :: FailedAtRuntime ( status) ) , 
904901                    ) 
905-                     . unwrap ( ) ; 
906- 
907-                     let  output = CommandOutput :: from_output ( result,  stdout,  stderr) ; 
908- 
909-                     if  stdout. captures ( )  { 
910-                         writeln ! ( message,  "\n STDOUT ----\n {}" ,  output. stdout( ) . trim( ) ) . unwrap ( ) ; 
911-                     } 
912-                     if  stderr. captures ( )  { 
913-                         writeln ! ( message,  "\n STDERR ----\n {}" ,  output. stderr( ) . trim( ) ) . unwrap ( ) ; 
914-                     } 
915- 
916-                     output
917902                } 
918903                Err ( e)  => { 
919904                    // Failed to wait for output 
920-                     use  std:: fmt:: Write ; 
921- 
922-                     writeln ! ( 
923-                         message, 
924-                         "\n \n Command {command:?} did not execute successfully.\  
925- \n It was not possible to execute the command: {e:?}"
905+                     ( 
906+                         CommandOutput :: not_finished ( stdout,  stderr) , 
907+                         Some ( FailureReason :: FailedToFinish ( e) ) , 
926908                    ) 
927-                     . unwrap ( ) ; 
928- 
929-                     CommandOutput :: did_not_start ( stdout,  stderr) 
930909                } 
931910            } , 
932911            Err ( e)  => { 
933912                // Failed to spawn the command 
934-                 use  std:: fmt:: Write ; 
935- 
936-                 writeln ! ( 
937-                     message, 
938-                     "\n \n Command {command:?} did not execute successfully.\  
939- \n It was not possible to execute the command: {e:?}"
940-                 ) 
941-                 . unwrap ( ) ; 
942- 
943-                 CommandOutput :: did_not_start ( stdout,  stderr) 
913+                 ( CommandOutput :: not_finished ( stdout,  stderr) ,  Some ( FailureReason :: FailedToStart ( e) ) ) 
944914            } 
945915        } ; 
946916
947-         if  !output. is_success ( )  { 
917+         if  let  Some ( fail_reason)  = fail_reason { 
918+             let  mut  error_message = String :: new ( ) ; 
919+             let  command_str = if  exec_ctx. is_verbose ( )  { 
920+                 format ! ( "{command:?}" ) 
921+             }  else  { 
922+                 command. fingerprint ( ) . format_short_cmd ( ) 
923+             } ; 
924+             let  action = match  fail_reason { 
925+                 FailureReason :: FailedAtRuntime ( e)  => { 
926+                     format ! ( "failed with exit code {}" ,  e. code( ) . unwrap_or( 1 ) ) 
927+                 } 
928+                 FailureReason :: FailedToFinish ( e)  => { 
929+                     format ! ( "failed to finish: {e:?}" ) 
930+                 } 
931+                 FailureReason :: FailedToStart ( e)  => { 
932+                     format ! ( "failed to start: {e:?}" ) 
933+                 } 
934+             } ; 
935+             writeln ! ( 
936+                 error_message, 
937+                 r#"Command `{command_str}` {action} 
938+ Created at: {created_at} 
939+ Executed at: {executed_at}"# , 
940+             ) 
941+             . unwrap ( ) ; 
942+             if  stdout. captures ( )  { 
943+                 writeln ! ( error_message,  "\n --- STDOUT vvv\n {}" ,  output. stdout( ) . trim( ) ) . unwrap ( ) ; 
944+             } 
945+             if  stderr. captures ( )  { 
946+                 writeln ! ( error_message,  "\n --- STDERR vvv\n {}" ,  output. stderr( ) . trim( ) ) . unwrap ( ) ; 
947+             } 
948+ 
948949            match  command. failure_behavior  { 
949950                BehaviorOnFailure :: DelayFail  => { 
950951                    if  exec_ctx. fail_fast  { 
951-                         exec_ctx. fail ( & message ,  output ) ; 
952+                         exec_ctx. fail ( & error_message ) ; 
952953                    } 
953-                     exec_ctx. add_to_delay_failure ( message ) ; 
954+                     exec_ctx. add_to_delay_failure ( error_message ) ; 
954955                } 
955956                BehaviorOnFailure :: Exit  => { 
956-                     exec_ctx. fail ( & message ,  output ) ; 
957+                     exec_ctx. fail ( & error_message ) ; 
957958                } 
958959                BehaviorOnFailure :: Ignore  => { 
959960                    // If failures are allowed, either the error has been printed already 
0 commit comments