Skip to content

Commit 1e4cd7f

Browse files
committed
refactor deferred command and make it compatible with new commandstate
1 parent 0a672a9 commit 1e4cd7f

File tree

1 file changed

+77
-23
lines changed

1 file changed

+77
-23
lines changed

src/bootstrap/src/utils/execution_context.rs

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,14 @@ enum CommandState<'a> {
3737
stdout: OutputMode,
3838
stderr: OutputMode,
3939
executed_at: &'a Location<'a>,
40+
cache_key: Option<CommandCacheKey>,
4041
},
4142
}
4243

44+
pub struct DeferredCommand<'a> {
45+
state: CommandState<'a>,
46+
}
47+
4348
impl CommandCache {
4449
pub fn new() -> Self {
4550
Self { cache: Mutex::new(HashMap::new()) }
@@ -122,13 +127,33 @@ impl ExecutionContext {
122127
stdout: OutputMode,
123128
stderr: OutputMode,
124129
) -> 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+
125141
command.mark_as_executed();
126142

127143
let created_at = command.get_created_location();
128144
let executed_at = std::panic::Location::caller();
129145

130146
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+
};
132157
}
133158

134159
#[cfg(feature = "tracing")]
@@ -144,7 +169,16 @@ impl ExecutionContext {
144169

145170
let child = cmd.spawn();
146171

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+
}
148182
}
149183

150184
/// Execute a command and return its output.
@@ -212,33 +246,53 @@ impl AsRef<ExecutionContext> for ExecutionContext {
212246
}
213247
}
214248

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-
223249
impl<'a> DeferredCommand<'a> {
224250
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 {
225279
let exec_ctx = exec_ctx.as_ref();
226280

227-
let process = match self.process.take() {
281+
let process = match process.take() {
228282
Some(p) => p,
229283
None => return CommandOutput::default(),
230284
};
231285

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;
234288

235289
let mut message = String::new();
236290

237291
let output = match process {
238292
Ok(child) => match child.wait_with_output() {
239293
Ok(result) if result.status.success() => {
240294
// Successful execution
241-
CommandOutput::from_output(result, self.stdout, self.stderr)
295+
CommandOutput::from_output(result, stdout, stderr)
242296
}
243297
Ok(result) => {
244298
// Command ran but failed
@@ -251,16 +305,16 @@ Command {:?} did not execute successfully.
251305
Expected success, got {}
252306
Created at: {created_at}
253307
Executed at: {executed_at}"#,
254-
self.command, result.status,
308+
command, result.status,
255309
)
256310
.unwrap();
257311

258-
let output = CommandOutput::from_output(result, self.stdout, self.stderr);
312+
let output = CommandOutput::from_output(result, stdout, stderr);
259313

260-
if self.stdout.captures() {
314+
if stdout.captures() {
261315
writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
262316
}
263-
if self.stderr.captures() {
317+
if stderr.captures() {
264318
writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
265319
}
266320

@@ -274,11 +328,11 @@ Executed at: {executed_at}"#,
274328
message,
275329
"\n\nCommand {:?} did not execute successfully.\
276330
\nIt was not possible to execute the command: {e:?}",
277-
self.command
331+
command
278332
)
279333
.unwrap();
280334

281-
CommandOutput::did_not_start(self.stdout, self.stderr)
335+
CommandOutput::did_not_start(stdout, stderr)
282336
}
283337
},
284338
Err(e) => {
@@ -289,16 +343,16 @@ Executed at: {executed_at}"#,
289343
message,
290344
"\n\nCommand {:?} did not execute successfully.\
291345
\nIt was not possible to execute the command: {e:?}",
292-
self.command
346+
command
293347
)
294348
.unwrap();
295349

296-
CommandOutput::did_not_start(self.stdout, self.stderr)
350+
CommandOutput::did_not_start(stdout, stderr)
297351
}
298352
};
299353

300354
if !output.is_success() {
301-
match self.command.failure_behavior {
355+
match command.failure_behavior {
302356
BehaviorOnFailure::DelayFail => {
303357
if exec_ctx.fail_fast {
304358
exec_ctx.fail(&message, output);

0 commit comments

Comments
 (0)