neiam
/lambent-flex-c6
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Pin the nightly, and keep the toolchain off the image layer
478e173 · Gabriel Morell · 2026-09-16 18:46
Message
{commit_body(@commit)}
Files changed
modified
.forgejo/workflows/firmware.yml
+28
−13
@@ -101,13 +101,15 @@ jobs:
| 101 | 101 | set -euo pipefail |
| 102 | 102 | |
| 103 | 103 | # Espressif's own Rust image, pinned to a version rather than |
| 104 | − # `esp32c6_latest`. The pin is load-bearing twice over: it fixes the | |
| 105 | − # ESP-IDF-capable toolchain, and it fixes the *nightly* the image | |
| 106 | − # installed when it was built. `rust-toolchain.toml` asks for | |
| 107 | − # "nightly", rustup uses whatever nightly is already installed rather | |
| 108 | − # than fetching a newer one, and a current nightly does not build | |
| 109 | − # `std` for this target at all (see CLAUDE.md, failure 1 — | |
| 110 | − # `AT_FDCWD`). | |
| 104 | + # `esp32c6_latest`, for the ESP-IDF-capable toolchain it carries: | |
| 105 | + # rustup, ldproxy and espflash. | |
| 106 | + # | |
| 107 | + # It is NOT what pins the compiler — rust-toolchain.toml is. Run #5 | |
| 108 | + # is why that distinction is written down: the image's `nightly` was | |
| 109 | + # missing rust-src, so rustup resynced the channel to satisfy the | |
| 110 | + # toolchain file and pulled the current nightly, which cannot build | |
| 111 | + # `std` for this target at all (CLAUDE.md, failure 1 — `AT_FDCWD`). | |
| 112 | + # A dated channel has nothing to resync to. | |
| 111 | 113 | # |
| 112 | 114 | # Kept next to the command that uses it. Run #4 lost the image |
| 113 | 115 | # argument from the `docker run` below in an edit, podman quietly |
@@ -183,18 +185,25 @@ jobs:
| 183 | 185 | # and the image's `esp` user need not agree, and root can write |
| 184 | 186 | # either way. |
| 185 | 187 | # |
| 186 | − # HOME points inside the workspace so ~/.espressif and ~/.cargo are | |
| 187 | − # cacheable; RUSTUP_HOME stays in the image, where the pinned | |
| 188 | − # nightly already lives — pointing it at the workspace would make | |
| 189 | − # rustup fetch a current nightly, which is the one thing this pin | |
| 190 | − # exists to prevent. | |
| 188 | + # HOME, CARGO_HOME and RUSTUP_HOME all point inside the workspace, | |
| 189 | + # so they are cacheable — and so that nothing writes into the image. | |
| 190 | + # Run #5 tried the image's own /home/esp/.rustup and rustup failed | |
| 191 | + # with "Invalid cross-device link (os error 18)": renaming a file out | |
| 192 | + # of a lower overlayfs layer is EXDEV under rootless podman. | |
| 193 | + # | |
| 194 | + # The toolchain that lands there is pinned by rust-toolchain.toml to | |
| 195 | + # a dated nightly. Run #5 also showed why that matters: the image's | |
| 196 | + # `nightly` was missing rust-src, so rustup went to satisfy the | |
| 197 | + # toolchain file and resynced the channel — pulling *today\'s* | |
| 198 | + # nightly, the one that cannot build std for this target at all. | |
| 199 | + # A dated channel has nothing to resync to. | |
| 191 | 200 | docker run --rm \ |
| 192 | 201 | --user 0:0 \ |
| 193 | 202 | "${mount[@]}" \ |
| 194 | 203 | -w "$PWD/lambent-flex-c6" \ |
| 195 | 204 | -e HOME="$PWD/.home" \ |
| 196 | 205 | -e CARGO_HOME="$PWD/.home/.cargo" \ |
| 197 | − -e RUSTUP_HOME=/home/esp/.rustup \ | |
| 206 | + -e RUSTUP_HOME="$PWD/.home/.rustup" \ | |
| 198 | 207 | -e LAMBENT_FLEX_VERSION="$VERSION" \ |
| 199 | 208 | -e HOST_UID="$(id -u)" \ |
| 200 | 209 | -e HOST_GID="$(id -g)" \ |
@@ -217,6 +226,12 @@ jobs:
| 217 | 226 | |
| 218 | 227 | echo "container: ulimit-u=$(ulimit -u) pids.max=$(cat /sys/fs/cgroup/pids.max 2>/dev/null || echo n/a) jobs=$CARGO_BUILD_JOBS" |
| 219 | 228 | |
| 229 | + # Install it explicitly, so a toolchain problem reports itself | |
| 230 | + # here rather than as a confusing failure inside cargo. Reads | |
| 231 | + # the channel from rust-toolchain.toml; idempotent once cached. | |
| 232 | + rustup show active-toolchain || rustup toolchain install --profile minimal | |
| 233 | + rustup show | |
| 234 | + | |
| 220 | 235 | cargo build --release |
| 221 | 236 | |
| 222 | 237 | mkdir -p dist |
modified
CLAUDE.md
+19
−6
@@ -81,7 +81,8 @@ cargo build --release
| 81 | 81 | cargo run --release # runner = ./flash.sh (espflash + the OTA layout) |
| 82 | 82 | ``` |
| 83 | 83 | |
| 84 | −Needs nightly + `rust-src` (see `rust-toolchain.toml`) for `build-std`, plus | |
| 84 | +Needs the pinned nightly + `rust-src` (see `rust-toolchain.toml`; the pin is | |
| 85 | +load-bearing — failure 1 below) for `build-std`, plus | |
| 85 | 86 | `ldproxy` and `espflash` on PATH. `~/.espressif` holds ESP-IDF v5.1.1 and |
| 86 | 87 | v5.2.3 side by side; v5.2.3 is the one in use. |
| 87 | 88 |
@@ -185,11 +186,23 @@ five of these were hit in sequence; the first is still a live local patch.
| 185 | 186 | says ESP-IDF should skip `O_NOFOLLOW`, so the intended behaviour is plain |
| 186 | 187 | `chmod`. |
| 187 | 188 | |
| 188 | − **Worked around by patching the rustup toolchain in place**, adding an | |
| 189 | − `any(target_os = "espidf", target_os = "horizon")` arm that calls | |
| 190 | − `set_perm`. Backup at `unix.rs.orig` beside it. **`rustup update` silently | |
| 191 | − reverts this and the build breaks again** — if `AT_FDCWD` reappears, that's | |
| 192 | − why. Not fixed by the dependency bump; it is purely a `std` problem. | |
| 189 | + ~~**Worked around by patching the rustup toolchain in place**~~ — | |
| 190 | + superseded on 2026-09-16 by **pinning the nightly**. `rust-toolchain.toml` | |
| 191 | + now says `channel = "nightly-2026-04-27"`, whose `unix.rs` has no | |
| 192 | + `set_perm_nofollow` in it at all (checked against the published `rust-src` | |
| 193 | + for that date, not assumed). So there is nothing to patch, `rustup update` | |
| 194 | + cannot break the build any more, and the same compiler builds it on a | |
| 195 | + laptop and in CI — it is also the nightly the CI image ships. | |
| 196 | + | |
| 197 | + The old workaround was an in-place edit of the toolchain's `unix.rs` | |
| 198 | + adding an `any(target_os = "espidf", target_os = "horizon")` arm calling | |
| 199 | + `set_perm`, with a backup at `unix.rs.orig` beside it. Harmless to leave; | |
| 200 | + nothing reads that toolchain now. | |
| 201 | + | |
| 202 | + **Moving the pin means re-checking this.** Take the date's `rust-src` | |
| 203 | + tarball and grep `library/std/src/sys/fs/unix.rs` for `set_perm_nofollow` | |
| 204 | + before trusting a newer nightly. It is purely a `std` problem — the | |
| 205 | + dependency bump had nothing to do with it. | |
| 193 | 206 | |
| 194 | 207 | 2. **~40 bindgen types emitted as opaque `{ _address: u8 }`**, including |
| 195 | 208 | `spi_transaction_t`, `struct stat`, `netif`, `esp_netif_config`, |
modified
README.md
+3
−2
@@ -126,8 +126,9 @@ cargo build --release
| 126 | 126 | cargo run --release # -> flash.sh: espflash with the OTA layout |
| 127 | 127 | ``` |
| 128 | 128 | |
| 129 | −Requires the nightly toolchain with `rust-src` (see `rust-toolchain.toml`) for | |
| 130 | −`build-std`, plus `ldproxy` and `espflash`. | |
| 129 | +Requires the pinned nightly with `rust-src` (`rust-toolchain.toml` — rustup | |
| 130 | +installs it for you), plus `ldproxy` and `espflash`. The pin is deliberate: | |
| 131 | +current nightlies do not build `std` for this target. | |
| 131 | 132 | |
| 132 | 133 | `cargo run` goes through `flash.sh`, which adds three things OTA needs: the |
| 133 | 134 | two-slot `partitions.csv`, our own bootloader (rollback is a bootloader |
modified
rust-toolchain.toml
+18
−1
@@ -1,3 +1,20 @@
| 1 | 1 | [toolchain] |
| 2 | −channel = "nightly" | |
| 2 | +# Pinned, not "nightly". | |
| 3 | +# | |
| 4 | +# `build-std` needs a nightly, and an unpinned one means the build depends on | |
| 5 | +# whatever was published this morning. That is not hypothetical here: the | |
| 6 | +# current nightly's `set_perm_nofollow` in library/std/src/sys/fs/unix.rs calls | |
| 7 | +# `libc::fchmodat(libc::AT_FDCWD, …)` on a `_` arm that espidf falls through | |
| 8 | +# to, and the espidf libc module defines neither symbol — so `std` does not | |
| 9 | +# compile at all (CLAUDE.md, failure 1). This date predates that: its | |
| 10 | +# unix.rs has no `set_perm_nofollow` in it, verified against the published | |
| 11 | +# rust-src for 2026-04-27 rather than assumed. | |
| 12 | +# | |
| 13 | +# It is also the nightly `espressif/idf-rust:esp32c6_1.95.0.0` ships, which is | |
| 14 | +# what CI builds in — so the pin makes a laptop and CI agree instead of merely | |
| 15 | +# being reproducible on their own. | |
| 16 | +# | |
| 17 | +# Moving this means re-checking that `std` still builds for a RISC-V espidf | |
| 18 | +# target. `rustup update` no longer silently decides that for you. | |
| 19 | +channel = "nightly-2026-04-27" | |
| 3 | 20 | components = ["rust-src"] |
Parents: 5614525