name: QLCplus Github Actions CI Build

on: ["push", "pull_request"]

jobs:
  build-linux:
    #if: false
    runs-on: ubuntu-20.04
    name: QLCplus Linux ${{matrix.task}}
    strategy:
      #fail-fast: false
      matrix:
        task: [compile-qt5, compile-qt5qml, coverage-qt5]
    env:
      CI_REPO_SLUG: ${{ github.repository }}
      CI_BRANCH: ${{ github.head_ref }}
      CI_PULL_REQUEST: ${{ github.event.number }}
      CC: gcc
      CXX: g++
      PACKAGES_BASE:
        gdb
        libasound2-dev
        libusb-1.0-0-dev
        libftdi1-dev
        shared-mime-info
        libudev-dev
        libmad0-dev
        libsndfile1-dev
        liblo-dev
        libfftw3-dev
        libgl1-mesa-dev
        libxml2-utils
        xvfb
        ccache
        wget
      PACKAGES_QML_BASE:
        libpulse-dev
      PACKAGES_COVERAGE_BASE:
        lcov
      QT_MODULES:
        qtscript
    defaults:
      run:
        shell: bash

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: false

      - name: Set General ENV variables
        run: |
          echo "PACKAGES_QT5=$(echo ${PACKAGES_BASE})" >> $GITHUB_ENV
          echo "PACKAGES_QML=$(echo ${PACKAGES_BASE} ${PACKAGES_QML_BASE})" >> $GITHUB_ENV
          echo "PACKAGES_COVERAGE=$(echo ${PACKAGES_BASE} ${PACKAGES_QT5_BASE} ${PACKAGES_COVERAGE_BASE})" >> $GITHUB_ENV
          echo "PACKAGES_ALL=$(echo ${PACKAGES_BASE} ${PACKAGES_QML_BASE} ${PACKAGES_COVERAGE_BASE})" >> $GITHUB_ENV
          echo "CI_BRANCH=$(echo $GITHUB_REF | cut -d '/' -f 3)" >> $GITHUB_ENV
          echo "CI_SECURE_ENV_VARS=$(if [ -z '${{ secrets.something }}' ]; then echo 'false'; else echo 'true'; fi)" >> $GITHUB_ENV
          echo "CI_EVENT_TYPE=$(if [ 'schedule' == '${{ github.event_name }}' ]; then echo 'cron'; else echo '${{ github.event_name }}'; fi)" >> $GITHUB_ENV
          echo "NPROC=$(nproc)" >> $GITHUB_ENV
          echo "TASK=$(echo '${{matrix.task}}' | cut -d '-' -f 2)" >> $GITHUB_ENV
          echo "INSTALL_ROOT=`pwd`/install_root" >> $GITHUB_ENV
          echo "BUILD_DATE=`date -u '+%Y%m%d'`" >> $GITHUB_ENV
          echo "GIT_REV=`git rev-parse --short HEAD`" >> $GITHUB_ENV

      - name: Set QT ENV variables (qt5)
        if: ${{ endsWith( matrix.task, 'qt5') }}
        run: |
          echo "QT=${QT:-$(echo '${{matrix.task}}' | cut -d '-' -f 2)}" >> $GITHUB_ENV
          echo "QT_INSTALL_DIR=/opt" >> $GITHUB_ENV
          echo "QT_VERSION=5.14.2" >> $GITHUB_ENV
          echo "QT_MODULES_INSTALL=$(echo ${QT_MODULES})" >> $GITHUB_ENV
          echo "APPVERSION=`grep '^!qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//;s/ /_/g'`" >> $GITHUB_ENV
          source $GITHUB_ENV && echo "QTDIR=${QT_INSTALL_DIR}/Qt/${QT_VERSION}/gcc_64" >> $GITHUB_ENV
          source $GITHUB_ENV && echo "QMAKE=${QTDIR}/bin/qmake" >> $GITHUB_ENV

      - name: Set QT ENV variables (qt5qml)
        if: ${{ endsWith( matrix.task, 'qt5qml') }}
        run: |
          echo "QT=${QT:-$(echo '${{matrix.task}}' | cut -d '-' -f 2)}" >> $GITHUB_ENV
          echo "QT_INSTALL_DIR=/opt" >> $GITHUB_ENV
          echo "QT_VERSION=5.14.2" >> $GITHUB_ENV
          echo "QT_MODULES_INSTALL=$(echo ${QT_MODULES})" >> $GITHUB_ENV
          echo "APPVERSION=`grep '^qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//;s/ /_/g'`" >> $GITHUB_ENV
          source $GITHUB_ENV && echo "QTDIR=${QT_INSTALL_DIR}/Qt/${QT_VERSION}/gcc_64" >> $GITHUB_ENV
          source $GITHUB_ENV && echo "QMAKE=${QTDIR}/bin/qmake" >> $GITHUB_ENV

      - name: Print ENV vars
        run: |
          echo "CI_BRANCH: ${CI_BRANCH}"
          echo "CI_PULL_REQUEST: ${CI_PULL_REQUEST}"
          echo "CI_REPO_SLUG: ${CI_REPO_SLUG}"
          echo "CI_EVENT_TYPE: ${CI_EVENT_TYPE}"
          echo "CI_SECURE_ENV_VARS: ${CI_SECURE_ENV_VARS}"
          echo "PACKAGES_QT5: ${PACKAGES_QT5}"
          echo "PACKAGES_QML: ${PACKAGES_QML}"
          echo "PACKAGES_COVERAGE: ${PACKAGES_COVERAGE}"
          echo "TASK: ${TASK}"
          echo "QT: ${QT}"
          echo "NPROC: ${NPROC}"

      - name: Download, cache and install packages
        uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: ${{env.PACKAGES_ALL}}
          version: ${{runner.os}}-apt

      - name: Install Qt through aqt (and python)
        uses: jurplel/install-qt-action@v3
        with:
          version: ${{ env.QT_VERSION }}
          cache: true
          dir: ${{ env.QT_INSTALL_DIR }}
          modules: ${{ env.QT_MODULES_INSTALL }}

      - name: Install python-lxml for fixtures-tool # with the now-registered aqt python
        run: pip install lxml

      - name: Setup ccache
        uses: Chocobo1/setup-ccache-action@v1
        with:
          update_packager_index: false
          install_ccache: false
          ccache_options: |
            max_size=5G
            compression=false

      - name: Setup ruby cache
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '2.7'
          bundler-cache: true

      - name: Install coveralls-lcov
        if: ${{ startsWith( matrix.task, 'coverage') }}
        run: sudo gem install coveralls-lcov

      - name: Print program versions and username
        run: |
          echo "CXX:"
          ${CXX} --version
          echo "QMAKE:"
          ${QMAKE} -v
          echo "Python:"
          python --version
          echo "WHOAMI:"
          whoami

      - name: Configure for QT5 build
        if: ${{ matrix.task == 'compile-qt5' }}
        run: |
          ${QMAKE} QMAKE_CXX="${CXX}" QMAKE_CC="${CC}" QMAKE_LINK="${CXX}" QMAKE_LINK_SHLIB="${CXX}" CONFIG+=appimage FORCECONFIG=release

      - name: Configure for QT5QML build
        if: ${{ matrix.task == 'compile-qt5qml' }}
        run: |
          $QMAKE QMAKE_CXX="$CXX" QMAKE_CC="$CC" QMAKE_LINK="$CXX" QMAKE_LINK_SHLIB="$CXX" CONFIG+=appimage CONFIG+=qmlui FORCECONFIG=release

      - name: Configure for QT5 coverage build
        if: ${{ matrix.task == 'coverage-qt5' }}
        run: |
          $QMAKE QMAKE_CXX="$CXX" QMAKE_CC="$CC" QMAKE_LINK="$CXX" QMAKE_LINK_SHLIB="$CXX" CONFIG+=coverage
          #QMAKE_CXXFLAGS+="-fno-sized-deallocation"

      - name: Build
        run: make -j $NPROC

      - name: Test
        if: ${{ ! startsWith( matrix.task, 'coverage') }}
        run: make check

      - name: Test with Coverage
        if: ${{ startsWith( matrix.task, 'coverage') }}
        run: make lcov

      - name: Coveralls
        if: ${{ startsWith( matrix.task, 'coverage') }}
        uses: coverallsapp/github-action@v2
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          file: coverage/coverage.info
          format: lcov

      - name: Install
        if: ${{ ! startsWith( matrix.task, 'coverage') }}
        run: |
          make INSTALL_ROOT=${INSTALL_ROOT} install
          cp -v resources/icons/svg/qlcplus.svg ${INSTALL_ROOT}
          cp -v platforms/linux/qlcplus.desktop ${INSTALL_ROOT}

      - name: Adapt qlcplus for AppImage (qt5)
        if: ${{ matrix.task == 'compile-qt5' }}
        run: |
          chrpath -r "../lib" ${INSTALL_ROOT}/usr/bin/qlcplus

      - name: Adapt qlcplus for AppImage (qt5qml)
        if: ${{ matrix.task == 'compile-qt5qml' }}
        run: |
          chrpath -r "../lib" ${INSTALL_ROOT}/usr/bin/qlcplus-qml
          sed -i -e 's/Exec=qlcplus --open %f/Exec=qlcplus-qml/g' ${INSTALL_ROOT}/qlcplus.desktop

      - name: Store original install artifacts before stripping and AppImage
        # Activate for debugging
        if: ${{ false && ! startsWith( matrix.task, 'coverage') }}
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.task }}-files
          path: ${{ env.INSTALL_ROOT }}

      - name: Strip Binaries (qt5)
        if: ${{ matrix.task == 'compile-qt5' }}
        run: |
          strip -v ${INSTALL_ROOT}/usr/bin/qlcplus
          find ${INSTALL_ROOT}/usr/lib/ -name libqlcplusengine.so.1.0.0 -exec strip -v {} \;

      - name: Strip Binaries (qt5qml)
        if: ${{ matrix.task == 'compile-qt5qml' }}
        run: |
          strip -v ${INSTALL_ROOT}/usr/bin/qlcplus-qml
          find ${INSTALL_ROOT}/usr/lib/ -name libqlcplusengine.so.1.0.0 -exec strip -v {} \;

      - name: Delete unused files for AppImage
        if: ${{ ! startsWith( matrix.task, 'coverage') }}
        run: |
          find ${INSTALL_ROOT}/usr/bin/ -name plugins.qmltypes -type f -delete
          find ${INSTALL_ROOT}/usr/bin -name *.qmlc -type f -delete
          rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Extras QtQuick/Particles.2 QtQuick/XmlListModel
          rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Controls.2/designer QtQuick/Controls.2/Material
          rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Controls.2/Universal QtQuick/Controls.2/Fusion
          rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Controls.2/Imagine QtQuick/Controls.2/Scene2D

      - name: Build AppImage
        if: ${{ ! startsWith( matrix.task, 'coverage') }}
        run: |
          wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-x86_64 -O ${INSTALL_ROOT}/AppRun
          chmod a+x ${INSTALL_ROOT}/AppRun
          wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O ./appimagetool-x86_64.AppImage
          chmod a+x ./appimagetool-x86_64.AppImage
          ./appimagetool-x86_64.AppImage -v ${INSTALL_ROOT}
          mv -v Q_Light_Controller_Plus-x86_64.AppImage qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage

      - name: Test Load AppImage
        if: ${{ matrix.task == 'compile-qt5qml' }}
        run: |
          ./qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage --platform offscreen --version

      - name: Store AppImage artifacts
        if: ${{ ! startsWith( matrix.task, 'coverage') }}
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.task }}-AppImage
          path: qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage

  build-windows:
    #if: false
    runs-on: windows-latest
    name: QLCplus Windows ${{matrix.task}}
    strategy:
      fail-fast: false
      matrix:
        task: [compile-qt5, compile-qt5qml]
    env:
      CI_REPO_SLUG: ${{ github.repository }}
      CI_BRANCH: ${{ github.head_ref }}
      CI_PULL_REQUEST: ${{ github.event.number }}
      QMAKESPEC: win32-g++
      QT_MODULES:
        qtscript
      CC: /mingw64/bin/x86_64-w64-mingw32-gcc.exe
      CXX: /mingw64/bin/x86_64-w64-mingw32-g++.exe

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: false

      - name: Set ENV variables
        shell: bash
        run: |
          echo "CI_BRANCH=$(echo $GITHUB_REF | cut -d '/' -f 3)" >> $GITHUB_ENV
          echo "CI_SECURE_ENV_VARS=$(if [ -z '${{ secrets.something }}' ]; then echo 'false'; else echo 'true'; fi)" >> $GITHUB_ENV
          echo "CI_EVENT_TYPE=$(if [ 'schedule' == '${{ github.event_name }}' ]; then echo 'cron'; else echo '${{ github.event_name }}'; fi)" >> $GITHUB_ENV
          echo "NPROC=$(nproc)" >> $GITHUB_ENV
          echo "TASK=$(echo '${{matrix.task}}' | cut -d '-' -f 2)" >> $GITHUB_ENV
          echo "QT=${QT:-$(echo '${{matrix.task}}' | cut -d '-' -f 2)}" >> $GITHUB_ENV
          echo "QT_VERSION=5.15.8" >> $GITHUB_ENV
          echo "QT_MODULES_INSTALL=$(echo ${QT_MODULES})" >> $GITHUB_ENV
          echo "INSTALL_ROOT=/c/" >> $GITHUB_ENV
          echo "BUILD_DATE=`date -u '+%Y%m%d'`" >> $GITHUB_ENV
          echo "GIT_REV=`git rev-parse --short HEAD`" >> $GITHUB_ENV

      - name: Set v4 ENV variables
        shell: bash
        if: ${{ matrix.task == 'compile-qt5' }}
        run: |
          echo "OUTFILE=`grep 'OutFile' platforms/windows/qlcplus4Qt5.nsi | cut -d'"' -f 2`" >> $GITHUB_ENV
          echo "APPVERSION=`grep '^!qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//' | cut -d ' ' -f 1`" >> $GITHUB_ENV
          echo "NSIS_SCRIPT=qlcplus4Qt5.nsi" >> $GITHUB_ENV

      - name: Set v5 ENV variables
        shell: bash
        if: ${{ matrix.task == 'compile-qt5qml' }}
        run: |
          echo "OUTFILE=`grep 'OutFile' platforms/windows/qlcplus5Qt5.nsi | cut -d'"' -f 2`" >> $GITHUB_ENV
          echo "APPVERSION=`grep '^qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//' | cut -d ' ' -f 1`" >> $GITHUB_ENV
          echo "NSIS_SCRIPT=qlcplus5Qt5.nsi" >> $GITHUB_ENV

      - name: Print ENV vars
        shell: bash
        run: |
          echo "CI_BRANCH: ${CI_BRANCH}"
          echo "CI_PULL_REQUEST: ${CI_PULL_REQUEST}"
          echo "CI_REPO_SLUG: ${CI_REPO_SLUG}"
          echo "CI_EVENT_TYPE: ${CI_EVENT_TYPE}"
          echo "CI_SECURE_ENV_VARS: ${CI_SECURE_ENV_VARS}"
          echo "TASK: ${TASK}"
          echo "QT: ${QT}"
          echo "NPROC: ${NPROC}"

      - name: Update and install MSYS2
        uses: msys2/setup-msys2@v2
        with:
          msystem: mingw64
          release: true
          update: false
          path-type: inherit
          install: >-
            wget
            unzip
            mingw-w64-x86_64-gcc
            mingw-w64-x86_64-gcc-libs
            mingw-w64-x86_64-cmake
            mingw-w64-x86_64-libmad
            mingw-w64-x86_64-libsndfile
            mingw-w64-x86_64-flac
            mingw-w64-x86_64-fftw
            mingw-w64-x86_64-libusb
            mingw-w64-x86_64-python-lxml
            mingw-w64-x86_64-qt5-base
            mingw-w64-x86_64-qt5-multimedia
            mingw-w64-x86_64-qt5-serialport
            mingw-w64-x86_64-qt5-script
            mingw-w64-x86_64-qt5-tools
            mingw-w64-x86_64-qt5-imageformats
            mingw-w64-x86_64-qt5-svg
            mingw-w64-x86_64-qt5-declarative
            mingw-w64-x86_64-qt5-quickcontrols
            mingw-w64-x86_64-qt5-quickcontrols2
            mingw-w64-x86_64-qt5-3d
            mingw-w64-x86_64-qt5-quick3d
            mingw-w64-x86_64-nsis

      - name: D2XX SDK
        shell: msys2 {0}
        run: |
          set MSYSTEM=MINGW64
          mkdir -p /c/projects/D2XXSDK
          wget https://ftdichip.com/wp-content/uploads/2023/09/CDM-v2.12.36.4-WHQL-Certified.zip -O /c/projects/D2XXSDK/cdm.zip
          cd /c/projects/D2XXSDK
          unzip cdm.zip
          cd amd64
          gendef.exe - ftd2xx64.dll > ftd2xx.def
          dlltool -k --input-def ftd2xx.def --dllname ftd2xx64.dll --output-lib libftd2xx.a

      - name: Print program versions
        shell: msys2 {0}
        run: |
          set MSYSTEM=MINGW64
          echo "pwd:"
          pwd
          echo "CXX:"
          which ${CXX} || true
          ${CXX} -v || true
          echo "cmake:"
          which cmake || true
          cmake --version || true
          pkg-config --modversion libusb-1.0

      - name: Fix build
        #if: false
        shell: msys2 {0}
        run: |
          set MSYSTEM=MINGW64
          # force a release build
          sed -i -e 's/Debug/Release/g' CMakeLists.txt
          # disable Velleman plugin
          sed -i -e 's/    add_subdirectory(velleman)/#    add_subdirectory(velleman)/g' plugins/CMakeLists.txt
          # fix MSYS2 system path
          sed -i -e 's/$ENV{SystemDrive}\/msys64/D:\/a\/_temp\/msys64/g' platforms/windows/CMakeLists.txt
          # fix project path in NSIS script
          sed -i -e 's/c\:\\projects/d:\\a\\qlcplus/g' platforms/windows/${{env.NSIS_SCRIPT}}

      - name: Configure v4 build for Windows
        shell: msys2 {0}
        if: ${{ matrix.task == 'compile-qt5' }}
        run: |
          set MSYSTEM=MINGW64
          mkdir build
          cd build
          cmake -G "Unix Makefiles" ..

      - name: Configure v5 build for Windows
        shell: msys2 {0}
        if: ${{ matrix.task == 'compile-qt5qml' }}
        run: |
          set MSYSTEM=MINGW64
          mkdir build
          cd build
          cmake -G "Unix Makefiles" -Dqmlui=ON ..

      - name: Build for Windows
        shell: msys2 {0}
        run: |
          set MSYSTEM=MINGW64
          cd build
          make -j${NPROC}

      - name: Install on Windows
        shell: msys2 {0}
        run: |
          set MSYSTEM=MINGW64
          #echo 'Silently installing QLC+...'
          cd build
          make install/fast
          cd ..
          cp *.qm /c/qlcplus

      - name: Build installation package
        shell: msys2 {0}
        run: |
          set MSYSTEM=MINGW64
          cd /c/qlcplus
          echo 'Creating package...'
          makensis -X'SetCompressor /FINAL lzma' ${{env.NSIS_SCRIPT}}
          mv /c/qlcplus/${{env.OUTFILE}} /d/a/qlcplus/qlcplus

      - name: Store executable artifact
        uses: actions/upload-artifact@v4
        with:
          name: QLC+-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.exe
          path: ${{env.OUTFILE}}
