extra: SDK stage for prebuilt extension deps (onnxruntime wasm w/ JSPI) - #24
Merged
Conversation
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.
This was referenced Aug 5, 2026
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
marked this pull request as ready for review
August 6, 2026 22:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-onnxwithwasm-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— alongsidecore/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
common/versions.shONNXRUNTIME_VERSIONpincommon/build-onnxruntime.shEXTRA_PLATFORM)WASM/onnxruntime.sh,macOS/onnxruntime.sh<platform>/all.shextrastage +STAGE=extra.github/workflows/wasm.ymlonnxruntime-wasm.tar.xzAdding another prebuilt = one
common/build-<dep>.sh+ a version pin + a line inbuild_extra().Scope / status
wasmleg already hascontinue-on-error, so an imperfect first run iterates safely.extralegs + theirsdk.ymlmatrix jobs are the next increment; the recipe already handles them viaEXTRA_PLATFORM.Downstream
score-addon-onnx/cmake/onnxruntime.cmakewill point its EMSCRIPTEN branch atossia/sdkreleases'onnxruntime-wasm.tar.xz(inline), once this lands and CI publishes the asset.🤖 Generated with Claude Code