bindings/r: build the C ABI from source for the WebAssembly target (#244)
## Problem
r-universe builds every package to WebAssembly (webR) in addition to the native platforms, calling `./configure --host=wasm32-unknown-emscripten`. `bindings/r/configure` only knew Linux/macOS/Windows and exited with `unsupported OS 'Emscripten'`, so the **WASM job was the single red check** on an otherwise fully-published r-universe build (all native platforms + deploy are green; the package installs fine everywhere).
## Fix
The r-universe wasm build image ships **cargo** (`/usr/local/cargo/bin`) and **emscripten** (`EMSDK` on PATH). So instead of needing a prebuilt wasm lib (which would risk an emscripten ABI/version mismatch), `configure` now detects the Emscripten host and **builds the C ABI staticlib from source** for `wasm32-unknown-emscripten` in-place — compiled with the image's own emscripten, so the ABI always matches. The static `libwickra.a` is linked into the package object (no shared lib, no rpath).
`wickra-core`'s rayon batch needs threads (absent on wasm), so the wasm build drops it via `--no-default-features`. `wickra-c` now takes `wickra-core` as a direct path dep with `default-features = false` plus a default `parallel` feature that re-enables it for native builds (a member-level `default-features = false` is ignored when inheriting a workspace dep — that was the trap).
## Validated locally
- `cargo build -p wickra-c` (default) → rayon present, builds.
- `cargo build -p wickra-c --no-default-features` (the wasm feature path) → rayon **gone**, builds.
- `cargo build --workspace`, clippy, fmt all clean; `configure` passes `sh -n`.
## Cannot be validated locally
No Rust/emscripten wasm toolchain here. The wasm build only runs in r-universe, and `configure` downloads the matching `v${version}` source tag — so this takes effect from the **first release that includes it** (the tagged source must contain the feature toggle). Open risks: whether the image has the `wasm32-unknown-emscripten` Rust target pre-installed (configure runs `rustup target add` best-effort) and the wasm build time. Worth one r-universe rebuild to confirm.
Not merging — review first.
This commit is contained in:
+14
-1
@@ -42,5 +42,18 @@ cast_sign_loss = "allow"
|
||||
similar_names = "allow"
|
||||
float_cmp = "allow"
|
||||
|
||||
[features]
|
||||
# `parallel` (rayon-backed batch in wickra-core) is on by default for native
|
||||
# builds. The wasm32-unknown-emscripten target has no threads, so the R
|
||||
# package's wasm build (r-universe / webR) compiles this crate with
|
||||
# --no-default-features to drop rayon; wickra-core falls back to its serial
|
||||
# batch path, which is cfg-gated behind the same feature.
|
||||
default = ["parallel"]
|
||||
parallel = ["wickra-core/parallel"]
|
||||
|
||||
[dependencies]
|
||||
wickra-core = { workspace = true }
|
||||
# Direct path dep rather than `workspace = true`: a member-level
|
||||
# `default-features = false` is ignored when inheriting a workspace dep that
|
||||
# does not set it, which would leave rayon in the wasm build. wickra-c is
|
||||
# `publish = false`, so no version pin is needed (and none to keep in sync).
|
||||
wickra-core = { path = "../../crates/wickra-core", default-features = false }
|
||||
|
||||
Vendored
+30
@@ -13,6 +13,36 @@ set -e
|
||||
|
||||
inc=""
|
||||
lib=""
|
||||
|
||||
# WebAssembly (r-universe / webR): there is no prebuilt wasm C ABI to download,
|
||||
# but the build image ships cargo (/usr/local/cargo/bin) and emscripten
|
||||
# (EMSDK on PATH), so build the C ABI staticlib from source for
|
||||
# wasm32-unknown-emscripten right here. Building it in the same image with the
|
||||
# same emscripten avoids any ABI/version mismatch a prebuilt lib would risk.
|
||||
# rayon (threads) is dropped via --no-default-features; the indicators are pure
|
||||
# computation, so the serial path is functionally identical.
|
||||
if [ "$(uname -s)" = "Emscripten" ]; then
|
||||
version=$(sed -n 's/^Version:[[:space:]]*//p' DESCRIPTION)
|
||||
echo "wickra: building C ABI from source for wasm32-unknown-emscripten (v${version})"
|
||||
build=$(mktemp -d)
|
||||
url="https://github.com/wickra-lib/wickra/archive/refs/tags/v${version}.tar.gz"
|
||||
"${R_HOME}/bin/Rscript" -e "download.file('${url}', '${build}/src.tar.gz', mode = 'wb', quiet = TRUE)" \
|
||||
|| { echo "wickra: failed to download source ${url}"; exit 1; }
|
||||
"${R_HOME}/bin/Rscript" -e "untar('${build}/src.tar.gz', exdir = '${build}')"
|
||||
root="${build}/wickra-${version}"
|
||||
rustup target add wasm32-unknown-emscripten 2>/dev/null || true
|
||||
( cd "${root}" && cargo build -p wickra-c --release \
|
||||
--target wasm32-unknown-emscripten --no-default-features ) \
|
||||
|| { echo "wickra: cargo wasm build failed"; exit 1; }
|
||||
cp "${root}/bindings/c/include/wickra.h" src/wickra.h
|
||||
cp "${root}/target/wasm32-unknown-emscripten/release/libwickra.a" src/libwickra.a
|
||||
rm -rf "${build}"
|
||||
# The static archive is linked into the package object; no shared lib to
|
||||
# bundle and no rpath needed.
|
||||
sed "s|@WICKRA_RPATH@||" src/Makevars.in > src/Makevars
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "${WICKRA_INCLUDE_DIR}" ] && [ -n "${WICKRA_LIB_DIR}" ]; then
|
||||
echo "wickra: using C ABI from WICKRA_INCLUDE_DIR / WICKRA_LIB_DIR (dev override)"
|
||||
inc="${WICKRA_INCLUDE_DIR}"
|
||||
|
||||
Reference in New Issue
Block a user