#!/usr/bin/env bash
#shellcheck disable=SC2120,SC2034

# Copyright 2023-2024 Gentoo Authors
# This script is installed by sys-kernel/installkernel, it is executed by
# systemd's kernel-install, NOT by the traditional installkernel. I.e. this
# plugin is run when the systemd USE flag is enabled or
# SYSTEMD_KERNEL_INSTALL=1 is set in the environment.

COMMAND="${1}"
KERNEL_VERSION="${2}"
ENTRY_DIR_ABS="${3}"
KERNEL_IMAGE="${4}"

if [[ "${COMMAND}" != "add" ]]; then
	exit 0
fi

# Array of external kernel modules to rebuild.
# Use @module-rebuild to rebuild all external kernel modules
MODULES=(
	@module-rebuild
)

candidates=(
	"${ROOT%/}/lib/modules/${KERNEL_VERSION}/build"
	"${ROOT%/}/lib/modules/${KERNEL_VERSION}/source"
	"${ROOT%/}/usr/src/linux-${KERNEL_VERSION}"
)

if [[ "${KERNEL_IMAGE:0:1}" == "/" ]]; then
	# If the kernel image is an absolute path, try to extract
	# the source root from this path
	rel_from_src=${KERNEL_IMAGE#*/usr/src/}
	candidates+=(
		"${ROOT%/}/usr/src/${rel_from_src%%/*}"
	)
else
	# If the kernel image is a relative path try the pwd as well
	candidates+=(
		"$(pwd)"
	)
fi

for dir in "${candidates[@]}"; do
	if [[ -d ${dir} ]]; then
		KERNEL_DIR="${dir}"
		break
	fi
done

if [[ -d ${KERNEL_DIR} ]]; then
	if [[ ${KERNEL_INSTALL_VERBOSE} == 1 ]]; then
		echo "Using kernel sources directory: ${KERNEL_DIR}"
	fi
else
	echo "Kernel sources directory not found"
	exit 1
fi


# From multiprocessing.eclass
get_nproc() {
	local nproc

	# GNU
	if type -P nproc &>/dev/null; then
		nproc=$(nproc)
	fi

	# BSD
	if [[ -z ${nproc} ]] && type -P sysctl &>/dev/null; then
		nproc=$(sysctl -n hw.ncpu 2>/dev/null)
	fi

	# fallback to python2.6+
	# note: this may fail (raise NotImplementedError)
	if [[ -z ${nproc} ]] && type -P python &>/dev/null; then
		nproc=$(python -c 'import multiprocessing; print(multiprocessing.cpu_count());' 2>/dev/null)
	fi

	if [[ -n ${nproc} ]]; then
		echo "${nproc}"
	else
		echo "${1:-1}"
	fi
}

_get_all_makeopts() {
	echo "${MAKEOPTS} ${GNUMAKEFLAGS} ${MAKEFLAGS}"
}

get_makeopts_jobs() {
	makeopts_jobs "$(_get_all_makeopts)" "${1:-1}"
}

makeopts_jobs() {
	[[ $# -eq 0 ]] && set -- "$(_get_all_makeopts)"
	# This assumes the first .* will be more greedy than the second .*
	# since POSIX doesn't specify a non-greedy match (i.e. ".*?").
	local jobs
	jobs=$(echo " $* " | sed -r -n \
		-e 's:.*[[:space:]](-[a-z]*j|--jobs[=[:space:]])[[:space:]]*([0-9]+).*:\2:p' \
		-e "s:.*[[:space:]](-[a-z]*j|--jobs)[[:space:]].*:${2:-$(( $(get_nproc) + 1 ))}:p")
	echo "${jobs:-1}"
}

get_makeopts_loadavg() {
	makeopts_loadavg "$(_get_all_makeopts)" "${1:-999}"
}

makeopts_loadavg() {
	[[ $# -eq 0 ]] && set -- "$(_get_all_makeopts)"
	# This assumes the first .* will be more greedy than the second .*
	# since POSIX doesn't specify a non-greedy match (i.e. ".*?").
	local lavg
	lavg=$(echo " $* " | sed -r -n \
		-e 's:.*[[:space:]](-[a-z]*l|--(load-average|max-load)[=[:space:]])[[:space:]]*([0-9]+(\.[0-9]+)?)[[:space:]].*:\3:p' \
		-e "s:.*[[:space:]](-[a-z]*l|--(load-average|max-load))[[:space:]].*:${2:-999}:p")
	# Default to ${inf} since the default is to not use a load limit.
	echo "${lavg:-${2:-999}}"
}

emerge_opts=(
	--oneshot
	--jobs="$(get_makeopts_jobs)"
	--load-average="$(get_makeopts_loadavg)"
	--ignore-default-opts
)

if [[ ${KERNEL_INSTALL_VERBOSE} == 1 ]]; then
	emerge_opts+=(
		--verbose
	)
else
	emerge_opts+=(
		--quiet=y
		--quiet-fail=y
		--quiet-build=y
	)
fi

# Since we are sort of running emerge from emerge/make we start with
# a dirty environment which creates a mess. Start clean instead and
# disable ebuild-locks.
# Binpkgs don't make sense here, they don't contain information on which
# kernel version they were built for.
# USE=dist-kernel doesn't make sense here, we would end up rebuilding twice.
# Instruct linux-mod(-r1).eclass (via linux-info.eclass) to use the just
# installed kernel instead of the eselected one.
[[ ${KERNEL_INSTALL_VERBOSE} == 1 ]] && echo "Rebuilding out-of-tree kernel modules..."
env -i \
	PATH="${PATH}" \
	FEATURES="-getbinpkg -buildpkg -ebuild-locks parallel-install" \
	USE="-dist-kernel" \
	KERNEL_DIR="${KERNEL_DIR}" \
	emerge "${emerge_opts[@]}" "${MODULES[@]}" || exit 1
