Repository navigation
250 lines (224 loc) · 10.7 KB
/
Copy pathbuild-executorch.yml
File metadata and controls
250 lines (224 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# 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"
# Without BUILD_VERSION, setup.py appends +<gitsha> to version.txt
# (mirrors build-torchaudio.yml / build-torchcodec.yml).
CIBW_ENVIRONMENT: >-
BUILD_VERSION=${{ env.EXECUTORCH_VERSION }}
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