@@ -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).
8282 pub 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.
436446 Finished ( 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,81 @@ impl<'a> DeferredCommand<'a> {
881879
882880 let created_at = command. get_created_location ( ) ;
883881
884- let mut message = String :: new ( ) ;
882+ enum FailureReason {
883+ FailedAtRuntime ( ExitStatus ) ,
884+ FailedToFinish ( std:: io:: Error ) ,
885+ FailedToStart ( std:: io:: Error ) ,
886+ }
885887
886- let output = match process {
888+ let ( output, fail_reason ) = match process {
887889 Ok ( child) => match child. wait_with_output ( ) {
888- Ok ( result ) if result . status . success ( ) => {
890+ Ok ( output ) if output . status . success ( ) => {
889891 // Successful execution
890- CommandOutput :: from_output ( result , stdout, stderr)
892+ ( CommandOutput :: from_output ( output , stdout, stderr) , None )
891893 }
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,
894+ Ok ( output) => {
895+ // Command started, but then it failed
896+ let status = output. status ;
897+ (
898+ CommandOutput :: from_output ( output, stdout, stderr) ,
899+ Some ( FailureReason :: FailedAtRuntime ( status) ) ,
904900 )
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
917901 }
918902 Err ( e) => {
919903 // 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:?}"
904+ (
905+ CommandOutput :: not_finished ( stdout, stderr) ,
906+ Some ( FailureReason :: FailedToFinish ( e) ) ,
926907 )
927- . unwrap ( ) ;
928-
929- CommandOutput :: did_not_start ( stdout, stderr)
930908 }
931909 } ,
932910 Err ( e) => {
933911 // 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)
912+ ( CommandOutput :: not_finished ( stdout, stderr) , Some ( FailureReason :: FailedToStart ( e) ) )
944913 }
945914 } ;
946915
947- if !output. is_success ( ) {
916+ if let Some ( fail_reason) = fail_reason {
917+ let mut error_message = String :: new ( ) ;
918+ let command_str = if exec_ctx. is_verbose ( ) {
919+ format ! ( "{command:?}" )
920+ } else {
921+ command. fingerprint ( ) . format_short_cmd ( )
922+ } ;
923+ let action = match fail_reason {
924+ FailureReason :: FailedAtRuntime ( e) => {
925+ format ! ( "failed with exit code {}" , e. code( ) . unwrap_or( 1 ) )
926+ }
927+ FailureReason :: FailedToFinish ( e) => {
928+ format ! ( "failed to finish: {e:?}" )
929+ }
930+ FailureReason :: FailedToStart ( e) => {
931+ format ! ( "failed to start: {e:?}" )
932+ }
933+ } ;
934+ writeln ! (
935+ error_message,
936+ r#"Command `{command_str}` {action}
937+ Created at: {created_at}
938+ Executed at: {executed_at}"# ,
939+ )
940+ . unwrap ( ) ;
941+ if stdout. captures ( ) {
942+ writeln ! ( error_message, "\n --- STDOUT vvv\n {}" , output. stdout( ) . trim( ) ) . unwrap ( ) ;
943+ }
944+ if stderr. captures ( ) {
945+ writeln ! ( error_message, "\n --- STDERR vvv\n {}" , output. stderr( ) . trim( ) ) . unwrap ( ) ;
946+ }
947+
948948 match command. failure_behavior {
949949 BehaviorOnFailure :: DelayFail => {
950950 if exec_ctx. fail_fast {
951- exec_ctx. fail ( & message , output ) ;
951+ exec_ctx. fail ( & error_message ) ;
952952 }
953- exec_ctx. add_to_delay_failure ( message ) ;
953+ exec_ctx. add_to_delay_failure ( error_message ) ;
954954 }
955955 BehaviorOnFailure :: Exit => {
956- exec_ctx. fail ( & message , output ) ;
956+ exec_ctx. fail ( & error_message ) ;
957957 }
958958 BehaviorOnFailure :: Ignore => {
959959 // If failures are allowed, either the error has been printed already
0 commit comments