#!/bin/bash
UPDATED="2025-04-24"

DISABLE=0     # disable options
ENABLE=1      # enable options
PATCHES=0     # enable/disable patch apply
UARCH=0       # apply more uarch patch
O3=1          # use -O3 vs -O2 (optimisation)
ARCH="native" # target architecture (uarch patch)
CONFIGCLOUD=0 # enable cloud 'from' config
CONFIGOLD=1   # enable old def config
CONFIGMOD=0   # enable all mod config
SCRATCH=0     # perform from scratch (remove preexisting content)
UNCOMPRESS=1  # perform uncompress if already exist
CLEANUP=1     # perform folder cleanup
SECURED=1     # enable/disable security
CLANG=0       # use Clang compiler (if not, use GCC)
TESTING=0     # add testing options
NVIDIA=1      # add support for nvidia
BARE=1        # compile kernel for bare-metal

#
# Sources:
#   https://github.com/sn99/Optimizing-linux#compiling-your-kernel
#   https://wiki.gentoo.org/wiki/Kernel/Optimization
#

# config-cloud-amd64:
#   https://packages.debian.org/source/sid/linux
#       linux-image-6.12.21-cloud-amd64-unsigned
#   Go down to download section, select amd64 and download deb.
#   Open .deb with archiver, browse to boot folder and grab 'config-6.12.21-cloud-amd64'.
#

# more-uarches-for-kernel.patch:
#   https://github.com/graysky2/kernel_compiler_patch
#       more-uarches-for-kernel.patch : more-uarches-for-kernel-6.8-rc4+.patch
#

#
# Prerequisites:
#   sudo apt install build-essential fakeroot dpkg-dev perl libssl-dev bc gnupg dirmngr libncurses-dev libelf-dev flex bison lsb-release rsync dwarves clang llvm lld debhelper
#

#
# In case of usage of uninstall.sh, you must reinstall 'linux-libc-dev' with the version of used kernel
#

#
# To make a diff of different .config file:
#   diff --side-by-side --suppress-common-lines --ignore-tab-expansion --ignore-trailing-space --ignore-space-change --ignore-blank-lines --text CONFIG1 CONFIG2
#

# Ubuntu:
#   sudo apt-get install linux-source
#   In kernel sources folder:
#       sudo openssl req -x509 -newkey rsa:4096 -keyout certs/mycert.pem -out certs/mycert.pem -nodes -days 3650
#       sudo nano .config
#           CONFIG_MODULE_SIG_KEY="certs/mycert.pem"
#           CONFIG_SYSTEM_TRUSTED_KEYRING=y
#           CONFIG_SYSTEM_TRUSTED_KEYS="certs/mycert.pem"
#           CONFIG_SYSTEM_EXTRA_CERTIFICATE=y
#           CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE=4096
#           CONFIG_SECONDARY_TRUSTED_KEYRING=y
#           CONFIG_SYSTEM_BLACKLIST_KEYRING=y
#           CONFIG_SYSTEM_BLACKLIST_HASH_LIST=""
#

doBuildSystem() {
    echo "v$UPDATED"
}

# Get target distribution name
doTargetName() {
    case ${TARGET} in
    debian)
        TARGETNAME="Debian"
        ;;
    ubuntu)
        TARGETNAME="Ubuntu"
        ;;
    esac
}

# Display introduction
doIntro() {
    echo
    doTargetName
    if [ $TESTING == 1 ]; then
        echo "$TARGETNAME Kernel Builder: $HOSTNAME [TEST]"
    else
        echo "$TARGETNAME Kernel Builder: $HOSTNAME"
    fi
    doBuildSystem
    echo
}

# Show date/time header
doHeader() {
    NOW=$(date +"%Y/%m/%d %H:%M:%S")
    echo "- $NOW"
    echo ""
}

if [ "$(id -u)" != "0" ]; then
    doIntro
    doHeader
    echo
    echo "This script must be run as root" 1>&2
    echo
    exit 1
fi

# Exit fail with message
doFail() {
    result=$1
    msg=$2
    if [ ! result==0 ]; then
        echo ">>> Error in download!"
        exit 1
    fi
}

LOGNAME=kernel
LOGEXT=log
LOGFILE=""
NPROC=$(nproc)
HOSTNAME=$(hostname)
SELF=$(realpath $0)
SCRIPT=$(basename $SELF)
CWD=$(dirname $SELF)
CURRENT=$CWD
CONFIGS=$CURRENT/configs
PATCHES=$CURRENT/patches
OPTIONS=$CURRENT/options
CERTS=$CURRENT/certs

BRANCH=$1
BRANCH="${BRANCH:=help}"
VERSION=$2
VERSION="${VERSION:=help}"
TARGET=$3
TARGET="${TARGET:=debian}"
WORKDIR=""
REVISION=1

cd $CURRENT

# Perform specific distribution adjustments
doTarget() {
    case ${TARGET} in
    ubuntu)
        DISABLE=0
        PATCHES=0
        UARCH=0
        ARCH="native"
        CONFIGCLOUD=0
        CONFIGMOD=0
        CONFIGOLD=1
        SCRATCH=0
        ;;
    *) ;;
    esac
}

# Perform adaptation bases on target specificity
doTargetAdapt() {
    cd $WORKDIR

    doTargetName
    case ${TARGET} in
    ubuntu)
        doEchoStep "Adaptation for: ${TARGETNAME}"

        doEchoStep " - Certificates generation"
        openssl req -x509 -newkey rsa:4096 -keyout certs/zogg.pem -out certs/zogg.pem -nodes -days 3650 -config $CERTS/zogg.cnf
        result=$? && doFail $result ">>> Error in openssl!"

        doEchoStep " - Options override"
        ./scripts/config --set-str CONFIG_MODULE_SIG_KEY "certs/zogg.pem"
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --enable CONFIG_SYSTEM_TRUSTED_KEYRING
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS "certs/zogg.pem"
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --enable CONFIG_SYSTEM_EXTRA_CERTIFICATE
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --set-val CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE 4096
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --enable CONFIG_SECONDARY_TRUSTED_KEYRING
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --enable CONFIG_SYSTEM_BLACKLIST_KEYRING
        result=$? && doFail $result ">>> Error in script-config!"

        ./scripts/config --set-str CONFIG_SYSTEM_BLACKLIST_HASH_LIST ""
        result=$? && doFail $result ">>> Error in script-config!"

        export CC="x86_64-pc-linux-gnu"
        ;;
    *) ;;
    esac
}

# Force sync & flush
doSync() {
    sync
    echo 3 >/proc/sys/vm/drop_caches
}

# Display header infos
doHead() {
    doIntro
    doHeader
}

# Display help
doHelp() {
    doIntro
    doHeader

    if [ $BRANCH != "help" ]; then
        echo ">>> Unspecified, unknown or invalid option specified!"
        echo
    fi
    echo "Usage: $SCRIPT 'branch' 'version' 'target'"
    echo
    echo "branch : Main branch (eg. 6.x) [*]"
    echo "version: Full version tag (eg. 6.6.1) [*]"
    echo "target: Target distribution (eg. debian/default, ubuntu)"
    echo
    echo " [*] : parameter required"
    echo
}

# Display step infos
doEchoStep() {
    NOW=$(date +"%Y/%m/%d %H:%M:%S")
    echo "### $NOW - $1"
}

# Scratch
doScratch() {
    if [ $SCRATCH == 1 ]; then
        if [ -d $WORKDIR ]; then
            doEchoStep "Scratch: remove existing content"
            rm -rf $WORKDIR
            result=$? && doFail $result ">>> Error!"
        else
            doEchoStep "Scratch: existing previous content not found"
        fi
    fi
}

# Download
doDownload() {
    if [[ -d $WORKDIR && -f $WORKDIR/linux-$VERSION.tar.xz ]]; then
        doEchoStep "$BRANCH/$VERSION already present (don't download)"
    else
        mkdir -p $WORKDIR
        cd $WORKDIR

        doEchoStep "Download branch '$BRANCH' version '$VERSION'"

        wget --compression=auto --show-progress --no-verbose --inet4-only https://cdn.kernel.org/pub/linux/kernel/v$BRANCH/linux-$VERSION.tar.sign
        result=$? && doFail $result ">>> Error in download (sign)!"

        wget --compression=auto --show-progress --no-verbose --inet4-only https://cdn.kernel.org/pub/linux/kernel/v$BRANCH/linux-$VERSION.tar.xz
        result=$? && doFail $result ">>> Error in download (kernel)!"

        doSync
    fi
}

# Uncompress
doPerformUncompress() {
    cd $WORKDIR
    doEchoStep "Uncompress"

    rm -rf linux-$VERSION

    tar -xaf linux-$VERSION.tar.xz
    result=$? && doFail $result ">>> Error in uncompress!"
}
doUncompress() {
    if [ -d $WORKDIR ]; then
        if [ $UNCOMPRESS == 1 ]; then
            doPerformUncompress
        else
            doEchoStep "$BRANCH/$VERSION already present (don't uncompress)"
        fi
    else
        doPerformUncompress
    fi

    doSync
}

# Clean folder
doCleanup() {
    if [ $CLEANUP == 1 ]; then
        cd $WORKDIR
        doEchoStep "Cleanup"

        if [ "$CLANG" == "1" ]; then
            make -j${NPROC} LLVM=1 CC="ccache clang" distclean
            result=$? && doFail $result ">>> Error in cleanup!"
        else
            make -j${NPROC} CC="ccache gcc" distclean
            result=$? && doFail $result ">>> Error in cleanup!"
        fi
    fi
}

# Copy .config from cloud kernel
doConfigCloud() {
    if [ $CONFIGCLOUD == 1 ]; then
        cd $WORKDIR
        doEchoStep "Copy cloud kernel .config"

        if [ -f .config ]; then
            cp .config .config.cloud.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        cp $CONFIGS/cloud-amd64 .config
        result=$? && doFail $result ">>> Error .config copy!"

        cp .config .config.cloud.after
    fi
}

# Generate .config from old kernel
doOldOne() {
    if [ $CONFIGOLD == 1 ]; then
        cd $WORKDIR
        doEchoStep "Generate config from old kernel .config"

        if [ -f .config ]; then
            cp .config .config.old.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        if [ "$CLANG" == "1" ]; then
            make -j${NPROC} LLVM=1 CC="ccache clang" olddefconfig
            result=$? && doFail $result ">>> Error in generate .config!"
        else
            make -j${NPROC} CC="ccache gcc" olddefconfig
            result=$? && doFail $result ">>> Error in generate .config!"
        fi

        cp .config .config.old.after
    fi
}

# Define all modules not included in kernel
doAllMods() {
    if [ $CONFIGMOD == 1 ]; then
        cd $WORKDIR
        doEchoStep "Set all modules to be 'module'"

        if [ -f .config ]; then
            cp .config .config.mod.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        if [ "$CLANG" == "1" ]; then
            make -j${NPROC} LLVM=1 CC="ccache clang" allmodconfig
            result=$? && doFail $result ">>> Error in set all modules not in kernel!"
        else
            make -j${NPROC} CC="ccache gcc" allmodconfig
            result=$? && doFail $result ">>> Error in set all modules not in kernel!"
        fi

        cp .config .config.mod.after
    fi
}

# Define permissions (user/group) to first user created (default 1000)
doPermissions() {
    cd $WORKDIR
    doEchoStep "Define user/group"

    # chown -R 1000:1000 $CURRENT/build/$BRANCH/$VERSION/.
    chown -R 1000:1000 $CURRENT/build
    result=$? && doFail $result ">>> Error in chown!"

    doSync
}

# Strip signature
doStripSig() {
    cd $WORKDIR
    doEchoStep "Remove signature/keys"

    if [ -f .config ]; then
        cp .config .config.stripsig.before
        result=$? && doFail $result ">>> Error .config stripsig (before)!"
    fi

    ./scripts/config --disable MODULE_SIG_ALL
    result=$? && doFail $result ">>> Error in script-config!"

    ./scripts/config --set-str CONFIG_MODULE_SIG_KEY ""
    result=$? && doFail $result ">>> Error in script-config!"

    ./scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEY ""
    result=$? && doFail $result ">>> Error in script-config!"

    ./scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
    result=$? && doFail $result ">>> Error in script-config!"

    cp .config .config.stripsig.after
}

# Strip debug informations
doStripDebug() {
    cd $WORKDIR
    doEchoStep "Remove debug informations"

    if [ -f .config ]; then
        cp .config .config.stripdebug.before
        result=$? && doFail $result ">>> Error .config copy (before)!"
    fi

    ./scripts/config --disable DEBUG_INFO
    result=$? && doFail $result ">>> Error in script-config!"

    ./scripts/config --enable DEBUG_INFO_NONE
    result=$? && doFail $result ">>> Error in script-config!"

    cp .config .config.stripdebug.after
}

# Apply more patchs
doApplyPatches() {
    if [ $PATCHES == 1 ]; then

        if [ $UARCH == 1 ] && [ -f $PATCHES/more-uarches.patch ]; then
            cd $WORKDIR
            doEchoStep "Apply 'uarches' patch"

            if [ -f .config ]; then
                cp .config .config.uarches.before
                result=$? && doFail $result ">>> Error .config copy (before)!"
            fi
            patch -p1 <$PATCHES/more-uarches.patch
            result=$? && doFail $result ">>> Error in 'uarches'!"

            cp .config .config.uarches.after
        fi

    fi
}

# Exexcute './scripts/config' from file input
doScriptsConfigFile() {
    SWITCH=$1
    FILE=$2

    while read -r option; do
        if [[ 
            (-n $option) &&
            (${option:0:1} != ';') ]]; then
            # if not null
            # skip when starting with ';' (comment)

            ./scripts/config --${SWITCH} $option
            result=$? && doFail $result ">>> Error in script-config!"
        fi
    done <"$FILE"
}

# Generate defaults 'disabled' options for this kernel
doDefaultsDisable() {
    if [ $DISABLE == 1 ]; then
        doEchoStep "Options: disable"

        if [ -f .config ]; then
            cp .config .config.disable.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        doScriptsConfigFile disable $OPTIONS/disable/debug.txt

        if [ $BARE == 1 ]; then
            doScriptsConfigFile disable $OPTIONS/disable/bare.txt
        else
            doScriptsConfigFile disable $OPTIONS/disable/vm.txt
        fi

        cp .config .config.disable.after
    fi
}

# Generate defaults 'enabled' options for this kernel
doDefaultsEnable() {
    if [ $ENABLE == 1 ]; then
        doEchoStep "Options: enable"

        if [ -f .config ]; then
            cp .config .config.enable.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        if [ "$CLANG" == "1" ]; then
            doScriptsConfigFile enable $OPTIONS/enable/clang.txt
        fi
        doScriptsConfigFile enable $OPTIONS/enable/vm.txt

        case ${ARCH} in
        "x86-64-v2")
            ./scripts/config --enable CONFIG_GENERIC_CPU2
            result=$? && doFail $result ">>> Error in script-config!"
            ;;
        "x86-64-v3")
            ./scripts/config --enable CONFIG_GENERIC_CPU3
            result=$? && doFail $result ">>> Error in script-config!"
            ;;
        "x86-64-v4")
            ./scripts/config --enable CONFIG_GENERIC_CPU4
            result=$? && doFail $result ">>> Error in script-config!"
            ;;
        esac

        cp .config .config.enable.after
    fi
}

# Generate defaults 'mitigations' options for this kernel
doDefaultMitigations() {
    if [ -f .config ]; then
        cp .config .config.mitigations.before
        result=$? && doFail $result ">>> Error .config copy (before)!"
    fi

    if [ $SECURED == 0 ]; then
        doEchoStep "Options: secured OFF"
        doScriptsConfigFile disable $OPTIONS/disable/unsecure.txt
    else
        doEchoStep "Options: secured ON"
        doScriptsConfigFile enable $OPTIONS/enable/secure.txt
    fi

    cp .config .config.mitigations.after
}

# Generate defaults 'testing' options for this kernel
doDefaultsTesting() {
    if [ $TESTING == 1 ]; then
        doEchoStep "Options: testings activated..."

        if [ -f .config ]; then
            cp .config .config.testing.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        ./scripts/config --set-str CONFIG_LOCALVERSION '-test'
        result=$? && doFail $result ">>> Error in script-config!"

        cp .config .config.testing.after
    fi
}

# Generate defaults 'NVIDIA' options for this kernel
doDefaultsNvidia() {
    if [ $NVIDIA == 1 ]; then
        doEchoStep "Options: NVIDIA specific used"

        if [ -f .config ]; then
            cp .config .config.nvidia.before
            result=$? && doFail $result ">>> Error .config copy (before)!"
        fi

        doEchoStep "Options: NVIDIA disable"
        doScriptsConfigFile disable $OPTIONS/disable/nvidia.txt

        cp .config .config.nvidia.after
    fi
}

# Generate defaults options for this kernel
doDefaults() {
    cd $WORKDIR

    if [ -f .config ]; then
        cp .config .config.default.before
        result=$? && doFail $result ">>> Error .config copy (before)!"
    fi

    doApplyPatches

    SEC="-unsecure"
    if [ "$SECURED" == "1" ]; then
        SEC="-secure"
    fi

    doEchoStep "Define options"
    if [ "$CLANG" == "1" ]; then
        ./scripts/config --set-str CONFIG_LOCALVERSION "${SEC}-clang"
        result=$? && doFail $result ">>> Error in script-config!"
    else
        ./scripts/config --set-str CONFIG_LOCALVERSION "${SEC}-gcc"
        result=$? && doFail $result ">>> Error in script-config!"
    fi

    doDefaultsDisable
    doDefaultsEnable
    doDefaultMitigations
    doDefaultsNvidia
    doDefaultsTesting

    cp .config .config.default.after
}

# Edit .config
doEditSettings() {
    cd $WORKDIR
    doEchoStep "Settings tuning!"

    if [ -f .config ]; then
        cp .config .config.edit.before
        result=$? && doFail $result ">>> Error .config copy (before)!"
    fi

    if [ "$CLANG" == "1" ]; then
        make -j${NPROC} LLVM=1 CC="ccache clang" menuconfig
        result=$? && doFail $result ">>> Error in settings edit!"
    else
        make -j${NPROC} CC="ccache gcc" menuconfig
        result=$? && doFail $result ">>> Error in settings edit!"
    fi

    cp .config .config.edit.after
}

doGenerateUninstall() {
    # remove old files
    READY=$CURRENT/build/$BRANCH/$VERSION/ready
    mkdir -p $READY
    rm -rf $READY/*.*

    if ls $WORKDIR/../*.deb 1>/dev/null 2>&1; then

        # find .deb packages and generate
        #   uninstall commands
        #   package name
        PACKAGES=
        TAG="_${VERSION}-${REVISION}_amd64.deb"
        search_dir=$WORKDIR/../

        for entry in "$search_dir"/*.deb; do
            name="${entry##*/}"
            pname=${name//$TAG/}

            if [ ! "$pname" == "linux-libc-dev" ]; then
                PACKAGES="$PACKAGES $pname"
            fi

            echo "$name" >>$READY/packages.log
        done

        # move files to destination folder
        mv -f $WORKDIR/../*.deb $READY/

        # prepare uninstall script
        cat <<-EOF >${READY}/uninstall.sh
#!/bin/bash
# ${UPDATED}
apt remove --purge ${PACKAGES}
sudo sync
exit 0
EOF
        result=$? && doFail $result ">>> Error generate uninstall.sh!"

        # generate install script
        cat <<-EOF >${READY}/install.sh
#!/bin/bash
# ${UPDATED}
echo 'blacklist pcspkr' > /etc/modprobe.d/pcspkr.conf
sudo dpkg -i *.deb
sudo sync
exit 0
EOF
        result=$? && doFail $result ">>> Error generate install.sh!"

        # Set execution attribute
        chmod +x $READY/*.sh
        result=$? && doFail $result ">>> Error chmod!"

    else
        echo ">>> No packages have been created!"
        exit 1
    fi

    # setup execution rights on packages
    chown -R root:root $READY/.
    result=$? && doFail $result ">>> Error chown!"
}

# Print compilation end informations
doEnding() {
    clear
    doHead

    doEchoStep "Generated packages :"
    echo
    cat $CURRENT/build/$BRANCH/$VERSION/ready/packages.log
    echo
    echo "In folder: $CURRENT/build/$BRANCH/$VERSION/ready"

    doSync
}

# Perform effective compilation
doCompile() {
    cd $WORKDIR

    if [ -f ../revision.log ]; then
        REVISION="$(head -1 ../revision.log)"
        REVISION=$((REVISION + 1))
    fi
    echo $REVISION >../revision.log

    doStripSig
    doStripDebug

    if [ $O3 == 1 ]; then
        GCCO="3"
    else
        GCCO="2"
    fi

    doEchoStep "Compilation time... Be patient!"

    if [ "$CLANG" == "1" ]; then
        doEchoStep "Compiler: CLANG"
    else
        doEchoStep "Compiler: GCC"
    fi

    export MAKEFLAGS="-j$((NPROC + 1)) -l${NPROC} -Wno-error"
    export CFLAGS="-march=${ARCH} -O${GCCO} -flto -pipe -msse -msse2 -msse3 -mmmx"
    export CXXFLAGS="${CFLAGS}"
    export KCFLAGS="-march=${ARCH} -O${GCCO}"
    export KCPPFLAGS="${KCFLAGS}"

    set CONFIG_SITE=/etc/dpkg-cross/cross-config.amd64
    set DEB_BUILD_OPTIONS=nocheck

    doEchoStep "MAKEFLAGS: $MAKEFLAGS"
    doEchoStep "CFLAGS: $CFLAGS"
    doEchoStep "CXXFLAGS: $CXXFLAGS"
    doEchoStep "KCFLAGS: $KCFLAGS"
    doEchoStep "KCPPFLAGS: $KCPPFLAGS"

    doEchoStep "make bindeb-pkg"

    if [ "$CLANG" == "1" ]; then
        make \
            -j${NPROC} \
            LLVM=1 LLVM_IAS=1 \
            CC='ccache clang' \
            HOSTCC='ccache clang' \
            bindeb-pkg \
            LOCALVERSION=-"$(dpkg --print-architecture)" \
            KDEB_PKGVERSION="$(make kernelversion)-${REVISION}"
        result=$? && doFail $result ">>> Error in 'make bindeb-pkg'!"
    else
        make \
            -j${NPROC} \
            CC='ccache gcc' \
            bindeb-pkg \
            LOCALVERSION=-"$(dpkg --print-architecture)" \
            KDEB_PKGVERSION="$(make kernelversion)-${REVISION}"
        result=$? && doFail $result ">>> Error in 'make bindeb-pkg'!"
    fi

    doGenerateUninstall
    doEnding

    doSync
}

# Proceed for Kernel build
doKernel() {
    WORKDIR=$CURRENT/build/$BRANCH/$VERSION

    doTarget

    doScratch
    if [ ! -d $WORKDIR ]; then
        mkdir -p $WORKDIR
    fi

    LOGFILE=$WORKDIR/$LOGNAME.$LOGEXT
    if [ -f $LOGFILE ]; then
        rm -rf $LOGFILE
    fi
    touch $LOGFILE

    doDownload > >(tee -a $LOGFILE) 2>&1
    doUncompress > >(tee -a $LOGFILE) 2>&1

    WORKDIR=$WORKDIR/linux-$VERSION

    doCleanup > >(tee -a $LOGFILE) 2>&1
    doConfigCloud > >(tee -a $LOGFILE) 2>&1
    doOldOne > >(tee -a $LOGFILE) 2>&1
    doAllMods > >(tee -a $LOGFILE) 2>&1
    doDefaults > >(tee -a $LOGFILE) 2>&1
    doTargetAdapt > >(tee -a $LOGFILE) 2>&1
    doPermissions > >(tee -a $LOGFILE) 2>&1

    doEditSettings
    while true; do
        read -p "Do you wish to run compile? " yn
        case $yn in
        [Yy]*)
            doCompile > >(tee -a $LOGFILE) 2>&1
            break
            ;;
        [Nn]*) exit ;;
        *) echo "Please answer yes or no." ;;
        esac
    done
}

# Entrypoint of operations
if [[ -z $BRANCH || "$BRANCH" == "help" || -z $VERSION || "$VERSION" == "help" ]]; then
    doHelp
    exit 1
else
    clear
    doHead
    doKernel
fi

exit 0