#!/usr/bin/env bash

######################################################################
# 4) configure and build openzfs modules.  This is run on the VMs.
#
# Usage:
#
#       qemu-4-build-vm.sh OS [--enable-debug][--dkms][--patch-level NUM]
#               [--poweroff][--release][--repo][--tarball]
#
# OS:           OS name like 'fedora41'
# --enable-debug:  Build RPMs with '--enable-debug' (for testing)
# --dkms:       Build DKMS RPMs as well
# --patch-level NUM:    Use a custom patch level number for packages.
# --poweroff:   Power-off the VM after building
# --release     Build zfs-release*.rpm as well
# --repo        After building everything, copy RPMs into /tmp/repo
#               in the ZFS RPM repository file structure.  Also
#               copy tarballs if they were built.
# --tarball:    Also build a tarball of ZFS source
######################################################################

ENABLE_DEBUG=""
DKMS=""
PATCH_LEVEL=""
POWEROFF=""
RELEASE=""
REPO=""
TARBALL=""
while [[ $# -gt 0 ]]; do
  case $1 in
    --enable-debug)
      ENABLE_DEBUG=1
      shift
      ;;
    --dkms)
      DKMS=1
      shift
      ;;
    --patch-level)
      PATCH_LEVEL=$2
      shift
      shift
      ;;
    --poweroff)
      POWEROFF=1
      shift
      ;;
    --release)
      RELEASE=1
      shift
      ;;
    --repo)
      REPO=1
      shift
      ;;
    --tarball)
      TARBALL=1
      shift
      ;;
    *)
      OS=$1
      shift
      ;;
  esac
done

set -eu

function run() {
  LOG="/var/tmp/build-stderr.txt"
  echo "****************************************************"
  echo "$(date) ($*)"
  echo "****************************************************"
  ($@ || echo $? > /tmp/rv) 3>&1 1>&2 2>&3 | stdbuf -eL -oL tee -a $LOG
  if [ -f /tmp/rv ]; then
    RV=$(cat /tmp/rv)
    echo "****************************************************"
    echo "exit with value=$RV ($*)"
    echo "****************************************************"
    echo 1 > /var/tmp/build-exitcode.txt
    exit $RV
  fi
}

# Look at the RPMs in the current directory and copy/move them to
# /tmp/repo, using the directory structure we use for the ZFS RPM repos.
#
# For example:
# /tmp/repo/epel-testing/9.5
# /tmp/repo/epel-testing/9.5/SRPMS
# /tmp/repo/epel-testing/9.5/SRPMS/zfs-2.3.99-1.el9.src.rpm
# /tmp/repo/epel-testing/9.5/SRPMS/zfs-kmod-2.3.99-1.el9.src.rpm
# /tmp/repo/epel-testing/9.5/kmod
# /tmp/repo/epel-testing/9.5/kmod/x86_64
# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug
# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug/kmod-zfs-debuginfo-2.3.99-1.el9.x86_64.rpm
# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug/libnvpair3-debuginfo-2.3.99-1.el9.x86_64.rpm
# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug/libuutil3-debuginfo-2.3.99-1.el9.x86_64.rpm
# ...
function copy_rpms_to_repo {
  # Pick a RPM to query. It doesn't matter which one - we just want to extract
  # the 'Build Host' value from it.
  rpm=$(ls zfs-*.rpm | head -n 1)

  # Get zfs version '2.2.99'
  zfs_ver=$(rpm -qpi $rpm | awk '/Version/{print $3}')

  # Get "2.1" or "2.2"
  zfs_major=$(echo $zfs_ver | grep -Eo [0-9]+\.[0-9]+)

  # Get 'almalinux9.5' or 'fedora41' type string
  build_host=$(rpm -qpi $rpm | awk '/Build Host/{print $4}')

  # Get '9.5' or '41' OS version
  os_ver=$(echo $build_host | grep -Eo '[0-9\.]+$')

  # Our ZFS version and OS name will determine which repo the RPMs
  # will go in (regular or testing).  Fedora always gets the newest
  # releases, and Alma gets the older releases.
  case $build_host in
  almalinux*)
    case $zfs_major in
    2.2)
      d="epel"
      ;;
    *)
      d="epel-testing"
      ;;
    esac
    ;;
  fedora*)
    d="fedora"
    ;;
  esac

  prefix=/tmp/repo
  dst="$prefix/$d/$os_ver"

  # Special case: move zfs-release*.rpm out of the way first (if we built them).
  # This will make filtering the other RPMs easier.
  mkdir -p $dst
  mv zfs-release*.rpm $dst || true

  # Copy source RPMs
  mkdir -p $dst/SRPMS
  cp $(ls *.src.rpm) $dst/SRPMS/

  if [[ "$build_host" =~ "almalinux" ]] ; then
    # Copy kmods+userspace
    mkdir -p $dst/kmod/x86_64/debug
    cp $(ls *.rpm | grep -Ev 'src.rpm|dkms|debuginfo') $dst/kmod/x86_64
    cp *debuginfo*.rpm $dst/kmod/x86_64/debug
  fi

  if [ -n "$DKMS" ] ; then
    # Copy dkms+userspace
    mkdir -p $dst/x86_64
    cp $(ls *.rpm | grep -Ev 'src.rpm|kmod|debuginfo') $dst/x86_64
  fi

  # Copy debug
  mkdir -p $dst/x86_64/debug
  cp $(ls *debuginfo*.rpm | grep -v kmod) $dst/x86_64/debug
}

function freebsd() {
  extra="${1:-}"

  export MAKE="gmake"
  echo "##[group]Autogen.sh"
  run ./autogen.sh
  echo "##[endgroup]"

  echo "##[group]Configure"
  run ./configure \
    --prefix=/usr/local \
    --with-libintl-prefix=/usr/local \
    --enable-pyzfs \
    --enable-debuginfo $extra
  echo "##[endgroup]"

  echo "##[group]Build"
  run gmake -j$(nproc)
  echo "##[endgroup]"

  echo "##[group]Install"
  run sudo gmake install
  echo "##[endgroup]"
}

function linux() {
  extra="${1:-}"

  echo "##[group]Autogen.sh"
  run ./autogen.sh
  echo "##[endgroup]"

  echo "##[group]Configure"
  run ./configure \
    --prefix=/usr \
    --enable-pyzfs \
    --enable-debuginfo $extra
  echo "##[endgroup]"

  echo "##[group]Build"
  run make -j$(nproc)
  echo "##[endgroup]"

  echo "##[group]Install"
  run sudo make install
  echo "##[endgroup]"
}

function rpm_build_and_install() {
  extra="${1:-}"

  # Build RPMs with XZ compression by default (since gzip decompression is slow)
  echo "%_binary_payload w7.xzdio" >> ~/.rpmmacros

  echo "##[group]Autogen.sh"
  run ./autogen.sh
  echo "##[endgroup]"

  if [ -n "$PATCH_LEVEL" ] ; then
    sed -i -E 's/(Release:\s+)1/\1'$PATCH_LEVEL'/g' META
  fi

  echo "##[group]Configure"
  run ./configure --enable-debuginfo $extra
  echo "##[endgroup]"

  echo "##[group]Build"
  run make pkg-kmod pkg-utils
  echo "##[endgroup]"

  if [ -n "$DKMS" ] ; then
    echo "##[group]DKMS"
    make rpm-dkms
    echo "##[endgroup]"
  fi

  if [ -n "$REPO" ] ; then
    echo "Skipping install since we're only building RPMs and nothing else"
  else
    echo "##[group]Install"
    run sudo dnf -y --nobest install $(ls *.rpm | grep -Ev 'dkms|src.rpm')
    echo "##[endgroup]"
  fi

  # Optionally build the zfs-release.*.rpm
  if [ -n "$RELEASE" ] ; then
    echo "##[group]Release"
    pushd ~
    sudo dnf -y install rpm-build || true
    # Check out a sparse copy of zfsonlinux.github.com.git so we don't get
    # all the binaries.  We just need a few kilobytes of files to build RPMs.
    git clone --depth 1 --no-checkout \
      https://github.com/zfsonlinux/zfsonlinux.github.com.git

    cd zfsonlinux.github.com
    git sparse-checkout set zfs-release
    git checkout
    cd zfs-release

    mkdir -p ~/rpmbuild/{BUILDROOT,SPECS,RPMS,SRPMS,SOURCES,BUILD}
    cp RPM-GPG-KEY-openzfs* *.repo ~/rpmbuild/SOURCES
    cp zfs-release.spec ~/rpmbuild/SPECS/
    rpmbuild -ba ~/rpmbuild/SPECS/zfs-release.spec

    # ZFS release RPMs are built.  Copy them to the ~/zfs directory just to
    # keep all the RPMs in the same place.
    cp ~/rpmbuild/RPMS/noarch/*.rpm ~/zfs
    cp ~/rpmbuild/SRPMS/*.rpm ~/zfs

    popd
    rm -fr ~/rpmbuild
    echo "##[endgroup]"
  fi

  if [ -n "$REPO" ] ; then
    echo "##[group]Repo"
    copy_rpms_to_repo
    echo "##[endgroup]"
  fi
}

function deb_build_and_install() {
  extra="${1:-}"

  echo "##[group]Autogen.sh"
  run ./autogen.sh
  echo "##[endgroup]"

  echo "##[group]Configure"
  run ./configure \
    --prefix=/usr \
    --enable-pyzfs \
    --enable-debuginfo $extra
  echo "##[endgroup]"

  echo "##[group]Build"
  run make native-deb-kmod native-deb-utils
  echo "##[endgroup]"

  echo "##[group]Install"
  # Do kmod install.  Note that when you build the native debs, the
  # packages themselves are placed in parent directory '../' rather than
  # in the source directory like the rpms are.
  run sudo apt-get -y install $(find ../ | grep -E '\.deb$' \
    | grep -Ev 'dkms|dracut')
  echo "##[endgroup]"
}

function build_tarball {
  if [ -n "$REPO" ] ; then
    ./autogen.sh
    ./configure --with-config=srpm
    make dist
    mkdir -p /tmp/repo/releases
    # The tarball name is based off of 'Version' field in the META file.
    mv *.tar.gz /tmp/repo/releases/
  fi
}

# Debug: show kernel cmdline
if [ -f /proc/cmdline ] ; then
  cat /proc/cmdline || true
fi

# Set our hostname to our OS name and version number.  Specifically, we set the
# major and minor number so that when we query the Build Host field in the RPMs
# we build, we can see what specific version of Fedora/Almalinux we were using
# to build them.  This is helpful for matching up KMOD versions.
#
# Examples:
#
# rhel8.10
# almalinux9.5
# fedora42
source /etc/os-release
 if which hostnamectl &> /dev/null ; then
  # Fedora 42+ use hostnamectl
  sudo hostnamectl set-hostname "$ID$VERSION_ID"
  sudo hostnamectl set-hostname --pretty "$ID$VERSION_ID"
else
  sudo hostname "$ID$VERSION_ID"
fi

# save some sysinfo
uname -a > /var/tmp/uname.txt

# Check if we're running this script from within a VM or on the runner itself.
# Most of the time we will be running in a VM, but the ARM builder actually
# runs this script on the runner.  If we happen to be running on the ARM
# runner, we will start in the ZFS source directory.  If we're running on a VM
# then we'll just start in our home directory, and will need to 'cd' into our
# source directory.
if [ ! -e META ] ; then
  cd $HOME/zfs
fi

export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin"

extra=""
if [ -n "$ENABLE_DEBUG" ] ; then
  extra="--enable-debug"
fi

# build
case "$OS" in
  freebsd*)
    freebsd "$extra"
    ;;
  alma*|centos*)
    rpm_build_and_install "--with-spec=redhat $extra"
    ;;
  fedora*)
    rpm_build_and_install "$extra"

    # Historically, we've always built the release tarballs on Fedora, since
    # there was one instance long ago where we built them on CentOS 7, and they
    # didn't work correctly for everyone.
    if [ -n "$TARBALL" ] ; then
        build_tarball
    fi
    ;;
  debian*|ubuntu*)
    deb_build_and_install "$extra"
    ;;
  *)
    linux "$extra"
    ;;
esac


# building the zfs module was ok
echo 0 > /var/tmp/build-exitcode.txt

# reset cloud-init configuration and poweroff
if [ -n "$POWEROFF" ] ; then
        sudo cloud-init clean --logs
        sync && sleep 2 && sudo poweroff &
fi
exit 0
