#! /usr/bin/env bash
# Copyright (c) 2016 - 2020 DisplayLink (UK) Ltd.

set -e
set -u

usage() {
  local bold=$(tput bold)
  local underline=$(tput smul)
  local normal=$(tput sgr0)

  cat << EOF
${bold}SYNOPSIS${normal}
      ${bold}${0}${normal} [MODE] [OPTION]... [PARAMETER]...

${bold}DESCRIPTION${normal}
      Script automates ${underline}module${normal} build against specific ${underline}kernel${normal} version.
      It can download fresh copy of sources from ${underline}github${normal}, untar it,
      prepare and build evdi in this 'sandboxed' environment.

${bold}MODES${normal}
      ${bold}--clean${normal}
            Get clean version of ${bold}tar.gz${normal}.
            This is the ${underline}default${normal} behaviour.
      ${bold}--cached${normal}
            Use previously downloaded ${bold}tar.gz${normal}.
      ${bold}--ci${normal}
            Use previously extracted sources from ${bold}tar.gz${normal}.

      ${bold}--repo${normal}
      ${bold}--repo-clean${normal}
            Use clean clone from updated local mirror of ${underline}kernel${normal} repository.
      ${bold}--repo-cached${normal}
            Use clean clone from local mirror of ${underline}kernel${normal} repository.
      ${bold}--repo-ci${normal}
            Use previous clone from local mirror of ${underline}kernel${normal} repository.

${bold}OPTIONS${normal}
      ${bold}-j${normal} ${underline}jobs${normal}, ${bold}--jobs=${normal}${underline}jobs${normal}, ${bold}--jobs${normal} ${underline}jobs${normal}
            Specifies the number of commands to run simultaneously.
            Similar as in ${bold}make${normal}.
            Default is ${underline}1${normal}.

      ${bold}-t${normal} ${underline}dir${normal}, ${bold}--tmp=${normal}${underline}dir${normal}, ${bold}--tmp${normal} ${underline}dir${normal}
            Subdirectory to store kernel sources.
            Default is ${underline}tmp${normal}.

      ${bold}--help${normal}
            Show this message.

${bold}PARAMETERS${normal}
      ${bold}all${normal}
            Build against all ${underline}KVER${normal} specified in ${bold}.travis.yml${normal} file.
      ${underline}KVERS${normal}
            List of kernel versions to build against (e.g. ${underline}3.19 4.10${normal}).

${bold}EXAMPLES${normal}
      ${bold}${0} 4.12${normal}
            Build against newly downloaded sources of ${underline}4.12${normal}.

      ${bold}${0} --ci master${normal}
            Build against already ${underline}extracted${normal} sources of ${underline}master${normal}.

      ${bold}${0} --ci master --cached 4.11${normal}
            Build against already ${underline}extracted${normal} sources of ${underline}master${normal} and
            clean sources of previously ${underline}cached${normal} ${bold}tar.gz${normal} of ${underline}4.11${normal}.

      ${bold}${0} -j 4 all${normal}
            Build against ${underline}all${normal} kernels listed in ${bold}.travis.yml${normal}
            file, passing ${underline}--jobs=4${normal} to ${bold}make${normal}.

      ${bold}${0} --repo-cached 5.5${normal}
            Build against clean clone of ${underline}5.5${normal} kernel from local
            ${underline}kernel${normal} mirror.

      ${bold}${0} --repo-ci 5.6-rc1${normal}
            Build against prevously cloned ${underline}5.6-rc1${normal} kernel from
            local ${underline}kernel${normal} mirror.
EOF
}

get_versions_from_file() { # file
  local PREFIX="^  \- KVER="
  grep "${PREFIX}" < "${1}" | sed -e "s/${PREFIX}//g"
}

prepare_tar_gz_sources() { # PACKAGE SOURCES CC
  local PACKAGE="${1}.tar.gz"
  local SOURCES="${2}"
  local CC="${3}"
  local SRC_DIR="$(pwd)/${SOURCES}"

  if [ "${MODE}" = "CLEAN" ]; then
    rm -f "${PACKAGE}"
  fi

  if [ ! -e "${PACKAGE}" ]; then
    wget -O "${PACKAGE}" "https://github.com/torvalds/linux/archive/${PACKAGE}"
  fi

  if [ "${MODE}" = "CLEAN" ] || [ "${MODE}" = "CACHED" ]; then
    rm -rf "${SOURCES}"
  fi

  if [ ! -e "${SOURCES}" ]; then
    tar -xzf "${PACKAGE}"
    prepare_kernel "${SRC_DIR}" "${CC}"
  elif [ "${MODE}" != "CI" ]; then
    prepare_kernel "${SRC_DIR}" "${CC}"
  fi
}

prepare_repo_sources() { # PACKAGE SOURCES CC
  local PACKAGE="${1}"
  local SOURCES="${2}"
  local CC="${3}"
  local SRC_DIR="$(pwd)/${SOURCES}"

  if [ ! -e linux.git ]; then
    git clone --bare https://github.com/torvalds/linux linux.git
    git remote add github https://github.com/torvalds/linux
  fi

  if [ "${MODE}" = "REPO-CLEAN" ]; then (
    cd linux.git
    git fetch github
  ) fi

  if [ "${MODE}" = "REPO-CLEAN" ] || [ "${MODE}" = "REPO-CACHED" ]; then
    rm -rf "${SOURCES}"
  fi

  if [ ! -e "${SOURCES}" ]; then
    git clone --depth 1 --branch "${PACKAGE}" "file://$(pwd)/linux.git" "${SOURCES}"
    prepare_kernel "${SRC_DIR}" "${CC}"
  elif [ "${MODE}" != "REPO-CI" ]; then
    prepare_kernel "${SRC_DIR}" "${CC}"
  fi
}

prepare_sources() { ( # PACKAGE SOURCES CC
  if [ ! -d "${TMP}" ]; then
    mkdir "${TMP}"
  fi

  cd "${TMP}"

  if [[ ${MODE} == REPO* ]]; then
    prepare_repo_sources "${1}" "${2}" "${3}"
  else
    prepare_tar_gz_sources "${1}" "${2}" "${3}"
  fi
) }

prepare_kernel() { ( # SRC_DIR CC
  cd "${1}"
  make olddefconfig CC="${2}" --jobs=${JOBS}
  make prepare CC="${2}" --jobs=${JOBS}
  make modules_prepare CC="${2}" --jobs=${JOBS}

  if [[ ${KVER:0:1} < "5" ]]; then
    make scripts CC="${2}" --jobs=${JOBS}
  fi
) }

build_one() { # KVER
  local KVER=${1}
  local CC="${CC:-gcc}"

  local HEADER="$(echo "# Running ${MODE} --jobs=${JOBS} build for ${KVER} in ${TMP}")"

  echo ""
  echo "${HEADER//?/#}"
  echo "${HEADER}"
  echo ""

  local src_dir
  if [ "${KVER}" = "master" ]; then
    prepare_sources "master" "linux-master" "${CC}"
    src_dir="$(pwd)/${TMP}/linux-master"
  else
    prepare_sources "v${KVER}" "linux-${KVER}" "${CC}"
    src_dir="$(pwd)/${TMP}/linux-${KVER}"
  fi

  make KDIR="${src_dir}" CC="${CC}" --jobs=${JOBS}
}

MODE="CLEAN"
JOBS="1"
TMP="tmp"
while [[ $# -gt 0 ]]
do
  case ${1} in
    --clean)
      MODE="CLEAN"
      ;;

    --cached)
      MODE="CACHED"
      ;;

    --ci)
      MODE="CI"
 	     ;;

    --repo|--repo-clean)
      MODE="REPO-CLEAN"
      ;;

    --repo-cached)
      MODE="REPO-CACHED"
      ;;

    --repo-ci)
      MODE="REPO-CI"
      ;;

    -j|--jobs)
      shift
      JOBS="${1}"
      ;;

    --jobs=*)
      JOBS="${1#*=}"
      ;;

    -t|--tmp)
      shift
      TMP="${1}"
      ;;

    --tmp=*)
      TMP="${1#*=}"
      ;;

    -h|--help)
      usage
      exit 0
      ;;

    -*)
      echo "Unknown option '${1}'" >&2
      usage >&2
      exit 1
      ;;

    all)
      for KVER in $(get_versions_from_file '.travis.yml'); do
        build_one "${KVER}"
      done
      ;;

    *)
      build_one "${1}"
      ;;
  esac

  shift
done

