diff --git a/crates/fbuild-config/src/board/loaders.rs b/crates/fbuild-config/src/board/loaders.rs index c366396f..e4df6b43 100644 --- a/crates/fbuild-config/src/board/loaders.rs +++ b/crates/fbuild-config/src/board/loaders.rs @@ -176,6 +176,7 @@ impl BoardConfig { .or_else(|| props.get("upload.protocol").cloned()), upload_speed: get("upload.speed").or_else(|| props.get("upload.speed").cloned()), monitor_filters: resolve_monitor_filters(overrides, &props, is_esp32_family), + check_tool: get("check_tool"), max_flash: resolve_max_flash(overrides, &props), max_ram: get("maximum_data_size") .or_else(|| props.get("maximum_data_size").cloned()) @@ -344,6 +345,10 @@ impl BoardConfig { .cloned() .or_else(|| defaults.get("upload.speed").cloned()), monitor_filters: resolve_monitor_filters(overrides, &defaults, is_esp32_family), + check_tool: overrides + .get("check_tool") + .cloned() + .or_else(|| defaults.get("check_tool").cloned()), max_flash: resolve_max_flash(overrides, &defaults), max_ram: overrides .get("maximum_data_size") diff --git a/crates/fbuild-config/src/board/methods.rs b/crates/fbuild-config/src/board/methods.rs index 40cbd1c1..a1c96cfb 100644 --- a/crates/fbuild-config/src/board/methods.rs +++ b/crates/fbuild-config/src/board/methods.rs @@ -51,6 +51,14 @@ impl BoardConfig { .map(|filters| filters.join(", ")) } + /// PlatformIO `check_tool` value to emit for static-analysis configs. + pub fn check_tool_ini_value(&self) -> Option<&str> { + self.check_tool + .as_deref() + .map(str::trim) + .filter(|tool| !tool.is_empty()) + } + /// Resolve the effective ESP32 SDK memory profile used for variant headers/libs. /// /// This keeps the SDK `sdkconfig.h` and memory-profile libraries aligned diff --git a/crates/fbuild-config/src/board/tests.rs b/crates/fbuild-config/src/board/tests.rs index b301e7f5..1bed745e 100644 --- a/crates/fbuild-config/src/board/tests.rs +++ b/crates/fbuild-config/src/board/tests.rs @@ -325,6 +325,35 @@ fn test_empty_monitor_filters_suppresses_emit() { assert_eq!(config.monitor_filters_ini_value(), None); } +#[test] +fn test_check_tool_default_absent() { + let config = BoardConfig::from_board_id("esp32dev", &HashMap::new()).unwrap(); + assert_eq!(config.check_tool, None); + assert_eq!(config.check_tool_ini_value(), None); +} + +#[test] +fn test_check_tool_override_emits_static_analysis_tool() { + let mut overrides = HashMap::new(); + overrides.insert("check_tool".to_string(), "clangtidy".to_string()); + + let config = BoardConfig::from_board_id("uno", &overrides).unwrap(); + + assert_eq!(config.check_tool.as_deref(), Some("clangtidy")); + assert_eq!(config.check_tool_ini_value(), Some("clangtidy")); +} + +#[test] +fn test_empty_check_tool_does_not_emit() { + let mut overrides = HashMap::new(); + overrides.insert("check_tool".to_string(), " ".to_string()); + + let config = BoardConfig::from_board_id("uno", &overrides).unwrap(); + + assert_eq!(config.check_tool.as_deref(), Some(" ")); + assert_eq!(config.check_tool_ini_value(), None); +} + #[test] fn test_parse_boards_txt_with_comments() { let props = parse_boards_txt( diff --git a/crates/fbuild-config/src/board/types.rs b/crates/fbuild-config/src/board/types.rs index b4889eb9..8ff560a8 100644 --- a/crates/fbuild-config/src/board/types.rs +++ b/crates/fbuild-config/src/board/types.rs @@ -66,6 +66,12 @@ pub struct BoardConfig { /// unset. An explicit empty list in project config suppresses that default. #[serde(default, skip_serializing_if = "Option::is_none")] pub monitor_filters: Option>, + /// PlatformIO static-analysis tool name. + /// + /// This is metadata for `pio check` compatibility and is not consumed by + /// fbuild's compile path. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub check_tool: Option, /// Maximum flash size in bytes pub max_flash: Option, /// Maximum RAM size in bytes diff --git a/crates/fbuild-config/src/ini_parser/mod.rs b/crates/fbuild-config/src/ini_parser/mod.rs index 01ec0c4a..54d33c01 100644 --- a/crates/fbuild-config/src/ini_parser/mod.rs +++ b/crates/fbuild-config/src/ini_parser/mod.rs @@ -348,7 +348,7 @@ impl PlatformIOConfig { overrides.insert(stripped.to_string(), value.clone()); } else if let Some(stripped) = key.strip_prefix("board_upload.") { overrides.insert(format!("upload.{}", stripped), value.clone()); - } else if key == "monitor_filters" { + } else if ["monitor_filters", "check_tool"].contains(&key.as_str()) { overrides.insert(key.clone(), value.clone()); } } diff --git a/crates/fbuild-config/src/ini_parser/tests.rs b/crates/fbuild-config/src/ini_parser/tests.rs index 1245227c..fc21b1eb 100644 --- a/crates/fbuild-config/src/ini_parser/tests.rs +++ b/crates/fbuild-config/src/ini_parser/tests.rs @@ -772,6 +772,28 @@ monitor_filters = ); } +#[test] +fn test_get_board_overrides_preserves_check_tool() { + let f = write_ini( + "\ +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +check_tool = clangtidy +build_flags = -DREAL_BUILD_FLAG +", + ); + let config = PlatformIOConfig::from_path(f.path()).unwrap(); + let overrides = config.get_board_overrides("esp32dev").unwrap(); + + assert_eq!(overrides.get("check_tool"), Some(&"clangtidy".to_string())); + assert_eq!( + config.get_build_flags("esp32dev").unwrap(), + vec!["-DREAL_BUILD_FLAG"] + ); +} + #[test] fn test_get_source_filter_prefers_build_src_filter() { let f = write_ini( diff --git a/docs/reference/platformio-ini.md b/docs/reference/platformio-ini.md index 4ea67f34..29355c9e 100644 --- a/docs/reference/platformio-ini.md +++ b/docs/reference/platformio-ini.md @@ -31,6 +31,7 @@ framework = arduino upload_port = COM3 monitor_speed = 9600 monitor_filters = default, esp32_exception_decoder +check_tool = clangtidy build_flags = -DDEBUG -DLED_PIN=13 @@ -50,6 +51,7 @@ Common keys: | `upload_port` | Preferred deploy port. | | `monitor_speed` | Serial monitor baud rate. | | `monitor_filters` | Serial monitor filters. ESP32-family boards default to `default, esp32_exception_decoder` when unset; use `monitor_filters = []` to suppress. | +| `check_tool` | Static-analysis tool for PlatformIO `pio check` compatibility; ignored by normal fbuild compilation. | | `build_flags` | Extra compiler flags. | | `lib_deps` | Library dependencies, including GitHub URLs. | | `build_type` | Build profile; `debug` preserves unwind metadata. |