Skip to content

feat(build): apply build_unflags to framework compile flags, ESP32 wired (#37)#63

Merged
zackees merged 1 commit intomainfrom
fix/issue-37-build-unflags-framework-scope
Apr 17, 2026
Merged

feat(build): apply build_unflags to framework compile flags, ESP32 wired (#37)#63
zackees merged 1 commit intomainfrom
fix/issue-37-build-unflags-framework-scope

Conversation

@zackees
Copy link
Copy Markdown
Member

@zackees zackees commented Apr 17, 2026

Summary

Extends PlatformIO build_unflags past user-facing scopes (already handled by BuildContext::new) into framework/toolchain-contributed flags. Before, build_unflags = -std=gnu++11 in platformio.ini stripped the token from build_flags entries but left the framework's own -std=gnu++11 in place, so the compile line still carried the flag the user meant to remove.

Mechanism

  • New Compiler::build_unflags() trait method with default &[] — zero behavior change for platforms that don't opt in.
  • Default compile_c / compile_cpp impls route platform flags AND extra_flags through a new apply_compile_unflags helper that filters via pipeline::remove_unflagged_tokens. Early return keeps the zero-unflags path allocation-free.
  • pipeline::remove_unflagged_tokens is now pub so platform code can use the same PlatformIO-compatible semantics (exact-token + value-pair removal) already used by BuildContext::new.
  • BuildContext gains a build_unflags field so orchestrators can pass the set to their compiler.

ESP32 reference impl

  • Esp32Compiler gets a build_unflags field and a with_build_unflags(...) builder.
  • Trait override returns &self.build_unflags.
  • All four Esp32Compiler::with_temp_dir(...) construction sites in esp32::orchestrator chain .with_build_unflags(ctx.build_unflags.clone()).

Other platforms remain unchanged. Follow-up PRs can wire them using the same one-line builder pattern if desired.

Context

Addresses #37 — which asked for build_unflags to apply to effective native flag scopes (compile / link / assembler), not just the user-provided build_flags layer. This PR lands the compile scope; link is already handled by the existing remove_unflagged_tokens(&mut overlay_link_flags, ...) call in BuildContext::new.

Test plan

  • uv run cargo clippy --workspace --all-targets -- -D warnings clean
  • uv run cargo test -p fbuild-build --lib — 447 passed (3 new)
  • CI green on Linux / macOS / Windows

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for excluding specific compiler flags from compilation commands, enabling fine-grained control over compiler behavior across all build phases.
  • Tests

    • Added unit tests for flag removal functionality.

…red (#37)

Extends PlatformIO `build_unflags` past user-facing scopes into the
framework/toolchain-contributed flag set. Before, `build_unflags = -std=gnu++11`
in platformio.ini would strip the token from `build_flags` entries but
leave the framework's own `-std=gnu++11` in place, so the compile line
still carried the flag the user meant to remove.

Mechanism:
- Adds `Compiler::build_unflags()` trait method with default `&[]` so
  existing platform impls are unaffected.
- Default `compile_c` / `compile_cpp` impls route the platform flags and
  `extra_flags` through `apply_compile_unflags`, which filters both via
  `pipeline::remove_unflagged_tokens` when the set is non-empty. Early
  return keeps the zero-unflags path allocation-free.
- `pipeline::remove_unflagged_tokens` is now `pub` so platform code can
  use the same PlatformIO-compatible removal semantics (exact-token +
  value-pair) already established by `BuildContext::new`.
- `BuildContext` now stores `build_unflags` so orchestrators can pass it
  to their compiler via a builder method.

ESP32 is wired as the reference implementation: `Esp32Compiler` gains a
`build_unflags` field + `with_build_unflags(...)` builder + trait
override. All four construction sites in `esp32::orchestrator` chain
`.with_build_unflags(ctx.build_unflags.clone())`. Other platforms are
untouched and will continue to only filter user/sketch-scope flags —
follow-up PRs can wire them using the same one-line builder pattern.

Tests cover: default trait impl returns empty, configured unflags
round-trip through the trait method, and end-to-end stripping of a
framework-contributed `-std=gnu++2b` from `cpp_flags()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

This PR introduces support for the build_unflags configuration mechanism throughout the fbuild compilation framework. The feature enables stripping specific compiler flags from compilation commands, with configuration sourced from PlatformIO settings and propagated from the build context through the orchestrator into compiler instances.

Changes

Cohort / File(s) Summary
Compiler Trait & Core Logic
crates/fbuild-build/src/compiler.rs
Added build_unflags(&self) -> &[String] trait method (defaults to empty slice) and introduced apply_compile_unflags() function to filter both platform-provided and extra compiler flags through the unflag list.
ESP32 Compiler Implementation
crates/fbuild-build/src/esp32/esp32_compiler.rs
Extended Esp32Compiler with new build_unflags field, added with_build_unflags() builder method, implemented the trait hook, and added three unit tests validating unflag exposure and flag stripping behavior.
Orchestrator Integration
crates/fbuild-build/src/esp32/orchestrator.rs
Threaded ctx.build_unflags into four compilation phase initializations (dependency/library, project-as-library, framework built-in, and sketch compilation) via the new with_build_unflags() builder.
Build Pipeline Configuration
crates/fbuild-build/src/pipeline.rs
Added build_unflags: Vec<String> field to BuildContext, populated from PlatformIOConfig::get_build_unflags(), promoted remove_unflagged_tokens() from private to public with expanded documentation, and ensured unflags are stripped from overlay link flags.

Sequence Diagram

sequenceDiagram
    participant Config as PlatformIO Config
    participant BuildCtx as BuildContext
    participant Orch as Orchestrator
    participant Compiler as Esp32Compiler
    participant Pipeline as Compile Pipeline
    
    Config->>BuildCtx: get_build_unflags()
    BuildCtx->>BuildCtx: store build_unflags
    BuildCtx->>Orch: pass ctx with build_unflags
    Orch->>Compiler: with_build_unflags(ctx.build_unflags)
    Compiler->>Compiler: store build_unflags
    Pipeline->>Compiler: compile_c/compile_cpp
    Compiler->>Compiler: apply_compile_unflags(flags, unflags)
    Compiler->>Pipeline: compile_one(filtered_flags)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 Flags stripped like carrots from the garden bed,
Unwanted -std options now quietly shed,
Through compiler and context, a pathway so clean,
Where PlatformIO's unflagging is perfectly seen! 🎯

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main feature: applying build_unflags to framework compile flags with ESP32 implementation. It accurately captures the PR's primary objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-37-build-unflags-framework-scope

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/fbuild-build/src/esp32/orchestrator.rs (1)

544-585: ⚠️ Potential issue | 🟠 Major

Filter temp compiler flags through remove_unflagged_tokens at extraction points; .with_build_unflags(...) on temp compilers is a no-op.

The library helpers ensure_libraries_sync and compile_library_with_jobs accept raw &[String] flags and do not re-apply unflags. Since Esp32Compiler::c_flags() and cpp_flags() don't consult self.build_unflags, the .with_build_unflags(...) calls on temp compilers strip nothing. Only compile_c/compile_cpp trait methods apply them—but those are bypassed when you extract and pass flags directly.

Result: library archives and the compile_commands.json file advertise unfiltered flags while the sketch is compiled with them stripped. This risks ABI/ODR mismatches (e.g., -std=gnu++2b in archives vs. -std=gnu++20 in sketch) and confuses clangd/IDEs.

Affected paths:

  • Lines 544–585: dep libs via ensure_libraries_sync
  • Lines 609–667: project-as-library via compile_project_as_library
  • Lines 694–810: framework built-in libs (WiFi/SPI/…) via compile_library_with_jobs
  • Lines 976–1014: local lib/* via compile_library_with_jobs
  • Lines 882–886 and 1045–1060: compile_commands.json via raw c_flags()/cpp_flags()

Fix: Apply pipeline::remove_unflagged_tokens right after each c_flags()/cpp_flags() extraction, before passing to helpers:

Proposed fix pattern
             let temp_compiler = Esp32Compiler::with_temp_dir(
                 ...
                 build_dir.join("tmp"),
-            )
-            .with_build_unflags(ctx.build_unflags.clone());
+            );
             // Apply user build_flags to library compilation (matching PlatformIO behavior).
             // User flags like -std=gnu++2a replace the MCU config's -std=gnu++2b.
-            let c_flags = apply_overlay_flags(&temp_compiler.c_flags(), &user_overlay, "dummy.c");
-            let cpp_flags =
-                apply_overlay_flags(&temp_compiler.cpp_flags(), &user_overlay, "dummy.cpp");
+            let mut base_c = temp_compiler.c_flags();
+            let mut base_cpp = temp_compiler.cpp_flags();
+            crate::pipeline::remove_unflagged_tokens(&mut base_c, &ctx.build_unflags);
+            crate::pipeline::remove_unflagged_tokens(&mut base_cpp, &ctx.build_unflags);
+            let c_flags = apply_overlay_flags(&base_c, &user_overlay, "dummy.c");
+            let cpp_flags = apply_overlay_flags(&base_cpp, &user_overlay, "dummy.cpp");

Repeat for p_compiler (609–623), fw_compiler (694–709), local-libs (976–1014), and both generate_compile_db call sites (882–886, 1045–1060).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/fbuild-build/src/esp32/orchestrator.rs` around lines 544 - 585, The
temp compilers' c_flags()/cpp_flags() are used directly so
.with_build_unflags(...) is a no-op; call pipeline::remove_unflagged_tokens on
each extracted flag vector before passing them to library helpers (e.g., after
temp_compiler.c_flags()/cpp_flags(), p_compiler, fw_compiler and local lib
compiler extractions) and before building compile_commands.json; update the uses
around Esp32Compiler::with_temp_dir (temp_compiler), the p_compiler block
(compile_project_as_library), fw_compiler block (compile_library_with_jobs),
local lib compilation sites, and the two generate_compile_db call sites to
replace raw c_flags()/cpp_flags() with the cleaned vectors from
pipeline::remove_unflagged_tokens.
🧹 Nitpick comments (1)
crates/fbuild-build/src/esp32/orchestrator.rs (1)

501-506: Minor: re-reads build_flags from config, bypassing ctx.user_flags.

Lines 504 re-parse ctx.config.get_build_flags(&params.env_name)? instead of using ctx.user_flags, which BuildContext::new already produced with user-scope build_unflags applied. So even after the library-path fix above, the user's own build_flags feeding user_overlay here haven't had unflags applied at the source — they'll only be stripped later via the framework-flag filter, which works only for exact token matches already present in both scopes.

Not a blocker (the overlay still gets filtered for framework tokens), but it's an inconsistency worth cleaning up so one source of truth (ctx.user_flags) flows through the whole orchestrator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/fbuild-build/src/esp32/orchestrator.rs` around lines 501 - 506,
Replace the redundant call to ctx.config.get_build_flags(&params.env_name)? with
the already-processed ctx.user_flags so the orchestrator uses the single source
of truth produced by BuildContext::new; specifically, in the block that builds
user_flags/user_build_flags (variables user_flags, user_build_flags, and where
params.extra_build_flags is extended), use ctx.user_flags as the base (and then
extend with params.extra_build_flags and prepend sdk_defines as before) so
unflags applied in BuildContext::new are preserved when computing the
user_overlay.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/fbuild-build/src/esp32/orchestrator.rs`:
- Around line 544-585: The temp compilers' c_flags()/cpp_flags() are used
directly so .with_build_unflags(...) is a no-op; call
pipeline::remove_unflagged_tokens on each extracted flag vector before passing
them to library helpers (e.g., after temp_compiler.c_flags()/cpp_flags(),
p_compiler, fw_compiler and local lib compiler extractions) and before building
compile_commands.json; update the uses around Esp32Compiler::with_temp_dir
(temp_compiler), the p_compiler block (compile_project_as_library), fw_compiler
block (compile_library_with_jobs), local lib compilation sites, and the two
generate_compile_db call sites to replace raw c_flags()/cpp_flags() with the
cleaned vectors from pipeline::remove_unflagged_tokens.

---

Nitpick comments:
In `@crates/fbuild-build/src/esp32/orchestrator.rs`:
- Around line 501-506: Replace the redundant call to
ctx.config.get_build_flags(&params.env_name)? with the already-processed
ctx.user_flags so the orchestrator uses the single source of truth produced by
BuildContext::new; specifically, in the block that builds
user_flags/user_build_flags (variables user_flags, user_build_flags, and where
params.extra_build_flags is extended), use ctx.user_flags as the base (and then
extend with params.extra_build_flags and prepend sdk_defines as before) so
unflags applied in BuildContext::new are preserved when computing the
user_overlay.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 331ccb8a-5c88-43f0-866c-6936e0adb56c

📥 Commits

Reviewing files that changed from the base of the PR and between 588879e and ef0f56e.

📒 Files selected for processing (4)
  • crates/fbuild-build/src/compiler.rs
  • crates/fbuild-build/src/esp32/esp32_compiler.rs
  • crates/fbuild-build/src/esp32/orchestrator.rs
  • crates/fbuild-build/src/pipeline.rs

@zackees zackees merged commit 12ddd5a into main Apr 17, 2026
75 of 76 checks passed
@zackees zackees deleted the fix/issue-37-build-unflags-framework-scope branch April 17, 2026 17:29
zackees added a commit that referenced this pull request Apr 17, 2026
…ers (#37)

Follow-up to the ESP32 reference implementation — applies the same
builder-pattern wiring to every other platform so PlatformIO
`build_unflags` now strips framework/toolchain-contributed flags on
AVR, Teensy, generic ARM (STM32 / RP2040 / Apollo3), nRF52, Renesas,
Silabs, CH32V, SAM, and ESP8266.

Per-compiler changes are identical in shape:
- Add `build_unflags: Vec<String>` field, initialized empty in constructor
- Add `with_build_unflags(...)` builder method
- Override `Compiler::build_unflags()` to return `&self.build_unflags`

Each platform orchestrator chains `.with_build_unflags(ctx.build_unflags.clone())`
on every `XxxCompiler::new(...)` site (one per orchestrator, except
stm32 which has two).

Default-impl `compile_c` / `compile_cpp` in the `Compiler` trait
already filter both platform and `extra_flags` through this set when
it's non-empty (added in PR #63), so wiring each platform picks up
framework-scope filtering automatically.

All 447 fbuild-build lib tests pass; clippy -D warnings clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zackees added a commit that referenced this pull request Apr 17, 2026
…ers (#37) (#64)

Follow-up to the ESP32 reference implementation — applies the same
builder-pattern wiring to every other platform so PlatformIO
`build_unflags` now strips framework/toolchain-contributed flags on
AVR, Teensy, generic ARM (STM32 / RP2040 / Apollo3), nRF52, Renesas,
Silabs, CH32V, SAM, and ESP8266.

Per-compiler changes are identical in shape:
- Add `build_unflags: Vec<String>` field, initialized empty in constructor
- Add `with_build_unflags(...)` builder method
- Override `Compiler::build_unflags()` to return `&self.build_unflags`

Each platform orchestrator chains `.with_build_unflags(ctx.build_unflags.clone())`
on every `XxxCompiler::new(...)` site (one per orchestrator, except
stm32 which has two).

Default-impl `compile_c` / `compile_cpp` in the `Compiler` trait
already filter both platform and `extra_flags` through this set when
it's non-empty (added in PR #63), so wiring each platform picks up
framework-scope filtering automatically.

All 447 fbuild-build lib tests pass; clippy -D warnings clean.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant