From d188d8ce664f08fd272b7fa51f0cefbe803e03b9 Mon Sep 17 00:00:00 2001 From: zackees Date: Sat, 1 Aug 2026 22:48:02 -0700 Subject: [PATCH 1/3] test(zccache): verify cold miss and warm hit Bump fbuild to 2.5.7 so the autonomous release publishes the completed running-process 4.8.1 and zccache 1.13.1 cascade. Exercise a real embedded compile twice to prove cache materialization and exported-symbol linkage. Refs #1239. --- Cargo.lock | 42 +++---- Cargo.toml | 2 +- .../tests/zccache_embedded_smoke.rs | 116 ++++++++++++++++++ pyproject.toml | 2 +- 4 files changed, 139 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f178b546..b79277a67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,7 +951,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fbuild-bench-fastled-examples" -version = "2.5.6" +version = "2.5.7" dependencies = [ "fbuild-core", "fbuild-library-select", @@ -965,7 +965,7 @@ dependencies = [ [[package]] name = "fbuild-build" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "blake3", @@ -1002,7 +1002,7 @@ dependencies = [ [[package]] name = "fbuild-build-arm" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "blake3", @@ -1036,7 +1036,7 @@ dependencies = [ [[package]] name = "fbuild-build-engine" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "blake3", @@ -1069,7 +1069,7 @@ dependencies = [ [[package]] name = "fbuild-build-esp" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "blake3", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "fbuild-build-mcu" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "blake3", @@ -1137,7 +1137,7 @@ dependencies = [ [[package]] name = "fbuild-cli" -version = "2.5.6" +version = "2.5.7" dependencies = [ "blake3", "clap", @@ -1170,7 +1170,7 @@ dependencies = [ [[package]] name = "fbuild-config" -version = "2.5.6" +version = "2.5.7" dependencies = [ "fbuild-core", "fbuild-paths", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "fbuild-core" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "fs2", @@ -1206,7 +1206,7 @@ dependencies = [ [[package]] name = "fbuild-daemon" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "axum", @@ -1246,7 +1246,7 @@ dependencies = [ [[package]] name = "fbuild-deploy" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "espflash", @@ -1275,7 +1275,7 @@ dependencies = [ [[package]] name = "fbuild-header-scan" -version = "2.5.6" +version = "2.5.7" dependencies = [ "criterion", "fbuild-paths", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "fbuild-library" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "axum", @@ -1316,7 +1316,7 @@ dependencies = [ [[package]] name = "fbuild-library-select" -version = "2.5.6" +version = "2.5.7" dependencies = [ "bincode", "blake3", @@ -1336,7 +1336,7 @@ dependencies = [ [[package]] name = "fbuild-packages" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "axum", @@ -1368,7 +1368,7 @@ dependencies = [ [[package]] name = "fbuild-packages-fetch" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "axum", @@ -1397,7 +1397,7 @@ dependencies = [ [[package]] name = "fbuild-paths" -version = "2.5.6" +version = "2.5.7" dependencies = [ "fbuild-core", "serde", @@ -1408,7 +1408,7 @@ dependencies = [ [[package]] name = "fbuild-python" -version = "2.5.6" +version = "2.5.7" dependencies = [ "base64", "fbuild-core", @@ -1430,7 +1430,7 @@ dependencies = [ [[package]] name = "fbuild-serial" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "base64", @@ -1454,7 +1454,7 @@ dependencies = [ [[package]] name = "fbuild-test-support" -version = "2.5.6" +version = "2.5.7" dependencies = [ "fbuild-config", "fbuild-core", @@ -1474,7 +1474,7 @@ dependencies = [ [[package]] name = "fbuild-toolchain" -version = "2.5.6" +version = "2.5.7" dependencies = [ "async-trait", "axum", diff --git a/Cargo.toml b/Cargo.toml index c0fdbf3de..ff6f3427a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ exclude = [ libraries = [{ path = "dylints/*" }] [workspace.package] -version = "2.5.6" +version = "2.5.7" edition = "2021" rust-version = "1.94.1" license = "MIT OR Apache-2.0" diff --git a/crates/fbuild-build/tests/zccache_embedded_smoke.rs b/crates/fbuild-build/tests/zccache_embedded_smoke.rs index 159997dea..969720f4d 100644 --- a/crates/fbuild-build/tests/zccache_embedded_smoke.rs +++ b/crates/fbuild-build/tests/zccache_embedded_smoke.rs @@ -7,6 +7,45 @@ use fbuild_build::zccache_embedded::FbuildZccacheService; use zccache::embedded::ShutdownMode; +fn find_c_compiler() -> std::path::PathBuf { + let path_dirs: Vec<_> = + std::env::split_paths(&std::env::var_os("PATH").expect("PATH should be set")).collect(); + let on_path = |name: &str| { + path_dirs + .iter() + .map(|dir| dir.join(name)) + .find(|candidate| candidate.is_file()) + }; + if cfg!(windows) { + if let Some(candidate) = on_path("clang.exe") { + return candidate; + } + if let Some(program_files) = std::env::var_os("ProgramFiles") { + let candidate = std::path::PathBuf::from(program_files) + .join("LLVM") + .join("bin") + .join("clang.exe"); + if candidate.is_file() { + return candidate; + } + } + if let Some(candidate) = on_path("gcc.exe") { + return candidate; + } + panic!("clang.exe or gcc.exe must be installed for this smoke test"); + } + for name in ["cc", "clang", "gcc"] { + if let Some(candidate) = path_dirs + .iter() + .map(|dir| dir.join(name)) + .find(|candidate| candidate.is_file()) + { + return candidate; + } + } + panic!("cc, clang, or gcc must be installed for this smoke test"); +} + /// `FbuildZccacheService::start_in` produces a working service /// handle: the cache root exists on disk, the identity carries our /// product tag, and a graceful shutdown returns cleanly. @@ -42,3 +81,80 @@ async fn embedded_service_starts_and_shuts_down() { .await .expect("graceful shutdown should succeed"); } + +/// A real compile traverses fbuild's embedded zccache boundary twice: the +/// first invocation populates a fresh cache and the second must materialize +/// the object from that cache. This is also a link-time guard against loading +/// two copies of running-process's unmangled `rp_*_public` exports. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn embedded_compilation_cold_miss_then_warm_hit() { + let tmp = tempfile::TempDir::new().expect("tempdir"); + let cache_root = tmp.path().join("zccache"); + let source = tmp.path().join("smoke.c"); + let object = tmp.path().join(if cfg!(windows) { + "smoke.obj" + } else { + "smoke.o" + }); + std::fs::write(&source, "int smoke(void) { return 42; }\n").expect("write source"); + + let svc = FbuildZccacheService::start_in(cache_root) + .await + .expect("embedded service should start"); + let compiler = find_c_compiler(); + let args = vec![ + "-c".to_string(), + source.to_string_lossy().into_owned(), + "-o".to_string(), + object.to_string_lossy().into_owned(), + ]; + let mut compile_env = fbuild_core::subprocess::compile_env_for_build(tmp.path()) + .expect("prepare the same hermetic compile environment used in production"); + compile_env.push(( + "ZCCACHE_WORKTREE_ROOT".to_string(), + tmp.path().to_string_lossy().into_owned(), + )); + + let cold = svc + .compile( + &compiler, + args.clone(), + tmp.path().to_path_buf(), + compile_env.clone(), + ) + .await + .expect("cold embedded compile should succeed"); + assert_eq!( + cold.exit_code, + 0, + "cold compile stderr: {}", + String::from_utf8_lossy(&cold.stderr) + ); + assert!(!cold.cached, "fresh cache unexpectedly reported a hit"); + assert!(object.is_file(), "cold compile should create an object"); + + svc.flush() + .await + .expect("flush cold compile into the cache"); + std::fs::remove_file(&object).expect("remove cold object before warm materialization"); + + let warm = svc + .compile(&compiler, args, tmp.path().to_path_buf(), compile_env) + .await + .expect("warm embedded compile should succeed"); + assert_eq!( + warm.exit_code, + 0, + "warm compile stderr: {}", + String::from_utf8_lossy(&warm.stderr) + ); + assert!( + warm.cached, + "second identical compile should be a cache hit" + ); + assert!(object.is_file(), "warm hit should materialize the object"); + + svc.shutdown(ShutdownMode::Graceful) + .await + .expect("graceful shutdown should succeed"); +} diff --git a/pyproject.toml b/pyproject.toml index 75d34859a..650a930ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fbuild" -version = "2.5.6" +version = "2.5.7" description = "PlatformIO-compatible embedded build tool (Rust implementation)" readme = "README.md" requires-python = ">=3.10" From cf0b11c83a7a7e6a007b000e8e79967d12d6d363 Mon Sep 17 00:00:00 2001 From: zackees Date: Sat, 1 Aug 2026 23:18:06 -0700 Subject: [PATCH 2/3] fix(ci): normalize smoke-test compiler paths Use fbuild_core::path::NormalizedPath for compiler discovery so the dependency cascade acceptance test satisfies the workspace PathBuf dylint. Refs #1239. --- crates/fbuild-build/tests/zccache_embedded_smoke.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/fbuild-build/tests/zccache_embedded_smoke.rs b/crates/fbuild-build/tests/zccache_embedded_smoke.rs index 969720f4d..6dab26253 100644 --- a/crates/fbuild-build/tests/zccache_embedded_smoke.rs +++ b/crates/fbuild-build/tests/zccache_embedded_smoke.rs @@ -5,9 +5,10 @@ //! with it). use fbuild_build::zccache_embedded::FbuildZccacheService; +use fbuild_core::path::NormalizedPath; use zccache::embedded::ShutdownMode; -fn find_c_compiler() -> std::path::PathBuf { +fn find_c_compiler() -> NormalizedPath { let path_dirs: Vec<_> = std::env::split_paths(&std::env::var_os("PATH").expect("PATH should be set")).collect(); let on_path = |name: &str| { @@ -15,13 +16,14 @@ fn find_c_compiler() -> std::path::PathBuf { .iter() .map(|dir| dir.join(name)) .find(|candidate| candidate.is_file()) + .map(NormalizedPath::from) }; if cfg!(windows) { if let Some(candidate) = on_path("clang.exe") { return candidate; } if let Some(program_files) = std::env::var_os("ProgramFiles") { - let candidate = std::path::PathBuf::from(program_files) + let candidate = NormalizedPath::new(std::path::Path::new(&program_files)) .join("LLVM") .join("bin") .join("clang.exe"); @@ -39,6 +41,7 @@ fn find_c_compiler() -> std::path::PathBuf { .iter() .map(|dir| dir.join(name)) .find(|candidate| candidate.is_file()) + .map(NormalizedPath::from) { return candidate; } From 63d518da71b02ebf71c1a01d4682fd13f4ec1c84 Mon Sep 17 00:00:00 2001 From: zackees Date: Sat, 1 Aug 2026 23:34:25 -0700 Subject: [PATCH 3/3] chore: address dependency cascade review Centralize smoke-test compiler lookup and update the embedded zccache pin comment to the released 1.13.1 revision. Refs #1239. --- crates/fbuild-build/Cargo.toml | 8 +++----- crates/fbuild-build/tests/zccache_embedded_smoke.rs | 7 +------ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/crates/fbuild-build/Cargo.toml b/crates/fbuild-build/Cargo.toml index 9aee3bb4f..1bc47f3a0 100644 --- a/crates/fbuild-build/Cargo.toml +++ b/crates/fbuild-build/Cargo.toml @@ -48,11 +48,9 @@ tree-sitter-cpp = { workspace = true } # child processes) was deleted in the same PR; there is no # `--no-default-features` escape hatch. # -# Pinned at zccache 1.12.17 (`5ee292f`, upstream main). soldr's -# `_vender/zccache` submodule currently sits 3 commits behind this at -# `4180af8`; both revs speak the same shared-broker protocol, so the two -# embedded services still interoperate. Re-check that submodule when -# repinning — a protocol-affecting zccache change must land in both. +# Pinned at zccache 1.13.1 (`8cf6dd0`, release tag), which pins +# running-process 4.8.1 at `359b4e3`. Keep both revisions aligned when +# repinning so fbuild links exactly one copy of the exported broker symbols. # # `running-process` in the workspace root must be pinned to the SAME git rev # zccache uses, or its unmangled `rp_*_public` C symbols get linked twice. diff --git a/crates/fbuild-build/tests/zccache_embedded_smoke.rs b/crates/fbuild-build/tests/zccache_embedded_smoke.rs index 6dab26253..56ceb934a 100644 --- a/crates/fbuild-build/tests/zccache_embedded_smoke.rs +++ b/crates/fbuild-build/tests/zccache_embedded_smoke.rs @@ -37,12 +37,7 @@ fn find_c_compiler() -> NormalizedPath { panic!("clang.exe or gcc.exe must be installed for this smoke test"); } for name in ["cc", "clang", "gcc"] { - if let Some(candidate) = path_dirs - .iter() - .map(|dir| dir.join(name)) - .find(|candidate| candidate.is_file()) - .map(NormalizedPath::from) - { + if let Some(candidate) = on_path(name) { return candidate; } }