@@ -1048,24 +1048,87 @@ impl<'a> Cmd<'a> {
1048
1048
Ok ( output)
1049
1049
}
1050
1050
1051
+ #[ cfg( not( windows) ) ]
1052
+ fn resolve_program ( & self ) -> OsString {
1053
+ self . data . prog . as_os_str ( ) . into ( )
1054
+ }
1055
+
1056
+ #[ cfg( windows) ]
1057
+ fn resolve_program ( & self ) -> OsString {
1058
+ if self . data . prog . extension ( ) . is_some ( ) {
1059
+ // fast path for explicit extension
1060
+ return self . data . prog . as_os_str ( ) . into ( ) ;
1061
+ }
1062
+
1063
+ // mimics `search_paths` behavior:
1064
+ // https://github.com/rust-lang/rust/blob/051478957371ee0084a7c0913941d2a8c4757bb9/library/std/src/sys/pal/windows/process.rs#L482
1065
+
1066
+ const ENV_PATH : & str = "PATH" ;
1067
+
1068
+ // 1. Child paths
1069
+ let paths = self
1070
+ . data
1071
+ . env_changes
1072
+ . iter ( )
1073
+ . filter_map ( |change| match change {
1074
+ EnvChange :: Set ( name, value) if name. eq_ignore_ascii_case ( ENV_PATH ) => Some ( value) ,
1075
+ _ => None ,
1076
+ } )
1077
+ . last ( ) ;
1078
+
1079
+ if let Some ( program_path) = self . find_in_paths ( paths) {
1080
+ return program_path;
1081
+ }
1082
+
1083
+ // 2. Application path
1084
+ let paths = env:: current_exe ( ) . ok ( ) . map ( |mut path| {
1085
+ path. pop ( ) ;
1086
+ OsString :: from ( path)
1087
+ } ) ;
1088
+
1089
+ if let Some ( program_path) = self . find_in_paths ( paths. as_ref ( ) ) {
1090
+ return program_path;
1091
+ }
1092
+
1093
+ // 3 & 4. System paths
1094
+ // Sort of compromise: use %SystemRoot% to avoid adding an additional dependency on the `windows` crate.
1095
+ // Usually %SystemRoot% expands to 'C:\WINDOWS' and 'C:\WINDOWS\SYSTEM32' exists in `PATH`,
1096
+ // so the compromise covers both `GetSystemDirectoryW` and `GetWindowsDirectoryW` cases.
1097
+ let paths = self . shell . var_os ( "SystemRoot" ) ;
1098
+ if let Some ( program_path) = self . find_in_paths ( paths. as_ref ( ) ) {
1099
+ return program_path;
1100
+ }
1101
+
1102
+ // 5. Parent paths
1103
+ let paths = self . shell . var_os ( ENV_PATH ) ;
1104
+ if let Some ( program_path) = self . find_in_paths ( paths. as_ref ( ) ) {
1105
+ return program_path;
1106
+ }
1107
+
1108
+ return self . data . prog . as_os_str ( ) . into ( ) ;
1109
+ }
1110
+
1111
+ fn find_in_paths ( & self , paths : Option < & OsString > ) -> Option < OsString > {
1112
+ paths. and_then ( |paths| {
1113
+ for folder in env:: split_paths ( & paths) . filter ( |p| !p. as_os_str ( ) . is_empty ( ) ) {
1114
+ for ext in [ "cmd" , "bat" ] {
1115
+ let path = folder. join ( self . data . prog . with_extension ( ext) ) ;
1116
+ if std:: fs:: metadata ( & path) . is_ok ( ) {
1117
+ return Some ( path. into_os_string ( ) ) ;
1118
+ }
1119
+ }
1120
+ }
1121
+
1122
+ None
1123
+ } )
1124
+ }
1125
+
1051
1126
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
- } ;
1127
+ let program = self . resolve_program ( ) ;
1128
+ let mut res = Command :: new ( program) ;
1129
+
1130
+ res. args ( & self . data . args ) ;
1131
+ res. current_dir ( self . shell . current_dir ( ) ) ;
1069
1132
1070
1133
for ( key, val) in & * self . shell . env . borrow ( ) {
1071
1134
res. env ( key, val) ;
0 commit comments