From 351fcdbc04e631fbe777043c035dd36c3830c57e Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Sun, 16 Aug 2026 18:55:55 -0400 Subject: [PATCH] Add Pyodide wheels to JS/WASM templates Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com> --- copier.yaml | 2 +- examples/cppjswasm.yaml | 1 + examples/rustjswasm.yaml | 1 + python/cpp/.github/workflows/build.yaml.jinja | 94 +++++++++---------- .../.github/workflows/build.yaml.jinja | 68 +++++++++++--- python/cppjswasm/.gitignore.jinja | 3 + python/cppjswasm/Makefile.jinja | 6 +- python/cppjswasm/pyproject.toml.jinja | 12 ++- ...add_pyodide %}__init__.py{% endif %}.jinja | 1 + .../rust/.github/workflows/build.yaml.jinja | 93 +++++++++--------- .../.github/workflows/build.yaml.jinja | 67 ++++++++++--- python/rustjswasm/.gitignore.jinja | 3 + python/rustjswasm/Makefile.jinja | 7 +- python/rustjswasm/pyproject.toml.jinja | 13 ++- .../{{module}}/tests/test_all.py.jinja | 4 +- ...add_pyodide %}__init__.py{% endif %}.jinja | 1 + 16 files changed, 247 insertions(+), 129 deletions(-) create mode 100644 python/cppjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja create mode 100644 python/rustjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja diff --git a/copier.yaml b/copier.yaml index 09b6dc7..582c6b3 100644 --- a/copier.yaml +++ b/copier.yaml @@ -75,7 +75,7 @@ add_pyodide: type: bool default: false help: Build and test Pyodide wheels - when: "{{ add_extension in ['cpp', 'rust'] }}" + when: "{{ add_extension in ['cpp', 'rust', 'cppjswasm', 'rustjswasm'] }}" python_version_primary: type: str diff --git a/examples/cppjswasm.yaml b/examples/cppjswasm.yaml index dfa020d..bbc3b06 100644 --- a/examples/cppjswasm.yaml +++ b/examples/cppjswasm.yaml @@ -1,5 +1,6 @@ --- add_docs: true +add_pyodide: false add_vcpkg: true add_wiki: true add_extension: cppjswasm diff --git a/examples/rustjswasm.yaml b/examples/rustjswasm.yaml index cf0ad16..4924a25 100644 --- a/examples/rustjswasm.yaml +++ b/examples/rustjswasm.yaml @@ -1,5 +1,6 @@ --- add_docs: true +add_pyodide: false add_wiki: true add_extension: rustjswasm email: 3105306+timkpaine@users.noreply.github.com diff --git a/python/cpp/.github/workflows/build.yaml.jinja b/python/cpp/.github/workflows/build.yaml.jinja index 38a9b21..bda24a5 100644 --- a/python/cpp/.github/workflows/build.yaml.jinja +++ b/python/cpp/.github/workflows/build.yaml.jinja @@ -25,11 +25,20 @@ permissions: jobs: build: - runs-on: {% raw %}${{ matrix.os }}{% endraw %} + name: build ({% raw %}${{ matrix.platform.name }}-${{ matrix.python.version }}{% endraw %}) + runs-on: {% raw %}${{ matrix.platform.os }}{% endraw %} strategy: matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + platform: + - name: ubuntu-latest + os: ubuntu-latest + - name: ubuntu-24.04-arm + os: ubuntu-24.04-arm + - name: macos-latest + os: macos-latest + - name: windows-latest + os: windows-latest python: {% if python_version_primary == "3.10" -%} - version: "3.10" @@ -49,6 +58,20 @@ jobs: - version: "3.13" cibuildwheel: "cp313" primary: {{ (python_version_primary == "3.13") | lower }} + include: + - target: native + dist_path: dist + {% if add_pyodide -%} + - platform: + name: pyodide + os: ubuntu-latest + python: + version: "3.14" + cibuildwheel: "cp314" + primary: false + target: pyodide + dist_path: dist/pyodide + {% endif %} steps: - name: Checkout @@ -71,38 +94,46 @@ jobs: {% endif -%} - name: Install dependencies run: make develop + if: matrix.target == 'native' - name: Lint run: make lint - if: matrix.os == 'ubuntu-latest' && matrix.python.primary + if: matrix.platform.name == 'ubuntu-latest' && matrix.python.primary - name: Checks run: make checks - if: matrix.os == 'ubuntu-latest' && matrix.python.primary + if: matrix.platform.name == 'ubuntu-latest' && matrix.python.primary - name: Test run: make coverage + if: matrix.target == 'native' + + - name: Build and test Pyodide wheel + run: make test-pyodide + if: matrix.target == 'pyodide' - name: Upload test results (Python) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}test-results-${{ matrix.os }}-${{ matrix.python.version }}{% endraw %} + name: {% raw %}test-results-${{ matrix.platform.name }}-${{ matrix.python.version }}{% endraw %} path: junit.xml - if: matrix.os == 'ubuntu-latest' && matrix.python.primary + if: matrix.platform.name == 'ubuntu-latest' && matrix.python.primary - name: Publish test results uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2.24.0 with: files: '**/junit.xml' - if: matrix.os == 'ubuntu-latest' && matrix.python.primary + if: matrix.platform.name == 'ubuntu-latest' && matrix.python.primary - name: Upload coverage uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: token: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} + if: matrix.target == 'native' - name: Install build dependencies run: pip install cibuildwheel + if: matrix.target == 'native' - name: Build distributions (Linux x86_64) run: | @@ -114,7 +145,7 @@ jobs: CIBW_BUILD: {% raw %}"${{ matrix.python.cibuildwheel }}-manylinux*"{% endraw %} CIBW_ARCHS_LINUX: native CIBW_BUILD_VERBOSITY: 3 - if: matrix.os == 'ubuntu-latest' + if: matrix.platform.name == 'ubuntu-latest' - name: Build distributions (Linux ARM64) run: | @@ -125,7 +156,7 @@ jobs: CIBW_BUILD: {% raw %}"${{ matrix.python.cibuildwheel }}-manylinux*"{% endraw %} CIBW_ARCHS_LINUX: native CIBW_BUILD_VERBOSITY: 3 - if: matrix.os == 'ubuntu-24.04-arm' + if: matrix.platform.name == 'ubuntu-24.04-arm' - name: Build distributions (macOS) run: | @@ -134,7 +165,7 @@ jobs: env: CIBW_BUILD: {% raw %}"${{ matrix.python.cibuildwheel }}-macos*"{% endraw %} CIBW_BUILD_VERBOSITY: 3 - if: matrix.os == 'macos-latest' + if: matrix.platform.name == 'macos-latest' - name: Build distributions (Windows) run: | @@ -142,55 +173,22 @@ jobs: make dist-py-wheel env: CIBW_BUILD: {% raw %}"${{ matrix.python.cibuildwheel }}-win_amd64"{% endraw %} - if: matrix.os == 'windows-latest' + if: matrix.platform.name == 'windows-latest' - name: Test wheel uses: actions-ext/python/test-wheel@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: runner.os == 'Linux' + if: matrix.target == 'native' && runner.os == 'Linux' - name: Test source distribution uses: actions-ext/python/test-sdist@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: matrix.os == 'ubuntu-latest' + if: matrix.platform.name == 'ubuntu-latest' - name: Upload distributions uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}dist-${{ matrix.os }}-${{ matrix.python.version }}{% endraw %} - path: dist - -{% if add_pyodide %} - pyodide: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Setup Python - uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 - with: - version: "3.14" - - - name: Setup C++ - uses: actions-ext/cpp/setup@c2b1e90366b3a1c628a1df0748135f5e345ab26f - - {% if add_vcpkg -%} - - name: Setup vcpkg cache - uses: actions-ext/cpp/setup-vcpkg-cache@c2b1e90366b3a1c628a1df0748135f5e345ab26f - - {% endif -%} - - name: Build and test Pyodide wheel - run: make test-pyodide - - - name: Upload Pyodide distribution - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: dist-pyodide - path: dist/pyodide -{% endif %} + name: {% raw %}dist-${{ matrix.platform.name }}-${{ matrix.python.version }}{% endraw %} + path: {% raw %}${{ matrix.dist_path }}{% endraw %} diff --git a/python/cppjswasm/.github/workflows/build.yaml.jinja b/python/cppjswasm/.github/workflows/build.yaml.jinja index d242db0..4df4cf8 100644 --- a/python/cppjswasm/.github/workflows/build.yaml.jinja +++ b/python/cppjswasm/.github/workflows/build.yaml.jinja @@ -25,11 +25,39 @@ permissions: jobs: build: + name: build ({% raw %}${{ matrix.name }}{% endraw %}) runs-on: {% raw %}${{ matrix.os }}{% endraw %} strategy: matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + include: + - name: ubuntu-latest + dist_path: dist + os: ubuntu-latest + python: "{{ python_version_primary }}" + target: native + - name: ubuntu-24.04-arm + dist_path: dist + os: ubuntu-24.04-arm + python: "{{ python_version_primary }}" + target: native + - name: macos-latest + dist_path: dist + os: macos-latest + python: "{{ python_version_primary }}" + target: native + - name: windows-latest + dist_path: dist + os: windows-latest + python: "{{ python_version_primary }}" + target: native + {% if add_pyodide -%} + - name: pyodide + dist_path: dist/pyodide + os: ubuntu-latest + python: "3.14" + target: pyodide + {% endif %} steps: - name: Checkout @@ -40,12 +68,13 @@ jobs: - name: Setup Python uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: - version: "{{ python_version_primary }}" + version: {% raw %}${{ matrix.python }}{% endraw %} - name: Setup Node.js uses: actions-ext/node/setup@0ea5bdcd24d2488b28e16bf611b446a2f8e1e12f with: version: 22.x + if: matrix.target == 'native' - name: Setup C++ uses: actions-ext/cpp/setup@c2b1e90366b3a1c628a1df0748135f5e345ab26f @@ -57,41 +86,50 @@ jobs: {% endif -%} - name: Setup Emscripten uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16 + if: matrix.target == 'native' - name: Install dependencies run: make develop + if: matrix.target == 'native' - name: Lint run: make lint - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Checks run: make checks - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Test run: make coverage + if: matrix.target == 'native' + + - name: Build and test Pyodide wheel + run: make test-pyodide + if: matrix.target == 'pyodide' - name: Upload test results (Python) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}test-results-${{ matrix.os }}{% endraw %} + name: {% raw %}test-results-${{ matrix.name }}{% endraw %} path: junit.xml - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Publish test results uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2.24.0 with: files: '**/junit.xml' - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Upload coverage uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: token: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} + if: matrix.target == 'native' - name: Install build dependencies run: pip install cibuildwheel + if: matrix.target == 'native' - name: Build distributions (Linux x86_64) run: | @@ -103,7 +141,7 @@ jobs: CIBW_BUILD: "cp{{ python_version_primary.replace('.', '') }}-manylinux*" CIBW_ARCHS_LINUX: native CIBW_BUILD_VERBOSITY: 3 - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Build distributions (Linux ARM64) run: | @@ -114,7 +152,7 @@ jobs: CIBW_BUILD: "cp{{ python_version_primary.replace('.', '') }}-manylinux*" CIBW_ARCHS_LINUX: native CIBW_BUILD_VERBOSITY: 3 - if: matrix.os == 'ubuntu-24.04-arm' + if: matrix.name == 'ubuntu-24.04-arm' - name: Build distributions (macOS) run: | @@ -123,7 +161,7 @@ jobs: env: CIBW_BUILD: "cp{{ python_version_primary.replace('.', '') }}-macos*" CIBW_BUILD_VERBOSITY: 3 - if: matrix.os == 'macos-latest' + if: matrix.name == 'macos-latest' - name: Build distributions (Windows) run: | @@ -131,22 +169,22 @@ jobs: make dist-py-wheel env: CIBW_BUILD: "cp{{ python_version_primary.replace('.', '') }}-win_amd64" - if: matrix.os == 'windows-latest' + if: matrix.name == 'windows-latest' - name: Test wheel uses: actions-ext/python/test-wheel@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: runner.os == 'Linux' + if: matrix.target == 'native' && runner.os == 'Linux' - name: Test source distribution uses: actions-ext/python/test-sdist@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Upload distributions uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}dist-${{ matrix.os }}{% endraw %} - path: dist + name: {% raw %}dist-${{ matrix.name }}{% endraw %} + path: {% raw %}${{ matrix.dist_path }}{% endraw %} diff --git a/python/cppjswasm/.gitignore.jinja b/python/cppjswasm/.gitignore.jinja index 0be369b..238c203 100644 --- a/python/cppjswasm/.gitignore.jinja +++ b/python/cppjswasm/.gitignore.jinja @@ -148,6 +148,9 @@ Untitled*.ipynb # Emscripten SDK (locally installed) emsdk +{% if add_pyodide -%} +.pyodide_build/ +{% endif %} # Mac .DS_Store diff --git a/python/cppjswasm/Makefile.jinja b/python/cppjswasm/Makefile.jinja index e6e05d9..50d0357 100644 --- a/python/cppjswasm/Makefile.jinja +++ b/python/cppjswasm/Makefile.jinja @@ -136,8 +136,12 @@ tests-cpp: test-cpp coverage-cpp: ## run C++ tests and collect test coverage make -C cpp coverage -.PHONY: test coverage tests +.PHONY: test{% if add_pyodide %} test-pyodide{% endif %} coverage tests test: test-py test-js test-cpp ## run all tests +{% if add_pyodide %} +test-pyodide: ## build and test the Python package in Pyodide + uvx --from cibuildwheel==4.2.0 cibuildwheel --only cp314-pyodide_wasm32 --output-dir dist/pyodide . +{% endif %} coverage: coverage-py coverage-js coverage-cpp ## run all tests and collect test coverage # alias diff --git a/python/cppjswasm/pyproject.toml.jinja b/python/cppjswasm/pyproject.toml.jinja index f8bbbd9..1dabaab 100644 --- a/python/cppjswasm/pyproject.toml.jinja +++ b/python/cppjswasm/pyproject.toml.jinja @@ -1,7 +1,7 @@ [build-system] requires = [ "hatchling", - "hatch-cpp", + "hatch-cpp{% if add_pyodide %}>=0.5.0{% endif %}", "hatch-js", "pybind11", ] @@ -48,7 +48,7 @@ develop = [ "check-dist", "cibuildwheel", "codespell", - "hatch-cpp", + "hatch-cpp{% if add_pyodide %}>=0.5.0{% endif %}", "hatch-js", "hatchling", "pybind11", @@ -111,6 +111,14 @@ archs = "arm64" environment = {SKIP_HATCH_JS="1"} skip = "*win32 *arm_64" +{% if add_pyodide -%} +[tool.cibuildwheel.pyodide] +environment = {SKIP_HATCH_JS="1"} +test-command = "python -m pytest -vvv --pyargs {{ module }}.tests" +test-extras = [] +test-requires = ["pytest"] +{% endif -%} + [tool.coverage.run] branch = true omit = [ diff --git a/python/cppjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja b/python/cppjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/python/cppjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja @@ -0,0 +1 @@ + diff --git a/python/rust/.github/workflows/build.yaml.jinja b/python/rust/.github/workflows/build.yaml.jinja index 0814ef8..f016d9a 100644 --- a/python/rust/.github/workflows/build.yaml.jinja +++ b/python/rust/.github/workflows/build.yaml.jinja @@ -25,11 +25,39 @@ permissions: jobs: build: + name: build ({% raw %}${{ matrix.name }}{% endraw %}) runs-on: {% raw %}${{ matrix.os }}{% endraw %} strategy: matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + include: + - name: ubuntu-latest + dist_path: dist + os: ubuntu-latest + python: "{{ python_version_primary }}" + target: native + - name: ubuntu-24.04-arm + dist_path: dist + os: ubuntu-24.04-arm + python: "{{ python_version_primary }}" + target: native + - name: macos-latest + dist_path: dist + os: macos-latest + python: "{{ python_version_primary }}" + target: native + - name: windows-latest + dist_path: dist + os: windows-latest + python: "{{ python_version_primary }}" + target: native + {% if add_pyodide -%} + - name: pyodide + dist_path: dist/pyodide + os: ubuntu-latest + python: "3.14" + target: pyodide + {% endif %} steps: - name: Checkout @@ -40,51 +68,60 @@ jobs: - name: Setup Python uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: - version: "{{ python_version_primary }}" + version: {% raw %}${{ matrix.python }}{% endraw %} - name: Setup Rust uses: actions-ext/rust/setup@2db8e3e4c56beb7320c7d730bd2c2d6a6447bf6b - name: Install dependencies run: make develop + if: matrix.target == 'native' - name: Lint run: make lint + if: matrix.target == 'native' - name: Checks run: make checks - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Build run: make build + if: matrix.target == 'native' - name: Test run: make coverage + if: matrix.target == 'native' + + - name: Build and test Pyodide wheel + run: make test-pyodide + if: matrix.target == 'pyodide' - name: Upload test results (Python) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}test-results-${{ matrix.os }}{% endraw %} + name: {% raw %}test-results-${{ matrix.name }}{% endraw %} path: '**/junit.xml' - if: always() + if: always() && matrix.target == 'native' - name: Publish test results uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2.24.0 with: files: '**/junit.xml' - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Upload coverage uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: token: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} + if: matrix.target == 'native' - name: Free disk space run: | sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup /usr/share/swift df -h / shell: bash - if: runner.os == 'Linux' + if: matrix.target == 'native' && runner.os == 'Linux' - name: Build distributions (Linux x86_64) run: | @@ -96,7 +133,7 @@ jobs: shell: bash env: CIBW_ARCHS_LINUX: native - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Build distributions (Linux ARM64) run: | @@ -106,7 +143,7 @@ jobs: shell: bash env: CIBW_ARCHS_LINUX: native - if: matrix.os == 'ubuntu-24.04-arm' + if: matrix.name == 'ubuntu-24.04-arm' - name: Build distributions (macOS/Windows) run: | @@ -116,50 +153,22 @@ jobs: shell: bash env: CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=11.0 - if: runner.os != 'Linux' + if: matrix.target == 'native' && runner.os != 'Linux' - name: Test wheel uses: actions-ext/python/test-wheel@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: runner.os == 'Linux' + if: matrix.target == 'native' && runner.os == 'Linux' - name: Test source distribution uses: actions-ext/python/test-sdist@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Upload distributions uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}dist-${{ matrix.os }}{% endraw %} - path: dist - -{% if add_pyodide %} - pyodide: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Setup Python - uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 - with: - version: "3.14" - - - name: Setup Rust - uses: actions-ext/rust/setup@2db8e3e4c56beb7320c7d730bd2c2d6a6447bf6b - - - name: Build and test Pyodide wheel - run: make test-pyodide - - - name: Upload Pyodide distribution - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: dist-pyodide - path: dist/pyodide -{% endif %} + name: {% raw %}dist-${{ matrix.name }}{% endraw %} + path: {% raw %}${{ matrix.dist_path }}{% endraw %} diff --git a/python/rustjswasm/.github/workflows/build.yaml.jinja b/python/rustjswasm/.github/workflows/build.yaml.jinja index 5cf2b6d..7fd0916 100644 --- a/python/rustjswasm/.github/workflows/build.yaml.jinja +++ b/python/rustjswasm/.github/workflows/build.yaml.jinja @@ -25,11 +25,39 @@ permissions: jobs: build: + name: build ({% raw %}${{ matrix.name }}{% endraw %}) runs-on: {% raw %}${{ matrix.os }}{% endraw %} strategy: matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + include: + - name: ubuntu-latest + dist_path: dist + os: ubuntu-latest + python: "{{ python_version_primary }}" + target: native + - name: ubuntu-24.04-arm + dist_path: dist + os: ubuntu-24.04-arm + python: "{{ python_version_primary }}" + target: native + - name: macos-latest + dist_path: dist + os: macos-latest + python: "{{ python_version_primary }}" + target: native + - name: windows-latest + dist_path: dist + os: windows-latest + python: "{{ python_version_primary }}" + target: native + {% if add_pyodide -%} + - name: pyodide + dist_path: dist/pyodide + os: ubuntu-latest + python: "3.14" + target: pyodide + {% endif %} steps: - name: Checkout @@ -40,7 +68,7 @@ jobs: - name: Setup Python uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: - version: "{{ python_version_primary }}" + version: {% raw %}${{ matrix.python }}{% endraw %} - name: Setup Rust uses: actions-ext/rust/setup@2db8e3e4c56beb7320c7d730bd2c2d6a6447bf6b @@ -49,48 +77,57 @@ jobs: uses: actions-ext/node/setup@0ea5bdcd24d2488b28e16bf611b446a2f8e1e12f with: version: 22.x + if: matrix.target == 'native' - name: Install dependencies run: make develop + if: matrix.target == 'native' - name: Lint run: make lint - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Checks run: make checks - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Build run: make build + if: matrix.target == 'native' - name: Test run: make coverage + if: matrix.target == 'native' + + - name: Build and test Pyodide wheel + run: make test-pyodide + if: matrix.target == 'pyodide' - name: Upload test results (Python) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}test-results-${{ matrix.os }}{% endraw %} + name: {% raw %}test-results-${{ matrix.name }}{% endraw %} path: '**/junit.xml' - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Publish test results uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2.24.0 with: files: '**/junit.xml' - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Upload coverage uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: token: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} + if: matrix.target == 'native' - name: Free disk space run: | sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup /usr/share/swift df -h / shell: bash - if: runner.os == 'Linux' + if: matrix.target == 'native' && runner.os == 'Linux' - name: Build distributions (Linux x86_64) run: | @@ -102,7 +139,7 @@ jobs: shell: bash env: CIBW_ARCHS_LINUX: native - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Build distributions (Linux ARM64) run: | @@ -112,7 +149,7 @@ jobs: shell: bash env: CIBW_ARCHS_LINUX: native - if: matrix.os == 'ubuntu-24.04-arm' + if: matrix.name == 'ubuntu-24.04-arm' - name: Build distributions (macOS/Windows) run: | @@ -122,22 +159,22 @@ jobs: shell: bash env: CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=11.0 - if: runner.os != 'Linux' + if: matrix.target == 'native' && runner.os != 'Linux' - name: Test wheel uses: actions-ext/python/test-wheel@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: runner.os == 'Linux' + if: matrix.target == 'native' && runner.os == 'Linux' - name: Test source distribution uses: actions-ext/python/test-sdist@6e1b91a408ea89f49bc8aff8724f83826f7b5446 with: module: {{module}} - if: matrix.os == 'ubuntu-latest' + if: matrix.name == 'ubuntu-latest' - name: Upload distributions uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: {% raw %}dist-${{ matrix.os }}{% endraw %} - path: dist + name: {% raw %}dist-${{ matrix.name }}{% endraw %} + path: {% raw %}${{ matrix.dist_path }}{% endraw %} diff --git a/python/rustjswasm/.gitignore.jinja b/python/rustjswasm/.gitignore.jinja index f57991a..c980a45 100644 --- a/python/rustjswasm/.gitignore.jinja +++ b/python/rustjswasm/.gitignore.jinja @@ -142,6 +142,9 @@ Untitled*.ipynb # Emscripten SDK (locally installed) emsdk +{% if add_pyodide -%} +.pyodide_build/ +{% endif %} # Mac .DS_Store diff --git a/python/rustjswasm/Makefile.jinja b/python/rustjswasm/Makefile.jinja index 03caa89..6998d74 100644 --- a/python/rustjswasm/Makefile.jinja +++ b/python/rustjswasm/Makefile.jinja @@ -133,8 +133,13 @@ tests-rs: test-rs coverage-rs: ## run rust tests and collect test coverage make -C rust coverage -.PHONY: test coverage tests +.PHONY: test{% if add_pyodide %} test-pyodide{% endif %} coverage tests test: test-py test-js test-rs ## run all tests +{% if add_pyodide %} +test-pyodide: ## build and test the Python package in Pyodide + rustup target add wasm32-unknown-emscripten + uvx --from cibuildwheel==4.2.0 cibuildwheel --only cp314-pyodide_wasm32 --output-dir dist/pyodide . +{% endif %} coverage: coverage-py coverage-js coverage-rs ## run all tests and collect test coverage # alias diff --git a/python/rustjswasm/pyproject.toml.jinja b/python/rustjswasm/pyproject.toml.jinja index c8bd654..fb7d08a 100644 --- a/python/rustjswasm/pyproject.toml.jinja +++ b/python/rustjswasm/pyproject.toml.jinja @@ -2,7 +2,7 @@ requires = [ "hatchling", "hatch-js", - "hatch-rs", + "hatch-rs{% if add_pyodide %}>=0.3.0{% endif %}", ] build-backend = "hatchling.build" @@ -48,7 +48,7 @@ develop = [ "cibuildwheel", "codespell", "hatch-js", - "hatch-rs", + "hatch-rs{% if add_pyodide %}>=0.3.0{% endif %}", "hatchling", "mdformat", "mdformat-tables>=1", @@ -130,6 +130,15 @@ archs = "arm64" environment = {PATH="$UserProfile\\.cargo\bin;$PATH", CARGO_TERM_COLOR="always"} skip = "*win32 *arm_64" +{% if add_pyodide -%} +[tool.cibuildwheel.pyodide] +environment = {SKIP_HATCH_JS="1"} +test-command = "python -m pytest -vvv --pyargs {{ module }}.tests" +test-extras = [] +test-requires = ["pytest"] +xbuild-tools = ["cargo", "rustc", "rustup"] +{% endif -%} + [tool.coverage.run] branch = true omit = [ diff --git a/python/rustjswasm/{{module}}/tests/test_all.py.jinja b/python/rustjswasm/{{module}}/tests/test_all.py.jinja index 152fb40..1c802c0 100644 --- a/python/rustjswasm/{{module}}/tests/test_all.py.jinja +++ b/python/rustjswasm/{{module}}/tests/test_all.py.jinja @@ -1,5 +1,5 @@ -from {{ module }} import * +from {{ module }}{% if add_pyodide %}.{{ module }} import Example{% else %} import *{% endif %} def test_all(): - assert True + assert {% if add_pyodide %}str(Example("test")) == "test"{% else %}True{% endif %} diff --git a/python/rustjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja b/python/rustjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/python/rustjswasm/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja @@ -0,0 +1 @@ +