@@ -1000,23 +1000,56 @@ impl<'a> Cmd<'a> {
1000
1000
Ok ( stream)
1001
1001
}
1002
1002
1003
+ fn spawn_command_as (
1004
+ & self ,
1005
+ read_stdout : bool ,
1006
+ read_stderr : bool ,
1007
+ program : Option < & OsStr > ,
1008
+ ) -> std:: io:: Result < std:: process:: Child > {
1009
+ let mut command = self . to_command_impl ( program) ;
1010
+
1011
+ if !self . data . ignore_stdout {
1012
+ command. stdout ( if read_stdout { Stdio :: piped ( ) } else { Stdio :: inherit ( ) } ) ;
1013
+ }
1014
+ if !self . data . ignore_stderr {
1015
+ command. stderr ( if read_stderr { Stdio :: piped ( ) } else { Stdio :: inherit ( ) } ) ;
1016
+ }
1017
+
1018
+ command. stdin ( match & self . data . stdin_contents {
1019
+ Some ( _) => Stdio :: piped ( ) ,
1020
+ None => Stdio :: null ( ) ,
1021
+ } ) ;
1022
+
1023
+ command. spawn ( )
1024
+ }
1025
+
1003
1026
fn output_impl ( & self , read_stdout : bool , read_stderr : bool ) -> Result < Output > {
1004
1027
let mut child = {
1005
- let mut command = self . to_command ( ) ;
1006
-
1007
- if !self . data . ignore_stdout {
1008
- command. stdout ( if read_stdout { Stdio :: piped ( ) } else { Stdio :: inherit ( ) } ) ;
1009
- }
1010
- if !self . data . ignore_stderr {
1011
- command. stderr ( if read_stderr { Stdio :: piped ( ) } else { Stdio :: inherit ( ) } ) ;
1028
+ let mut child = self . spawn_command_as ( read_stdout, read_stderr, None ) ;
1029
+
1030
+ // rust stdlib has all necessary code to run `.cmd` and `.bat` files on Windows via 'cmd /c' invocation,
1031
+ // but requires the explicit extension. If the command does not have one, add 'cmd' or 'bat' and retry.
1032
+ //
1033
+ // Ref: https://github.com/rust-lang/rust/blob/051478957371ee0084a7c0913941d2a8c4757bb9/library/std/src/sys/pal/windows/process.rs#L274
1034
+ #[ cfg( windows) ]
1035
+ if matches ! ( & child, Err ( err) if err. kind( ) == io:: ErrorKind :: NotFound )
1036
+ && self . data . prog . extension ( ) . is_none ( )
1037
+ {
1038
+ for ext in [ "cmd" , "bat" ] {
1039
+ let possible_executable = self . data . prog . with_extension ( ext) ;
1040
+ let retry_child = self . spawn_command_as (
1041
+ read_stdout,
1042
+ read_stderr,
1043
+ Some ( possible_executable. as_os_str ( ) ) ,
1044
+ ) ;
1045
+ if retry_child. is_ok ( ) {
1046
+ child = retry_child;
1047
+ break ;
1048
+ }
1049
+ }
1012
1050
}
1013
1051
1014
- command. stdin ( match & self . data . stdin_contents {
1015
- Some ( _) => Stdio :: piped ( ) ,
1016
- None => Stdio :: null ( ) ,
1017
- } ) ;
1018
-
1019
- command. spawn ( ) . map_err ( |err| {
1052
+ child. map_err ( |err| {
1020
1053
// Try to determine whether the command failed because the current
1021
1054
// directory does not exist. Return an appropriate error in such a
1022
1055
// case.
@@ -1049,23 +1082,13 @@ impl<'a> Cmd<'a> {
1049
1082
}
1050
1083
1051
1084
fn to_command ( & self ) -> Command {
1052
- let mut res = if cfg ! ( windows) {
1053
- // On windows have to use "cmd /c" workaround to allow batch (command) files
1054
- let mut res = Command :: new ( "cmd" ) ;
1055
- res. current_dir ( self . shell . current_dir ( ) ) ;
1056
- res. args (
1057
- [ OsStr :: new ( "/c" ) , self . data . prog . as_os_str ( ) ]
1058
- . iter ( )
1059
- . map ( |it| * it)
1060
- . chain ( self . data . args . iter ( ) . map ( |it| it. as_os_str ( ) ) ) ,
1061
- ) ;
1062
- res
1063
- } else {
1064
- let mut res = Command :: new ( & self . data . prog ) ;
1065
- res. current_dir ( self . shell . current_dir ( ) ) ;
1066
- res. args ( & self . data . args ) ;
1067
- res
1068
- } ;
1085
+ self . to_command_impl ( None )
1086
+ }
1087
+
1088
+ fn to_command_impl ( & self , program : Option < & OsStr > ) -> Command {
1089
+ let mut res = Command :: new ( program. unwrap_or ( self . data . prog . as_os_str ( ) ) ) ;
1090
+ res. args ( & self . data . args ) ;
1091
+ res. current_dir ( self . shell . current_dir ( ) ) ;
1069
1092
1070
1093
for ( key, val) in & * self . shell . env . borrow ( ) {
1071
1094
res. env ( key, val) ;
0 commit comments