name: numba_osx-arm64_wheel_builder

on:
  schedule:
    - cron: '5 7 * * 6'  # Saturday 7:05 AM UTC - osx-arm64 wheel (staggered to manage osx runner limit)
  pull_request:
    paths:
      - .github/workflows/numba_osx-arm64_wheel_builder.yml
      - .github/workflows/wheel_workflow_matrix.json
      - setup.py
  workflow_dispatch:
    inputs:
      llvmlite_wheel_runid:
        description: 'llvmlite wheel workflow run ID (optional)'
        required: false
        type: string

# Add concurrency control
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

env:
  ARTIFACT_RETENTION_DAYS: 7
  WHEELS_INDEX_URL: https://pypi.anaconda.org/numba/label/dev/simple

jobs:
  load-matrix:
    runs-on: ubuntu-latest
    outputs:
      build-matrix-json: ${{ steps.load_matrix_json.outputs.build-matrix-json }}
      test-matrix-json: ${{ steps.load_matrix_json.outputs.test-matrix-json }}
    steps:
      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
      - id: load_matrix_json
        name: Load Workflow Matrix JSON
        run: |
          BUILD_MATRIX_JSON=$(jq -c .wheel_build_matrix .github/workflows/wheel_workflow_matrix.json)
          TEST_MATRIX_JSON=$(jq -c .wheel_test_matrix .github/workflows/wheel_workflow_matrix.json)

          # Add canonical python versions to both matrices
          BUILD_MATRIX_EXTENDED=$(echo "$BUILD_MATRIX_JSON" | jq -c '
            map(. + {"python_canonical": (.["python-version"] | split(".") | .[:2] | join("."))})
          ')

          TEST_MATRIX_EXTENDED=$(echo "$TEST_MATRIX_JSON" | jq -c '
            map(. + {"python_canonical": (.["python-version"] | split(".") | .[:2] | join("."))})
          ')

          echo "Build Matrix JSON: $BUILD_MATRIX_EXTENDED"
          echo "Test Matrix JSON: $TEST_MATRIX_EXTENDED"
          echo "build-matrix-json=$BUILD_MATRIX_EXTENDED" >> $GITHUB_OUTPUT
          echo "test-matrix-json=$TEST_MATRIX_EXTENDED" >> $GITHUB_OUTPUT

  osx-arm64-build:
    name: osx-arm64-build-wheel (py ${{ matrix.python-version }}, np ${{ matrix.numpy_build }})
    needs: load-matrix
    runs-on: macos-14
    defaults:
      run:
        shell: bash -elx {0}
    strategy:
      matrix:
        include: ${{ fromJson(needs.load-matrix.outputs.build-matrix-json) }}
      fail-fast: false

    steps:
      - name: Clone repository
        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
        with:
          fetch-depth: 0

      - name: Setup Miniconda
        uses: conda-incubator/setup-miniconda@835234971496cad1653abb28a638a281cf32541f # v3.2.0
        with:
          python-version: ${{ matrix.python-version }}
          auto-update-conda: true
          auto-activate-base: true

      - name: Download llvmlite wheel
        if: inputs.llvmlite_wheel_runid != ''
        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
        with:
          name: llvmlite-osx-arm64-py${{ matrix.python_canonical }}
          path: llvmlite_wheels
          run-id: ${{ inputs.llvmlite_wheel_runid }}
          repository: numba/llvmlite
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Install build dependencies
        run: |
          if [ "${{ inputs.llvmlite_wheel_runid }}" != "" ]; then
              arch -arm64 python -m pip install llvmlite_wheels/*.whl
          else
              arch -arm64 python -m pip install -i ${{ env.WHEELS_INDEX_URL }} llvmlite
          fi
          python -m pip install build numpy==${{ matrix.numpy_build }} setuptools wheel

      - name: Setup llvm-openmp for build
        run: |
          # Download and extract llvm-openmp conda package
          curl -L "https://repo.anaconda.com/pkgs/main/osx-arm64/llvm-openmp-19.1.7-h3b2fb71_2.tar.bz2" -o llvm-openmp.tar.bz2
          mkdir -p omp && tar -xjf llvm-openmp.tar.bz2 -C omp

          # Create symlinks for setup.py OpenMP detection
          mkdir -p omp/lib/clang/19/include
          ln -s ${{ github.workspace }}/omp/include/omp.h omp/lib/clang/19/include/omp.h

          # Export environment vars for build to detect OpenMP
          echo "PREFIX=$PWD/omp" >> $GITHUB_ENV
          echo "CPPFLAGS=-I$PWD/omp/include" >> $GITHUB_ENV
          echo "LDFLAGS=-L$PWD/omp/lib" >> $GITHUB_ENV

      - name: Build sdist [once - py3.10]
        if: matrix.python-version == '3.10'
        run: arch -arm64 python -m build --sdist

      - name: Build wheel
        env:
          MACOSX_DEPLOYMENT_TARGET: "11.0"
        run: arch -arm64 python -m build --wheel --no-isolation

      - name: Fix macOS wheel library paths
        run: |
          arch -arm64 python -m pip install wheel
          cd dist
          arch -arm64 python ../buildscripts/github/patch_wheel_dylib.py ./*.whl
          ls -la ./*.whl
          # inspect the wheel
          find . -name "*.so" -o -name "*.dylib" | xargs -I {} otool -L {}

      - name: Upload numba wheel
        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
        with:
          name: numba_osx-arm64_wheel_py${{ matrix.python_canonical }}
          path: dist/*.whl
          compression-level: 0
          retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
          if-no-files-found: error

      - name: Upload numba sdist
        if: matrix.python-version == '3.10'
        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
        with:
            name: numba-sdist
            path: dist/*.tar.gz
            compression-level: 0
            retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
            if-no-files-found: error

  osx-arm64-test:
    name: osx-arm64-test (py ${{ matrix.python-version }}, np ${{ matrix.numpy_test }})
    needs: [load-matrix, osx-arm64-build]
    runs-on: macos-14
    defaults:
      run:
        shell: bash -elx {0}
    strategy:
      matrix:
        include: ${{ fromJson(needs.load-matrix.outputs.test-matrix-json) }}
      fail-fast: false

    steps:
      - name: Set Python versions
        run: |
          FULL_VERSION="${{ matrix.python-version }}"
          CANONICAL_VERSION=$(echo "$FULL_VERSION" | cut -d. -f1,2)

          echo "PYTHON_INSTALL_VERSION=$FULL_VERSION" >> $GITHUB_ENV
          echo "PYTHON_CANONICAL_VERSION=$CANONICAL_VERSION" >> $GITHUB_ENV

          echo "Using install version: $FULL_VERSION"
          echo "Using canonical version: $CANONICAL_VERSION"

      - name: Setup Python
        uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true

      - name: Download Numba wheel artifact
        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
        with:
          name: numba_osx-arm64_wheel_py${{ matrix.python_canonical }}
          path: dist

      - name: Download llvmlite wheel
        if: inputs.llvmlite_wheel_runid != ''
        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
        with:
          name: llvmlite-osx-arm64-py${{ matrix.python_canonical }}
          path: llvmlite_wheels
          run-id: ${{ inputs.llvmlite_wheel_runid }}
          repository: numba/llvmlite
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Install libomp and set DYLD_FALLBACK_LIBRARY_PATH
        run: |
          brew install libomp
          echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix libomp)/lib" >> $GITHUB_ENV
          echo "Set DYLD_FALLBACK_LIBRARY_PATH to: $(brew --prefix libomp)/lib"

      - name: Validate and test wheel
        run: |
          PYTHON_PATH=$(which python${{ matrix.python_canonical }})
          "$PYTHON_PATH" -m venv .venv && source .venv/bin/activate
          arch -arm64 python -m pip install --upgrade pip twine
          ls -l dist
          if [ "${{ inputs.llvmlite_wheel_runid }}" != "" ]; then
              arch -arm64 python -m pip install llvmlite_wheels/*.whl
          else
              arch -arm64 python -m pip install --pre -i ${{ env.WHEELS_INDEX_URL }} llvmlite
          fi
          arch -arm64 python -m twine check dist/*.whl
          arch -arm64 python -m pip install dist/numba*.whl numpy==${{ matrix.numpy_test }}

          # print Numba system information
          arch -arm64 python -m numba -s

          # run tests
          arch -arm64 python -m numba.runtests -m 4 -v
