#!/bin/sh
# -*- shell-script -*-
# $Id: BUILD-NetBSD,v 1.26 2012/04/18 16:35:00 gdt Exp $

# Copyright (c) 2004 Gregory D. Troxel.  All rights reserved.
# Copyright (c) 2004--2009 BBN Technologies Corp.  All rights reserved.
# Copyright (c) 2010 Raytheon BBN Technologies.  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.
# 3. Neither the name of BBN Technologies nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY RAYTHEON BBN TECHNOLOGIES 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 BBN TECHNOLOGIES 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.

# This software was developed under funding from DARPA's ACERT program
# under contract number NBCH050166.

## This script supports automatic building and installing of NetBSD,
## and functions from netbsd-2 onward.  It is mostly a wrapper around
## build.sh.

## The script is documented by included comments, as it is assumed
## that users of the script will have some understanding of how it
## works.

## The script assumes that src and xsrc are present in the current
## directory, checked out with matching (possibly null) tags.

## It is intended that builds be done unprivileged, and that installs
## are done as root.

## Build a release with x for the current architecture
##   BUILD-NetBSD -x all
## Build a release without x
##   BUILD-NetBSD all
## Build a release with x for sparc64
##   BUILD-NetBSD -x sparc64
## Install an already built release
##   BUILD-NetBSD -x install
## Install just kernels from an already built release
##   BUILD-NetBSD -x installkernel

echo -n "BUILDALL OVERALL start "; date

## The following files store information about the kernel config that
## should be installed on the current machine, and the branch of
## NetBSD that the machine is currently running.
kernel_file=/.BUILD-NetBSD.kernel
branch_file=/.BUILD-NetBSD.branch

SRCFILE=src/sys/arch/i386/conf/GENERIC
X11SRCFILE=xfree/xc/RELNOTES.txt

# Check a source file to verify that we (probably) have a source tree.
if [ ! -f $SRCFILE ]; then
    echo "$SRCFILE not present"
    exit 1
fi

## Find branch for use in OBJDIR etc.

# Check for a user-declared branch.
if [ -f .branch ]; then
    branch=`cat .branch`
fi

# Infer branch from CVS, if present.
if [ -z "$branch" -a -d src/CVS ]; then

    # CVS branches have an explicit tag, and the trunk is called "current".
    if [ -f src/CVS/Tag ]; then
	cvstag=`cat src/CVS/Tag`
    else
	cvstag='Tcurrent'
    fi

# Set token for directory name based on branch.  First we sort by
# major release, and then newer branches come earlier.
    case "$cvstag" in
	Tcurrent)
	    branch=current
	    ;;
	Tnetbsd-6-0)
    	    branch=6-0
	    ;;
	Tnetbsd-6)
    	    branch=6
	    ;;
	Tnetbsd-5-1)
    	    branch=5-1
	    ;;
	Tnetbsd-5-0)
    	    branch=5-0
	    ;;
	Tnetbsd-5)
    	    branch=5
	    ;;
	Tnetbsd-4-0)
    	    branch=4-0
	    ;;
	Tnetbsd-4)
    	    branch=4
	    ;;
	Tnetbsd-3-1)
    	    branch=3-1
	    ;;
	Tnetbsd-3-0)
    	    branch=3-0
	    ;;
	Tnetbsd-3)
    	    branch=3
	    ;;
	*)
    	    echo "UNKNOWN/UNSUPPORTED BRANCH $progname"
	    exit 1
	    ;;
    esac
fi

if [ -z "$branch" ]; then
    echo "No .branch defined and cannot infer branch from VCS."
    echo "(.branch is used for naming, and also in case building is different)"
    exit 1;
fi

echo "Building with branch token $branch"

## Set up build directories.
# The general plan is to have a separate obj/dest/release dir for each
# source tree.  "Each source tree" distinguishes branches of NetBSD
# and also which user is doing the build.  This requires thought, so
# we simply force that thought and fail if it is not specified, rather
# than attempting to infer what is desired and getting it wrong.

# Allowable contents of .tree are
#   single word, e.g. "auto" to denote system-level build of $branch
#   two words:
#	path absolute	to denote that the objroot shoud be path
#	path relative	to denote that the objroot shoud be ./path
# For a single word foo, we'll place the container for objdir, tooldir,
# and so on at /usr/obj/foo-$branch.  For "local bar", we'll place it as ./bar.
TREEFILE=.tree
if [ -r $TREEFILE ]; then
    read tree1 tree2 < $TREEFILE
else
    echo "The file .tree does not exist.  BUILD-NetBSD requires that you"
    echo "specify the location of the OBJDIR container directory."
    echo "Read the comments in the script for instructions."
    exit 1
fi

if [ "$tree1" = "" ]; then
    echo "fatal: .tree file is empty"
    exit 1
fi

# Choose base location.  It is intentional not to allow separating
# tools, obj, destdir, and releasedir.
case "$tree2" in
    "")
	# Be compatible with historical behavior, which if .tree
	# contained "auto" and branch was 5 would build into
	# /usr/obj/auto-5.  This lets one put their username in .tree
	# and keep 4/5/current build trees separate.
	BUILDBASE=/usr/obj/$tree1-$branch
	;;
    "absolute")
	# $tree1 should begin with /.  Arguably not having a leading / should
	# just be an error.
        case "$tree1" in
            /*) BUILDBASE=${tree1};;
            *) BUILDBASE=/${tree1};;
        esac
	;;
    "relative")
	BUILDBASE=`/bin/pwd`/$tree1
	;;
    *)
	echo "fatal: Invalid second word <$tree2> in .tree"
	exit
esac
    

# Use xsrc next to src, to enable cross builds.
X11SRCDIR=`/bin/pwd`/xsrc

ETCSETS=etc

# Build X if -x flag is given.
if [ "$1" = "-x" ]; then
    xflag="-x"
    ETCSETS="$ETCSETS xetc"
    shift
    # Check that X11 sources are present.
    if [ ! -f $X11SRCDIR/$X11SRCFILE ]; then
	echo "$X11SRCFILE not present."
	exit 1
    fi
else
    xflag=""
fi

# Parse fixed arguments.
cmd=$1
arch=$2

# Make first-cut decision about UNPRIV (UNPRIV if non-root)
if [ 0 != `id -u` ]; then
  echo "Not ROOT: doing UNPRIVed build"
  pflag="-U"
else
  pflag=""
fi

# Function to check for root at install time, and set -U if build
# was done UNPRIVed.
installrootcheck () {
    if [ 0 != `id -u` ]; then
	echo "MUST BE ROOT TO INSTALL"
	exit 1
    fi
    if [ -f $DESTDIR/METALOG ]; then
	echo "INSTALL with METALOG: doing UNPRIVed install"
	pflag="-U"
    else
	echo "INSTALL without METALOG: not setting UNPRIVed"
	pflag=""
    fi
}

# Set 'update' flag to avoid make clean. This is possibly dangerous.
# Remove uflag, or nuke /usr/obj if problematic.
uflag="-u"

# Set parallel flag to have 2 jobs running per CPU.  XXX other OS
jflag="-j2"
if [ `uname` = NetBSD ]; then
    jflag=-j`sysctl hw.ncpu|awk '{print 2*$3}'`
fi

## Compute and set build.sh environment variables.

# determine arch (do a self-hosted build if not given)
if [ "$arch" = "" ]; then
  arch=`uname -m`
fi

# Put tools within base (not dependent on $arch).  Put tools bin in PATH
# so we can run nbmake-$arch later.
export TOOLDIR=$BUILDBASE/tools; export TOOLDIR
TOOLSBIN=$TOOLDIR/bin

# Set OBJDIR to be unique for this architecture.
export OBJDIR=$BUILDBASE/$arch

# Place all destdirs together, separated by architecture.
export DESTDIR=$BUILDBASE/destdir/$arch

# Releasedir has $arch subdirs by default, so omit implied "/$arch".
export RELEASEDIR=$BUILDBASE/releasedir

if [ "$cmd" = "" ]; then
    cmd=all
    echo "Command omitted - assuming \"all\"" 
fi

case "$cmd" in
    # these targets invoke the same-named build.sh target
    distribution|release)
	target=${cmd}
	;;
    # Build an installable ISO image.
    iso)
	target=none
	echo "ISO: ASSUMING RELEASE ALREADY COMPLETE!"
	;;
    # first do release, then do iso
    all)
	target=release
	echo "ALL: doing full build to iso"
	;;
    # Install from previous distribution.
    install)
	target="install=/"
	installrootcheck
	echo "INSTALL: ASSUMING DISTRIBUTION ALREADY COMPLETE!"
	;;
    # Install from previous distribution.
    installkernel)
	target=none
	installrootcheck
	echo "INSTALL: ASSUMING DISTRIBUTION ALREADY COMPLETE!"
	;;
    *)
	cat <<EOF >&2
Usage: $0 [-x] [all|distribution|release|iso|install|installkernel]
EOF
	exit 1
	;;
esac

## Look for control file to specify a custom kernel.
if [ -f $kernel_file ]; then
    read kern < $kernel_file
fi

# Put tools in path and obtain path to our variant of make.
PATH=$TOOLSBIN:$PATH
MAKE=$TOOLSBIN/nbmake-$arch

## For install, install a kernel before invoking build.sh.
case $cmd in
    ## Install kernel first, so that if userland uses new system calls
    ## and the machine wedges, rebooting will fix it.
    ## XXX Split install to a separate script to enable binary-only updates.
    ## XXX Check the branch, and do just kernel for cross-branch upgrades,
    ## and then on next iteration after reboot do kernel and userland.
    install|installkernel)
    (cd $OBJDIR/sys/arch/$arch/compile/$kern && $MAKE DESTDIR=/ install)
    # Install gzipped kernels for GRUB/XEN if they are present.
    for k in `(cd / &&
        ls netbsd-*.gz |
	egrep 'netbsd-[A-Z0-9_]*.gz' |
	sed 's/netbsd-\([A-Z0-9_]*\).gz/\1/')`; do
      echo "COPYING gzipped KERNEL $k"
      cp -pf /netbsd-$k.gz /netbsd-$k.ok.gz
      cp -pf $RELEASEDIR/$arch/binary/kernel/netbsd-$k.gz /
    done
    ;;
esac

## Run build.sh as main target - distribution or release.
if [ "$target" != "none" ]; then
    echo -n "BUILDALL MAIN $target start "; date
    # Modules change names every time the system version changes, and
    # this results in failed builds due to extra files in DESTDIR.
    rm -rf $DESTDIR/stand
    (cd src && ./build.sh -m $arch $jflag $xflag $uflag $pflag \
	-O $OBJDIR -T $TOOLDIR -D $DESTDIR -R $RELEASEDIR \
	-X $X11SRCDIR \
	$target) || exit 1
    echo -n "BUILDALL MAIN $target finish "; date
fi

if [ x"$kern" != x ]; then
    if [ ! -f src/sys/arch/$arch/conf/$kern ]; then
	echo "CANNOT FIND KERNEL CONFIG src/sys/arch/$arch/conf/$kern!"
	exit 1
    fi
    ## For release (and thus all), our chosen kernel may not be part
    ## of the normal build, so specifically request that it be built.
    case $cmd in
	release|all)
	    echo -n "BUILDALL KERNEL start $kern for $arch "; date
	    (cd src && ./build.sh -m $arch $jflag $xflag $uflag $pflag \
		-O $OBJDIR -T $TOOLDIR -D $DESTDIR -R $RELEASEDIR \
		-X $X11SRCDIR \
		kernel=$kern) || exit 1
	    echo -n "BUILDALL KERNEL finish $kern for $arch "; date
	    ;;
    esac
fi

## For release (and thus all), generate an etcmanage manifest.
case $cmd in
    release|all)
    etcmanage --generate $DESTDIR > $RELEASEDIR/$arch/binary/sets/ETCMANAGE-NetBSD-$branch
    ;;
esac

## If installing, run etcmanage.
## XXX Work around etcmanage bugs by running it 10 times.
case $cmd in
    install)
    NETBSDETC=/usr/netbsd-etc
    rm -rf ${NETBSDETC}
    mkdir -p ${NETBSDETC}
    for set in $ETCSETS; do
	cat $RELEASEDIR/$arch/binary/sets/${set}.tgz | (cd ${NETBSDETC} && pax -rz -pe)
    done

    for step in 0 1 2 3 4 5 6 7 8 9; do
	echo BUILDALL: etcmanage: $step
	[ -f ${NETBSDETC}/etc/defaults/rc.conf ] && \
	    etcmanage --update ${NETBSDETC}
    done
    # XXX need etc contents in other sets
    cp -p ${DESTDIR}/etc/release /etc
    cd /dev && ./MAKEDEV all
    ;;
esac

# For iso and all, build the iso image.
case $cmd in
    iso|all)
	echo -n "BUILDALL ISO start for $arch "; date
	case "$branch" in
	    2*|3*)
		(cd src/etc && $MAKE iso-image)
		;;
	    4*|5*|6*|current)
		(cd src && ./build.sh -m $arch $jflag $xflag $uflag $pflag \
		    -O $OBJDIR -T $TOOLDIR -D $DESTDIR -R $RELEASEDIR \
		    -X $X11SRCDIR \
		    iso-image) || exit 1
		;;
	    *)
		echo "BUILD-NetBSD: Don't know how to build iso for branch: $branch."
		exit 1
		;;
	    esac
	echo -n "BUILDALL ISO finish for $arch "; date
    ;;
esac

echo -n "BUILDALL OVERALL finish "; date
