#!/bin/sh
#
# Copyright 2005-2006 Movial
#
# This program 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 2 of the License, or
# (at your option) any later version.

sb-conf in -e # dpkg-architecture needs working uid

DEFAULT_MIRROR="deb-src ftp://ftp.debian.org/debian sarge main"
DEB_BUILD_GNU_CPU=`dpkg-architecture -qDEB_BUILD_GNU_CPU`
DEB_HOST_GNU_SYSTEM=`dpkg-architecture -qDEB_HOST_GNU_SYSTEM`
PATCH_DIRS="patches patches/$DEB_BUILD_GNU_CPU patches/$DEB_HOST_GNU_SYSTEM"
PASS=0
CROCODILE_BASE=`pwd`
ENV_DIRS="$CROCODILE_BASE/variables $CROCODILE_BASE/variables/$DEB_BUILD_GNU_CPU $CROCODILE_BASE/variables/$DEB_HOST_GNU_SYSTEM"


function usage
{
	cat << EOF >&2
Usage: $0 [options] [<plan>]
       -c | --clean           clean build directory before staring
       -h | --help            display this menu
       -r | --no-reset        don't reset (prepare) the target
       -p | --provided <path> specify a path for provided packages
EOF
}

function prepare_target {

(
ppath=/scratchbox/compilers/`sb-conf show -c`/packages
if [ "z$1" != "z" ] ; then
	ppath="$1"
fi
cd $CROCODILE_BASE
	sb-conf reset -f
	sb-conf install --etc --devkits --fakeroot
	if [ -e etc/sources.list ]; then
		cp etc/sources.list /etc/apt/
	else
		echo "$DEFAULT_MIRROR" > /etc/apt/sources.list
	fi
	if [ -e etc/apt.conf ]; then
		cp etc/apt.conf /etc/apt
	fi
	echo "deb file:$CROCODILE_BASE/work/built ./" >> /etc/apt/sources.list
	mkdir -p /usr/sbin/
	mkdir -p /usr/local/bin/ # openssl
	# prevent daemons from starting up
	echo "exit 101" > /usr/sbin/policy-rc.d
	chmod a+x /usr/sbin/policy-rc.d
			
	mkdir -p /etc 2>/dev/null
	echo -n "Europe/Helsinki" > /etc/timezone
	echo set timezone
	
	mkdir -p /var/cache/debconf 2>/dev/null
	cat <<EOF > /var/cache/debconf/config.dat
Name: debconf/frontend
Template: debconf/frontend
Value: Noninteractive
Owners: debconf
Flags: seen

Name: debconf/priority
Template: debconf/priority
Value: critical
Owners: debconf
Flags: seen

EOF
	export DEBIAN_FRONTEND=noninteractive
	export DEBIAN_PRIORITY=critical
	dpkg -i "${ppath}/"*.deb
)
}

function log {
	local stamp=`date +"%Y-%m-%d %T"`

	printf   "%s %-17s %s\n" "$stamp" "$1" "$2" >> $CROCODILE_BASE/build.log
	printf "  %s %-17s %s\n" "$stamp" "$1" "$2"  >&2
}

function log_separator {
	echo >&2
	echo >&2
}

function check_user {
	if ! grep -q $USER /etc/passwd
	then
		log "adding $USER to /etc/passwd"
		sb-conf in --etc
	fi
}

function build {
	local src=$1
	shift
	local env=$@

	if [ -e work/cookies/build/$src ]; then
		log "Already built" "$src"
	else
		(
			set -e

			find work/build -type d -maxdepth 1 -name "$src-*" -exec rm -rf {} \;

			log "Fetching sources" "$src"
			log_separator

			check_user
			lib/apt.py source $src > /dev/null 
			dir=`find work/build -type d -maxdepth 1 -name "$src-*" | sort | tail -n1`
			test "$dir"
			
			local ver=`echo "$dir" | sed -e 's/.\+-\([^-]\+\)$/\1/'`

			log_separator

                        
                        for patchdir in $PATCH_DIRS; do
                            for patchfile in "${src}.patch" "${src}-${ver}.patch" ; do
                                if [ -r  "${patchdir}/${patchfile}" ]; then
                                    log "Applying" "${patchdir}/${patchfile}"
                                    log_separator
                                    patch -p1 -d $dir < "${patchdir}/${patchfile}"
                                    log_separator
                                fi
                            done
                        done


			log "Building" `echo $dir | cut -d/ -f3-`

			(
			cd $dir
			for envdir in $ENV_DIRS; do
				for envfile in "${src}.env" "${src}-${ver}.env" ; do
					if [ -r "${envdir}/${envfile}" ];then
						log "workaround env:" "${envdir}/${envfile}"
						source "${envdir}/${envfile}"
					fi
				done
				if [ -r $envdir/build.env ];then
					log "workaround env:" "$envdir/build.env"
					. $envdir/build.env
				fi
			done
			check_user
			set +e
			fakeroot $CROCODILE_BASE/lib/pbuilder-satisfydepends --scratchbox $DEB_BINARY_TARGET
			if [ "$?" = 0 ]; then
				check_user
				set -e
				log_separator
				if [ x$DEB_BINARY_TARGET = x ]; then
				  log "target: binary-all" "$src"
				  log_separator
			    	  dpkg-buildpackage -rfakeroot -b -uc
				else
				  log "target: binary-arch" "$src"
				  log_separator
			    	  dpkg-buildpackage -rfakeroot -B -uc
				fi
			else
				check_user
				fakeroot dpkg --configure -a
				if [ "$?" != 0 ]; then
					log "Target broken, resetting"
					prepare_target "$pkgdir"
					apt-get update >/dev/null 2>&1
				fi
				set -e
				log_separator
				log "Skip:" $PASS
				touch $CROCODILE_BASE/work/cookies/pass/$src.$PASS
				false
			fi
			) >  $CROCODILE_BASE/work/logs/$src.$PASS 2>&1
		)
		if [ "$?" = 0 ]; then
			(
			log "Updating Packages.gz"
			mv work/build/*deb work/build/*.changes work/built
			mv $CROCODILE_BASE/work/logs/$src.$PASS $CROCODILE_BASE/work/logs/$src.$PASS.ok
			find work/build -type d -maxdepth 1 -name "$src-*" -exec rm -rf {} \;
			cd work/built
			dpkg-scanpackages . /dev/null 2>/dev/null | gzip > Packages.gz
			)
			touch work/cookies/build/$src

			log_separator
			log "Passed" ""
			apt-get update >/dev/null 2>&1
		else
			touch $CROCODILE_BASE/work/cookies/pass/$src.$PASS
			log_separator
			tail $CROCODILE_BASE/work/logs/$src.$PASS
			log_separator
			log "try again: $PASS" 
			log "" "FAILED"
		fi
	fi
}

function builds_progress {
	if [ $PASS = 0 ] || [ $PASS = 1 ]; then return 0; fi

	for prevfile in work/cookies/pass/*.$prev;
	do
		if `echo $prevfile|grep -q \*`; then
			log "bruteforce.plan:" "SUCCESS"
			return 42
		fi
		
		file=`echo $prevfile|sed -e s/$prev'$'/$PASS/`
		if [ ! -e $file ]; then 
			log "hope exists $prevfile succeeded" ""
			return 0; 
		else
			log "$prevfile and $file both failed." ""
		fi
		
	done
	# we are stuck
	log "bruteforce.plan" "STUCK, ending"
	RETURN_VALUE=1
	return 1
}

function bruteforce_plan { 

	log "bruteforce.plan:" "INITIAL PASS"
	log_separator

	for source in `grep -h '^Package: ' work/lists/*Sources|sed -e 's/Package: //'|uniq`; 
	do
		build $source
	done

	while builds_progress;
	do
		prev=$PASS
		PASS=$[ $PASS + 1 ]
		log  "bruteforce.plan:" "ITERATIVE PASS $PASS"
		
		for source in `ls work/cookies/pass/*.$prev|sed -e 's,.*/,,' -e 's,\.'$prev'$',,`; 
		do
			build $source
		done
	done
}

RETURN_VALUE=0
do_reset=true
do_clean=false
plan=
pkgdir=

var=
flag=
count=0
for i in "$@" ; do
	count=`expr "$count" + 1`
	if [ "z$var" != 'z' ] ; then
		case $var in
			pkgdir)
				if [ ! -d "$i" ] ; then
					echo "Directory does not exist: $i"
					exit 1
				else
					pkgdir="$i"
				fi
				;;
			*)
				echo "Expected argument after flag: $flag"
				;;
		esac
		var=
		flag=
		continue
	fi
	case $i in
		--clean|-c)
			do_clean=true
			;;
		--help|-h)
			usage
			exit 0
			;;
		--no-reset|-r)
			do_reset=false
			;;
		--provided*|-p)
			val=`echo $i | cut -d = -f 2`
			if [ "$val" != "$i" ] && [ "z$val" != "z" ] ; then
				if [ ! -d "$val" ] ; then
					echo "Directory does not exist: $val"
					exit 1
				else
					pkgdir="$val"
				fi
			else
				var='pkgdir'
				flag="$val"
			fi
			;;
		*)
			if [ "$count" -ge "$#" ] ; then
				if [ -e "$i" ] ; then
					plan="$i"
				else
					echo "Plan does not exist: $i"
					exit 1
				fi
			else
				usage
				exit 1
			fi
			;;
	esac
done

if [ "z$var" != 'z' ] ; then
	echo "Expected argument after flag: $flag"
	exit 1
fi

echo >>build.log
(
	if $do_clean; then
		log "Cleaning build directory" ""
		rm -rf work
	else
		# continuing a existing build
		rm -f work/cookies/pass/*
		rm -fr work/logs
	fi

	mkdir -p work/build || exit 1
	mkdir -p work/cookies/build || exit 1
	mkdir -p work/cookies/pass || exit 1
	mkdir -p work/logs || exit 1

	lib/apt.py update

	if $do_reset; then
		log "Resetting target" ""
		log_separator
		(
			set -e
			prepare_target "$pkgdir"
		)
		log_separator
	fi

	(
	mkdir -p work/built
    	cd work/built
	dpkg-scanpackages . /dev/null 2>/dev/null | gzip > Packages.gz
	)

	apt-get update >/dev/null 2>&1
	
	if [ x"$plan" = x ]; then
		bruteforce_plan	
	else
		. $plan
	fi

	echo $RETURN_VALUE > $CROCODILE_BASE/return_value


	log "Finished" ""

) 2>&1 | tee $CROCODILE_BASE/output.log

RETVAL=`cat $CROCODILE_BASE/return_value`
rm -f $CROCODILE_BASE/return_value
exit $RETVAL


