name: numba_linux-64_wheel_builder

on:
  schedule:
    - cron: '30 7 * * 6'  # Saturday 7:30 AM UTC - linux-64 wheel
  pull_request:
    paths:
      - .github/workflows/numba_linux-64_wheel_builder.yml
      - .github/workflows/wheel_workflow_matrix.json
      - buildscripts/github/build_wheel_linux.sh
      - buildscripts/github/repair_wheel_linux.sh
      - setup.py
  workflow_dispatch:
    inputs:
      llvmlite_wheel_runid:
        description: 'llvmlite wheel workflow run ID (optional)'
        required: false
        type: string
      use_tbb:
        description: 'use TBB (Threading Building Blocks)'
        required: false
        type: boolean
        default: true

# 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
  CONDA_CHANNEL_NUMBA: numba/label/dev
  # Set USE_TBB to true by default, only override if explicitly set to false
  USE_TBB: ${{ github.event_name == 'workflow_dispatch' && inputs.use_tbb != false || true }}

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

  linux-64-build:
    name: linux-64-build-wheel (py ${{ matrix.python-version }}, np ${{ matrix.numpy_build }})
    needs: load-matrix
    runs-on: ubuntu-latest
    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: Download llvmlite wheel
        if: inputs.llvmlite_wheel_runid != ''
        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
        with:
          name: llvmlite-linux-64-py${{ matrix.python_canonical }}
          path: llvmlite_wheels
          run-id: ${{ inputs.llvmlite_wheel_runid }}
          repository: numba/llvmlite
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Build wheel in manylinux docker container
        run: |
          # Construct the expected Python executable path inside the container
          PYTHON_EXECUTABLE="/opt/python/${{ matrix.python_tag }}-${{ matrix.python_tag }}/bin/python"
          echo "Using Python executable: $PYTHON_EXECUTABLE"

          # Prepare build environment
          mkdir -p dist wheelhouse

          # Run the build script inside Docker container
          docker run --rm \
            -v ${{ github.workspace }}:/io \
            quay.io/pypa/manylinux2014_x86_64 \
            bash /io/buildscripts/github/build_wheel_linux.sh \
            "${PYTHON_EXECUTABLE}" \
            "${USE_TBB}" \
            "${{ matrix.numpy_build }}" \
            "/io/llvmlite_wheels" \
            "${WHEELS_INDEX_URL}"

          # List built wheels
          ls -la wheelhouse/

      - name: Repair and patch wheel in manylinux docker container
        run: |
          # Construct the expected Python executable path inside the container
          PYTHON_EXECUTABLE="/opt/python/${{ matrix.python_tag }}-${{ matrix.python_tag }}/bin/python"
          echo "Using Python executable: $PYTHON_EXECUTABLE"

          # Run the repair script in Docker
          docker run --rm \
            -v ${{ github.workspace }}:/io \
            quay.io/pypa/manylinux2014_x86_64 \
            bash /io/buildscripts/github/repair_wheel_linux.sh \
            "$PYTHON_EXECUTABLE" \
            "$USE_TBB" \
            "/io/wheelhouse"

          # List repaired wheels
          ls -la wheelhouse/

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


  linux-64-test:
    name: linux-64-test (py ${{ matrix.python-version }}, np ${{ matrix.numpy_test }})
    needs: [load-matrix, linux-64-build]
    runs-on: ubuntu-latest
    env:
      USE_TBB: ${{ github.event_name == 'workflow_dispatch' && inputs.use_tbb != false || true }}
    defaults:
      run:
        shell: bash -elx {0}
    strategy:
      matrix:
        include: ${{ fromJson(needs.load-matrix.outputs.test-matrix-json) }}
      fail-fast: false

    steps:
      - 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_linux-64_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-linux-64-py${{ matrix.python_canonical }}
          path: llvmlite_wheels
          run-id: ${{ inputs.llvmlite_wheel_runid }}
          repository: numba/llvmlite
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Validate and test wheel
        run: |
          PYTHON_PATH=$(which python${{ matrix.python_canonical }})
          $PYTHON_PATH -m pip install --upgrade pip twine
          ls -l dist
          if [ "${{ inputs.llvmlite_wheel_runid }}" != "" ]; then
              $PYTHON_PATH -m pip install --no-cache-dir llvmlite_wheels/*.whl
          else
              $PYTHON_PATH -m pip install --no-cache-dir --pre -i $WHEELS_INDEX_URL llvmlite
          fi
          $PYTHON_PATH -m twine check dist/*.whl

          $PYTHON_PATH -m pip install dist/numba*.whl numpy==${{ matrix.numpy_test }} setuptools

          if [ "$USE_TBB" = "true" ]; then
            $PYTHON_PATH -m pip install tbb
            # Verify TBB threading layer
            NUMBA_THREADING_LAYER=tbb $PYTHON_PATH -c "import numba; numba.get_num_threads(); print(numba.threading_layer())"
          fi

          # Disable NumPy dispatching to AVX512_SKX feature extensions if the chip is
          # reported to support the feature and NumPy >= 1.22 as this results in the use
          # of low accuracy SVML libm replacements in ufunc loops.
          _NPY_CMD='from numba.misc import numba_sysinfo;\
                    sysinfo=numba_sysinfo.get_sysinfo();\
                    print(sysinfo["NumPy AVX512_SKX detected"] and
                          sysinfo["NumPy Version"]>="1.22")'
          NUMPY_DETECTS_AVX512_SKX_NP_GT_122=$($PYTHON_PATH -c "$_NPY_CMD")
          echo "NumPy >= 1.22 with AVX512_SKX detected: $NUMPY_DETECTS_AVX512_SKX_NP_GT_122"

          if [[ "$NUMPY_DETECTS_AVX512_SKX_NP_GT_122" == "True" ]]; then
              export NPY_DISABLE_CPU_FEATURES="AVX512_SKX"
          fi

          # print Numba system information
          $PYTHON_PATH -m numba -s

          # run tests
          $PYTHON_PATH -m numba.runtests -m 4 -v
