#!/bin/sh

# to make sure that lines like "rm $tmp/*" doesn't remove root
set -u

PROGNAME=`basename $0`
CUSTOMTMP="no"
OUTDIR="./"
KEEP="no"
DEVELOP=
TIMERS=
VERBOSE=

usage() {
	echo "usage: $PROGNAME architecture Packages Sources [pkgname]" >&2
	echo >&2
	echo "Find the reasons why source packages are B-D transitive essential." >&2
	echo "Assumes that Architecture:all packages need not be bootstrapped." >&2
	echo "Operates on the strong dependency graph." >&2
	echo >&2
	echo "If the optional \"pkgname\" argument is specified as well, then only" >&2
	echo "the results for that one package will be output." >&2
	echo >&2
	echo "options:" >&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 " -o, --output=DIR Output directory. Default is the current directory" >&2
	echo " -t, --tmp=DIR    Temporary directory. Default is created by mktemp(1). Implies --keep." >&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 [ "$KEEP" = "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,output:,tmp:,verbose,debug,develop --options hkTo:t:vdD -- "$@"`
else
	# Original getopt is available
	ARGS=`getopt hkTo: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="yes";;
		-T | --timers)   TIMERS=--timers;;
		-o | --output)   OUTDIR="$2"; shift;;
		-t | --tmp)      CUSTOMTMP="yes"; KEEP="yes"; tmp="$2"; shift;;
		-v | --verbose)  VERBOSE=--verbose;;
		-d | --debug)    set -x;;
		-D | --develop)  DEVELOP=--develop;;
		--)              shift; break;;
	esac
	shift
done

getocamlexec() {
	execname="$1"
	for ext in "native" "p.native" "byte" "d.byte"; do
		path="./$execname.$ext"
		if [ -f "$path" ]; then
			echo "$path"
			return
		fi
	done
	die "cannot find executable for $execname"
}

bin_buildcheck=dose-builddebcheck
if [ -n "$DEVELOP" ]; then
	bin_bin2src=$(getocamlexec bin2src)
	bin_create_graph=$(getocamlexec create-graph)
	bin_latest_version=./tools/latest-version.py
	bin_graph_shortest_path=./tools/graph-shortest-path.py
	bin_graphml2dot=./tools/graphml2dot.py
	bin_graph2text=./tools/graph2text.py
else
	bin_bin2src=botch-bin2src
	bin_create_graph=botch-create-graph
	bin_latest_version=botch-latest-version
	bin_graph_shortest_path=botch-graph-shortest-path
	bin_graphml2dot=botch-graphml2dot
	bin_graph2text=botch-graph2text
fi

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

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

if [ $CUSTOMTMP = "yes" ]; then
	mkdir -p "$tmp" || die "cannot mkdir $tmp"
else
	tmp=`mktemp --directory`
fi

mkdir -p "$OUTDIR" || die "cannot mkdir $OUTDIR"

if [ "$KEEP" != "yes" ] && [ -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

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

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

if [ ! -s "$tmp/ess-buildgraph.xml" ]; then
	die "$tmp/ess-buildgraph.xml is empty"
fi

run $bin_graph2text ${tmp}/ess-buildgraph.xml "{name} {version} {__ID__}" --vertex type:src \
	| sort \
	| while read name version vertex; do
		if [ -n "$pkgname" -a "$pkgname" != "$name" ]; then
			continue
		fi
		fname="${name}_${version}_${vertex}"
		run $bin_graph_shortest_path ${tmp}/ess-buildgraph.xml ${tmp}/${fname}.xml --source name:build-essential --target __ID__:$vertex || die "botch-graph-shortest-path failed"
		run $bin_graphml2dot ${tmp}/${fname}.xml ${OUTDIR}/${fname}.dot || die "botch-graphml2dot failed"
		run dot -Tsvg ${OUTDIR}/${fname}.dot > ${OUTDIR}/${fname}.svg || die "dot failed"
		# execute graph-easy but do not fail if it doesn't exist or doesn't work as advertised
		# redirect graph output to standard error because this tool is
		# not supposed to create something on standard output to then
		# be piped somewhere else
		{ graph-easy --input ${OUTDIR}/${fname}.dot --as_ascii || true; } | tee ${OUTDIR}/${fname}.txt >&2
	done

# we would like to print whether above look found any matches but unfortunately
# the body of the loop is run in a sub-shell and is thus unable to communicate
# variable values with its parent

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