diff --git a/Cargo.lock b/Cargo.lock index 6f178b54..b79277a6 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 c0fdbf3d..ff6f3427 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/Cargo.toml b/crates/fbuild-build/Cargo.toml index 9aee3bb4..1bc47f3a 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 159997de..56ceb934 100644 --- a/crates/fbuild-build/tests/zccache_embedded_smoke.rs +++ b/crates/fbuild-build/tests/zccache_embedded_smoke.rs @@ -5,8 +5,45 @@ //! with it). use fbuild_build::zccache_embedded::FbuildZccacheService; +use fbuild_core::path::NormalizedPath; use zccache::embedded::ShutdownMode; +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| { + path_dirs + .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 = NormalizedPath::new(std::path::Path::new(&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) = on_path(name) { + 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 +79,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 75d34859..650a930b 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"