name: CI

on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - master
      - stable
      - v*

permissions: read-all

concurrency:
  group: test-${{ github.ref }}
  cancel-in-progress: true

env:
  PIP_BREAK_SYSTEM_PACKAGES: 1
  PIP_ONLY_BINARY: numpy
  FORCE_COLOR: 3
  PYTEST_TIMEOUT: 300
  # For cmake:
  VERBOSE: 1

jobs:
  # This is the "main" test suite, which tests a large number of different
  # versions of default compilers and Python versions in GitHub Actions.
  standard:
    strategy:
      fail-fast: false
      matrix:
        runs-on: [ubuntu-20.04, windows-2022, macos-latest]
        python:
        - '3.6'
        - '3.9'
        - '3.10'
        - '3.11'
        - '3.12'
        - 'pypy-3.8'
        - 'pypy-3.9'
        - 'pypy-3.10'

        # Items in here will either be added to the build matrix (if not
        # present), or add new keys to an existing matrix element if all the
        # existing keys match.
        #
        # We support an optional key: args, for cmake args
        include:
          # Just add a key
          - runs-on: ubuntu-20.04
            python: '3.6'
            args: >
              -DPYBIND11_FINDPYTHON=ON
              -DCMAKE_CXX_FLAGS="-D_=1"
          - runs-on: ubuntu-20.04
            python: 'pypy-3.8'
            args: >
              -DPYBIND11_FINDPYTHON=ON
          - runs-on: windows-2019
            python: '3.6'
            args: >
              -DPYBIND11_FINDPYTHON=ON
          # Inject a couple Windows 2019 runs
          - runs-on: windows-2019
            python: '3.9'

    name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}"
    runs-on: ${{ matrix.runs-on }}

    steps:
    - uses: actions/checkout@v4

    - name: Setup Python ${{ matrix.python }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python }}

    - name: Setup Boost (Linux)
      # Can't use boost + define _
      if: runner.os == 'Linux' && matrix.python != '3.6'
      run: sudo apt-get install libboost-dev

    - name: Setup Boost (macOS)
      if: runner.os == 'macOS'
      run: brew install boost

    - name: Update CMake
      uses: jwlawson/actions-setup-cmake@v2.0

    - name: Cache wheels
      if: runner.os == 'macOS'
      uses: actions/cache@v4
      with:
        # This path is specific to macOS - we really only need it for PyPy NumPy wheels
        # See https://github.com/actions/cache/blob/master/examples.md#python---pip
        # for ways to do this more generally
        path: ~/Library/Caches/pip
        # Look to see if there is a cache hit for the corresponding requirements file
        key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }}

    - name: Prepare env
      run: |
        python -m pip install -r tests/requirements.txt

    - name: Setup annotations on Linux
      if: runner.os == 'Linux'
      run: python -m pip install pytest-github-actions-annotate-failures

    # First build - C++11 mode and inplace
    # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here
    # (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime).
    - name: Configure C++11 ${{ matrix.args }}
      run: >
        cmake -S . -B .
        -DPYBIND11_WERROR=ON
        -DPYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION=ON
        -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
        -DPYBIND11_NUMPY_1_ONLY=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=11
        ${{ matrix.args }}

    - name: Build C++11
      run: cmake --build . -j 2

    - name: Python tests C++11
      run: cmake --build . --target pytest -j 2

    - name: C++11 tests
      # TODO: Figure out how to load the DLL on Python 3.8+
      if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))"
      run: cmake --build .  --target cpptest -j 2

    - name: Interface test C++11
      run: cmake --build . --target test_cmake_build

    - name: Clean directory
      run: git clean -fdx

    # Second build - C++17 mode and in a build directory
    # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here.
    # (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime).
    - name: Configure C++17
      run: >
        cmake -S . -B build2
        -DPYBIND11_WERROR=ON
        -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
        -DPYBIND11_NUMPY_1_ONLY=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=17
        ${{ matrix.args }}

    - name: Build
      run: cmake --build build2 -j 2

    - name: Python tests
      run: cmake --build build2 --target pytest

    - name: C++ tests
      # TODO: Figure out how to load the DLL on Python 3.8+
      if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))"
      run: cmake --build build2 --target cpptest

    # Third build - C++17 mode with unstable ABI
    - name: Configure (unstable ABI)
      run: >
        cmake -S . -B build3
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=17
        -DPYBIND11_INTERNALS_VERSION=10000000
        ${{ matrix.args }}

    - name: Build (unstable ABI)
      run: cmake --build build3 -j 2

    - name: Python tests (unstable ABI)
      run: cmake --build build3 --target pytest

    - name: Interface test
      run: cmake --build build2 --target test_cmake_build

    # This makes sure the setup_helpers module can build packages using
    # setuptools
    - name: Setuptools helpers test
      run: |
        pip install setuptools
        pytest tests/extra_setuptools
      if: "!(matrix.runs-on == 'windows-2022')"


  deadsnakes:
    strategy:
      fail-fast: false
      matrix:
        include:
        # TODO: Fails on 3.10, investigate
        # JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4889
        # - python-version: "3.9"
        #   python-debug: true
        #   valgrind: true
        - python-version: "3.11"
          python-debug: false

    name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
    runs-on: ubuntu-20.04

    steps:
    - uses: actions/checkout@v4

    - name: Setup Python ${{ matrix.python-version }} (deadsnakes)
      uses: deadsnakes/action@v3.1.0
      with:
        python-version: ${{ matrix.python-version }}
        debug: ${{ matrix.python-debug }}

    - name: Update CMake
      uses: jwlawson/actions-setup-cmake@v2.0

    - name: Valgrind cache
      if: matrix.valgrind
      uses: actions/cache@v4
      id: cache-valgrind
      with:
        path: valgrind
        key: 3.16.1 # Valgrind version

    - name: Compile Valgrind
      if: matrix.valgrind && steps.cache-valgrind.outputs.cache-hit != 'true'
      run: |
        VALGRIND_VERSION=3.16.1
        curl https://sourceware.org/pub/valgrind/valgrind-$VALGRIND_VERSION.tar.bz2 -o - | tar xj
        mv valgrind-$VALGRIND_VERSION valgrind
        cd valgrind
        ./configure
        make -j 2 > /dev/null

    - name: Install Valgrind
      if: matrix.valgrind
      working-directory: valgrind
      run: |
        sudo make install
        sudo apt-get update
        sudo apt-get install libc6-dbg  # Needed by Valgrind

    - name: Prepare env
      run: |
        python -m pip install -r tests/requirements.txt

    - name: Configure
      run: >
        cmake -S . -B build
        -DCMAKE_BUILD_TYPE=Debug
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=17

    - name: Build
      run: cmake --build build -j 2

    - name: Python tests
      run: cmake --build build --target pytest

    - name: C++ tests
      run: cmake --build build --target cpptest

    - name: Run Valgrind on Python tests
      if: matrix.valgrind
      run: cmake --build build --target memcheck


  # Testing on clang using the excellent silkeh clang docker images
  clang:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        clang:
          - 3.6
          - 3.7
          - 3.9
          - 7
          - 9
          - dev
        std:
          - 11
        container_suffix:
          - ""
        include:
          - clang: 5
            std: 14
          - clang: 10
            std: 17
          - clang: 11
            std: 20
          - clang: 12
            std: 20
          - clang: 13
            std: 20
          - clang: 14
            std: 20
          - clang: 15
            std: 20
            container_suffix: "-bullseye"
          - clang: 16
            std: 20
            container_suffix: "-bullseye"

    name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64"
    container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}"

    steps:
    - uses: actions/checkout@v4

    - name: Add wget and python3
      run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev

    - name: Configure
      shell: bash
      run: >
        cmake -S . -B build
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DCMAKE_CXX_STANDARD=${{ matrix.std }}
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

    - name: Build
      run: cmake --build build -j 2

    - name: Python tests
      run: cmake --build build --target pytest

    - name: C++ tests
      run: cmake --build build --target cpptest

    - name: Interface test
      run: cmake --build build --target test_cmake_build


  # Testing NVCC; forces sources to behave like .cu files
  cuda:
    runs-on: ubuntu-latest
    name: "🐍 3.10 • CUDA 12.2 • Ubuntu 22.04"
    container: nvidia/cuda:12.2.0-devel-ubuntu22.04

    steps:
    - uses: actions/checkout@v4

    # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
    - name: Install 🐍 3
      run: apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y cmake git python3-dev python3-pytest python3-numpy

    - name: Configure
      run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON

    - name: Build
      run: cmake --build build -j2 --verbose

    - name: Python tests
      run: cmake --build build --target pytest


# TODO: Internal compiler error - report to NVidia
#  # Testing CentOS 8 + PGI compilers
#  centos-nvhpc8:
#    runs-on: ubuntu-latest
#    name: "🐍 3 • CentOS8 / PGI 20.11 • x64"
#    container: centos:8
#
#    steps:
#    - uses: actions/checkout@v4
#
#    - name: Add Python 3 and a few requirements
#      run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules
#
#    - name: Install CMake with pip
#      run: |
#        python3 -m pip install --upgrade pip
#        python3 -m pip install cmake --prefer-binary
#
#    - name: Install NVidia HPC SDK
#      run: >
#        yum -y install
#        https://developer.download.nvidia.com/hpc-sdk/20.11/nvhpc-20-11-20.11-1.x86_64.rpm
#        https://developer.download.nvidia.com/hpc-sdk/20.11/nvhpc-2020-20.11-1.x86_64.rpm
#
#    - name: Configure
#      shell: bash
#      run: |
#        source /etc/profile.d/modules.sh
#        module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.11
#        cmake -S . -B build -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=14 -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
#
#    - name: Build
#      run: cmake --build build -j 2 --verbose
#
#    - name: Python tests
#      run: cmake --build build --target pytest
#
#    - name: C++ tests
#      run: cmake --build build --target cpptest
#
#    - name: Interface test
#      run: cmake --build build --target test_cmake_build


  # Testing on Ubuntu + NVHPC (previous PGI) compilers, which seems to require more workarounds
  ubuntu-nvhpc7:
    runs-on: ubuntu-20.04
    name: "🐍 3 • NVHPC 23.5 • C++17 • x64"

    env:
      # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
      DEBIAN_FRONTEND: 'noninteractive'
    steps:
    - uses: actions/checkout@v4

    - name: Add NVHPC Repo
      run: |
        echo 'deb [trusted=yes] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | \
          sudo tee /etc/apt/sources.list.d/nvhpc.list

    - name: Install 🐍 3 & NVHPC
      run: |
        sudo apt-get update -y && \
        sudo apt-get install -y cmake environment-modules git python3-dev python3-pip python3-numpy && \
        sudo apt-get install -y --no-install-recommends nvhpc-23-5 && \
        sudo rm -rf /var/lib/apt/lists/*
        python3 -m pip install --upgrade pip
        python3 -m pip install --upgrade pytest

    # On some systems, you many need further workarounds:
    # https://github.com/pybind/pybind11/pull/2475
    - name: Configure
      shell: bash
      run: |
        source /etc/profile.d/modules.sh
        module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/23.5
        cmake -S . -B build -DDOWNLOAD_CATCH=ON \
                            -DCMAKE_CXX_STANDARD=17 \
                            -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \
                            -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0" \
                            -DPYBIND11_TEST_FILTER="test_smart_ptr.cpp"

    - name: Build
      run: cmake --build build -j 2 --verbose

    - name: Python tests
      run: cmake --build build --target pytest

    - name: C++ tests
      run: cmake --build build --target cpptest

    - name: Interface test
      run: cmake --build build --target test_cmake_build


  # Testing on GCC using the GCC docker images (only recent images supported)
  gcc:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - { gcc: 7, std: 11 }
          - { gcc: 7, std: 17 }
          - { gcc: 8, std: 14 }
          - { gcc: 8, std: 17 }
          - { gcc: 10, std: 17 }
          - { gcc: 11, std: 20 }
          - { gcc: 12, std: 20 }
          - { gcc: 13, std: 20 }

    name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64"
    container: "gcc:${{ matrix.gcc }}"

    steps:
    - uses: actions/checkout@v4

    - name: Add Python 3
      run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev

    - name: Update pip
      run: python3 -m pip install --upgrade pip

    - name: Update CMake
      uses: jwlawson/actions-setup-cmake@v2.0

    - name: Configure
      shell: bash
      run: >
        cmake -S . -B build
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DCMAKE_CXX_STANDARD=${{ matrix.std }}
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

    - name: Build
      run: cmake --build build -j 2

    - name: Python tests
      run: cmake --build build --target pytest

    - name: C++ tests
      run: cmake --build build --target cpptest

    - name: Interface test
      run: cmake --build build --target test_cmake_build

    - name: Configure - Exercise cmake -DPYBIND11_TEST_OVERRIDE
      if: matrix.gcc == '12'
      shell: bash
      run: >
        cmake -S . -B build_partial
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DCMAKE_CXX_STANDARD=${{ matrix.std }}
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
        "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"

    - name: Build - Exercise cmake -DPYBIND11_TEST_OVERRIDE
      if: matrix.gcc == '12'
      run: cmake --build build_partial -j 2

    - name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
      if: matrix.gcc == '12'
      run: cmake --build build_partial --target pytest

  # Testing on ICC using the oneAPI apt repo
  icc:
    runs-on: ubuntu-20.04

    name: "🐍 3 • ICC latest • x64"

    steps:
    - uses: actions/checkout@v4

    - name: Add apt repo
      run: |
        sudo apt-get update
        sudo apt-get install -y wget build-essential pkg-config cmake ca-certificates gnupg
        wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB
        sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB
        echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list

    - name: Add ICC & Python 3
      run: sudo apt-get update; sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic cmake python3-dev python3-numpy python3-pytest python3-pip

    - name: Update pip
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        python3 -m pip install --upgrade pip

    - name: Install dependencies
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        python3 -m pip install -r tests/requirements.txt

    - name: Configure C++11
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake -S . -B build-11     \
        -DPYBIND11_WERROR=ON    \
        -DDOWNLOAD_CATCH=ON     \
        -DDOWNLOAD_EIGEN=OFF    \
        -DCMAKE_CXX_STANDARD=11             \
        -DCMAKE_CXX_COMPILER=$(which icpc)  \
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

    - name: Build C++11
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake --build build-11 -j 2 -v

    - name: Python tests C++11
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        sudo service apport stop
        cmake --build build-11 --target check

    - name: C++ tests C++11
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake --build build-11 --target cpptest

    - name: Interface test C++11
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake --build build-11 --target test_cmake_build

    - name: Configure C++17
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake -S . -B build-17     \
        -DPYBIND11_WERROR=ON    \
        -DDOWNLOAD_CATCH=ON     \
        -DDOWNLOAD_EIGEN=OFF    \
        -DCMAKE_CXX_STANDARD=17             \
        -DCMAKE_CXX_COMPILER=$(which icpc)  \
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

    - name: Build C++17
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake --build build-17 -j 2 -v

    - name: Python tests C++17
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        sudo service apport stop
        cmake --build build-17 --target check

    - name: C++ tests C++17
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake --build build-17 --target cpptest

    - name: Interface test C++17
      run: |
        set +e; source /opt/intel/oneapi/setvars.sh; set -e
        cmake --build build-17 --target test_cmake_build


  # Testing on CentOS (manylinux uses a centos base, and this is an easy way
  # to get GCC 4.8, which is the manylinux1 compiler).
  centos:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        container:
          - "centos:7"  # GCC 4.8
          - "almalinux:8"
          - "almalinux:9"

    name: "🐍 3 • ${{ matrix.container }} • x64"
    container: "${{ matrix.container }}"

    steps:
    - name: Latest actions/checkout
      uses: actions/checkout@v4
      if: matrix.container != 'centos:7'

    - name: Pin actions/checkout as required for centos:7
      uses: actions/checkout@v3
      if: matrix.container == 'centos:7'

    - name: Add Python 3 (RHEL 7)
      if: matrix.container == 'centos:7'
      run: yum update -y && yum install -y python3-devel gcc-c++ make git

    - name: Add Python 3 (RHEL 8+)
      if: matrix.container != 'centos:7'
      run: dnf update -y && dnf install -y python3-devel gcc-c++ make git

    - name: Update pip
      run: python3 -m pip install --upgrade pip

    - name: Install dependencies
      run: |
        python3 -m pip install cmake -r tests/requirements.txt

    - name: Ensure NumPy 2 is used (required Python >= 3.9)
      if: matrix.container == 'almalinux:9'
      run: |
        python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1'

    - name: Configure
      shell: bash
      run: >
        cmake -S . -B build
        -DCMAKE_BUILD_TYPE=MinSizeRel
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=11
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

    - name: Build
      run: cmake --build build -j 2

    - name: Python tests
      run: cmake --build build --target pytest

    - name: C++ tests
      run: cmake --build build --target cpptest

    - name: Interface test
      run: cmake --build build --target test_cmake_build


  # This tests an "install" with the CMake tools
  install-classic:
    name: "🐍 3.7 • Debian • x86 •  Install"
    runs-on: ubuntu-latest
    container: i386/debian:buster

    steps:
    - uses: actions/checkout@v1  # v1 is required to run inside docker

    - name: Install requirements
      run: |
        apt-get update
        apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip
        pip3 install "pytest==6.*"

    - name: Configure for install
      run: >
        cmake .
        -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

    - name: Make and install
      run: make install

    - name: Copy tests to new directory
      run: cp -a tests /pybind11-tests

    - name: Make a new test directory
      run: mkdir /build-tests

    - name: Configure tests
      run: >
        cmake ../pybind11-tests
        -DDOWNLOAD_CATCH=ON
        -DPYBIND11_WERROR=ON
        -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
      working-directory: /build-tests

    - name: Python tests
      run: make pytest -j 2
      working-directory: /build-tests


  # This verifies that the documentation is not horribly broken, and does a
  # basic validation check on the SDist.
  doxygen:
    name: "Documentation build test"
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-python@v5
      with:
        python-version: "3.x"

    - name: Install Doxygen
      run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04

    - name: Build docs
      run: pipx run nox -s docs

    - name: Make SDist
      run: pipx run nox -s build -- --sdist

    - run: git status --ignored

    - name: Check local include dir
      run: >
        ls pybind11;
        python3 -c "import pybind11, pathlib; assert (a := pybind11.get_include()) == (b := str(pathlib.Path('include').resolve())), f'{a} != {b}'"

    - name: Compare Dists (headers only)
      working-directory: include
      run: |
        python3 -m pip install --user -U ../dist/*.tar.gz
        installed=$(python3 -c "import pybind11; print(pybind11.get_include() + '/pybind11')")
        diff -rq $installed ./pybind11

  win32:
    strategy:
      fail-fast: false
      matrix:
        python:
        - 3.6
        - 3.7
        - 3.8
        - 3.9

        include:
          - python: 3.9
            args: -DCMAKE_CXX_STANDARD=20
          - python: 3.8
            args: -DCMAKE_CXX_STANDARD=17
          - python: 3.7
            args: -DCMAKE_CXX_STANDARD=14


    name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}"
    runs-on: windows-2019

    steps:
    - uses: actions/checkout@v4

    - name: Setup Python ${{ matrix.python }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python }}
        architecture: x86

    - name: Update CMake
      uses: jwlawson/actions-setup-cmake@v2.0

    - name: Prepare MSVC
      uses: ilammy/msvc-dev-cmd@v1.13.0
      with:
        arch: x86

    - name: Prepare env
      run: |
        python -m pip install -r tests/requirements.txt

    # First build - C++11 mode and inplace
    - name: Configure ${{ matrix.args }}
      run: >
        cmake -S . -B build
        -G "Visual Studio 16 2019" -A Win32
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        ${{ matrix.args }}
    - name: Build C++11
      run: cmake --build build -j 2

    - name: Python tests
      run: cmake --build build -t pytest

  win32-debug:
    strategy:
      fail-fast: false
      matrix:
        python:
        - 3.8
        - 3.9

        include:
          - python: 3.9
            args: -DCMAKE_CXX_STANDARD=20
          - python: 3.8
            args: -DCMAKE_CXX_STANDARD=17

    name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}"
    runs-on: windows-2019

    steps:
    - uses: actions/checkout@v4

    - name: Setup Python ${{ matrix.python }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python }}
        architecture: x86

    - name: Update CMake
      uses: jwlawson/actions-setup-cmake@v2.0

    - name: Prepare MSVC
      uses: ilammy/msvc-dev-cmd@v1.13.0
      with:
        arch: x86

    - name: Prepare env
      run: |
        python -m pip install -r tests/requirements.txt

    # First build - C++11 mode and inplace
    - name: Configure ${{ matrix.args }}
      run: >
        cmake -S . -B build
        -G "Visual Studio 16 2019" -A Win32
        -DCMAKE_BUILD_TYPE=Debug
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        ${{ matrix.args }}
    - name: Build C++11
      run: cmake --build build --config Debug -j 2

    - name: Python tests
      run: cmake --build build --config Debug -t pytest


  windows-2022:
    strategy:
      fail-fast: false
      matrix:
        python:
        - 3.9

    name: "🐍 ${{ matrix.python }} • MSVC 2022 C++20 • x64"
    runs-on: windows-2022

    steps:
    - uses: actions/checkout@v4

    - name: Setup Python ${{ matrix.python }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python }}

    - name: Prepare env
      # Ensure use of NumPy 2 (via NumPy nightlies but can be changed soon)
      run: |
        python3 -m pip install -r tests/requirements.txt
        python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1'

    - name: Update CMake
      uses: jwlawson/actions-setup-cmake@v2.0

    - name: Configure C++20
      run: >
        cmake -S . -B build
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=20

    - name: Build C++20
      run: cmake --build build -j 2

    - name: Python tests
      run: cmake --build build --target pytest

    - name: C++20 tests
      run: cmake --build build --target cpptest -j 2

    - name: Interface test C++20
      run: cmake --build build --target test_cmake_build

    - name: Configure C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
      run: >
        cmake -S . -B build_partial
        -DPYBIND11_WERROR=ON
        -DDOWNLOAD_CATCH=ON
        -DDOWNLOAD_EIGEN=ON
        -DCMAKE_CXX_STANDARD=20
        "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"

    - name: Build C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
      run: cmake --build build_partial -j 2

    - name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
      run: cmake --build build_partial --target pytest

  mingw:
    name: "🐍 3 • windows-latest • ${{ matrix.sys }}"
    runs-on: windows-latest
    defaults:
      run:
        shell: msys2 {0}
    strategy:
      fail-fast: false
      matrix:
        include:
          - { sys: mingw64, env: x86_64 }
          - { sys: mingw32, env: i686 }
    steps:
    - uses: msys2/setup-msys2@v2
      with:
        msystem: ${{matrix.sys}}
        install: >-
          git
          mingw-w64-${{matrix.env}}-gcc
          mingw-w64-${{matrix.env}}-python-pip
          mingw-w64-${{matrix.env}}-python-numpy
          mingw-w64-${{matrix.env}}-cmake
          mingw-w64-${{matrix.env}}-make
          mingw-w64-${{matrix.env}}-python-pytest
          mingw-w64-${{matrix.env}}-eigen3
          mingw-w64-${{matrix.env}}-boost
          mingw-w64-${{matrix.env}}-catch

    - uses: msys2/setup-msys2@v2
      if: matrix.sys == 'mingw64'
      with:
        msystem: ${{matrix.sys}}
        install: >-
          git
          mingw-w64-${{matrix.env}}-python-scipy

    - uses: actions/checkout@v4

    - name: Configure C++11
      # LTO leads to many undefined reference like
      # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&)
      run: >-
        cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
        -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
        -S . -B build

    - name: Build C++11
      run: cmake --build build -j 2

    - name: Python tests C++11
      run: cmake --build build --target pytest -j 2

    - name: C++11 tests
      run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target cpptest -j 2

    - name: Interface test C++11
      run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target test_cmake_build

    - name: Clean directory
      run: git clean -fdx

    - name: Configure C++14
      run: >-
        cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
        -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
        -S . -B build2

    - name: Build C++14
      run: cmake --build build2 -j 2

    - name: Python tests C++14
      run: cmake --build build2 --target pytest -j 2

    - name: C++14 tests
      run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target cpptest -j 2

    - name: Interface test C++14
      run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target test_cmake_build

    - name: Clean directory
      run: git clean -fdx

    - name: Configure C++17
      run: >-
        cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
        -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
        -S . -B build3

    - name: Build C++17
      run: cmake --build build3 -j 2

    - name: Python tests C++17
      run: cmake --build build3 --target pytest -j 2

    - name: C++17 tests
      run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target cpptest -j 2

    - name: Interface test C++17
      run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build

  windows_clang:

    strategy:
      matrix:
        os: [windows-latest]
        python: ['3.10']

    runs-on: "${{ matrix.os }}"

    name: "🐍 ${{ matrix.python }} • ${{ matrix.os }} • clang-latest"

    steps:
      - name: Show env
        run: env

      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Clang
        uses: egor-tensin/setup-clang@v1

      - name: Setup Python ${{ matrix.python }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}

      - name: Update CMake
        uses: jwlawson/actions-setup-cmake@v2.0

      - name: Install ninja-build tool
        uses: seanmiddleditch/gha-setup-ninja@v4

      - name: Run pip installs
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r tests/requirements.txt

      - name: Show Clang++ version
        run: clang++ --version

      - name: Show CMake version
        run: cmake --version

      # TODO: WERROR=ON
      - name: Configure Clang
        run: >
          cmake -G Ninja -S . -B .
          -DPYBIND11_WERROR=OFF
          -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
          -DDOWNLOAD_CATCH=ON
          -DDOWNLOAD_EIGEN=ON
          -DCMAKE_CXX_COMPILER=clang++
          -DCMAKE_CXX_STANDARD=17

      - name: Build
        run: cmake --build . -j 2

      - name: Python tests
        run: cmake --build . --target pytest -j 2

      - name: C++ tests
        run: cmake --build . --target cpptest -j 2

      - name: Interface test
        run: cmake --build . --target test_cmake_build -j 2

      - name: Clean directory
        run: git clean -fdx

  macos_brew_install_llvm:
    name: "macos-latest • brew install llvm"
    runs-on: macos-latest

    env:
      # https://apple.stackexchange.com/questions/227026/how-to-install-recent-clang-with-homebrew
      LDFLAGS: '-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib'

    steps:
      - name: Update PATH
        run: echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH

      - name: Show env
        run: env

      - name: Checkout
        uses: actions/checkout@v4

      - name: Show Clang++ version before brew install llvm
        run: clang++ --version

      - name: brew install llvm
        run: brew install llvm

      - name: Show Clang++ version after brew install llvm
        run: clang++ --version

      - name: Update CMake
        uses: jwlawson/actions-setup-cmake@v2.0

      - name: Run pip installs
        run: |
          python3 -m pip install --upgrade pip
          python3 -m pip install -r tests/requirements.txt
          python3 -m pip install numpy
          python3 -m pip install scipy

      - name: Show CMake version
        run: cmake --version

      - name: CMake Configure
        run: >
          cmake -S . -B .
          -DPYBIND11_WERROR=ON
          -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
          -DDOWNLOAD_CATCH=ON
          -DDOWNLOAD_EIGEN=ON
          -DCMAKE_CXX_COMPILER=clang++
          -DCMAKE_CXX_STANDARD=17
          -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

      - name: Build
        run: cmake --build . -j 2

      - name: Python tests
        run: cmake --build . --target pytest -j 2

      - name: C++ tests
        run: cmake --build . --target cpptest -j 2

      - name: Interface test
        run: cmake --build . --target test_cmake_build -j 2

      - name: CMake Configure - Exercise cmake -DPYBIND11_TEST_OVERRIDE
        run: >
          cmake -S . -B build_partial
          -DPYBIND11_WERROR=ON
          -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
          -DDOWNLOAD_CATCH=ON
          -DDOWNLOAD_EIGEN=ON
          -DCMAKE_CXX_COMPILER=clang++
          -DCMAKE_CXX_STANDARD=17
          -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
          "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"

      - name: Build - Exercise cmake -DPYBIND11_TEST_OVERRIDE
        run: cmake --build build_partial -j 2

      - name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
        run: cmake --build build_partial --target pytest -j 2

      - name: Clean directory
        run: git clean -fdx
