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
156 changes: 156 additions & 0 deletions crates/fbuild-build-esp/src/esp8266/board_props.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//! Pure rules that adapt the ESP8266 recipe to a board's `boards.txt` menu
//! selections. Each rule is a side-effect-free `config -> config` function;
//! the orchestrator applies [`for_board_props`] once, in one line.

use std::collections::HashMap;

use super::mcu_config::Esp8266McuConfig;
use crate::esp32::mcu_config::DefineEntry;

/// The recipe adapted to the board's `boards.txt` menu selections: the SDK
/// define, `-D` overrides from the flag properties, and the lwIP and libstdc++
/// variants.
pub(super) fn for_board_props(
config: Esp8266McuConfig,
board_props: &Option<HashMap<String, String>>,
) -> Esp8266McuConfig {
let Some(props) = board_props.as_ref() else {
return config;
};
let config = match props.get("sdk") {
Some(sdk_name) => with_sdk_define(config, sdk_name),
None => config,
};
let config = board_define_flags(props)
.into_iter()
.fold(config, |config, (name, value)| {
with_define(config, name, value)
});
let config = match props.get("lwip_lib") {
Some(lib) => with_first_lib_replaced(config, |l| l.starts_with("-llwip"), lib),
None => config,
};
match props.get("stdcpp_lib") {
Some(lib) => {
with_first_lib_replaced(config, |l| l == "-lstdc++" || l == "-lstdc++-exc", lib)
}
None => config,
}
}

/// Replace the recipe's `NONOSDK*` key/value define with the board's SDK.
fn with_sdk_define(mut config: Esp8266McuConfig, sdk_name: &str) -> Esp8266McuConfig {
config.defines.retain(
|entry| !matches!(entry, DefineEntry::KeyValue(name, _) if name.starts_with("NONOSDK")),
);
config
.defines
.push(DefineEntry::KeyValue(sdk_name.to_string(), "1".to_string()));
config
}

/// The `-D` defines in the board's flag properties, in application order.
fn board_define_flags(props: &HashMap<String, String>) -> Vec<(String, String)> {
["flash_flags", "lwip_flags", "mmuflags", "vtable_flags"]
.into_iter()
.filter_map(|key| props.get(key))
.flat_map(|flags| fbuild_core::shell_split::split(flags))
.filter_map(|token| {
let def = token.strip_prefix("-D")?;
Some(
def.split_once('=')
.map(|(name, value)| (name.to_string(), value.to_string()))
.unwrap_or_else(|| (def.to_string(), "1".to_string())),
)
})
.collect()
}

/// Set `name` to `value`, replacing any existing define of that name.
fn with_define(mut config: Esp8266McuConfig, name: String, value: String) -> Esp8266McuConfig {
config.defines.retain(|entry| match entry {
DefineEntry::Simple(existing) | DefineEntry::KeyValue(existing, _) => existing != &name,
});
config.defines.push(DefineEntry::KeyValue(name, value));
config
}

/// Replace the first linker lib that `matches` with `lib`.
fn with_first_lib_replaced(
mut config: Esp8266McuConfig,
matches: impl Fn(&str) -> bool,
lib: &str,
) -> Esp8266McuConfig {
if let Some(slot) = config.linker_libs.iter_mut().find(|l| matches(l)) {
*slot = lib.to_string();
}
config
}

#[cfg(test)]
mod tests {
use super::*;
use crate::esp8266::mcu_config::get_esp8266_config;

fn define_names(config: &Esp8266McuConfig) -> Vec<(String, Option<String>)> {
config
.defines
.iter()
.map(|entry| match entry {
DefineEntry::Simple(name) => (name.clone(), None),
DefineEntry::KeyValue(name, value) => (name.clone(), Some(value.clone())),
})
.collect()
}

#[test]
fn board_props_none_keeps_recipe() {
let base = get_esp8266_config().unwrap();
let config = for_board_props(base.clone(), &None);
assert_eq!(define_names(&config), define_names(&base));
assert_eq!(config.linker_libs, base.linker_libs);
}

#[test]
fn board_props_rewrite_sdk_defines_and_libs() {
let base = get_esp8266_config().unwrap();
let props = HashMap::from([
("sdk".to_string(), "NONOSDK3V0".to_string()),
(
"mmuflags".to_string(),
"-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DFOO".to_string(),
),
(
"vtable_flags".to_string(),
"-DMMU_IRAM_SIZE=0x8000".to_string(),
),
("lwip_lib".to_string(), "-llwip2-1460-feat".to_string()),
("stdcpp_lib".to_string(), "-lstdc++-exc".to_string()),
]);
let config = for_board_props(base.clone(), &Some(props));
let defines = define_names(&config);

assert!(!defines.iter().any(|(name, _)| name == "NONOSDK22x_190703"));
assert!(defines.contains(&("NONOSDK3V0".into(), Some("1".into()))));
// Later properties override earlier ones; each name appears once.
let iram: Vec<_> = defines
.iter()
.filter(|(n, _)| n == "MMU_IRAM_SIZE")
.collect();
assert_eq!(
iram,
[&("MMU_IRAM_SIZE".to_string(), Some("0x8000".to_string()))]
);
assert!(defines.contains(&("FOO".into(), Some("1".into()))));

assert!(
config
.linker_libs
.contains(&"-llwip2-1460-feat".to_string())
);
assert!(!config.linker_libs.contains(&"-llwip2-536-feat".to_string()));
assert!(config.linker_libs.contains(&"-lstdc++-exc".to_string()));
assert!(!config.linker_libs.contains(&"-lstdc++".to_string()));
assert_eq!(config.linker_libs.len(), base.linker_libs.len());
}
}
1 change: 1 addition & 0 deletions crates/fbuild-build-esp/src/esp8266/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! ESP8266 platform build support (NodeMCU, Wemos D1, etc.)

mod board_props;
pub mod esp8266_compiler;
pub mod esp8266_linker;
pub mod mcu_config;
Expand Down
66 changes: 2 additions & 64 deletions crates/fbuild-build-esp/src/esp8266/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::compile_database::TargetArchitecture;
use crate::pipeline;
use crate::{BuildOrchestrator, BuildParams, BuildResult, SourceScanner};

use super::board_props::for_board_props;
use super::esp8266_compiler::Esp8266Compiler;
use super::esp8266_linker::Esp8266Linker;
use super::mcu_config::get_esp8266_config;
Expand Down Expand Up @@ -254,8 +255,7 @@ impl BuildOrchestrator for Esp8266Orchestrator {
let variant_dir = framework.get_variant_dir(&ctx.board.variant);

// 5. Load MCU config
let mut mcu_config = get_esp8266_config()?;
apply_esp8266_board_props(&board_props, &mut mcu_config);
let mcu_config = for_board_props(get_esp8266_config()?, &board_props);

// Compute flash_freq early for the fast-path fingerprint (also used by
// the linker constructor below).
Expand Down Expand Up @@ -591,68 +591,6 @@ fn apply_define_flags_from_props(
}
}

fn apply_esp8266_board_props(
board_props: &Option<HashMap<String, String>>,
mcu_config: &mut super::mcu_config::Esp8266McuConfig,
) {
let Some(props) = board_props.as_ref() else {
return;
};

if let Some(sdk_name) = props.get("sdk") {
mcu_config
.defines
.retain(|entry| !matches!(entry, crate::esp32::mcu_config::DefineEntry::KeyValue(name, _) if name.starts_with("NONOSDK")));
mcu_config
.defines
.push(crate::esp32::mcu_config::DefineEntry::KeyValue(
sdk_name.clone(),
"1".to_string(),
));
}

for key in ["flash_flags", "lwip_flags", "mmuflags", "vtable_flags"] {
if let Some(flags) = props.get(key) {
for token in fbuild_core::shell_split::split(flags) {
if let Some(def) = token.strip_prefix("-D") {
let (name, value) = def
.split_once('=')
.map(|(name, value)| (name.to_string(), value.to_string()))
.unwrap_or_else(|| (def.to_string(), "1".to_string()));
mcu_config.defines.retain(|entry| match entry {
crate::esp32::mcu_config::DefineEntry::Simple(existing) => {
existing != &name
}
crate::esp32::mcu_config::DefineEntry::KeyValue(existing, _) => {
existing != &name
}
});
mcu_config
.defines
.push(crate::esp32::mcu_config::DefineEntry::KeyValue(name, value));
}
}
}
}

if let Some(lwip_lib) = props.get("lwip_lib") {
for lib in &mut mcu_config.linker_libs {
if lib.starts_with("-llwip") {
*lib = lwip_lib.clone();
break;
}
}
}
if let Some(stdcpp_lib) = props.get("stdcpp_lib") {
for lib in &mut mcu_config.linker_libs {
if lib == "-lstdc++" || lib == "-lstdc++-exc" {
*lib = stdcpp_lib.clone();
break;
}
}
}
}

fn apply_esp8266_board_identity(
board_props: &Option<HashMap<String, String>>,
board_id: &str,
Expand Down
Loading