name: Build

on:
  push:
    branches: ["*"]
  pull_request:
  workflow_dispatch:

env:
  BUILD_TYPE: RelWithDebInfo

jobs:

  # Windows build
  win:
    runs-on: windows-2022
    name: 🪟 Windows MINGW64
    defaults:
      run:
        shell: msys2 {0}
    env:
      CCACHE_DIR:      "${{ github.workspace }}/.ccache"
    steps:
    - name: 🧰 Checkout
      uses: actions/checkout@v3
      with:
        submodules: recursive

    - name: 📜 Setup ccache
      uses:  hendrikmuhs/ccache-action@v1.2
      id:    cache-ccache
      with:
        key: ${{ runner.os }}-ccache-${{ github.run_id }}
        restore-keys: ${{ runner.os }}-ccache
        max-size: 1G

    - name: 📜 Restore CMakeCache
      uses:  actions/cache@v3
      with:
        path: |
          build/CMakeCache.txt
        key: ${{ runner.os }}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}

    - name: 🟦 Install msys2
      uses: msys2/setup-msys2@v2
      with:
        msystem: mingw64
        pacboy: >-
          gcc:p
          lld:p
          cmake:p
          ccache:p
          glfw:p
          file:p
          mbedtls:p
          freetype:p
          dlfcn:p
          libbacktrace:p
          ninja:p
          curl-winssl:p
          capstone:p

    - name: ⬇️ Install .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.100'

    - name: 📜 Set version variable
      run: |
        echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV

    # Windows cmake build
    - name: 🛠️ Build
      run: |
        set -x
        mkdir -p build
        cd build

        cmake -G "Ninja"                                \
          -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}        \
          -DCMAKE_INSTALL_PREFIX="$PWD/install"         \
          -DIMHEX_GENERATE_PACKAGE=ON                   \
          -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON         \
          -DIMHEX_PATTERNS_PULL_MASTER=ON               \
          -DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}"  \
          -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}"      \
          -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}"     \
          -DUSE_SYSTEM_CAPSTONE=ON                      \
          -DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe"  \
          ..
        ninja install
        cpack
        mv ImHex-*.msi ../imhex-${{env.IMHEX_VERSION}}-Windows-x86_64.msi

        echo "ImHex checks for the existence of this file to determine if it is running in portable mode. You should not delete this file" > $PWD/install/PORTABLE

    - name: ⬆️ Upload Windows Installer
      uses: actions/upload-artifact@v3
      with:
        if-no-files-found: error
        name: Windows Installer x86_64
        path: |
          imhex-*.msi

    - name: ⬆️ Upload Portable ZIP
      uses: actions/upload-artifact@v3
      with:
        if-no-files-found: error
        name: Windows Portable x86_64
        path: |
          build/install/*

    - name: ⬇️ Download Mesa3D for NoGPU version
      shell: bash
      run: |
        set -x
        echo "NoGPU version Powered by Mesa 3D : https://fdossena.com/?p=mesa%2Findex.frag" > build/install/MESA.md
        curl https://werwolv.net/downloads/mesa/MesaForWindows-x64-latest.7z -L -o mesa.7z
        7z e mesa.7z
        mv opengl32.dll build/install

    - name: ⬆️ Upload NoGPU Portable ZIP
      uses: actions/upload-artifact@v3
      with:
        if-no-files-found: error
        name: Windows Portable NoGPU x86_64
        path: |
          build/install/*

  # MacOS build
  macos:
    runs-on: macos-12

    strategy:
      fail-fast: false
      matrix:
        include:
          - suffix: "-NoGPU"
            custom_glfw: true
          - suffix: ""
            custom_glfw: false

    name: 🍎 macOS 12.0${{matrix.suffix}}

    steps:
    - name: 🧰 Checkout
      uses: actions/checkout@v3
      with:
        submodules: recursive

    - name: 📜 Set version variable
      run: |
        echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV

    - name: 📜 Setup ccache
      uses: hendrikmuhs/ccache-action@v1.2
      with:
        key: ${{ runner.os }}${{ matrix.suffix }}-ccache-${{ github.run_id }}
        restore-keys: ${{ runner.os }}${{ matrix.suffix }}-ccache
        max-size: 1G

    - name: 📜 Restore CMakeCache
      uses: actions/cache@v3
      with:
        path: |
          build/CMakeCache.txt
        key: ${{ runner.os }}-${{ matrix.suffix }}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}

    - name: ⬇️ Install dependencies
      run: |
        set -x
        brew reinstall python || brew link --overwrite python
        brew bundle --no-lock --file dist/Brewfile
        rm -rf /usr/local/Cellar/capstone

    - name: ⬇️ Install classic glfw
      if: ${{! matrix.custom_glfw}}
      run: |
        brew install glfw

    - name: ⬇️ Install .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.100'

    - name: 🧰 Checkout glfw
      if: ${{matrix.custom_glfw}}
      uses: actions/checkout@v3
      with:
        repository: glfw/glfw
        path: glfw
    
    # GLFW custom build (to allow software rendering)
    - name: ⬇️ Patch and install custom glfw
      if: ${{matrix.custom_glfw}}
      run: |
        set -x
        cd glfw
        git apply ../dist/macOS/0001-glfw-SW.patch

        mkdir build
        cd build

        cmake -G "Ninja"                                \
          -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}        \
          -DBUILD_SHARED_LIBS=ON                        \
          -DCMAKE_C_COMPILER_LAUNCHER=ccache            \
          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache          \
          -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache         \
          -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache       \
        ..
        ninja install

    # MacOS cmake build
    - name: 🛠️ Build
      run: |
        set -x
        mkdir -p build
        cd build
        CC=$(brew --prefix gcc@12)/bin/gcc-12                                                       \
        CXX=$(brew --prefix gcc@12)/bin/g++-12                                                      \
        OBJC=$(brew --prefix llvm)/bin/clang                                                        \
        OBJCXX=$(brew --prefix llvm)/bin/clang++                                                    \
        PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig"   \
        cmake -G "Ninja"                                                                            \
          -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}                                                    \
          -DIMHEX_GENERATE_PACKAGE=ON                                                               \
          -DCMAKE_C_COMPILER_LAUNCHER=ccache                                                        \
          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                                                      \
          -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache                                                     \
          -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache                                                   \
          -DIMHEX_PATTERNS_PULL_MASTER=ON                                                           \
          -DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}"                                              \
          -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}"                                                  \
          -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}"                                                 \
          -DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64"   \
          ..
        ninja package

    - name: ⬆️ Upload DMG
      uses: actions/upload-artifact@v3
      with:
        if-no-files-found: error
        name: macOS DMG${{matrix.suffix}} x86_64
        path: build/*.dmg

  macos-arm64:
    runs-on: ubuntu-22.04
    name: 🍎 macOS 12.1 arm64
    steps:
    - name: 🧰 Checkout
      uses: actions/checkout@v3
      with:
        submodules: recursive
    
    - name: 📁 Restore docker /cache
      uses: actions/cache@v3
      with:
        path: cache
        key: build-macos-arm64-cache

    - name: 🐳 Inject /cache into docker
      uses: reproducible-containers/buildkit-cache-dance@v2.1.2 # Doesn't work with a MacOS runner ?
      with:
        cache-source: cache
        cache-target: /cache

    - name: 🛠️ Build using docker
      run: |
        docker buildx build . -f dist/macOS/arm64.Dockerfile --progress=plain --build-arg 'JOBS=4' --build-arg "BUILD_TYPE=$(BUILD_TYPE)" --build-context imhex=$(pwd) --output out

    - name: ⬆️ Upload artifacts
      uses: actions/upload-artifact@v3
      with:
        name: macOS ZIP arm64
        path: out/

      # See https://github.com/actions/cache/issues/342#issuecomment-1711054115
    - name: 🗑️ Delete old cache
      continue-on-error: true
      env:
        GH_TOKEN: ${{ github.token }}
      run: |
          gh extension install actions/gh-actions-cache
          gh actions-cache delete "build-macos-arm64-cache" --confirm
  
  # Ubuntu build
  ubuntu:
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Ubuntu
            release_num: 22.04
          - name: Ubuntu
            release_num: 23.04

    name: 🐧 Ubuntu ${{ matrix.release_num }}
    runs-on: ubuntu-latest

    container:
      image: "ubuntu:${{ matrix.release_num }}"
      options: --privileged

    steps:
      - name: ⬇️ Install setup dependencies
        run: apt update && apt install -y git curl

      - name: 🧰 Checkout
        uses: actions/checkout@v3
        with:
          submodules: recursive

      - name: 📜 Setup ccache
        uses: hendrikmuhs/ccache-action@v1.2
        with:
          key: Ubuntu-${{matrix.release_num}}-ccache-${{ github.run_id }}
          restore-keys: Ubuntu-${{matrix.release_num}}-ccache
          max-size: 1G

      - name: 📜 Restore CMakeCache
        uses: actions/cache@v3
        with:
          path: |
            build/CMakeCache.txt
          key: Ubuntu-${{matrix.release_num}}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}

      - name: ⬇️ Install dependencies
        run: |
          apt update
          bash dist/get_deps_debian.sh

      - name: ⬇️ Install .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '8.0.100'

      - name: 🏔️ Set Environment variables
        run: |
          echo COMMIT_SHA_SHORT=$(git rev-parse --short HEAD) >> $GITHUB_ENV
          echo COMMIT_SHA_LONG=$(git rev-parse HEAD) >> $GITHUB_ENV
          echo COMMIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) >> $GITHUB_ENV

      # Ubuntu cmake build
      - name: 🛠️ Build
        run: |
          set -x
          git config --global --add safe.directory '*'
          mkdir -p build
          cd build
          CC=gcc-12 CXX=g++-12 cmake -G "Ninja"                       \
            -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}                    \
            -DCMAKE_INSTALL_PREFIX="/usr"                             \
            -DCMAKE_C_COMPILER_LAUNCHER=ccache                        \
            -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                      \
            -DIMHEX_PATTERNS_PULL_MASTER=ON                           \
            -DIMHEX_COMMIT_HASH_SHORT="${{ env.COMMIT_SHA_SHORT }}"   \
            -DIMHEX_COMMIT_HASH_LONG="${{ env.COMMIT_SHA_LONG }}"     \
            -DIMHEX_COMMIT_BRANCH="${{ env.COMMIT_BRANCH }}"          \
            -DIMHEX_ENABLE_LTO=ON                                     \
            -DIMHEX_USE_GTK_FILE_PICKER=ON                            \
            -DDOTNET_EXECUTABLE="dotnet"                              \
            ..
          DESTDIR=DebDir ninja install 

      - name: 📜 Set version variable
        run: |
          echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV

      - name: 📦 Bundle DEB
        run: |
          cp -r build/DEBIAN build/DebDir
          dpkg-deb -Zgzip --build build/DebDir
          mv build/DebDir.deb imhex-${{env.IMHEX_VERSION}}-Ubuntu-${{ matrix.release_num }}-x86_64.deb

      - name: ⬆️ Upload DEB
        uses: actions/upload-artifact@v3
        with:
          if-no-files-found: error
          name: Ubuntu ${{ matrix.release_num }} DEB x86_64
          path: '*.deb'

  # AppImage build
  appimage:
    runs-on: ubuntu-22.04
    name: ⬇️ AppImage
    steps:
      - name: 🧰 Checkout
        uses: actions/checkout@v3
        with:
          submodules: recursive

      - name: 📁 Restore docker /cache
        uses: actions/cache@v3
        with:
          path: cache
          key: appimage-ccache-${{ github.run_id }}
          restore-keys: appimage-cache
      
      - name: 🐳 Inject /cache into docker
        uses: reproducible-containers/buildkit-cache-dance@v2.1.2
        with:
          cache-source: cache
          cache-target: /cache

      - name: 🛠️ Build using docker
        run: |
          docker buildx build . -f dist/appimage/Dockerfile --progress=plain --build-arg "BUILD_TYPE=$BUILD_TYPE" \
          --build-arg "GIT_COMMIT_HASH=$GITHUB_SHA" --build-arg "GIT_BRANCH=${GITHUB_REF##*/}" --output out


      - name: ⬆️ Upload AppImage
        uses: actions/upload-artifact@v3
        with:
          if-no-files-found: error
          name: Linux AppImage x86_64
          path: 'out/*.AppImage'

      - name: ⬆️ Upload AppImage zsync
        uses: actions/upload-artifact@v3
        with:
          if-no-files-found: error
          name: Linux AppImage zsync x86_64
          path: 'out/*.AppImage.zsync'

  # ArchLinux build
  archlinux-build:
    name: 🐧 ArchLinux
    runs-on: ubuntu-latest

    container:
      image: archlinux:base-devel

    steps:
      - name: ⬇️ Update all packages
        run: |
          pacman -Syyu --noconfirm

      - name: ⬇️ Install setup dependencies
        run: |
          pacman -Syu git ccache --noconfirm

      - name: 🧰 Checkout
        uses: actions/checkout@v3
        with:
          submodules: recursive

      - name: ⬇️ Install ImHex dependencies
        run: |
          dist/get_deps_archlinux.sh --noconfirm

      - name: ⬇️ Install .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '8.0.100'

      - name: 📜 Setup ccache
        uses: hendrikmuhs/ccache-action@v1.2
        with:
          key: archlinux-ccache-${{ github.run_id }}
          restore-keys: archlinux-ccache
          max-size: 1G

      - name: 📜 Restore CMakeCache
        uses: actions/cache@v3
        with:
          path: |
            build/CMakeCache.txt
          key: archlinux-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}

      # ArchLinux cmake build
      - name: 🛠️ Build
        run: |
          set -x
          mkdir -p build
          cd build
          CC=gcc CXX=g++ cmake -G "Ninja"               \
          -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}        \
          -DCMAKE_INSTALL_PREFIX="/usr"                 \
          -DCMAKE_C_COMPILER_LAUNCHER=ccache            \
          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache          \
          -DUSE_SYSTEM_FMT=ON                           \
          -DUSE_SYSTEM_YARA=ON                          \
          -DUSE_SYSTEM_NLOHMANN_JSON=ON                 \
          -DUSE_SYSTEM_CAPSTONE=OFF                     \
          -DIMHEX_PATTERNS_PULL_MASTER=ON               \
          -DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}"  \
          -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}"      \
          -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}"     \
          -DIMHEX_ENABLE_LTO=ON                         \
          -DIMHEX_USE_GTK_FILE_PICKER=ON                \
          ..
          DESTDIR=installDir ninja install 

      - name: 📜 Set version variable
        run: |
          echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV

      - name: ✒️ Prepare PKGBUILD
        run: |
          cp dist/Arch/PKGBUILD build
          sed -i 's/%version%/${{env.IMHEX_VERSION}}/g' build/PKGBUILD

    # makepkg doesn't want to run as root, so I had to chmod 777 all over
      - name: 📦 Package ArchLinux .pkg.tar.zst
        run: |
          set -x
          cd build

          # the name is a small trick to make makepkg recognize it as the source
          # else, it would try to download the file from the release
          tar -cvf imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst -C installDir .

          chmod -R 777 .

          sudo -u nobody makepkg

          # Replace the old file
          rm imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst
          mv *.pkg.tar.zst imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst

      - name: ⬆️ Upload imhex-archlinux.pkg.tar.zst
        uses: actions/upload-artifact@v3
        with:
          if-no-files-found: error
          name: ArchLinux .pkg.tar.zst x86_64
          path: |
            build/imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst

  # RPM distro builds
  rpm-build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Fedora
            mock_release: rawhide
            release_num: rawhide
            mock_config: fedora-rawhide
          - name: Fedora
            mock_release: f38
            release_num: 38
            mock_config: fedora-38
          - name: Fedora
            mock_release: f37
            release_num: 37
            mock_config: fedora-37
          - name: RHEL-AlmaLinux
            mock_release: epel9
            release_num: 9
            mock_config: "alma+epel-9"

    name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }}
    runs-on: ubuntu-latest

    container:
      image: "fedora:latest"
      options: --privileged

    steps:
      - name: ⬇️ Install git-core
        run: dnf install --disablerepo="*" --enablerepo="fedora" git-core -y

      - name: 🧰 Checkout
        uses: actions/checkout@v3
        with:
          path: ImHex
          submodules: recursive

      - name: 📜 Setup DNF Cache
        uses: actions/cache@v3
        with:
          path: /var/cache/dnf
          key: ${{ matrix.mock_release }}-dnf-${{ github.run_id }}
          restore-keys: |
            ${{ matrix.mock_release }}-dnf-

      - name: ⬇️ Update all packages and install dependencies
        run: |
          set -x
          dnf upgrade --disablerepo="*" --enablerepo="fedora,updates" -y
          dnf install --disablerepo="*" --enablerepo="fedora,updates" -y \
          fedpkg         \
          ccache

      - name: ⬇️ Install .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '8.0.100'

      - name: 📜 Setup ccache
        uses: hendrikmuhs/ccache-action@v1.2.5
        with:
          key: ${{ matrix.mock_release }}-rpm-${{ github.run_id }}
          restore-keys: ${{ matrix.mock_release }}-rpm
          max-size: 1G

      - name: 📜 Set version variable
        run: |
          echo "IMHEX_VERSION=`cat ImHex/VERSION`" >> $GITHUB_ENV

      - name: 🗜️ Create tarball from sources with dependencies
        run: tar --exclude-vcs -czf $GITHUB_WORKSPACE/imhex-$IMHEX_VERSION.tar.gz ImHex

      - name: ✒️ Modify spec file
        run: |
          sed -i \
          -e 's/Version:        [0-9]*\.[0-9]*\.[0-9]*$/Version:        ${{env.IMHEX_VERSION}}/g' \
          -e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g'                                 \
          -e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\'                     \
          -e '/BuildRequires:  cmake/a BuildRequires:  git-core'                                  \
          -e '/%files/a %{_datadir}/%{name}/'                                                     \
          $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec

      - name: 📜 Fix ccache on EL9
        if: matrix.mock_release == 'epel9'
        run: sed -i '/\. \/opt\/rh\/gcc-toolset-12\/enable/a PATH=/usr/lib64/ccache:$PATH' $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec

      - name: 🟩 Copy spec file to build root
        run: mv $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec $GITHUB_WORKSPACE/imhex.spec

      - name: 📜 Enable ccache for mock
        run: |
          cat <<EOT > $GITHUB_WORKSPACE/mock.cfg
          include('${{ matrix.mock_config }}-x86_64.cfg')
          config_opts['plugin_conf']['ccache_enable'] = True
          config_opts['plugin_conf']['ccache_opts']['max_cache_size'] = '1G'
          config_opts['plugin_conf']['ccache_opts']['compress'] = True
          config_opts['plugin_conf']['ccache_opts']['dir'] = "$GITHUB_WORKSPACE/.ccache"
          EOT

      - name: 📜 Setup Mock Cache
        uses: actions/cache@v3
        with:
          path: /var/cache/mock
          key: ${{ matrix.mock_release }}-mock-${{ github.run_id }}
          restore-keys: |
            ${{ matrix.mock_release }}-mock

      # Fedora cmake build (in imhex.spec)
      - name: 📦 Build RPM
        run: |
          fedpkg --path $GITHUB_WORKSPACE --release ${{ matrix.mock_release }} mockbuild --enable-network -N --root $GITHUB_WORKSPACE/mock.cfg extra_args -- -v

      - name: 🟩 Move and rename finished RPM
        run: |
          mv $GITHUB_WORKSPACE/results_imhex/${{env.IMHEX_VERSION}}/*/imhex-${{env.IMHEX_VERSION}}-0.*.x86_64.rpm \
          $GITHUB_WORKSPACE/imhex-${{env.IMHEX_VERSION}}-${{matrix.name}}-${{matrix.release_num}}-x86_64.rpm

      - name: ⬆️ Upload RPM
        uses: actions/upload-artifact@v3
        with:
          if-no-files-found: error
          name: ${{ matrix.name }} ${{ matrix.release_num }} RPM x86_64
          path: |
            imhex-${{env.IMHEX_VERSION}}-${{matrix.name}}-${{matrix.release_num}}-x86_64.rpm
