name: Windows

on: [push, pull_request]

jobs:
  build-msvc:
    name: ${{matrix.msvc}}-${{matrix.arch}}-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}}
    runs-on: ${{matrix.os}}
    permissions:
      actions: read
      contents: read
      security-events: write
    defaults:
      run:
        shell: powershell
    env:
      CL: /MP
      CXXFLAGS: /WX /permissive-
    strategy:
      fail-fast: true
      matrix:
        arch: [Win32, x64]
        build_type: [Debug, Release]
        lib: [shared, static]
        msvc: [VS-16-2019, VS-17-2022]
        std: [14, 17, 20, 23]
        include:
          - msvc: VS-16-2019
            os: windows-2019
            generator: 'Visual Studio 16 2019'
          - msvc: VS-17-2022
            os: windows-2022
            generator: 'Visual Studio 17 2022'

    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: cpp

      - name: Cache GTest
        id: cache-gtest
        uses: actions/cache@v4
        with:
          path: gtest/
          key: ${{runner.os}}-gtest-1.11-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}

      - name: Download GTest
        if: steps.cache-gtest.outputs.cache-hit != 'true'
        run: |
          (New-Object System.Net.WebClient).DownloadFile("https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip", "release-1.11.0.zip")
          Expand-Archive release-1.11.0.zip .

      - name: Build GTest
        if: steps.cache-gtest.outputs.cache-hit != 'true'
        run: |
          cmake -S googletest-release-1.11.0 -B build-googletest `
                -A ${{matrix.arch}} `
                -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} `
                -Dgtest_force_shared_crt=ON `
                -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gtest
          cmake --build build-googletest `
                --config ${{matrix.build_type}} `
                --target install

      - name: Cache gflags
        id: cache-gflags
        uses: actions/cache@v4
        with:
          path: gflags/
          key: ${{runner.os}}-gflags-2.2.2-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}

      - name: Download gflags
        if: steps.cache-gflags.outputs.cache-hit != 'true'
        run: |
          (New-Object System.Net.WebClient).DownloadFile("https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.zip", "v2.2.2.zip")
          Expand-Archive v2.2.2.zip .

      - name: Build gflags
        if: steps.cache-gflags.outputs.cache-hit != 'true'
        run: |
          cmake -S gflags-2.2.2 -B build-gflags `
                -A ${{matrix.arch}} `
                -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} `
                -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gflags
          cmake --build build-gflags `
                --config ${{matrix.build_type}} `
                --target install

      - name: Setup Environment
        run: |
          echo "GTest_ROOT=$((Get-Item .).FullName)/gtest" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
          echo "gflags_ROOT=$((Get-Item .).FullName)/gflags" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
          echo "${{github.workspace}}/gtest/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
          echo "${{github.workspace}}/gflags/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

      - name: Setup Release Environment
        if: matrix.build_type != 'Debug'
        run: |
          echo "CXXFLAGS=/Zi ${{env.CXXFLAGS}}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

      - name: Configure
        run: |
          cmake -S . -B build_${{matrix.build_type}} `
                -A ${{matrix.arch}} `
                -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} `
                -DCMAKE_CXX_EXTENSIONS=OFF `
                -DCMAKE_CXX_STANDARD=${{matrix.std}} `
                -DCMAKE_CXX_STANDARD_REQUIRED=ON `
                -DCMAKE_EXE_LINKER_FLAGS='/NOIMPLIB' `
                -DCMAKE_EXE_LINKER_FLAGS_RELEASE='/INCREMENTAL:NO /DEBUG' `
                -DCMAKE_INSTALL_PREFIX:PATH=./install `
                -DCMAKE_MSVC_RUNTIME_LIBRARY='MultiThreaded$<$<CONFIG:Debug>:Debug>DLL' `
                -G "${{matrix.generator}}" `
                -Werror

      - name: Build
        run: cmake --build build_${{matrix.build_type}} `
                   --config ${{matrix.build_type}}

      - name: Test
        env:
          CTEST_OUTPUT_ON_FAILURE: 1
        run: |
          cmake --build build_${{matrix.build_type}}/ `
                --config ${{matrix.build_type}} `
                --target RUN_TESTS

      - name: Install
        run: |
          cmake --build build_${{matrix.build_type}}/ `
                --config ${{matrix.build_type}} `
                --target install

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: language:cpp

  build-mingw:
    name: ${{matrix.sys}}-${{matrix.env}}-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}}
    runs-on: windows-2022
    permissions:
      actions: read
      contents: read
      security-events: write
    env:
      BUILDDIR: 'build_${{matrix.sys}}-${{matrix.env}}-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}}'
    defaults:
      run:
        shell: msys2 {0}
    strategy:
      fail-fast: true
      matrix:
        build_type: [Debug]
        lib: [shared, static]
        std: [14, 17, 20, 23]
        sys: [mingw32, mingw64]
        include:
         - sys: mingw32
           env: i686
         - sys: mingw64
           env: x86_64

    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: cpp

      - uses: msys2/setup-msys2@v2
        with:
          msystem: ${{matrix.sys}}
          install: >-
            mingw-w64-${{matrix.env}}-cmake
            mingw-w64-${{matrix.env}}-gcc
            mingw-w64-${{matrix.env}}-gflags
            mingw-w64-${{matrix.env}}-ninja
            mingw-w64-${{matrix.env}}-python-jinja
            mingw-w64-${{matrix.env}}-python-lxml
            mingw-w64-${{matrix.env}}-python-pip
            mingw-w64-${{matrix.env}}-python-pygments

      - name: Setup Coverage Dependencies
        if: matrix.build_type == 'Debug'
        run: |
          pip install 'gcovr==7.0'

      - name: Setup Environment
        if: matrix.build_type == 'Debug'
        run: |
          echo 'CXXFLAGS=--coverage ${{env.CXXFLAGS}}' >> $GITHUB_ENV

      - name: Configure
        env:
          CXXFLAGS: -Wall -Wextra -Wpedantic -Wsign-conversion -Wtautological-compare -Wformat-nonliteral -Wundef -Werror ${{env.CXXFLAGS}}
        run: |
          cmake -S . -B build_${{matrix.build_type}}/ \
                -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} \
                -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
                -DCMAKE_CXX_EXTENSIONS=OFF \
                -DCMAKE_CXX_STANDARD=${{matrix.std}} \
                -DCMAKE_CXX_STANDARD_REQUIRED=ON \
                -DCMAKE_INSTALL_PREFIX:PATH=./install \
                -G Ninja \
                -Werror

      - name: Build
        run: |
          cmake --build build_${{matrix.build_type}}/ --config ${{matrix.build_type}}

      - name: Test
        env:
          CTEST_OUTPUT_ON_FAILURE: 1
        run: |
          cmake --build build_${{matrix.build_type}}/ --config ${{matrix.build_type}} \
                --target test

      - name: Install
        run: |
          cmake --build build_${{matrix.build_type}}/ \
                --config ${{matrix.build_type}} \
                --target install

      - name: Generate Coverage
        if: matrix.build_type == 'Debug'
        run: |
          cd build_${{matrix.build_type}}
          gcovr -r .. . -s --cobertura coverage.xml

      - name: Upload Coverage to Codecov
        if: matrix.build_type == 'Debug'
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: build_${{matrix.build_type}}/coverage.xml
          fail_ci_if_error: true
          verbose: true

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
