# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the OpenImageIO Project.
#
# This workflow will run on under two independent conditions:
#   1) On the official AcademySoftwareFoundation repo, according to a nightly schedule.
#   2) On forks, when submitting a PR that modifies Python-related files.

name: Wheel

permissions:
  contents: read
  id-token: write

on:
  push:
    # Workflow run on tags for v3 only, or pre-tagged pushes of release, or
    # dev-3.* branches. Also if the branch name itself contains the substrings
    # 'wheel' or 'python'.
    tags:
      - v3.*
    branches:
      - release
      - dev-3.*
      - "*wheel*"
      - "*python*"
  pull_request:
    # Workflow run on pull_request only when related files change, or when the
    # branch name itself contains the substrings 'wheel' or 'python'.
    paths:
      - .github/workflows/wheel.yml
      - pyproject.toml
      - src/python/*.py
      - src/python/*.h
      - src/python/*.cpp
      - src/cmake/*.cmake
  schedule:
    # Nightly build
    - cron:  "0 8 * * *"
  workflow_dispatch:
    # This allows manual triggering of the workflow from the web

# Allow subsequent pushes to the same PR or REF to cancel any previous jobs.
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true


jobs:
  # Linux jobs run in Docker containers (manylinux), so the latest OS version
  # is OK. macOS and Windows jobs need to be locked to specific virtual
  # environment versions to mitigate issues from OS updates, and will require
  # maintenance as OS versions are retired.

  # ---------------------------------------------------------------------------
  # Source Distribution
  # ---------------------------------------------------------------------------

  sdist:
    name: Build SDist
    runs-on: ubuntu-latest
    if: |
      github.event_name != 'schedule' ||
      github.repository == 'AcademySoftwareFoundation/OpenImageIO'

    steps:

    - name: Checkout repo
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

    - name: Build SDist
      run: pipx run build --sdist

    - name: Check metadata
      run: pipx run twine check dist/*

    - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
      with:
        name: cibw-sdist
        path: dist/*.tar.gz

  # ---------------------------------------------------------------------------
  # Linux Wheels
  # ---------------------------------------------------------------------------

  linux:
    name: Build wheels on Linux
    runs-on: ubuntu-latest
    if: |
      github.event_name != 'schedule' ||
      github.repository == 'AcademySoftwareFoundation/OpenImageIO'
    strategy:
      matrix:
        include:
          # -------------------------------------------------------------------
          # CPython 64 bits manylinux_2_28
          # -------------------------------------------------------------------
          - build: CPython 3.9 64 bits manylinux_2_28
            manylinux: manylinux_2_28
            python: cp39-manylinux_x86_64
            arch: x86_64
          - build: CPython 3.10 64 bits manylinux_2_28
            manylinux: manylinux_2_28
            python: cp310-manylinux_x86_64
            arch: x86_64
          - build: CPython 3.11 64 bits manylinux_2_28
            manylinux: manylinux_2_28
            python: cp311-manylinux_x86_64
            arch: x86_64
          - build: CPython 3.12 64 bits manylinux_2_28
            manylinux: manylinux_2_28
            python: cp312-manylinux_x86_64
            arch: x86_64
          - build: CPython 3.13 64 bits manylinux_2_28
            manylinux: manylinux_2_28
            python: cp313-manylinux_x86_64
            arch: x86_64
          - build: CPython 3.14 64 bits manylinux_2_28
            manylinux: manylinux_2_28
            python: cp314-manylinux_x86_64
            arch: x86_64
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Install Python
        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
        with:
          python-version: '3.9'

      - name: ccache-restore
        id: ccache-restore
        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.manylinux}}-${{matrix.python}}
          restore-keys: wheel-${{runner.os}}-${{matrix.manylinux}}-${{matrix.python}}

      - name: Build wheels
        # Note: the version of cibuildwheel should be kept in sync with src/python/stubs/CMakeLists.txt
        uses: pypa/cibuildwheel@9c00cb4f6b517705a3794b22395aedc36257242c # v3.2.1
        env:
          # pass GITHUB_ACTIONS through to the build container so that custom
          # processes can tell they are running in CI.
          CIBW_ENVIRONMENT_PASS_LINUX: GITHUB_ACTIONS
          CIBW_BEFORE_ALL: "source src/build-scripts/build_ccache.bash && pwd && ext/dist/bin/ccache --max-size=200M && ext/dist/bin/ccache -sv && export CMAKE_C_COMPILER_LAUNCHER=/project/ext/dist/bin/ccache CMAKE_CXX_COMPILER_LAUNCHER=/project/ext/dist/bin/ccache"
          CIBW_BEFORE_TEST: "ext/dist/bin/ccache -s"
          CIBW_BUILD: ${{ matrix.python }}
          CIBW_ARCHS: ${{ matrix.arch }}
          CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
          CIBW_ENVIRONMENT: >
              CCACHE_DIR=/host//home/runner/.ccache
              CCACHE_COMPRESSION=yes
              CCACHE_PREBUILT=1
              CMAKE_BUILD_PARALLEL_LEVEL=4
              CTEST_PARALLEL_LEVEL=4
              SKBUILD_CMAKE_ARGS="-DLINKSTATIC=1"
              SKBUILD_CMAKE_BUILD_TYPE="MinSizeRel"
              SKBUILD_BUILD_DIR=/project/build
              CXXFLAGS="-Wno-error=stringop-overflow -Wno-pragmas"
              WebP_BUILD_VERSION="1.5.0"
          # FIXME: Getting build problems when using WebP 1.6.0, so hold it back
          # CMAKE_GENERATOR = "Ninja"

      - name: ccache-save
        id: ccache-save
        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.manylinux}}-${{matrix.python}}

      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: cibw-wheels-${{matrix.manylinux}}-${{ matrix.python }}-${{ matrix.manylinux }}
          path: |
            ./wheelhouse/*.whl

      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: stubs-${{ matrix.python }}-${{ matrix.manylinux }}
          path: |
            ./wheelhouse/OpenImageIO/__init__.pyi
        # if stub validation fails we want to upload the stubs for users to review.
        # keep the python build in sync with the version specified in tool.cibuildwheel.overrides 
        # section of pyproject.toml
        if: always() && contains(matrix.python, 'cp311-manylinux')

  # ---------------------------------------------------------------------------
  # Linux ARM Wheels
  # ---------------------------------------------------------------------------

  linux-arm:
    name: Build wheels on Linux ARM
    runs-on: ubuntu-24.04-arm
    if: |
      github.event_name != 'schedule' ||
      github.repository == 'AcademySoftwareFoundation/OpenImageIO'
    strategy:
        matrix:
          include:
            # -------------------------------------------------------------------
            # CPython ARM 64 bits manylinux2014
            # -------------------------------------------------------------------
            - build: CPython 3.9 ARM 64 bits manylinux_2_28
              manylinux: manylinux_2_28
              python: cp39-manylinux_aarch64
              arch: aarch64
            - build: CPython 3.10 ARM 64 bits manylinux_2_28
              manylinux: manylinux_2_28
              python: cp310-manylinux_aarch64
              arch: aarch64
            - build: CPython 3.11 ARM 64 bits manylinux_2_28
              manylinux: manylinux_2_28
              python: cp311-manylinux_aarch64
              arch: aarch64
            - build: CPython 3.12 ARM 64 bits manylinux_2_28
              manylinux: manylinux_2_28
              python: cp312-manylinux_aarch64
              arch: aarch64
            - build: CPython 3.13 ARM 64 bits manylinux_2_28
              manylinux: manylinux_2_28
              python: cp313-manylinux_aarch64
              arch: aarch64
            - build: CPython 3.14 ARM 64 bits manylinux_2_28
              manylinux: manylinux_2_28
              python: cp314-manylinux_aarch64
              arch: aarch64

    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Install Python
        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
        with:
          python-version: '3.9'
        
      - name: ccache-restore
        id: ccache-restore
        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.python}}
          restore-keys: wheel-${{runner.os}}-${{matrix.python}}

      - name: Build wheels
        uses: pypa/cibuildwheel@9c00cb4f6b517705a3794b22395aedc36257242c # v3.2.1
        env:
          CIBW_ENVIRONMENT_PASS_LINUX: GITHUB_ACTIONS
          CIBW_BEFORE_ALL: "source src/build-scripts/build_ccache.bash && pwd && /project/ext/dist/bin/ccache --max-size=200M && /project/ext/dist/bin/ccache -sv && export CMAKE_C_COMPILER_LAUNCHER=/project/ext/dist/bin/ccache CMAKE_CXX_COMPILER_LAUNCHER=/project/ext/dist/bin/ccache"
          CIBW_BEFORE_TEST: "ext/dist/bin/ccache -s"
          CIBW_BUILD: ${{ matrix.python }}
          CIBW_ARCHS: ${{ matrix.arch }}
          CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux }}
          CIBW_ENVIRONMENT: >
              CCACHE_DIR=/host//home/runner/.ccache
              CCACHE_COMPRESSION=yes
              CCACHE_PREBUILT=0
              CMAKE_BUILD_PARALLEL_LEVEL=6
              CTEST_PARALLEL_LEVEL=6
              SKBUILD_CMAKE_ARGS="-DLINKSTATIC=1"
              SKBUILD_CMAKE_BUILD_TYPE="MinSizeRel"
              SKBUILD_BUILD_DIR=/project/build
              CXXFLAGS="-Wno-error=stringop-overflow -Wno-pragmas"
              WebP_BUILD_VERSION="1.5.0"

      - name: ccache-save
        id: ccache-save
        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.python}}

      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: cibw-wheels-${{ matrix.python }}-${{ matrix.manylinux }}
          path: |
            ./wheelhouse/*.whl
      
      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: stubs-${{ matrix.python }}-${{ matrix.manylinux }}
          path: |
            ./wheelhouse/OpenImageIO/__init__.pyi
        # if stub validation fails we want to upload the stubs for users to review.
        # keep the python build in sync with the version specified in tool.cibuildwheel.overrides 
        # section of pyproject.toml
        if: always() && contains(matrix.python, 'cp311-manylinux')

  # ---------------------------------------------------------------------------
  # macOS Wheels
  # ---------------------------------------------------------------------------

  macos:
    name: Build wheels on macOS
    runs-on: macos-15-intel
    if: |
      github.event_name != 'schedule' ||
      github.repository == 'AcademySoftwareFoundation/OpenImageIO'
    strategy:
      matrix:
        include:
          # -------------------------------------------------------------------
          # CPython 64 bits
          # -------------------------------------------------------------------
          - build: CPython 3.9 64 bits
            python: cp39-macosx_x86_64
            arch: x86_64
          - build: CPython 3.10 64 bits
            python: cp310-macosx_x86_64
            arch: x86_64
          - build: CPython 3.11 64 bits
            python: cp311-macosx_x86_64
            arch: x86_64
          - build: CPython 3.12 64 bits
            python: cp312-macosx_x86_64
            arch: x86_64
          - build: CPython 3.13 64 bits
            python: cp313-macosx_x86_64
            arch: x86_64
          - build: CPython 3.14 64 bits
            python: cp314-macosx_x86_64
            arch: x86_64

    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Install Python
        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
        with:
          python-version: '3.9'

      - name: ccache-restore
        id: ccache-restore
        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.python}}
          restore-keys: wheel-${{runner.os}}-${{matrix.python}}

      - name: Install build tools
        run: |
            brew install ninja ccache || true
        
      - name: Brew uninstall problematic dependencies
        run: |
            brew uninstall -f --ignore-dependencies openexr imath expat cmake || true
        
      - name: Build wheels
        uses: pypa/cibuildwheel@9c00cb4f6b517705a3794b22395aedc36257242c # v3.2.1
        env:
          CIBW_BUILD: ${{ matrix.python }}
          CIBW_ARCHS: ${{ matrix.arch }}
          MACOSX_DEPLOYMENT_TARGET: 10.15
          CMAKE_GENERATOR: "Unix Makefiles"
          # TODO: Re-enable HEIF when we provide a build recipe that does 
          # not include GPL-licensed dynamic libraries.
          USE_Libheif: 'OFF'
          CMAKE_BUILD_PARALLEL_LEVEL: 6
          CTEST_PARALLEL_LEVEL: 6
          SKBUILD_BUILD_DIR: "/Users/runner/work/OpenImageIO/OpenImageIO/build"
          CCACHE_DIR: /Users/runner/.ccache
          CCACHE_COMPRESSION: yes

      - name: ccache-save
        id: ccache-save
        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.python}}

      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: cibw-wheels-${{ matrix.python }}
          path: ./wheelhouse/*.whl


  # ---------------------------------------------------------------------------
  # macOS ARM Wheels
  # ---------------------------------------------------------------------------

  macos-arm:
    name: Build wheels on macOS ARM
    runs-on: macos-14
    if: |
      github.event_name != 'schedule' ||
      github.repository == 'AcademySoftwareFoundation/OpenImageIO'
    strategy:
      matrix:
        include:
          # -------------------------------------------------------------------
          # CPython ARM 64 bits
          # -------------------------------------------------------------------
          - build: CPython 3.9 ARM 64 bits
            python: cp39-macosx_arm64
            arch: arm64
          - build: CPython 3.10 ARM 64 bits
            python: cp310-macosx_arm64
            arch: arm64
          - build: CPython 3.11 ARM 64 bits
            python: cp311-macosx_arm64
            arch: arm64
          - build: CPython 3.12 ARM 64 bits
            python: cp312-macosx_arm64
            arch: arm64
          - build: CPython 3.13 ARM 64 bits
            python: cp313-macosx_arm64
            arch: arm64
          - build: CPython 3.14 ARM 64 bits
            python: cp314-macosx_arm64
            arch: arm64

    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        
      - name: Install Python
        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
        with:
          python-version: '3.9'
        
      - name: ccache-restore
        id: ccache-restore
        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.python}}
          restore-keys: wheel-${{runner.os}}-${{matrix.python}}

      - name: Install build tools
        run: |
            brew install ninja ccache || true
        
      - name: Build wheels
        uses: pypa/cibuildwheel@9c00cb4f6b517705a3794b22395aedc36257242c # v3.2.1
        env:
          CIBW_BUILD: ${{ matrix.python }}
          CIBW_ARCHS: ${{ matrix.arch }}
          CMAKE_GENERATOR: "Unix Makefiles"
          CMAKE_BUILD_PARALLEL_LEVEL: 6
          CTEST_PARALLEL_LEVEL: 6
          SKBUILD_BUILD_DIR: "/Users/runner/work/OpenImageIO/OpenImageIO/build"
          CCACHE_DIR: /Users/runner/.ccache
          CCACHE_COMPRESSION: yes

      - name: ccache-save
        id: ccache-save
        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: ~/.ccache
          key: wheel-${{runner.os}}-${{matrix.python}}

      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: cibw-wheels-${{ matrix.python }}
          path: ./wheelhouse/*.whl

  # ---------------------------------------------------------------------------
  # Windows Wheels
  # ---------------------------------------------------------------------------

  windows:
    name: Build wheels on Windows
    runs-on: windows-2022
    if: |
      github.event_name != 'schedule' ||
      github.repository == 'AcademySoftwareFoundation/OpenImageIO'
    strategy:
      matrix:
        include:
          # -------------------------------------------------------------------
          # CPython 64 bits
          # -------------------------------------------------------------------
          - build: CPython 3.9 64 bits
            python: cp39-win_amd64
            arch: AMD64
          - build: CPython 3.10 64 bits
            python: cp310-win_amd64
            arch: AMD64
          - build: CPython 3.11 64 bits
            python: cp311-win_amd64
            arch: AMD64
          - build: CPython 3.12 64 bits
            python: cp312-win_amd64
            arch: AMD64
          - build: CPython 3.13 64 bits
            python: cp313-win_amd64
            arch: AMD64
          - build: CPython 3.14 64 bits
            python: cp314-win_amd64
            arch: AMD64

    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Install Python
        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
        with:
          python-version: '3.9'

      - name: Build wheels
        uses: pypa/cibuildwheel@9c00cb4f6b517705a3794b22395aedc36257242c # v3.2.1
        env:
          CIBW_BUILD: ${{ matrix.python }}
          CIBW_ARCHS: ${{ matrix.arch }}
          CMAKE_POLICY_VERSION_MINIMUM: 3.5
          CMAKE_BUILD_PARALLEL_LEVEL: 4
          CTEST_PARALLEL_LEVEL: 4
          SKBUILD_BUILD_DIR: "$HOME/OpenImageIO/OpenImageIO/build"
          CCACHE_DIR: ~/.ccache
          CCACHE_COMPRESSION: yes

      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
        with:
          name: cibw-wheels-${{ matrix.python }}
          path: ./wheelhouse/*.whl


  upload_pypi:
    needs: [sdist, linux, linux-arm, macos, macos-arm, windows]
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags/v3.0.') || startsWith(github.event.ref, 'refs/tags/v3.1.')) && github.repository == 'AcademySoftwareFoundation/OpenImageIO'
    steps:
      - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0

      - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
        with:
          pattern: cibw-*
          path: dist
          merge-multiple: true

      - uses: pypa/gh-action-pypi-publish@897895f1e160c830e369f9779632ebc134688e1b # release/v1
