name: CI

on:
  pull_request:
  push:
    branches:
      - master
      - develop
      - feature/**

env:
  GIT_FETCH_JOBS: 8
  NET_RETRY_COUNT: 5
  DEFAULT_BUILD_VARIANT: debug,release

jobs:
  posix:
    defaults:
      run:
        shell: bash

    strategy:
      fail-fast: false
      matrix:
        include:
          #------------------
          # Linux, gcc
          #------------------
          # Linux, gcc-4.6
          - toolset: gcc-4.6
            cxxstd: "03,0x"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - g++-4.6-multilib
            sources:
              - "ppa:ubuntu-toolchain-r/test"
          # Linux, gcc-4.7
          - toolset: gcc-4.7
            cxxstd: "03,11"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - g++-4.7-multilib
          # Linux, gcc-4.8
          - toolset: gcc-4.8
            cxxstd: "03,11"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - g++-4.8-multilib
          # Linux, gcc-4.9
          - toolset: gcc-4.9
            cxxstd: "03,11"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - g++-4.9-multilib
          # Linux, gcc-5
          - toolset: gcc-5
            cxxstd: "03,11,14,1z"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - g++-5-multilib
          # Linux, gcc-6
          - toolset: gcc-6
            cxxstd: "03,11,14,1z"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - g++-6-multilib
          # Linux, gcc-7
          - toolset: gcc-7
            cxxstd: "03,11,14,17"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - g++-7-multilib
          # Linux, gcc-8
          - toolset: gcc-8
            cxxstd: "03,11,14,17,2a"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - g++-8-multilib
          # Linux, gcc-9
          - toolset: gcc-9
            cxxstd: "03,11,14,17,2a"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:20.04
            install:
              - g++-9-multilib
          # Linux, gcc-10
          - toolset: gcc-10
            cxxstd: "03,11,14,17,20"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:20.04
            install:
              - g++-10-multilib
          # Linux, gcc-11
          - toolset: gcc-11
            cxxstd: "03,11,14,17,20,23"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - g++-11-multilib
          # Linux, gcc-12
          - toolset: gcc-12
            cxxstd: "03,11,14,17,20,23"
            address-model: 32,64
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - g++-12-multilib
          # Linux, gcc-13
          - toolset: gcc-13
            cxxstd: "11,14,17,20,2b"
            os: ubuntu-latest
            container: ubuntu:24.04
            install: g++-13-multilib
            address-model: 32,64
          # Linux, gcc-14
          - toolset: gcc-14
            cxxstd: "11,14,17,20,2b"
            os: ubuntu-latest
            container: ubuntu:24.04
            install: g++-14-multilib
            address-model: 32,64
          # Linux, gcc-12 UBSAN
          - name: UBSAN
            toolset: gcc-12
            cxxstd: "03,11,14,17,20,23"
            ubsan: 1
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - g++-12
          #------------------
          # Linux, clang
          #------------------
          # Linux, clang-3.5
          - toolset: clang
            compiler: clang++-3.5
            cxxstd: "03,11"
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - clang-3.5
          # Linux, clang-3.6
          - toolset: clang
            compiler: clang++-3.6
            cxxstd: "03,11,14"
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - clang-3.6
          # Linux, clang-3.7
          - toolset: clang
            compiler: clang++-3.7
            cxxstd: "03,11,14"
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - clang-3.7
          # Linux, clang-3.8
          - toolset: clang
            compiler: clang++-3.8
            cxxstd: "03,11,14"
            os: ubuntu-latest
            container: ubuntu:16.04
            install:
              - clang-3.8
          # Linux, clang-3.9
          - toolset: clang
            compiler: clang++-3.9
            cxxstd: "03,11,14"
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - clang-3.9
          # Linux, clang-4.0
          - toolset: clang
            compiler: clang++-4.0
            cxxstd: "03,11,14"
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - clang-4.0
          # Linux, clang-5.0
          - toolset: clang
            compiler: clang++-5.0
            cxxstd: "03,11,14,1z"
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - clang-5.0
          # Linux, clang-6.0
          - toolset: clang
            compiler: clang++-6.0
            cxxstd: "03,11,14,17"
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - clang-6.0
          # Linux, clang-7
          - toolset: clang
            compiler: clang++-7
            cxxstd: "03,11,14,17"
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - clang-7
          # Linux, clang-8
          # Note: clang-8 does not fully support C++20, so it is not compatible with libstdc++-8 in this mode
          - toolset: clang
            compiler: clang++-8
            cxxstd: "03,11,14,17,2a"
            os: ubuntu-latest
            container: ubuntu:18.04
            install:
              - clang-8
              - g++-7
            gcc_toolchain: 7
          # Linux, clang-9
          - toolset: clang
            compiler: clang++-9
            cxxstd: "03,11,14,17,2a"
            os: ubuntu-latest
            container: ubuntu:20.04
            install:
              - clang-9
          # Linux, clang-10
          - toolset: clang
            compiler: clang++-10
            cxxstd: "03,11,14,17,20"
            os: ubuntu-latest
            container: ubuntu:20.04
            install:
              - clang-10
          # Linux, clang-11
          - toolset: clang
            compiler: clang++-11
            cxxstd: "03,11,14,17,20"
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - clang-11
          # Linux, clang-12
          - toolset: clang
            compiler: clang++-12
            cxxstd: "03,11,14,17,20"
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - clang-12
          # Linux, clang-13
          - toolset: clang
            compiler: clang++-13
            cxxstd: "03,11,14,17,20"
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - clang-13
          # Linux, clang-14
          - toolset: clang
            compiler: clang++-14
            cxxstd: "03,11,14,17,20"
            os: ubuntu-latest
            container: ubuntu:22.04
            install:
              - clang-14
          # Linux, clang-15
          - toolset: clang
            compiler: clang++-15
            cxxstd: "03,11,14,17,20"
            container: ubuntu:22.04
            os: ubuntu-latest
            install: clang-15
          # Linux, clang-16
          - toolset: clang
            compiler: clang++-16
            cxxstd: "03,11,14,17,20,2b"
            container: ubuntu:24.04
            os: ubuntu-latest
            install: clang-16
          # Linux, clang-17
          - toolset: clang
            compiler: clang++-17
            cxxstd: "03,11,14,17,20,2b"
            container: ubuntu:24.04
            os: ubuntu-latest
            install: clang-17
          # Linux, clang-18
          - toolset: clang
            compiler: clang++-18
            cxxstd: "03,11,14,17,20,2b"
            container: ubuntu:24.04
            os: ubuntu-latest
            install: clang-18
          # Linux, clang-19
          - toolset: clang
            compiler: clang++-19
            cxxstd: "03,11,14,17,20,2b"
            container: ubuntu:24.04
            os: ubuntu-latest
            install: clang-19
          # Linux, clang-15 libc++
          - toolset: clang
            compiler: clang++-15
            cxxstd: "03,11,14,17,20,2b"
            os: ubuntu-22.04
            install:
              - clang-15
              - libc++-15-dev
              - libc++abi-15-dev
            cxxflags: -stdlib=libc++
            linkflags: -stdlib=libc++
          # Linux, clang-18 libc++
          - toolset: clang
            compiler: clang++-18
            cxxstd: "11,14,17,20,2b"
            os: ubuntu-24.04
            install:
              - clang-18
              - libc++-18-dev
              - libc++abi-18-dev
            cxxflags: -stdlib=libc++
            linkflags: -stdlib=libc++
          # Linux, clang-14 libc++, ubsan
          - name: UBSAN
            toolset: clang
            compiler: clang++-14
            cxxstd: "03,11,14,17,20"
            cxxflags: -stdlib=libc++
            linkflags: -stdlib=libc++
            ubsan: 1
            os: ubuntu-22.04
            install:
              - clang-14
              - libc++-14-dev
              - libc++abi-14-dev
          # Linux, clang-18 libc++, ubsan
          - name: UBSAN
            toolset: clang
            compiler: clang++-18
            cxxstd: "11,14,17,20"
            cxxflags: -stdlib=libc++
            linkflags: -stdlib=libc++
            ubsan: 1
            os: ubuntu-24.04
            install:
              - clang-18
              - libc++-18-dev
              - libc++abi-18-dev
          #------------------
          # MacOS, clang
          #------------------
          - toolset: clang
            cxxstd: "11,14,17,20,2b"
            os: macos-13
          - toolset: clang
            cxxstd: "11,14,17,20,2b"
            os: macos-14
          - toolset: clang
            cxxstd: "11,14,17,20,2b"
            os: macos-15

    timeout-minutes: 180
    runs-on: ${{matrix.os}}
    container: ${{matrix.container}}

    steps:
      - name: Setup environment
        run: |
            if [ -f "/etc/debian_version" ]
            then
                echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV
                export DEBIAN_FRONTEND=noninteractive
            fi
            if [ -n "${{matrix.container}}" ]
            then
                echo "GHA_CONTAINER=${{matrix.container}}" >> $GITHUB_ENV
                if [ -f "/etc/debian_version" ]
                then
                    apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
                    if [ "$(apt-cache search "^python-is-python3$" | wc -l)" -ne 0 ]
                    then
                        PYTHON_PACKAGE="python-is-python3"
                    else
                        PYTHON_PACKAGE="python"
                    fi
                    apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common tzdata wget curl apt-transport-https ca-certificates make build-essential g++ $PYTHON_PACKAGE python3 perl git cmake
                fi
            fi
            git config --global pack.threads 0

      - name: Install packages
        if: matrix.install
        run: |
            declare -a SOURCE_KEYS SOURCES
            if [ -n "${{join(matrix.source_keys, ' ')}}" ]
            then
                SOURCE_KEYS=("${{join(matrix.source_keys, '" "')}}")
            fi
            if [ -n "${{join(matrix.sources, ' ')}}" ]
            then
                SOURCES=("${{join(matrix.sources, '" "')}}")
            fi
            for key in "${SOURCE_KEYS[@]}"
            do
                for i in {1..$NET_RETRY_COUNT}
                do
                    echo "Adding key: $key"
                    wget -O - "$key" | sudo apt-key add - && break || sleep 2
                done
            done
            if [ ${#SOURCES[@]} -gt 0 ]
            then
                APT_ADD_REPO_COMMON_ARGS=("-y")
                APT_ADD_REPO_SUPPORTED_ARGS="$(apt-add-repository --help | perl -ne 'if (/^\s*-n/) { print "n"; } elsif (/^\s*-P/) { print "P"; } elsif (/^\s*-S/) { print "S"; } elsif (/^\s*-U/) { print "U"; }')"
                if [ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*n*}" ]
                then
                    APT_ADD_REPO_COMMON_ARGS+=("-n")
                fi
                APT_ADD_REPO_HAS_SOURCE_ARGS="$([ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*P*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*S*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*U*}" ] && echo 1 || echo 0)"
                for source in "${SOURCES[@]}"
                do
                    for i in {1..$NET_RETRY_COUNT}
                    do
                        APT_ADD_REPO_ARGS=("${APT_ADD_REPO_COMMON_ARGS[@]}")
                        if [ $APT_ADD_REPO_HAS_SOURCE_ARGS -ne 0 ]
                        then
                            case "$source" in
                            "ppa:"*)
                                APT_ADD_REPO_ARGS+=("-P")
                                ;;
                            "deb "*)
                                APT_ADD_REPO_ARGS+=("-S")
                                ;;
                            *)
                                APT_ADD_REPO_ARGS+=("-U")
                                ;;
                            esac
                        fi
                        APT_ADD_REPO_ARGS+=("$source")
                        echo "apt-add-repository ${APT_ADD_REPO_ARGS[@]}"
                        sudo -E apt-add-repository "${APT_ADD_REPO_ARGS[@]}" && break || sleep 2
                    done
                done
            fi
            sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
            sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y ${{join(matrix.install, ' ')}}

      - name: Setup GCC Toolchain
        if: matrix.gcc_toolchain
        run: |
            GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain"
            echo "GCC_TOOLCHAIN_ROOT=\"$GCC_TOOLCHAIN_ROOT\"" >> $GITHUB_ENV
            MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
            mkdir -p "$GCC_TOOLCHAIN_ROOT"
            ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include"
            ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin"
            mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET"
            ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}"

      - name: Setup Boost
        run: |
            echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
            LIBRARY=${GITHUB_REPOSITORY#*/}
            echo LIBRARY: $LIBRARY
            echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
            echo GITHUB_BASE_REF: $GITHUB_BASE_REF
            echo GITHUB_REF: $GITHUB_REF
            REF=${GITHUB_BASE_REF:-$GITHUB_REF}
            REF=${REF#refs/heads/}
            echo REF: $REF
            BOOST_BRANCH=develop && [ "$REF" = "master" ] && BOOST_BRANCH=master || true
            echo BOOST_BRANCH: $BOOST_BRANCH
            BUILD_JOBS=$((nproc || sysctl -n hw.ncpu) 2> /dev/null)
            echo "BUILD_JOBS=$BUILD_JOBS" >> $GITHUB_ENV
            echo "CMAKE_BUILD_PARALLEL_LEVEL=$BUILD_JOBS" >> $GITHUB_ENV
            DEPINST_ARGS=()
            GIT_VERSION="$(git --version | sed -e 's/git version //')"
            GIT_HAS_JOBS=1
            if [ -f "/etc/debian_version" ]
            then
                if $(dpkg --compare-versions "$GIT_VERSION" lt 2.8.0)
                then
                    GIT_HAS_JOBS=0
                fi
            else
                declare -a GIT_VER=(${GIT_VERSION//./ })
                declare -a GIT_MIN_VER=(2 8 0)
                for ((i=0; i<${#GIT_VER[@]}; i++))
                do
                    if [ -z "${GIT_MIN_VER[i]}" ]
                    then
                        GIT_MIN_VER[i]=0
                    fi
                    if [ "${GIT_VER[i]}" -lt "${GIT_MIN_VER[i]}" ]
                    then
                        GIT_HAS_JOBS=0
                        break
                    fi
                done
            fi
            if [ "$GIT_HAS_JOBS" -ne 0 ]
            then
                DEPINST_ARGS+=("--git_args" "--jobs $GIT_FETCH_JOBS")
            fi
            mkdir -p snapshot
            cd snapshot
            echo "Downloading library snapshot: https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz"
            curl -L --retry "$NET_RETRY_COUNT" -o "${LIBRARY}-${GITHUB_SHA}.tar.gz" "https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz"
            tar -xf "${LIBRARY}-${GITHUB_SHA}.tar.gz"
            if [ ! -d "${LIBRARY}-${GITHUB_SHA}" ]
            then
                echo "Library snapshot does not contain the library directory ${LIBRARY}-${GITHUB_SHA}:"
                ls -la
                exit 1
            fi
            rm -f "${LIBRARY}-${GITHUB_SHA}.tar.gz"
            cd ..
            git clone -b "$BOOST_BRANCH" --depth 1 "https://github.com/boostorg/boost.git" "boost-root"
            cd boost-root
            mkdir -p libs
            rm -rf "libs/$LIBRARY"
            mv -f "../snapshot/${LIBRARY}-${GITHUB_SHA}" "libs/$LIBRARY"
            rm -rf "../snapshot"
            git submodule update --init tools/boostdep
            DEPINST_ARGS+=("$LIBRARY")
            python tools/boostdep/depinst/depinst.py "${DEPINST_ARGS[@]}"
            ./bootstrap.sh
            ./b2 headers
            if [ -n "${{matrix.compiler}}" -o -n "$GCC_TOOLCHAIN_ROOT" ]
            then
                echo -n "using ${{matrix.toolset}} : : ${{matrix.compiler}}" > ~/user-config.jam
                if [ -n "$GCC_TOOLCHAIN_ROOT" ]
                then
                    echo -n " : <compileflags>\"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\" <linkflags>\"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\"" >> ~/user-config.jam
                fi
                echo " ;" >> ~/user-config.jam
            fi

      - name: Run tests
        if: matrix.cmake_tests == ''
        run: |
            cd boost-root
            B2_ARGS=("-j" "$BUILD_JOBS" "toolset=${{matrix.toolset}}" "cxxstd=${{matrix.cxxstd}}")
            if [ -n "${{matrix.build_variant}}" ]
            then
                B2_ARGS+=("variant=${{matrix.build_variant}}")
            else
                B2_ARGS+=("variant=$DEFAULT_BUILD_VARIANT")
            fi
            if [ -n "${{matrix.threading}}" ]
            then
                B2_ARGS+=("threading=${{matrix.threading}}")
            fi
            if [ -n "${{matrix.ubsan}}" ]
            then
                export UBSAN_OPTIONS="print_stacktrace=1"
                B2_ARGS+=("undefined-sanitizer=norecover" "linkflags=-fuse-ld=gold" "define=UBSAN=1" "debug-symbols=on" "visibility=global")
            fi
            if [ -n "${{matrix.cxxflags}}" ]
            then
                B2_ARGS+=("cxxflags=${{matrix.cxxflags}}")
            fi
            if [ -n "${{matrix.address-model}}" ]
            then
                B2_ARGS+=("address-model=${{matrix.address-model}}")
            fi
            if [ -n "${{matrix.linkflags}}" ]
            then
                B2_ARGS+=("linkflags=${{matrix.linkflags}}")
            fi
            B2_ARGS+=("libs/$LIBRARY/test")
            ./b2 "${B2_ARGS[@]}"

  windows:
    strategy:
      fail-fast: false
      matrix:
        include:
          - toolset: msvc-14.0
            cxxstd: "14"
            addrmd: 32,64
            os: windows-2019
          - toolset: msvc-14.2
            cxxstd: "14,17,20,latest"
            addrmd: 32,64
            os: windows-2019
          - toolset: msvc-14.3
            cxxstd: "14,17,20,latest"
            addrmd: 32,64
            os: windows-2022
          - toolset: clang-win
            cxxstd: "14,17,latest"
            addrmd: 64
            os: windows-2022
          - toolset: gcc
            cxxstd: "03,11,14,17,2a"
            addrmd: 64
            os: windows-2019

    timeout-minutes: 180
    runs-on: ${{matrix.os}}

    steps:
      - name: Setup Boost
        shell: cmd
        run: |
            echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
            for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
            echo LIBRARY: %LIBRARY%
            echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
            echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
            echo GITHUB_REF: %GITHUB_REF%
            if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
            set BOOST_BRANCH=develop
            for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
            echo BOOST_BRANCH: %BOOST_BRANCH%
            mkdir snapshot
            cd snapshot
            echo Downloading library snapshot: https://github.com/%GITHUB_REPOSITORY%/archive/%GITHUB_SHA%.zip
            curl -L --retry %NET_RETRY_COUNT% -o "%LIBRARY%-%GITHUB_SHA%.zip" "https://github.com/%GITHUB_REPOSITORY%/archive/%GITHUB_SHA%.zip"
            tar -xf "%LIBRARY%-%GITHUB_SHA%.zip"
            if not exist "%LIBRARY%-%GITHUB_SHA%\" (
                echo Library snapshot does not contain the library directory %LIBRARY%-%GITHUB_SHA%:
                dir
                exit /b 1
            )
            del /f "%LIBRARY%-%GITHUB_SHA%.zip"
            cd ..
            git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
            cd boost-root
            if not exist "libs\" mkdir libs
            if exist "libs\%LIBRARY%\" rmdir /s /q "libs\%LIBRARY%"
            move /Y "..\snapshot\%LIBRARY%-%GITHUB_SHA%" "libs\%LIBRARY%"
            rmdir /s /q "..\snapshot"
            git submodule update --init tools/boostdep
            python tools/boostdep/depinst/depinst.py --git_args "--jobs %GIT_FETCH_JOBS%" %LIBRARY%
            cmd /c bootstrap
            b2 -d0 headers

      - name: Run tests
        shell: cmd
        run: |
            cd boost-root
            b2 -j %NUMBER_OF_PROCESSORS% libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker

