Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CI tests for wasm32-emscripten wheels in Pyodide #244

Merged
merged 4 commits into from
Aug 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,52 @@ jobs:
shell: msys2 {0}
run: |
PATH="$PATH:/c/Users/runneradmin/.cargo/bin" nox -s test-mingw

test-emscripten:
name: Test Emscripten
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: |
PYODIDE_VERSION=0.21.0-alpha.2

cd emscripten
npm i pyodide@0.21.0-alpha.2 prettier
messense marked this conversation as resolved.
Show resolved Hide resolved
cd node_modules/pyodide/
node ../prettier/bin-prettier.js -w pyodide.asm.js
EMSCRIPTEN_VERSION=$(node -p "require('./repodata.json').info.platform.split('_').slice(1).join('.')")
PYTHON_VERSION=3.10.2

echo "PYODIDE_VERSION=$PYODIDE_VERSION" >> $GITHUB_ENV
echo "EMSCRIPTEN_VERSION=$EMSCRIPTEN_VERSION" >> $GITHUB_ENV
echo "PYTHON_VERSION=$PYTHON_VERSION" >> $GITHUB_ENV
echo "ORIG_PATH=$PATH" >> $GITHUB_ENV
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
components: rust-src
target: wasm32-unknown-emscripten
override: true
- uses: mymindstorm/setup-emsdk@v11
with:
version: ${{env.EMSCRIPTEN_VERSION}}
actions-cache-folder: emsdk-cache
- uses: actions/setup-python@v2
id: setup-python
with:
python-version: ${{env.PYTHON_VERSION}}
- run: pip install nox
- uses: actions/cache@v3
with:
path: |
tests/pyodide
key: ${{ hashFiles('tests/*.js') }} - ${{ hashFiles('noxfile.py') }} - ${{ steps.setup-python.outputs.python-path }}
- uses: Swatinem/rust-cache@v1
- name: Test
run: |
export PATH=$ORIG_PATH:$PATH
nox -s test-examples-emscripten
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

## Unreleased
### Added
- Add support for extension modules built for wasm32-unknown-emscripten with Pyodide.

### Changed
- 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)
- Fix RustBin build without wheel. [#273](https://github.com/PyO3/setuptools-rust/pull/273)
Expand Down
6 changes: 6 additions & 0 deletions emscripten/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
builddir
main.*
!main.c
pybuilddir.txt
pyodide
node_modules
16 changes: 16 additions & 0 deletions emscripten/_sysconfigdata__emscripten_wasm32-emscripten.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# system configuration generated and used by the sysconfig module
build_time_vars = {
"ABIFLAGS": "",
"AR": "/src/emsdk/emsdk/upstream/emscripten/emar",
"ARFLAGS": "rcs",
"BLDSHARED": "emcc -sSIDE_MODULE=1 -L/src/emscripten/python-lib/",
"CC": "emcc -I/src/emscripten/python-include/",
"CCSHARED": "",
"CFLAGS": "-Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g "
"-fwrapv -O3 -Wall -O2 -g0 -fPIC",
"EXT_SUFFIX": ".cpython-310-wasm32-emscripten.so",
"HOST_GNU_TYPE": "wasm32-unknown-emscripten",
"LDSHARED": "emcc -sSIDE_MODULE=1",
"Py_DEBUG": "0",
"py_version_nodot": "310",
}
23 changes: 23 additions & 0 deletions emscripten/emcc_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import subprocess
import sys


def update_args(args):
# remove -lc. Not sure if it makes a difference but -lc doesn't belong here.
# https://github.com/emscripten-core/emscripten/issues/17191
for i in reversed(range(len(args))):
if args[i] == "c" and args[i - 1] == "-l":
del args[i - 1 : i + 1]

return args


def main(args):
args = update_args(args)
return subprocess.call(["emcc"] + args)


if __name__ == "__main__":
args = sys.argv[1:]
sys.exit(main(args))
7 changes: 7 additions & 0 deletions emscripten/pyo3_config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
implementation=CPython
version=3.10
shared=true
abi3=false
lib_name=python3.10
pointer_width=32
suppress_build_script_link_lines=false
38 changes: 38 additions & 0 deletions emscripten/runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { opendir } = require("node:fs/promises");
const { loadPyodide } = require("pyodide");

async function findWheel(distDir) {
const dir = await opendir(distDir);
for await (const dirent of dir) {
if (dirent.name.endsWith("whl")) {
return dirent.name;
}
}
}

const pkgDir = process.argv[2];
const distDir = pkgDir + "/dist";
const testDir = pkgDir + "/tests";

async function main() {
const wheelName = await findWheel(distDir);
const wheelURL = `file:${distDir}/${wheelName}`;

try {
pyodide = await loadPyodide();
const FS = pyodide.FS;
const NODEFS = FS.filesystems.NODEFS;
FS.mkdir("/test_dir");
FS.mount(NODEFS, { root: testDir }, "/test_dir");
await pyodide.loadPackage(["micropip", "pytest", "tomli"]);
const micropip = pyodide.pyimport("micropip");
await micropip.install(wheelURL);
const pytest = pyodide.pyimport("pytest");
errcode = pytest.main(pyodide.toPy(["/test_dir", "-vv"]));
} catch (e) {
console.error(e);
process.exit(1);
}
}

main();
11 changes: 11 additions & 0 deletions examples/html-py-ever/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sys
import pytest

if sys.platform == "emscripten":

@pytest.fixture
def benchmark():
def result(func, *args, **kwargs):
return func(*args, **kwargs)

return result
40 changes: 40 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys
import tarfile
from glob import glob
from pathlib import Path
Expand Down Expand Up @@ -73,3 +74,42 @@ def chdir(path: Path):
session.install("pytest", "cffi")
session.install("--no-build-isolation", str(examples / "html-py-ever"))
session.run("pytest", str(examples / "html-py-ever"))


@nox.session(name="test-examples-emscripten")
def test_examples_emscripten(session: nox.Session):
session.install(".")
emscripten_dir = Path("./emscripten").resolve()

session.run(
"rustup",
"component",
"add",
"rust-src",
"--toolchain",
"nightly",
external=True,
)
examples_dir = Path("examples").absolute()
test_crates = [
examples_dir / "html-py-ever",
examples_dir / "namespace_package",
]
for example in test_crates:
env = os.environ.copy()
env.update(
RUSTUP_TOOLCHAIN="nightly",
PYTHONPATH=str(emscripten_dir),
_PYTHON_SYSCONFIGDATA_NAME="_sysconfigdata__emscripten_wasm32-emscripten",
_PYTHON_HOST_PLATFORM="emscripten_3_1_14_wasm32",
CARGO_BUILD_TARGET="wasm32-unknown-emscripten",
CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_LINKER=str(
emscripten_dir / "emcc_wrapper.py"
),
PYO3_CONFIG_FILE=str(emscripten_dir / "pyo3_config.ini"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this should I infer that pyo3_build_config doesn't configure for pyodide correctly? Do you think we would be able to upstream a fix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this should I infer that pyo3_build_config doesn't configure for pyodide correctly?

I am not sure. I think it may just be that I don't know how to use the tool correctly. How would I check this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So usually in cross-compile situations like for emscripten the pyo3_build_config build scripts will try to look for a sysconfigdata file and configure from that. Alternatively, can set the abi3-py310 feature and then it will do a "standard" cross compile, which might just work.

I view the PYO3_CONFIG_FILE env var as an escape hatch when nothing built-in works, so if either of the above work (or can be made to work) that's probably preferable.

)
with session.chdir(example):
session.run("python", "setup.py", "bdist_wheel", env=env, external=True)

with session.chdir(emscripten_dir):
session.run("node", "runner.js", str(example), external=True)
10 changes: 8 additions & 2 deletions setuptools_rust/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import glob
import json
import os
import pkg_resources
import platform
import shutil
import subprocess
Expand All @@ -20,6 +19,7 @@
from pathlib import Path
from typing import Dict, Iterable, List, NamedTuple, Optional, Set, Tuple, cast

import pkg_resources
from setuptools.command.build import build as CommandBuild # type: ignore[import]
from setuptools.command.build_ext import build_ext as CommandBuildExt
from setuptools.command.build_ext import get_abi3_suffix
Expand Down Expand Up @@ -184,7 +184,7 @@ def build_extension(
]

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

elif (rustc_cfgs.get("target_arch"), rustc_cfgs.get("target_os")) == (
"wasm32",
"emscripten",
):
rustc_args.extend(["-C", f"link-args=-sSIDE_MODULE=2 -sWASM_BIGINT"])

command = [
self.cargo,
"rustc",
Expand Down