Skip to content

Commit 3cc09b8

Browse files
authored
build: centralize warm-build fast-path contract (#196)
* build: centralize warm-build fast-path contract * build: address fast-path review fixes
1 parent dab5a0c commit 3cc09b8

10 files changed

Lines changed: 479 additions & 733 deletions

File tree

crates/fbuild-build/src/avr/orchestrator.rs

Lines changed: 21 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ use fbuild_core::{Platform, Result};
1919
use serde::Serialize;
2020

2121
use crate::build_fingerprint::{
22-
hash_watch_set_stamps_cached, save_json, stable_hash_json, FastPathInputs,
23-
PersistedBuildFingerprint, BUILD_FINGERPRINT_VERSION,
22+
expected_fast_path_artifacts, stable_hash_json, FastPathCheckInputs, FastPathContract,
23+
FastPathPersistInputs, BUILD_FINGERPRINT_VERSION,
2424
};
25-
use crate::compile_database::{CompileDatabase, TargetArchitecture};
25+
use crate::compile_database::TargetArchitecture;
2626
use crate::compiler::Compiler as _;
2727
use crate::pipeline;
28-
use crate::zccache::FingerprintWatch;
2928
use crate::{BuildOrchestrator, BuildParams, BuildResult, SourceScanner};
3029

3130
use super::avr_compiler::AvrCompiler;
@@ -67,33 +66,6 @@ fn profile_label(profile: fbuild_core::BuildProfile) -> &'static str {
6766
}
6867
}
6968

70-
fn build_fingerprint_path(build_dir: &Path) -> PathBuf {
71-
build_dir.join("build_fingerprint.json")
72-
}
73-
74-
/// Build the watch set for the AVR fast-path check.
75-
///
76-
/// Covers the project directory (sketch + local `lib/`) and the
77-
/// resolved libraries directory if present. Mirrors the ESP32
78-
/// orchestrator's policy — any directory that can produce a source
79-
/// file consumed by the build must be watched, or we risk reusing
80-
/// stale artifacts.
81-
fn collect_fast_path_watches(build_dir: &Path, project_dir: &Path) -> Vec<FingerprintWatch> {
82-
let mut watches = Vec::new();
83-
if let Some(watch) =
84-
crate::build_fingerprint::fast_path_watch("project", build_dir, project_dir)
85-
{
86-
watches.push(watch);
87-
}
88-
let resolved_libs_dir = build_dir.join("libs");
89-
if let Some(watch) =
90-
crate::build_fingerprint::fast_path_watch("dep_libs", build_dir, &resolved_libs_dir)
91-
{
92-
watches.push(watch);
93-
}
94-
watches
95-
}
96-
9769
impl BuildOrchestrator for AvrOrchestrator {
9870
fn platform(&self) -> Platform {
9971
Platform::AtmelAvr
@@ -150,7 +122,6 @@ impl BuildOrchestrator for AvrOrchestrator {
150122
// This lives here rather than before `ensure_installed` so the hashed
151123
// toolchain/framework paths reflect the real install location.
152124
let build_dir = &ctx.build_dir;
153-
let fingerprint_path = build_fingerprint_path(build_dir);
154125
let metadata_hash = stable_hash_json(&AvrFingerprintMetadata {
155126
version: BUILD_FINGERPRINT_VERSION,
156127
env_name: params.env_name.clone(),
@@ -168,31 +139,29 @@ impl BuildOrchestrator for AvrOrchestrator {
168139
max_flash: ctx.board.max_flash,
169140
max_ram: ctx.board.max_ram,
170141
})?;
171-
let fingerprint_watches = {
142+
let (fast_elf, [fast_hex], fast_compile_db) =
143+
expected_fast_path_artifacts(build_dir, &params.project_dir, ["firmware.hex"]);
144+
let fast_path = {
172145
let _g = perf.phase("fp-watches-collect");
173-
collect_fast_path_watches(build_dir, &params.project_dir)
146+
FastPathContract::for_project_outputs(
147+
build_dir,
148+
&params.project_dir,
149+
[fast_elf.clone(), fast_hex.clone(), fast_compile_db.clone()],
150+
)
174151
};
175152

176153
if !params.compiledb_only
177154
&& !params.symbol_analysis
178155
&& params.symbol_analysis_path.is_none()
179156
{
180157
let _fast_path_phase = perf.phase("fast-path-check");
181-
let fast_elf = build_dir.join("firmware.elf");
182-
let fast_hex = build_dir.join("firmware.hex");
183-
let fast_compile_db =
184-
CompileDatabase::expected_output_path(build_dir, &params.project_dir);
185-
let required_artifacts = [fast_elf.clone(), fast_hex.clone(), fast_compile_db.clone()];
186-
let inputs = FastPathInputs {
187-
fingerprint_path: &fingerprint_path,
158+
let inputs = FastPathCheckInputs {
188159
metadata_hash: &metadata_hash,
189-
watches: &fingerprint_watches,
190-
required_artifacts: &required_artifacts,
191160
extra_artifact_ok: None,
192161
watch_set_cache: params.watch_set_cache.as_deref(),
193162
compiler_cache: compiler_cache.as_deref(),
194163
};
195-
if let Some(hit) = crate::build_fingerprint::fast_path_check(&inputs)? {
164+
if let Some(hit) = crate::build_fingerprint::fast_path_check(&fast_path, &inputs)? {
196165
ctx.build_log
197166
.push("No-op fingerprint matched; reusing existing AVR artifacts.".to_string());
198167
let elapsed = start.elapsed().as_secs_f64();
@@ -315,35 +284,15 @@ impl BuildOrchestrator for AvrOrchestrator {
315284
&& !params.symbol_analysis
316285
&& params.symbol_analysis_path.is_none()
317286
{
318-
let persisted_fingerprint = PersistedBuildFingerprint {
319-
version: BUILD_FINGERPRINT_VERSION,
320-
metadata_hash: metadata_hash.clone(),
321-
file_set_hash: match hash_watch_set_stamps_cached(
322-
&fingerprint_watches,
323-
params.watch_set_cache.as_deref(),
324-
) {
325-
Ok(hash) => Some(hash),
326-
Err(e) => {
327-
tracing::warn!("failed to hash watched inputs for fingerprint save: {}", e);
328-
None
329-
}
287+
crate::build_fingerprint::persist_fast_path_success(
288+
&fast_path,
289+
&FastPathPersistInputs {
290+
metadata_hash: &metadata_hash,
291+
size_info: build_result.size_info.clone(),
292+
watch_set_cache: params.watch_set_cache.as_deref(),
293+
compiler_cache: compiler_cache.as_deref(),
330294
},
331-
size_info: build_result.size_info.clone(),
332-
};
333-
if let Err(e) = save_json(&fingerprint_path, &persisted_fingerprint) {
334-
tracing::warn!("failed to write build fingerprint: {}", e);
335-
}
336-
if let Some(ref zcc) = compiler_cache {
337-
for watch in &fingerprint_watches {
338-
if let Err(e) = crate::zccache::mark_fingerprint_success(zcc, watch) {
339-
tracing::warn!(
340-
"failed to mark zccache fingerprint success for {}: {}",
341-
watch.root.display(),
342-
e
343-
);
344-
}
345-
}
346-
}
295+
);
347296
}
348297

349298
Ok(build_result)

crates/fbuild-build/src/build_fingerprint/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ nothing relevant has changed.
1111
primitives (`hash_watch_set`, `hash_watch_set_stamps`,
1212
`hash_watch_set_stamps_cached`), and the `WatchSetStampCache` trait
1313
the daemon implements for cross-invocation memoisation.
14-
- **`fast_path.rs`** -- Shared fast-path check extracted from the
15-
ESP32 orchestrator. Takes a `FastPathInputs` (metadata hash,
16-
watches, required artifacts, optional zccache + stamp-cache) and
17-
returns `Some(FastPathHit)` when the caller can skip the pipeline
18-
entirely. Used by ESP32 + AVR today; Teensy / RP2040 / STM32 will
19-
follow.
14+
- **`fast_path.rs`** -- Shared warm-build cache contract. Orchestrators
15+
declare required outputs through `FastPathContract`, then use
16+
`fast_path_check` for reuse decisions and
17+
`persist_fast_path_success` to write `build_fingerprint.json` and
18+
mark zccache watch roots. Used by ESP32, AVR, Teensy, RP2040, SAM,
19+
NRF52, and Renesas.

0 commit comments

Comments
 (0)