Repository navigation
Build Wheel (macOS) #53
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
| # Copyright (c) 2026 Huawei Technologies Co., Ltd. | |
| # This program is free software, you can redistribute it and/or modify it under the terms and conditions of | |
| # CANN Open Software License Agreement Version 2.0 (the "License"). | |
| # Please refer to the License for details. You may not use this file except in compliance with the License. | |
| # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | |
| # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | |
| # See LICENSE in the root of the software repository for the full text of the License. | |
| name: Build Wheel (macOS) | |
| on: | |
| push: | |
| tags-ignore: | |
| - '**' | |
| # Nightly build & publish (UTC time, offset from Linux workflow). | |
| schedule: | |
| - cron: "40 17 * * *" | |
| workflow_dispatch: | |
| release: | |
| types: [prereleased, released] | |
| permissions: | |
| contents: write | |
| env: | |
| LLVM_REPOSITORY: https://github.com/LinxISA/llvm-project.git | |
| LLVM_COMMIT: b7c83f68bf84125e696a70bec4b665c70a3b584d | |
| jobs: | |
| build_wheel: | |
| name: Build wheel (Python ${{ matrix.python }}, ${{ matrix.arch }}) | |
| runs-on: ${{ matrix.arch == 'x86_64' && 'macos-15-intel' || 'macos-26' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ["3.10", "3.11", "3.12"] | |
| arch: ["x86_64", "aarch64"] | |
| # Keep macOS matrix jobs at 5 to stay within GitHub Actions macOS job limits. | |
| exclude: | |
| - python: "3.10" | |
| arch: "x86_64" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Select runner metadata | |
| id: runner-meta | |
| run: | | |
| if [ "${{ matrix.arch }}" = "x86_64" ]; then | |
| echo "label=macos-15-intel" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "label=macos-26" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: Set Python path | |
| run: | | |
| echo "PY_PATH=$(python -c 'import sys; print(sys.prefix)')" >> $GITHUB_ENV | |
| - name: Compute PTOAS CLI version | |
| run: | | |
| if [ "${GITHUB_EVENT_NAME}" = "release" ]; then | |
| EXPECTED_RELEASE_VERSION="$(python .github/scripts/compute_ptoas_version.py --mode release)" | |
| PTOAS_VERSION="${GITHUB_REF_NAME#v}" | |
| if [ "${PTOAS_VERSION}" != "${EXPECTED_RELEASE_VERSION}" ]; then | |
| echo "release tag '${GITHUB_REF_NAME}' does not match computed version '${EXPECTED_RELEASE_VERSION}'" >&2 | |
| exit 1 | |
| fi | |
| else | |
| PTOAS_VERSION="$(python .github/scripts/compute_ptoas_version.py --mode dev)" | |
| fi | |
| echo "PTOAS_VERSION=${PTOAS_VERSION}" >> "$GITHUB_ENV" | |
| echo "Using PTOAS CLI version: ${PTOAS_VERSION}" | |
| - name: Install system dependencies | |
| run: | | |
| brew install ninja ccache | |
| - name: Install Python dependencies | |
| run: | | |
| pip install --no-cache-dir numpy 'nanobind>=2.9,<3' typing_extensions setuptools wheel delocate | |
| - name: Set build directories | |
| run: | | |
| echo "WORKSPACE_DIR=${RUNNER_TEMP}/llvm-workspace" >> $GITHUB_ENV | |
| echo "LLVM_SOURCE_DIR=${RUNNER_TEMP}/llvm-workspace/llvm-project" >> $GITHUB_ENV | |
| echo "LLVM_BUILD_DIR=${RUNNER_TEMP}/llvm-workspace/llvm-project/build-shared" >> $GITHUB_ENV | |
| echo "PTO_SOURCE_DIR=$GITHUB_WORKSPACE" >> $GITHUB_ENV | |
| echo "PTO_INSTALL_DIR=$GITHUB_WORKSPACE/install" >> $GITHUB_ENV | |
| - name: Restore LLVM build cache (exact key) | |
| id: cache-llvm | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: ${{ runner.temp }}/llvm-workspace/llvm-project/build-shared | |
| key: llvm-${{ env.LLVM_COMMIT }}-${{ steps.runner-meta.outputs.label }}-${{ matrix.arch }}-${{ matrix.python }}-pto-off-v2 | |
| - name: Prepare LLVM source | |
| run: | | |
| mkdir -p $LLVM_SOURCE_DIR | |
| cd $LLVM_SOURCE_DIR | |
| if [ ! -d .git ]; then | |
| git init | |
| git remote add origin ${{ env.LLVM_REPOSITORY }} | |
| fi | |
| git remote set-url origin ${{ env.LLVM_REPOSITORY }} | |
| git fetch --depth 1 origin ${{ env.LLVM_COMMIT }} | |
| git checkout --detach ${{ env.LLVM_COMMIT }} | |
| test "$(git rev-parse HEAD)" = "${{ env.LLVM_COMMIT }}" | |
| - name: Warn when default-branch cache is missing for PR/release runs | |
| if: steps.cache-llvm.outputs.cache-hit != 'true' && (github.event_name == 'pull_request' || github.event_name == 'release') | |
| run: | | |
| echo "LLVM cache miss while running on PR/release." | |
| echo "PR/release runs are cache-consumers only and should reuse cache generated on the default branch." | |
| echo "Please run this workflow on the default branch first to populate cache key:" | |
| echo "llvm-${{ env.LLVM_COMMIT }}-${{ steps.runner-meta.outputs.label }}-${{ matrix.arch }}-${{ matrix.python }}-pto-off-v2" | |
| - name: Build LLVM/MLIR | |
| if: steps.cache-llvm.outputs.cache-hit != 'true' | |
| run: | | |
| cd $LLVM_SOURCE_DIR | |
| cmake -G Ninja -S llvm -B $LLVM_BUILD_DIR \ | |
| -DLLVM_ENABLE_PROJECTS="mlir;clang" \ | |
| -DMLIR_ENABLE_LINXISA_PTO=OFF \ | |
| -DLLVM_INCLUDE_TESTS=OFF \ | |
| -DLLVM_INCLUDE_EXAMPLES=OFF \ | |
| -DLLVM_INCLUDE_BENCHMARKS=OFF \ | |
| -DBUILD_SHARED_LIBS=ON \ | |
| -DMLIR_ENABLE_BINDINGS_PYTHON=ON \ | |
| -DPython3_EXECUTABLE=$(which python) \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DLLVM_TARGETS_TO_BUILD="host" | |
| ninja -C $LLVM_BUILD_DIR | |
| # Upload cache immediately after the build step, to avoid `build-shared` directory being polluted by later steps. | |
| - name: Save LLVM cache | |
| # Allow cache writes except for PR/release runs to avoid duplicated tag/merge caches. | |
| if: steps.cache-llvm.outputs.cache-hit != 'true' && github.event_name != 'pull_request' && github.event_name != 'release' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: ${{ runner.temp }}/llvm-workspace/llvm-project/build-shared | |
| key: llvm-${{ env.LLVM_COMMIT }}-${{ steps.runner-meta.outputs.label }}-${{ matrix.arch }}-${{ matrix.python }}-pto-off-v2 | |
| - name: Build PTOAS | |
| run: | | |
| cd $PTO_SOURCE_DIR | |
| cmake -G Ninja \ | |
| -S . \ | |
| -B build \ | |
| -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ | |
| -DMLIR_DIR=$LLVM_BUILD_DIR/lib/cmake/mlir \ | |
| -DPython3_ROOT_DIR=${PY_PATH} \ | |
| -DPython3_EXECUTABLE=$(which python) \ | |
| -DPython_EXECUTABLE=$(which python) \ | |
| -DPython3_FIND_STRATEGY=LOCATION \ | |
| -Dnanobind_DIR=$(python -m nanobind --cmake_dir) \ | |
| -DMLIR_PYTHON_PACKAGE_DIR=${LLVM_BUILD_DIR}/tools/mlir/python_packages/mlir_core \ | |
| -DPTOAS_RELEASE_VERSION_OVERRIDE=${PTOAS_VERSION} \ | |
| -DPTOAS_ENABLE_WERROR=OFF \ | |
| -DCMAKE_INSTALL_PREFIX=${PTO_INSTALL_DIR} \ | |
| -DCMAKE_BUILD_TYPE=Release | |
| ninja -C build | |
| ninja -C build install | |
| - name: Create Python wheel | |
| run: | | |
| export PY_PACKAGE_DIR=$LLVM_BUILD_DIR/tools/mlir/python_packages/mlir_core | |
| export PTOAS_PYTHON_PACKAGE_VERSION="${PTOAS_VERSION}" | |
| if [ "${{ matrix.arch }}" = "x86_64" ]; then | |
| TARGET_ARCH="x86_64" | |
| else | |
| TARGET_ARCH="arm64" | |
| fi | |
| # setup-python may provide a universal2 interpreter on macOS. | |
| # Force a single-arch wheel tag to match the matrix target and delocate checks. | |
| export WHEEL_PLAT_NAME=$(python "$PTO_SOURCE_DIR/docker/get_macos_wheel_plat_name.py" "$TARGET_ARCH") | |
| bash $PTO_SOURCE_DIR/docker/create_wheel.sh | |
| shopt -s nullglob | |
| built_wheels=("$PY_PACKAGE_DIR"/dist/ptoas*.whl) | |
| if [ "${#built_wheels[@]}" -gt 0 ]; then | |
| printf 'Built wheel file: %s\n' "$(basename "${built_wheels[0]}")" | |
| fi | |
| - name: Repair wheel with delocate | |
| run: | | |
| set -euo pipefail | |
| export PY_PACKAGE_DIR=$LLVM_BUILD_DIR/tools/mlir/python_packages/mlir_core | |
| cd $PY_PACKAGE_DIR | |
| if [ "${{ matrix.arch }}" = "x86_64" ]; then | |
| REQUIRED_ARCH="x86_64" | |
| else | |
| REQUIRED_ARCH="arm64" | |
| fi | |
| shopt -s nullglob | |
| wheels=(dist/ptoas*.whl) | |
| if [ "${#wheels[@]}" -ne 1 ]; then | |
| echo "Expected exactly one wheel in dist/, found ${#wheels[@]}:" | |
| printf ' %s\n' "${wheels[@]}" | |
| exit 1 | |
| fi | |
| echo "Runner label: ${{ steps.runner-meta.outputs.label }}" | |
| echo "Wheel size:" | |
| du -sh "${wheels[0]}" | |
| echo "dist contents:" | |
| ls -lh dist | |
| echo "delocate dependency summary:" | |
| delocate-listdeps "${wheels[0]}" || true | |
| delocate-wheel \ | |
| --require-archs "${REQUIRED_ARCH}" \ | |
| --wheel-dir wheelhouse \ | |
| "${wheels[0]}" & | |
| delocate_pid=$! | |
| ( | |
| sleep 4500 | |
| if kill -0 "$delocate_pid" 2>/dev/null; then | |
| echo "ERROR: delocate-wheel exceeded 75 minutes, terminating pid ${delocate_pid}" | |
| kill -TERM "$delocate_pid" 2>/dev/null || true | |
| sleep 15 | |
| kill -KILL "$delocate_pid" 2>/dev/null || true | |
| fi | |
| ) & | |
| watchdog_pid=$! | |
| while kill -0 "$delocate_pid" 2>/dev/null; do | |
| echo "[$(date '+%Y-%m-%d %H:%M:%S')] delocate still running (pid=${delocate_pid})" | |
| ps -o pid=,ppid=,%cpu=,%mem=,etime=,command= -p "$delocate_pid" || true | |
| du -sh dist wheelhouse 2>/dev/null || true | |
| sleep 60 | |
| done | |
| set +e | |
| wait "$delocate_pid" | |
| rc=$? | |
| set -e | |
| kill "$watchdog_pid" 2>/dev/null || true | |
| wait "$watchdog_pid" 2>/dev/null || true | |
| if [ "$rc" -ne 0 ]; then | |
| echo "delocate-wheel failed with rc=${rc}" | |
| echo "Active Python processes:" | |
| ps -ax -o pid=,ppid=,%cpu=,%mem=,etime=,command= | grep -E '[d]elocate|[p]ython' || true | |
| echo "Filesystem usage:" | |
| df -h . | |
| echo "dist contents after failure:" | |
| ls -lh dist || true | |
| echo "wheelhouse contents after failure:" | |
| ls -lh wheelhouse || true | |
| exit "$rc" | |
| fi | |
| echo "wheelhouse contents:" | |
| ls -lh wheelhouse | |
| - name: Test wheel installation | |
| run: | | |
| export PY_PACKAGE_DIR=$LLVM_BUILD_DIR/tools/mlir/python_packages/mlir_core | |
| pip install $PY_PACKAGE_DIR/wheelhouse/ptoas*.whl | |
| export DYLD_LIBRARY_PATH=$LLVM_BUILD_DIR/lib:$PTO_INSTALL_DIR/lib:${DYLD_LIBRARY_PATH} | |
| bash $PTO_SOURCE_DIR/docker/test_wheel_imports.sh | |
| - name: Test ptoas CLI | |
| run: | | |
| export DYLD_LIBRARY_PATH=$LLVM_BUILD_DIR/lib:$PTO_INSTALL_DIR/lib:${DYLD_LIBRARY_PATH} | |
| bash $PTO_SOURCE_DIR/docker/test_ptoas_cli.sh | |
| - name: Copy wheel to workspace | |
| run: | | |
| export PY_PACKAGE_DIR=$LLVM_BUILD_DIR/tools/mlir/python_packages/mlir_core | |
| mkdir -p $GITHUB_WORKSPACE/wheelhouse | |
| cp $PY_PACKAGE_DIR/wheelhouse/ptoas*.whl $GITHUB_WORKSPACE/wheelhouse/ | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ptoas-wheel-macos-py${{ matrix.python }}-${{ matrix.arch }} | |
| path: wheelhouse/*.whl | |
| - name: Collect ptoas binary and dependencies | |
| if: matrix.python == '3.11' | |
| run: | | |
| bash "$PTO_SOURCE_DIR/docker/collect_ptoas_dist_mac.sh" "$GITHUB_WORKSPACE/ptoas-dist" | |
| - name: Archive ptoas binary artifact | |
| if: matrix.python == '3.11' | |
| run: | | |
| chmod +x "$GITHUB_WORKSPACE/ptoas-dist/ptoas" "$GITHUB_WORKSPACE/ptoas-dist/bin/ptoas" | |
| tar -czf "$GITHUB_WORKSPACE/ptoas-bin-macos-${{ matrix.arch }}.tar.gz" \ | |
| -C "$GITHUB_WORKSPACE/ptoas-dist" . | |
| - name: Smoke test archived ptoas binary artifact | |
| if: matrix.python == '3.11' | |
| run: | | |
| TEST_DIR="$RUNNER_TEMP/ptoas-dist-smoke-${{ matrix.arch }}" | |
| rm -rf "$TEST_DIR" | |
| mkdir -p "$TEST_DIR/extracted" | |
| tar -xzf "$GITHUB_WORKSPACE/ptoas-bin-macos-${{ matrix.arch }}.tar.gz" -C "$TEST_DIR/extracted" | |
| test -x "$TEST_DIR/extracted/ptoas" | |
| test -x "$TEST_DIR/extracted/bin/ptoas" | |
| env -u PYTHONPATH -u DYLD_LIBRARY_PATH -u LD_LIBRARY_PATH \ | |
| "$TEST_DIR/extracted/ptoas" --version | |
| env -u PYTHONPATH -u DYLD_LIBRARY_PATH -u LD_LIBRARY_PATH \ | |
| "$TEST_DIR/extracted/ptoas" \ | |
| "$PTO_SOURCE_DIR/test/lit/pto/kernel_kind_vector_scf_while_emitc.pto" \ | |
| >/dev/null | |
| - name: Smoke test wheel imports after collecting artifacts | |
| if: matrix.python == '3.11' | |
| run: | | |
| # Test the copied wheel artifact from an isolated env with no build-tree | |
| # paths, so post-collect/release regressions (e.g. ARM cs_invalid_page) | |
| # are caught before upload. | |
| brew install uv | |
| uv python install 3.11 | |
| TEST_DIR="$RUNNER_TEMP/wheel-smoke-test" | |
| VENV_DIR="$TEST_DIR/.venv" | |
| mkdir -p "$TEST_DIR" | |
| cd "$TEST_DIR" | |
| uv venv --python 3.11 "$VENV_DIR" | |
| source "$VENV_DIR/bin/activate" | |
| unset PYTHONPATH | |
| unset DYLD_LIBRARY_PATH | |
| unset LD_LIBRARY_PATH | |
| shopt -s nullglob | |
| wheels=("$GITHUB_WORKSPACE"/wheelhouse/ptoas*.whl) | |
| if [ "${#wheels[@]}" -ne 1 ]; then | |
| echo "Expected exactly one wheel in \$GITHUB_WORKSPACE/wheelhouse, found ${#wheels[@]}:" | |
| printf ' %s\n' "${wheels[@]}" | |
| exit 1 | |
| fi | |
| uv pip install "${wheels[0]}" | |
| if [ "${{ matrix.arch }}" = "aarch64" ]; then | |
| DYLD_PRINT_LIBRARIES=1 python -c "import mlir.ir; print('mlir.ir imported successfully')" | |
| DYLD_PRINT_LIBRARIES=1 python -c "from mlir.dialects import pto; print('pto dialect imported successfully')" | |
| else | |
| python -c "import mlir.ir; print('mlir.ir imported successfully')" | |
| python -c "from mlir.dialects import pto; print('pto dialect imported successfully')" | |
| fi | |
| - name: Upload ptoas binary artifact | |
| if: matrix.python == '3.11' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ptoas-bin-macos-${{ matrix.arch }} | |
| path: ptoas-bin-macos-${{ matrix.arch }}.tar.gz | |
| upload_release_assets: | |
| name: Upload release assets | |
| if: github.event_name == 'release' || github.event_name == 'schedule' | |
| needs: build_wheel | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Compute release metadata | |
| run: | | |
| if [ "${GITHUB_EVENT_NAME}" = "release" ]; then | |
| echo "RELEASE_TAG=${GITHUB_REF_NAME}" >> "$GITHUB_ENV" | |
| echo "RELEASE_NAME=${GITHUB_REF_NAME}" >> "$GITHUB_ENV" | |
| echo "RELEASE_PRERELEASE=false" >> "$GITHUB_ENV" | |
| echo "RELEASE_MAKE_LATEST=true" >> "$GITHUB_ENV" | |
| { | |
| echo "RELEASE_BODY<<EOF" | |
| echo "Published release assets for ${GITHUB_REF_NAME}." | |
| echo "EOF" | |
| } >> "$GITHUB_ENV" | |
| else | |
| echo "RELEASE_TAG=nightly" >> "$GITHUB_ENV" | |
| echo "RELEASE_NAME=Nightly Build" >> "$GITHUB_ENV" | |
| echo "RELEASE_PRERELEASE=true" >> "$GITHUB_ENV" | |
| echo "RELEASE_MAKE_LATEST=false" >> "$GITHUB_ENV" | |
| { | |
| echo "RELEASE_BODY<<EOF" | |
| echo "Automated nightly build." | |
| echo "" | |
| echo "- Commit: ${GITHUB_SHA}" | |
| echo "- Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| echo "- Date (UTC): $(date -u +'%Y-%m-%d %H:%M:%S')" | |
| echo "EOF" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| - name: Download all wheel artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: ptoas-wheel-macos-* | |
| path: release-artifacts | |
| merge-multiple: true | |
| - name: Download x86_64 binary artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ptoas-bin-macos-x86_64 | |
| path: release-artifacts/ptoas-bin-macos-x86_64 | |
| - name: Download aarch64 binary artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ptoas-bin-macos-aarch64 | |
| path: release-artifacts/ptoas-bin-macos-aarch64 | |
| - name: Collect packaged ptoas binaries | |
| run: | | |
| set -euo pipefail | |
| mv "release-artifacts/ptoas-bin-macos-x86_64/ptoas-bin-macos-x86_64.tar.gz" \ | |
| "release-artifacts/ptoas-bin-macos-x86_64.tar.gz" | |
| mv "release-artifacts/ptoas-bin-macos-aarch64/ptoas-bin-macos-aarch64.tar.gz" \ | |
| "release-artifacts/ptoas-bin-macos-aarch64.tar.gz" | |
| - name: Upload assets to GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ env.RELEASE_TAG }} | |
| name: ${{ env.RELEASE_NAME }} | |
| target_commitish: ${{ github.sha }} | |
| prerelease: ${{ env.RELEASE_PRERELEASE }} | |
| make_latest: ${{ env.RELEASE_MAKE_LATEST }} | |
| body: ${{ env.RELEASE_BODY }} | |
| overwrite_files: true | |
| files: | | |
| release-artifacts/*.whl | |
| release-artifacts/*.tar.gz |