#!/bin/bash
set -e
#  empdebuild : emdebian version of pdebuild.
#
#  Emdebian chroot builder - initially supporting the creation of a
#  chroot capable of running emdebian-tools to reduce the number of
#  cross dependencies that need to be installed on the build system.
#
#  Copyright (C) 2006, 2007  Neil Williams <codehelp@debian.org>
#
#  This package is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Note 1: Most configuration values are calculated, not taken from the command line
# or an rc file.
# Note 2: emdebuild creates a build log for us - just the output of dpkg-buildpackage,
# not the rest of the chroot operations.

. /usr/lib/emdebian-tools/empbuilderlib

SUITE=unstable

function usagehelp () {
    # print out help message
    cat <<EOF
empdebuild - cross-building chroot Emdebian package builder
version $OURVERSION

Syntax: sudo empdebuild [OPTIONS] [COMMAND]
Only one command can be actioned at any one time.
Options must precede the command to take effect.

Commands:
-?|-h|--help|-version:   print this help message and exit
--create|create:         create a cross-building base .tgz for unstable
--update|update:         update the base .tgz and save changes
--build|build:           build the current package in the chroot
                         (needs to be run in the top source directory)
--login|login:           Login to the chroot to run tests or fix problems.

Options:
--arch:               Override the default cross-build architecture (from dpkg-cross).
--testing:            Override the default suite (unstable) to use testing
                      instead. This is a fallback option for use during
                      toolchain transitions in unstable.
--save-after-login:   Allow changes made within the chroot login to persist.

Although based on pbuilder, empdebuild cannot support the full range of
pbuilder commands or options. In addition, creating a cross-building chroot for
testing involves a variety of changes to the creation of a similar chroot for
unstable so testing is only supported as an override, rather than using the
pbuilder method of a --distribution option.

EOF
}

function createemchroot()
{
	if [ $SUITE == testing ]; then
		BASETGZ="${WORKDIR}/emdebian-testing.tgz"
		SUITE=testing
		. /usr/lib/emdebian-tools/embootstrap --arch $ARCH testing
		echo "Creating an embootstrap testing chroot"
	else
		BASETGZ="$WORKDIR/emdebian.tgz"
		. /usr/lib/emdebian-tools/embootstrap --arch $ARCH
	fi
}

function checkembuilddep () {
	# call satisfydepends
	local BUILDOPT="--binary-all"
	case "${BINARY_ARCH}" in
		yes) BUILDOPT="--binary-arch";;
		*) ;;
	esac
	PBUILDERSATISFYDEPENDSCMD=/usr/lib/pbuilder/pbuilder-satisfydepends
	CONTROL=$BUILDPLACE/trunk/$SVN/debian/control
	# this version parses debian/control, NOT the .dsc
	# so that when Emdebian removes dependencies, the chroot does too.
	echo "Running: $PBUILDERSATISFYDEPENDSCMD --control $CONTROL --chroot $BUILDPLACE ${BUILDOPT}"
	if "$PBUILDERSATISFYDEPENDSCMD" --control "$CONTROL" --chroot "${BUILDPLACE}" "${BUILDOPT}" ; then
		echo " -> installed ${HOST_ARCH} dependencies."
	else
		echo "E: pbuilder-satisfydepends failed." >&2
	exit 2
	fi
	# install extra packages to the chroot
	if [ -n "$EXTRAPACKAGES" ]; then
		if echo "usr/bin/apt-get -y --force-yes install ${EXTRAPACKAGES}" | chroot $BUILDPLACE /bin/sh; then
		:
		else
		# if apt failed (maybe update needs to be run), save so far and exit cleanly
			save_aptcache
			umountproc
			cleanbuildplace
			exit 1;
		fi
	fi
}

# get $ARCH .deb and put into /var/cache/apt/archives to
# allow these to be saved between builds.
function save_aptcrosscache() {
    # save the current aptcache archive
    # it is safe to call this function several times.
    local doit
    if [ -n "$APTCACHE" ]; then
	echo "Copying back the cached apt-cross archive contents"
	mkdir -p "$APTCACHE" ;
	if [ "$APTCACHEHARDLINK" = "yes" ]; then
	    doit=ln
	else
	    doit=cp
	fi
	# apt-cross puts the archives in the chroot /
	find "$BUILDPLACE/" -maxdepth 1 -name \*.deb | \
		while read A ;do
		if [ ! -f "$APTCACHE/"$(basename "$A") -a -f "$A" ]; then
		echo " -> new cache content "$(basename "$A")" added"
		$doit "$A" "$APTCACHE/" || true
	    fi
	done
    fi
}

function get_cross_depends ()
{
# derived from checkbuilddep_internal
# Use this function to fulfill the cross-dependencies
	DEBIAN_XCONTROL=debian/xcontrol
	BD_REGEXP="build-cross-depends"

	local INSTALLPKG
	local INSTALLPKGLIST
	local INSTALLPKGMULTI
	local CURRENTREALPKGNAME
	if [ ! -f ${DEBIAN_XCONTROL} ]; then
		return
	fi
	echo "   -> updating apt-cross cache"
	chroot $BUILDPLACE /usr/bin/apt-cross -a $ARCH -u
	echo " -> Attempting to parse the cross-build dependencies"
	# read debian/xcontrol
	for INSTALLPKGMULTI in $(cat ${DEBIAN_XCONTROL} | \
	awk '
BEGIN{source=1}
/^$/      {source=0}
/^Source:/      {source=1}
/^[^ ]*:/ {p=0}
tolower($0) ~ /^'"${BD_REGEXP}"':/   {p=1}
{if(p && source) {print $0}}'  | \
	sed 's/^[^: ]*://' | \
	tr " " "/" | \
	awk 'BEGIN{RS=","} {print}'); do
		echo " -> Considering cross-build-dep$(echo "$INSTALLPKGMULTI" | tr "/" " " )"
		# parse the list - removing commas etc.
		for INSTALLPKG in $(echo "$INSTALLPKGMULTI" | \
			awk 'BEGIN{RS="|"} {print}'); do
			CURRENTREALPKGNAME=$(echo "$INSTALLPKG" | sed -e 's/^[/]*//' -e 's/[[/(].*//') #)# syntax hack
			INSTALLPKGLIST="${INSTALLPKGLIST} ${CURRENTREALPKGNAME}"
		done; # end parse list
	done; # end parse debian/xcontrol
	if [ -n "${INSTALLPKGLIST}" ]; then
		echo " -> Installing ${INSTALLPKGLIST}"
		chroot $BUILDPLACE /usr/bin/apt-cross -q -k -a $ARCH --install ${INSTALLPKGLIST}
		save_aptcrosscache
	fi
	echo " -> Finished parsing the cross build-deps"
}

function update_emchroot ()
{
	if [ $SUITE == testing ]; then
		BASETGZ="${WORKDIR}/emdebian-testing.tgz"
		SUITE=testing
		echo "Updating the embootstrap testing chroot"
	else
		BASETGZ="$WORKDIR/emdebian.tgz"
	fi
	. /usr/lib/pbuilder/pbuilder-modules
	extractembuildplace
	echo "File extracted to: $BUILDPLACE"
	echo ""
	mountproc
	echo " -> upgrading packages"
	chroot $BUILDPLACE /usr/bin/apt-get update
	recover_aptcache
	# force the sources list - a bit of a hack really.
	# TODO this should be configurable - emdebian-tools will add a primary later.
	cp /usr/share/emdebian-tools/emsources.$SUITE $BUILDPLACE/etc/apt/sources.list
	# TODO persistent problems with the toolchains mean this needs to be optional.
	OPTIONS="-o Apt::InstallRecommends=false -o Apt::Get::AutomaticRemove=false"
	chroot $BUILDPLACE /usr/bin/apt-get $OPTIONS -y --force-yes dist-upgrade
	chroot $BUILDPLACE /usr/bin/apt-get autoclean
	save_aptcache
	echo "   -> updating devscripts configuration"
	if [ -f /home/$SUDO_USER/.devscripts ]; then
		cp /home/$SUDO_USER/.devscripts $BUILDPLACE/home/$SUDO_USER/.devscripts
	fi
	echo "   -> updating apt-cross cache"
	if [ ! -f $DPKG_CROSS/apt.conf-${SUITE} ]; then
		chroot $BUILDPLACE /usr/bin/apt-cross -a $ARCH -S $SUITE -u
	else
		# this code is now old - /home/$SUDO_USER/.apt-cross is the new
		# directory but easier to let apt-cross >= 0.3.0 recreate that.
		echo "     -> copying existing apt-cross cache"
		DPKG_CROSS="/home/$SUDO_USER/.dpkg-cross"
		cp $DPKG_CROSS/apt.conf-unstable $BUILDPLACE/$DPKG_CROSS/
		cp $DPKG_CROSS/sources.unstable $BUILDPLACE/$DPKG_CROSS/
		cp $DPKG_CROSS/status-unstable $BUILDPLACE/$DPKG_CROSS/
		cp -r $DPKG_CROSS/unstable $BUILDPLACE/$DPKG_CROSS/
		chroot $BUILDPLACE /usr/bin/apt-cross -a $ARCH -u
	fi
	VAL=`chroot $BUILDPLACE hostname -f 2>&1`
	if [ "$VAL" != "" ]; then
		echo "   -> updating /etc/hosts"
		cp /etc/hosts $BUILDPLACE/etc/hosts
	fi
	chroot $BUILDPLACE /usr/bin/emsetup --arch $ARCH --yes
	umountproc
	create_emdebiantgz
}

function emchrootbuild()
{
		if [ $SUITE == testing ]; then
			BASETGZ="${WORKDIR}/emdebian-testing.tgz"
			SUITE=testing
			echo "Building in the embootstrap testing chroot"
		else
			BASETGZ="$WORKDIR/emdebian.tgz"
		fi
		. /usr/lib/pbuilder/pbuilder-buildpackage-funcs

		BUILDRESULTUID="${BUILDRESULTUID:-${SUDO_UID:-0}}"
		BUILDRESULTGID="${BUILDRESULTGID:-${SUDO_GID:-0}}"

		while ! test -d ./debian -o "$(pwd)" = "$WORKDIR" ; do
		    cd ..;
		done
		if test ! -d ./debian; then
		    echo "Cannot find ./debian dir"
			cleanbuildplace
		    exit 1
		fi;

		PKG_SOURCENAME=`dpkg-parsechangelog|sed -n 's/^Source: //p'`
		PKG_VERSION=`dpkg-parsechangelog|sed -n 's/^Version: \(.*:\|\)//p'`
		HOST_ARCH=`dpkg-architecture -qDEB_HOST_ARCH`

		echo "Building ${PKG_SOURCENAME} ${PKG_VERSION} on ${HOST_ARCH} for $ARCH"
		INITIAL=`echo $PKG_SOURCENAME | cut -b1`
		PKG=`basename \$PWD`;
		TRUNK="$INITIAL/${PKG_SOURCENAME}/trunk"
		SVN="$TRUNK/${PKG}"

		echo " -> source location: trunk/$SVN"
		# get the Debian .dsc
		DEB_VER=`echo $PKG_VERSION  | sed -n 's/em[0-9]*$//p'`
		if [ "$DEB_VER" == "" ]; then
			echo "$PKG_VERSION is not an Emdebian version string."
			cleanbuildplace
			exit 1;
		fi
		DSC="${PKG_SOURCENAME}_${DEB_VER}.dsc"
		if [ ! -f "../$DSC" ]; then
			echo "Cannot find ../$DSC"
			cleanbuildplace
			exit 1;
		fi
		echo " -> using $DSC"

		# don't duplicate the downloads made by emsource
		EMDIFFS=`find ../ -name emdebian-*\.patch`
		DEBDIFFS=`find ../ -name debian-patch*\.patch`
		DIFFS="$EMDIFFS $DEBDIFFS"
		OLDSRC="$PKG_SOURCENAME.old"
		echobacktime
		extractembuildplace
		echo "File extracted to: $BUILDPLACE"
		echo ""
		recover_aptcache
		createbuilduser

		if [ -f "/etc/devscripts.conf" ]; then
			cp /etc/devscripts.conf $BUILDPLACE/etc/devscripts.conf
		fi
		if [ -f "/home/$SUDO_USER/.devscripts" ]; then
			mkdir -p $BUILDPLACE/home/$SUDO_USER/
			cp /home/$SUDO_USER/.devscripts $BUILDPLACE/home/$SUDO_USER/.devscripts
		fi

		COLOUR=
		if [ $ANSI_COLORS_DISABLED ]; then
			COLOUR=ANSI_COLORS_DISABLED=1
		fi
		echo "Checking the Emdebian toolchain"
		# fails with non-zero if no toolchain is found
		echo "$COLOUR /usr/bin/emsetup --arch $ARCH --report" | chroot $BUILDPLACE /bin/sh
		echo "Copying Debian source"
		chroot $BUILDPLACE mkdir -p trunk/$TRUNK
		copydsc "../$DSC" "$BUILDPLACE/trunk/${TRUNK}/"
		chroot $BUILDPLACE /usr/bin/dpkg-source -x trunk/${TRUNK}/$DSC trunk/$SVN

		echo "Applying Emdebian changes"
		for PATCH in $DIFFS
		do
			cp "$PATCH" "$BUILDPLACE/trunk/$TRUNK"
			if echo "cd /trunk/$SVN; patch -p1 < $PATCH" | chroot $BUILDPLACE /bin/sh; then
			:
			else
			umountproc
			cleanbuildplace
			exit 1;
		fi
		done

		# only check for dependencies AFTER debian/control has been patched for Emdebian.
		echo " -> installing build dependencies for $PKG_SOURCENAME"
		checkembuilddep
		# new function to parse and install cross-depends.
		# read debian/xcontrol, pass Build-Cross-Depends to apt-cross -i
		get_cross_depends
		save_aptcache
		echo " -> copying ../$OLDSRC"
		cp -r "../$OLDSRC" "$BUILDPLACE/trunk/$TRUNK"
		echo "Running emdebuild -a $ARCH: "
		COLOUR=
		if [ $ANSI_COLORS_DISABLED ]; then
			COLOUR=ANSI_COLORS_DISABLED=1
		fi
		if echo "cd /trunk/$SVN; $COLOUR ARCH=$ARCH emdebuild -a $ARCH" | chroot $BUILDPLACE /bin/sh; then
		:
		else
			save_aptcache
			umountproc
			cleanbuildplace
			exit 1;
		fi
		if [ $? -ne 0 ]; then
			echo "  -> emdebuild failed"
			if [ "${LOGIN_AFTER_FAIL}" = "yes" ]; then
				echo "   -> Logging into the chroot"
				echo "   -> Build directory: /trunk/$SVN"
				echobacktime
				chroot $BUILDPLACE /bin/sh
				save_aptcache
				umountproc
				cleanbuildplace
				exit 1;
			fi
		fi
		FULLPATH="$BUILDPLACE/trunk/$TRUNK/"
		FULLPATH=`echo $FULLPATH | tr -s \/`
		PKGRESULT="${BUILDRESULT}${INITIAL}/${PKG_SOURCENAME}/trunk"
		echo "Copying build results to ${PKGRESULT}"
		mkdir -p "$PKGRESULT"
		if [ -d "${PKGRESULT}" ]; then
		    chown "${BUILDRESULTUID}:${BUILDRESULTGID}" "${FULLPATH}"*
		    chgrp "${BUILDRESULTGID}" "${FULLPATH}"*
		    cp -p "${FULLPATH}"/* "${PKGRESULT}" 2>/dev/null || true
		else
		    echo "E: BUILDRESULT=[$BUILDRESULT] is not a directory." >&2
		fi
		save_aptcache
		umountproc
		cleanbuildplace
		echobacktime
}

function emlogin()
{
	if [ $SUITE == testing ]; then
		BASETGZ="${WORKDIR}/emdebian-testing.tgz"
		echo "Logging into the embootstrap testing chroot"
	else
		BASETGZ="$WORKDIR/emdebian.tgz"
	fi
	extractembuildplace
	recover_aptcache
	echo " -> entering the shell"
	echo "File extracted to: $BUILDPLACE"
	echo " -> checking Emdebian setup..."
	COLOUR=
	if [ $ANSI_COLORS_DISABLED ]; then
		COLOUR=ANSI_COLORS_DISABLED=1
	fi
	echo "$COLOUR /usr/bin/apt-cross -a $ARCH -c" | chroot $BUILDPLACE /bin/sh
	echo "$COLOUR /usr/bin/emsetup --arch $ARCH --report" | chroot $BUILDPLACE /bin/sh
	chroot $BUILDPLACE bin/sh
	save_aptcache
	# saving the buildplace afterwards
	if [ "${SAVE_AFTER_LOGIN}" = "yes" ]; then
		echo " -> Saving the results, modifications to this session will persist"
		umountproc
		chroot $BUILDPLACE /usr/bin/apt-get clean || true
		create_emdebiantgz
	    fi
	cleanbuildplace
}

# test for sudo - normally empdebuild is installed in /usr/sbin so this
# test is really only for SVN users.
# make sure sudo is in use.
# bash cannot seem to do this when set -e is enabled
# because grep returns non-zero on a non-match
# so I use perl. :-)
ISSUDOSET=`perl -e '$e=\`printenv\`; ($e =~ /SUDO_USER/) ? print "yes" : print "no";'`
if [ $ISSUDOSET == "no" ] ; then
	AREWEROOT=`perl -e '$e=\`printenv\`; ($e =~ /LOGNAME=root/) ? print "yes" : print "no";'`
	if [ $AREWEROOT == "no" ]; then
		echo "empdebuild needs to be run under sudo or as root."
		exit 2
	fi
fi

if [ ! $1 ];then
	usagehelp
	exit;
fi
while [ -n "$1" ]; do
case "$1" in
	--help|-h|-?|--version)
		usagehelp
		exit;
	;;
	-a|--arch)
		shift
		ARCH=$1
		# chomp the argument to --arch
		shift
	;;
	--testing)
		shift
		SUITE=testing
	;;
	--create|create)
		shift;
		checkarch
		createemchroot
		exit;
	;;
	--update|update)
		shift;
		checkarch
		update_emchroot
		exit;
	;;
	--build|build)
		shift;
		checkarch
		emchrootbuild
		exit;
	;;
	--save-after-login)
		shift
		SAVE_AFTER_LOGIN="yes"
	;;
	--login-after-fail)
		shift
		LOGIN_AFTER_FAIL="yes"
	;;
    --login|login)
    	shift
    	checkarch
    	emlogin
    	exit;
	;;
	*)
		echo "Unrecognised option: $1"
		echo
		usagehelp
		exit;
	;;
esac
done

