Skip to content

Commit b244fe7

Browse files
authored
Merge pull request #244 from hoodmane/emscripten-ci2
Add CI tests for wasm32-emscripten wheels in Pyodide
2 parents d1db3ef + 7354170 commit b244fe7

File tree

10 files changed

+200
-1
lines changed

10 files changed

+200
-1
lines changed

.github/workflows/ci.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,52 @@ jobs:
402402
shell: msys2 {0}
403403
run: |
404404
PATH="$PATH:/c/Users/runneradmin/.cargo/bin" nox -s test-mingw
405+
406+
test-emscripten:
407+
name: Test Emscripten
408+
runs-on: ubuntu-latest
409+
steps:
410+
- uses: actions/checkout@v3
411+
- uses: actions/setup-node@v3
412+
with:
413+
node-version: 18
414+
- run: |
415+
PYODIDE_VERSION=0.21.0-alpha.3
416+
417+
cd emscripten
418+
npm i [email protected] prettier
419+
cd node_modules/pyodide/
420+
node ../prettier/bin-prettier.js -w pyodide.asm.js
421+
EMSCRIPTEN_VERSION=$(node -p "require('./repodata.json').info.platform.split('_').slice(1).join('.')")
422+
PYTHON_VERSION=3.10.2
423+
424+
echo "PYODIDE_VERSION=$PYODIDE_VERSION" >> $GITHUB_ENV
425+
echo "EMSCRIPTEN_VERSION=$EMSCRIPTEN_VERSION" >> $GITHUB_ENV
426+
echo "PYTHON_VERSION=$PYTHON_VERSION" >> $GITHUB_ENV
427+
echo "ORIG_PATH=$PATH" >> $GITHUB_ENV
428+
- uses: actions-rs/toolchain@v1
429+
with:
430+
profile: minimal
431+
toolchain: nightly
432+
components: rust-src
433+
target: wasm32-unknown-emscripten
434+
override: true
435+
- uses: mymindstorm/setup-emsdk@v11
436+
with:
437+
version: ${{env.EMSCRIPTEN_VERSION}}
438+
actions-cache-folder: emsdk-cache
439+
- uses: actions/setup-python@v2
440+
id: setup-python
441+
with:
442+
python-version: ${{env.PYTHON_VERSION}}
443+
- run: pip install nox
444+
- uses: actions/cache@v3
445+
with:
446+
path: |
447+
tests/pyodide
448+
key: ${{ hashFiles('tests/*.js') }} - ${{ hashFiles('noxfile.py') }} - ${{ steps.setup-python.outputs.python-path }}
449+
- uses: Swatinem/rust-cache@v1
450+
- name: Test
451+
run: |
452+
export PATH=$ORIG_PATH:$PATH
453+
nox -s test-examples-emscripten

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22

33
## Unreleased
4+
### Added
5+
- Add support for extension modules built for wasm32-unknown-emscripten with Pyodide.
6+
47
### Changed
58
- Locate cdylib artifacts by handling messages from cargo instead of searching target dir (fixes build on MSYS2). [#267](https://github.com/PyO3/setuptools-rust/pull/267)
69
- No longer guess cross-compile environment using `HOST_GNU_TYPE` / `BUILD_GNU_TYPE` sysconfig variables. [#269](https://github.com/PyO3/setuptools-rust/pull/269)

emscripten/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
builddir
2+
main.*
3+
!main.c
4+
pybuilddir.txt
5+
pyodide
6+
node_modules
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# system configuration generated and used by the sysconfig module
2+
build_time_vars = {
3+
"ABIFLAGS": "",
4+
"AR": "/src/emsdk/emsdk/upstream/emscripten/emar",
5+
"ARFLAGS": "rcs",
6+
"BLDSHARED": "emcc -sSIDE_MODULE=1 -L/src/emscripten/python-lib/",
7+
"CC": "emcc -I/src/emscripten/python-include/",
8+
"CCSHARED": "",
9+
"CFLAGS": "-Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g "
10+
"-fwrapv -O3 -Wall -O2 -g0 -fPIC",
11+
"EXT_SUFFIX": ".cpython-310-wasm32-emscripten.so",
12+
"HOST_GNU_TYPE": "wasm32-unknown-emscripten",
13+
"LDSHARED": "emcc -sSIDE_MODULE=1",
14+
"Py_DEBUG": "0",
15+
"py_version_nodot": "310",
16+
}

emscripten/emcc_wrapper.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python3
2+
import subprocess
3+
import sys
4+
5+
6+
def update_args(args):
7+
# remove -lc. Not sure if it makes a difference but -lc doesn't belong here.
8+
# https://github.com/emscripten-core/emscripten/issues/17191
9+
for i in reversed(range(len(args))):
10+
if args[i] == "c" and args[i - 1] == "-l":
11+
del args[i - 1 : i + 1]
12+
13+
return args
14+
15+
16+
def main(args):
17+
args = update_args(args)
18+
return subprocess.call(["emcc"] + args)
19+
20+
21+
if __name__ == "__main__":
22+
args = sys.argv[1:]
23+
sys.exit(main(args))

emscripten/pyo3_config.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
implementation=CPython
2+
version=3.10
3+
shared=true
4+
abi3=false
5+
lib_name=python3.10
6+
pointer_width=32
7+
suppress_build_script_link_lines=false

emscripten/runner.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const { opendir } = require("node:fs/promises");
2+
const { loadPyodide } = require("pyodide");
3+
4+
async function findWheel(distDir) {
5+
const dir = await opendir(distDir);
6+
for await (const dirent of dir) {
7+
if (dirent.name.endsWith("whl")) {
8+
return dirent.name;
9+
}
10+
}
11+
}
12+
13+
const pkgDir = process.argv[2];
14+
const distDir = pkgDir + "/dist";
15+
const testDir = pkgDir + "/tests";
16+
17+
async function main() {
18+
const wheelName = await findWheel(distDir);
19+
const wheelURL = `file:${distDir}/${wheelName}`;
20+
21+
try {
22+
pyodide = await loadPyodide();
23+
const FS = pyodide.FS;
24+
const NODEFS = FS.filesystems.NODEFS;
25+
FS.mkdir("/test_dir");
26+
FS.mount(NODEFS, { root: testDir }, "/test_dir");
27+
await pyodide.loadPackage(["micropip", "pytest", "tomli"]);
28+
const micropip = pyodide.pyimport("micropip");
29+
await micropip.install(wheelURL);
30+
const pytest = pyodide.pyimport("pytest");
31+
errcode = pytest.main(pyodide.toPy(["/test_dir", "-vv"]));
32+
} catch (e) {
33+
console.error(e);
34+
process.exit(1);
35+
}
36+
}
37+
38+
main();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sys
2+
import pytest
3+
4+
if sys.platform == "emscripten":
5+
6+
@pytest.fixture
7+
def benchmark():
8+
def result(func, *args, **kwargs):
9+
return func(*args, **kwargs)
10+
11+
return result

noxfile.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import sys
23
import tarfile
34
from glob import glob
45
from pathlib import Path
@@ -73,3 +74,42 @@ def chdir(path: Path):
7374
session.install("pytest", "cffi")
7475
session.install("--no-build-isolation", str(examples / "html-py-ever"))
7576
session.run("pytest", str(examples / "html-py-ever"))
77+
78+
79+
@nox.session(name="test-examples-emscripten")
80+
def test_examples_emscripten(session: nox.Session):
81+
session.install(".")
82+
emscripten_dir = Path("./emscripten").resolve()
83+
84+
session.run(
85+
"rustup",
86+
"component",
87+
"add",
88+
"rust-src",
89+
"--toolchain",
90+
"nightly",
91+
external=True,
92+
)
93+
examples_dir = Path("examples").absolute()
94+
test_crates = [
95+
examples_dir / "html-py-ever",
96+
examples_dir / "namespace_package",
97+
]
98+
for example in test_crates:
99+
env = os.environ.copy()
100+
env.update(
101+
RUSTUP_TOOLCHAIN="nightly",
102+
PYTHONPATH=str(emscripten_dir),
103+
_PYTHON_SYSCONFIGDATA_NAME="_sysconfigdata__emscripten_wasm32-emscripten",
104+
_PYTHON_HOST_PLATFORM="emscripten_3_1_14_wasm32",
105+
CARGO_BUILD_TARGET="wasm32-unknown-emscripten",
106+
CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_LINKER=str(
107+
emscripten_dir / "emcc_wrapper.py"
108+
),
109+
PYO3_CONFIG_FILE=str(emscripten_dir / "pyo3_config.ini"),
110+
)
111+
with session.chdir(example):
112+
session.run("python", "setup.py", "bdist_wheel", env=env, external=True)
113+
114+
with session.chdir(emscripten_dir):
115+
session.run("node", "runner.js", str(example), external=True)

setuptools_rust/build.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def build_extension(
168168
]
169169

170170
# OSX requires special linker arguments
171-
if sys.platform == "darwin":
171+
if rustc_cfgs.get("target_os") == "macos":
172172
ext_basename = os.path.basename(self.get_dylib_ext_path(ext, ext.name))
173173
rustc_args.extend(
174174
[
@@ -184,6 +184,12 @@ def build_extension(
184184
# the cdylib, see https://github.com/rust-lang/cargo/issues/10143
185185
rustflags.append("-Ctarget-feature=-crt-static")
186186

187+
elif (rustc_cfgs.get("target_arch"), rustc_cfgs.get("target_os")) == (
188+
"wasm32",
189+
"emscripten",
190+
):
191+
rustc_args.extend(["-C", f"link-args=-sSIDE_MODULE=2 -sWASM_BIGINT"])
192+
187193
command = [
188194
self.cargo,
189195
"rustc",

0 commit comments

Comments
 (0)