name: Build (and publish) Python Packages

on:
  workflow_dispatch:
      inputs:
        publish-TestPyPI:
          description: 'Publish packages to TestPyPI'
          required: false
          default: false
          type: boolean
        publish-PyPI:
          description: 'Publish packages to PyPI'
          required: false
          default: false
          type: boolean
  release:
    types: [published]

jobs:
  build_sdist:
    name: Build sdist
    runs-on: ubuntu-latest

    outputs:
      vrna_sdist_file: ${{ steps.create_sdist.outputs.file }}

    steps:
      - uses: actions/checkout@v4
      - name: install prerequisites
        run:  |
          sudo apt-get update
          sudo apt-get -y install build-essential autoconf automake gengetopt help2man libtool check graphviz python3-sphinx 
      - name: Checkout swig repo
        uses: actions/checkout@v6
        with:
          repository: swig/swig
          ref: 'v4.4.0'
          path: swig
      - name: Install swig
        run:  |
          cd swig
          ./autogen.sh && ./configure --prefix=/usr
          make -j $(nproc)
          sudo make install
      - name: Checkout doxygen repo
        uses: actions/checkout@v6
        with:
          repository: doxygen/doxygen
          ref: 'Release_1_15_0'
          path: doxygen
      - name: Install doxygen
        run:  |
          cd doxygen
          mkdir build
          cd build
          cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX:PATH=/usr -Dbuild_doc:BOOL=OFF ..
          make -j $(nproc)
          sudo make install
      - name: autotools setup
        run:  |
          autoreconf -i
          tar -xjf src/dlib-20.0.tar.bz2 -C src
          tar -xzf src/libsvm-3.35.tar.gz -C src
      - name: configure
        run: ./configure --without-kinfold --without-forester --without-rnalocmin --without-rnaxplorer --without-perl --without-gsl --disable-mpfr
      - name: prepare source files
        run:  |
          cd src/ViennaRNA/static
          make
      - name: prepare doxygen XML
        run: |
          cd doc/doxygen
          make doxygen-xml
      - name: prepare Python wrapper
        run: |
          cd interfaces/Python
          make RNA/RNA.py
      - id: create_sdist
        name: build Python source distribution
        run:  |
          python -m pip install --upgrade build
          python -m build --sdist
          echo "file=$(cd dist && ls viennarna-*.tar.gz)" >> "$GITHUB_OUTPUT"
      - name: Archive production artifacts
        uses: actions/upload-artifact@v4
        with:
          name: sdist
          path: dist/

  build_wheels_linux:
    name: Build Linux wheels for ${{ matrix.pyver }} on ${{ matrix.os }} [ ${{ matrix.archs }} ]
    needs: build_sdist
    runs-on: ${{ matrix.os }}

    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        os : [ubuntu-latest]
        pyver: [cp310, cp311, cp312, cp313, cp314]
        archs: [x86_64]

    steps:
      - uses: actions/download-artifact@v4
        with:
          name: sdist
      - name: Set sdist environment variable
        run:  |
          echo "VRNA_SDIST_FILE=${{needs.build_sdist.outputs.vrna_sdist_file}}" >> "$GITHUB_ENV"
      - name: Build wheels
        uses: pypa/cibuildwheel@v3.3.0
        with:
          package-dir: "$VRNA_SDIST_FILE"
          output-dir: dist
        env:
          CIBW_BUILD_VERBOSITY: 3
          CIBW_BUILD: ${{matrix.pyver}}-*
          CIBW_ARCHS: ${{matrix.archs}}
          CIBW_ENVIRONMENT: VRNA_SDIST_FILE=$VRNA_SDIST_FILE
          CIBW_TEST_REQUIRES: pytest
          CIBW_TEST_COMMAND: pytest --datadir {package}/tests/data {package}/tests/python
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-linux-${{ strategy.job-index }}
          path: ./dist/*.whl
          compression-level: 0 # no compression

  build_wheels_mac:
    name: Build MacOSX wheels for ${{ matrix.pyver }} on ${{ matrix.os }}
    needs: build_sdist
    runs-on: ${{ matrix.os }}

    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        os: [macos-15-intel, macos-14]
        pyver: [cp310, cp311, cp312, cp313, cp314]

    steps:
      - uses: actions/download-artifact@v4
        with:
          name: sdist
      - name: Set sdist environment variable
        run:  |
          echo "VRNA_SDIST_FILE=${{needs.build_sdist.outputs.vrna_sdist_file}}" >> "$GITHUB_ENV"
      - name: Build wheels
        uses: pypa/cibuildwheel@v3.3.0
        with:
          package-dir: "$VRNA_SDIST_FILE"
          output-dir: dist
        env:
          CIBW_BUILD_VERBOSITY: 3
          CIBW_BUILD: ${{matrix.pyver}}-*
          CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=10.14 CC=clang CXX=clang++
          CIBW_ARCHS_MACOS: auto
          CIBW_TEST_REQUIRES: pytest
          CIBW_TEST_COMMAND: pytest --datadir {package}/tests/data {package}/tests/python
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-mac-${{ strategy.job-index }}
          path: ./dist/*.whl
          compression-level: 0 # no compression

  build_wheels_win:
    name: Build Windows wheels for ${{ matrix.pyver }} ${{ matrix.archs }}
    needs: build_sdist
    runs-on: windows-latest

    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        pyver: [cp310, cp311, cp312, cp313, cp314]
        archs: [AMD64]

    steps:
      - uses: actions/download-artifact@v4
        with:
          name: sdist
      - name: Set sdist environment variable
        run:  |
          echo "VRNA_SDIST_FILE=${{needs.build_sdist.outputs.vrna_sdist_file}}" >> "$env:GITHUB_ENV"
      - name: Build wheels
        uses: pypa/cibuildwheel@v3.3.0
        with:
          package-dir: "$env:VRNA_SDIST_FILE"
          output-dir: dist
        # to supply options, put them in 'env', like:
        # env:
        #   CIBW_SOME_OPTION: value
        env:
          CIBW_BUILD: ${{matrix.pyver}}-*
          CIBW_ARCHS_WINDOWS: ${{ matrix.archs }}
          CIBW_ENVIRONMENT_WINDOWS: VRNA_SDIST_FILE=$env:VRNA_SDIST_FILE
          CIBW_TEST_REQUIRES: pytest
          CIBW_TEST_COMMAND_WINDOWS: pytest --datadir {package}\tests\data {package}\tests\python
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-win-${{ strategy.job-index }}
          path: ./dist/*.whl
          compression-level: 0 # no compression

  build_wheels_cross:
    name: Build cross compiled Linux wheels for ${{ matrix.pyver }} on ${{ matrix.os }} [ ${{ matrix.archs }} ]
    needs: build_sdist
    runs-on:  ${{ matrix.os }}

    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        os : [ubuntu-latest]
        pyver: [cp310, cp311, cp312, cp313]
        archs: [aarch64]

    steps:
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: arm64
      - uses: actions/download-artifact@v4
        with:
          name: sdist
      - name: Set sdist environment variable
        run:  |
          echo "VRNA_SDIST_FILE=${{needs.build_sdist.outputs.vrna_sdist_file}}" >> "$GITHUB_ENV"
      - name: Build wheels
        uses: pypa/cibuildwheel@v2.20.0
        with:
          package-dir: "$VRNA_SDIST_FILE"
          output-dir: dist
        env:
          CIBW_BUILD_VERBOSITY: 3
          CIBW_PROJECT_REQUIRES_PYTHON: ">= 3.10"
          CIBW_BUILD: ${{matrix.pyver}}-*
          CIBW_ARCHS: ${{matrix.archs}}
          CIBW_ENVIRONMENT: VRNA_SDIST_FILE=$VRNA_SDIST_FILE
          CIBW_TEST_REQUIRES: pytest
          CIBW_TEST_COMMAND: pytest --datadir {package}/tests/data {package}/tests/python
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-cross-${{ strategy.job-index }}
          path: ./dist/*.whl
          compression-level: 0 # no compression

  upload_test_pypi:
    needs: [build_wheels_linux, build_wheels_mac, build_wheels_win, build_wheels_cross, build_sdist]
    runs-on: ubuntu-latest
    environment: production
    if: (github.event_name == 'workflow_dispatch' && inputs.publish-TestPyPI) || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
    steps:
      - uses: actions/download-artifact@v4
        with:
          # unpacks all wheels into dist/
          pattern: wheels-*
          merge-multiple: true
          path: dist

      - uses: actions/download-artifact@v4
        with:
          # unpacks sdist into dist/
          name: sdist
          path: dist

      - name: Publish package to TestPyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.TEST_PYPI_API_TOKEN }}
          repository-url: https://test.pypi.org/legacy/

  upload_to_pypi:
    needs: [build_wheels_linux, build_wheels_mac, build_wheels_win, build_wheels_cross, build_sdist]
    runs-on: ubuntu-latest
    if: (github.event_name == 'workflow_dispatch' && inputs.publish-PyPI) || (github.event_name == 'release' && github.event.action == 'published')
    steps:
      - uses: actions/download-artifact@v4
        with:
          # unpacks all wheels into dist/
          pattern: wheels-*
          merge-multiple: true
          path: dist

      - uses: actions/download-artifact@v4
        with:
          # unpacks sdist into dist/
          name: sdist
          path: dist

      - name: Publish package to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.PYPI_API_TOKEN }}
