From 9f6321d7d6078aace4958de2cfc8b29d9c107611 Mon Sep 17 00:00:00 2001 From: sylph-pi Date: Mon, 7 Sep 2026 21:06:26 +0200 Subject: [PATCH 1/5] fix(formats): drop the unused normal `tokio` dependency `sylpheed-formats` declared `tokio` as a normal dependency and never used it as one. All four references in its `src/` are inside a `mod tests` -- three runtime builders in ship.rs (539, 566, 690; `mod tests` at 498) and one `#[tokio::test]` in xiso.rs (182; `mod tests` at 179) -- and tokio was ALREADY present in `[dev-dependencies]`, so the tests keep compiling unchanged. The unused normal dependency pulled `tokio/full`, whose `net` feature drags in `mio`, which does not build for wasm32: error: This wasm target is unsupported by mio. Removing it is right on its own terms; the WASM job is merely what exposed it. Native is unaffected -- `cargo check --workspace` exits 0 on x86_64. This is separated from the CI configuration it was found through because it is the one change here that touches another crate, and #11 is `state/proposed` around the getrandom error alone. It is ordered first so that every commit builds: the reverse order would leave an intermediate commit still failing the wasm check on `mio`. WARNING: this does NOT reach `sylpheed-export`, which builds `sylpheed-formats` from the git pin `formats-pin-2026-09-01` (`1cd5b8b1`, contained in `auto/frame-blend-draw-path` only) rather than the workspace path crate. The dependency is not gone tree-wide until that pin resolves, so anyone later adding `-p sylpheed-export` to the WASM job will hit `mio` with this fix apparently already applied. Refs #11 Co-Authored-By: Claude Opus 5 --- crates/sylpheed-formats/Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/sylpheed-formats/Cargo.toml b/crates/sylpheed-formats/Cargo.toml index 1cd5d220..b08124e6 100644 --- a/crates/sylpheed-formats/Cargo.toml +++ b/crates/sylpheed-formats/Cargo.toml @@ -11,7 +11,12 @@ xdvdfs = { workspace = true } binrw = { workspace = true } flate2 = "1" # zlib/DEFLATE for IPFB "Z1" entries (miniz_oxide backend, WASM-safe) ttf-parser = { version = "0.24", default-features = false, features = ["std", "opentype-layout"] } # font metadata (OTF/TTF/ttcf), WASM-safe -tokio = { workspace = true } +# tokio is a DEV dependency only (see [dev-dependencies] below). Every use in +# this crate is inside a `mod tests`: three runtime builders in ship.rs and one +# `#[tokio::test]` in xiso.rs. As a normal dependency it pulled `tokio/full`, +# whose `net` feature drags in `mio`, which does not build for wasm32 — +# error: This wasm target is unsupported by mio. +# so the unused dependency was breaking the WASM job. futures = { workspace = true } thiserror = { workspace = true } anyhow = { workspace = true } From a7af8a41c26d19c5866a07540e19e85e65fc0263 Mon Sep 17 00:00:00 2001 From: sylph-pi Date: Mon, 7 Sep 2026 21:06:41 +0200 Subject: [PATCH 2/5] fix(wasm): make `cargo check --target wasm32` compile With the `mio` subtree gone (previous commit), two blockers remain. Each was invisible until the one before it was cleared, which is why #11 was written around only the first. 1. getrandom 0.3 refuses wasm32-unknown-unknown without being told which backend to use. It needs `--cfg getrandom_backend="wasm_js"` AND the crate's `wasm_js` feature; its own error is explicit that either alone is insufficient. Nothing here depends on getrandom directly -- it arrives through `ahash`, in `sylpheed-viewer` only -- so the feature half is declared there purely to switch it on. 2. error: bevy_egui uses unstable APIs to support clipboard on web. Needs `--cfg web_sys_unstable_apis`. Both cfgs live in /.cargo/config.toml scoped to the wasm target, so native builds are untouched. Verified on aarch64 / rustc 1.98.1 -- the runner's toolchain -- from scratch with the cache cleared: exit 0 in 163s. Independently reproduced on x86_64 / rustc 1.90.0 as a controlled A/B against the parent, both running the job's exact invocation: with this branch exit 0, zero errors, 38s same command at 885b4d4 exit 101, the getrandom error The control also shows `bevy_egui` is never reached when getrandom fails, and the passing case contains no `mio` and no `tokio` in the wasm graph at all -- so each blocker is confirmed separately rather than by the aggregate exit code. This does NOT make the WASM job green, and the next failure is already identified rather than left to be discovered. `Install Trunk` uses `jetli/trunk-action@v0.5.0`, whose bundled `dist/index.js` contains the string `x86_64-unknown-linux-gnu` exactly once and `aarch64` not at all, while calling `os.arch()` five times with no mapping for it. On this aarch64 runner it will fetch an x86_64 binary. Upstream trunk does ship `trunk-aarch64-unknown-linux-gnu.tar.gz`, so the asset exists and only the action's selection is wrong -- but replacing the install step means picking a version to pin and an install method, which is a decision, not a fix. Left for #11 to decide. Refs #11 Co-Authored-By: Claude Opus 5 --- .cargo/config.toml | 29 +++++++++++++++++++++++++++++ Cargo.lock | 3 +++ crates/sylpheed-viewer/Cargo.toml | 7 +++++++ 3 files changed, 39 insertions(+) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..dd11e687 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,29 @@ +# getrandom 0.3 refuses to build for wasm32-unknown-unknown unless it is told +# which backend to use. The target has no OS entropy source, so the crate will +# not guess: it wants the `wasm_js` backend named explicitly, AND the matching +# feature enabled on the crate itself. Its own error is unusually clear that one +# without the other is not enough: +# +# error: The wasm32-unknown-unknown targets are not supported by default; you +# may need to enable the "wasm_js" configuration flag. Note that enabling the +# `wasm_js` feature flag alone is insufficient. +# +# This is the cfg half. The feature half is a wasm32-only dependency in +# crates/sylpheed-viewer/Cargo.toml — that is the only crate reaching getrandom +# here, transitively through `ahash`. +# +# Scoped to the wasm target, so native builds are untouched. Note that a +# RUSTFLAGS environment variable, if one is ever set, replaces this rather than +# adding to it. +# +# The second cfg is bevy_egui's. Its web clipboard support calls web-sys APIs +# that are still gated behind an unstable flag, and it refuses to build without +# it rather than silently dropping the feature: +# +# error: bevy_egui uses unstable APIs to support clipboard on web. +# +[target.wasm32-unknown-unknown] +rustflags = [ + '--cfg', 'getrandom_backend="wasm_js"', + '--cfg', 'web_sys_unstable_apis', +] diff --git a/Cargo.lock b/Cargo.lock index 83cbd322..2c962ed3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2497,9 +2497,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -4675,6 +4677,7 @@ dependencies = [ "bevy", "bevy_egui", "futures", + "getrandom 0.3.4", "image", "rfd", "rodio", diff --git a/crates/sylpheed-viewer/Cargo.toml b/crates/sylpheed-viewer/Cargo.toml index 455938d6..3a3816e5 100644 --- a/crates/sylpheed-viewer/Cargo.toml +++ b/crates/sylpheed-viewer/Cargo.toml @@ -56,3 +56,10 @@ rodio = { workspace = true } image = { version = "0.25", default-features = false, features = ["png"] } # Off-thread rasterization of embedded-font samples (avoids egui's global fonts). ab_glyph = "0.2" + +# Reached only through `ahash`, which needs an entropy source for its random +# state. Declared here purely to turn on the `wasm_js` feature; nothing in this +# crate calls getrandom directly. The paired `--cfg getrandom_backend` lives in +# /.cargo/config.toml — both are required, neither is sufficient. +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.3", features = ["wasm_js"] } From 90365b17518e729b8242b6b3f6dbb17e72c9454a Mon Sep 17 00:00:00 2001 From: sylph-pi Date: Mon, 7 Sep 2026 21:32:41 +0200 Subject: [PATCH 3/5] ci(wasm): bump trunk-action v0.5.0 -> v0.5.1 for aarch64 `Install Trunk` has never run to completion here, because the two steps before it always failed first. With those cleared it becomes reachable, and on this runner v0.5.0 would fetch the wrong binary. v0.5.0 switches on PLATFORM alone and never consults the architecture: case 'linux': arch = 'x86_64-unknown-linux-gnu'; break; v0.5.1 reads it and maps it, failing loudly rather than wrongly: const arch = process.env['ARCH'] || process.arch; case 'x64': targetArch = 'x86_64'; break; case 'arm64': targetArch = 'aarch64'; break; default: core.setFailed(`Unsupported architecture: ${arch}`); return; Node reports `arm64` here, so it resolves to `aarch64`, and upstream does publish trunk-aarch64-unknown-linux-gnu.tar.gz. There is also an `ARCH` env override if the mapping is ever wrong. Read out of the two bundled dist/index.js files, not the release notes. Counts moved as predicted: `x86_64-unknown-linux-gnu` 1 -> 0, `aarch64` 0 -> 1, `os.arch()` 5 -> 5. Confirmed independently on both machines. Two further changes the bump carries, neither of them about architecture: * the download host moves thedodd/trunk -> trunk-rs/trunk. Trunk moved repositories and v0.5.0 still points at the old one -- arguably the more durable half of the fix. * `io.mv` 1 -> 0 and `io.cp` 2 -> 3. A cross-filesystem move throws EXDEV; a copy does not. This is the fix for self-hosted runners whose /tmp is a separate filesystem, which is ours. NOTE: the string EXDEV appears zero times in either bundle, so this cannot be found by grepping for the error it prevents -- it is visible only as the primitive swap. STILL UNVERIFIED: whether `trunk build --release` then succeeds. Everything above concerns selecting and fetching the binary. The step after it has never run in this repository's history, on any architecture, so there is no basis to predict it. Expect to read that log fresh. Refs #11 Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19dc9943..d1b11f20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,7 +111,16 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Install Trunk - uses: jetli/trunk-action@v0.5.0 + # v0.5.0 selects the download by PLATFORM ONLY and never consults the + # architecture -- `case 'linux': arch = 'x86_64-unknown-linux-gnu'` -- + # so on this aarch64 runner it fetches an x86_64 binary. v0.5.1 adds + # `process.arch` with 'x64' -> 'x86_64', 'arm64' -> 'aarch64' and + # core.setFailed otherwise, so a wrong arch now fails loudly instead of + # silently. It also moves the download host thedodd/trunk -> + # trunk-rs/trunk (trunk moved repositories; v0.5.0 still points at the + # old one), and swaps io.mv for io.cp, which is what avoids EXDEV on a + # self-hosted runner whose /tmp is a separate filesystem -- ours. + uses: jetli/trunk-action@v0.5.1 - name: Check WASM compile run: > From 7285a566dd75ec6869aef01947936721ef0ac5c8 Mon Sep 17 00:00:00 2001 From: Fabian Hamm Date: Tue, 8 Sep 2026 18:55:05 +0200 Subject: [PATCH 4/5] fix(wasm): select the bin target, so trunk emits a real bundle `trunk build --release` reached the asset pipeline for the first time and failed there: found more than one target artifact: ["sylpheed_viewer", "sylpheed-viewer"] The crate declares both a [[bin]] `sylpheed-viewer` (src/main.rs) and a [lib] `sylpheed_viewer` cdylib (src/lib.rs), and the index.html link named neither, so trunk refused to guess. Trunk's error offers two ways out and THEY ARE NOT EQUIVALENT. Measured, both exiting 0: data-target-name="sylpheed_viewer" 1_478 bytes, 1 app symbol data-bin="sylpheed-viewer" 21_298_268 bytes, 2_998 app symbols Selecting the lib "succeeds" while linking nothing, because there is no wasm entry point in it -- no wasm-bindgen dependency, no import, no `#[wasm_bindgen(start)]`. The linker drops the whole app and trunk emits an empty module. That would have turned this job GREEN on a bundle that cannot start, which is worse than the red it replaced. `main()` is a valid wasm entry: it calls `sylpheed_viewer::run()` and its only native-specific code is already `#[cfg(not(target_arch = "wasm32"))]`. With the bin selected, trunk injects a real init -- `import init`, an integrity-checked module preload, `__wbindgen_start`, and the `TrunkApplicationStarted` event. The lib.rs docs claimed this file was the WASM entry point "called from `wasm_bindgen` init on the web". Nothing ever called it. That comment is what made the lib look like the right target, so it is corrected here rather than left to mislead the next reader. Verified locally with trunk 0.21.7 on x86_64. The exit code does not distinguish these two cases -- only the artifact does. Refs #11 --- crates/sylpheed-viewer/index.html | 2 +- crates/sylpheed-viewer/src/lib.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/sylpheed-viewer/index.html b/crates/sylpheed-viewer/index.html index d67d87ab..e1559e3f 100644 --- a/crates/sylpheed-viewer/index.html +++ b/crates/sylpheed-viewer/index.html @@ -186,7 +186,7 @@ - +