#!/bin/bash -e

# Acquire lock on source tree
parent=$(ps h -o comm $PPID)
if [ "$parent" != "flock" ]; then
	exec flock ".lock" $0 "$@"
fi

usage()
{
	prog=$(basename $0)

	echo ""
	echo "Usage:"
	echo "  $prog [options] <build_combo>"
	echo ""
	echo "  Options:"
	echo "    -a  Automated build (check for clean working dir)"
	echo "    -c  Clean output before build"
	echo "        Given once, clean device output (out/target/product)"
	echo "        Given twice, clean target output (out/target)"
	echo "        Given thrice, clean all output (out)"
	echo "    -C  Clean output after successful build"
	echo "        Given once, clean device output (out/target/product)"
	echo "        Given twice, clean target output (out/target)"
	echo "        Given thrice, clean all output (out)"
	echo "    -j  Specify -j value for make [CPUS]"
	echo "    -l  Save build output to log file"
	echo "    -o  Specify output directory"
	echo "    -O  Specify Android out dir"
	echo "    -s  Show commands"
	echo "    -t  Specify make target [auto]"
	echo "    -v  Specify version [none]"
	echo ""
	echo "Exit status: 0 on success, !0 on failure"
	echo ""
	echo "On success, output will be copied to \$TARGET_PRODUCT.zip."
	echo ""
	exit 1
}

# Tool locations
# XXX: not darwin compatible
CCACHE="prebuilts/misc/linux-x86/ccache/ccache"

ccache_stats()
{
    # Calculate ccache stats
    local ch=0
    local cm=0
    local ct=0
    local cpct="NA"

    OLDIFS="$IFS"
    IFS=$'\n'
    lines=($($CCACHE -s))
    IFS="$OLDIFS"
    for (( n=0; $n < ${#lines[*]}; n=$(($n+1)) )); do
        line=${lines[$n]}
        if [ "${line:0:9}" = "cache hit" ]; then
            ch=$(( ch + $(echo $line | sed 's/[^0-9]*//') ))
        elif [ "${line:0:10}" = "cache miss" ]; then
            read -a fields <<<$line
            cm=$(( cm + $(echo $line | sed 's/[^0-9]*//') ))
        fi
    done
    IFS="$OLDIFS"
    if [ -n "$ch" -a -n "$cm" ]; then
        ct=$(($ch+$cm))
    fi
    if [ "$ct" -gt 0 ]; then
            cpct=$((ch*100/ct))
    fi

    echo "ch=$ch"
    echo "cm=$cm"
    echo "ct=$ct"
    echo "cpct=$cpct"
}

# Set some useful vars
export USE_CCACHE=1
export CPUS=$(grep "^processor" /proc/cpuinfo | wc -l)
export MEM=$(cat /proc/meminfo | grep "^MemTotal" | grep -o "[0-9]*")

# Find actual value for jobs.
#   - No more than 8 jobs
#   - No more than one job per cpu
#   - No more than one job per 1.5gb
max_jobs=8
cpu_jobs="$CPUS"
if [ "$cpu_jobs" -lt "$max_jobs" ]; then
	max_jobs="$cpu_jobs"
fi
mem_jobs=$(($MEM/1572864))
if [ "$mem_jobs" -lt "$max_jobs" ]; then
	max_jobs="$mem_jobs"
fi

# Init options
opt_automated=0
opt_clean=0
opt_postclean=0
opt_jobs="$max_jobs"
opt_log=0
opt_outdir="."
opt_outandroid=""
opt_maketarget="auto"
opt_showcommands=0
opt_version=""
while getopts "acCj:lo:O:st:v:" opt; do
	case "$opt" in
	a) opt_automated=1 ;;
	c) opt_clean=$(($opt_clean+1)) ;;
	C) opt_postclean=$(($opt_postclean+1)) ;;
	j) opt_jobs="$OPTARG" ;;
	l) opt_log=1 ;;
	o) opt_outdir="$OPTARG" ;;
	O) opt_outandroid="$OPTARG" ;;
	s) opt_showcommands=1 ;;
	t) opt_maketarget="$OPTARG" ;;
	v) opt_version="$OPTARG" ;;
	*) usage
	esac
done
shift $(($OPTIND-1))
if [ $# -ne 1 ]; then
	usage
fi

if [ -n "$opt_outandroid" ]; then
	export OUT_DIR_COMMON_BASE="$opt_outandroid"
fi

mkdir -p "$opt_outdir"

# NB: "$combo" is used by build scripts
build_combo="$1"
project_device=$(echo $build_combo | cut -d'-' -f1)
project=$(echo $project_device | cut -d'_' -f1)
device=$(echo $project_device | cut -d'_' -f2)

# Desired output files
if [ -n "$opt_version" ]; then
	out_name="${opt_outdir}/${project_device}-${opt_version}"
else
	out_name="${opt_outdir}/${project_device}"
fi

if [ "$opt_log" -ne 0 ]; then
	rm -f "${out_name}.log"
	exec >> "${out_name}.log" 2>&1
fi

if [ ! -d ".repo" ]; then
	echo "Invalid build tree"
	exit 1
fi

case "$opt_clean" in
0) # Do nothing
	;;
1) # Clean device output
	rm -rf "$OUT_DIR_COMMON_BASE/target/product"
	;;
2) # Clean target output
	rm -rf "$OUT_DIR_COMMON_BASE/target"
	;;
*) # Clean all output
	rm -rf "$OUT_DIR_COMMON_BASE"
	;;
esac

# Detect make target
if [ -z "$opt_maketarget" -o "$opt_maketarget" = "auto" ]; then
	opt_maketarget="otapackage"
	if [ -f "build/core/Makefile" ]; then
		if grep -q "^bacon:" "build/core/Makefile"; then
			opt_maketarget="bacon"
		fi
	fi
fi

if [ "$opt_showcommands" -ne 0 ]; then
	opt_maketarget="showcommands $opt_maketarget"
fi

# Ensure working directory is clean
if [ "$opt_automated" -ne 0 ]; then
	android-repo status | grep -q "working directory clean"
fi

. build/envsetup.sh
lunch "$build_combo"

# Setup ccache
if [ -z "$CCACHE_ROOT" ]; then
	CCACHE_ROOT="$HOME"
fi

export CCACHE_DIR="$CCACHE_ROOT/.ccache-$project_device"
if [ ! -d "$CCACHE_DIR" ]; then
	mkdir -p "$CCACHE_DIR"
	$CCACHE -M 8G
fi

# Clean some things
rm -f $OUT/system/build.prop

eval $(ccache_stats)
sch=$ch
scm=$cm
sct=$ct
scpct=$cpct

# Do the build
stime=$(date +%s)
make -j${opt_jobs} ${opt_maketarget}
etime=$(date +%s)

# Find output zip
built_zip=$(ls -t $OUT/*.zip | head -1)
if [ ! -f "$built_zip" ]; then
	echo "Error: cannot find built zip in $OUT"
	exit 1
fi

# Copy output zip to well known place
cp "$built_zip" "${out_name}.zip"

# Calculate elapsed time
elapsedsec=$((etime - stime))
elapsed=$(printf "%02d:%02d" $((elapsedsec/60)) $((elapsedsec%60)))

eval $(ccache_stats)
ech=$ch
ecm=$cm
ect=$ct
ecpct=$cpct

dch=$((ech - sch))
dcm=$((ecm - scm))
dct=$((ect - sct))
dcpct="NA"
if [ "$dct" -gt 0 ]; then
	dcpct=$((dch*100/dct))
fi

echo "Build complete."
echo "elapsed time : $elapsed"
echo "ccache totals: $ech/$ect ($ecpct%)"
echo "ccache deltas: $dch/$dct ($dcpct%)"

case "$opt_postclean" in
0) # Do nothing
	;;
1) # Clean device output
	rm -rf "$OUT_DIR_COMMON_BASE/target/product"
	;;
2) # Clean target output
	rm -rf "$OUT_DIR_COMMON_BASE/target"
	;;
*) # Clean all output
	rm -rf "$OUT_DIR_COMMON_BASE"
	;;
esac