Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion crates/fbuild-build-engine/src/pipeline/library.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Library compilation helpers: `LibraryBuildEnv`, archiver selection,
//! extra library roots, and the "project-as-library" compile path.
//! extra library roots, the "project-as-library" compile path, and
//! `ensure_lib_deps` for downloading registry/remote `lib_deps` entries
//! before the compiler is created.

use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -303,6 +305,84 @@ pub async fn compile_project_as_library(
}
}

/// Download and compile `lib_deps` entries from `platformio.ini`.
///
/// Called by build orchestrators **before** the compiler is created, so the
/// returned include directories can be folded into the compiler's search path.
/// The caller is responsible for:
///
/// 1. Adding the returned `include_dirs` to the compiler's include list.
/// 2. Passing the returned `archives` to the linker (via
/// `run_sequential_build_with_libs`' `extra_link_inputs` parameter).
///
/// `base_includes` is the set of include directories available before any
/// `lib_deps` are downloaded (core, variant, sketch, toolchain sysroot).
/// `libs_dir` is where downloaded libraries will be staged — callers typically
/// use `<build_dir>/libs`.
///
/// When `lib_deps` is empty this returns `(vec![], vec![])` immediately with
/// no I/O or network access.
///
/// FastLED/fbuild#1276: registry dependencies (`fastled/FastLED@^3.10.3`)
/// were classified by `fbuild sync` but never downloaded by the build
/// orchestrator, so the compile step couldn't find their headers.
#[allow(clippy::too_many_arguments)]
pub async fn ensure_lib_deps(
lib_deps: &[String],
lib_ignore: &[String],
gcc_path: &Path,
gxx_path: &Path,
ar_path: &Path,
c_flags: &[String],
cpp_flags: &[String],
base_includes: &[PathBuf],
project_dir: &Path,
libs_dir: &Path,
verbose: bool,
jobs: usize,
compiler_cache: Option<&Path>,
) -> Result<(Vec<PathBuf>, Vec<PathBuf>)> {
if lib_deps.is_empty() {
return Ok((vec![], vec![]));
}

tracing::info!(
"downloading {} lib_deps to {}",
lib_deps.len(),
libs_dir.display()
);

let lib_result = fbuild_packages::library::library_manager::ensure_libraries(
lib_deps,
lib_ignore,
gcc_path,
gxx_path,
ar_path,
c_flags,
cpp_flags,
base_includes,
project_dir,
libs_dir,
verbose,
jobs,
compiler_cache,
)
.await?;

// FastLED/fbuild#966: sort include dirs so `-I` flags are deterministic
// across checkouts (read_dir order varies by filesystem).
let mut lib_include_dirs = lib_result.include_dirs;
lib_include_dirs.sort();

tracing::info!(
"lib_deps: {} include dirs, {} archives",
lib_include_dirs.len(),
lib_result.archives.len()
);

Ok((lib_include_dirs, lib_result.archives))
}

#[cfg(test)]
mod pick_archiver_tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/fbuild-build-engine/src/pipeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub use compile::{
pub use context::BuildContext;
pub use library::{
LibraryBuildEnv, add_extra_library_include_dirs, compile_extra_libraries,
compile_project_as_library, discover_extra_library_roots, pick_archiver,
compile_project_as_library, discover_extra_library_roots, ensure_lib_deps, pick_archiver,
};
pub use link::{assemble_build_result, handle_link_result};
pub use project_discovery::{discover_project_includes, is_platform_project, is_project_a_library};
Expand Down
69 changes: 65 additions & 4 deletions crates/fbuild-build-mcu/src/avr/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ impl BuildOrchestrator for AvrOrchestrator {
sources.variant_sources.len(),
);

// 6. Build include dirs + compiler
// 6. Build include dirs (initial set — lib_deps dirs are added below).
let defines = ctx.board.get_defines();
// Use the resolved core_dir/variant_dir directly — board.get_include_paths()
// Use the resolved core_dir/variant_dir directly board.get_include_paths()
// uses the raw board core name which may differ from the actual directory
// (e.g. MiniCore's core dir is "MCUdude_corefiles", not "MiniCore").
let mut include_dirs = vec![core_dir.clone(), variant_dir.clone()];
Expand All @@ -244,6 +244,66 @@ impl BuildOrchestrator for AvrOrchestrator {

let mcu_config = super::mcu_config::get_avr_config()?;

// 6a. Download `lib_deps` from the registry / remote URLs before
// creating the compiler, so the downloaded library include directories
// are available during compilation (FastLED/fbuild#1276).
let lib_deps = ctx.config.get_lib_deps(&params.env_name)?;
let lib_ignore = ctx
.config
.get_lib_ignore(&params.env_name)
.unwrap_or_default();
let lib_archives: Vec<PathBuf>;
if !lib_deps.is_empty() {
// Build a temp compiler solely to get the c/cxx flags for library
// compilation. The temp compiler is discarded — the *real* compiler
// is created afterwards with the full include-dir set.
let temp_compiler = AvrCompiler::new(
toolchain.get_gcc_path(),
toolchain.get_gxx_path(),
&ctx.board.mcu,
&ctx.board.f_cpu,
defines.clone(),
include_dirs.clone(),
mcu_config.clone(),
params.profile,
params.verbose,
);
let c_flags_temp = temp_compiler.c_flags();
let cpp_flags_temp = temp_compiler.cpp_flags();
let dep_ar_path = toolchain.get_ar_path();
let dep_gcc_ar_path = toolchain.get_gcc_ar_path();
let dep_lib_ar_path = pipeline::pick_archiver(
&dep_ar_path,
&dep_gcc_ar_path,
&c_flags_temp,
&cpp_flags_temp,
);
let libs_dir = build_dir.join("libs");
let (lib_include_dirs, archives) = pipeline::ensure_lib_deps(
&lib_deps,
&lib_ignore,
&toolchain.get_gcc_path(),
&toolchain.get_gxx_path(),
dep_lib_ar_path,
&c_flags_temp,
&cpp_flags_temp,
&include_dirs,
&params.project_dir,
&libs_dir,
params.verbose,
crate::parallel::effective_jobs(params.jobs),
compiler_cache.as_deref(),
)
.await?;
include_dirs.extend(lib_include_dirs);
lib_archives = archives;
} else {
lib_archives = Vec::new();
}
// temp_compiler is dropped here — its only purpose was c_flags/cpp_flags.

// 6b. Create compiler with the full include-dir set (core, variant,
// toolchain sysroot, *and* downloaded library paths).
let compiler = AvrCompiler::new(
toolchain.get_gcc_path(),
toolchain.get_gxx_path(),
Expand Down Expand Up @@ -296,14 +356,15 @@ impl BuildOrchestrator for AvrOrchestrator {
compiler_cache: None,
};

// 9. Run shared sequential build pipeline
// 9. Run shared sequential build pipeline — pass downloaded library
// archives as extra link inputs (FastLED/fbuild#1276).
let build_result = pipeline::run_sequential_build_with_libs(
&compiler,
&linker,
ctx,
params,
&sources,
&[],
&lib_archives,
Some(&lib_env),
TargetArchitecture::Avr,
"AVR",
Expand Down
Loading