3
3
//! This module provides the [`ExecutionContext`] type, which holds global configuration
4
4
//! relevant during the execution of commands in bootstrap. This includes dry-run
5
5
//! mode, verbosity level, and behavior on failure.
6
- #![ allow( warnings) ]
7
6
use std:: collections:: HashMap ;
8
7
use std:: panic:: Location ;
9
8
use std:: process:: Child ;
@@ -37,14 +36,15 @@ enum CommandState<'a> {
37
36
stdout : OutputMode ,
38
37
stderr : OutputMode ,
39
38
executed_at : & ' a Location < ' a > ,
39
+ cache_key : Option < CommandCacheKey > ,
40
40
} ,
41
41
}
42
42
43
- impl CommandCache {
44
- pub fn new ( ) -> Self {
45
- Self { cache : Mutex :: new ( HashMap :: new ( ) ) }
46
- }
43
+ pub struct DeferredCommand < ' a > {
44
+ state : CommandState < ' a > ,
45
+ }
47
46
47
+ impl CommandCache {
48
48
pub fn get ( & self , key : & CommandCacheKey ) -> Option < CommandOutput > {
49
49
self . cache . lock ( ) . unwrap ( ) . get ( key) . cloned ( )
50
50
}
@@ -122,13 +122,33 @@ impl ExecutionContext {
122
122
stdout : OutputMode ,
123
123
stderr : OutputMode ,
124
124
) -> DeferredCommand < ' a > {
125
+ let cache_key = command. cache_key ( ) ;
126
+
127
+ if let Some ( cached_output) = cache_key. as_ref ( ) . and_then ( |key| self . command_cache . get ( key) )
128
+ {
129
+ command. mark_as_executed ( ) ;
130
+
131
+ self . verbose ( || println ! ( "Cache hit: {command:?}" ) ) ;
132
+
133
+ return DeferredCommand { state : CommandState :: Cached ( cached_output) } ;
134
+ }
135
+
125
136
command. mark_as_executed ( ) ;
126
137
127
138
let created_at = command. get_created_location ( ) ;
128
139
let executed_at = std:: panic:: Location :: caller ( ) ;
129
140
130
141
if self . dry_run ( ) && !command. run_always {
131
- return DeferredCommand { process : None , stdout, stderr, command, executed_at } ;
142
+ return DeferredCommand {
143
+ state : CommandState :: Deferred {
144
+ process : None ,
145
+ command,
146
+ stdout,
147
+ stderr,
148
+ executed_at,
149
+ cache_key,
150
+ } ,
151
+ } ;
132
152
}
133
153
134
154
#[ cfg( feature = "tracing" ) ]
@@ -144,7 +164,16 @@ impl ExecutionContext {
144
164
145
165
let child = cmd. spawn ( ) ;
146
166
147
- DeferredCommand { process : Some ( child) , stdout, stderr, command, executed_at }
167
+ DeferredCommand {
168
+ state : CommandState :: Deferred {
169
+ process : Some ( child) ,
170
+ command,
171
+ stdout,
172
+ stderr,
173
+ executed_at,
174
+ cache_key,
175
+ } ,
176
+ }
148
177
}
149
178
150
179
/// Execute a command and return its output.
@@ -157,29 +186,7 @@ impl ExecutionContext {
157
186
stdout : OutputMode ,
158
187
stderr : OutputMode ,
159
188
) -> CommandOutput {
160
- let cache_key = command. cache_key ( ) ;
161
-
162
- if let Some ( cached_output) = cache_key. as_ref ( ) . and_then ( |key| self . command_cache . get ( key) )
163
- {
164
- command. mark_as_executed ( ) ;
165
-
166
- if self . dry_run ( ) && !command. run_always {
167
- return CommandOutput :: default ( ) ;
168
- }
169
-
170
- self . verbose ( || println ! ( "Cache hit: {:?}" , command) ) ;
171
- return cached_output;
172
- }
173
-
174
- let output = self . start ( command, stdout, stderr) . wait_for_output ( self ) ;
175
-
176
- if !self . dry_run ( ) || command. run_always {
177
- if let Some ( cache_key) = cache_key {
178
- self . command_cache . insert ( cache_key, output. clone ( ) ) ;
179
- }
180
- }
181
-
182
- output
189
+ self . start ( command, stdout, stderr) . wait_for_output ( self )
183
190
}
184
191
185
192
fn fail ( & self , message : & str , output : CommandOutput ) -> ! {
@@ -212,33 +219,50 @@ impl AsRef<ExecutionContext> for ExecutionContext {
212
219
}
213
220
}
214
221
215
- pub struct DeferredCommand < ' a > {
216
- process : Option < Result < Child , std:: io:: Error > > ,
217
- command : & ' a mut BootstrapCommand ,
218
- stdout : OutputMode ,
219
- stderr : OutputMode ,
220
- executed_at : & ' a Location < ' a > ,
221
- }
222
-
223
222
impl < ' a > DeferredCommand < ' a > {
224
- pub fn wait_for_output ( mut self , exec_ctx : impl AsRef < ExecutionContext > ) -> CommandOutput {
225
- let exec_ctx = exec_ctx. as_ref ( ) ;
223
+ pub fn wait_for_output ( self , exec_ctx : impl AsRef < ExecutionContext > ) -> CommandOutput {
224
+ match self . state {
225
+ CommandState :: Cached ( output) => output,
226
+ CommandState :: Deferred { process, command, stdout, stderr, executed_at, cache_key } => {
227
+ let exec_ctx = exec_ctx. as_ref ( ) ;
228
+
229
+ let output =
230
+ Self :: finish_process ( process, command, stdout, stderr, executed_at, exec_ctx) ;
231
+
232
+ if ( !exec_ctx. dry_run ( ) || command. run_always )
233
+ && let ( Some ( cache_key) , Some ( _) ) = ( & cache_key, output. status ( ) )
234
+ {
235
+ exec_ctx. command_cache . insert ( cache_key. clone ( ) , output. clone ( ) ) ;
236
+ }
237
+
238
+ output
239
+ }
240
+ }
241
+ }
226
242
227
- let process = match self . process . take ( ) {
243
+ pub fn finish_process (
244
+ mut process : Option < Result < Child , std:: io:: Error > > ,
245
+ command : & mut BootstrapCommand ,
246
+ stdout : OutputMode ,
247
+ stderr : OutputMode ,
248
+ executed_at : & ' a std:: panic:: Location < ' a > ,
249
+ exec_ctx : & ExecutionContext ,
250
+ ) -> CommandOutput {
251
+ let process = match process. take ( ) {
228
252
Some ( p) => p,
229
253
None => return CommandOutput :: default ( ) ,
230
254
} ;
231
255
232
- let created_at = self . command . get_created_location ( ) ;
233
- let executed_at = self . executed_at ;
256
+ let created_at = command. get_created_location ( ) ;
257
+ let executed_at = executed_at;
234
258
235
259
let mut message = String :: new ( ) ;
236
260
237
261
let output = match process {
238
262
Ok ( child) => match child. wait_with_output ( ) {
239
263
Ok ( result) if result. status . success ( ) => {
240
264
// Successful execution
241
- CommandOutput :: from_output ( result, self . stdout , self . stderr )
265
+ CommandOutput :: from_output ( result, stdout, stderr)
242
266
}
243
267
Ok ( result) => {
244
268
// Command ran but failed
@@ -247,20 +271,20 @@ impl<'a> DeferredCommand<'a> {
247
271
writeln ! (
248
272
message,
249
273
r#"
250
- Command {:?} did not execute successfully.
274
+ Command {command :?} did not execute successfully.
251
275
Expected success, got {}
252
276
Created at: {created_at}
253
277
Executed at: {executed_at}"# ,
254
- self . command , result. status,
278
+ result. status,
255
279
)
256
280
. unwrap ( ) ;
257
281
258
- let output = CommandOutput :: from_output ( result, self . stdout , self . stderr ) ;
282
+ let output = CommandOutput :: from_output ( result, stdout, stderr) ;
259
283
260
- if self . stdout . captures ( ) {
284
+ if stdout. captures ( ) {
261
285
writeln ! ( message, "\n STDOUT ----\n {}" , output. stdout( ) . trim( ) ) . unwrap ( ) ;
262
286
}
263
- if self . stderr . captures ( ) {
287
+ if stderr. captures ( ) {
264
288
writeln ! ( message, "\n STDERR ----\n {}" , output. stderr( ) . trim( ) ) . unwrap ( ) ;
265
289
}
266
290
@@ -272,13 +296,12 @@ Executed at: {executed_at}"#,
272
296
273
297
writeln ! (
274
298
message,
275
- "\n \n Command {:?} did not execute successfully.\
276
- \n It was not possible to execute the command: {e:?}",
277
- self . command
299
+ "\n \n Command {command:?} did not execute successfully.\
300
+ \n It was not possible to execute the command: {e:?}"
278
301
)
279
302
. unwrap ( ) ;
280
303
281
- CommandOutput :: did_not_start ( self . stdout , self . stderr )
304
+ CommandOutput :: did_not_start ( stdout, stderr)
282
305
}
283
306
} ,
284
307
Err ( e) => {
@@ -287,18 +310,17 @@ Executed at: {executed_at}"#,
287
310
288
311
writeln ! (
289
312
message,
290
- "\n \n Command {:?} did not execute successfully.\
291
- \n It was not possible to execute the command: {e:?}",
292
- self . command
313
+ "\n \n Command {command:?} did not execute successfully.\
314
+ \n It was not possible to execute the command: {e:?}"
293
315
)
294
316
. unwrap ( ) ;
295
317
296
- CommandOutput :: did_not_start ( self . stdout , self . stderr )
318
+ CommandOutput :: did_not_start ( stdout, stderr)
297
319
}
298
320
} ;
299
321
300
322
if !output. is_success ( ) {
301
- match self . command . failure_behavior {
323
+ match command. failure_behavior {
302
324
BehaviorOnFailure :: DelayFail => {
303
325
if exec_ctx. fail_fast {
304
326
exec_ctx. fail ( & message, output) ;
0 commit comments