33//! This module provides the [`ExecutionContext`] type, which holds global configuration
44//! relevant during the execution of commands in bootstrap. This includes dry-run
55//! mode, verbosity level, and behavior on failure.
6+ #![ allow( warnings) ]
7+ use std:: collections:: HashMap ;
68use std:: panic:: Location ;
79use std:: process:: Child ;
810use std:: sync:: { Arc , Mutex } ;
911
1012use crate :: core:: config:: DryRun ;
1113#[ cfg( feature = "tracing" ) ]
1214use crate :: trace_cmd;
15+ use crate :: utils:: exec:: CommandCacheKey ;
1316use crate :: { BehaviorOnFailure , BootstrapCommand , CommandOutput , OutputMode , exit} ;
1417
1518#[ derive( Clone , Default ) ]
@@ -18,6 +21,26 @@ pub struct ExecutionContext {
1821 verbose : u8 ,
1922 pub fail_fast : bool ,
2023 delayed_failures : Arc < Mutex < Vec < String > > > ,
24+ command_cache : Arc < CommandCache > ,
25+ }
26+
27+ #[ derive( Default ) ]
28+ pub struct CommandCache {
29+ cache : Mutex < HashMap < CommandCacheKey , CommandOutput > > ,
30+ }
31+
32+ impl CommandCache {
33+ pub fn new ( ) -> Self {
34+ Self { cache : Mutex :: new ( HashMap :: new ( ) ) }
35+ }
36+
37+ pub fn get ( & self , key : & CommandCacheKey ) -> Option < CommandOutput > {
38+ self . cache . lock ( ) . unwrap ( ) . get ( key) . cloned ( )
39+ }
40+
41+ pub fn insert ( & self , key : CommandCacheKey , output : CommandOutput ) {
42+ self . cache . lock ( ) . unwrap ( ) . insert ( key, output) ;
43+ }
2144}
2245
2346impl ExecutionContext {
@@ -123,7 +146,24 @@ impl ExecutionContext {
123146 stdout : OutputMode ,
124147 stderr : OutputMode ,
125148 ) -> CommandOutput {
126- self . start ( command, stdout, stderr) . wait_for_output ( self )
149+ let cache_key = command. cache_key ( ) ;
150+
151+ if let Some ( cached_output) = self . command_cache . get ( & cache_key) {
152+ command. mark_as_executed ( ) ;
153+ if self . dry_run ( ) && !command. run_always {
154+ return CommandOutput :: default ( ) ;
155+ }
156+ self . verbose ( || println ! ( "Cache hit: {:?}" , command) ) ;
157+ return cached_output;
158+ }
159+
160+ let output = self . start ( command, stdout, stderr) . wait_for_output ( self ) ;
161+
162+ if output != CommandOutput :: default ( ) {
163+ self . command_cache . insert ( cache_key, output. clone ( ) ) ;
164+ }
165+
166+ output
127167 }
128168
129169 fn fail ( & self , message : & str , output : CommandOutput ) -> ! {
0 commit comments