calculate-toolkit/cl-kernel

767 lines
24 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Copyright 2015-2016 Mir Calculate. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TEXTDOMAIN=cl_kernel
CL_KERNEL_VERSION=0.2.7
DESCRIPTION=$"Kernel building tool"
DEFAULT_KERNEL_DIRECTORY="$(readlink -f /usr/src/linux)"
SRC_DIRECTORY=/usr/src
LOCAL_TEMPLATES_DIR=/var/calculate/templates/kernel
TEMPLATES_BACKUP=${LOCAL_TEMPLATES_DIR}/backup
DEBUG_LOG=/var/log/calculate/cl-kernel.log
KVER=
KERNEL_DIR=${DEFAULT_KERNEL_DIRECTORY}
# создавать базовую конфигурацию ядра
# create the base kernel configuration
CREATE_BASE=1
# создавать текущую конфигурацию ядра
# create the current kernel configuration
CREATE_NEW=1
# собирать ядро после конфигурации
# compile the kernel once configuration is complete
BUILD_KERNEL=1
# обновить MBR запись диска во время установки ядра
# update the MBR record of the disk during the kernel installation
UPDATE_MBR=1
# очистить исходники ядра перед сборкой
# clean the kernel sources before the compilation
CLEAN_KERNEL=1
# запускать изменение конфигурации ядра пользователем
# execute manual kernel configuration
MANUAL_CONFIG=1
# права на файл шаблона
# template file privileges
CHMOD=0644
# наличие dracut в системе
# dracut present or not in the system
DRACUT=$(which dracut 2>/dev/null)
declare -a TAILOUT=()
# поддержка настроек ccache
# support ccache
source <(cat /etc/portage/make.conf/* | grep CCACHE_DIR)
export CCACHE_DIR
if [[ -n $CCACHE_DIR ]]
then
export PATH="/usr/lib/ccache/bin:/lib/rc/bin:$PATH"
else
export PATH="/lib/rc/bin:$PATH"
fi
# проверить пользователя
# check user
if [[ $(id -u) -ne 0 ]]
then
eerror $"This program must be run as root"
exit 1
fi
# прервать скрипт в случае ошибки любой из команд
# break the script execution in case of a command error
set -e
: >$DEBUG_LOG
# вывод короткой справки
# show the short help message
usage() {
echo $"Usage: $0 [OPTION]
Version: $CL_KERNEL_VERSION
${DESCRIPTION}
-h, --help display all options and exit"
}
# вывод полной справки
# show the long help message
long_usage() {
echo $"Usage: $0 [OPTION]
Version: $CL_KERNEL_VERSION
${DESCRIPTION}
--kver [VERSION] specify the kernel version ('list' for displaying possible values)
--kver-old [VERSION] specify the kernel version for new options ('list' for displaying possible values)
--convert migrate .config from the kernel directory to templates
-s, --skip-build do not build the kernel after configuration
--mbr [ON/OFF] update MBR
--skip-config do not manual configure the kernel
--noclean do not clean the kernel sources before the compilation
--march [ARCH] kernel architecture (x86 or x86_64)
--safemode create an additional initrd with all modules (only for calculate-sources)
--help display this help and exit
"
}
# подготовить параметры командной строки
# prepare the commmand line parameters
rearrange_params() {
set +e
TEMP=$(unset POSIXLY_CORRECT; getopt \
-o "hs" \
--long help \
--long kver: \
--long kver-old: \
--long march: \
--long convert \
--long noclean \
--long skip-build \
--long mbr: \
--long skip-config \
--long safemode \
-- "$@" 2>&1)
if (( $? != 0 )); then
echo "$TEMP" | sed 's/getopt: /cl-kernel: /;$d'
exit 1
fi
set -e
}
# выполнить параметры командной строки
# apply the command line parameters
do_args() {
while :; do
case $1 in
-h|--help)
long_usage
exit 0
;;
--kver)
KVER="$2"
shift
;;
--kver-old)
KVER_OLD="$2"
shift
;;
--safemode)
SAFEMODE=1
;;
--march)
MARCH="$2"
if [[ ${MARCH} != "x86" ]] && [[ ${MARCH} != "x86_64" ]]
then
eerror $"Error in parameter --march. The value may be 'x86' or 'x86_64'"
fi
shift
;;
--convert)
MIGRATE=1
CREATE_NEW=
;;
-s|--skip-build)
BUILD_KERNEL=
;;
--mbr)
UPDATE_MBR="$2"
if [[ ${UPDATE_MBR} =~ ^[Oo][Nn]$ ]]
then
UPDATE_MBR='on'
elif [[ ${UPDATE_MBR} =~ ^[Oo][Ff][Ff]$ ]]
then
UPDATE_MBR='off'
else
eerror $"Error in parameter --mbr. The value may be 'on' or 'off'"
fi
shift
;;
--skip-config)
MANUAL_CONFIG=
;;
--noclean)
CLEAN_KERNEL=
;;
--) shift;break;;
*) usage;
eerror $"Unknown option: $1"
;;
esac
shift
done
if [[ -n $1 ]]
then
usage;
eerror $"Unknown argument: $1"
fi
}
# получить значение переменной calculate
# get the value of variable 'calculate'
variable_value()
{
local varname=$1
/usr/libexec/calculate/cl-variable --value $varname
}
# оставить только названия параметров + "="
# keep parameter names only + "="
options_name() {
sed -r 's/^# (CON.*) is not set.*$/\1=/' | sed -r 's/^(CON.*=).*/\1/'
}
# преобразовать опции в синтаксис удаления параметра
# convert the options into parameter removal syntax
remove_syntax() {
sed -r 's/^(.*=).*$/!\1/'
}
# преобразовать CONFIG_XXX=n -> # CONFIG_XXX is not set
# make the conversion # CONFIG_XXX is not set -> CONFIG_XXX=n
n2not_set() {
sed -r 's/(CONFIG.*)=n/# \1 is not set/'
}
# получить разницу в конфигурационных файлах
# get a difference of configuration files
diff_config() {
diff -u <(grep CONFIG_ $1 | sort | n2not_set) <(grep CONFIG_ $2 | sort | n2not_set)
}
# получить разницу в параметрах конфигурационных файлов
# get paramters difference of configuration files
diff_config_options() {
diff -u <(cat $1 | options_name | sort) <(cat $2 | options_name | sort)
}
# изменённые параметры
# changed parameters
append_options() {
diff_config $1 $2 | grep -e "^+CON" -e "^+# CON" | sed 's/^.//' | sort
}
# удаленные параметры
# removed parameters
removed_options() {
diff_config_options $1 $2 | grep -e "^-CON" | sed 's/^.//' | sort
}
# получить содержимое шаблона
# get the template contents
diff_template_body() {
append_options $1 $2
removed_options $1 $2 | remove_syntax
}
# вывести заголов для шаблона
# show the template headers
# Args:
# category/package name
# package version
diff_template_head() {
local category_pn=$1
local pv=$2
echo "# Calculate format=kernel name=.config os_install_arch_machine==${TEMPLATE_ARCH}&&merge(${category_pn})>=${pv}"
}
# вывести полный шаблон
# show the full template
# Args:
# category/package name
# package version
# base configuration file
# new configuration file
create_template() {
local category_pn=$1
local pv=$2
local base_config=$3
local new_config=$4
diff_template_head ${category_pn} ${pv}
diff_template_body ${base_config} ${new_config}
}
# получить конфигурацию ядра
# get the kernel configuration
# Args:
# kernel sources directory
# category/package name
# package version
# used templates (locations)
create_kernel_config() {
local kernel_dir=$1
local category_pn=( ${2/\// } )
local category=${category_pn[0]}
local pn=${category_pn[1]}
local pv=$3
# создать временную директорию для выполнения шаблонов
# create temporary directory for templates applying
local tempdir=$(${MKTEMP} -d)
[[ -n $4 ]] && local templates="-T $4"
# получить конфигурацию ядра по умолчанию, нужной архитектуры
# get default kernel configuration for architecture
local temp_config=".config_clkernel_${ARCH}"
(cd ${kernel_dir};ARCH=$MARCH KCONFIG_CONFIG=${temp_config} make defconfig;mv ${temp_config} ${tempdir}/.config) &>>$DEBUG_LOG ||
eerror $"Failed to create the default kernel config"
# получить тип системы
if [[ "$(variable_value main.os_linux_system)" == "desktop" ]]
then
USEVALUE=desktop
else
USEVALUE=-desktop
fi
# выполнить шаблоны (патчи)
# apply templates (patches)
USE="$USEVALUE" /usr/sbin/cl-core --method core_patch --march=$TEMPLATE_ARCH --pkg-name ${pn} --pkg-category ${category} --pkg-version=${pv} --pkg-slot=${pv} --pkg-path=$tempdir $templates &>>$DEBUG_LOG || eerror $"Failed to apply kernel templates"
# вывести содержимое файла конфигурации ядра
# display content of kernel configuration file
cat $tempdir/.config || eerror $"Kernel configuration file not found"
rm -rf $tempdir &>>$DEBUG_LOG
}
# проверить, содержит ли каталог полный исходный код ядра
# check if the directory contains the full kernel sources
check_kernel_sources() {
local sources_dir=$1
[[ -f ${sources_dir}/arch/x86/configs/i386_defconfig ]]
}
# версия исходного кода ядра
# kernel sources version
sources_ver() {
basename "$(readlink -f $1)" | sed 's/linux-//' || true
}
# текущее ядро
# current_kernel
current_kernel() {
sources_ver /usr/src/linux
}
# вывести список версий ядер с полным исходным кодом
# list all kernels with full sources available
list_kernel() {
local asterisk=$(echo -e "\033[1m*\033[0m")
local green_asterisk=$(echo -e "\033[1;32m*\033[0m")
local red_asterisk=$(echo -e "\033[1;31m*\033[0m")
local curver=$1
for f in $(ls -drv /usr/src/linux-[[:digit:]]*); do
local ver=$(sources_ver "${f}")
[[ $ver == $curver ]] && mark=$asterisk || mark=
check_kernel_sources $f && echo " ${green_asterisk}" $ver $mark || echo " ${red_asterisk}" $ver $mark
done
}
# вывести сообщение и добавить его в список выводимых после сборки ядра сообщений
# show the message and add it to the list of messages to be displayed after compilation
einfo_tail() {
einfo $*
TAILOUT+=( "$*" )
}
# получить содержимое текущего конфига
# get the content of current kernel configuration
# .config into kernel sources directory
# /boot/config-
# /proc/config.gz
get_old_config() {
local source_config="${KERNEL_DIR}/.config"
local boot_config="/boot/config-$(uname -r)"
local proc_config="/proc/config.gz"
if [[ -f $source_config ]]
then
config=$source_config
elif [[ -f $boot_config ]]
then
config=$boot_config
cp $config $NEW_CONFIG
elif [[ -f $proc_config ]]
then
config=$proc_config
zcat $proc_config >$NEW_CONFIG
fi
if [[ -n $config ]]
then
einfo $"Will be used $config kernel configuration"
else
eerror $"Failed to get current kernel configuration"
fi
}
# qfile hack
_qfile() {
(cd /; qfile $*)
}
qfile_pkgname() {
_qfile $* | sed -r 's/:? .*$//;'
}
# проверить принадлежит ли директория только одному пакету
# check that the directory belongs to only one package
check_belong() {
local fn=$1
local linenum=$(_qfile -C $fn | wc -l)
if [[ $linenum -lt 1 ]]
then
eerror $"${fn} does not belong to any package"
elif [[ $linenum -gt 1 ]]
then
eerror $"${fn} belongs to multiple packages"
fi
return 0
}
real_kernel_version() {
local kdir=$1
file $kdir/arch/x86/boot/bzImage | sed -r 's/.*version\s(\S+)\s.*/\1/'
}
######################
# Обработать параметры
# Process the options
######################
rearrange_params "$@"
eval set -- "$TEMP"
do_args "$@"
########################
# Подготовить переменные
# Prepare variables
########################
[[ "$(variable_value main.cl_chroot_status)" == "off" ]] && NOT_CHROOT=1
# вычислить архитектуру
# detect the architecture
[[ -z $MARCH ]] && MARCH=$(/usr/bin/arch)
if [[ "$MARCH" == "x86" ]] || [[ "$MARCH" == "i686" ]]
then
# архитектура для конфигурации по умолчанию
# default configuration architecture
MARCH=i386
# templates architecture
# архитектура для шаблонов
TEMPLATE_ARCH=i686
# название архитектуры
# architecture name
NAME_ARCH=x86
else
TEMPLATE_ARCH=$MARCH
NAME_ARCH=$MARCH
fi
# подготовить имя для шаблона очистить старые временные данные
# prepare the template name; clear old temporary data
TMP_TEMPLATE=/tmp/cl_kernel_${MARCH}
MKTEMP="/usr/bin/mktemp ${TMP_TEMPLATE}.XXXXXX"
rm -rf ${TMP_TEMPLATE}*
# пропустить сборку ядра если выбранная архитектура и архитектура машины отличаются
# skip the kernel compilation if selected architecture is different from machine architecture
if [[ $TEMPLATE_ARCH != $(/usr/bin/arch) ]]
then
OTHER_ARCH=1
BUILD_KERNEL=
fi
# вывести список доступных ядер
# list avaiable kernels
if [[ $KVER == "list" ]] || [[ $KVER_OLD == "list" ]]
then
list_kernel $(current_kernel)
exit 0
fi
# получить директорию ядра по версии
# get the kernel directory by version
if [[ -n $KVER ]]
then
KERNEL_DIR=${SRC_DIRECTORY}/linux-${KVER}
fi
if [[ -n $KVER_OLD ]]
then
KERNEL_OLD_DIR=${SRC_DIRECTORY}/linux-${KVER_OLD}
fi
# проверить правильность исходников
# check the integrity of the sources
for check_dir in ${KERNEL_DIR} ${KERNEL_OLD_DIR}
do
[[ -d ${check_dir} ]] || eerror $"Kernel directory ${check_dir} not found"
check_kernel_sources ${check_dir} || eerror $"Kernel directory ${check_dir} has no full sources"
done
# получить версию из директории ядра
# get the version from the kernel directory
if [[ -z $KVER ]]
then
KVER=$(sources_ver $KERNEL_DIR)
fi
# проверка доступности safemode
# check if safemode is available
if [[ -n $SAFEMODE ]] && [[ -z $NOT_CHROOT ]]
then
eerror $"--safemode unavailable for chroot"
fi
if [[ -n $SAFEMODE ]] && ! [[ $KVER =~ -calculate ]]
then
eerror $"--safemode available for calculate-sources only"
fi
if [[ -n $SAFEMODE ]] && [[ -z $DRACUT ]]
then
eerror $"--safemode unavailable without dracut"
fi
# создать каталог в локальных шаблонах для шаблонов ядра
# create the directory for kernel templates in the local tempalte tree
if ! [[ -d $LOCAL_TEMPLATES_DIR ]]
then
(mkdir -p $LOCAL_TEMPLATES_DIR ;
echo "# Calculate env=install ac_install_patch==on append=skip" >${LOCAL_TEMPLATES_DIR}/.calculate_directory) ||
eerror $"Failed to create the kernel template directory"
fi
# если другая архитектура
# if other architecture
if [[ -n $OTHER_ARCH ]]
then
NEW_CONFIG=${KERNEL_DIR}/.config_${TEMPLATE_ARCH}
else
NEW_CONFIG=${KERNEL_DIR}/.config
fi
check_belong ${KERNEL_DIR}/Makefile
# получение параметров пакета, которому принадлежат исходники
# getting the parameters for the package whose sources are being processed
CATEGORY_PN=$( qfile_pkgname -C ${KERNEL_DIR}/Makefile )
PV=$( qfile_pkgname -Cv ${KERNEL_DIR}/Makefile )
PV=${PV/$CATEGORY_PN-/}
if [[ -n $KERNEL_OLD_DIR ]]
then
check_belong ${KERNEL_OLD_DIR}/Makefile
CATEGORY_PN_OLD=$( qfile_pkgname -C ${KERNEL_OLD_DIR}/Makefile )
PV_OLD=$( qfile_pkgname -Cv ${KERNEL_OLD_DIR}/Makefile )
PV_OLD=${PV_OLD/${CATEGORY_PN_OLD}-/}
fi
# получить версии MAJOR.MINOR для условий в шаблонах
# get the version (MAJOR.MINOR format) for conditions in templates
[[ $KVER =~ ^([[:digit:]]+\.[[:digit:]]+) ]] && PV2=${BASH_REMATCH[0]} || PV2=$PV
# определение имени шаблонов для пакета
# define templates name for the package
if [[ $CATEGORY_PN =~ ^.*/(.*)-sources ]]
then
TEMPLATE_NAME_PREFIX=10-${BASH_REMATCH[1]}-
else
TEMPLATE_NAME_PREFIX=10-config-
fi
TEMPLATE_NAME_ARCH_PREFIX=${TEMPLATE_NAME_PREFIX}${NAME_ARCH}-
TEMPLATE_NAME="${LOCAL_TEMPLATES_DIR}/${TEMPLATE_NAME_ARCH_PREFIX}${PV2}"
########################################
# Подготовка новой конфигурации ядра
# Preparing the new kernel configuration
########################################
CONFIG_GZ=/proc/config.gz
if [[ -n $CREATE_NEW ]]
then
ebegin $"Preparing the current kernel configuration"
create_kernel_config ${KERNEL_OLD_DIR:-${KERNEL_DIR}} \
${CATEGORY_PN_OLD:-${CATEGORY_PN}} \
${PV_OLD:-${PV}} >$NEW_CONFIG
eend
else
if [[ -n $OTHER_ARCH ]]
then
eerror $"--convert is uncompatible with --march"
fi
if [[ -n $KVER_OLD ]]
then
eerror $"--convert is uncompatible with --kver-old"
fi
get_old_config
fi
#########################################
# Подготовка базовой конфигурации ядра
# Preparing the base kernel configuration
#########################################
BASE_CONFIG=$( ${MKTEMP} )
ebegin $"Preparing the basic kernel configuration"
if [[ -n $CREATE_BASE ]]
then
# будут использоваться только шаблоны оверлеев
# will be used the overlay templates only
TEMPLATES=$(variable_value main.cl_template_location)
create_kernel_config ${KERNEL_DIR} ${CATEGORY_PN} ${PV} ${TEMPLATES/,local,remote/} >$BASE_CONFIG
else
cp $NEW_CONFIG $BASE_CONFIG
fi
eend
###########################################
# Изменение конфигурации ядра пользователем
# Manual kernel configuration
###########################################
if [[ -n $MANUAL_CONFIG ]]
then
(cd $KERNEL_DIR; [[ -n ${KERNEL_OLD_DIR} || -z ${CREATE_NEW} ]] && KCONFIG_CONFIG=$(basename $NEW_CONFIG) make oldconfig;KCONFIG_CONFIG=$(basename $NEW_CONFIG) make -s menuconfig) || true
fi
###########################
# Создание шаблона
# Template creation
###########################
NEW_TEMPLATE=$( ${MKTEMP} )
create_template $CATEGORY_PN $PV2 $BASE_CONFIG $NEW_CONFIG >${NEW_TEMPLATE}
##################################
# Создание резервной копии шаблона
# Template backup
##################################
if ls ${LOCAL_TEMPLATES_DIR}/${TEMPLATE_NAME_ARCH_PREFIX}* &>/dev/null
then
for i in ${LOCAL_TEMPLATES_DIR}/${TEMPLATE_NAME_ARCH_PREFIX}*
do
if diff -u $i $NEW_TEMPLATE &>/dev/null
then
einfo_tail $"The kernel configuration has not changed"
rm $i
SKIP_CREATE_INFO=1
else
newname="$(basename $i)-$(date +%Y%m%d_%H%M -r $i)"
einfo_tail $"Backing up the template" "$(basename $i) -> ${newname}"
if ! [[ -d ${TEMPLATES_BACKUP} ]]
then
(mkdir -p ${TEMPLATES_BACKUP} &&
echo "# Calculate cl_action==skip" >${TEMPLATES_BACKUP}/.calculate_directory) || eerror $"Failed to create a backup directory"
fi
mv $i ${TEMPLATES_BACKUP}/${newname}
fi
done
fi
# пропуск создания пустого шаблона
# skip the empty template
if [[ $(sed 1d $NEW_TEMPLATE | wc -l) -gt 0 ]]
then
mv $NEW_TEMPLATE $TEMPLATE_NAME
chmod ${CHMOD} $TEMPLATE_NAME
if [[ -z $SKIP_CREATE_INFO ]]
then
einfo_tail $"Creating the template" "$(basename $TEMPLATE_NAME)"
fi
else
einfo_tail $"Skipping the empty template"
fi
rm -f $BASE_CONFIG
STARTTIME=$(date +%s)
####################
# Сборка ядра
# Kernel compilation
####################
if [[ -n ${BUILD_KERNEL} ]]
then
cd $KERNEL_DIR
(
MAKEOPTS=$(variable_value install.os_install_makeopts)
echo -- $MAKEOPTS
set -e
if [[ -n ${CLEAN_KERNEL} ]]
then
make clean || eerror $"Failed to clean the kernel sources" || exit 1
fi
make $MAKEOPTS || eerror $"Failed to compile the kernel" || exit 1
make $MAKEOPTS modules_install || eerror $"Failed to compile the kernel modules" || exit 1
make $MAKEOPTS install || eerror $"Failed to install the kernel" || exit 1
) || eerror $"Failed to build kernel"
# использовать сжатие если поддерживается ядром (zstd,gzip,xz)
if grep -q "CONFIG_RD_ZSTD=y" ${NEW_CONFIG}
then
COMPRESS=--zstd
elif grep -q "CONFIG_RD_GZIP=y" ${NEW_CONFIG}
then
COMPRESS=--gzip
elif grep -q "CONFIG_RD_XZ=y" ${NEW_CONFIG}
then
COMPRESS=--xz
fi
# сборка initramfs
# initramfs building
VMLINUZ_VER=$(real_kernel_version $KERNEL_DIR)
if [[ -n $DRACUT ]] && grep -q "CONFIG_BLK_DEV_INITRD=y" ${NEW_CONFIG}
then
${DRACUT} -f${NOT_CHROOT:+H} $COMPRESS --kver=$VMLINUZ_VER /boot/initramfs-${VMLINUZ_VER}.img || eerror $"Failed to create the host-only initramfs"
if [[ $KVER =~ calculate ]] && [[ -n $SAFEMODE ]]
then
${DRACUT} -f $COMPRESS --kver=$VMLINUZ_VER /boot/initramfs-${VMLINUZ_VER/-calculate/-SafeMode-calculate}.img || eerror $"Failed to create the safemode initramfs"
fi
else
# удалить старый ramfs если его поддержка выключена в ядре
# remove the old ramfs if its support disabled in the kernel configuration
rm -f /boot/initramfs-${VMLINUZ_VER}.img /boot/initramfs-${VMLINUZ_VER/-calculate/-SafeMode-calculate}.img
fi
# выполнение шаблонов для настройки загрузки системы
# applying the templates for boot configuration
if [[ -n $NOT_CHROOT ]]
then
mbr_set=
if [[ $UPDATE_MBR == 'off' ]]
then
mbr_set=' --mbr none'
fi
cl-setup-boot $mbr_set
fi
# вывод времени компиляции ядра
# display kernel compilation time
DELTATIME=$(( $(date +%s) - $STARTTIME ))
HOUR=$(( $DELTATIME / 3600 ))
DELTATIME=$(( $DELTATIME % 3600 ))
MIN=$(( $DELTATIME / 60 ))
SEC=$(( $DELTATIME % 60 ))
echo -en " \033[1;32m*\033[0m "
echo -n $"Kernel build time: "
if [[ ${HOUR} -gt 0 ]]
then
printf $"%d hour %d min %d sec\n" ${HOUR} ${MIN} ${SEC}
else
printf $"%d min %d sec\n" ${MIN} ${SEC}
fi
# вывод информационных сообщений, отображённых до сборки ядра
# display info messages, show before kernel compilation
for line in "${TAILOUT[@]}"
do
einfo $line
done
# rebuild kernel modules if kernel directory is /usr/src/linux
if [[ "$(readlink -f ${DEFAULT_KERNEL_DIRECTORY})" == "$(readlink -f $KERNEL_DIR)" ]]
then
ewarn $"To rebuild kernel modules packages, please run:"
ewarn ""
ewarn " emerge -a @module-rebuild"
fi
fi
einfo $"All done!"