name: windows

on:
  push:
    paths-ignore:
      - '.agents/**'
      - '.github/workflows/linux.yml'
      - '.github/workflows/macos.yml'
      - '.github/workflows/cmake.yml'
      - 'AGENTS.md'
      - 'CHANGES*'
      - 'Doc/**'
      - 'Tools/CI-linux-*'
      - 'Tools/GHA-linux-*'
  pull_request:
    branches: master
    paths-ignore:
      - '.agents/**'
      - '.github/workflows/linux.yml'
      - '.github/workflows/macos.yml'
      - '.github/workflows/cmake.yml'
      - 'AGENTS.md'
      - 'CHANGES*'
      - 'Doc/**'
      - 'Tools/CI-linux-*'
      - 'Tools/GHA-linux-*'

permissions:
  contents: read

jobs:
  # Code format checks, see https://github.com/marketplace/actions/clang-format-github-action
  # The clang-format version used is slightly different for each OS workflow file
  # This job gates the main build job so must succeed before the main build job is run
  clang-format:
    runs-on: ubuntu-latest
    name: clang-format 21

    steps:
    - name: Checkout
      uses: actions/checkout@v6
      with:
        show-progress: false
        persist-credentials: false

    - name: Check with clang-format
      uses: RafikFarhad/clang-format-github-action@v6.0.1
      with:
        sources: "Source/*/*.c,Source/*/*.cxx,Source/*/*.h"
        style: file

  # This action build using MSYS2 environment
  # We use two compilers C/C++:
  # - Microsoft Visual Studio compiler.
  # - MinGW-w64 GCC compiler with MSVCRT or UCRT C standard library.
  # We use 2 package managers
  # - NuGet packages are compatible with Visual Studio compiler.
  # - MSYS2 pacman with MSYS2 packages and MING-w64 packages.
  #
  # We try to use already installed software on GitHub windows
  #  and install only missed software.
  # We use different versions of installed languages by using
  #  proper envirounment.
  build:
    # When continue-on-error is true for an individual build,
    # that build can fail (it'll show red),
    # but it won't fail the overall tests
    continue-on-error: ${{ matrix.continue-on-error || false }}

    # https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md
    runs-on: ${{ matrix.os || 'windows-2025' }}

    # The name of the test follow the tested language
    name: >
      ${{ matrix.SWIGLANG }} ${{ matrix.VER }}
      ${{ matrix.SWIG_FEATURES }}
      ${{ matrix.COMPILER || 'msvc' }}
      ${{ matrix.CPPSTD }}
      ${{ matrix.CSTD }}
      ${{ matrix.MSYS2_ENV }}
      ${{ matrix.os }}
      ${{ matrix.continue-on-error && '(can fail)' }}

    needs: [clang-format]

    strategy:
      matrix:
        include:
        - SWIGLANG: csharp
          INSTALL: 'true'
        - SWIGLANG: csharp
          os: windows-2025-vs2026
        - SWIGLANG: csharp
          INSTALL: 'true'
          COMPILER: gcc
        - SWIGLANG: csharp
          COMPILER: gcc
          MSYS2_ENV: ucrt64
        - SWIGLANG: java
          VER: 8
        - SWIGLANG: java
          VER: 17
        - SWIGLANG: java
          VER: 21
        - SWIGLANG: java
          VER: 25
        - SWIGLANG: java
          COMPILER: gcc
          CSTD: gnu99
          VER: 8
        - SWIGLANG: java
          COMPILER: gcc
          VER: 11
        - SWIGLANG: java
          COMPILER: gcc
          VER: 25
        - SWIGLANG: java
          COMPILER: gcc
          MSYS2_ENV: ucrt64
        - SWIGLANG: python
          VER: '3.10'
          SWIG_FEATURES: -builtin
        - SWIGLANG: python
          VER: '3.13'
        - SWIGLANG: python
          VER: '3.14'
        - SWIGLANG: python
          COMPILER: gcc
          MSYS2_ENV: ucrt64
# To fix - swig fails to link, see job failure: https://github.com/swig/swig/actions/runs/27424393216/job/81058274348
#       - SWIGLANG: ruby
#         COMPILER: gcc
#         MSYS2_ENV: ucrt64
        - SWIGLANG: ruby
          VER: '3.2'
          COMPILER: gcc
          CPPSTD: c++11
          MSYS2_ENV: ucrt64
        - SWIGLANG: ruby
          VER: '3.3'
          COMPILER: gcc
          MSYS2_ENV: ucrt64
        - SWIGLANG: go
          VER: '1.25'
          COMPILER: gcc
          CSTD: gnu11
        - SWIGLANG: go
          COMPILER: gcc
          CSTD: gnu11
          MSYS2_ENV: ucrt64
      # Run all of them, as opposed to aborting when one fails
      fail-fast: false

    env:
      CFLAGS: '-O2'
      CXXFLAGS: '-O2'
      CCCL_OPTIONS: '--cccl-muffle /W3 /EHsc'
      PCRE2_CCCL_LD: '-lpcre2-8-static --cccl-link /NODEFAULTLIB:MSVCRT'
      CHECK_OPTIONS: 'CSHARPOPTIONS=-platform:x64'
      SWIGLANG: ${{ matrix.SWIGLANG }}
      VER: ${{ matrix.VER }}
      SWIG_FEATURES: ${{ matrix.SWIG_FEATURES }}
      MSYS2_ENV: ${{ matrix.MSYS2_ENV || 'mingw64' }}
      CSTD: ${{ matrix.CSTD }}
      CPPSTD: ${{ matrix.CPPSTD }}
      COMPILER: ${{ matrix.COMPILER }}
      OS: ${{ matrix.os }}
      INSTALL: ${{ matrix.INSTALL }}

# cl.exe:
#  https://learn.microsoft.com//cpp/build/reference/compiler-options
#  /EHc  extern "C" defaults to nothrow.
#  /EHs  Enable C++ exception handling (no SEH exceptions).
#  /W3   Warning level.
#  /WX   Treat warnings as errors.
#  https://learn.microsoft.com//cpp/build/reference/linker-options
#  cl.exe pass linker:
#  /VERBOSE:LIB        Outputs progress messages during the link process.
#  /NODEFAULTLIB:lib   Ignore library 'lib'

    steps:
    - name: Machine Info
      shell: powershell
      run: |
          systeminfo | findstr /B /C:"OS Name" /B /C:"OS Version"

    - name: Checkout
      uses: actions/checkout@v6
      with:
        show-progress: false
        persist-credentials: false

    - name: Install CCache
      uses: hendrikmuhs/ccache-action@v1.2.23
      with:
        key: ${{ matrix.os || 'windows-2025' }}-${{ matrix.COMPILER || 'msvc' }}

    - name: Install NuGet Packages
      if: ${{ env.COMPILER == '' }}
      shell: powershell
      run: |
          # 'nuget build for .NET: https://www.nuget.org/packages'
          nuget install PCRE2 -OutputDirectory C:\Tools
          nuget install boost -OutputDirectory C:\Tools

    # Set MSVC compilers path and environment variables.
    - name: Setup MSVC
      if: ${{ env.COMPILER == '' || env.SWIGLANG == 'csharp' }}
      uses: TheMrMilchmann/setup-msvc-dev@v4
      with:
        arch: x64

    - name: Prepare Environment
      shell: bash
      run: |
          set -x
          uname --all

          if [[ "$COMPILER" = "gcc" ]]; then
            # MinGW-w64 variant to use
            # See: https://www.msys2.org/docs/environments
            MSYS2_UENV="${MSYS2_ENV^^}" # Change to uppercase
            if [[ "$MSYSTEM" != "$MSYS2_UENV" ]]; then
                MSYSTEM="$MSYS2_UENV"
                # Change MSYS2 active environment
                echo "MSYSTEM=$MSYSTEM" >> $GITHUB_ENV
            fi
            case "$MSYSTEM" in
            MINGW64) # Use MinGW-w64 with old Microsoft Visual C++ Runtime
              # The matched MSYS2 packages prefix
              mingw_variant=mingw-w64-x86_64-
              ;;
            UCRT64) # Use MinGW-w64 with new Microsoft Universal C Runtime
              # The matched MSYS2 packages prefix
              mingw_variant=mingw-w64-ucrt-x86_64-
              ;;
            CLANG64) # Use LLVM/Clang
              # The matched MSYS2 packages prefix
              mingw_variant=mingw-w64-clang-x86_64-
              ;;
            esac
            # The matched MSYS2 active environment path prefix
            if [[ -z "$mingw_dir" ]]; then
              mingw_dir="/${MSYSTEM,,}" # Change to lowecase
            fi
            # Note regarding 'mingw_dir':
            #  The MSYSTEM=MSYS, which supports 'Cygwin', uses the '/usr' path prefix.
            #  We do not use it at the moment.

            # MinGW-w64 package list
            ming_pkgs+=(binutils make autotools pcre2)

            case "$SWIGLANG" in
            python)
              ming_pkgs+=(python)
              ;;
            ruby)
              if [[ -n "$VER" ]]; then
                ruby_dir="$(ls -d /c/hostedtoolcache/windows/Ruby/$VER.*)/x64/bin"
                RUBYDIR="$(cygpath -w $ruby_dir)"
                echo "$RUBYDIR" >> $GITHUB_PATH
              else
                ming_pkgs+=(ruby)
              fi
              ;;
            go)
              if [[ -n "$VER" ]]; then
                go_dir="$(ls -d /c/hostedtoolcache/windows/go/$VER.*)/x64/bin"
                GODIR="$(cygpath -w $go_dir)"
                echo "$GODIR" >> $GITHUB_PATH
              else
                MORE_MSYS_PKGS+=" ${mingw_variant}go"
                echo "GOROOT=$mingw_dir/lib/go" >> $GITHUB_ENV
              fi
              ;;
            esac

            # More MSYS2 packages to install
            MORE_MSYS_PKGS+=" base-devel"

            # Boost is needed with languages, we can skip for CCache tests
            if [[ -n "$SWIGLANG" ]]; then
              ming_pkgs+=(boost)
            fi
            # Add MinGW-w64 packages to MSYS2 packages list
            for n in "${ming_pkgs[@]}"; do
              MORE_MSYS_PKGS+=" ${mingw_variant}$n"
            done

            # MinGW-w64 pcre2
            echo "PCRE2_CFLAGS=-I$mingw_dir/include -DPCRE2_STATIC" >> $GITHUB_ENV
            echo "PCRE2_LIBS=-L$mingw_dir/lib -lpcre2-8" >> $GITHUB_ENV

            echo "MORE_MSYS_PKGS=$MORE_MSYS_PKGS" >> $GITHUB_ENV
            echo "BOOST_PATH=/c/msys64$mingw_dir" >> $GITHUB_ENV

            # Set MSYS2 GCC compiler location
            echo "$mingw_dir/bin" >> $GITHUB_PATH
          else
            # COMPILER: cccl wrapping MSVC
            curl --retry 15 -s -L https://github.com/swig/cccl/raw/cccl-1.4/cccl -o /usr/bin/cccl
            chmod +x /usr/bin/cccl
            /usr/bin/cccl --version
            cp -p /usr/bin/cccl /c/msys64/usr/bin/cccl

            # Using pcre2 installed with NuGet
            PCRE2_PATH=$(ls -d /c/tools/PCRE2*)
            echo "PCRE2_CFLAGS=-I$PCRE2_PATH/include -DPCRE2_STATIC" >> $GITHUB_ENV
            echo "PCRE2_LIBS=-L$PCRE2_PATH/lib $PCRE2_CCCL_LD" >> $GITHUB_ENV

            echo "CXX=/usr/bin/cccl" >> $GITHUB_ENV
            echo "CC=/usr/bin/cccl" >> $GITHUB_ENV
            echo "BOOST_PATH=$(ls -d /c/tools/boost*)/lib/native" >> $GITHUB_ENV

            if [[ -n "$VER" ]]; then
              case "$SWIGLANG" in
              python)
                python_dir="$(ls -d /c/hostedtoolcache/windows/Python/$VER.*)/x64"
                PYTHONDIR="$(cygpath -w $python_dir)"
                echo "$PYTHONDIR\\Script" >> $GITHUB_PATH
                echo "$PYTHONDIR" >> $GITHUB_PATH
                ;;
              esac
            fi
          fi # COMPILER

          # Extra tools for debugging, like hexdump, rev, hardlink.
          # See 'Files:' in https://packages.msys2.org/packages/util-linux
          # MORE_MSYS_PKGS+=" util-linux"

          if [[ "$SWIGLANG" = "java" ]] && [[ -n "$VER" ]]; then
            java_path="JAVA_HOME_${VER}_X64"
            echo "JAVA_HOME=${!java_path}" >> $GITHUB_ENV
          fi

          echo "SWIGJOBS=-j$NUMBER_OF_PROCESSORS" >> $GITHUB_ENV

          echo 'C:\msys64\usr\bin' >> $GITHUB_PATH

    - name: Install MSYS2 Packages
      shell: cmd
      run: |
          # We must run in `cmd`.
          # As the update will terminate all running bash terminals!
          # Including the terminal that may be used to execute the 'pacman' itself.
          rem 'MSYS2 uses MinGW-w64 https://packages.msys2.org/'
          pacman -Syu --noconfirm --needed
          if %ErrorLevel% NEQ 0 (exit 1)
          pacman -Syu --noconfirm --needed autoconf automake bison %MORE_MSYS_PKGS%
          if %ErrorLevel% NEQ 0 (exit 1)

    - name: Autoconf
      shell: bash
      run: |
          set -x
          if [[ -z "$COMPILER" ]]; then
            which cl.exe
            cl.exe /? 2>&1 | head -n1
          else
            which gcc
            gcc --version | head -n1
            which g++
            g++ --version | head -n1
          fi

          WITHLANG="$SWIGLANG"
          case "$SWIGLANG" in
          csharp)
            which csc.exe
            csc.exe /? | head -n1
            ;;
          python)
            which python.exe
            python -V
            WITHLANG=python3
            ;;
          ruby)
            which ruby.exe
            ruby -v
            ;;
          esac
          echo "WITHLANG=$WITHLANG" >> $GITHUB_ENV

          if [[ -z "$CSTD" ]]; then
            case "$CPPSTD" in
              c++11) CSTD=c11 ;;
              c++14) CSTD=c11 ;;
              c++17) CSTD=c17 ;;
              c++20) CSTD=c17 ;;
            esac
            if [[ -n "$CSTD" ]]; then
              echo "CSTD=$CSTD" >> $GITHUB_ENV
            fi
          fi
          if [[ -n "$CSTD" ]]; then
            echo "CFLAGS=$CFLAGS -std=$CSTD" >> $GITHUB_ENV
          fi
          if [[ -n "$CPPSTD" ]]; then
            echo "CXXFLAGS=$CXXFLAGS -std=$CPPSTD" >> $GITHUB_ENV
          fi

          make --version | head -n2

          ./autogen.sh

    - name: Configure
      shell: bash
      run: |
          set -x
          CONFIGOPTS+=(--disable-dependency-tracking)
          if [[ -n "$WITHLANG" ]]; then
            CONFIGOPTS+=(--with-boost="$BOOST_PATH")
            CONFIGOPTS+=(--without-alllang --with-$WITHLANG)
          fi
          if [[ $SWIGLANG = 'csharp' ]]; then
            CONFIGOPTS+=(--with-csharp-compiler="csc.exe")
          fi
          if [[ -z "$COMPILER" ]]; then
            # MSVC do not support C cache
            CONFIGOPTS+=(--disable-ccache)
          fi
          echo "${CONFIGOPTS[@]}"
          ./configure "${CONFIGOPTS[@]}"

    - name: Build
      shell: bash
      run: |
          make -s $SWIGJOBS

    # Test the Windows swig have a proper SWIG library path
    - name: Test swiglib
      shell: bash
      run: |
          echo "Output of ./swig.exe -swiglib"
          ./swig.exe -swiglib
          # Use first line with SWIG_LIB,
          # ignore second line which uses SWIG_LIB_WIN_UNIX
          win_swig_lib="$(./swig.exe -swiglib | head -1)"
          pwd_based_lib="$(cygpath -w "$PWD/Lib")"
          # path is based on executable location
          if ! [[ "$win_swig_lib" = "$pwd_based_lib" ]]; then
            echo "First test fail"
            exit 1
          fi
          # Ensure path change based on application path
          mkdir other_dir
          cp swig.exe other_dir/
          win_swig_lib2="$(./other_dir/swig.exe -swiglib | head -1)"
          pwd_based_lib2="$(cygpath -w "$PWD/other_dir/Lib")"
          if ! [[ "$win_swig_lib2" = "$pwd_based_lib2" ]]; then
            echo "Second test fail"
            exit 1
          fi

    - name: Test
      shell: bash
      run: |
          set -x
          ./swig.exe -version
          make check-$SWIGLANG-version
          make check-$SWIGLANG-enabled
          make -k check-$SWIGLANG-examples $SWIGJOBS $CHECK_OPTIONS
          make -k check-$SWIGLANG-test-suite $SWIGJOBS $CHECK_OPTIONS

    - name: Install
      if: ${{ env.INSTALL == 'true' }}
      shell: bash
      run: |
          make -s install > /dev/null

          which swig.exe
          swig.exe -version

          # TODO: Make install of ccache-swig do not work on Windows
          #if [[ "$COMPILER" = "gcc" ]]; then
          #  which ccache-swig.exe
          #  ccache-swig.exe -V
          #fi

    # The test by itself is not related to the installation.
    # We just want to save testing time :-)
    - name: Clean
      if: ${{ env.INSTALL == 'true' }}
      shell: bash
      run: |
          make check-maintainer-clean
