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
5 changes: 3 additions & 2 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ CI/CD workflows for the fbuild project, covering lint, test, documentation, and

## CI Checks (push/PR)

- **`check-{macos,ubuntu,windows}.yml`** -- Clippy + tests per platform
- **`check-{ubuntu,windows}.yml`** -- Clippy + tests per platform (no macOS runner: macOS
binaries are cross-built from Linux in the release matrix, see below)
- **`fmt.yml`** -- Rustfmt check | **`docs.yml`** -- Doc build with `-D warnings`
- **`msrv.yml`** -- MSRV 1.95.0 verification | **`validate-boards.yml`** -- Board JSON validation
- **`platform-boundary-research.yml`** -- Windows/Linux/macOS reconciliation of the #1307 research inventory and RED fixture
- **`platform-boundary-research.yml`** -- Windows/Linux reconciliation of the #1307 research inventory and RED fixture
- **`loc-gate.yml`** -- Reject `.rs` files over 1000 LOC | **`lint-subprocess.yml`** -- Forbid direct subprocess spawns
- **`crate-gate.yml`** -- Reject new workspace crates (monocrate policy, `ci/check_workspace_crates.py`)

Expand Down
16 changes: 11 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,21 @@ jobs:
linux_cross: true
macos_cross: false

# x86_64-apple-darwin removed: Intel Macs use ARM wheel via Rosetta.
# Cross-compiling ring crate ARM→x86 fails; native macos-13 runners
# are perpetually unavailable. Re-enable when ring supports cross or
# GitHub restores Intel runner capacity.
# x86_64-apple-darwin is not built here. This workflow builds
# binaries on demand; the release matrix (release-auto.yml) is
# what produces the Intel archive when a release needs one, and
# it cross-builds that target from Linux too.

# aarch64-apple-darwin cross-compiles from the Linux runner via
# soldr + cargo-zigbuild + soldr's managed Apple SDK, matching the
# release matrix. No macos-latest runner is used anywhere in this
# workflow.
- target: aarch64-apple-darwin
runner: macos-latest
runner: ubuntu-latest
binary_ext: ""
linux_cross: false
macos_cross: false
mac_cross_linux: true

- target: x86_64-pc-windows-msvc
runner: windows-latest
Expand All @@ -59,4 +64,5 @@ jobs:
binary_ext: ${{ matrix.binary_ext }}
linux_cross: ${{ matrix.linux_cross }}
macos_cross: ${{ matrix.macos_cross }}
mac_cross_linux: ${{ matrix.mac_cross_linux == true }}
ref: ${{ github.event.inputs.ref || 'main' }}
58 changes: 0 additions & 58 deletions .github/workflows/check-macos.yml

This file was deleted.

2 changes: 0 additions & 2 deletions .github/workflows/platform-boundary-research.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ jobs:
host: linux
- os: windows-latest
host: windows
- os: macos-latest
host: macos
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
Expand Down
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ The four rules an agent must internalize before doing anything else (all listed
- **Always use a globally-installed `soldr` to execute Rust commands.** Bare cargo/rustc and legacy `uv run cargo` shims are blocked by hook. soldr uses `rustup which` to pick the rustup-managed toolchain from `rust-toolchain.toml`. The standard Cargo path is `soldr cargo ...`, so repo Rust builds get soldr's managed zccache path by default; do not add repo-specific `RUSTC_WRAPPER` wiring for normal builds. Install soldr globally via `uv tool install soldr` (or see https://github.com/zackees/soldr).
- **Always use `uv` for Python.** Bare `python`/`pip` are blocked by hook. Use `uv run ...` or `uv pip ...`.
- MSRV: 1.95.0 | Edition: 2021 | Toolchain: 1.95.0 pinned in `rust-toolchain.toml` (clippy + rustfmt)
- CI: Linux, macOS, Windows. All warnings denied (`RUSTFLAGS="-D warnings"`)
- CI hosts: Linux, Windows. All warnings denied (`RUSTFLAGS="-D warnings"`). There are
**no macOS runners** — macOS is a build *target*, not a test host: every apple-darwin
binary is cross-built from Linux (soldr + `cargo-zigbuild` + managed Apple SDK) by the
Linux-hosted native workflows, `release-auto.yml` and `build.yml`. Do not add a
`macos-latest` lane back.
- Every directory with files must have a README.md (enforced by hook)

## Commands
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ files already used by PlatformIO sketches, but uses a Rust-native, data-driven
build pipeline.

[![Check Ubuntu](https://github.com/fastled/fbuild/actions/workflows/check-ubuntu.yml/badge.svg)](https://github.com/fastled/fbuild/actions/workflows/check-ubuntu.yml)
[![Check macOS](https://github.com/fastled/fbuild/actions/workflows/check-macos.yml/badge.svg)](https://github.com/fastled/fbuild/actions/workflows/check-macos.yml)
[![Check Windows](https://github.com/fastled/fbuild/actions/workflows/check-windows.yml/badge.svg)](https://github.com/fastled/fbuild/actions/workflows/check-windows.yml)
[![Formatting](https://github.com/fastled/fbuild/actions/workflows/fmt.yml/badge.svg)](https://github.com/fastled/fbuild/actions/workflows/fmt.yml)
[![Documentation](https://github.com/fastled/fbuild/actions/workflows/docs.yml/badge.svg)](https://github.com/fastled/fbuild/actions/workflows/docs.yml)
Expand Down
77 changes: 76 additions & 1 deletion crates/fbuild-library/src/library/esp32_framework/libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use std::path::{Path, PathBuf};
use super::Esp32Framework;
use super::fs_utils::copy_dir_recursive;

/// Archive whose presence proves a per-MCU SDK tree carries real libraries
/// and not just the partial directory some core archives ship.
const FREERTOS_ARCHIVE: &str = "libfreertos.a";

const NEW_SDK_LAYOUT: &str = "esp32-arduino-libs";
const OLD_SDK_LAYOUT: &str = "sdk";

Expand Down Expand Up @@ -57,7 +61,31 @@ fn mcu_sdk_complete(mcu_dir: &Path) -> bool {
.join("FreeRTOS.h")
.exists()
&& mcu_dir.join("flags").join("includes").exists()
&& mcu_dir.join("lib").join("libfreertos.a").exists()
&& has_freertos_archive(mcu_dir)
}

/// Locate `libfreertos.a` inside an installed per-MCU SDK tree.
///
/// Most MCUs put every archive in `lib/`. ESP32-S3 does not: its FreeRTOS
/// build differs per flash/PSRAM mode, so `libfreertos.a` ships **only**
/// under the memory-type variant dirs (`dio_opi`, `qio_qspi`, ...) while
/// `lib/` holds the other 165 archives. Requiring `lib/libfreertos.a`
/// therefore judged a complete S3 install incomplete forever, and
/// [`Esp32Framework::ensure_libs`] re-downloaded and re-extracted the
/// 298 MB SDK archive on *every* build — 132 s of a 136 s no-op
/// (FastLED/fbuild#1411).
fn has_freertos_archive(mcu_dir: &Path) -> bool {
if mcu_dir.join("lib").join(FREERTOS_ARCHIVE).exists() {
return true;
}
// Only one level deep: the variant dirs sit directly under the MCU dir.
std::fs::read_dir(mcu_dir)
.map(|entries| {
entries
.flatten()
.any(|entry| entry.path().join(FREERTOS_ARCHIVE).exists())
})
.unwrap_or(false)
}

/// Merge a requested MCU directory from a skeleton archive, regardless of a
Expand Down Expand Up @@ -129,6 +157,22 @@ impl Esp32Framework {
}
}

// A present-but-incomplete MCU tree means the ~300 MB SDK is about
// to be installed again — re-extracted, and re-downloaded too
// unless the archive is still cached below. That is correct on a
// genuinely partial install and catastrophic when the completion
// check is simply wrong about the layout — the silent form of this
// cost every build 132 s on ESP32-S3 (FastLED/fbuild#1411). Say so.
for mcu_dir in mcu_sdk_dir_candidates(&tools_dir, mcu) {
if mcu_dir.exists() {
tracing::warn!(
"{} SDK dir {} exists but is incomplete; reinstalling the SDK libs",
mcu,
mcu_dir.display()
);
}
}

std::fs::create_dir_all(&tools_dir)?;

// Check for already-downloaded archive (skip re-download)
Expand Down Expand Up @@ -250,6 +294,37 @@ mod tests {
write(&mcu_dir.join("lib").join("libfreertos.a"), "");
}

/// ESP32-S3 is the one MCU whose `esp32-arduino-libs` tree ships
/// `libfreertos.a` **only** under the per-memory-type variant dirs
/// (`dio_opi`, `qio_qspi`, ...) — `lib/` holds the other 165 archives
/// but not that one. Requiring `lib/libfreertos.a` therefore judged
/// a fully-installed S3 SDK incomplete on every build, and
/// `ensure_libs` re-downloaded + re-extracted the 298 MB archive each
/// time: 132 s of a 136 s no-op build (FastLED/fbuild#1411).
#[test]
fn mcu_sdk_complete_accepts_memory_type_variant_freertos_lib() {
let tmp = tempfile::TempDir::new().unwrap();
let mcu_dir = tmp.path().join("esp32s3");

write(
&mcu_dir
.join("include")
.join("freertos")
.join("FreeRTOS-Kernel")
.join("include")
.join("freertos")
.join("FreeRTOS.h"),
"",
);
write(&mcu_dir.join("flags").join("includes"), "");
// 165 archives land in `lib/`, but not libfreertos.a.
write(&mcu_dir.join("lib").join("libdriver.a"), "");
assert!(!mcu_sdk_complete(&mcu_dir));

write(&mcu_dir.join("qio_qspi").join("libfreertos.a"), "");
assert!(mcu_sdk_complete(&mcu_dir));
}

#[test]
fn mcu_sdk_complete_requires_freertos_kernel_header() {
let tmp = tempfile::TempDir::new().unwrap();
Expand Down
10 changes: 6 additions & 4 deletions docs/platform-boundary-research-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ external/vendor sources are outside this research union.

Because the scanner walks source rather than expanded modules, the same checkout
produces the same union on every host. `.github/workflows/platform-boundary-research.yml`
runs the drift check and fixture tests on Windows, Linux, and macOS and prints a
host-labelled total. Phase 2 must still reconcile the three raw parser/Dylint
inventories before it freezes its authoritative ledger; compiler hooks alone
cannot see wrong-host or orphaned module files.
runs the drift check and fixture tests on Windows and Linux and prints a
host-labelled total (the macOS runner lane was dropped from CI; macOS binaries
are cross-built from Linux, and the host-independent scanner reports the same
union there). Phase 2 must still reconcile the raw parser/Dylint inventories
before it freezes its authoritative ledger; compiler hooks alone cannot see
wrong-host or orphaned module files.

Reproduce locally with:

Expand Down
14 changes: 10 additions & 4 deletions docs/platform-boundary-research.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ initially reported 490 candidate occurrences. Phase 2 Dylint/scanner
reconciliation first corrected the union to 496 after adding missed constructs
and removing local-module false positives, then incorporated eight host-cfg
occurrences added to `fbuild-paths` on `main` before the baseline merged. The
authoritative union is therefore 504. The checked-in rows and reproducible three-host protocol are described in
authoritative union is therefore 504. The checked-in rows and the reproducible
cross-host protocol (three hosts as run for phase 1; the macOS lane has since
been dropped from CI) are described in
`platform-boundary-research-inventory.md`. This is reviewed research input, not
the phase-2 exact-occurrence baseline.

Expand Down Expand Up @@ -123,7 +125,9 @@ No current fbuild component needs a permanent specialized-artifact zone.
| build/test fixtures | Generic or concrete-facade tests | Tests are not an exemption from the source boundary. |

Phase 2 must revalidate this decision against its parser-derived three-host
union. If it discovers a genuine artifact ABI constraint, the exception must be
union (the scanner is host-independent; CI now runs it on Linux and Windows
only -- the macOS runner lane was dropped and macOS binaries are cross-built
from Linux). If it discovers a genuine artifact ABI constraint, the exception must be
named and narrowly linted; a file/directory wildcard is not acceptable.

## RED evidence
Expand All @@ -132,8 +136,10 @@ named and narrowly linted; a file/directory wildcard is not acceptable.
attribute, active-host native import, `cfg!` expression, and compile-time host
fact. It compiles on Windows, Linux, and macOS today because fbuild has no
host-platform boundary lint. The phase-1 workflow preserves that positive
compile result on all three hosts. Phase 2 converts the same constructs into
negative Dylint/scanner fixtures whose expected result is a boundary error.
compile result on the Linux and Windows CI hosts (the macOS runner lane was
dropped from CI; macOS binaries are cross-built from Linux). Phase 2 converts
the same constructs into negative Dylint/scanner fixtures whose expected result
is a boundary error.

Existing production and test sources provide additional RED evidence: the
research inventory includes private/inactive attributes, 77 native paths, and
Expand Down
Loading