name: ZMat CI

on: [push, pull_request]

jobs:
  octave_test:
    name: Octave tests
    strategy:
      # tested octave versions: ubuntu-22.04 = 5.2, ubuntu-22.04 = 6.4, macos-13 = 8.1, windows-2022 = 8.3
      matrix:
        os: [ubuntu-22.04, ubuntu-24.04, macos-15-intel, macos-14, windows-2022]
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout repo
        uses: actions/checkout@v3
        with:
          submodules: 'recursive'
      - name: Install dependencies
        run: |
          if [[ "$RUNNER_OS" == "Linux" ]]; then
            sudo apt-get update && (sudo apt-get install -y liboctave-dev valgrind || sudo apt-get install -y octave-dev valgrind)
            curl --retry 3 -kL https://github.com/upx/upx/releases/download/v4.1.0/upx-4.1.0-amd64_linux.tar.xz --output upx.tar.xz
            tar -xvf upx.tar.xz
            sudo mv upx-4.1.0-amd64_linux/upx /usr/bin
            rm -rf upx-4.1.0-amd64_linux upx.tar.xz
          fi
          if [[ "$RUNNER_OS" == "macOS" ]]; then
            brew install octave
            brew install gcc@12
          fi
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            choco install upx
            which upx
            curl --retry 3 -kL http://cdimage.debian.org/mirror/gnu.org/gnu/octave/windows/octave-10.3.0-w64-64.7z --output octave_10.3.0.7z
            7z x octave_10.3.0.7z -ooctave -y
            echo 'C:\msys64\mingw64\bin' >> $GITHUB_PATH
            echo "$PWD/octave/octave-10.3.0-w64-64/mingw64/bin" >> $GITHUB_PATH
          fi
      - name: Install msys2 libraries (Windows only)
        if: ${{ runner.os == 'Windows' }}
        uses: msys2/setup-msys2@v2
        with:
          update: true
          install: >-
            mingw-w64-x86_64-gcc
            mingw-w64-x86_64-zlib
            mingw-w64-x86_64-winpthreads-git
            base-devel
      - name: Set up mex gcc (Windows only)
        if: ${{ runner.os == 'Windows' }}
        run: |
          echo "MW_MINGW64_LOC=${{ runner.temp }}/msys64/mingw64" >> $GITHUB_ENV
      - name: Build static library
        run: |
          which gcc
          which g++
          make -C src lib CC=gcc CXX=g++
          make -C example/c all CC=gcc CXX=g++ LIBTYPE=
          if [ "$RUNNER_OS" == "macOS" ]; then
             DYLD_LIBRARY_PATH=lib example/c/testzmat;
          else
             LD_LIBRARY_PATH=lib example/c/testzmat;
          fi
          octave-cli --eval "fprintf(1,['OCTAVE_ARCH=' regexprep(computer('arch'), 'darwin[0-9.]+-', 'darwin-')])"
          octave-cli --eval "fprintf(1,['OCTAVE_ARCH=' regexprep(computer('arch'), 'darwin[0-9.]+-', 'darwin-')])" >> $GITHUB_ENV
      - name: Build dynamic library
        run: |
          make -C src dll CC=gcc CXX=g++
          make -C example/c all  CC=gcc CXX=g++ LIBTYPE=
          if [ "$RUNNER_OS" == "macOS" ]; then
             DYLD_LIBRARY_PATH=lib example/c/testzmat;
          else
             LD_LIBRARY_PATH=lib example/c/testzmat;
          fi
      - name: Build octave mex (Windows only)
        if: ${{ runner.os == 'Windows' }}
        shell: msys2 {0}
        run: |
          export PATH="/mingw64/bin":"$PWD/octave/octave-10.3.0-w64-64/mingw64/bin":$PATH
          which gcc
          gcc -v
          make -C src oct CC=gcc CXX=g++ USERLINKOPT="-static-libgcc -static-libstdc++ -fstack-protector -L/mingw64/lib -Wl,-Bstatic -lz -lwinpthread -Wl,-Bdynamic -v -s '$PWD/octave/octave-10.3.0-w64-64/mingw64/lib/octave/10.3.0/liboctinterp.dll.a'"
          objdump -p zipmat.mex | grep "DLL Name:"
          strip zipmat.mex
          echo "$PWD/octave/octave-10.3.0-w64-64/mingw64/bin" >> $GITHUB_PATH
      - name: Compress octave mex (Windows only)
        if: ${{ runner.os == 'Windows' }}
        run: |
          #upx -9 zipmat.mex || true
      - name: Build octave mex (Linux and Mac)
        if: ${{ runner.os != 'Windows' }}
        run: |
          gcc -v
          if [ "$RUNNER_OS" == "macOS" ]; then
              make -C src oct CC=gcc-12 CXX=g++-12 MEXLINKOPT="-static-libgcc -static-libstdc++" USERLINKOPT="-v -s"
              otool -L zipmat.mex
              #upx -9 zipmat.mex || true
          else
              make -C src oct CC=gcc CXX=g++ MEXLINKOPT="-static-libgcc -static-libstdc++ -Wl,-Bstatic -lm -lpthread -Wl,-Bdynamic" USERLINKOPT="-v -s"
              ldd zipmat.mex
          fi
          strip -S zipmat.mex
          #upx -9 zipmat.mex || true
      - name: Run octave test
        if: ${{ runner.os != 'Linux' }}
        run: |
          octave-cli --version
          if [ "$RUNNER_OS" == "Windows" ]; then
              export PATH="$PWD/octave/octave-10.3.0-w64-64/mingw64/bin":$PATH
          fi
          octave-cli --eval "addpath(pwd);cd test;run_zmat_test;cd ../example;demo_zmat_basic;zmat_speedbench"
      - name: Run octave test
        if: ${{ runner.os == 'Linux' }}
        run: |
          octave-cli --version
          valgrind octave-cli --eval "addpath(pwd);cd test;run_zmat_test;cd ../example;demo_zmat_basic"
          octave-cli --eval "addpath(pwd);cd example;zmat_speedbench"
      - name: Create package folder
        run: |
          mkdir packages
          mkdir zmat
          mkdir -p zmat/octave/${{ env.OCTAVE_ARCH }}
          cp *.{m,txt,rst} zmat/
          cp PKG_ADD* zmat/
          cp INDEX zmat/
          cp DESCRIPTION zmat/
          cp COPYING zmat/
          cp -a example zmat/
          cp -a test zmat/
      - name: Create release tag
        run: perl -e "print 'RELEASE_TAG='. lc('${{ runner.os }}-${{ runner.arch }}-github-latest')" >> $GITHUB_ENV
      - name: Zip zmat
        run: |
          mv zipmat.mex zmat/octave/${{ env.OCTAVE_ARCH }}
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            7z a -tzip packages/zmat-octave-${{ env.RELEASE_TAG }}.zip zmat
          else
            zip -FSr --symlink packages/zmat-octave-${{ env.RELEASE_TAG }}.zip zmat
          fi
      - name: Upload zmat package
        if: ${{ matrix.os == 'ubuntu-22.04' || matrix.os == 'macos-15-intel' || matrix.os == 'macos-14' || matrix.os == 'windows-2022' }}
        uses: actions/upload-artifact@v4
        with:
          name: all-zmat-octave-${{ env.RELEASE_TAG }}
          path: packages/zmat-octave-${{ env.RELEASE_TAG }}.zip

  matlab_test:
    name: MATLAB test
    strategy:
      matrix:
        os: [ubuntu-22.04, ubuntu-24.04, macos-15-intel, macos-14, windows-2022]
    defaults:
      run:
        shell: bash

    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout repo
        uses: actions/checkout@v3
        with:
          submodules: 'recursive'
      - name: Install dependencies
        run: |
          if [[ "$RUNNER_OS" == "Linux" ]]; then
            curl --retry 3 -kL https://github.com/upx/upx/releases/download/v4.1.0/upx-4.1.0-amd64_linux.tar.xz --output upx.tar.xz
            tar -xvf upx.tar.xz
            sudo mv upx-4.1.0-amd64_linux/upx /usr/bin || true
            rm -rf upx-4.1.0-amd64_linux upx.tar.xz || true
          fi
          #[[ "$RUNNER_OS" == "macOS" ]] && brew install upx
          [[ "$RUNNER_OS" == "Windows" ]] && choco install upx
          echo "installed upx"
      - name: Install gcc-12
        if: ${{ runner.os == 'macOS' }}
        run: brew install gcc@12
      - name: Set up MATLAB (non-MacOS14)
        if: ${{ runner.os != 'macOS' }}
        uses: matlab-actions/setup-matlab@v2
        with:
          release: R2022a
      - name: Set up MATLAB (MacOS-14)
        if: ${{ runner.os == 'macOS' }}
        uses: matlab-actions/setup-matlab@v2
        with:
          release: R2023b
      - name: Build .mltbx package
        if: ${{ matrix.os == 'ubuntu-22.04' }}
        uses: matlab-actions/run-command@v2
        with:
          command: matlab.addons.toolbox.packageToolbox('zmat.prj')
      - name: Upload mltbx artifact
        if: ${{ matrix.os == 'ubuntu-22.04' }}
        uses: actions/upload-artifact@v4
        with:
          name: zmat-mltbx-github-latest
          path: zmat.mltbx
      - name: Install msys2 libraries (Windows only)
        if: ${{ runner.os == 'Windows' }}
        uses: msys2/setup-msys2@v2
        with:
          update: true
          install: >-
            mingw-w64-x86_64-gcc
            mingw-w64-x86_64-zlib
            mingw-w64-x86_64-winpthreads-git
            base-devel
      - name: Set up mex gcc (Windows only)
        if: ${{ runner.os == 'Windows' }}
        run: |
          echo "MW_MINGW64_LOC=${{ runner.temp }}/msys64/mingw64" >> $GITHUB_ENV
          echo 'COMPFLAGS=""' >> $GITHUB_ENV
      - name: Build MATLAB mex
        run: |
          rm -rf private
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            make -C src mex CC=gcc CXX=g++ MEX="cmd //s //c mex  -f mexopts_msys2_gcc.xml " USERLINKOPT="-v " CXXLIBS="-Lblosc2/lib -L/blosc2/internal-complibs/zstd-1.5.2"
            objdump -p zipmat.mexw* | grep "DLL Name:"
          elif [[ "$RUNNER_OS" == "macOS" ]]; then
            make -C src mex CC=gcc-12 CXX=g++-12 MEXLINKOPT="-static-libstdc++"
            otool -L zipmat.mex*
            #upx -9 zipmat.mex* || true
          else
            make -C src mex CC=gcc CXX=g++ MEXLINKOPT="-static-libgcc -static-libstdc++ -Wl,-Bstatic -lz -Wl,-Bdynamic" MEX="mex -v"
            ldd zipmat.mex*
          fi
          strip -S zipmat.mex*
          upx -9 zipmat.mex* || true
      - name: Run MATLAB examples
        uses: matlab-actions/run-command@v2
        with:
          command: addpath(pwd);cd test;run_zmat_test;cd ../example;demo_zmat_basic;zmat_speedbench
      - name: Create package folder
        run: |
          mkdir packages
          mkdir zmat
          mkdir -p zmat/private
          mkdir -p zmat/example
          cp *.{m,txt,rst} zmat/
          cp -a example/*.m zmat/example
          cp -a test zmat/
      - name: Create release tag
        run: perl -e "print 'RELEASE_TAG='. lc('${{ runner.os }}-${{ runner.arch }}-github-latest')" >> $GITHUB_ENV
      - name: Zip zmat
        run: |
          mv zipmat.mex* zmat/private
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            7z a -tzip packages/zmat-matlab-${{ env.RELEASE_TAG }}.zip zmat
          else
            zip -FSr --symlink packages/zmat-matlab-${{ env.RELEASE_TAG }}.zip zmat
          fi
      - name: Upload zmat package
        if: ${{ matrix.os == 'ubuntu-22.04' || matrix.os == 'macos-15-intel' || matrix.os == 'macos-14' || matrix.os == 'windows-2022' }}
        uses: actions/upload-artifact@v4
        with:
          name: all-zmat-matlab-${{ env.RELEASE_TAG }}
          path: packages/zmat-matlab-${{ env.RELEASE_TAG }}.zip

  upload_package:
    name: Upload Packages
    needs: [octave_test, matlab_test]
    runs-on: ubuntu-22.04
    if: ${{ github.repository_owner == 'NeuroJSON' && github.event_name != 'pull_request'}}
    steps:
      - name: Download zmat
        uses: actions/download-artifact@v4
        with:
          pattern: all-zmat*
          merge-multiple: true
          path: packages
      - name: Download mltbx
        uses: actions/download-artifact@v4
        with:
          name: zmat-mltbx-github-latest
          path: packages
      - name: Create all-in-one zmat package
        run: |
          cd packages
          unzip -n 'zmat*.zip'
          rm -rf zmat/example/c/testzmat.exe zmat/example/c/testzmat zmat/example/c/testzmat.dSYM
          tree .
          zip -FSr --symlink zmat-allinone-github-latest.zip zmat
          rm -rf zmat
      - name: Display structure of downloaded files
        run: ls -R packages
      - name: Copy package to server
        if: ${{ github.repository_owner == 'NeuroJSON' && github.event_name != 'pull_request'}}
        uses: wlixcc/SFTP-Deploy-Action@v1.2.4
        with:
          server: ${{ secrets.MCX_SERVER }}
          username: ${{ secrets.MCX_SERVER_USER }}
          ssh_private_key: ${{ secrets.MCX_SERVER_SSH_KEY }}
          local_path: "packages/zmat-allinone-github-latest.zip"
          remote_path: ${{ secrets.MCX_CI_PATH }}
      - name: Copy mltbx to server
        if: ${{ github.repository_owner == 'NeuroJSON' && github.event_name != 'pull_request'}}
        uses: wlixcc/SFTP-Deploy-Action@v1.2.4
        with:
          server: ${{ secrets.MCX_SERVER }}
          username: ${{ secrets.MCX_SERVER_USER }}
          ssh_private_key: ${{ secrets.MCX_SERVER_SSH_KEY }}
          local_path: "packages/zmat.mltbx"
          remote_path: ${{ secrets.MCX_CI_PATH }}
