#!/usr/bin/env bash # Copyright (C) 2024-Ω Miquel Sabaté Solà # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ##### # Build the Linux Kernel and bundle it into a tarball that can then be used on # another machine. After you call this script it will: # 1. Either call `defconfig` or re-use an existing .config as per user request. # 2. Call `menuconfig` so the user can double check the configuration. # 3. Build the Kernel. # 4. Copy into a tarball the resulting image, modules, dtbs, and headers. An # install.sh script is also bundled in. # # You can also pass two options: # 1. `-s/--skip-build`: tell to skip the call to `defconfig` and/or # `menuconfig`. This is useful if you are just developing over the same # `.config` file again and again. # 2. `-n/--no-install`: do not prepare the install/uninstall bundle. # # After that just send the resulting tarball to the machine where you want to # install this kernel. In there you just need to untar the given bundle and then # run the `install.sh` script from within the resulting directory. This will: # 1. Copy recursively all the files into their proper paths. # 2. Make an initramfs for the new image. # 3. Run your $EDITOR to update the /boot/extlinux/extlinux.conf file. # # *NOTE*: this script expects the `ARCH` environment variable to be set, and if # that doesn't match the current architecture, then it will expect to have # `CROSS_COMPILE` set as well. # *NOTE*: we assume that either `update-initramfs` or `dracut` exist. If you are # using something else, send me a patch. ##### set -e ## # Arguments that can be passed. while [[ $# -gt 0 ]]; do case $1 in -s | --skip-config) SKIP_CONFIG="t" shift ;; -n | --no-install) NO_INSTALL="t" shift ;; -*) echo "kbuild (error): unknown option '$1'." exit 1 ;; *) echo "kbuild (warning): argument '$1' will be ignored." shift # past argument ;; esac done ## # Initial checks. # Most of the times, we are safer if we just set the `ARCH` env. variable. if [ -z "$ARCH" ]; then echo "kbuild (error): \$ARCH has to be defined." exit 1 fi # Is cross compilation the thing we want? if [ "$ARCH" != "$(uname -m)" ]; then # Confusingly enough, riscv64 is supposed to be passed as 'riscv' to the # kernel. Hence, don't take that as being different. if [ "$(uname -m)" != "riscv64" ] || [ "$ARCH" != "riscv" ]; then # We are indeed cross compiling. If `CROSS_COMPILE` is not set at this # point we should raise an error. if [ -z "$CROSS_COMPILE" ]; then echo "kbuild (error): \$CROSS_COMPILE has to be defined." exit 1 fi fi fi # To avoid embarrassing moments, let's make sure that we are in the Linux repo. if [ ! -f "Kbuild" ]; then echo "kbuild (error): we are not in the Linux Kernel." exit 1 fi ## # .config if [ -z "$SKIP_CONFIG" ]; then if [ ! -f ".config" ]; then echo "kbuild (info): no '.config' file found" make defconfig fi config_options=("From a default configuration (defconfig)" "From an existing .config file") echo "kconfig (info): how do you want to configure the build?" PS3="> " select _opt in "${config_options[@]}" "Quit"; do case "$REPLY" in 1) make defconfig echo "kbuild (info): running 'menuconfig' to double check." make menuconfig break ;; 2) echo "kbuild (info): running 'menuconfig' to double check." make menuconfig break ;; 3) echo "kbuild (info): bye!" exit 0 ;; *) echo "kbuild (info): not an option. Try again..." continue ;; esac done else echo "kbuild (info): skipping configuration step..." fi ## # Build the kernel. echo "kbuild (info): building the Linux kernel..." make -j "$(nproc)" ## # Installing into a local bundle. if [ -n "$NO_INSTALL" ]; then echo "kbuild (info): done!" else name=$(cat include/config/kernel.release) if [ -z "$INSTALL_PATH" ]; then INSTALL_PATH="buildroot-$name" fi mkdir -p "$INSTALL_PATH/usr/lib/modules/$name" pushd "$INSTALL_PATH" >/dev/null ln -s usr/lib lib popd >/dev/null make modules_install INSTALL_MOD_PATH="$INSTALL_PATH" make dtbs_install INSTALL_DTBS_PATH="$INSTALL_PATH/boot/dtbs/$name" make headers_install INSTALL_HDR_PATH="$INSTALL_PATH/usr" unlink "$INSTALL_PATH/lib" cp "arch/$ARCH/boot/Image.gz" "$INSTALL_PATH/boot/vmlinuz-$name" cp .config "$INSTALL_PATH/boot/config-$name" ## # Setup install.sh script. # Files that are going to be installed. Funnily enough, the pipeline will create # the "files.txt" first, so it will show on the `find` output. Hence the stupid # workaround you see of moving it in later. find "$INSTALL_PATH" -type f -printf "/%P\n" >files.txt mv files.txt "$INSTALL_PATH/files.txt" # Now write the actual install script. cat <