#!/bin/sh

PROGNAME=`basename $0`
OUTDIR="./"

usage() {
	echo "usage: $PROGNAME architecture Packages Sources" >&2
	echo >&2
	echo "Find the reasons why Debian is currently not bootstrappable." >&2
	echo "Assumes that Architecture:all packages need not be bootstrapped." >&2
	echo "Operates on the strong dependency graph." >&2
	echo >&2
	echo " -h, --help       Show this help message and exit" >&2
	echo " -k, --keep       Keep the temporary files" >&2
	echo " -T, --timers     time all program executions" >&2
	echo " -m, --allowsrcmismatch Allow binary packages to be associated with a source " >&2
	echo "                        package of a different version than themselves" >&2
	echo " -o, --output=DIR Output directory. Default is the current directory" >&2
	echo " -t, --tmp=DIR    Temporary directory. Default is created by mktemp(1)" >&2
	echo " -v, --verbose    Be more verbose" >&2
	echo " -d, --debug      Maximum verbosity" >&2
	echo " -D, --develop    Execute tools from the source checkout instead of \$PATH" >&2
}


die() {
	echo " + error $@"
	if [ -n "$KEEP" ] || [ "$CUSTOMTMP" = "yes" ]; then
		echo " + temporary files stored in $tmp" >&2
	else
		rm -f "$tmp"/*
		rm -fd $tmp
	fi
	exit 1
}

run() {
	if [ -n "$TIMERS" ]; then
		time --format "%e seconds" "$@"
	else
		"$@"
	fi
}

# clean up when sighup, sigint or sigterm is received
trap "die \"got signal\"" 1 2 15

getopt -T > /dev/null && exitcode=0 || exitcode=$?
if [ $exitcode -eq 4 ]; then
	# GNU enhanced getopt is available
	ARGS=`getopt --long help,keep,timers,allowsrcmismatch,output:,tmp:,verbose,debug,develop --options hkTmo:t:vdD -- "$@"`
else
	# Original getopt is available
	ARGS=`getopt hkTmo:t:vdD "$@"`
fi

if [ $? -ne 0 ]; then
	exit 1
fi

eval set -- $ARGS

while [ $# -gt 0 ]; do
	case "$1" in
		-h | --help)     usage; exit 0;;
		-k | --keep)     KEEP=true;;
		-T | --timers)   TIMERS=true;;
		-m | --allowsrcmismatch) ALLOWSRCMISMATCH=--allowsrcmismatch;;
		-o | --output)   OUTDIR="$2"; shift;;
		-t | --tmp)      tmp="$2"; shift;;
		-v | --verbose)  VERBOSE=--verbose;;
		-d | --debug)    set -x;;
		-D | --develop)  DEVELOP=true;;
		--)              shift; break;;
	esac
	shift
done

bin_buildcheck=dose-builddebcheck
if [ -n "$DEVELOP" ]; then
	[ -f ./bin2src.native ] && bin_bin2src=./bin2src.native || bin_bin2src=./bin2src.d.byte
	[ -f ./create-graph.native ] && bin_create_graph=./create-graph.native || bin_create_graph=./create-graph.d.byte
	bin_latest_version=./tools/latest-version.py
	bin_buildgraph2packages=./tools/buildgraph2packages.py
	bin_graph_shortest_path=./tools/graph-shortest-path.py
	bin_graphml2dot=./tools/graphml2dot.py
else
	bin_bin2src=botch-bin2src
	bin_create_graph=botch-create-graph
	bin_selfcycles=botch-selfcycles
	bin_latest_version=botch-latest-version
	bin_buildgraph2packages=botch-buildgraph2packages
	bin_graph_shortest_path=botch-graph-shortest-path
	bin_graphml2dot=botch-graphml2dot
fi

mkdir -p "$OUTDIR"

if [ $# -ne 3 ]; then
	usage
	exit 1
fi

arch="$1"
packages="$2"
sources="$3"

if [ -n "$tmp" ]; then
	mkdir -p "$tmp"
else
	tmp=`mktemp --directory`
	CUSTOMTMP="no"
fi

if [ "$KEEP" != "true" ]; then
	if [ -n "$(ls -A $tmp)" ]; then
		echo "$tmp is not empty and you did not specify --keep" >&2
		echo "refusing to run and delete that directory" >&2
		exit 1
	fi
fi

zcat -f "$packages" | grep-dctrl --exact-match -F Architecture all > "$tmp/available-noall"

zcat -f "$packages" | grep-dctrl --exact-match --field Package build-essential \
	| run $bin_latest_version - - \
	| run $bin_bin2src $ALLOWSRCMISMATCH --deb-native-arch="$arch" - "$sources" \
	| run $bin_create_graph $VERBOSE --progress $ALLOWSRCMISMATCH \
		--deb-native-arch="$arch" --strongtype \
		--available "$tmp/available-noall" --bg "$sources" "$packages" - \
	> "$tmp/buildgraph.xml"

$bin_buildgraph2packages "$tmp/buildgraph.xml" "$packages" \
	| grep-dctrl --not --exact-match -F Architecture all \
	| $bin_bin2src $ALLOWSRCMISMATCH --deb-native-arch="$arch" - "$sources" \
	| grep-dctrl --no-field-names --show-field=Package,Version '' \
	| paste --serial --delimiter="  \n" \
	| sed 's/\s\+$//; /^$/d' \
	| sort \
	> "$tmp/buildgraph.list"

[ -f "$tmp/buildgraph.list" -a ! -s "$tmp/buildgraph.list" ] && die "created graph is empty"

$bin_buildcheck --deb-native-arch="$arch" -f -m "$packages" "$sources" \
	| sed -n 's/^\s\+failure: src:\([^ ]\+\) (= \([^)]\+\))/\1 \2/p' \
	| sort \
	> "$tmp/failures.list"

[ -f "$tmp/failures.list" -a ! -s "$tmp/failures.list" ] && die "all packages compile"

reasons=""

comm -12 "$tmp/buildgraph.list" "$tmp/failures.list" \
	| while read name version; do \
		fname="${name}_$version"
		run $bin_graph_shortest_path "$tmp/buildgraph.xml" ${tmp}/${fname}.xml --source type:bin name:build-essential --target type:src name:$name version:$version || die "botch-shortest-path failed"
		run $bin_graphml2dot ${tmp}/${fname}.xml ${OUTDIR}/${fname}.dot || die "botch-graphml2dot failed"
		run dot -Tpng ${OUTDIR}/${fname}.dot > ${OUTDIR}/${fname}.png || die "dot failed"
		# execute graph-easy but do not fail if it doesn't exist or doesn't work as advertised
		graph-easy --input ${OUTDIR}/${fname}.dot --as_ascii >&2 || true
		echo "stored explanation graph for $name (= $version) in ${OUTDIR}/${fname}.dot and a rendering in ${fname}.png" >&2
		reasons="$reasons $name"
	done

if [ -n "$reasons" ]; then
	echo "failure reasons: $reasons" >&2
fi

if [ -n "$KEEP" ] || [ "$CUSTOMTMP" = "yes" ]; then
	echo " + temporary files stored in $tmp" >&2
else
	rm -f "$tmp"/*
	rm -fd $tmp
fi

if [ -n "$reasons" ]; then
	exit 1
else
	exit 0
fi
