diff --git a/Cargo.lock b/Cargo.lock index 0ecf0d53..af784056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,7 +951,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fbuild-bench-fastled-examples" -version = "2.5.8" +version = "2.5.9" dependencies = [ "fbuild-core", "fbuild-library-select", @@ -965,7 +965,7 @@ dependencies = [ [[package]] name = "fbuild-build" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "blake3", @@ -1002,7 +1002,7 @@ dependencies = [ [[package]] name = "fbuild-build-arm" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "blake3", @@ -1036,7 +1036,7 @@ dependencies = [ [[package]] name = "fbuild-build-engine" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "blake3", @@ -1069,7 +1069,7 @@ dependencies = [ [[package]] name = "fbuild-build-esp" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "blake3", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "fbuild-build-mcu" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "blake3", @@ -1137,7 +1137,7 @@ dependencies = [ [[package]] name = "fbuild-cli" -version = "2.5.8" +version = "2.5.9" dependencies = [ "blake3", "clap", @@ -1170,7 +1170,7 @@ dependencies = [ [[package]] name = "fbuild-config" -version = "2.5.8" +version = "2.5.9" dependencies = [ "fbuild-core", "fbuild-paths", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "fbuild-core" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "fs2", @@ -1206,7 +1206,7 @@ dependencies = [ [[package]] name = "fbuild-daemon" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "axum", @@ -1246,7 +1246,7 @@ dependencies = [ [[package]] name = "fbuild-deploy" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "espflash", @@ -1275,7 +1275,7 @@ dependencies = [ [[package]] name = "fbuild-header-scan" -version = "2.5.8" +version = "2.5.9" dependencies = [ "criterion", "fbuild-paths", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "fbuild-library" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "axum", @@ -1316,7 +1316,7 @@ dependencies = [ [[package]] name = "fbuild-library-select" -version = "2.5.8" +version = "2.5.9" dependencies = [ "bincode", "blake3", @@ -1336,7 +1336,7 @@ dependencies = [ [[package]] name = "fbuild-packages" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "axum", @@ -1368,7 +1368,7 @@ dependencies = [ [[package]] name = "fbuild-packages-fetch" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "axum", @@ -1397,7 +1397,7 @@ dependencies = [ [[package]] name = "fbuild-paths" -version = "2.5.8" +version = "2.5.9" dependencies = [ "fbuild-core", "serde", @@ -1408,7 +1408,7 @@ dependencies = [ [[package]] name = "fbuild-python" -version = "2.5.8" +version = "2.5.9" dependencies = [ "base64", "fbuild-core", @@ -1430,7 +1430,7 @@ dependencies = [ [[package]] name = "fbuild-serial" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "base64", @@ -1454,7 +1454,7 @@ dependencies = [ [[package]] name = "fbuild-test-support" -version = "2.5.8" +version = "2.5.9" dependencies = [ "fbuild-config", "fbuild-core", @@ -1474,7 +1474,7 @@ dependencies = [ [[package]] name = "fbuild-toolchain" -version = "2.5.8" +version = "2.5.9" dependencies = [ "async-trait", "axum", diff --git a/Cargo.toml b/Cargo.toml index fe2007df..9a2f8ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ exclude = [ libraries = [{ path = "dylints/*" }] [workspace.package] -version = "2.5.8" +version = "2.5.9" edition = "2021" rust-version = "1.94.1" license = "MIT OR Apache-2.0" diff --git a/crates/fbuild-build-arm/src/rp2040/orchestrator.rs b/crates/fbuild-build-arm/src/rp2040/orchestrator.rs index f52f8a20..45413384 100644 --- a/crates/fbuild-build-arm/src/rp2040/orchestrator.rs +++ b/crates/fbuild-build-arm/src/rp2040/orchestrator.rs @@ -26,8 +26,8 @@ use crate::build_fingerprint::{ use crate::compile_database::TargetArchitecture; use crate::compiler::Compiler as _; use crate::framework_libs::{ - library_select_kv_store, resolve_framework_library_sources_active_declared, - resolve_framework_library_sources_cached, warn_if_lib_ldf_mode_unsupported, + library_select_kv_store, resolve_framework_library_selection_active_declared, + resolve_framework_library_selection_cached, warn_if_lib_ldf_mode_unsupported, }; use crate::generic_arm::{ArmCompiler, ArmLinker}; use crate::pipeline; @@ -240,7 +240,7 @@ impl BuildOrchestrator for Rp2040Orchestrator { .flatten() .as_deref(), ); - let framework_library_sources = match library_select_kv_store() { + let framework_library_selection = match library_select_kv_store() { Some(store) => { let key_inputs = fbuild_library_select::cache::CacheKeyInputs { toolchain_triple: "rp2040-arm-none-eabi", @@ -249,7 +249,7 @@ impl BuildOrchestrator for Rp2040Orchestrator { preprocessor_defines: &defines, declared_deps: &declared_deps, }; - resolve_framework_library_sources_cached( + resolve_framework_library_selection_cached( &framework_libs, ¶ms.project_dir, &ctx.src_dir, @@ -257,7 +257,7 @@ impl BuildOrchestrator for Rp2040Orchestrator { store, ) } - None => resolve_framework_library_sources_active_declared( + None => resolve_framework_library_selection_active_declared( &framework_libs, ¶ms.project_dir, &ctx.src_dir, @@ -265,12 +265,14 @@ impl BuildOrchestrator for Rp2040Orchestrator { &declared_deps, ), }; - if !framework_library_sources.is_empty() { + if !framework_library_selection.source_files.is_empty() { tracing::info!( "RP2040 framework library sources added: {}", - framework_library_sources.len() + framework_library_selection.source_files.len() ); - sources.core_sources.extend(framework_library_sources); + sources + .core_sources + .extend(framework_library_selection.source_files.clone()); } // Use the resolved core_dir/variant_dir instead of board.get_include_paths(): // RP2040 board metadata reports `core = earlephilhower`, while the actual @@ -285,7 +287,7 @@ impl BuildOrchestrator for Rp2040Orchestrator { add_rp_family_includes(&framework_include, &ctx.board.mcu, &mut include_dirs); } add_rp_board_includes(&board_props, &framework_dir, &mut include_dirs); - include_dirs.extend(framework.get_framework_library_include_dirs()); + include_dirs.extend(framework_library_selection.include_dirs); include_dirs.push(ctx.src_dir.clone()); pipeline::discover_project_includes(¶ms.project_dir, &mut include_dirs); // Toolchain sysroot includes diff --git a/crates/fbuild-build-engine/src/framework_libs.rs b/crates/fbuild-build-engine/src/framework_libs.rs index 4ce42629..9cbfb7fc 100644 --- a/crates/fbuild-build-engine/src/framework_libs.rs +++ b/crates/fbuild-build-engine/src/framework_libs.rs @@ -60,6 +60,28 @@ pub fn resolve_framework_library_sources_active_declared( defines: &HashMap, declared: &[String], ) -> Vec { + resolve_framework_library_selection_active_declared( + libraries, + project_dir, + src_dir, + defines, + declared, + ) + .source_files +} + +/// Resolve the full active-branch framework-library selection. +/// +/// Orchestrators need both the selected source files and their include +/// directories. Passing every framework library's include directory can make +/// identically named private headers resolve from an unrelated library. +pub fn resolve_framework_library_selection_active_declared( + libraries: &[FrameworkLibrary], + project_dir: &Path, + src_dir: &Path, + defines: &HashMap, + declared: &[String], +) -> fbuild_library_select::Selection { let roots = framework_include_scan_roots(project_dir, src_dir); let filtered = filter_framework_libs_shadowed_by_project(libraries, &roots); let seeds = collect_project_seeds(&roots); @@ -72,7 +94,6 @@ pub fn resolve_framework_library_sources_active_declared( declared, ) .0 - .source_files } /// Warn when a project sets `lib_ldf_mode`, which fbuild does not implement. @@ -280,6 +301,45 @@ pub fn resolve_framework_library_sources_cached( sources } +/// Cached counterpart to [`resolve_framework_library_selection_active_declared`]. +pub fn resolve_framework_library_selection_cached( + libraries: &[FrameworkLibrary], + project_dir: &Path, + src_dir: &Path, + key_inputs: &CacheKeyInputs<'_>, + store: &FileKvStore, +) -> fbuild_library_select::Selection { + let roots = framework_include_scan_roots(project_dir, src_dir); + if libraries.is_empty() { + return fbuild_library_select::Selection::default(); + } + + let filtered = filter_framework_libs_shadowed_by_project(libraries, &roots); + if filtered.is_empty() { + return fbuild_library_select::Selection::default(); + } + + let seeds = collect_project_seeds(&roots); + let search_paths = project_search_paths(&roots); + match resolve_cached(&seeds, &search_paths, &filtered, key_inputs, store) { + Ok(cached) => cached.selection, + Err(err) => { + tracing::warn!( + error = %err, + "library-select cache backend error; falling back to uncached resolve" + ); + fbuild_library_select::resolve_with_stats_active_declared( + &seeds, + &search_paths, + &filtered, + key_inputs.preprocessor_defines, + key_inputs.declared_deps, + ) + .0 + } + } +} + /// Internal helper that returns `(sources, from_cache)` so tests can assert /// hit/miss without the public API surfacing that bit. The hit flag is /// `false` whenever the cache backend errored and we fell back to the @@ -995,4 +1055,58 @@ mod tests { assert!(hit_second, "second call must hit the cache"); assert_eq!(first, second, "cache hit must yield identical sources"); } + + #[test] + fn selected_library_include_dirs_exclude_unrelated_lwip_backends() { + let tmp = tempfile::TempDir::new().unwrap(); + let project_dir = tmp.path().join("project"); + let src_dir = project_dir.join("src"); + std::fs::create_dir_all(&src_dir).unwrap(); + std::fs::write(src_dir.join("main.cpp"), "#include \n").unwrap(); + + let wifi_dir = tmp.path().join("framework/libraries/WiFi/src"); + let cyw43_dir = tmp.path().join("framework/libraries/lwIP_CYW43/src"); + let ethernet_dir = tmp.path().join("framework/libraries/lwIP_Ethernet/src"); + std::fs::create_dir_all(&wifi_dir).unwrap(); + std::fs::create_dir_all(&cyw43_dir).unwrap(); + std::fs::create_dir_all(ðernet_dir).unwrap(); + std::fs::write(wifi_dir.join("WiFi.h"), "#include \n").unwrap(); + std::fs::write(wifi_dir.join("WiFi.cpp"), "").unwrap(); + std::fs::write(cyw43_dir.join("lwIP_CYW43.h"), "").unwrap(); + std::fs::write(ethernet_dir.join("LwipIntfDev.h"), "").unwrap(); + + let libraries = vec![ + FrameworkLibrary { + name: "WiFi".to_string(), + dir: wifi_dir.parent().unwrap().to_path_buf(), + include_dirs: vec![wifi_dir.clone()], + source_files: vec![wifi_dir.join("WiFi.cpp")], + }, + FrameworkLibrary { + name: "lwIP_CYW43".to_string(), + dir: cyw43_dir.parent().unwrap().to_path_buf(), + include_dirs: vec![cyw43_dir.clone()], + source_files: Vec::new(), + }, + FrameworkLibrary { + name: "lwIP_Ethernet".to_string(), + dir: ethernet_dir.parent().unwrap().to_path_buf(), + include_dirs: vec![ethernet_dir.clone()], + source_files: Vec::new(), + }, + ]; + + let selection = resolve_framework_library_selection_active_declared( + &libraries, + &project_dir, + &src_dir, + &HashMap::new(), + &[], + ); + + assert_eq!(selection.source_files, vec![wifi_dir.join("WiFi.cpp")]); + assert!(selection.include_dirs.contains(&wifi_dir)); + assert!(selection.include_dirs.contains(&cyw43_dir)); + assert!(!selection.include_dirs.contains(ðernet_dir)); + } } diff --git a/pyproject.toml b/pyproject.toml index 8c8be701..f79e4d1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fbuild" -version = "2.5.8" +version = "2.5.9" description = "PlatformIO-compatible embedded build tool (Rust implementation)" readme = "README.md" requires-python = ">=3.10"