Background
While fixing #515 / #518 it became clear that fbuild has several near-duplicate code paths that all want to resolve a board id (and other config pieces) but each reach for the underlying primitive independently. Every time one of those primitives gains a new parameter — like the recently added project_dir for project-local boards/*.json — every call site has to be updated by hand, and missing one is silent (the build just behaves as if the new feature isn't there).
The proximate example: #516 added BoardConfig::from_board_id_in_project(..., project_dir) and updated compile_many::platform_for_board. But pipeline::BuildContext::new_with_perf (the daemon's actual fbuild build path) and script_runtime::build_script_runtime_board_config were both missed — they kept calling the legacy from_board_id and silently dropped the project_dir context. End users still saw unknown board 'X' (no built-in defaults) even though the underlying lookup primitive could find it. PR #518 catches them up, but the structural issue remains.
Ask
Audit the workspace for duplicated config-resolution logic and consolidate into a small set of shared entry points that take ALL needed context (project_dir, env_name, overrides, etc.) once. Concretely:
Board lookup (immediate)
crates/fbuild-build/src/compile_many.rs:platform_for_board
crates/fbuild-build/src/pipeline/context.rs:BuildContext::new_with_perf
crates/fbuild-build/src/script_runtime.rs:build_script_runtime_board_config
crates/fbuild-cli/src/cli/deploy.rs:run_deploy (two consecutive from_board_id calls with a fallback pattern repeated 3+ times in daemon/src/handlers/operations/deploy.rs)
crates/fbuild-daemon/src/handlers/operations/deploy.rs — there are ~5 instances of the same from_board_id(&board_id, &deploy_board_overrides).or_else(|_| from_board_id(&board_id, &HashMap::new())) pattern (lines ~154, ~427, ~667, ~699). That fallback strongly suggests a shared helper.
Other likely candidates worth checking
- platformio.ini reading: are there multiple places that parse / hold a
PlatformIOConfig per build? from_path + from_path_with_overrides + get_env_config are called from many sites.
- project_dir handling: many places independently derive things like
project_dir.join(\"platformio.ini\"), project_dir.join(\".fbuild\"), etc.
- toolchain resolution + extra-script overlay setup — large per-orchestrator copies might share a common pre-build shim.
Proposed shape
A small fbuild_build::resolution (or similar) module with a couple of entry points:
pub struct ResolutionContext<'a> {
pub project_dir: &'a Path,
pub env_name: &'a str,
pub config: &'a PlatformIOConfig,
pub board_overrides: &'a HashMap<String, String>,
}
impl ResolutionContext<'_> {
pub fn resolve_board(&self) -> Result<BoardConfig> { ... }
pub fn resolve_platform(&self) -> Result<Platform> { ... }
pub fn resolve_extra_script_overlay(&self) -> Result<BuildOverlay> { ... }
}
Then every existing site becomes one call instead of three. Adding the next parameter is then a one-line change to the context struct rather than a workspace-wide rename.
Acceptance
- A single grep for
BoardConfig::from_board_id (and variants) returns ≤2 call sites outside fbuild-config (the new entry points + one test helper), down from the ~12 production sites today.
- Tests still green, no behavior change.
- A short doc comment on the new module pointing future feature work at the right injection point.
Why now
Every feature that adds a knob (project_dir was just the most recent) gets paid for N times instead of once, and N-1 of those payments are silent regressions waiting for a CI run to surface them. This is cheap to consolidate while the call-site list is still small enough to enumerate.
Filed alongside #518 (which is the minimum fix to unblock the immediate user).
Background
While fixing #515 / #518 it became clear that fbuild has several near-duplicate code paths that all want to resolve a board id (and other config pieces) but each reach for the underlying primitive independently. Every time one of those primitives gains a new parameter — like the recently added
project_dirfor project-local boards/*.json — every call site has to be updated by hand, and missing one is silent (the build just behaves as if the new feature isn't there).The proximate example: #516 added
BoardConfig::from_board_id_in_project(..., project_dir)and updatedcompile_many::platform_for_board. Butpipeline::BuildContext::new_with_perf(the daemon's actualfbuild buildpath) andscript_runtime::build_script_runtime_board_configwere both missed — they kept calling the legacyfrom_board_idand silently dropped the project_dir context. End users still sawunknown board 'X' (no built-in defaults)even though the underlying lookup primitive could find it. PR #518 catches them up, but the structural issue remains.Ask
Audit the workspace for duplicated config-resolution logic and consolidate into a small set of shared entry points that take ALL needed context (project_dir, env_name, overrides, etc.) once. Concretely:
Board lookup (immediate)
crates/fbuild-build/src/compile_many.rs:platform_for_boardcrates/fbuild-build/src/pipeline/context.rs:BuildContext::new_with_perfcrates/fbuild-build/src/script_runtime.rs:build_script_runtime_board_configcrates/fbuild-cli/src/cli/deploy.rs:run_deploy(two consecutivefrom_board_idcalls with a fallback pattern repeated 3+ times indaemon/src/handlers/operations/deploy.rs)crates/fbuild-daemon/src/handlers/operations/deploy.rs— there are ~5 instances of the samefrom_board_id(&board_id, &deploy_board_overrides).or_else(|_| from_board_id(&board_id, &HashMap::new()))pattern (lines ~154, ~427, ~667, ~699). That fallback strongly suggests a shared helper.Other likely candidates worth checking
PlatformIOConfigper build?from_path+from_path_with_overrides+get_env_configare called from many sites.project_dir.join(\"platformio.ini\"),project_dir.join(\".fbuild\"), etc.Proposed shape
A small
fbuild_build::resolution(or similar) module with a couple of entry points:Then every existing site becomes one call instead of three. Adding the next parameter is then a one-line change to the context struct rather than a workspace-wide rename.
Acceptance
BoardConfig::from_board_id(and variants) returns ≤2 call sites outsidefbuild-config(the new entry points + one test helper), down from the ~12 production sites today.Why now
Every feature that adds a knob (project_dir was just the most recent) gets paid for N times instead of once, and N-1 of those payments are silent regressions waiting for a CI run to surface them. This is cheap to consolidate while the call-site list is still small enough to enumerate.
Filed alongside #518 (which is the minimum fix to unblock the immediate user).