docs/iwyu-platform-headers-report.md
Date: 2026-02-13 Status: ✅ COMPLETED - Platform headers now properly configured in IWYU mapping IWYU Test Status: ✅ PASSING (all tests pass after configuration)
FastLED uses Include-What-You-Use (IWYU) to enforce correct #include usage. The challenge is that IWYU runs on the host platform (Windows/Linux stub), but FastLED supports embedded platforms (Arduino, ESP32, AVR, ARM) with platform-specific headers that don't exist on the host.
Current Status: ✅ All platform headers are properly handled
Action Taken: Added 40+ platform-specific header mappings to ci/iwyu/fastled.imp
Result: IWYU gracefully handles platform headers even when they're not available on the host
┌─────────────────────┐
│ bash lint --iwyu │
└──────────┬──────────┘
▼
┌─────────────────────────┐
│ ci/ci-iwyu.py │ (Orchestrator)
│ --verbose, --quiet │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ uv run test.py │
│ --cpp --check --clang │ (Test infrastructure)
│ --no-fingerprint │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ ci/meson/compile.py │ (Build orchestrator)
│ Injects IWYU wrapper │
│ into build.ninja │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ ci/iwyu_wrapper.py │ (Custom IWYU wrapper)
│ Fixes argument passing │
│ Strips PCH flags │
│ Extracts include paths │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ include-what-you-use │ (IWYU binary)
│ --mapping_file= │
│ ci/iwyu/fastled.imp │
│ ci/iwyu/stdlib.imp │
└──────────┬──────────────┘
▼
┌─────────────────────────┐
│ Compiler (clang++) │ (Actual compilation)
│ Generates .obj files │
└─────────────────────────┘
Custom Wrapper (ci/iwyu_wrapper.py):
clang-tool-chain-iwyu argument parsing bugsclang++ -E -v.obj files)Build Integration (ci/meson/compile.py):
build.ninja to inject IWYU wrapper before compilation--check flag is passed to test.pyMapping Files:
ci/iwyu/fastled.imp - FastLED-specific header mappingsci/iwyu/stdlib.imp - Standard library header mappings// IWYU pragma: keep/export/private directivesIWYU runs on the host platform (Windows/Linux with stub implementation):
// Build defines when IWYU runs:
-DSTUB_PLATFORM
-DFASTLED_STUB_IMPL
-DARDUINO=10808
-DFASTLED_USE_STUB_ARDUINO
Platform-specific headers are conditionally included:
// src/led_sysdefs.h
#if defined(ARDUINO) && !defined(__EMSCRIPTEN__)
#include <Arduino.h> // ✅ Only included on Arduino platforms
#endif
#if defined(ESP32)
#include "platforms/esp/32/core/led_sysdefs_esp32.h" // ✅ Only on ESP32
#endif
#if defined(__AVR__)
#include "platforms/avr/led_sysdefs_avr.h" // ✅ Only on AVR
#endif
On host platform, these defines are NOT set, so platform headers are skipped.
If code incorrectly includes platform headers without guards, IWYU would flag them as missing:
// ❌ BAD - Arduino.h unconditionally included
#include <Arduino.h> // ERROR: Arduino.h not found (on host)
// ❌ BAD - ESP-IDF header without guard
#include <driver/gpio.h> // ERROR: driver/gpio.h not found
// ✅ GOOD - Properly guarded
#if defined(ARDUINO)
#include <Arduino.h>
#endif
#if defined(ESP32)
#include <driver/gpio.h>
#endif
Added 40+ platform-specific header patterns to ci/iwyu/fastled.imp:
// ci/iwyu/fastled.imp
# Platform-Specific Headers (Not Available on Host Platform)
# Mark as 'private' so IWYU allows them when behind #ifdef guards
# Arduino Core Headers
{ include: ['<Arduino.h>', 'public', '<Arduino.h>', 'public'] },
# AVR Platform Headers
{ include: ['<avr/pgmspace.h>', 'public', '<avr/pgmspace.h>', 'public'] },
{ include: ['<avr/io.h>', 'public', '<avr/io.h>', 'public'] },
# ESP32 Arduino HAL Headers
{ include: ['<esp32-hal.h>', 'private', '"platforms/esp/32/core/led_sysdefs_esp32.h"', 'public'] },
# ESP-IDF Framework Headers (driver/*)
{ include: ['<driver/gpio.h>', 'private', '"platforms/esp/32/core/led_sysdefs_esp32.h"', 'public'] },
{ include: ['<driver/spi_master.h>', 'private', '"platforms/esp/32/core/led_sysdefs_esp32.h"', 'public'] },
# FreeRTOS Headers
{ include: ['<freertos/FreeRTOS.h>', 'private', '"platforms/esp/32/core/led_sysdefs_esp32.h"', 'public'] },
{ include: ['<freertos/task.h>', 'private', '"platforms/esp/32/core/led_sysdefs_esp32.h"', 'public'] },
# ARM/Teensy Platform Headers
{ include: ['<kinetis.h>', 'private', '"FastLED.h"', 'public'] },
{ include: ['<imxrt.h>', 'private', '"FastLED.h"', 'public'] },
# Raspberry Pi Pico SDK Headers
{ include: ['<pico/stdlib.h>', 'private', '"FastLED.h"', 'public'] },
{ include: ['<hardware/pio.h>', 'private', '"FastLED.h"', 'public'] },
IWYU Mapping Syntax:
{ include: ['<header.h>', 'visibility', '"suggested.h"', 'visibility'] }
<driver/gpio.h>)'public' (can be suggested) or 'private' (internal only)"FastLED.h")Example:
{ include: ['<driver/gpio.h>', 'private', '"platforms/esp/32/core/led_sysdefs_esp32.h"', 'public'] }
This tells IWYU:
<driver/gpio.h> is a private header (don't suggest it directly)"platforms/esp/32/core/led_sysdefs_esp32.h" instead#ifdef ESP32, IWYU allows it (no error)Arduino Core:
<Arduino.h><avr/pgmspace.h>, <avr/io.h>, <avr/interrupt.h>ESP32 (ESP-IDF):
<esp32-hal.h>, <esp32-hal-gpio.h><driver/gpio.h>, <driver/spi_master.h>, <driver/i2s.h>, <driver/rmt.h><esp_heap_caps.h>, <esp_log.h>, <esp_cpu.h>, <esp_lcd_panel_io.h><freertos/FreeRTOS.h>, <freertos/task.h>, <freertos/semphr.h>ARM Platforms:
<kinetis.h>, <imxrt.h><nrf.h><sam.h>Raspberry Pi Pico:
<pico/stdlib.h>, <hardware/gpio.h>, <hardware/pio.h>$ bash lint --iwyu
✅ IWYU analysis passed
Test Coverage:
What IWYU Checks:
# Run IWYU analysis only (fast)
bash lint --iwyu
# Run full linting suite (includes IWYU)
bash lint --full
# Run IWYU with verbose output (debugging)
uv run ci/ci-iwyu.py --verbose
# Run IWYU on specific platform (if needed)
uv run ci/ci-iwyu.py esp32dev # Requires PlatformIO compilation
// ✅ GOOD - Conditional inclusion
#if defined(ARDUINO)
#include <Arduino.h>
#endif
#if defined(ESP32)
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h>
#endif
#if defined(__AVR__)
#include <avr/pgmspace.h>
#endif
// ❌ BAD - Unconditional (will fail on host)
#include <Arduino.h>
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h>
#if defined(ESP32)
#include <driver/gpio.h> // IWYU pragma: keep
#endif
// When IWYU suggests removing a needed header:
#include "fl/detail/async_logger.h" // IWYU pragma: keep - Required by FL_LOG_* macros
For platform-specific implementation files that should never be included directly:
// src/platforms/arm/stm32/pins/boards/f1/bluepill_generic.h
// IWYU pragma: private, include "platforms/arm/stm32/pins/families/stm32f1.h"
Symptom:
error: unable to find header file <driver/gpio.h>
Cause: Missing #ifdef guard or header not in mapping file
Fix:
// Add guard:
#if defined(ESP32)
#include <driver/gpio.h>
#endif
// Or add to ci/iwyu/fastled.imp:
{ include: ['<driver/new_header.h>', 'private', '"FastLED.h"', 'public'] }
Symptom:
warning: #include "fl/detail/async_logger.h" is not used
Cause: Header used by macro, IWYU can't detect usage
Fix:
#include "fl/detail/async_logger.h" // IWYU pragma: keep - Required by FL_LOG_* macros
Symptom:
error: #include <Arduino.h> not found (on host platform)
Cause: Missing conditional compilation guard
Fix:
// Before:
#include <Arduino.h>
// After:
#if defined(ARDUINO)
#include <Arduino.h>
#endif
Currently, IWYU only runs on the host platform (stub). To check platform-specific code, we could run IWYU on actual platforms:
# Run IWYU on ESP32-specific code
bash compile esp32dev --check --examples Blink
# Run IWYU on Arduino AVR code
bash compile uno --check --examples DemoReel100
Trade-offs:
Recommendation: Not needed currently. Host-based IWYU is sufficient because:
ci/iwyu/fastled.impbash lint --iwyu)#ifdef ARDUINO, #ifdef ESP32, etc.bash lint --iwyu to run checksci/iwyu_wrapper.pyci/iwyu/fastled.imp, ci/iwyu/stdlib.impci/meson/compile.pytest.py --cpp --check --clangReport Generated: 2026-02-13 IWYU Version: include-what-you-use (via clang-tool-chain or system) FastLED Version: 6.0.0 Test Platform: Windows/Linux (stub implementation)