Skip to content

Commit f27e2b5

Browse files
committed
add command cache key
1 parent f4abfac commit f27e2b5

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/bootstrap/src/utils/exec.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ impl OutputMode {
5555
}
5656
}
5757

58+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
59+
pub struct CommandCacheKey {
60+
program: String,
61+
args: Vec<String>,
62+
envs: Vec<(String, String)>,
63+
cwd: Option<PathBuf>,
64+
}
65+
5866
/// Wrapper around `std::process::Command`.
5967
///
6068
/// By default, the command will exit bootstrap if it fails.
@@ -225,6 +233,15 @@ impl<'a> BootstrapCommand {
225233
self.env("TERM", "xterm").args(["--color", "always"]);
226234
}
227235
}
236+
237+
pub fn cache_key(&self) -> CommandCacheKey {
238+
CommandCacheKey {
239+
program: self.program.clone(),
240+
args: self.args.clone(),
241+
envs: self.envs.clone(),
242+
cwd: self.cwd.clone(),
243+
}
244+
}
228245
}
229246

230247
impl Debug for BootstrapCommand {

src/bootstrap/src/utils/execution_context.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
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;
68
use std::panic::Location;
79
use std::process::Child;
810
use std::sync::{Arc, Mutex};
911

1012
use crate::core::config::DryRun;
1113
#[cfg(feature = "tracing")]
1214
use crate::trace_cmd;
15+
use crate::utils::exec::CommandCacheKey;
1316
use 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

2346
impl 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

Comments
 (0)