@@ -37,9 +37,14 @@ enum CommandState<'a> {
37
37
stdout : OutputMode ,
38
38
stderr : OutputMode ,
39
39
executed_at : & ' a Location < ' a > ,
40
+ cache_key : Option < CommandCacheKey > ,
40
41
} ,
41
42
}
42
43
44
+ pub struct DeferredCommand < ' a > {
45
+ state : CommandState < ' a > ,
46
+ }
47
+
43
48
impl CommandCache {
44
49
pub fn new ( ) -> Self {
45
50
Self { cache : Mutex :: new ( HashMap :: new ( ) ) }
@@ -122,13 +127,33 @@ impl ExecutionContext {
122
127
stdout : OutputMode ,
123
128
stderr : OutputMode ,
124
129
) -> DeferredCommand < ' a > {
130
+ let cache_key = command. cache_key ( ) ;
131
+
132
+ if let Some ( cached_output) = cache_key. as_ref ( ) . and_then ( |key| self . command_cache . get ( key) )
133
+ {
134
+ command. mark_as_executed ( ) ;
135
+
136
+ self . verbose ( || println ! ( "Cache hit: {:?}" , command) ) ;
137
+
138
+ return DeferredCommand { state : CommandState :: Cached ( cached_output) } ;
139
+ }
140
+
125
141
command. mark_as_executed ( ) ;
126
142
127
143
let created_at = command. get_created_location ( ) ;
128
144
let executed_at = std:: panic:: Location :: caller ( ) ;
129
145
130
146
if self . dry_run ( ) && !command. run_always {
131
- return DeferredCommand { process : None , stdout, stderr, command, executed_at } ;
147
+ return DeferredCommand {
148
+ state : CommandState :: Deferred {
149
+ process : None ,
150
+ command,
151
+ stdout,
152
+ stderr,
153
+ executed_at,
154
+ cache_key,
155
+ } ,
156
+ } ;
132
157
}
133
158
134
159
#[ cfg( feature = "tracing" ) ]
@@ -144,7 +169,16 @@ impl ExecutionContext {
144
169
145
170
let child = cmd. spawn ( ) ;
146
171
147
- DeferredCommand { process : Some ( child) , stdout, stderr, command, executed_at }
172
+ DeferredCommand {
173
+ state : CommandState :: Deferred {
174
+ process : Some ( child) ,
175
+ command,
176
+ stdout,
177
+ stderr,
178
+ executed_at,
179
+ cache_key,
180
+ } ,
181
+ }
148
182
}
149
183
150
184
/// Execute a command and return its output.
@@ -212,33 +246,53 @@ impl AsRef<ExecutionContext> for ExecutionContext {
212
246
}
213
247
}
214
248
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
249
impl < ' a > DeferredCommand < ' a > {
224
250
pub fn wait_for_output ( mut self , exec_ctx : impl AsRef < ExecutionContext > ) -> CommandOutput {
251
+ match self . state {
252
+ CommandState :: Cached ( output) => output,
253
+ CommandState :: Deferred { process, command, stdout, stderr, executed_at, cache_key } => {
254
+ let exec_ctx = exec_ctx. as_ref ( ) ;
255
+
256
+ let output =
257
+ Self :: execute_process ( process, command, stdout, stderr, executed_at, exec_ctx) ;
258
+
259
+ if ( !exec_ctx. dry_run ( ) || command. run_always )
260
+ && cache_key. is_some ( )
261
+ && output. status ( ) . is_some ( )
262
+ {
263
+ exec_ctx. command_cache . insert ( cache_key. unwrap ( ) , output. clone ( ) ) ;
264
+ }
265
+
266
+ output
267
+ }
268
+ }
269
+ }
270
+
271
+ pub fn execute_process (
272
+ mut process : Option < Result < Child , std:: io:: Error > > ,
273
+ command : & mut BootstrapCommand ,
274
+ stdout : OutputMode ,
275
+ stderr : OutputMode ,
276
+ executed_at : & ' a std:: panic:: Location < ' a > ,
277
+ exec_ctx : & ExecutionContext ,
278
+ ) -> CommandOutput {
225
279
let exec_ctx = exec_ctx. as_ref ( ) ;
226
280
227
- let process = match self . process . take ( ) {
281
+ let process = match process. take ( ) {
228
282
Some ( p) => p,
229
283
None => return CommandOutput :: default ( ) ,
230
284
} ;
231
285
232
- let created_at = self . command . get_created_location ( ) ;
233
- let executed_at = self . executed_at ;
286
+ let created_at = command. get_created_location ( ) ;
287
+ let executed_at = executed_at;
234
288
235
289
let mut message = String :: new ( ) ;
236
290
237
291
let output = match process {
238
292
Ok ( child) => match child. wait_with_output ( ) {
239
293
Ok ( result) if result. status . success ( ) => {
240
294
// Successful execution
241
- CommandOutput :: from_output ( result, self . stdout , self . stderr )
295
+ CommandOutput :: from_output ( result, stdout, stderr)
242
296
}
243
297
Ok ( result) => {
244
298
// Command ran but failed
@@ -251,16 +305,16 @@ Command {:?} did not execute successfully.
251
305
Expected success, got {}
252
306
Created at: {created_at}
253
307
Executed at: {executed_at}"# ,
254
- self . command, result. status,
308
+ command, result. status,
255
309
)
256
310
. unwrap ( ) ;
257
311
258
- let output = CommandOutput :: from_output ( result, self . stdout , self . stderr ) ;
312
+ let output = CommandOutput :: from_output ( result, stdout, stderr) ;
259
313
260
- if self . stdout . captures ( ) {
314
+ if stdout. captures ( ) {
261
315
writeln ! ( message, "\n STDOUT ----\n {}" , output. stdout( ) . trim( ) ) . unwrap ( ) ;
262
316
}
263
- if self . stderr . captures ( ) {
317
+ if stderr. captures ( ) {
264
318
writeln ! ( message, "\n STDERR ----\n {}" , output. stderr( ) . trim( ) ) . unwrap ( ) ;
265
319
}
266
320
@@ -274,11 +328,11 @@ Executed at: {executed_at}"#,
274
328
message,
275
329
"\n \n Command {:?} did not execute successfully.\
276
330
\n It was not possible to execute the command: {e:?}",
277
- self . command
331
+ command
278
332
)
279
333
. unwrap ( ) ;
280
334
281
- CommandOutput :: did_not_start ( self . stdout , self . stderr )
335
+ CommandOutput :: did_not_start ( stdout, stderr)
282
336
}
283
337
} ,
284
338
Err ( e) => {
@@ -289,16 +343,16 @@ Executed at: {executed_at}"#,
289
343
message,
290
344
"\n \n Command {:?} did not execute successfully.\
291
345
\n It was not possible to execute the command: {e:?}",
292
- self . command
346
+ command
293
347
)
294
348
. unwrap ( ) ;
295
349
296
- CommandOutput :: did_not_start ( self . stdout , self . stderr )
350
+ CommandOutput :: did_not_start ( stdout, stderr)
297
351
}
298
352
} ;
299
353
300
354
if !output. is_success ( ) {
301
- match self . command . failure_behavior {
355
+ match command. failure_behavior {
302
356
BehaviorOnFailure :: DelayFail => {
303
357
if exec_ctx. fail_fast {
304
358
exec_ctx. fail ( & message, output) ;
0 commit comments