Skip to content

feat(symbols): ingest toolchain paths from build_info.json - #436

Merged
zackees merged 2 commits into
mainfrom
feat/issue-428-buildinfo-toolchain-paths
Jun 6, 2026
Merged

feat(symbols): ingest toolchain paths from build_info.json#436
zackees merged 2 commits into
mainfrom
feat/issue-428-buildinfo-toolchain-paths

Conversation

@zackees

Copy link
Copy Markdown
Member

Summary

Closes#428 (Phase 1 of #434).

  • Extends BuildInfo with nm_path / cppfilt_path / readelf_path / objdump_path plus a PIO-shape aliases block that mirrors them onto short keys (nm, c++filt, etc.). PlatformIO consumers (FastLED ci/util/symbol_analysis.py, ci/inspect_binary.py) keep working drop-in against fbuild-built artifacts.
  • Derives the four paths centrally in BuildInfo::new from size_path using the GCC cross-tool naming convention (<prefix>-size<prefix>-{nm,c++filt,readelf,objdump}). All of fbuild's existing orchestrators (AVR, ESP32, RP2040, STM32, Teensy, ESP8266, NRF52, SAM, Renesas, Apollo3, LPC, Silabs, CH32V) ship GCC cross-toolchains, so no orchestrator surgery is needed.
  • Teaches fbuild symbols to auto-locate build_info.json by walking up from the ELF directory; adds a --build-info <path> explicit override. New resolution order:
    1. --nm / --cppfilt CLI flags (user wins).
    2. --build-info <path> if provided.
    3. Auto-discovery: walk up from ELF dir for build_info.json or build_info_<env>.json.
    4. PATH lookup of bare nm, with c++filt derived by stem.
    5. Hard error with a message pointing at all four sources.
  • Common case becomes flag-less:
    fbuild symbols .fbuild/build/uno/firmware.elf
    fbuild symbols .pio/build/esp32s3/firmware.elf
    
  • Adds docs/symbols.md walking through the resolution order, the aliases schema, and PIO/fbuild interop.

Test plan

  • soldr cargo check --workspace --all-targets
  • soldr cargo clippy -p fbuild-build -p fbuild-cli --all-targets -- -D warnings
  • soldr cargo test -p fbuild-build --lib — 627 passed, 0 failed
  • soldr cargo test -p fbuild-cli --bin fbuild — 25 passed, 0 failed
  • New tests:
    • derive_gcc_tool_path_avr_prefix / xtensa_with_exe_suffix / bare_size_falls_back — derivation rules
    • build_info_populates_new_toolchain_fields — the four new fields land
    • build_info_aliases_block_mirrors_paths — PIO-shape mirror
    • build_info_aliases_omit_empty_optional_tools — ESP8266 (no ar/objcopy) keeps consumers safe with key in aliases
    • emit_writes_aliases_into_json — round-trip via JSON
    • find_build_info_walks_up_from_elf_dir / prefers_env_specific / returns_none_when_absent — discovery walker
    • load_build_info_unwraps_single_env — single-env outer object contract
    • resolve_reads_nm_from_build_info_auto_discovery — CLI resolver: build_info auto-pickup
    • resolve_explicit_nm_wins_over_build_info — CLI resolver: explicit override precedence
    • resolve_explicit_build_info_path_is_honoured — CLI resolver: --build-info flag

🤖 Generated with Claude Code

Extends `BuildInfo` with `nm_path` / `cppfilt_path` / `readelf_path` /
`objdump_path` and mirrors them onto a PIO-shape `aliases` block so
existing PlatformIO consumers (FastLED `ci/util/symbol_analysis.py`,
`ci/inspect_binary.py`) read fbuild-built artifacts drop-in. Paths are
derived centrally in `BuildInfo::new` from `size_path` using the GCC
cross-tool naming convention — no orchestrator surgery needed.
Teaches `fbuild symbols` to auto-locate `build_info.json` by walking up
from the ELF directory; adds a `--build-info <path>` override. The
common case becomes flag-less:
fbuild symbols .fbuild/build/uno/firmware.elf
fbuild symbols .pio/build/esp32s3/firmware.elf
Adds `docs/symbols.md` walking through the resolution order, the
`aliases` schema, and PIO/fbuild interop.
Closes#428.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jun 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@zackees, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a97d28c-7b86-4989-9acf-78b0599c035c

📥 Commits

Reviewing files that changed from the base of the PR and between 619d6ca and e284f3f.

📒 Files selected for processing (9)
  • crates/fbuild-build/src/build_info.rs
  • crates/fbuild-build/src/symbol_analyzer.rs
  • crates/fbuild-cli/src/cli/args.rs
  • crates/fbuild-cli/src/cli/dispatch.rs
  • crates/fbuild-cli/src/cli/symbols_cmd.rs
  • crates/fbuild-core/src/symbol_analysis/mod.rs
  • crates/fbuild-core/src/symbol_analysis/tests.rs
  • docs/CLAUDE.md
  • docs/symbols.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-428-buildinfo-toolchain-paths

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.

`cargo fmt --all` flagged a stray blank line in `mod.rs` and a
short-line wrap in `tests.rs`. Both were left over from when the
file was split out — they pass locally only because rustfmt
default formatting matches them when they already exist. CI runs
`cargo fmt -- --check`, which doesn't.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zackees
zackees merged commit 6eda97e into mainJun 6, 2026
83 of 84 checks passed
zackees added a commit that referenced this pull request Jun 29, 2026
…rdening (#833)
* dylint(826): port ban_std_pathbuf + ban_unrooted_tempdir from zccache
Adopt zccache's two PathBuf/tempdir lints into fbuild with allowlists
matching the fbuild source tree:
- ban_std_pathbuf: 193-entry allowlist of legacy PathBuf call sites;
steers new code at fbuild_core::path::NormalizedPath.
- ban_unrooted_tempdir: 92-entry allowlist of legacy unrooted tempdir
call sites; steers new code at fbuild_paths::get_cache_root() and
the tempfile::*_in(...) variants.
Both lints are ON and deny-by-default for new code. Target state is
zero allowlist entries; the lists shrink as migrations land.
Add the dylint crate dirs to the crate_guard allowlist so future
edits to those Cargo.tomls aren't blocked by the monocrate hook.
Refs #826, #436, #437, #282.
* dylint(826): add 3 net-new fbuild-specific lints
Add three lints that enforce fbuild's own architectural rules:
- ban_direct_serialport: bans `serialport::*` references outside
crates/fbuild-serial/ so the Windows USB-CDC contract stays in one
place. Legacy allowlist covers fbuild-cli diagnostics, the daemon
device manager, and the fbuild-deploy bootloader paths.
- ban_file_based_locks: bans `OpenOptions::create_new(true)`, the
`fs2::FileExt` API, and `libc::flock` / `nix::flock`. Allowlist is
empty — fbuild has no file-based locks per CLAUDE.md "Key
Constraints"; this locks the invariant in.
- ban_deploy_tool_direct_invocation: bans `Command::new("esptool" |
"esptool.py" | "avrdude" | "picotool" | "dfu-util" | "pyocd")`
outside crates/fbuild-deploy/. Pattern-matches the binary-name
string literal; complements ban_raw_subprocess (which scopes by
spawn shape, not binary).
All three follow the ban_raw_subprocess skeleton: pinned nightly,
file-path scope (crates/*/src/), file-level allowlist via include_str!,
forward-slash normalized. Each carries unit tests for the scope/dir
helpers.
Refs #826, #694.
* fix(daemon/ws): replace 5 unwraps in error-reply paths with fallback
The WebSocket error-reply paths in `handle_serial_ws` previously panicked
via `serde_json::to_string(&err_msg).unwrap()` if the JSON serializer
ever failed. While `SerialServerMessage` always serializes cleanly today,
a panic in the cold error path tears down the entire WebSocket — exactly
the wrong failure mode for a code path whose only job is to surface an
error to the client.
Add a small `serialize_or_fallback` helper that returns a hardcoded
JSON error frame on serializer failure, and route the 5 affected
`Message::Text(...)` constructions through it (open_port failure,
unexpected-message-shape, parse-error, attach_reader=None,
attached-confirmation send).
The 3 remaining `to_string(...).unwrap()` calls at lines ~441/450/467
are on normal data-forwarding paths (not error-reply paths) and are
out of scope for this fix; #826 only flagged the
error-reply ones as a daemon-stability hazard.
Refs #826.
* build: add new dylint crates to workspace exclude
cargo metadata refuses to operate on dylint sub-crates that are
neither members nor excluded; add the 5 new lint crates from #826
to the existing exclude list (alongside ban_raw_subprocess).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zackees added a commit that referenced this pull request Aug 7, 2026
…1274) (#1278)
The per-project build-serialization lock was keyed on the raw,
client-supplied project dir (PathBuf::from(&req.project_dir), never
normalized). Two requests naming one project with different casing or
slash direction (C:\proj vs c:/proj) keyed distinct DashMap entries and
received different locks — so two builds could run on the same project
concurrently. This is the #436/#437 path-identity bug class as a
correctness defect, not a cache annoyance.
Key project_locks on fbuild_core::path::NormalizedPath, whose Eq/Hash
use the case-folded, slash-normalized, UNC-stripped key. Display stays
readable (NormalizedPath derefs to Path; the stored path preserves
original casing).
Scope is deliberately narrow (see #1274): other path-keyed maps
(image_hash_memo, compiler identity cache, LDF walker caches) are safe
by construction — daemon-internal, single-source, or canonicalized
before insert — and are left as PathBuf.
Regression test asserts two spellings of one project share a lock on
case-insensitive platforms (Windows/macOS CI) and stay independent on
case-sensitive ones.
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

fbuild symbols: ingest toolchain paths from build_info.json + extend schema with nm/c++filt/readelf/objdump

1 participant

@zackees