#!/bin/sh
# Fatdog64 kernel builder
# Copyright (C) James Budiono 2026
# License: GNU GPL Version 3 or later
#
# This will download, build, and package kernel for use with Fatdog64.
# Tested only on Fatdog64 and only on x86_64 platform. May work on others.
#
# Version 1: May 2026: Initial version
# Version 2: Jun 2026: aufs optional, optional kernel signing, support CIP kernels
#

### configuration
CONCURRENCY=${CONCURRENCY:-4} # default concurrency
#CIP_NO_GITLAB= # if set, download CIP kernel.org instead of from gitlab (=slow)
#FD_KEY_PATH=   # path to key/cert for signing, ".key" and ".crt" will be appeneded to path
MINSPACE=${MINSPACE:-6} # in GB, minimum freespace in the directory

### constants
VERSION=2.6

KERNEL_DL_URL="https://www.kernel.org/pub/linux/kernel"
KERNEL_CIP_DL_URL="https://git.kernel.org/pub/scm/linux/kernel/git/cip/linux-cip.git/snapshot" # slow
KERNEL_CIP_GITLAB_DL_URL="https://gitlab.com/cip-project/cip-kernel/linux-cip/-/archive"

AUFS_CLONE_URL="https://github.com/sfjro/aufs-standalone"
AUFS4_CLONE_URL="https://github.com/sfjro/aufs4-standalone"
AUFS_UTIL_CLONE_URL="git://git.code.sf.net/p/aufs/aufs-util"

STATE_DIR=0state
STAGE_01="start"
STAGE_02="aufs-option"
STAGE_03="download-aufs"
STAGE_04="checkout-aufs"
STAGE_05="download-extract-kernel"
STAGE_06="apply-aufs"
STAGE_07="apply-patches"
STAGE_08="kernel-config"
STAGE_09="build-kernel"
STAGE_10="package-kernel"
STAGE_11="package-source"
STAGE_12="complete"
FIRST_STAGE=$STAGE_01
LAST_STAGE=$STAGE_12
ALL_STAGES="$STAGE_01 $STAGE_02 $STAGE_03 $STAGE_04 $STAGE_05 $STAGE_06
$STAGE_07 $STAGE_08 $STAGE_09 $STAGE_10 $STAGE_11 $STAGE_12 $STAGE_13"
AUFS_STAGES="$STAGE_03 $STAGE_04 $STAGE_06" # so we can skip them

### variables
STAGE=
KVER= KVER_MAJOR= KVER_MINOR= KVER_PATCH=
TARBALL= KDIR=


################################
########## helpers #############
################################

#
showhelp() {
	cat << EOF | less

Fatdog64 Kernel Builder version $VERSION
========================================

To use this tool, you MUST BE root.

How to use this script:
---
1. Create a directory in a partition with large free space (6GB or more)
2. Put this script in that empty directory.
3. Run it like this: 'sh $0' (or make it executable and
   run it directly). 

How it works:
---
This script build the kernel in stages. You run this script multiple
times to advance between the stages. It will download, patch, and build
and package the kernel. It will run whatever it can run, and stops when
human intervention is needed.

Once you have resolved what the script asks you to do, you can re-run
the script. The script remembers where it left previously and will
automatically continue to the next stage.

You can interrupt the script anytime by pressing Ctrl-C. When you re-run
it, it will pick up where it left and continue; except when you are
explicitly warned NOT to interrupt.

The stages are (from first to last):
---  -------------------------------
$(echo $ALL_STAGES | tr ' ' '\n' | nl -w3 -s"  ")

Note: the number is not part of the stage name. It is just there to
show the order in which the stages will be run.

You can force the script to run directly in one of the above stages
by passing the stage-name as the first parameter, but beware, this
may end badly unless you know what you are doing.

The script stores its state in a directory called '$STATE_DIR'.
An empty file with the name of the stage (a 'stamp-file') is created when
a stage has been completed. You can manipulate this by adding/removing
the stamps yourself, but again, beware that this may have unintended
consequences unless you know what you are doing.

EOF
}


### stage management

# $1 stage
stage_done() {
	! [ -e $STATE_DIR ] && mkdir $STATE_DIR
	touch $STATE_DIR/$1
}

# out: STAGE
get_stage() {
	local p
	! [ -e $STATE_DIR ] && STAGE=$FIRST_STAGE && return
	for p in $ALL_STAGES; do
		! [ -e $STATE_DIR/$p ] && STAGE=$p && return
	done
	STAGE=$LAST_STAGE
}

### success/fail
# $1 extra msg
success() {
	stage_done $STAGE
	[ "$1" ] && echo "$1"
	echo "Stage '$STAGE' complete! Re-run the script when you're ready for the next stage."
	exit 0
}
# $1-extra msg, $2-suppress failure msg
fail() {
	[ "$1" ] && echo "$1"
	[ -z "$2" ] && echo "Stage '$STAGE' failed. Fix it, and re-run the script again."
	exit 1
}

### misc helpers

# this is the entirety of make-kernel-source.sh, with "exit" replaced with "return"
# and everything is run in a subshell
make_kernel_source() {
	(
	#!/bin/dash
	# x86_64 version
	# Note: Must be run after prep-images.sh is run!
	# $1-dirname

	TMPDIR=${TMPDIR:-/tmp}
	WORKDIR=$TMPDIR/make-source-source.$$
	SFSDIR=$WORKDIR/kernel

	### make a copy
	[ -z "$1" ] && SRC_DIR=$(pwd) || SRC_DIR=$(readlink -f $1)
	KVER=$(ls $SRC_DIR/modules/lib/modules | head -n1)
	KDIR=$SFSDIR/usr/src/linux-$KVER

	mkdir -p $WORKDIR/upper $WORKDIR/work $KDIR
	mount -t aufs aufs -obr:$WORKDIR/upper=rw:$SRC_DIR=rr $KDIR ||
	mount -t overlay overlay -oupperdir=$WORKDIR/upper,workdir=$WORKDIR/work,lowerdir=$SRC_DIR $KDIR ||
	{
		rmdir $KDIR &&
		cp  -a $SRC_DIR $KDIR
	} || {
		echo "Cannot make a copy of kernel source"
		rm -r $WORKDIR
		return 1 # instead of exit
	}

	### remove extra stuff
	cd $KDIR
	if [ -e aufs-util ]; then
		( cd aufs-util; make clean; rm -rf out )
	fi
	rm -rf aufs*/.git
	rm -rf .git modules headers prep-images.sh vmlinuz* kernel*.sfs* *.ko 2>/dev/null

	### clean the dir, prepare for modules build
	make mrproper
	cp $SRC_DIR/.config .
	make oldconfig
	make modules_prepare

	### copy important data from original build
	for p in System.map Module.symvers modules.builtin modules.order; do
		cp $SRC_DIR/$p .
	done

	### bundle it
	cd $TMPDIR
	mksquashfs $SFSDIR kernel-source-${KVER}.sfs -comp xz -noappend
	umount $KDIR
	rm -r $WORKDIR
	mv kernel-source-${KVER}.sfs $SRC_DIR
	)
}

# difference between this and the standalone prep-images.sh:
# - aufs-util is built using musl-gcc and not aboriginal gcc
# - split into two parts, and stop (not pause) in between
# and everything is run in a subshell
prep_images() {
	(
	PART=
	if [ -e modules ]; then
		if [ -e modules/part1-done ]; then
			PART=PART2
		else
			PART=PART1
		fi
	else
		PART=PART1
	fi
	
	# PWD must be the kernel source
	case $PART in
		PART1) ### install modules and firmware
			make modules_install INSTALL_MOD_PATH=modules                  &&
			rm -f modules/lib/modules/*/build modules/lib/modules/*/source &&
			KVER=$(ls modules/lib/modules | head -n1)                      &&
			ln -s /usr/src/linux-$KVER modules/lib/modules/$KVER/build     &&
			ln -s /usr/src/linux-$KVER modules/lib/modules/$KVER/source    &&

			echo Copying firmware from current kernel ... &&
			cp -a /lib/firmware modules/lib               &&
			touch modules/part1-done                      &&
			cat << EOF && return 2 || return 1 ;;
---
Now pausing to give you the chance to add/remove firmware and 3rd party
modules.

Root directory (of what will become the kernel-modules.sfs):
$(pwd)/modules

Firmware location: /modules/lib/firmware
Modules  location: /modules/lib/modules/$KVER

When you re-run the script, the packaging process will continue.
EOF

		PART2) ### build aufs-utils and package
			KVER=$(ls modules/lib/modules | head -n1) &&
			depmod -a -b modules $KVER                &&

			# build aufs utilities, if source exist and we have musl-gcc
			if [ -e aufs-util ]; then
				if type musl-gcc; then
					make headers_install INSTALL_HDR_PATH=headers &&
						( 
						cd aufs-util
						sed -i 's/BuildFHSM = yes/BuildFHSM = no/' ./Makefile
						grep -RFl 'AuFin(NULL)' | xargs sed -i -e 's|AuFin(NULL)|AuFin("")|'
						make clean &&
						make HOSTCC=musl-gcc CC=musl-gcc CPPFLAGS="-I../headers/include -I../../headers/include"  CFLAGS="-static" &&
						make install DESTDIR=$(pwd)/out CPPFLAGS="-I../headers/include -I../../headers/include" CFLAGS="-static"
						) &&
					cp -av aufs-util/out/* modules &&
					rm -rf modules/usr/lib64 modules/usr/lib || # don't keep broken libau.so
					cat << EOF
----------
aufs-util build FAILED, so aufs-util tools will not be included in the
resulting kernel-modules.sfs. This is not critical, the aufs can operate
without aufs-util. However if you want aufs-util to be included for
completeness reason, you need to investigate and fix this build failure.
----------
EOF
				else
					cat << EOF
----------
aufs-util NOT built because musl-gcc doesn't exist.
----------
EOF
				fi
			fi &&
			mksquashfs modules ../kernel-modules.sfs-$KVER -comp xz -Xbcj x86 -noappend &&
			cp -a arch/x86/boot/bzImage ../vmlinuz-$KVER && # unsigned kernel
			rm modules/part1-done &&

			# kernel signing
			if [ "$FD_KEY_PATH" ]; then
				if [ -e "${FD_KEY_PATH}.key" ] && [ -e "${FD_KEY_PATH}.crt" ]; then
					if type sbsign; then
						sbsign --key ${FD_KEY_PATH}.key --cert ${FD_KEY_PATH}.crt \
							--output ../vmlinuz-$KVER arch/x86/boot/bzImage # signed kernel
					else
						echo "sbsign unavailable, kernel is not signed."
						return 1 # if signing is asked but we can't sign, it's an error
					fi
				else
					echo "Can't find key and cert in $FD_KEY_PATH, kernel is not signed."
					return 1 # if signing is asked but we can't sign, it's an error
				fi		
			fi && return 0 || return 1;;
	esac
	)
}

# out: KVER, KVER_MAJOR, KVER_MINOR, KVER_PATCH
get_kernel_version() {
	# if already exist, then use it
	if [ -e $STATE_DIR/KVER ]; then
		. $STATE_DIR/KVER
		return
	fi

	# otherwise ask
	cat << EOF

START STAGE
===========

To start building the kernel, you need to specify the kernel version that
you want to build.

The kernel version looks like three numbers, separated by dots.
Something like x.y.z, or, more concretely, 6.12.89 or 7.0.8.

You can check out the list of available kernels from here:
https://kernel.org/

You can also build CIP kernels, see list from here:
https://git.kernel.org/pub/scm/linux/kernel/git/cip/linux-cip.git/refs/tags

Note that only released kernels are supported. The script does not
support building "rc" kernels.

Please enter the kernel version you want to build:
EOF
	read KVER
	echo $KVER | read KVER # trim spaces
	#echo $KVER

	# parse
	OIFS=$IFS
	IFS=.
	set -- $KVER
	IFS=$OIFS
	KVER_MAJOR=$1
	KVER_MINOR=$2
	KVER_PATCH=$3

	# sanity checks
	if [ -z $KVER_MAJOR ] || [ -z $KVER_MINOR ]; then
		echo "Kernel version must be x.y or x.y.z."
		return 1
	fi
	if [ $KVER_MAJOR -lt 4 ]; then
		echo "Ancient kernels are not supported. Must be at least kernel version 4.x."
		return 1
	fi
	return 0
}



##############################
######### the stages #########
##############################

#
stage_start() {
	local p DL_URL
	# sanity check
	if ! type gcc; then
		fail "Cannot find gcc. Please load devx."
	fi
	if ! type make; then
		fail "Cannot find make. Please load devx."
	fi	
	if ! type git; then
		fail "Cannot find git. Please load devx."
	fi
	if [ $(df -k . | awk 'NR==2 {print $4}') -le $((MINSPACE*1024*1024)) ]; then
		fail "Freespace on this directory is too small. You need at least $MINSPACE GB free."
	fi
	p=$(ls | wc -l)
	if [ $p -ne 1 ]; then
		echo "WARNING: you're running this script NOT in an empty directory."
	fi
	
	# get kernel version
	if get_kernel_version; then
		get_kernel_download_url # to get TARBALL, DL_URL, KDIR
		if kernel_exist_in_repo "$DL_URL"; then
			stage_done $STAGE # this makes sure $STAGE_DIR exist
			output_KVER
			success "Ok, configured to build kernel $KVER."
		else
			fail "Kernel $KVER does not exist."
		fi
	fi
}
output_KVER() {
	{
	echo "BUILDER=$VERSION" # for detecting backward/forward compatibility
	echo "KVER=$KVER" 
	echo "KVER_MAJOR=$KVER_MAJOR"
	echo "KVER_MINOR=$KVER_MINOR"
	echo "KVER_PATCH=$KVER_PATCH"
	echo "TARBALL=$TARBALL"
	echo "KDIR=$KDIR"	
	} > $STATE_DIR/KVER
}
# out: TARBALL, DL_URL, KDIR
get_kernel_download_url() {
	case $KVER in
		*cip*) # cip kernel
			if [ "$CIP_NO_GITLAB" ]; then
				TARBALL=linux-cip-$KVER.tar.gz
				DL_URL="$KERNEL_CIP_DL_URL/$TARBALL"
				KDIR=linux-cip-$KVER
			else # download from gitlab
				TARBALL=linux-cip-v$KVER.tar.bz2
				DL_URL="${KERNEL_CIP_GITLAB_DL_URL}/v$KVER/$TARBALL"
				KDIR=linux-cip-v$KVER
			fi
			;;		
		*) # standard kernel
			TARBALL=linux-$KVER.tar.xz
			DL_URL="$KERNEL_DL_URL/v${KVER_MAJOR}.x/$TARBALL"
			KDIR=linux-$KVER
			;;
	esac
}
# $1-DL_URL
kernel_exist_in_repo() {
	echo "Checking if kernel $KVER exists, please wait ..."
	wget -q --spider "$DL_URL"
}

#
stage_aufs_option() {
	local p
	get_kernel_version
	cat << EOF

AUFS Usage
==========

Fatdog needs to a kernel compiled with aufs support. But maybe you are
going to build a kernel for other systems which does not use aufs.

Type 'N' to chose not to use aufs, or just press Enter to use aufs.
EOF
	read p
	if [ "$p" = 'N' ] || [ "$p" = 'n' ]; then
		# mark all the aufs-stages as completed
		for p in $AUFS_STAGES; do
			stage_done $p
		done
		success "All aufs-related stages will be skipped."
	else
		success
	fi
}

#
stage_download_aufs() {
	get_kernel_version
	echo "Downloading aufs, please wait ..."
	if ! [ -e aufs-standalone ]; then
		if [ $KVER_MAJOR -eq 4 ]; then
			git clone "$AUFS4_CLONE_URL" aufs-standalone
		else
			git clone "$AUFS_CLONE_URL" aufs-standalone
		fi
	else
		true
	fi &&
	if ! [ -e aufs-util ]; then
		git clone "$AUFS_UTIL_CLONE_URL" aufs-util
	else
		true
	fi &&
	success || fail "Failed to download aufs."	
}

#
stage_checkout_aufs() {
	local AUFS_NOT_DONE= AUFS_UTIL_NOT_DONE=
	get_kernel_version
	( cd aufs-standalone &&
	  git status | grep -q master
	) && AUFS_NOT_DONE=yes

	( cd aufs-util &&
	  git status | grep -q master
	) && AUFS_UTIL_NOT_DONE=yes

	if [ -z "$AUFS_NOT_DONE" ] && [ -z "$AUFS_UTIL_NOT_DONE" ]; then
		success "aufs and aufs-util have been checked out."
	fi

	if [ $AUFS_NOT_DONE ]; then
	cat << EOF

CHECK-OUT AUFS
==============

You need to 'checkout' the aufs version for use with the kernel. 

To do that:
1. cd into aufs-standalone
2. run 'git branch -a' to see the available versions (you can use
   cursor keys to scroll up and down, and press 'q' when done).
3. run 'git checkout aufs\$VERSION' where \$VERSION is the closest to the
   kernel version $KVER. The suggested version to use is 'aufs${KVER_MAJOR}.${KVER_MINOR}',
   but if you see something newer (and closer to $KVER), like
   'aufs${KVER_MAJOR}.${KVER_MINOR}.17', use that instead of 'aufs${KVER_MAJOR}.${KVER_MINOR}',
   but do not use a version newer that $KVER itself.

Re-run the script when you're done.
EOF
	return
	fi

	if [ $AUFS_UTIL_NOT_DONE ]; then
	cat << EOF

CHECK-OUT AUFS-UTIL
===================

You need to 'checkout' the aufs-util versions for use with the kernel. 

To do that:
1. cd into aufs-util
2. run 'git branch -a' to see the available versions (you can use
   cursor keys to scroll up and down, and press 'q' when done).
3. run 'git checkout aufs\$VERSION' where \$VERSION is the closest to the
   kernel version $KVER. The suggested version to use is 'aufs${KVER_MAJOR}.0',
   but if you see something newer (and closer to $KVER), like 'aufs${KVER_MAJOR}.1'
   use that instead of 'aufs${KVER_MAJOR}.0', but do not use
   a version newer that 'aufs${KVER_MAJOR}.${KVER_MINOR}'.

Re-run the script when you're done.
EOF
	return
	fi
}

#
stage_download_extract_kernel() {
	local DL_URL
	get_kernel_version
	get_kernel_download_url
	
	if [ -e $TARBALL ]; then
		echo "$TARBALL already exist, re-using."
	else
		wget "$DL_URL" && echo "$TARBALL downloaded"
	fi &&
	if [ -e $KDIR ]; then
		echo "Extracted kernel directory $KDIR already exist, re-using."
	else
		output_KVER # update KVER based on updated value of TARBALL and KDIR	
		echo "Extracting $TARBALL to $KDIR, please wait ..."
		tar -xf $TARBALL
	fi &&
	success || failed
}

#
stage_apply_aufs() {
	local p PATCH_BASE
	get_kernel_version

	# copy aufs to $KDIR
	if ! [ -e $KDIR/aufs-standalone ]; then
		cp -a aufs-standalone $KDIR
	fi
	if ! [ -e $KDIR/aufs-util ]; then
		cp -a aufs-util $KDIR
	fi
	
	# detect previous runs, success or fail
	if [ -e $KDIR/aufs-standalone/applied ]; then
		success "aufs already applied, skipping this step."
	fi
	if [ -e $KDIR/aufs-standalone/failed ]; then
cat << EOF

WARNING WARNING
===============

The script has previously tried to apply aufs to this kernel, but
failed. Have you fixed it?

Type 'Y' if you have, and want to continue. Anything else cancels.
EOF
		read p
		if [ "$p" = 'Y' ] || [ "$p" = 'y' ]; then
			success
		else
			fail "Cancelled."
		fi
	fi

	# first run
	cat << EOF

IMPORTANT, PLEASE READ FIRST
============================

The script is about to apply aufs to the kernel. This is a complicated
process which performs various transformations, and each and every one
of these transformations can fail.

If the script informs you that this process fails, do not proceed to
continue building the kernel. You will only experience grief.

Instead, fix it manually if you can (this script cannot offer you
any advice, because if it could, it would have already applied those
fixes automatically for you). 

If you are unable, copy the error messages and ask for help. Chances are,
other people have experienced the same errors and they would be able to
give you the steps to fix the errors.

Once you resolve the problem, re-run the script.

---------------------------

Now press Enter to continue.
EOF
	read p
	PATCH_BASE=aufs$KVER_MAJOR
	if (
		cd $KDIR &&
		# copy
		cp -av aufs-standalone/Documentation . &&
		cp -av aufs-standalone/fs . &&
		cp -av aufs-standalone/include/uapi/linux/aufs_type.h include/uapi/linux &&
		# patch
		patch -Np1 < aufs-standalone/$PATCH_BASE-kbuild.patch   &&
		patch -Np1 < aufs-standalone/$PATCH_BASE-base.patch     &&
		patch -Np1 < aufs-standalone/$PATCH_BASE-mmap.patch     &&
		patch -Np1 < aufs-standalone/$PATCH_BASE-loopback.patch &&
		patch -Np1 < aufs-standalone/vfs-ino.patch              &&
		patch -Np1 < aufs-standalone/tmpfs-idr.patch
	); then
		touch $KDIR/aufs-standalone/applied
		success "aufs applied successfully."
	else
		touch $KDIR/aufs-standalone/failed
		fail "Failed to apply aufs. Fix, then re-run the script."
	fi 
}

#
stage_apply_patches() {
	local p
	get_kernel_version
	cat << EOF

APPLYING OTHER PATCHES
======================

If the kernel you're going to build requires other patches, now is the
time. The script will exit to give you the chance to apply those
patches.

Now type 'Y' if you want to exit the script to apply other patches,
or just press Enter to continue to the next stage. 
EOF
	read p
	if [ "$p" = 'Y' ] || [ "$p" = 'y' ]; then
		fail "Paused for user intervention." no-fail-msg
	else
		success
	fi

}

#
stage_kernel_config() {
	local p
	get_kernel_version
	if [ -e $KDIR/.config ]; then
		if
		( cd $KDIR &&
		  yes "" | make oldconfig
		); then
			success "Config applied. Next we will build the kernel."
		else
			fail "Unable to apply the config."
		fi
	fi

	cat << EOF

KERNEL CONFIGURATION
====================

You need to configure the kernel before you can build it.
There are a few ways to do it.

1. (Easy) Find an existing good configuration file (commonly called
DOTconfig in the Fatdog/Puppy community), and put them under $KDIR with
the name '.config'. Something like:

'cp /path/to/known/good/DOTconfig $KDIR/.config'

2. (HARD) Or you can start from scratch. You can do this by creating
a default configuration ('defconfig') and then start customising.
You can do this by doing the following:

'cd $KDIR'
'make defconfig'
'make menuconfig'

3. (Medium) Or, you can do (2), but start from an existing configuration
instead of from scratch. Do it by doing the following:

'cp /path/to/known/good/DOTconfig $KDIR/.config'
'cd $KDIR'
'yes "" | make oldconfig'
'make menuconfig'

NOTE: neither (1), (2) or (3) guarantees that the kernel will boot.
There are 1001 knobs on the kernel and if you get even one of them wrong,
the house of cards will crumble. It's not your fault. It just is.

EOF
	fail "Paused for user intervention." no-fail-msg
}

#
stage_build_kernel() {
	get_kernel_version
	cat << EOF

KERNEL BUILD
============

The script is about to build the kernel. This is your last chance to
make any manual changes to the kernel source.

The build will take a very long time. Get your coffee or any other
favorite drink and let the computer do its job.

The build can be interrupted by pressing Ctrl-C at any time.
You re-run the script to continue the build. It will pick up where 
it left.

---------------

Type 'Y' to start building now, or any other key to exit.
EOF
	read p
	if [ "$p" = 'Y' ] || [ "$p" = 'y' ]; then
		if (cd $KDIR && make -j$CONCURRENCY bzImage modules); then
			success "Kernel has been built."
		else
			fail "Failed to build the kernel"
		fi
	else
		fail "Paused for user intervention." no-fail-msg
	fi
}

#
stage_package_kernel() {
	local PACKAGER
	get_kernel_version
	cat << EOF

PACKAGE KERNEL
==============

To make the kernel usable for Fatdog, you need to package it.
The script can do the packaging, but you can also use an external
packager.

The external packager will be run with no parameters, with the current
directory set to the $KDIR.

If you want to use an external packager, please provide the full path
to the packager. Otherwise just press Enter to use the internal
packager.

EOF
	read PACKAGER
	if [ "$PACKAGER" ]; then
		# external packager
		if ! [ -e "$PACKAGER" ]; then
			fail "Cannot find $PACKAGER."
		fi
		if ! [ -x "$PACKAGER" ]; then
			fail "$PACKAGER is not executable."
		fi
		if [ -d "$PACKAGER" ]; then
			fail "Path cannot point to a directory."
		fi		
		if (
		  cd $KDIR
		  $PACKAGER
		); then
			success "Packaging (external) is successful."
		else
			fail "Packging (external) failed."
		fi
	else
		# internal packager
		( cd $KDIR &&
		  prep_images
		)
		case $? in
			0) success  ;; # complete
			1) fail     ;; # failure
			2) return 0 ;; # pause to add/remove firmware
		esac
	fi
}

#
stage_package_source() {
	local p
	get_kernel_version
	cat << EOF

PACKAGE KERNEL SOURCE
=====================

This will take a while. Please do not interrupt, or you will be left
with dangling mounts that you have to clean yourself.

Type 'Y' to start packaging now, or any other key to exit.
EOF
	read p

	if [ "$p" = 'Y' ] || [ "$p" = 'y' ]; then
		if (
		  cd $KDIR &&
		  make_kernel_source
		); then
			if mv -v $KDIR/kernel-source*.sfs .; then
				success "Kernel source packaged successfully."
			else
				fail "Failed to package kernel source."
			fi
		else
			fail "Failed to package kernel source."
		fi
	else
		fail "Paused for user intervention." no-fail-msg
	fi
}

#
stage_complete() {
	get_kernel_version
	cat << EOF

CONGRATULATIONS!
================

The build process is complete.
You will find vmlinuz-$KVER, kernel-modules.sfs-$KVER, and
kernel-source-$KVER.sfs in the directory.

The name might be slightly different if you use CONFIG_LOCALVERSION,
or if you use the CIP kernel, but they should still reflect $KVER.

EOF
	stage_done $STAGE
}

######################
### main
### $1 is forced stage
######################
STAGE=${STAGE:-$1}
[ -z $STAGE ] && get_stage
case $STAGE in
	$STAGE_01) stage_start ;;
	$STAGE_02) stage_aufs_option ;;
	$STAGE_03) stage_download_aufs ;;
	$STAGE_04) stage_checkout_aufs ;;
	$STAGE_05) stage_download_extract_kernel ;;
	$STAGE_06) stage_apply_aufs ;;
	$STAGE_07) stage_apply_patches ;;
	$STAGE_08) stage_kernel_config ;;
	$STAGE_09) stage_build_kernel ;;
	$STAGE_10) stage_package_kernel ;;
	$STAGE_11) stage_package_source ;;
	$STAGE_12) stage_complete ;;
	--help|-h) showhelp ;;
	--version|-v) echo $VERSION ;;
	*) echo Invalid stage $STAGE; exit ;;
esac

