name: linux

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

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
  # These jobs gate the main build job so must succeed before the main build job is run
  # Oldest and newest supported versions are checked here in parallel, the others in the macos/windows files
  clang-format:
    runs-on: ubuntu-latest
    name: clang-format 18 # 18 is the oldest supported version

    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@v4.1
      with:
        sources: "Source/*/*.c,Source/*/*.cxx,Source/*/*.h"
        style: file

  clang-format-22:
    runs-on: ubuntu-latest
    name: clang-format 22 # 22 is the newest supported version

    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@v7
      with:
        sources: "Source/*/*.c,Source/*/*.cxx,Source/*/*.h"
        style: file

  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 build
    continue-on-error: ${{ matrix.continue-on-error || false }}

    runs-on: ${{ matrix.os || 'ubuntu-22.04' }}

    # By default, the name of the build is the language used and SWIG options, but matrix entries
    # can define the additional "desc" field with any additional information to include in the name.
    name: ${{ matrix.SWIGLANG || 'none' }} ${{ matrix.ENGINE}} ${{ matrix.VER }} ${{ matrix.PY_ABI_VER && format('abi={0}', matrix.PY_ABI_VER) }} ${{ matrix.SWIG_FEATURES }} ${{ (matrix.compiler || 'gcc') }}${{ matrix.GCC }} ${{ matrix.CPPSTD }} ${{ matrix.CSTD }} ${{ matrix.desc }} ${{ matrix.continue-on-error && '(can fail)' }}

    needs: [clang-format, clang-format-22]

    strategy:
      matrix:
        include:
        - SWIGLANG: ""
          CPPFLAGS: "-DDOH_POISON"
        - SWIGLANG: ""
          GCC: 9
          CPPSTD: c++98
          CSTD: c99
        - SWIGLANG: ""
          GCC: 9
        - SWIGLANG: ""
          GCC: 10
        - SWIGLANG: ""
          GCC: 11
        - SWIGLANG: ""
          GCC: 12
          os: ubuntu-22.04
        - SWIGLANG: ""
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: ""
          GCC: 14
          os: ubuntu-24.04
        - SWIGLANG: ""
          compiler: clang
          os: ubuntu-22.04
        - SWIGLANG: c
          CPPSTD: c++11
        - SWIGLANG: csharp
        - SWIGLANG: d
          VER: 'ldc'
          CPPSTD: c++14
        - SWIGLANG: d
          VER: 'gdmd'
          CPPSTD: c++11
        - SWIGLANG: d
          VER: '2.103.1'
          CPPSTD: c++17
        - SWIGLANG: go
          VER: '1.20'
          CSTD: gnu11
        - SWIGLANG: go
          VER: '1.22'
        - SWIGLANG: go
          VER: '1.23'
        - SWIGLANG: go
          VER: '1.24'
          CSTD: gnu99 # Go 1.24 doesn't build with -Wdeclaration-after-statement
        - SWIGLANG: go
          VER: '1.25'
        - SWIGLANG: guile
        - SWIGLANG: guile
          VER: '3.0'
        - SWIGLANG: java
        - SWIGLANG: javascript
          ENGINE: jsc
          VER: '4.0'
        - SWIGLANG: javascript
          ENGINE: node
          VER: '12'
          CPPSTD: c++11
        - SWIGLANG: lua
          VER: '5.1'
        - SWIGLANG: lua
          VER: '5.2'
        - SWIGLANG: lua
          VER: '5.3'
        - SWIGLANG: octave
          os: ubuntu-22.04 # Octave 6.4.0
          CPPSTD: c++11
        - SWIGLANG: octave
          VER: '8.4'
          os: ubuntu-24.04 # Octave 8.4.0
          CPPSTD: c++11
        - SWIGLANG: perl5
        - SWIGLANG: php
          VER: '8.0'
        - SWIGLANG: php
          VER: '8.1'
        - SWIGLANG: php
          VER: '8.2'
        - SWIGLANG: php
          VER: '8.3'
          CPPSTD: c++11 # C++98 not supported by PHP.
          os: ubuntu-24.04
        - SWIGLANG: php
          VER: '8.4'
          CPPSTD: c++11 # C++98 not supported by PHP.
        - SWIGLANG: php
          VER: '8.5'
          CPPSTD: c++11 # C++98 not supported by PHP.
        #- SWIGLANG: python
        #  VER: '3.5'
        #  os: ubuntu-20.04 # Python < 3.7 not available for 22.04.
        #- SWIGLANG: python
        #  VER: '3.6'
        #  os: ubuntu-20.04 # Python < 3.7 not available for 22.04.
        - SWIGLANG: python
          VER: '3.7'
        - SWIGLANG: python
          VER: '3.8'
        - SWIGLANG: python
          VER: '3.9'
        - SWIGLANG: python
          VER: '3.10'
        - SWIGLANG: python
          VER: '3.11'
        - SWIGLANG: python
          VER: '3.12'
          CSTD: gnu99
        - SWIGLANG: python
          VER: '3.13'
          CSTD: gnu99
        - SWIGLANG: python
          VER: '3.8'
          PY_ABI_VER: '3.5'
        - SWIGLANG: python
          VER: '3.13'
          PY_ABI_VER: '3.9'
          CSTD: gnu99
        - SWIGLANG: python
          VER: '3.14'
          PY_ABI_VER: '3.14'
          CSTD: gnu99
        - SWIGLANG: python
          VER: '3.14'
          CSTD: gnu99
        - SWIGLANG: python
          VER: '3.14'
          CSTD: gnu99
          SWIG_FEATURES: -typehints
        - SWIGLANG: python
          VER: '3.14'
          CSTD: gnu99
          SWIG_FEATURES: -pyi -typehints
        - SWIGLANG: python
          VER: '3.14'
          CSTD: gnu99
          SWIG_FEATURES: -builtin -typehints
        - SWIGLANG: python
          VER: '3.14'
          CSTD: gnu99
          SWIG_FEATURES: -builtin -pyi -typehints
        - SWIGLANG: python
          VER: '3.14-dbg'
          CSTD: gnu99
        - SWIGLANG: python
          VER: '3.14'
          CSTD: gnu99
          SWIG_FEATURES: -nogil  # Test that -nogil has no effect on non-free-threading Python
        - SWIGLANG: python
          VER: '3.14t'
          CSTD: gnu99
          SWIG_FEATURES: -nogil
        - SWIGLANG: python
          SWIG_FEATURES: -builtin
        - SWIGLANG: python
          VER: '3.8'
          SWIG_FEATURES: -builtin
        - SWIGLANG: python
          SWIG_FEATURES: -builtin -O
        - SWIGLANG: python
          VER: '3.14'
          SWIG_FEATURES: -builtin
        - SWIGLANG: python
          VER: '3.14'
          SWIG_FEATURES: -builtin -O
        - SWIGLANG: python
          VER: '3.15'
          continue-on-error: true
        - SWIGLANG: python
          VER: '3.15'
          SWIG_FEATURES: -builtin
          continue-on-error: true
        - SWIGLANG: r
          os: ubuntu-24.04
        - SWIGLANG: ruby
          VER: '2.0'
        - SWIGLANG: ruby
          VER: '2.1'
        - SWIGLANG: ruby
          VER: '2.2'
        - SWIGLANG: ruby
          VER: '2.3'
        - SWIGLANG: ruby
          VER: '2.5'
        - SWIGLANG: ruby
          VER: '2.7'
        - SWIGLANG: ruby
          VER: '3.0'
          CPPSTD: c++11
        - SWIGLANG: ruby
          VER: '3.1'
          CPPSTD: c++11
        - SWIGLANG: ruby
          CPPSTD: c++11
          VER: '3.2'
        - SWIGLANG: scilab
          VER: '5.5.2'
        - SWIGLANG: scilab
          VER: '6.0.2'
        - SWIGLANG: scilab
          os: ubuntu-22.04 # scilab 6.1.1
        - SWIGLANG: scilab
          VER: '2023.1.0'
        - SWIGLANG: scilab
          VER: '2024.1.0'
        - SWIGLANG: scilab
          VER: '2025.1.0'
        - SWIGLANG: tcl
        # c++11 testing
        - SWIGLANG: csharp
          CPPSTD: c++11
        - SWIGLANG: go
          VER: '1.20'
          CPPSTD: c++11
          CSTD: gnu11
        - SWIGLANG: guile
          CPPSTD: c++11
        - SWIGLANG: java
          CPPSTD: c++11
        - SWIGLANG: javascript
          ENGINE: jsc
          VER: '4.1'
          os: ubuntu-22.04
          CPPSTD: c++11
        - SWIGLANG: javascript
          ENGINE: node
          VER: '14'
          CPPSTD: c++11
        - SWIGLANG: lua
          VER: '5.2'
          CPPSTD: c++11
        - SWIGLANG: perl5
          CPPSTD: c++11
        - SWIGLANG: php
          CPPSTD: c++11
          CSTD: gnu11
        - SWIGLANG: python
          CPPSTD: c++11
        - SWIGLANG: r
          CPPSTD: c++11
        - SWIGLANG: ruby
          CPPSTD: c++11
          VER: '3.3'
        - SWIGLANG: ruby
          CPPSTD: c++11
          VER: '3.4'
        - SWIGLANG: ruby
          CPPSTD: c++11
          VER: '4.0'
        - SWIGLANG: ruby
          CPPSTD: c++11
        - SWIGLANG: scilab
          CPPSTD: c++11
        - SWIGLANG: tcl
          CPPSTD: c++11
        # c++14 testing
        - SWIGLANG: csharp
          CPPSTD: c++14
        - SWIGLANG: go
          VER: '1.20'
          CPPSTD: c++14
          CSTD: gnu11
        - SWIGLANG: guile
          CPPSTD: c++14
        - SWIGLANG: java
          CPPSTD: c++14
        - SWIGLANG: javascript
          ENGINE: node
          VER: '16'
          CPPSTD: c++14
        - SWIGLANG: lua
          VER: '5.2'
          CPPSTD: c++14
        - SWIGLANG: octave
          CPPSTD: c++14
        - SWIGLANG: perl5
          CPPSTD: c++14
        - SWIGLANG: php
          CPPSTD: c++14
          CSTD: gnu11
        - SWIGLANG: python
          CPPSTD: c++14
        - SWIGLANG: r
          CPPSTD: c++14
        - SWIGLANG: ruby
          CPPSTD: c++14
        - SWIGLANG: scilab
          CPPSTD: c++14
        - SWIGLANG: tcl
          CPPSTD: c++14
        # c++17 testing (using gcc13)
        - SWIGLANG: csharp
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: go
          VER: '1.20'
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
          CSTD: gnu17
        - SWIGLANG: guile
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: java
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: java
          VER: 8
        - SWIGLANG: java
          VER: 11
        - SWIGLANG: java
          VER: 17
        - SWIGLANG: java
          VER: 21
        - SWIGLANG: java
          VER: 25
        - SWIGLANG: javascript
          ENGINE: node
          VER: '18'
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: napi
          VER: '20'
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: lua
          VER: '5.4'
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: octave
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: perl5
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: php
          CPPSTD: c++17
          CSTD: gnu17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: python
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: r
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: ruby
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: scilab
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: tcl
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
        # c++20 testing (using gcc13)
        - SWIGLANG: c
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: python
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: jsc
          CPPSTD: c++20
          VER: '4.1'
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: napi
          VER: '22'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: napi
          VER: '24'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: napi
          VER: '26'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: node
          VER: '20'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: node
          VER: '22'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: node
          VER: '24'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: node
          VER: '26'
          CPPSTD: c++20
          GCC: 13
          os: ubuntu-24.04
        - SWIGLANG: javascript
          ENGINE: v8
          CPPSTD: c++14
          VER: '7.8' # oldest we can easily test
          os: ubuntu-22.04
        - SWIGLANG: javascript
          ENGINE: v8
          CPPSTD: c++14
          VER: '10.2'
          os: ubuntu-24.04
        - SWIGLANG: ocaml
          CPPSTD: c++17
          GCC: 13
          os: ubuntu-24.04
          continue-on-error: true
        - SWIGLANG: lua
          CPPSTD: c++20
          VER: '5.5'
          os: ubuntu-26.04
        - SWIGLANG: tcl
          CPPSTD: c++20
          VER: '9.0'
          os: ubuntu-26.04
      # Run all of them, as opposed to aborting when one fails
      fail-fast: false

    env:
      SWIGLANG: ${{ matrix.SWIGLANG }}
      compiler: ${{ matrix.compiler }}
      VER: ${{ matrix.VER }}
      ENGINE: ${{ matrix.ENGINE }}
      SWIG_FEATURES: ${{ matrix.SWIG_FEATURES }}
      GCC: ${{ matrix.GCC }}
      CSTD: ${{ matrix.CSTD }}
      CPPSTD: ${{ matrix.CPPSTD }}
      CPPFLAGS: ${{ matrix.CPPFLAGS }}
      PY_ABI_VER: ${{ matrix.PY_ABI_VER }}
      SKIP_CACHED_TOOLS: ${{ matrix.SKIP_CACHED_TOOLS }}

    steps:
    - name: Machine Info
      run: |
          echo "nproc..."
          nproc --all
          echo "uname..."
          uname --all
          echo "meminfo..."
          cat /proc/meminfo
          echo "lsb-release..."
          cat /etc/lsb-release

    - 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 || 'ubuntu-22.04' }}-${{ matrix.compiler || 'gcc' }}${{ matrix.GCC }}

# Uncomment to debug via ssh, see https://github.com/mxschmitt/action-tmate
#    - name: Setup tmate session
#      uses: mxschmitt/action-tmate@v3

    - name: Install Dependencies
      run: |
          set -x
          source $GITHUB_WORKSPACE/Tools/GHA-linux-install.sh

          cpu_count=$(nproc)
          if test $cpu_count -gt 1; then
            echo SWIGJOBS=-j$cpu_count >> $GITHUB_ENV
          fi

    - name: Configure
      run: |
          source $GITHUB_WORKSPACE/Tools/CI-linux-environment.sh
          set -x

          if test -z "$CSTD"; then
            case "$CPPSTD" in
              c++11) CSTD=c11 ;;
              c++14) CSTD=c11 ;;
              c++17) CSTD=c17 ;;
              c++20) CSTD=c17 ;;
            esac
            if test -n "$CSTD"; then echo "CSTD=$CSTD" >> $GITHUB_ENV; fi
          fi
          if test -n "$CPPSTD"; then
            CONFIGOPTS+=("CXXFLAGS=-std=$CPPSTD $CXXFLAGS")
            echo "Use C++ standard $CPPSTD"
          else
            CONFIGOPTS+=("--disable-cpp11-testing")
            $CXX -x c++ -dM -E - < /dev/null | grep __cplusplus
          fi
          if test -n "$CSTD"; then
            CONFIGOPTS+=("CFLAGS=-std=$CSTD $CFLAGS")
            echo "Use C standard $CSTD"
          else
            $CC -dM -E - < /dev/null | grep __STDC_VERSION__
          fi
          if test -n "$SWIGLANG"; then CONFIGOPTS+=(--without-alllang --with-$WITHLANG); fi
          echo "${CONFIGOPTS[@]}"
          ./autogen.sh && mkdir -p build/build && cd build/build && ../../configure "${CONFIGOPTS[@]}"

    - name: Build
      working-directory: build/build
      run: |
          set -x
          make -s $SWIGJOBS
          ./swig -version && ./swig -pcreversion

    - name: Test
      working-directory: build/build
      run: |
          source $GITHUB_WORKSPACE/Tools/CI-linux-environment.sh
          set -x

          if test -z "$SWIGLANG"; then
            make $SWIGJOBS check-ccache
            make $SWIGJOBS check-errors-test-suite
          else
            case "$SWIGLANG" in
              ocaml)
                # `make check-ocaml-test-suite` fails with parallel make with:
                #
                # File "swig.ml", line 1:
                # Error: Could not find the .cmi file for interface swig.mli.
                #
                # Apparently we ought to be using `ocamldep` to generate make
                # dependencies.
                unset SWIGJOBS
                ;;
              scilab)
                case $VER in
                  ""|6.*|2023.*|2024.0.*|2024.1.*)
                    # Some older versions of scilab sporadically fail with:
                    # terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
                    # #2942 says that it was probably introduced in 6.x (we've
                    # seen it with 6.1.0.0, but haven't with 5.x); it also says
                    # "this issue was fixed in Scilab 2024.1.0" but we are
                    # still seeing sporadic failures with that version.
                    unset SWIGJOBS
                    ;;
                esac
                ;;
            esac

            # Stricter compile flags for examples. Various headers and SWIG generated code prevents full use of -pedantic.
            cflags="$($GITHUB_WORKSPACE/Tools/testflags.py --language $SWIGLANG --cflags --std=$CSTD --compiler=$CC)"
            cxxflags="$($GITHUB_WORKSPACE/Tools/testflags.py --language $SWIGLANG --cxxflags --std=$CPPSTD --compiler=$CC)"
            make check-$SWIGLANG-version
            make check-$SWIGLANG-enabled
            make $SWIGJOBS check-$SWIGLANG-examples CFLAGS="$cflags" CXXFLAGS="$cxxflags" PY_ABI_VER="$PY_ABI_VER"
            make $SWIGJOBS check-$SWIGLANG-test-suite CFLAGS="$cflags" CXXFLAGS="$cxxflags" PY_ABI_VER="$PY_ABI_VER"
          fi

    - name: Install
      working-directory: build/build
      run: |
          set -x
          if test -z "$SWIGLANG"; then sudo make install && swig -version && ccache-swig -V; fi

    - name: Clean
      working-directory: build/build
      run: |
          set -x
          make check-maintainer-clean && ../../configure
