diff --git a/crates/fbuild-daemon/src/main.rs b/crates/fbuild-daemon/src/main.rs index b3c1895c..0414a447 100644 --- a/crates/fbuild-daemon/src/main.rs +++ b/crates/fbuild-daemon/src/main.rs @@ -254,7 +254,7 @@ async fn main() { let identity = fbuild_paths::running_process::DaemonCacheIdentity::discover(); let claim = fbuild_paths::daemon_ownership::OwnerClaim { pid: std::process::id(), - exe, + exe: exe.into(), version: env!("CARGO_PKG_VERSION").to_string(), mode: identity.mode.to_string(), cache_root_key: identity.cache_root_key.clone(), diff --git a/crates/fbuild-library/src/library/esp32_framework/libs.rs b/crates/fbuild-library/src/library/esp32_framework/libs.rs index 52388a06..15ed7b70 100644 --- a/crates/fbuild-library/src/library/esp32_framework/libs.rs +++ b/crates/fbuild-library/src/library/esp32_framework/libs.rs @@ -60,13 +60,33 @@ fn mcu_sdk_complete(mcu_dir: &Path) -> bool { && mcu_dir.join("lib").join("libfreertos.a").exists() } -/// A skeleton archive's package manifest proves that its MCU payload was -/// extracted. Package metadata is merged beside the MCU directories, not -/// inside them. Use this only after extracting the requested skeleton, never -/// to skip its download. -fn mcu_skeleton_extracted(tools_dir: &Path, mcu: &str) -> bool { - let sdk_dir = tools_dir.join(NEW_SDK_LAYOUT); - sdk_dir.join(mcu).is_dir() && sdk_dir.join("dependencies.lock").is_file() +/// Merge a requested MCU directory from a skeleton archive, regardless of a +/// package wrapper directory added by the archive producer. Returns whether +/// the archive actually contained the requested MCU payload. +fn merge_skeleton_mcu_entries( + temp_dir: &Path, + tools_dir: &Path, + mcu: &str, +) -> fbuild_core::Result { + fn visit(dir: &Path, destination: &Path, mcu: &str) -> fbuild_core::Result { + let mut found = false; + for entry in std::fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if !path.is_dir() { + continue; + } + if entry.file_name() == mcu { + copy_dir_recursive(&path, destination)?; + found = true; + } else { + found |= visit(&path, destination, mcu)?; + } + } + Ok(found) + } + + visit(temp_dir, &tools_dir.join(NEW_SDK_LAYOUT).join(mcu), mcu) } fn patch_mcu_compatibility(mcu_dir: &Path, mcu: &str) -> fbuild_core::Result<()> { @@ -187,10 +207,10 @@ impl Esp32Framework { // Skeleton archives such as c2_arduino_compile_skeleton.zip extract as // a direct esp32c2/ directory. Merge direct MCU roots into the new SDK // layout so sdk_mcu_dir() finds the completed tree. - merge_sdk_archive_entries(temp_dir.path(), &tools_dir)?; + let merged_skeleton = merge_skeleton_mcu_entries(temp_dir.path(), &tools_dir, mcu)?; for mcu_dir in mcu_sdk_dir_candidates(&tools_dir, mcu) { - if mcu_sdk_complete(&mcu_dir) || mcu_skeleton_extracted(&tools_dir, mcu) { + if mcu_sdk_complete(&mcu_dir) || merged_skeleton { patch_mcu_compatibility(&mcu_dir, mcu)?; tracing::info!("{} skeleton libs installed", mcu); return Ok(()); @@ -244,15 +264,22 @@ mod tests { } #[test] - fn skeleton_marker_requires_package_manifest() { + fn skeleton_merge_finds_mcu_under_package_wrapper() { let tmp = tempfile::TempDir::new().unwrap(); let tools_dir = tmp.path().join("tools"); - let sdk_dir = tools_dir.join(NEW_SDK_LAYOUT); - std::fs::create_dir_all(sdk_dir.join("esp32c2")).unwrap(); + let archive = tmp.path().join("archive"); + let source_mcu = archive.join("package-wrapper").join("esp32c2"); + write(&source_mcu.join("lib").join("libfreertos.a"), ""); - assert!(!mcu_skeleton_extracted(&tools_dir, "esp32c2")); - write(&sdk_dir.join("dependencies.lock"), ""); - assert!(mcu_skeleton_extracted(&tools_dir, "esp32c2")); + assert!(merge_skeleton_mcu_entries(&archive, &tools_dir, "esp32c2").unwrap()); + assert!( + tools_dir + .join(NEW_SDK_LAYOUT) + .join("esp32c2") + .join("lib") + .join("libfreertos.a") + .is_file() + ); } #[test] diff --git a/crates/fbuild-paths/src/daemon_ownership.rs b/crates/fbuild-paths/src/daemon_ownership.rs index 14a1aed5..91910935 100644 --- a/crates/fbuild-paths/src/daemon_ownership.rs +++ b/crates/fbuild-paths/src/daemon_ownership.rs @@ -22,9 +22,10 @@ //! the lock when the holding process exits (including a hard kill); the //! file itself is not the lock. -use std::path::{Path, PathBuf}; +use std::path::Path; use fbuild_core::file_lock::{self, FileLockGuard, FileLockMode}; +use fbuild_core::path::NormalizedPath; use crate::get_daemon_dir; @@ -91,18 +92,18 @@ pub fn try_acquire_spawn_lock_at(path: &Path) -> Option { } /// Path to the root-ownership lock file. -pub fn root_owner_lock_path() -> PathBuf { - get_daemon_dir().join(ROOT_OWNER_LOCK_NAME) +pub fn root_owner_lock_path() -> NormalizedPath { + get_daemon_dir().join(ROOT_OWNER_LOCK_NAME).into() } /// Path to the spawn-herd lock file. -pub fn spawn_lock_path() -> PathBuf { - get_daemon_dir().join(SPAWN_LOCK_NAME) +pub fn spawn_lock_path() -> NormalizedPath { + get_daemon_dir().join(SPAWN_LOCK_NAME).into() } /// Path to the [`OwnerClaim`] JSON file. -pub fn owner_claim_path() -> PathBuf { - get_daemon_dir().join(OWNER_CLAIM_NAME) +pub fn owner_claim_path() -> NormalizedPath { + get_daemon_dir().join(OWNER_CLAIM_NAME).into() } /// Advisory claim written by the daemon after it has acquired root @@ -117,7 +118,7 @@ pub fn owner_claim_path() -> PathBuf { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct OwnerClaim { pub pid: u32, - pub exe: PathBuf, + pub exe: NormalizedPath, /// `env!("CARGO_PKG_VERSION")` of the daemon that wrote the claim. pub version: String, /// `"dev"` | `"prod"`. @@ -157,6 +158,7 @@ pub fn remove_owner_claim() { #[cfg(test)] mod tests { use super::*; + use std::path::PathBuf; use tempfile::TempDir; #[test] @@ -241,7 +243,7 @@ mod tests { let path = temp.path().join(OWNER_CLAIM_NAME); let claim = OwnerClaim { pid: 4242, - exe: PathBuf::from("/usr/local/bin/fbuild-daemon"), + exe: PathBuf::from("/usr/local/bin/fbuild-daemon").into(), version: "9.9.9".to_string(), mode: "dev".to_string(), cache_root_key: "abc123".to_string(),