Here's a disgusting attempt to automate the process
#!/bin/sh
set -eu
usage() {
cat <<EOF
Usage: $0 [options]
Automates fetching, extracting, and building a custom NetBSD kernel.
Options:
-h, --help Show this help message and exit
-n <name> Set custom kernel config name (default: MYKERNEL)
-j <jobs> Set number of build jobs (default: 4)
-v <version> NetBSD version (default: 10.1), e.g. 11.0, 11.1_RC2
--dry-run Show what would happen without doing it
--no-fetch Skip downloading source tarballs
--no-clean Skip cleanup prompt at end
Requires:
- doas with 'setenv' support for \$DOAS_USER
- ftp, tar, build.sh, and vim
EOF
}
# === Defaults ===
KERNEL_NAME="MYKERNEL"
CORES=4
NETBSD_VERSION="10.1"
DRY_RUN=0
SKIP_FETCH=0
SKIP_CLEAN=0
# === Option Parsing ===
while [ $# -gt 0 ]; do
case "$1" in
-h|--help) usage; exit 0 ;;
-n) shift; KERNEL_NAME=$1 ;;
-j) shift; CORES=$1 ;;
-v) shift; NETBSD_VERSION=$1 ;;
--dry-run) DRY_RUN=1 ;;
--no-fetch) SKIP_FETCH=1 ;;
--no-clean) SKIP_CLEAN=1 ;;
*)
echo "Unknown option: $1" >&2
usage; exit 1
;;
esac
shift
done
USERNAME=${DOAS_USER:-${USER:-$(id -un)}}
SOURCE_DIR="$HOME/netbsd-sources"
BUILD_DIR="$HOME/netbsd-build"
ftp_base_url="ftp://ftp.NetBSD.org/pub/NetBSD/NetBSD-${NETBSD_VERSION}/source/sets"
run() {
if [ "$DRY_RUN" -eq 1 ]; then
echo "[dry-run] $*"
else
eval "$@"
fi
}
echo "==> Starting NetBSD kernel rebuild script"
echo " User: $USERNAME"
echo " Version: $NETBSD_VERSION"
echo " Kernel config: $KERNEL_NAME"
echo " Build jobs: $CORES"
echo " Dry run: $DRY_RUN"
echo " Skip fetch: $SKIP_FETCH"
echo " Skip clean: $SKIP_CLEAN"
echo ""
# === Step 1: Download source tarballs ===
if [ "$SKIP_FETCH" -eq 0 ]; then
echo "==> Fetching source sets to $SOURCE_DIR"
run "mkdir -p \"$SOURCE_DIR\""
cd "$SOURCE_DIR"
for file in src.tgz syssrc.tgz sharesrc.tgz gnusrc.tgz xsrc.tgz; do
if [ ! -f "$file" ]; then
echo " -> Downloading $file"
run "ftp -i \"${ftp_base_url}/${file}\""
else
echo " -> $file already exists, skipping"
fi
done
else
echo "==> Skipping source tarball fetch (--no-fetch)"
fi
# === Step 2: Ensure /usr/src and /usr/xsrc exist and owned ===
echo "==> Ensuring /usr/src and /usr/xsrc exist and owned by $USERNAME"
for dir in /usr/src /usr/xsrc; do
if [ ! -d "$dir" ]; then
echo " -> Creating $dir"
run "doas mkdir -p \"$dir\""
else
echo " -> $dir already exists"
fi
owner=$(stat -f '%Su' "$dir")
if [ "$owner" != "$USERNAME" ]; then
echo " -> Changing ownership of $dir to $USERNAME"
run "doas chown -R \"$USERNAME:$USERNAME\" \"$dir\""
else
echo " -> $dir already owned by $USERNAME"
fi
done
# === Step 3: Extract sources ===
echo "==> Extracting source sets"
for file in "$SOURCE_DIR"/*.tgz; do
echo " -> Extracting $(basename "$file")"
run "doas tar -xzf \"$file\" -C /"
done
# === Step 4: Create build dirs ===
echo "==> Creating build directories in $BUILD_DIR"
for sub in obj tools; do
path="$BUILD_DIR/$sub"
if [ ! -d "$path" ]; then
echo " -> Creating $path"
run "mkdir -p \"$path\""
else
echo " -> $path already exists"
fi
done
# === Step 5: Build toolchain ===
echo "==> Building toolchain..."
cd /usr/src
run "./build.sh -U -O \"$BUILD_DIR/obj\" -T \"$BUILD_DIR/tools\" -j$CORES tools"
# === Step 6: Copy and edit kernel config ===
CONF_DIR="/usr/src/sys/arch/amd64/conf"
cd "$CONF_DIR"
if [ ! -f "$KERNEL_NAME" ]; then
echo "==> Creating kernel config: $KERNEL_NAME"
run "cp GENERIC \"$KERNEL_NAME\""
[ "$DRY_RUN" -eq 0 ] && vim "$KERNEL_NAME"
else
echo "==> Kernel config $KERNEL_NAME already exists"
fi
# === Step 7: Build kernel ===
echo "==> Building kernel: $KERNEL_NAME"
cd /usr/src
run "./build.sh -U -O \"$BUILD_DIR/obj\" -T \"$BUILD_DIR/tools\" -j$CORES kernel=\"$KERNEL_NAME\""
# === Step 8: Install kernel ===
KERNEL_OUTPUT="$BUILD_DIR/obj/sys/arch/amd64/compile/$KERNEL_NAME/netbsd"
if [ "$DRY_RUN" -eq 0 ] && [ ! -f "$KERNEL_OUTPUT" ]; then
echo "!! ERROR: Kernel not found at $KERNEL_OUTPUT"
exit 1
fi
echo ""
echo "==> Kernel build complete!"
echo " Output: $KERNEL_OUTPUT"
echo ""
if [ "$DRY_RUN" -eq 0 ]; then
printf "Do you want to install this kernel now? (y/n) "
read ans
case "$ans" in
y|Y)
echo "==> Installing kernel (backing up to /netbsd.old)"
run "doas cp /netbsd /netbsd.old"
run "doas cp \"$KERNEL_OUTPUT\" /netbsd"
echo "==> Done. Reboot to test the new kernel."
;;
*)
echo "==> Skipping install. You can manually run:"
echo " doas cp $KERNEL_OUTPUT /netbsd"
;;
esac
else
echo "[dry-run] Skipping kernel installation step"
fi
# === Step 9: Optional cleanup ===
if [ "$DRY_RUN" -eq 0 ] && [ "$SKIP_CLEAN" -eq 0 ]; then
echo ""
printf "Do you want to remove the build directories in $BUILD_DIR? (y/n) "
read cleanup_ans
case "$cleanup_ans" in
y|Y)
echo "==> Removing $BUILD_DIR"
run "rm -rf \"$BUILD_DIR\""
echo "==> Done. Build environment cleaned up."
;;
*)
echo "==> Keeping build directories"
;;
esac
else
echo "==> Cleanup skipped (--no-clean or dry-run)"
fi
I'm yet to test it. I might swap out Alpine for NetBSD on my test machine and see what amount of damage it does 😋