Skip to content

extra: SDK stage for prebuilt extension deps (onnxruntime wasm w/ JSPI) - #24

Merged
jcelerier merged 7 commits into
masterfrom
extra-stage-prebuilt-deps
Aug 6, 2026
Merged

extra: SDK stage for prebuilt extension deps (onnxruntime wasm w/ JSPI)#24
jcelerier merged 7 commits into
masterfrom
extra-stage-prebuilt-deps

Conversation

@jcelerier

Copy link
Copy Markdown
Member

Why

Extensions increasingly need heavy deps prebuilt to match the SDK's ABI. The concrete trigger: score's WASM build fails to link score-addon-onnx with wasm-ld: undefined symbol: __resumeException, because the public onnxruntime wasm prebuilts are legacy-EH (-s DISABLE_EXCEPTION_CATCHING=0) while the whole SDK + score compile with -fwasm-exceptions (score links -sJSPI). This is red on master today.

What

A third build stage — extra — alongside core/media. It pre-builds per-addon deps against the just-built SDK toolchain and ships them as their own release assets (not bundled in the base SDK), so addons drop them in instead of fetching mismatched upstream binaries.

onnxruntime, wasm: built from source with --enable_wasm_jspi, which onnxruntime maps to -fwasm-exceptions -s WASM_LEGACY_EXCEPTIONS=0 (adjust_global_compile_flags.cmake) — the same EH ABI score links. A hard guard (llvm-nm | grep __resumeException) fails the build if JSPI didn't take, so a bad lib can never reach an addon. Desktop repackages the upstream release (native EH already matches).

Framework

file role
common/versions.sh ONNXRUNTIME_VERSION pin
common/build-onnxruntime.sh shared recipe, platform dispatch (EXTRA_PLATFORM)
WASM/onnxruntime.sh, macOS/onnxruntime.sh thin per-platform entry
<platform>/all.sh extra stage + STAGE=extra
.github/workflows/wasm.yml build + publish onnxruntime-wasm.tar.xz

Adding another prebuilt = one common/build-<dep>.sh + a version pin + a line in build_extra().

Scope / status

  • WASM onnx is fully wired here (the actual bug) and validates in this repo's CI — the wasm leg already has continue-on-error, so an imperfect first run iterates safely.
  • macOS demonstrates the desktop repackage path.
  • Linux/MSVC/MSYS/ARM extra legs + their sdk.yml matrix jobs are the next increment; the recipe already handles them via EXTRA_PLATFORM.

Downstream

score-addon-onnx/cmake/onnxruntime.cmake will point its EMSCRIPTEN branch at ossia/sdk releases' onnxruntime-wasm.tar.xz (inline), once this lands and CI publishes the asset.

🤖 Generated with Claude Code

Jean-Michaël Celerier and others added 5 commits August 3, 2026 22:37
Adds a third build stage, `extra`, alongside core/media: it pre-builds
per-addon heavy dependencies against the freshly-built SDK toolchain and ships
them as their own release assets (not bundled in the base SDK), so addons drop
them in instead of fetching mismatched upstream binaries.

First dep: onnxruntime. WASM is built from source with --enable_wasm_jspi,
which compiles it with `-fwasm-exceptions` -- matching score's -sJSPI link. The
public prebuilt (and csukuangfj's) are legacy-EH and leave __resumeException
undefined at score's wasm link; build-onnxruntime.sh guards against that with an
`llvm-nm | grep __resumeException` check that fails the build if JSPI didn't
take. Desktop repackages the upstream release (native EH already matches).

Framework:
  common/versions.sh          ONNXRUNTIME_VERSION pin
  common/build-onnxruntime.sh shared recipe, platform dispatch
  <platform>/onnxruntime.sh   thin per-platform entry (WASM, macOS)
  <platform>/all.sh           `extra` stage + STAGE=extra
  .github/workflows/wasm.yml  build + publish onnxruntime-wasm.tar.xz

Adding another prebuilt = one common/build-<dep>.sh + a version pin + a line in
build_extra(). Linux/MSVC/MSYS/ARM extra legs + their sdk.yml matrix jobs are the
next increment (the recipe already handles them via EXTRA_PLATFORM).
ARM desktop (macOS arm64, Linux aarch64) now builds onnxruntime from source
with the EPs that help score's real-time CV workload instead of repackaging the
CPU-only upstream prebuilt:
  - MLAS + KleidiAI + int8 dotprod/i8mm (default on aarch64 via build.py)
  - XNNPACK EP (conv-heavy fp32)
  - CoreML EP (MLProgram) on Apple Silicon; --build_apple_framework merges the
    static libs into one
  - Linux aarch64 also enables onnxruntime_USE_ARM_NEON_NCHWC; ships the full
    static-lib set + a link-order manifest (from-source ORT is many .a's)
Deliberately NOT enabled: ACL, ArmNN (removed in ORT v1.25), DirectML, WebGPU,
Vulkan/OpenCL -- unmaintained or regressive for this workload. QNN (Snapdragon
NPU, quantized-only) is a separate artifact via the onnxruntime-qnn plugin repo.
x86_64 keeps repackaging the (already well-tuned) upstream prebuilt.
Static is only needed for wasm. Building ARM desktop (macOS arm64, Linux
aarch64) as a shared libonnxruntime.so/.dylib (--build_shared_lib) bundles the
EPs + third-party deps (abseil/protobuf/onnx/re2/...) inside the .so: the
consumer links one library and there are no protobuf/Abseil symbol clashes with
score's own copies -- removing the multi-lib static-link problem entirely.
--skip_tests only skips running them. onnxruntime_perf_test still got
built and failed to link with an undefined HasUSDot(), which is an MLAS
ARM feature probe that is never defined for wasm. The macOS and Linux
invocations already pass this.
build.py installs an emsdk of its own into cmake/external/emsdk and uses
it for the toolchain file. Symlink that to $EMSDK so the archive is built
by the same emscripten score links with.

The exported CFLAGS/CXXFLAGS/LDFLAGS reached CMAKE_CXX_FLAGS and added
-mrelaxed-simd and -flto. The former declares MLAS relaxed-SIMD dispatch
that ORT does not compile, leaving it undefined in the archive; the
latter turns the archive into bitcode tied to one LLVM. ORT already
passes -msimd128 -fwasm-exceptions -pthread on its own.

Replace the __resumeException grep with a link test: undefined symbols
in a static archive are only a problem at the consumer's link, so link
one here.

Also drop continue-on-error from the job, which reported success while
this step was failing.
git submodule sync refuses a symlink at a registered submodule path, so
pointing cmake/external/emsdk at ours made build.py fail before configure.
The source is cloned recursively already.
Consumers pin an onnxruntime version, not an SDK release, and several of
them have to agree on it. onnxruntime-1.27.0-wasm.tar.xz lets them ask
for one; onnxruntime-wasm.tar.xz only lets them take what is there.
@jcelerier
jcelerier marked this pull request as ready for review August 6, 2026 22:33
@jcelerier
jcelerier merged commit 1639590 into master Aug 6, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant