executorch: Add version 1.4.1 #11
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
| # SPDX-FileCopyrightText: 2026 The RISE Project | |
| # SPDX-License-Identifier: MIT | |
| --- | |
| # This workflow is based on: | |
| # https://github.com/pytorch/executorch/blob/v1.4.1/.github/workflows/build-wheels-linux.yml | |
| name: Build executorch wheels (riscv64) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version glob to (re)build; empty builds every version of docs/packages/executorch.yaml not released yet' | |
| required: false | |
| default: '' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/build-executorch.yml' | |
| - 'docs/packages/executorch.yaml' | |
| - 'patches/executorch/**' | |
| push: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/build-executorch.yml' | |
| - 'docs/packages/executorch.yaml' | |
| - 'patches/executorch/**' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 | |
| jobs: | |
| setup: | |
| uses: $/.github/workflows/_setup.yml | |
| with: | |
| package: executorch | |
| version: ${{ inputs.version }} | |
| build_wheels: | |
| needs: [setup] | |
| if: needs.setup.outputs.versions != '[]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| version: ${{ fromJSON(needs.setup.outputs.versions) }} | |
| # cp310/cp311 are dropped: torch and pytorch-tokenizers (both hard runtime | |
| # deps) have no riscv64 wheel for either on pypi.riseproject.dev. cp314t | |
| # is dropped too: upstream itself ships no free-threaded wheel. | |
| python: ["cp312", "cp313", "cp314"] | |
| name: Build executorch ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 | |
| runs-on: ubuntu-24.04-riscv | |
| timeout-minutes: 720 | |
| env: | |
| EXECUTORCH_VERSION: ${{ matrix.version }} | |
| steps: | |
| - name: Checkout executorch v${{ env.EXECUTORCH_VERSION }} | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: pytorch/executorch | |
| ref: v${{ env.EXECUTORCH_VERSION }} | |
| submodules: recursive | |
| persist-credentials: false | |
| - name: Checkout python-wheels | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| path: python-wheels | |
| persist-credentials: false | |
| - name: Apply patches | |
| run: git apply python-wheels/patches/executorch/${{ env.EXECUTORCH_VERSION }}/*.patch | |
| # Every library below is statically linked into the compiled extensions, and | |
| # setuptools' license-files (widened by the patch above) only reaches the | |
| # project root. | |
| - name: Stage the vendored licences | |
| run: | | |
| set -euo pipefail | |
| cp backends/xnnpack/third-party/FP16/LICENSE LICENSE.FP16 | |
| cp backends/xnnpack/third-party/FXdiv/LICENSE LICENSE.FXdiv | |
| cp backends/xnnpack/third-party/XNNPACK/LICENSE LICENSE.XNNPACK | |
| cp backends/xnnpack/third-party/cpuinfo/LICENSE LICENSE.cpuinfo | |
| cp backends/xnnpack/third-party/pthreadpool/LICENSE LICENSE.pthreadpool | |
| cp third-party/flatbuffers/LICENSE LICENSE.flatbuffers | |
| cp third-party/flatcc/LICENSE LICENSE.flatcc | |
| cp kernels/optimized/third-party/eigen/LICENSE LICENSE.eigen | |
| cp third-party/pocketfft/LICENSE.md LICENSE.pocketfft | |
| cp third-party/json/LICENSE.MIT LICENSE.json | |
| cp third-party/pybind11/LICENSE LICENSE.pybind11 | |
| # Upstream's own smoke test (.ci/scripts/wheel/test_linux.py) exercises the | |
| # examples/ tree, which needs torchvision for its MobileNetV3 model; | |
| # torchvision has no riscv64 wheel. This test keeps upstream's own | |
| # assertions (registered backend names) and replaces the model with a small | |
| # hand-built one, run through both the portable (non-delegated) path and a | |
| # real XNNPACK-delegated export, so the compiled extension, the portable | |
| # kernels and the XNNPACK backend are all genuinely exercised end to end. | |
| - name: Write the riscv64 smoke test | |
| run: | | |
| cat > riscv64_smoke_test.py <<'EOF' | |
| import platform | |
| import torch | |
| from torch.export import export | |
| from executorch.backends.xnnpack.partition.xnnpack_partitioner import ( | |
| XnnpackPartitioner, | |
| ) | |
| from executorch.exir import to_edge, to_edge_transform_and_lower | |
| from executorch.extension.pybindings.portable_lib import ( | |
| _get_registered_backend_names, | |
| _load_for_executorch_from_buffer, | |
| ) | |
| registered = _get_registered_backend_names() | |
| assert "XnnpackBackend" in registered, registered | |
| assert "OpenvinoBackend" in registered, registered | |
| if platform.machine() in ("x86_64", "amd64"): | |
| assert "QnnBackend" in registered, registered | |
| class Model(torch.nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.linear = torch.nn.Linear(4, 4) | |
| def forward(self, x): | |
| return self.linear(x).relu() | |
| model = Model().eval() | |
| example_inputs = (torch.randn(2, 4),) | |
| expected = model(*example_inputs) | |
| portable_prog = to_edge(export(model, example_inputs, strict=True)).to_executorch() | |
| portable_module = _load_for_executorch_from_buffer(portable_prog.buffer) | |
| portable_out = portable_module.forward(example_inputs)[0] | |
| assert torch.allclose(portable_out, expected, atol=1e-4), (portable_out, expected) | |
| print("portable (non-delegated) execution OK") | |
| xnnpack_prog = to_edge_transform_and_lower( | |
| export(model, example_inputs, strict=True), | |
| partitioner=[XnnpackPartitioner()], | |
| ).to_executorch() | |
| xnnpack_module = _load_for_executorch_from_buffer(xnnpack_prog.buffer) | |
| xnnpack_out = xnnpack_module.forward(example_inputs)[0] | |
| assert torch.allclose(xnnpack_out, expected, atol=1e-4), (xnnpack_out, expected) | |
| print("XNNPACK-delegated execution OK") | |
| EOF | |
| - uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 | |
| with: | |
| only: ${{ matrix.python }}-manylinux_riscv64 | |
| env: | |
| CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} | |
| # No riscv64 cmake wheel exists, so pip builds it from source as a | |
| # build dependency; its own CMake bootstrap needs OpenSSL's dev | |
| # headers to find libcrypto. | |
| CIBW_BEFORE_ALL_LINUX: dnf -y install openssl-devel | |
| # get_torch_base_path (tools/cmake/Utils.cmake) runs `import torch` in | |
| # the same interpreter as the build backend to add it to | |
| # CMAKE_PREFIX_PATH, but torch is only a runtime dependency | |
| # (setup.py), not a build-system.requires entry, so PEP 517 isolation | |
| # hides it from that interpreter. Preinstall build-system.requires | |
| # plus torch and turn isolation off (mirrors build-torchaudio.yml / | |
| # build-torchcodec.yml). | |
| CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation" | |
| # torch_pin.py pins TORCH_VERSION="2.13.0": this CMake build compiles | |
| # against the installed torch's real headers alongside executorch's own | |
| # vendored, 2.13.0-frozen copy of them (runtime/core/portable_type/c10), | |
| # so an unbounded floor (setup.py's own unpinned "torch>=2.13.0a0") lets | |
| # pip resolve a newer registry release whose headers conflict with the | |
| # vendored ones in the same translation unit (gotcha 615). | |
| CIBW_BEFORE_BUILD: >- | |
| pip install "cmake>=3.24,<4.0.0" "packaging>=24.2" pyyaml | |
| "setuptools>=77.0.3" wheel zstd certifi && | |
| pip install --only-binary=:all: "torch==2.13.0" | |
| CIBW_ENVIRONMENT: >- | |
| CMAKE_BUILD_PARALLEL_LEVEL=8 | |
| PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ | |
| # The torch wheel already carries these and loads them RTLD_GLOBAL (gotcha 17). | |
| CIBW_REPAIR_WHEEL_COMMAND: >- | |
| auditwheel repair -w {dest_dir} {wheel} | |
| --exclude libtorch.so | |
| --exclude libtorch_cpu.so | |
| --exclude libtorch_python.so | |
| --exclude libtorch_global_deps.so | |
| --exclude libc10.so | |
| --exclude libgomp.so.1 | |
| --exclude libgfortran.so.5 | |
| --exclude libopenblas.so.0 | |
| # Match the build-time pin (gotcha 615): the wheel's own metadata still | |
| # says "torch>=2.13.0a0", so without this the test install would resolve | |
| # a newer torch than the extension was actually compiled against. | |
| CIBW_TEST_REQUIRES: torch==2.13.0 | |
| CIBW_TEST_SOURCES: riscv64_smoke_test.py | |
| CIBW_TEST_COMMAND: python -X faulthandler riscv64_smoke_test.py | |
| - name: Check the extensions and the vendored licences made it into the wheel | |
| run: | | |
| python3 - wheelhouse/*.whl <<'EOF' | |
| import sys, zipfile | |
| expected = {"LICENSE", "LICENSE.FP16", "LICENSE.FXdiv", "LICENSE.XNNPACK", | |
| "LICENSE.cpuinfo", "LICENSE.pthreadpool", "LICENSE.flatbuffers", | |
| "LICENSE.flatcc", "LICENSE.eigen", "LICENSE.pocketfft", | |
| "LICENSE.json", "LICENSE.pybind11"} | |
| for whl in sys.argv[1:]: | |
| names = zipfile.ZipFile(whl).namelist() | |
| assert [n for n in names | |
| if n.startswith("executorch/extension/pybindings/_portable_lib.") | |
| and n.endswith(".so")], whl | |
| licenses = {n.split(".dist-info/licenses/", 1)[1] for n in names | |
| if ".dist-info/licenses/" in n and not n.endswith("/")} | |
| assert licenses == expected, f"{whl}: {sorted(licenses)}" | |
| print(whl, "ok") | |
| EOF | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: executorch-${{ env.EXECUTORCH_VERSION }}-${{ matrix.python }}-manylinux_riscv64 | |
| path: wheelhouse/*.whl | |
| if-no-files-found: error | |
| publish: | |
| name: Publish executorch ${{ matrix.version }} | |
| needs: [setup, build_wheels] | |
| if: needs.setup.outputs.versions != '[]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| version: ${{ fromJSON(needs.setup.outputs.versions) }} | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| uses: $/.github/workflows/_publish-wheel.yml | |
| secrets: | |
| app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} | |
| with: | |
| artifact-pattern: executorch-${{ matrix.version }}-*-manylinux_riscv64 |