#!/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.6
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
# очистить исходники ядра перед сборкой
# 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
  --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 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=
            ;;
        --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
        cl-setup-boot
    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!"
