@@ -167,9 +167,97 @@ jobs:
167167 path : dist/*.whl
168168 if-no-files-found : error
169169
170+ build-wheel-macos :
171+ name : Build macOS arm64 wheel (cp${{ matrix.python-version }})
172+ needs : [resolve]
173+ runs-on : macos-15
174+ timeout-minutes : 120
175+ permissions :
176+ contents : write
177+
178+ strategy :
179+ fail-fast : false
180+ matrix : ${{ fromJSON(needs.resolve.outputs.matrix) }}
181+
182+ steps :
183+ - name : Check out release source
184+ uses : actions/checkout@v7
185+ with :
186+ ref : ${{ needs.resolve.outputs.tag }}
187+
188+ - name : Set up Python
189+ uses : actions/setup-python@v7
190+ with :
191+ python-version : ${{ matrix.python-version }}
192+ cache : pip
193+
194+ # The wheel bundles Homebrew's libomp, so it can only claim the macOS
195+ # version that libomp was built for.
196+ - name : Install libomp and set deployment target
197+ run : |
198+ brew install libomp
199+ prefix="$(brew --prefix libomp)"
200+ minos="$(otool -l "$prefix/lib/libomp.dylib" | awk '/LC_BUILD_VERSION/{f=1} f&&/minos/{print $2; exit}')"
201+ echo "LIBOMP_PREFIX=$prefix" >> "$GITHUB_ENV"
202+ echo "MACOSX_DEPLOYMENT_TARGET=$minos" >> "$GITHUB_ENV"
203+ echo "libomp at $prefix, macOS deployment target $minos"
204+
205+ - name : Install build tooling
206+ run : |
207+ python -m pip install --upgrade pip
208+ python -m pip install build wheel delocate
209+
210+ - name : Build wheel
211+ run : python -m build --wheel --outdir ../dist
212+ working-directory : senpy
213+
214+ - name : Bundle native libraries into wheel
215+ run : |
216+ cd dist
217+ for whl in *.whl; do
218+ delocate-wheel --require-archs arm64 -w repaired -v "$whl"
219+ rm "$whl"
220+ done
221+ mv repaired/*.whl .
222+ rmdir repaired
223+ delocate-listdeps --all *.whl
224+ cd ..
225+
226+ - name : List wheel
227+ run : ls -lh dist
228+
229+ # Remove Homebrew's libomp so the smoke test proves the wheel is self-contained.
230+ - name : Smoke test wheel
231+ run : |
232+ brew uninstall --ignore-dependencies libomp
233+ python -m pip install dist/*.whl
234+ cp senpy/example.py "$RUNNER_TEMP/example.py"
235+ cd "$RUNNER_TEMP"
236+ python -c "import pathlib, senpy; assert pathlib.Path(senpy.__file__).resolve().is_relative_to(pathlib.Path('${{ github.workspace }}').resolve()) is False; print('senpy imported successfully')"
237+ python example.py
238+
239+ - name : Verify release exists
240+ env :
241+ GH_TOKEN : ${{ github.token }}
242+ TAG : ${{ needs.resolve.outputs.tag }}
243+ run : gh release view "$TAG"
244+
245+ - name : Upload wheel to release
246+ env :
247+ GH_TOKEN : ${{ github.token }}
248+ TAG : ${{ needs.resolve.outputs.tag }}
249+ run : gh release upload "$TAG" dist/*.whl --clobber
250+
251+ - name : Upload wheel artifact
252+ uses : actions/upload-artifact@v7
253+ with :
254+ name : pypi-wheel-macos-${{ matrix.python-version }}
255+ path : dist/*.whl
256+ if-no-files-found : error
257+
170258 publish-pypi :
171259 name : Publish to PyPI
172- needs : [resolve, build-wheel]
260+ needs : [resolve, build-wheel, build-wheel-macos ]
173261 # On release events always publish; on manual runs only when requested.
174262 if : github.event_name == 'release' || inputs.publish-to-pypi
175263 runs-on : ubuntu-latest
0 commit comments