Hello,
I have a hacky bash script to setup an rpmbuild environment, pull down the latest Rocky Linux 9 x86_64 kernel RPM src package, apply some patches to the kernel source, compile it, and build the associated RPMs. See below (might help someone else who wants to do something similar):
#!/usr/bin/env bash
# Builds the rpmbuild environment, pulls down the latest kernel
# rpm src, applies custom patches and kconfigs to the kernel source,
# compiles the kernel, and deploys the RPMs to a DNF server via NFS.
# Exit on error, fail if an unset variable is referenced, turn on tracing, and fail if a command fails in a pipe.
set -o errexit -o nounset -o xtrace -o pipefail
#DNF_SERVER="<dnf_repo_server>"
REMOTE_MNT_LOCATION="/var_dnf_repo"
KERNEL_SRC_REPO_FQDN="mirrors.vcea.wsu.edu"
LOCAL_MNT_LOCATION="/mnt/dnf_repo/"
PATCHES_DIRECTORY="/root/patches/kernel"
KCONFIGS_DIRECTORY="/root/patches/kernel/kconfigs"
NEW_KERNEL_FEATURES=(CONFIG_ATH10K_THERMAL)
function generate_var() {
# Variables section
## Get CentOS release version
ROCKY_RELEASE=$(head /etc/redhat-release | cut --delimiter ' ' --fields 4 | cut --delimiter '.' --fields 1)
# Build base URL
KERNEL_SRC_BASE_URL="https://"${KERNEL_SRC_REPO_FQDN}"/rocky/"${ROCKY_RELEASE}"/BaseOS/source/tree/Packages/k/"
## Grab the current kernel version for our helper config file
if [[ ! -e /etc/kernel-version ]]; then
CURRENT_KERNEL_VERSION=""
else
CURRENT_KERNEL_VERSION=$(cat /etc/kernel-version)
fi
## Grab the latest kernel version from vault.centos.org
NEW_KERNEL_VERSION_SRC_RPM=$(curl -s "${KERNEL_SRC_BASE_URL}" | grep --extended -io "a href=\"kernel-[0-9]*\.[0-9]*\.[0-9]*\-[0-9]*\.?.*\.el${ROCKY_RELEASE}.*\.src\.rpm\"" | cut --delimiter '"' --fields 2 | sort --version-sort | tail -1)
## Get new kernel version without src rpm extension
NEW_KERNEL_VERSION=$(echo "${NEW_KERNEL_VERSION_SRC_RPM}" | rev | cut --delimiter '.' --fields 3- | rev)
## Construct the URL to download the SRC RPM for the kernel
NEW_KERNEL_VERSION_URL="${KERNEL_SRC_BASE_URL}${NEW_KERNEL_VERSION_SRC_RPM}"
## Get this computers architecture
ARCH=$(uname -m)
# Construct an array of patches we will apply
readarray -d '' PATCHES < <(find "${PATCHES_DIRECTORY}" -mindepth 1 -maxdepth 1 -type f -name "*.patch" -printf "%f\0")
# Construct an array of Kconfigs we will apply
readarray -d '' KCONFIGS < <(find "${KCONFIGS_DIRECTORY}" -mindepth 1 -maxdepth 1 -type f -name "*.patch" -printf "%f\0")
echo "VARIABLES:"
echo "ROCKY_RELEASE = ${ROCKY_RELEASE}"
echo "CURRENT_KERNEL_VERSION = ${CURRENT_KERNEL_VERSION}"
echo "NEW_KERNEL_VERSION = ${NEW_KERNEL_VERSION}"
echo "NEW_KERNEL_VERSION_URL = ${NEW_KERNEL_VERSION_URL}"
echo "ARCH = ${ARCH}"
echo "PATCHES = ${PATCHES[*]}"
echo "KCONFIGS = ${KCONFIGS[*]}"
echo "GENERATE ENVIRONMENT = ${GENERATE}"
echo "BUILD KERNEL = ${BUILD}"
echo "DEPLOY KERNEL = ${DEPLOY}"
echo "RPM = ${RPM}"
echo "NEW_KERNEL_FEATURES = ${NEW_KERNEL_FEATURES[*]}"
echo ""
}
function generate_env {
echo -e "[\e[93mDOING\e[39m] Verifying new kernel version is not the same as the current version before proceeding."
if [[ "${NEW_KERNEL_VERSION}" == "${CURRENT_KERNEL_VERSION}" ]]; then
if [[ ${FORCE} == False ]]; then
echo -e "[\e[92mOK\e[39m] Current kernel version ${CURRENT_KERNEL_VERSION} is the same as the new version ${NEW_KERNEL_VERSION}"
exit 0
else
echo -e "[\e[92mOK\e[39m] Current kernel version ${CURRENT_KERNEL_VERSION} is the same as the new version ${NEW_KERNEL_VERSION}, but FORCE is set to True so continuing anyway."
fi
else
echo -e "[\e[92mOK\e[39m] Current kernel version ${CURRENT_KERNEL_VERSION} is NOT the same as the new version ${NEW_KERNEL_VERSION}"
fi
echo -e "[\e[93mDOING\e[39m] Removing build directories if they exit."
rm --recursive --force ~/rpmbuild
echo -e "[\e[93mDOING\e[39m] Setting up build directories."
mkdir --parents ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
echo -e "[\e[93mDOING\e[39m] Updating macros."
echo '%_topdir %(echo ${HOME})/rpmbuild' > ~/.rpmmacros
echo -e "[\e[93mDOING\e[39m] Downloading the kernel src rpm to /tmp/${NEW_KERNEL_VERSION_SRC_RPM}"
wget --quiet --directory-prefix=/tmp "${NEW_KERNEL_VERSION_URL}"
echo -e "[\e[93mDOING\e[39m] Installing kernel src rpm /tmp/${NEW_KERNEL_VERSION_SRC_RPM}."
rpm --install /tmp/"${NEW_KERNEL_VERSION_SRC_RPM}"
echo -e "[\e[93mDOING\e[39m] Removing kernel src rpm /tmp/${NEW_KERNEL_VERSION_SRC_RPM}."
rm --force /tmp/"${NEW_KERNEL_VERSION_SRC_RPM}"
echo -e "[\e[93mDOING\e[39m] Attempting to build environment if no package dependencies are necessary. This will take a little while"
PACKAGES=$( (rpmbuild -bp --target="${ARCH}" ~/rpmbuild/SPECS/kernel.spec || true) 2>&1 | (grep 'is needed by' || true) | awk '{print $1}' | tr '\n' ' ')
if [[ ${PACKAGES} != "" ]]; then
for pkg in ${PACKAGES}; do
echo -e "[\e[93mDOING\e[39m] (sudo) Attempting to install package dependency ${pkg}"
dnf install "${pkg}" --assumeyes --quiet
done
echo -e "\n[\e[93mDOING\e[39m] Setting up build environment after installing dependencies. This will take a little while."
rpmbuild -bp --target="${ARCH}" ~/rpmbuild/SPECS/kernel.spec 2>&1
fi
local linux_dir
linux_dir=$(echo ~/rpmbuild/BUILD/kernel-*/linux-*/)
local arch_config
arch_config=$(ls ~/rpmbuild/BUILD/kernel-*/linux-*/configs/kernel*-"${ARCH}".config)
echo -e "[\e[93mDOING\e[39m] Copying patches to the SOURCES directory."
command cp "${PATCHES_DIRECTORY}"/*.patch ~/rpmbuild/SOURCES
echo -e "[\e[93mDOING\e[39m] Copying our current boot .config to the BUILD directory."
command cp "${arch_config}" "${linux_dir}".config
echo -e "[\e[93mDOING\e[39m] Executing make oldconfig"
build_dir=$(readlink -f ~/rpmbuild/BUILD/kernel-*/linux-*/)
make --directory="${build_dir}" olddefconfig 2>&1
echo -e "\n[\e[93mDOING\e[39m] Adding the ARCH ${ARCH} to the top of the .config file."
sed --in-place -- "1i # ${ARCH}" "${linux_dir}".config
echo -e "[\e[93mDOING\e[39m] Patching Kconfigs prior to adding new modules/features."
for i in "${!KCONFIGS[@]}"; do
if [[ $( (grep --extended "^\+\+\+ b.+" "${KCONFIGS_DIRECTORY}"/"${KCONFIGS[i]}" || true) | wc -l) != "1" ]]; then
echo -e "[\e[31mFAIL\e[39m] Unable to determine patch file location in kconfig patch ${KCONFIGS[i]}."
exit 1
fi
kconfig_mod_line=$( (grep --extended "^\+\+\+ b.+" "${KCONFIGS_DIRECTORY}"/"${KCONFIGS[i]}" || true) | cut --delimiter " " --fields 2 | cut --delimiter "/" --fields 2-)
echo -e "[\e[93mDOING\e[39m] Patching ${kconfig_mod_line} with patch ${KCONFIGS[i]}."
patch -u "${build_dir}"/"${kconfig_mod_line}" "${KCONFIGS_DIRECTORY}"/"${KCONFIGS[i]}"
done
echo -e "[\e[93mDOING\e[39m] Adding new modules/features."
for i in "${!NEW_KERNEL_FEATURES[@]}"; do
echo -e "[\e[93mDOING\e[39m] Adding new feature ${NEW_KERNEL_FEATURES[i]}."
"${build_dir}"/scripts/config --set-val "${NEW_KERNEL_FEATURES[i]}" y
done
echo -e "[\e[93mDOING\e[39m] Copying .config to the configs/kernel-.*-${ARCH}.config file."
command cp ~/rpmbuild/BUILD/kernel-*/linux-*/.config "${arch_config}"
echo -e "[\e[93mDOING\e[39m] Copying configs to the SOURCES directory."
command cp ~/rpmbuild/BUILD/kernel-*/linux-*/configs/* ~/rpmbuild/SOURCES/
echo -e "[\e[93mDOING\e[39m] Backup the kernel.spec file to kernel.spec.distro."
command cp ~/rpmbuild/SPECS/kernel.spec ~/rpmbuild/SPECS/kernel.spec.distro
echo -e "[\e[93mDOING\e[39m] Create a unique buildid for the kernel name."
sed --in-place -- 's/# define buildid .local/%define buildid .ath_hack/g' ~/rpmbuild/SPECS/kernel.spec
echo -e "[\e[93mDOING\e[39m] Updating the pkg_release variable in the kernel.spec file."
sed --in-place -- 's/%define pkg_release %{specrelease}%{?buildid}/%define pkg_release %{pkgrelease}%{?buildid}/g' ~/rpmbuild/SPECS/kernel.spec
echo -e "[\e[93mDOING\e[39m] Adding patch definitions to the kernel spec."
local test_patch_line
test_patch_line=$(( $(sed --quiet '/Patch999999: linux-kernel-test.patch/=' ~/rpmbuild/SPECS/kernel.spec) - 1 ))
if [[ "${test_patch_line}" == "" ]]; then
echo -e "[\e[31mFAIL\e[39m] Failed to find the final patch line for Patch999999 in ~/rpmbuild/SPECS/kernel.spec."
exit 1
fi
for i in "${!PATCHES[@]}"; do
echo -e "[\e[93mDOING\e[39m] Adding patch 'Patch$((i+2)): ${PATCHES[i]}' to the kernel.spec file."
sed --in-place -- "$(( test_patch_line + i ))i Patch$((i+2)): ${PATCHES[i]}" ~/rpmbuild/SPECS/kernel.spec
done
local test_apply_optional_patch_line
test_apply_optional_patch_line=$(sed --quiet '/ApplyOptionalPatch linux-kernel-test.patch/=' ~/rpmbuild/SPECS/kernel.spec)
if [[ "${test_apply_optional_patch_line}" == "" ]]; then
echo -e "[\e[31mFAIL\e[39m] Failed to find beginning of the ApplyOptionalPatch section in ~/rpmbuild/SPECS/kernel.spec."
exit 1
fi
for i in "${!PATCHES[@]}"; do
echo -e "[\e[93mDOING\e[39m] Adding patch 'ApplyOptionalPatch ${PATCHES[i]}' to the kernel.spec file."
sed --in-place -- "$(( test_apply_optional_patch_line + i ))i ApplyOptionalPatch ${PATCHES[i]}" ~/rpmbuild/SPECS/kernel.spec
done
echo -e "[\e[92mOK\e[39m] Finished preparing the kernel build environment."
}
function build_kernel() {
echo -e "\n[\e[93mDOING\e[39m] Building the kernel. The build log is in /tmp/build-out.log and the error log is in /tmp/build-err.log"
rpmbuild --with baseonly --without debug --without debuginfo --without kabichk -bb --target="${ARCH}" ~/rpmbuild/SPECS/kernel.spec 2> /tmp/build-err.log | tee /tmp/build-out.log
echo -e "[\e[92mOK\e[39m] Finished building the kernel."
}
function deploy_kernel() {
echo -e "\n[\e[93mDOING\e[39m] Copying RPMs to /tmp"
cp ~/rpmbuild/RPMS/"${ARCH}"/kernel-*.rpm /tmp
echo -e "[\e[93mDOING\e[39m] Mounting the DNF repo directory."
mount --types nfs4 --options sec=sys "${DNF_SERVER}":"${REMOTE_MNT_LOCATION}" "${LOCAL_MNT_LOCATION}"
echo -e "[\e[93mDOING\e[39m] Moving RPMs to the yum REPO."
mv ~/rpmbuild/RPMS/"${ARCH}"/kernel-*.rpm "${REMOTE_MNT_LOCATION}"
echo -e "[\e[93mDOING\e[39m] Unmounting the DNF repo directory."
umount "${LOCAL_MNT_LOCATION}"
echo -e "[\e[93mDOING\e[39m] Writing out the new kernel version to our helper config file."
bash -c "echo ${NEW_KERNEL_VERSION} > /etc/kernel-version"
echo -e "[\e[93mDOING\e[39m] Removing the rpmbuild directory."
rm --recursive --force ~/rpmbuild
}
function usage() {
echo "Usage: build_custom_kernel.sh [-g <generates_userspace>] [-b <builds_the_kernel>] [-i <deploys_the_kernel>] [-r <location_of_kernel_src_rpm>] [-f <force_build_if_new_kernel_equals_old_kernel>]"
exit 0
}
BUILD=False
DEPLOY=False
FORCE=False
GENERATE=False
RPM=""
# Parse arguments
while getopts ":befghr:" opt; do
case ${opt} in
b) BUILD=True;;
e) DEPLOY=True;;
f) FORCE=True;;
g) GENERATE=True;;
h) usage;;
r) RPM=${OPTARG};;
\?) usage;;
esac
done
# Do everything if no option is specified.
if [[ ${GENERATE} = False ]] && [[ ${BUILD} == False ]] && [[ ${DEPLOY} == False ]]; then
GENERATE=True
BUILD=True
DEPLOY=True
fi
generate_var
if [[ ${GENERATE} == True ]]; then
generate_env ${FORCE}
fi
if [[ ${BUILD} == True ]]; then
build_kernel
fi
if [[ ${DEPLOY} == True ]]; then
deploy_kernel
fi
It works well for what it. However, despite using the same RPM spec as Rocky Linux, when I install the compiled kernel it is NOT set as the default boot kernel. I have to do that manually with a grubby --set-default /boot/vmlinuz...
command. Am I missing something when building the RPM with rpmbuild under the function build_kernel()
section? Otherwise, is my only other option an ugly hack in the %post
section to run the grubby --set-default
command?