#!/bin/sh
# $NetBSD: build,v 1.91 2005/12/05 10:19:14 rillig Exp $

#
# Copyright (c) 1999, 2000 Hubert Feyrer <hubertf@NetBSD.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.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by Hubert Feyrer for
#	the NetBSD Project.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
#

#
# Do bulk build
#

usage() {
	cat <<EOF
usage: $0 [options]
       $0 -h | --help

Runs a bulk pkgsrc build.

The following options are supported:

   -c | --config <file>
	Load the following configuration file instead of the default
	one.

   -e | --no-email
	Don't send email when the bulk build is finished, it will put
	the results into a file (FTP/pkgsrc-results.txt).

   -h | --help
	Displays this message.

   -m | --mirror_only
	Downloads all distfiles needed for the build but does not run
	the build.  IMPORTANT:  Note that this will still run all the
	pre-build stuff which involves removing all of your installed
	packages.

	The only difference between this option and a regular bulk build
	is that the packages are not actually built.

   -r | --restart | --resume | restart
	Restart a previously interrupted bulk build.  The last form of
	this option is for backwards compatibility and may be removed in
	future versions of this script.

	The --restart option may be combined with the --mirror_only
	option.

   -s | --specific-pkgs
	Sets SPECIFIC_PKGS=1 when building packages.  This option is
	used for building a subset of pkgsrc.

EOF
}

die() {
	echo "$0: error:" 1>&2
	for i in "$@"; do
		echo "    $i" 1>&2
	done
	exit 1
}

# This function can be overridden in the build.conf file to change the
# output format of the bulk build. It is used in a pipe, so if you want
# the original output, just define post_filter_cmd() { cat; }.
#
# For more sophisticated output, you may use all the variables that this
# example function uses.
post_filter_cmd() {
	${SED} "s;^;`date '+%Y/%m/%d %H:%M:%S'`  ${built}/${tot}=${percent} ${pkgdir} @ ${MACHINE_ARCH}> ;g"
}


#
# Find out where we are
#
scriptdir=`dirname "$0"`
scriptdir=`cd "${scriptdir}" && pwd`

#
# Default values for command line options.
#
restart=no
mirror_only=no
target=bulk-package
makeargs=""
noemail=no

#
# Parse the command line.
#
while test $# -gt 0; do
	case $1 in
	-c|--config)
		shift
		BULK_BUILD_CONF=$1; shift
		;;
	-e|--no-email)
		noemail=yes
		shift
		;;
	-h|--help)
		usage
		exit 0
		;;
	-m|--mirror_only)
		mirror_only=yes
		target=mirror-distfiles
		shift
		;;
	-r|--restart|--resume|restart)
		restart=yes
		shift
		;;
	-s|--specific-pkgs)
		makeargs="$makeargs SPECIFIC_PKGS=1"
		shift
		;;
	*)
		echo "unknown option:  $1" 1>&2
		usage 1>&2
		exit 1
		;;
	esac
done

#
# Choose an appropriate value for BMAKE depending on the operating system.
#
opsys=`uname -s`
case "$opsys" in
NetBSD)	BMAKE=make ;;
*)	BMAKE=bmake ;;
esac
export BMAKE

#
# Set resource limits as high as possible
#
ulimit -S -s `ulimit -H -s`
ulimit -S -d `ulimit -H -d`

#
# Find the configuration file.
#
BULK_BUILD_CONF="${BULK_BUILD_CONF-${scriptdir}/build.conf}"
case $BULK_BUILD_CONF in
/*) ;;
*)	BULK_BUILD_CONF="${PWD}/${BULK_BUILD_CONF}"
esac

#
# Load the variables from the configuration file.
#
{ test -f "${BULK_BUILD_CONF}" \
  && . "${BULK_BUILD_CONF}" \
  && . "${scriptdir}/post-build-conf" \
  && check_config_vars \
  && export_config_vars
} || die "Cannot load config file ${BULK_BUILD_CONF}, aborting."

#
# Set the paths to commonly used directories.
#
pkgsrc_dir="${USR_PKGSRC}"
pkglint_dir="${USR_PKGSRC}/pkgtools/pkglint"

#
# Set up variables specific for the bulk build.
#
BATCH="1"
DEPENDS_TARGET="bulk-install"
export BATCH DEPENDS_TARGET

#
# Unset some environment variables that could disturbe the build.
#
unset CDPATH || true	# ensure cd does not print new cwd to stdout, which
			# confuses the printindex script.
unset DISPLAY || true	# allow sane failure for gimp, xlispstat

#
# It starts ...
#
echo "build> Bulk build started: `date`"
echo ""
show_config_vars

#
# Check that the package tools are up to date.
#
echo "build> Checking if the pkgtools are up-to-date"
( cd "${pkglint_dir}" \
  && ${BMAKE} fetch >/dev/null 2>&1
) || {
	echo "build> Updating pkgtools"
	( cd "${pkgsrc_dir}/pkgtools/pkg_install" \
	  && ${BMAKE} clean \
	  && ${BMAKE} install \
	  && ${BMAKE} clean
	) || die "Could not update the package tools."
}

#
# Run the pre-build script if necessary.
#
case $restart in
yes)	echo "build> Resuming -- skipping pre-build script";;
*)	# make veryveryclean :)
	( cd "${pkgsrc_dir}" \
	  && /bin/sh mk/bulk/pre-build
	) || die "Error during bulk-build preparations, aborting.";;
esac

#
# Load pkgsrc variables that affect the build process.
#

# usage: load_pkgsrc_var <varname> <bmake-args...>
load_pkgsrc_var() {
	case $1 in
	-z)	lpv_check_nonempty=no; shift;;
	*)	lpv_check_nonempty=yes;;
	esac

	lpv_varname="$1"; shift

	if lpv_value=`cd "${pkglint_dir}" && ${BMAKE} show-var VARNAME="${lpv_varname}" "$@"`; then
		case "$lpv_check_nonempty,$lpv_value" in
		yes,"")
			die "${lpv_varname} must not be empty.";;
		esac
	else
		die	"Could not get the value for the ${lpv_varname} pkgsrc variable."
	fi
	eval "${lpv_varname}=\${lpv_value}"
	printf "%-15s = %s\\n" "${lpv_varname}" "${lpv_value}"
}

echo "+----------------------------------------+"
echo "| Some variables used in the bulk build: |"
echo "+----------------------------------------+"
load_pkgsrc_var OPSYS
load_pkgsrc_var OS_VERSION
load_pkgsrc_var MACHINE_ARCH
load_pkgsrc_var -z BULK_PREREQ

load_pkgsrc_var BULKFILESDIR
load_pkgsrc_var BULK_DBFILE
load_pkgsrc_var DEPENDSFILE
load_pkgsrc_var DEPENDSTREEFILE
load_pkgsrc_var INDEXFILE
load_pkgsrc_var ORDERFILE
load_pkgsrc_var STARTFILE
load_pkgsrc_var SUPPORTSFILE

load_pkgsrc_var BUILDLOG
load_pkgsrc_var BROKENFILE
load_pkgsrc_var BROKENWRKLOG

load_pkgsrc_var AWK		USE_TOOLS=awk
load_pkgsrc_var GREP		USE_TOOLS=grep
load_pkgsrc_var MAIL_CMD	USE_TOOLS=mail
load_pkgsrc_var MKDIR		USE_TOOLS=mkdir
load_pkgsrc_var PERL5		USE_TOOLS=perl
load_pkgsrc_var PKG_DELETE
load_pkgsrc_var PKG_INFO
load_pkgsrc_var SED		USE_TOOLS=sed
echo "------------------------------------------"

#
# Get the location of commonly used files
#
main_buildlog="${BULKFILESDIR}/${BUILDLOG}"

#
# Create the directory for the log files if necessary
#
if [ "${BULKFILESDIR}" != "${pkgsrc_dir}" ]; then
	${MKDIR} "${BULKFILESDIR}"
fi

#
# Install prerequisite packages.
#
# Note: we do this _before_ the depends tree because some packages like
# xpkgwedge only become DEPENDS if they are installed.
#
echo "build> Installing prerequisite packages specified with BULK_PREREQ..."
for pkgdir in $BULK_PREREQ; do
	echo "build> Installing prerequisite package $pkgdir"
	( cd "${pkgsrc_dir}/${pkgdir}" \
	  && ${BMAKE} bulk-install
	) || die "Could not install prerequisite packages."
done

#
# Create the bulk cache files.
#
if [ "x$restart" != "xyes" ]; then
	( cd "${pkgsrc_dir}" \
	  && env PKGLIST="${PKGLIST-}" ${BMAKE} bulk-cache $makeargs
	) || die "Could not create the bulk build cache."
else
	if [ ! -f "${ORDERFILE}" ]; then
		die	"The ${ORDERFILE} does not exist." \
			"(You cannot resume a bulk build that has not yet started.)"
	fi
fi

#
# Everything is prepared. We can start building the real packages now.
#
cd "${pkgsrc_dir}" || die "The pkgsrc directory does not exist."

echo "build> Starting actual build using the order specified in $ORDERFILE..."

# Loop over every package in the correct order.  Before building
# each one, check to see if we've already processed this package
# before.  This could happen if the build got interrupted and we
# started it again with the 'restart' option.  This prevents us
# from having to do a potentially very large number of make's to
# get back to where we let off.  After we build each package, add
# it to the top level buildlog
# (usually '.make' or '.make.${MACHINE}').  As a side benefit, this
# can make a progress-meter very simple to add!

# make sure we have something to grep in in the build loop
touch "${main_buildlog}" || die "Cannot write to ${main_buildlog}."

tot=`wc -l "${ORDERFILE}" | ${AWK} '{print $1}'`
for pkgdir in `cat "${ORDERFILE}"`
do
	if ${GREP} -q "^${pkgdir}\$" "${main_buildlog}"; then
		: "skip this package"
	else
		built=`wc -l "${main_buildlog}" | ${AWK} '{print $1}'`
		percent=`echo $built $tot | ${AWK} '{printf("%4.1f%%",$1*100/$2);}'`
		( cd "${pkgsrc_dir}/${pkgdir}" \
		  && ${NICE_LEVEL} ${BMAKE} USE_BULK_CACHE=yes "${target}" \
			$makeargs </dev/null | post_filter_cmd
		) || true
		echo "$pkgdir" >> "${main_buildlog}"
	fi
done

echo "build> Build finished.  Removing all installed packages left over from build..."
for pkgname in `${PKG_INFO} -e \*`
do
	if ${PKG_INFO} -qe "${pkgname}"; then
		pkgdir=`${AWK} '$2 == "'"$pkgname"'" { print $1; }' "$INDEXFILE"`
		case "${BULK_PREREQ}" in
			*"${pkgdir}"* )
				echo "build> Keeping BULK_PREREQ: $pkgname ($pkgdir)" ;
				;;
			* )
				echo "build> ${PKG_DELETE} -r ${pkgname}"
				${PKG_DELETE} -r "${pkgname}"
				if ${PKG_INFO} -qe "${pkgname}"; then
					echo "build> $pkgname ($pkgdir) did not deinstall nicely.  Forcing the deinstall"
					${PKG_DELETE} -f "${pkgname}" || true
				fi
				;;
		esac
	fi
done

# for now, just quit if we were only mirroring distfiles.  At somepoint we
# should teach the post-build script to generate a nice report about how many
# distfiles were downloaded, how many had bad checksums, failed master sites,
# network speed, etc.

if [ "x$mirror_only" = "xyes" ]; then
	echo "build> Bulk mirror of distfiles completed: `date`"
	exit 0
fi

echo "build> Post processing bulk build results..."

# Re-install BULK_PREREQ as we may need functionality (e.g. SMTP) provided by
# them for post-build to run.
echo "build> Re-installing prerequisite packages specified with BULK_PREREQ..."
for pkgdir in $BULK_PREREQ lang/perl5; do
	echo "build> Installing prerequisite package $pkgdir"
	( cd "${pkgsrc_dir}/${pkgdir}" \
	  && ${BMAKE} bulk-install
	) || die "Failed to install prerequisite packages."
done

#
# Generate the post-build report.
#
echo "build> Generating the bulk build report..."
BUILDDATE=`date +%Y-%m-%d`
mkdir -p "${FTP}"
( cd "${pkgsrc_dir}" \
  && ${PERL5} mk/bulk/post-build \
     > ${FTP}/pkgsrc-results-${BUILDDATE}.txt
) || die "Could not write the results file."

#
# Notify the ADMIN of the finished build.
#
case $noemail in
no)	cat "${FTP}/pkgsrc-results-${BUILDDATE}.txt" \
	| ${MAIL_CMD} -s "pkgsrc ${OPSYS} ${OS_VERSION}/${MACHINE_ARCH} bulk build results $BUILDDATE" "$ADMIN"
esac

# Done!
echo ""
echo "build> Bulk build ended: `date`"
