name: CI
on: [push, pull_request]
jobs:
    linux-ci:
        name: Linux
        strategy:
            fail-fast: false
            matrix:
                build_type: [Release, Debug]
                config:
                    - cc: gcc-9
                      cxx: g++-9
                      os: ubuntu-20.04
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: gcc-10
                      cxx: g++-10
                      os: ubuntu-20.04
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: gcc-11
                      cxx: g++-11
                      os: ubuntu-22.04
                      python3_ver: '3.10'

                    - cc: gcc-12
                      cxx: g++-12
                      os: ubuntu-22.04
                      python3_ver: '3.10'

                    - cc: clang-10
                      cxx: clang++-10
                      os: ubuntu-20.04
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: clang-11
                      cxx: clang++-11
                      os: ubuntu-20.04
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: clang-12
                      cxx: clang++-12
                      os: ubuntu-20.04
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: clang-13
                      cxx: clang++-13
                      os: ubuntu-22.04
                      python3_ver: '3.10'

                    - cc: clang-14
                      cxx: clang++-14
                      os: ubuntu-22.04
                      python3_ver: '3.10'
        runs-on: ${{matrix.config.os}}
        env:
            CC: ${{matrix.config.cc}}
            CXX: ${{matrix.config.cxx}}
            INSTALL_PREFIX: /usr/local
        steps:
          - uses: actions/checkout@v2
          - name: Install dependencies
            run: |
                sudo apt update --fix-missing
                sudo apt install -y libpython2-dev libpython3-dev python3-numpy doxygen luajit lua-ldoc

                # Note: we need the grep because apt-cache will return 0 even when no packages are found
                if sudo apt-cache search python-numpy | grep 'numpy -'; then sudo apt install -y python-numpy; fi
                if sudo apt-cache search python2-numpy | grep 'numpy -'; then sudo apt install -y python2-numpy; fi

                # LuaRocks stopped installing properly through apt, so just copy the file from its repo
                git clone https://github.com/bluebird75/luaunit -b LUAUNIT_V3_4
                sudo mkdir -p /usr/local/share/lua/5.1
                sudo cp luaunit/luaunit.lua /usr/local/share/lua/5.1
          - name: Build SoapySDR
            run: |
                mkdir -p ${{github.workspace}}/build
                cd ${{github.workspace}}/build
                cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}
                make
          - name: Install
            run: |
                cd ${{github.workspace}}/build
                sudo make install
                sudo ldconfig
          - name: Run unit tests
            run: |
                cd ${{github.workspace}}/build
                ctest --output-on-failure
          - name: Test SoapySDRUtil
            run: |
                SoapySDRUtil --info
                SoapySDRUtil --check=null
                SoapySDRUtil --make="driver=null"
          - name: Test Python2 bindings
            if: ${{matrix.config.python2_ver}}
            run: |
                export PYTHONPATH=${INSTALL_PREFIX}/$(python${{matrix.config.python2_ver}} ${{github.workspace}}/swig/python/get_python_lib.py ${INSTALL_PREFIX})
                python${{matrix.config.python2_ver}} -c "import SoapySDR; print(SoapySDR.getAPIVersion())"
                python${{matrix.config.python2_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_ABI_VERSION)"
                python${{matrix.config.python2_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_TIMEOUT)"
                python${{matrix.config.python2_ver}} -c "import SoapySDR; print(SoapySDR.errToStr(SoapySDR.SOAPY_SDR_TIMEOUT))"
                python${{matrix.config.python2_ver}} -c "import SoapySDR; print(SoapySDR.Device.make('driver=null'))"
          - name: Test Python3 bindings
            run: |
                export PYTHONPATH=${INSTALL_PREFIX}/$(python${{matrix.config.python3_ver}} ${{github.workspace}}/swig/python/get_python_lib.py ${INSTALL_PREFIX})
                python${{matrix.config.python3_ver}} -c "import SoapySDR; print(SoapySDR.getAPIVersion())"
                python${{matrix.config.python3_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_ABI_VERSION)"
                python${{matrix.config.python3_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_TIMEOUT)"
                python${{matrix.config.python3_ver}} -c "import SoapySDR; print(SoapySDR.errToStr(SoapySDR.SOAPY_SDR_TIMEOUT))"
                python${{matrix.config.python3_ver}} -c "import SoapySDR; print(SoapySDR.Device.make('driver=null'))"
          - name: Test LuaJIT bindings
            run: |
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.API_VERSION)'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.ABI_VERSION)'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Error.TIMEOUT)'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Error.ToString(SoapySDR.Error.TIMEOUT))'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Device.new("driver=null"))'
          - name: Test module registration
            run: |
                mkdir -p ${{github.workspace}}/build-example
                cd ${{github.workspace}}/build-example
                cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}/ExampleDriver
                sudo make install
                SoapySDRUtil --check=my_device
    osx-ci:
        name: OS X
        strategy:
            fail-fast: false
            matrix:
                build_type: [Release, Debug]
                config:
                    - cc: gcc-10
                      cxx: g++-10
                      test_luajit: false
                      os: macos-11
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: gcc-11
                      cxx: g++-11
                      test_luajit: false
                      os: macos-11
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: clang
                      cxx: clang++
                      test_luajit: true
                      os: macos-11
                      python2_ver: '2.7'
                      python3_ver: '3.8'

                    - cc: clang
                      cxx: clang++
                      os: macos-12
                      python2_ver: '2.7'
                      python3_ver: '3.9'
        runs-on: ${{matrix.config.os}}
        env:
            CC: ${{matrix.config.cc}}
            CXX: ${{matrix.config.cxx}}
            INSTALL_PREFIX: /usr/local
        steps:
          - uses: actions/checkout@v2
          - name: Install dependencies
            run: |
                brew install doxygen luajit
                which luajit
                pip install numpy
                pip3 install numpy

                # Installing Lua 5.1 dependencies via package is ugly
                cd ${{runner.workspace}}
                git clone https://github.com/bluebird75/luaunit.git -b LUAUNIT_V3_4
                sudo mkdir -p /usr/local/share/lua/5.1
                sudo cp luaunit/luaunit.lua /usr/local/share/lua/5.1
          - name: Build SoapySDR
            run: |
                mkdir -p ${{github.workspace}}/build
                cd ${{github.workspace}}/build
                cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}
                make
          - name: Install
            run: |
                cd ${{github.workspace}}/build
                sudo make install
          - name: Run unit tests
            run: |
                cd ${{github.workspace}}/build
                ctest --output-on-failure
          - name: Test SoapySDRUtil
            run: |
                SoapySDRUtil --info
                SoapySDRUtil --check=null
                SoapySDRUtil --make="driver=null"
          - name: Set up Python2
            uses: actions/setup-python@v4
            with:
                python-version: ${{matrix.config.python2_ver}}
          - name: Test Python2 bindings
            run: |
                export DYLD_LIBRARY_PATH=/usr/local/lib
                export PYTHONPATH=${INSTALL_PREFIX}/$(python${{matrix.config.python2_ver}} ${{github.workspace}}/swig/python/get_python_lib.py ${INSTALL_PREFIX})
                python${{matrix.config.python2_ver}} -c "import SoapySDR; print(SoapySDR.getAPIVersion())"
                python${{matrix.config.python2_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_ABI_VERSION)"
                python${{matrix.config.python2_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_TIMEOUT)"
                python${{matrix.config.python2_ver}} -c "import SoapySDR; print(SoapySDR.errToStr(SoapySDR.SOAPY_SDR_TIMEOUT))"
                python${{matrix.config.python2_ver}} -c "import SoapySDR; print(SoapySDR.Device.make('driver=null'))"
          - name: Set up Python3
            uses: actions/setup-python@v4
            with:
                python-version: ${{matrix.config.python3_ver}}
          - name: Test Python3 bindings
            run: |
                export DYLD_LIBRARY_PATH=/usr/local/lib
                export PYTHONPATH=${INSTALL_PREFIX}/$(python${{matrix.config.python3_ver}} ${{github.workspace}}/swig/python/get_python_lib.py ${INSTALL_PREFIX})
                python${{matrix.config.python3_ver}} -c "import SoapySDR; print(SoapySDR.getAPIVersion())"
                python${{matrix.config.python3_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_ABI_VERSION)"
                python${{matrix.config.python3_ver}} -c "from SoapySDR import *; print(SOAPY_SDR_TIMEOUT)"
                python${{matrix.config.python3_ver}} -c "import SoapySDR; print(SoapySDR.errToStr(SoapySDR.SOAPY_SDR_TIMEOUT))"
                python${{matrix.config.python3_ver}} -c "import SoapySDR; print(SoapySDR.Device.make('driver=null'))"
          - name: Test LuaJIT bindings
            if: ${{ matrix.config.test_luajit }}
            run: |
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.API_VERSION)'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.ABI_VERSION)'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Error.TIMEOUT)'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Error.ToString(SoapySDR.Error.TIMEOUT))'
                luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Device.new("driver=null"))'
          - name: Test module registration
            run: |
                mkdir -p ${{github.workspace}}/build-example
                cd ${{github.workspace}}/build-example
                cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}/ExampleDriver
                sudo make install
                SoapySDRUtil --check=my_device
    windows-ci:
        name: Windows
        strategy:
            fail-fast: false
            matrix:
                platform:
                    - cmake_gen: '"Visual Studio 14 2015" -A "Win32"'
                      msvc: true
                      arch: win32
                      python_arch: x86
                      os: windows-2019

                    - cmake_gen: '"Visual Studio 14 2015" -A "x64"'
                      msvc: true
                      arch: x64
                      python_arch: x64
                      os: windows-2019

                    - cmake_gen: '"Visual Studio 16 2019" -A "Win32"'
                      msvc: true
                      arch: win32
                      python_arch: x86
                      os: windows-2019

                    - cmake_gen: '"Visual Studio 16 2019" -A "x64"'
                      msvc: true
                      arch: x64
                      python_arch: x64
                      os: windows-2019

                    - cmake_gen: '"Visual Studio 17 2022" -A "Win32"'
                      msvc: true
                      arch: win32
                      python_arch: x86
                      os: windows-2022

                    - cmake_gen: '"Visual Studio 17 2022" -A "x64"'
                      msvc: true
                      arch: x64
                      python_arch: x64
                      os: windows-2022

#                    - cmake_gen: '"MinGW Makefiles"'
#                      msvc: false
#                      python_arch: x64
#                      os: windows-2019

#                    TODO: restore if Github upgrades past buggy 11.2
#                    - cmake_gen: 'MinGW Makefiles'
#                      msvc: false
#                      python_arch: x64
#                      os: windows-2022

                build:
                    - type: Release
                      python_version: '2.7.18'
                      python_name: 'python'
                      cmake_flags: '-DENABLE_PYTHON2=ON -DENABLE_PYTHON3=OFF'

                    - type: Release
                      python_version: '3.9.13'
                      python_name: 'python3'
                      cmake_flags: '-DENABLE_PYTHON2=OFF -DENABLE_PYTHON3=ON'

                    - type: Debug
                      cmake_flags: '-DENABLE_PYTHON2=OFF -DENABLE_PYTHON3=OFF'

                exclude:
                    - platform: {os: windows-2022}
                      build: {python_version: '2.7.18'}
        runs-on: ${{matrix.platform.os}}
        env:
            INSTALL_PREFIX: 'C:\Program Files\SoapySDR'
            LUAJIT_INSTALL_PREFIX: 'C:\Program Files\LuaJIT'
            LUAJIT_LUA_DIR: 'C:\Program Files\LuaJIT\lua'
            LUA_PATH: '.\?.lua;C:\Program Files\LuaJIT\lua\?.lua;C:\Program Files\LuaJIT\lua\?\init.lua;C:\Program Files\SoapySDR\share\lua\5.1\?.lua;C:\Program Files\SoapySDR\share\lua\5.1\?\?.lua;C:\Program Files\SoapySDR\share\lua\5.1\?\init.lua'
        steps:
          - uses: actions/checkout@v2
          - uses: ilammy/msvc-dev-cmd@v1
            if: ${{matrix.platform.msvc}}
            with:
                arch: ${{matrix.platform.arch}}
          - name: Install common dependencies
            run: |
                mkdir -p "$Env:LUAJIT_LUA_DIR"

                cd ${{runner.workspace}}
                git clone https://github.com/LuaJIT/LuaJIT.git -b v2.1

                git clone https://github.com/bluebird75/luaunit.git -b LUAUNIT_V3_4
                cp -v luaunit\luaunit.lua "$Env:LUAJIT_LUA_DIR"

                # TODO: re-enable after issue fixed: http://disq.us/p/2pvugqv
                #choco install doxygen.install
          - name: Install MSVC dependencies
            if:
                ${{matrix.platform.msvc}}
            run: |
                cd ${{runner.workspace}}\LuaJIT\src
                .\msvcbuild.bat
                cp -v luajit.exe "$Env:LUAJIT_INSTALL_PREFIX"
                cp -v lua51.dll "$Env:LUAJIT_INSTALL_PREFIX"
                cp -v -r jit "$Env:LUAJIT_INSTALL_PREFIX"
          - name: Install MinGW dependencies
            if:
                ${{matrix.platform.msvc == false}}
            run: |
                cd ${{runner.workspace}}\LuaJIT\src
                mingw32-make
                cp -v luajit.exe "$Env:LUAJIT_INSTALL_PREFIX"
                cp -v lua51.dll "$Env:LUAJIT_INSTALL_PREFIX"
                cp -v -r jit "$Env:LUAJIT_INSTALL_PREFIX"
          - name: Build SoapySDR
            run: |
                $Env:PATH += ";$Env:LUAJIT_INSTALL_PREFIX"
                mkdir ${{github.workspace}}\build
                cd ${{github.workspace}}\build
                cmake -G ${{matrix.platform.cmake_gen}} -DCMAKE_INSTALL_PREFIX="$Env:INSTALL_PREFIX" -DCMAKE_BUILD_TYPE=${{matrix.build.type}} ${{matrix.build.cmake_flags}} ${{github.workspace}}
                cmake --build . --config ${{matrix.build.type}}
          - name: Install
            run: |
                cd ${{github.workspace}}\build
                cmake --install . --config ${{matrix.build.type}}
          - name: Run unit tests
            run: |
                $Env:PATH += ";$Env:INSTALL_PREFIX\bin;$Env:LUAJIT_INSTALL_PREFIX"
                cd ${{github.workspace}}\build
                ctest --output-on-failure -C ${{matrix.build.type}}
          - name: Test SoapySDRUtil
            run: |
                $Env:PATH += ";$Env:INSTALL_PREFIX\bin"
                SoapySDRUtil --info
                SoapySDRUtil --check=null
                SoapySDRUtil --make="driver=null"
          - name: Test Python bindings
            if: ${{matrix.build.python_name}}
            env:
                PYTHON_EXE: 'C:\hostedtoolcache\windows\Python\${{matrix.build.python_version}}\${{matrix.platform.python_arch}}\${{matrix.build.python_name}}.exe'
            run: |
                $Env:PATH += ";$Env:INSTALL_PREFIX\bin"
                $Env:PYTHONPATH += ";$Env:INSTALL_PREFIX\Lib\site-packages"
                echo $Env:PYTHONPATH
                & "$Env:PYTHON_EXE" -c "import SoapySDR; print(SoapySDR.getAPIVersion())"
                & "$Env:PYTHON_EXE" -c "from SoapySDR import *; print(SOAPY_SDR_ABI_VERSION)"
                & "$Env:PYTHON_EXE" -c "from SoapySDR import *; print(SOAPY_SDR_TIMEOUT)"
                & "$Env:PYTHON_EXE" -c "import SoapySDR; print(SoapySDR.errToStr(SoapySDR.SOAPY_SDR_TIMEOUT))"
                & "$Env:PYTHON_EXE" -c "import SoapySDR; print(SoapySDR.Device.make('driver=null'))"
          - name: Test LuaJIT bindings
            run: |
                $Env:PATH += ";$Env:INSTALL_PREFIX\bin;$Env:LUAJIT_INSTALL_PREFIX"
                luajit -e "SoapySDR = require('SoapySDR'); print(SoapySDR.API_VERSION)"
                luajit -e "SoapySDR = require('SoapySDR'); print(SoapySDR.ABI_VERSION)"
                luajit -e "SoapySDR = require('SoapySDR'); print(SoapySDR.Error.TIMEOUT)"
                luajit -e "SoapySDR = require('SoapySDR'); print(SoapySDR.Error.ToString(SoapySDR.Error.TIMEOUT))"
                luajit -e "SoapySDR = require('SoapySDR'); print(SoapySDR.Device.new('driver=null'))"
          - name: Test module registration
            run: |
                $Env:PATH += ";$Env:INSTALL_PREFIX\bin"
                mkdir -p ${{github.workspace}}\build-example
                cd ${{github.workspace}}\build-example
                cmake -G ${{matrix.platform.cmake_gen}} -DCMAKE_INSTALL_PREFIX="$Env:INSTALL_PREFIX" -DCMAKE_BUILD_TYPE=${{matrix.build.type}} ${{github.workspace}}\ExampleDriver
                cmake --build . --config ${{matrix.build.type}}
                cmake --install . --config ${{matrix.build.type}}
                SoapySDRUtil --check=my_device
    freebsd-ci:
        name: FreeBSD
        runs-on: macos-12
        strategy:
            fail-fast: false
            matrix:
                build_type: [Release, Debug]
                release: ['12.3', '13.1'] # Skip 13.0, EOL and has broken Python package
        env:
            INSTALL_PREFIX: /usr/local
            PYTHON3_VER: '3.9'
        steps:
        - uses: actions/checkout@v2
        - uses: vmactions/freebsd-vm@v0
          name: Test in FreeBSD
          with:
              envs: 'INSTALL_PREFIX PYTHON3_VER'
              release: ${{matrix.release}}
              copyback: false
              prepare: |
                  pkg install -y cmake devel/swig lang/python3 lang/luajit
              run: |
                  # We can't separate these steps, so add prints for clarity.

                  echo
                  echo "----------------------------------"
                  echo "Building..."
                  echo "----------------------------------"
                  mkdir build
                  cd build
                  cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}
                  make

                  echo
                  echo "----------------------------------"
                  echo "Installing..."
                  echo "----------------------------------"
                  make install

                  echo
                  echo "----------------------------------"
                  echo "Running unit tests..."
                  echo "----------------------------------"
                  ctest --output-on-failure

                  echo
                  echo "----------------------------------"
                  echo "Testing SoapySDRUtil..."
                  echo "----------------------------------"
                  SoapySDRUtil --info
                  SoapySDRUtil --check=null
                  SoapySDRUtil --make="driver=null"

                  echo
                  echo "----------------------------------"
                  echo "Testing Python 3 bindings..."
                  echo "----------------------------------"
                  python${PYTHON3_VER} -c "import SoapySDR; print(SoapySDR.getAPIVersion())" || exit 1
                  python${PYTHON3_VER} -c "from SoapySDR import *; print(SOAPY_SDR_ABI_VERSION)" || exit 1
                  python${PYTHON3_VER} -c "from SoapySDR import *; print(SOAPY_SDR_TIMEOUT)" || exit 1
                  python${PYTHON3_VER} -c "import SoapySDR; print(SoapySDR.errToStr(SoapySDR.SOAPY_SDR_TIMEOUT))" || exit 1
                  python${PYTHON3_VER} -c "import SoapySDR; print(SoapySDR.Device.make('driver=null'))" || exit 1

                  echo
                  echo "----------------------------------"
                  echo "Testing LuaJIT bindings..."
                  echo "----------------------------------"
                  luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.API_VERSION)' || exit 1
                  luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.ABI_VERSION)' || exit 1
                  luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Error.TIMEOUT)' || exit 1
                  luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Error.ToString(SoapySDR.Error.TIMEOUT))' || exit 1
                  luajit -e 'SoapySDR = require("SoapySDR"); print(SoapySDR.Device.new("driver=null"))' || exit 1

                  echo
                  echo "----------------------------------"
                  echo "Testing module registration..."
                  echo "----------------------------------"
                  mkdir -p ${{github.workspace}}/build-example
                  cd ${{github.workspace}}/build-example
                  cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}/ExampleDriver
                  make install
                  SoapySDRUtil --check=my_device