#!/bin/sh

# Copyright (c) 2004-2011  Sean Farley <scf@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

#
# Program:  fbinst.sh
# Version:  See ${VERSION}
# URL:  http://www.farley.org/
#
# $Id: fbinst.sh 244 2011-09-09 00:19:25Z sean $
#

############################################################

# Initialize version and path to required tools.
readonly VERSION="0.9.5"
readonly PATH=/sbin:/bin:/usr/bin:/usr/sbin

############################################################


# Help for the user.
usage()
{
	echo "Usage:  `basename ${0}` [-B dir] [-D dir] [-J dir]" \
		"[-S supfile]"
	echo "        [-T TARGET_ARCH[:TARGET]] [-c] [-h] [-n N] [-bw] [-iw]"
	echo "        [[-b(k|a)] [-i(k|a)] [-d DEFAULT_KERNEL]" \
		"[KERNEL1 KERNEL2 ...]]"
	echo ""
	echo "Options:"
	echo "  -B dir      Base directory for src and obj dirs (default: /usr)"
	echo "  -D dir      Destination directory (default:  /)"
	echo "  -J dir      Jail directory to configure"
	echo "  -S file     supfile used with CVSup"
	echo "  -T TARGET_ARCH[:TARGET]"
	echo "              Target platform (default:  `uname -p`:`uname -m`)"
	echo "  -a          Automatic execution; no prompts"
	echo "  -b opt      Build (a)ll, (k)ernels or (w)orld"
	echo "  -c          CVSup before building world or all"
	echo "  -d opt      Kernel to install as default kernel"
	echo "  -h          Help"
	echo "  -i opt      Install (a)ll, (k)ernels or (w)orld"
	echo "  -j jobs     Maximum number of jobs (default:  ${JOBS};" \
		"see make(1))"
	echo "  -n N        Nice level for building (default:  ${NICE})"
	echo "  -V          Display version"

	return
}


# Install kernels.
install_kernels()
{
	# Install kernel(s).
	if [ "${INSTALL}" = "k" -o "${INSTALL}" = "a" ]
	then
		# Verify that at least one kernel is to be installed.
		if [ "${DEFAULTKERNEL}" = "" -a "${EXTRAKERNELS}" = "" ]
		then
			echo "Specify a kernel to install with this request."
			exit 1
		fi

		cd ${SRCDIR}
		for k in ${DEFAULTKERNEL} ${EXTRAKERNELS}
		do
			# Install non-default kernels as /kernel.NAME.
			if [ "${k}" != "${DEFAULTKERNEL}" ]
			then
				INSTKERNNAME="INSTKERNNAME=kernel.${k}"
			else
				INSTKERNNAME=""
			fi

			# Install kernel.
			echo -n "Installing kernel:  ${k}... "
			INSTKERNELLOG=${LOGDIR}/ik.${k}.${HOSTNAME}.log
			nice -n ${NICE} make -DALWAYS_CHECK_MAKE installkernel \
				${DESTDIREQ} KERNCONF=${k} ${BUILDTARGET} \
				${INSTKERNNAME} > ${INSTKERNELLOG} 2>&1
			if [ ${?} -ne 0 ]
			then
				echo ${FAILUREMSG}
				echo "Install kernel (${k}) failed. " \
					"Review ${INSTKERNELLOG}."
				exit 1
			fi
			echo ${SUCCESSMSG}
		done
	fi
}


# Install world.
install_world()
{
	# World installation.
	if [ "${INSTALL}" = "w" -o "${INSTALL}" = "a" ]
	then
		cd ${SRCDIR}

		# Install world.
		echo -n "Installing world... "
		nice -n ${NICE} make installworld ${DESTDIREQ} ${BUILDTARGET} \
			> ${INSTWORLDLOG} 2>&1
		if [ ${?} -ne 0 ]
		then
			echo ${FAILUREMSG}
			echo "Install world failed.  Review ${INSTWORLDLOG}."
			exit 1
		fi
		echo ${SUCCESSMSG}
	fi
}


# Install new distribution set of files.
install_dist()
{
	if [ "${INSTALL}" = "w" -o "${INSTALL}" = "a" ]
	then
		cd ${SRCDIR}/etc

		# Install distribution.  Change MAKEOBJDIRPREFIX to be
		# architecture-specific if built on a different architecture
		# else it will fail when installing freebsd*.cf files from
		# etc/sendmail.  Change back after the distribution is complete.
		set_target_obj
		echo -n "Installing distribution... "
		nice -n ${NICE} make distribution ${DESTDIREQ} ${BUILDTARGET} \
			> ${INSTDESTLOG} 2>&1
		exitVal=${?}
		reset_target_obj

		# The install of /etc/ttys is dependent upon the make tool used
		# since MACHINE and MACHINE_TARGET (see make distribution rule)
		# are compiled into make (or are they?).  Also,
		# /boot/device.hints has the same problem.  Install it manually
		# here.
		if [ ${exitVal} -eq 0 -a "${TARGET}" != "${HOST_MACHINE}" ]
		then
			nice -n ${NICE} install -o root -g wheel -m 644 \
				etc.${TARGET_ARCH}/ttys ${DESTDIR}/etc \
				>> ${INSTDESTLOG} 2>&1
			exitVal=${?}
			cd ${SRCDIR}
			nice -n ${NICE} install -o root -g wheel -m 444 \
				sys/${TARGET}/conf/GENERIC.hints \
				${DESTDIR}/boot/device.hints >> \
				${INSTDESTLOG} 2>&1
			exitVal=$((${?} + exitVal))
		fi

		if [ ${exitVal} -ne 0 ]
		then
			echo ${FAILUREMSG}
			echo "Install distribution failed. " \
				"Review ${INSTDESTLOG}."
			exit 1
		fi
		echo ${SUCCESSMSG}

		NEWDIST="YES"
	fi
}


# Set MAKEOBJDIRPREFIX in relation to the target architecture directory below
# the parent obj directory.
set_target_obj()
{
	if [ "${TARGET}" != "${HOST_MACHINE}" ]
	then
		moDirPrefix="${MAKEOBJDIRPREFIX}"
		export MAKEOBJDIRPREFIX="${moDirPrefix}/${TARGET}"
	fi
}


# Reset MAKEOBJDIRPREFIX back to the parent obj directory containing the target
# architecture directory.
reset_target_obj()
{
	if [ "${TARGET}" != "${HOST_MACHINE}" ]
	then
		export MAKEOBJDIRPREFIX="${moDirPrefix}"
	fi
}


# Clean a build with a specific MAKEOBJDIRPREFIX and build target.  This is used
# to clean the standard target as well as compatible binaries such as lib32 for
# the amd64 target.
clean_build()
{
	local makeObjDirPrefix=${1}
	local buildTarget="${2}"

	for clean in cleandir cleanworld
	do
		echo -n "Executing \"make ${clean} ${buildTarget}\"... "
		MAKEOBJDIRPREFIX=${makeObjDirPrefix} nice -n ${NICE} \
			make ${clean} ${buildTarget} > ${CLEANDIRLOG} 2>&1
		if [ ${?} -ne 0 ]
		then
			echo ${FAILUREMSG}
			echo "Error executing \"make ${clean} ${buildTarget}\"."
			exit 1
		fi
		echo ${SUCCESSMSG}
	done
}


# Gather command-line arguments.
JOBS=`sysctl -n kern.smp.cpus 2> /dev/null` || JOBS=0
NICE=20
while getopts "B:D:J:S:T:Vab:cd:hj:i:n:" opt
do
	case ${opt} in
	B)
		SRCOBJBASEDIR="`realpath ${OPTARG}`"
		;;
	D)
		readonly DESTDIR="`realpath ${OPTARG}`"
		;;
	J)
		JAILDIR="`realpath ${OPTARG}`"
		if [ ! -d ${JAILDIR} ]
		then
			echo "Jail directory \"${JAILDIR}\" not found."
			usage
			exit 1
		elif [ ! -d ${JAILDIR}/etc ]
		then
			echo "Jail directory \"${JAILDIR}/etc\" not found."
			usage
			exit 1
		else
			JAILDIRS="${JAILDIRS} ${JAILDIR}"
		fi
		;;
	S)
		SUPFILE="`realpath ${OPTARG}`"
		;;
	T)
		TARGET_ARCH=${OPTARG%:*}
		if [ "${TARGET_ARCH}" != "${OPTARG}" ]
		then
			TARGET=${OPTARG#*:}
		fi
		;;
	V)
		echo "FreeBSD installer (`basename ${0}`) v${VERSION}"
		exit 0
		;;
	a)
		AUTO="YES"
		;;
	b|i)
		if [ ${opt} = "b" ]
		then
			BUILD=${OPTARG}
			MESG="Build"
		else
			INSTALL=${OPTARG}
			MESG="Install"
		fi

		if [ "${OPTARG}" != "a" -a "${OPTARG}" != "k" -a \
			"${OPTARG}" != "w" ]
		then
			echo "${MESG} [all|kernel|world]"
			usage
			exit 1
		fi
		;;
	c)
		readonly CVSUP="YES"
		;;
	d)
		DEFAULTKERNEL="${OPTARG}"
		;;
	j)
		JOBS="${OPTARG}"
		;;
	n)
		NICE="${OPTARG}"
		;;
	h|\?)
		usage
		exit 0
		;;
	esac
done
shift $((${OPTIND} - 1))

# Additional kernels to build and/or install.  Installed as kernel.NAME.
# Remaining arguments are taken as kernels to process.
readonly EXTRAKERNELS="${*}"

# System source and object directories.
readonly SRCOBJBASEDIR=${SRCOBJBASEDIR:-"/usr"}
readonly OBJDIR="${SRCOBJBASEDIR}/obj"
readonly SRCDIR="${SRCOBJBASEDIR}/src"

# Initialization.  Also set defaults, if values were not provided by user.
readonly HOST_MACHINE=`uname -m`
readonly HOST_MACHINE_ARCH=`uname -p`
if [ -z ${TARGET_ARCH} ]
then
	TARGET_ARCH=${HOST_MACHINE_ARCH}
fi
if [ -z ${TARGET} ]
then
	TARGET=${TARGET_ARCH}
fi
readonly BUILDTARGET="TARGET_ARCH=${TARGET_ARCH} TARGET=${TARGET}"
readonly AUTO=${AUTO:-"NO"}
readonly DATE=`date "+%Y%m%d-%H%M%S"`
readonly DEFAULTKERNEL=${DEFAULTKERNEL:-""}
readonly DESTDIREQ="${DESTDIR:+DESTDIR=${DESTDIR}}"
readonly FREEBSDVER=`uname -r | sed -e "s/\..*//"`
readonly HOSTNAME=`hostname -s`
export readonly LANG=C
export readonly LC_ALL=C
export readonly MAKEFLAGS="-m ${SRCDIR}/share/mk"
readonly JOBS=${JOBS:+"-j ${JOBS}"}
readonly NICE=${NICE}
readonly SUPFILE="${SUPFILE:+SUPFILE=${SUPFILE}}"

# Verify the src directory can be found.
if [ ! -d ${SRCDIR} ]
then
	echo "Source directory (${SRCDIR}) not found."
	exit 1
fi

# Logs.
readonly LOGDIR="${SRCDIR}/buildlogs/${DATE}/${TARGET_ARCH}/${TARGET}"
readonly BUILDSYSINSTALLLOG=${LOGDIR}/bsi.log
readonly BUILDWORLDLOG=${LOGDIR}/bw.log
readonly CLEANDIRLOG=${LOGDIR}/clean.log
readonly CVSUPLOG=${LOGDIR}/cvsup.log
readonly INSTALLSYSINSTALLLOG=${LOGDIR}/isi.${HOSTNAME}.log
readonly INSTDESTLOG=${LOGDIR}/id.${HOSTNAME}.log
readonly INSTWORLDLOG=${LOGDIR}/iw.${HOSTNAME}.log
readonly MERGELOG=${LOGDIR}/merge.${HOSTNAME}.log

# Messages.
readonly FAILUREMSG="failed."
readonly SUCCESSMSG="succeeded."

# Build environment.
#
# XXX:  CROSS_BUILD_TESTING makes a target that is the native environment uses a
# subdirectory under MAKEOBJDIRPREFIX.  Unfortunately, it does not work during
# the install of the 32-bit compatibility binaries for amd64.  MAKEOBJDIRPREFIX
# gets set by the build system to .../obj/amd64/amd64/lib32.  I left this here
# in case the build system gets fixed.
#export CROSS_BUILD_TESTING=yes
export MAKEOBJDIRPREFIX="${OBJDIR}"

# Verify acceptable system version.
# XXX: should be testing the code version too?
if [ ${FREEBSDVER} -lt 4 -o ${FREEBSDVER} -gt 8 ]
then
	echo "Untested on FreeBSD ${FREEBSDVER}."
	exit 1
fi

# Was at least one option provided?
if [ "${BUILD}" = "" -a "${INSTALL}" = "" -a "${JAILDIRS}" = "" -a \
	"${CVSUP}" = "" ]
then
	echo "Please indicate whether to CVSup, build, install and/or" \
		"configure jail."
	usage
	exit 1
fi

# mergemaster settings.
# 8.x and later 7.x versions take "-m ${SRCDIR}".  Use grep to determine.
mmpath="${SRCDIR}/usr.sbin/mergemaster/mergemaster.sh"
if [ ! -f ${mmpath} ]
then
	echo "mergemaster (${mmpath}) not found."
	exit 1
fi
if [ ${FREEBSDVER} -ge 7 -a `grep -c '/usr/src/etc/Makefile' ${mmpath}` -gt 0 ]
then
	mmopt="${SRCDIR}"
else
	mmopt="${SRCDIR}/etc"
fi
if [ -n ${TARGET_ARCH} ]
then
	# Set the target via a variable that mergemaster understands since -A
	# will not suffice.
	export ARCHSTRING="TARGET_ARCH=${TARGET_ARCH} TARGET=${TARGET}"
fi
mmTempRoot="/var/tmp/temproot${TARGET_ARCH:+.${TARGET_ARCH}.${TARGET}}"
readonly MERGEMASTER="${mmpath} -m ${mmopt} -t ${mmTempRoot}"
umask 022

# CVSup should not be used with install alone.  A build must be involved or no
# install.
if [ "${CVSUP}" != "" -a "${INSTALL}" != "" -a "${BUILD}" = "" ]
then
	echo "Specify a build with CVSup or drop the install request."
	usage
	exit 1
fi

# A valid source directory is required.
if [ ! -d ${SRCDIR} ]
then
	echo "Source directory \"${SRCDIR}\" not usable."
	exit 1
fi

# Check for valid kernel configurations if building kernels.
if [ "${BUILD}" = "k" -o "${BUILD}" = "a" ]
then
	# Verify that at least one kernel is to be built.
	if [ "${DEFAULTKERNEL}" = "" -a "${EXTRAKERNELS}" = "" ]
	then
		echo "Specify a kernel to build with this request."
		exit 1
	fi

	for k in ${DEFAULTKERNEL} ${EXTRAKERNELS}
	do
		KERNCONF="${SRCDIR}/sys/${TARGET_ARCH:-${HOST_MACHINE_ARCH}}/conf/${k}"
		if [ ! -f ${KERNCONF} ]
		then
			echo "Kernel configuration \"${KERNCONF}\" not found."
			exit 1
		fi
	done
fi

# Verify DESTDIR is set if installing on a different architecture.
if [ -z "${DESTDIR}" -a -n "${INSTALL}" -a \
	\( \( -n "${TARGET_ARCH}" -a "${TARGET_ARCH}" != `uname -p` \) -o \
	\( -n "${TARGET}" -a "${TARGET}" != ${HOST_MACHINE_ARCH} \) \) ]
then
	echo "Due to difference in target architectures, please specify" \
		"destination (-D)."
	exit 1
fi

# Clear the screen.
clear

# Create log directory.
echo "Creating log directory (${LOGDIR})."
mkdir -p ${LOGDIR}
if [ ${?} -ne 0 ]
then
	echo "Could not create \"${LOGDIR}\"."
	exit 1
fi

# World-building setup.
if [ "${BUILD}" = "a" -o "${BUILD}" = "w" ]
then
	# Clean out object directory for the specific architecture if it exists.
	# Try to be a little paranoid since a directory is going to be wiped.
	readonly TMPOBJDIR=`echo ${OBJDIR}/${TARGET} | \
		sed 's/^[ \t]*//;s/[ \t]*$//'`
	if [ 1 -eq 0 -a -n "${TARGET_ARCH}" -a -d ${TMPOBJDIR} ]
	then
		if [ "${TMPOBJDIR}" = "" -o "`realpath ${TMPOBJDIR}`" = "/" ]
		then
			echo "Unable/unwilling to clean \"${TMPOBJDIR}\"."
			exit 1
		else
			echo -n "Cleaning ${TMPOBJDIR}... "
			nice -n ${NICE} chflags -R noschg ${TMPOBJDIR} \
				2> /dev/null
			if [ ${?} -ne 0 ]
			then
				echo ${FAILUREMSG}
				echo "Error executing \"chflags -R noschg" \
					"${TMPOBJDIR}\"."
				exit 1
			fi
			nice -n ${NICE} rm -rf ${TMPOBJDIR} 2> /dev/null
			if [ ${?} -ne 0 ]
			then
				echo ${FAILUREMSG}
				echo "Error executing \"rm -rf ${TMPOBJDIR}\"."
				exit 1
			fi
			echo ${SUCCESSMSG}
		fi
	fi

	# Clean out previous build using build system.
	set_target_obj
	cd ${SRCDIR}
	clean_build ${MAKEOBJDIRPREFIX} "${BUILDTARGET}"
	if [ -d ${MAKEOBJDIRPREFIX}/lib32 ]
	then
		clean_build ${MAKEOBJDIRPREFIX}/lib32 \
			"MACHINE_ARCH=i386 MACHINE=i386"
		lib32Dir=${MAKEOBJDIRPREFIX}/lib32
	fi

	# find/rmdir removes a few stragglers that the build system does not
	# handle.  Also, remember to remove lib32 directory if the target builds
	# it.
	for straggler in ${MAKEOBJDIRPREFIX}/usr ${lib32Dir}
	do
		if [ -d ${straggler} ]
		then
			echo -n "Deleting straggler directory" \
				"\"${straggler}\"... "
			find ${straggler} -type d -depth -exec rmdir {} +
			if [ ${?} -ne 0 ]
			then
				echo ${FAILUREMSG}
				echo "Error executing \"find ${straggler}" \
					"-type d -depth -exec rmdir {} +\"."
				exit 1
			fi
			echo ${SUCCESSMSG}
		fi
	done

	# Create the MAKEOBJDIRPREFIX directory.  It may have been removed by
	# the cleaning above.
	mkdir -p ${MAKEOBJDIRPREFIX}
	reset_target_obj
fi

# CVSup.
if [ "${CVSUP}" = "YES" ]
then
	# Save off UPDATING to be diff'd later.
	cp ${SRCDIR}/UPDATING ${LOGDIR}/UPDATING.old
	if [ ${?} -ne 0 ]
	then
		echo "Unable to copy ${SRCDIR}/UPDATING to" \
			"${LOGDIR}/UPDATING.old."
		exit 1
	fi

	# Update system source (no docs nor ports).
	case ${AUTO} in
	[Nn][Oo])
		echo -n "Updating system source... "
		;;
	esac
	cd ${SRCDIR}
	nice -n ${NICE} make -DNO_DOCUPDATE -DNO_PORTSUPDATE update \
		${SUPFILE} > ${CVSUPLOG} 2>&1
	if [ ${?} -ne 0 ]
	then
		echo ${FAILUREMSG}
		echo "Update failed.  Review ${CVSUPLOG}."
		exit 1
	fi
	echo ${SUCCESSMSG}

	# Only CVSup was requested.  Exit here.
	if [ "${BUILD}" = "" ]
	then
		echo -e "CVSup successful.\nLogs are in ${LOGDIR}."
		exit 0
	fi

	# Review UPDATING differences.
	case ${AUTO} in
	[Nn][Oo])
		# Show differences only if they exist.
		cmp ${LOGDIR}/UPDATING.old ${SRCDIR}/UPDATING
		if [ ${?} -ne 0 ]
		then
			diff -u ${LOGDIR}/UPDATING.old ${SRCDIR}/UPDATING | \
			grep "^[\+|F][^\+$]" | sed "s/^\+//" | \
			less -c -M -PM'UPDATING diff  -  lines %lt-%lb?L/%L'
		fi
		;;
	esac

	# Check to see if the user wishes to continue after the CVSup.
	case ${AUTO} in
	[Nn][Oo])
		printf "\nDo you wish to continue? (y/N) "
		read CONTINUE
		case ${CONTINUE} in
		[yY])
			;;
		*)
			echo "Exiting by request."
			exit 0
			;;
		esac
	esac
fi

# Build world.
if [ "${BUILD}" = "a" -o "${BUILD}" = "w" ]
then
	# Pre-buildworld checking.
	case ${AUTO} in
	[Nn][Oo])
		${MERGEMASTER} -p
		if [ ${?} -ne 0 ]
		then
			echo "Mergemaster failed."
			exit 1
		fi
		clear
		;;
	esac

	# Build world.
	echo -n "Building world... "
	nice -n ${NICE} make ${JOBS} buildworld ${BUILDTARGET} > \
		${BUILDWORLDLOG} 2>&1
	if [ ${?} -ne 0 ]
	then
		echo ${FAILUREMSG}
		echo "Build world failed.  Review ${BUILDWORLDLOG}."
		exit 1
	fi
	echo ${SUCCESSMSG}
else
	# Clear screen before next action.
	clear
fi

# Build kernel(s).
if [ "${BUILD}" = "a" -o "${BUILD}" = "k" ]
then
	cd ${SRCDIR}
	for k in ${DEFAULTKERNEL} ${EXTRAKERNELS}
	do
		# Build non-default kernels as kernel.NAME.
		if [ "${k}" != "${DEFAULTKERNEL}" ]
		then
			INSTKERNNAME="INSTKERNNAME=kernel.${k}"
		else
			INSTKERNNAME=""
		fi

		# Build kernel.
		echo -n "Building kernel:  ${k}... "
		BUILDKERNELLOG=${LOGDIR}/bk.${k}.log
		nice -n ${NICE} make ${JOBS} -DALWAYS_CHECK_MAKE buildkernel \
			KERNCONF=${k} ${INSTKERNNAME} \
			${BUILDTARGET} > ${BUILDKERNELLOG} 2>&1
		if [ ${?} -ne 0 ]
		then
			echo ${FAILUREMSG}
			echo "Build kernel (${k}) failed. " \
				"Review ${BUILDKERNELLOG}."
			exit 1
		fi
		echo ${SUCCESSMSG}
	done
fi

# Create destination directory.
if [ -n "${DESTDIR}" -a ! -d ${DESTDIR} ]
then
	echo "Creating destination directory (${DESTDIR})."
	mkdir -p ${DESTDIR}
	if [ ${?} -ne 0 ]
	then
		echo "Could not create \"${DESTDIR}\"."
		exit 1
	fi
fi

# Install kernel or world first depending on existance of /etc/rc (previous
# install).  If this is a brand new install, install the distribution.
#
# While the first kernel will install, subsequent kernel installs will fail due
# to a lack of /boot/device.hints when /boot exists.
if [ -f ${DESTDIR}/etc/rc ]
then
	install_kernels
	install_world
else
	install_world
	install_dist
	install_kernels
fi

# Post-install configuration.
if [ \( "${INSTALL}" = "w" -o "${INSTALL}" = "a" -o "${JAILDIRS}" != "" \) -a \
	-z "${NEWDIST}" ]
then
	# Handle install configuration along with jails.
	for INSTALLDIR in ${INSTALL:+""} `echo ${JAILDIRS}`
	do
		INSTALLDIR=`realpath ${DESTDIR}/${INSTALLDIR}`
		ETCDIR=`realpath ${INSTALLDIR}/etc`

		# Backup /etc into /etc.TIMESTAMP.  jails copied here too.
		echo -n "Backing up ${ETCDIR} to ${ETCDIR}.${DATE}... "
		cp -Rp ${ETCDIR} ${ETCDIR}.${DATE}
		if [ ${?} -ne 0 ]
		then
			echo ${FAILUREMSG}
			echo "Backup failed."
			exit 1
		fi
		echo ${SUCCESSMSG}

		# Merging.
		case ${AUTO} in
		[Yy][Ee][Ss])
			echo -n "Merging old configuration with new... "
			MERGEAUTO="-a"
			MERGELOGGING="> ${MERGELOG} 2>&1"
			;;
		*)
			MERGEAUTO="-U"
			MERGECOMPARE="-C"
			;;
		esac

		# Merging /etc or a separate partition or jail's /etc.
		if [ "${INSTALLDIR}" = "/" ]
		then
			MERGEDIR=""
		else
			MERGEDIR="-D ${INSTALLDIR}"
		fi

		${MERGEMASTER} -i -F ${MERGEAUTO} ${MERGECOMPARE} ${MERGEDIR} \
			${MERGELOGGING}
		if [ ${?} -ne 0 ]
		then
			echo "Mergemaster failed merging ${ETCDIR}."
			exit 1
		fi

		case ${AUTO} in
		[Yy][Ee][Ss])
			echo ${SUCCESSMSG}
			;;
		esac
	done
fi

# Delete obsolete base files, directories and libraries.
if [ \( "${INSTALL}" = "w" -o "${INSTALL}" = "a" \) -a \
	-f "${SRCDIR}/ObsoleteFiles.inc" -a -z "${NEWDIST}" ]
then
	# In automatic-mode, delete without confirmation.
	case ${AUTO} in
	[Yy][Ee][Ss])
		readonly DELOPT="-DBATCH_DELETE_OLD_FILES"
		;;
	*)
		readonly DELOPT=""
		;;
	esac
	cd ${SRCDIR}
	nice -n ${NICE} make ${DELOPT} delete-old delete-old-libs ${DESTDIREQ} \
		${BUILDTARGET}
	if [ ${?} -ne 0 ]
	then
		echo "Obsolete file removal failed."
		exit 1
	fi
fi

# Build sysinstall on FreeBSD 4.
if [ "${FREEBSDVER}" = "4" -a \( "${BUILD}" = "w" -o "${BUILD}" = "a" \) ]
then
	# First, install keymaps to avoid compilation failure of sysinstall.
	cd ${SRCDIR}/share/syscons/keymaps
	echo -n "Installing keymaps... "
	nice -n ${NICE} make install ${DESTDIREQ} ${BUILDTARGET} > \
		${BUILDSYSINSTALLLOG} 2>&1
	if [ ${?} -ne 0 ]
	then
		echo ${FAILUREMSG}
		echo "Keymaps install failed.  Review ${BUILDSYSINSTALLLOG}."
		exit 1
	fi
	echo ${SUCCESSMSG}

	# Build.
	set_target_obj
	cd ${SRCDIR}/release/sysinstall
	echo -n "Building sysinstall... "
	nice -n ${NICE} make clean all ${BUILDTARGET} >> \
		${BUILDSYSINSTALLLOG} 2>&1
	if [ ${?} -ne 0 ]
	then
		echo ${FAILUREMSG}
		echo "Build sysinstall failed.  Review ${BUILDSYSINSTALLLOG}."
		exit 1
	fi
	echo ${SUCCESSMSG}
	reset_target_obj
fi

# Install sysinstall on FreeBSD 4.
if [ "${FREEBSDVER}" = "4" -a \( "${INSTALL}" = "w" -o "${INSTALL}" = "a" \) ]
then
	cd ${SRCDIR}/release/sysinstall
	echo -n "Installing sysinstall... "
	nice -n ${NICE} make install ${DESTDIREQ} ${BUILDTARGET} > \
		${INSTALLSYSINSTALLLOG}
	if [ ${?} -ne 0 ]
	then
		echo ${FAILUREMSG}
		echo "Install sysinstall failed. " \
			"Review ${INSTALLSYSINSTALLLOG}."
		exit 1
	fi
	echo ${SUCCESSMSG}
fi

# Finished.
if [ "${BUILD}" != "" -a "${INSTALL}" != "" ]
then
	FINISHED="Build and install were"
elif [ "${BUILD}" != "" ]
then
	FINISHED="Build was"
elif [ "${INSTALL}" != "" ]
then
	FINISHED="Install was"
elif [ "${JAILDIRS}" != "" ]
then
	FINISHED="Jail configuration(s) were"
fi
echo -e "${FINISHED} successful.\nLogs are in ${LOGDIR}."

