Add Pyodide WebAssembly wheel support - #283
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #283 +/- ##
==========================================
- Coverage 86.82% 83.62% -3.20%
==========================================
Files 5 5
Lines 516 513 -3
Branches 82 98 +16
==========================================
- Hits 448 429 -19
Misses 26 26
- Partials 42 58 +16 ☔ View full report in Codecov by Harness. |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Audited this branch against current master (559aacd). It is already exactly zero commits behind, so I preserved the existing head rather than creating a no-op rebase. Fresh native validation passes: the x86-64 translation smoke test and all 46 unit tests are green, all pre-commit hooks pass, and merge-base lint/type scores do not regress. I also rebuilt the exact cp314-pyodide_wasm32 path with cibuildwheel 4.1.0 and Pyodide 314/Emscripten 5.0.3; the produced wheel passes the workflow real pypcode import/translation smoke test (wheel SHA-256 ab05c0ca87734921dec686a35b46a323b6020078c0796d0fcb3424748a81107b). No source changes were needed. |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
session: decbench |
Empty commit; no source tree changes.
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Full Pyodide packaging result for translating the x86-64 Before — the release workflow has no WebAssembly wheel path: pypcode baselineAfter — the target extension and host SLEIGH compiler are built separately: with this change |
#6550 moved claripy into angr.rustylib, so the WebAssembly build no longer needs a claripy wheel. It also made Z3 a link-time dependency of the Rust extension rather than a Python-level one. configure_z3() resolved Z3 against the build interpreter and raised LibError when z3-solver was not importable there. On a cross build that interpreter is the host's, and its libz3 is the wrong architecture, so the check was both fatal and pointless: the caller has already said which libz3 it wants. It now consults the interpreter only when Z3_LIBRARY_PATH_OVERRIDE is unset. Master already honoured a pre-set override, because it assigned through setdefault. Comparing the two across a pre-set, empty, blank and unset override, crossed with Z3_NO_PKG_CONFIG set and unset and with z3-solver present or not, they differ in exactly one class of state: with the override already set and z3-solver unavailable, master raises before setting anything and this does not. z3-solver 5.1.0.0 -- the version pyproject.toml already pins -- publishes a pyemscripten_2026_0_wasm32 wheel, so build_wheels.sh downloads it instead of building Z3 from source, and unpacks its libz3.so for the Rust link. That retires the z3-build-tools make shim, the Z3 checkout in CI, and the instruction to check out Z3 beside angr. The version is read out of pyproject.toml so the two cannot drift. pypcode stays an unconditional dependency, as twizmwazin asked on this pull request: the Emscripten wheel comes from angr/pypcode#283, so there is no reason to mark it out on that platform.
#6550 moved claripy into angr.rustylib, so the WebAssembly build no longer needs a claripy wheel. It also made Z3 a link-time dependency of the Rust extension rather than a Python-level one. configure_z3() resolved Z3 against the build interpreter and raised LibError when z3-solver was not importable there. On a cross build that interpreter is the host's, and its libz3 is the wrong architecture, so the check was both fatal and pointless: the caller has already said which libz3 it wants. It now consults the interpreter only when Z3_LIBRARY_PATH_OVERRIDE is unset. Master already honoured a pre-set override, because it assigned through setdefault. Comparing the two across a pre-set, empty, blank and unset override, crossed with Z3_NO_PKG_CONFIG set and unset and with z3-solver present or not, they differ in exactly one class of state: with the override already set and z3-solver unavailable, master raises before setting anything and this does not. z3-solver 5.1.0.0 -- the version pyproject.toml already pins -- publishes a pyemscripten_2026_0_wasm32 wheel, so build_wheels.sh downloads it instead of building Z3 from source, and unpacks its libz3.so for the Rust link. That retires the z3-build-tools make shim, the Z3 checkout in CI, and the instruction to check out Z3 beside angr. The version is read out of pyproject.toml so the two cannot drift. pypcode stays an unconditional dependency, as twizmwazin asked on this pull request: the Emscripten wheel comes from angr/pypcode#283, so there is no reason to mark it out on that platform. The lmdb shim reassigns its exported name -- `lmdb = _LmdbUnavailable()` when the real module is absent -- so pyright sees a variable rather than a module, and `lmdb.Environment` is not usable in a type expression. RuntimeDb annotated two of its three lmdb-typed signatures against the TYPE_CHECKING-only `lmdb_types` alias, which is never reassigned; the third now matches. The WebAssembly documentation said the build runs in a browser. It does not yet: Pyodide cannot resolve libz3.so when it loads angr.rustylib, so importing angr fails. Both files now say so.
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
The pypcode release workflow has no
cp314-pyodide_wasm32wheel, so browser consumers cannot importpypcodeand translate the one-byte x86-64retinputc3at address0x0000.Root cause
The build coupled the nanobind extension and the host SLEIGH compiler into one CMake invocation. Under Emscripten, the target toolchain can build the extension but cannot provide the native SLEIGH executable needed to compile processor specifications.
Fix
Split the CMake targets so Emscripten builds only the extension while a sanitized host CMake environment builds SLEIGH. Add the Pyodide cibuildwheel job to the existing release workflow and make publication depend on it.
Testing
The exact-head workflow built
cp314-pyodide_wasm32, importedpypcode, and assertedpypcode.Context('x86:LE:64:default').translate(b'\xc3').ops; both fresh hosted runs passed all 21 build, test, lint, wheel, and upload-test jobs. Validation: #283 (comment)session: sharpen