#!/bin/sh

# TODO:
# get rid of global logdir in autorepo_set_logdir? or transform into an arch logdir?

# external variables

# optionally set from client; '' by default
# $hashernumber
# use_hashercache=
# keep_hashercache=


# autorepo-config, but those can be separated from AUTOREPO_
# $AUTOREPO_HOME (in fact, used as BUILDLOGS HOME)
# $AUTOREPO_BRANCH (optional, helps build defaults for no config)
# autorepo-config, duplicate
# $GB_ARCH
# autorepo-build-config
# $AR_BUILDER_HASHER_PREFIX
# $AR_BUILDER_HASHER_OPTS
# $AR_BUILDER_HASHER_BUILD_OPTS
# $AR_BUILDER_LOCAL_APT_DIR
# $AR_BUILDER_GLOBAL_APT_DIR

# static variables:
# logdir
# current_hasherdir
# hashercache_dir
# initroot_stamp
# aptconf_for_arch

autorepo_set_logdir()
{
    logdir=${AUTOREPO_HOME:-~}/logs${hashernumber:-}
}

set_hasher_dir()
{
    local arch
    arch=$1
    current_hasherdir=${AR_BUILDER_HASHER_PREFIX:-~/hasher}${hashernumber:-}.$arch
}

set_hashercache_dir()
{
    local arch
    arch=$1
    hashercache_dir=${AR_BUILDER_HASHER_PREFIX:-~/hasher}.$arch
}

set_initroot_stamp()
{
    local arch
    arch=$1
    autorepo_set_logdir
    initroot_stamp="$logdir"/initroot-${arch}.done
}

get_apt_conf_for_arch()
{
    local arch task repobranch conffile
    arch=$1
    task=${2:-apt}
    repobranch=$AUTOREPO_BRANCH
    aptconf_for_arch=
    for conffile in \
    "$AR_BUILDER_LOCAL_APT_DIR/${task}.conf.$repobranch.$arch" \
    "$AR_BUILDER_LOCAL_APT_DIR/${task}.conf.$arch" \
    "$AR_BUILDER_LOCAL_APT_DIR/apt.conf.$repobranch.$arch" \
    "$AR_BUILDER_LOCAL_APT_DIR/apt.conf.$arch" \
    "$AR_BUILDER_GLOBAL_APT_DIR/apt.conf.$repobranch.$arch" \
	; do
	if [ -e "$conffile" ]; then
	    aptconf_for_arch="$conffile"
	    return
	fi
    done
    echo "get_apt_conf_for_arch: failed to find apt.conf for $task ($repobranch/$arch) in $AR_BUILDER_LOCAL_APT_DIR:$AR_BUILDER_GLOBAL_APT_DIR"
    exit 1
}

arch_cleanup_chroot()
{
    local arch
    arch=$1
    shift;
    set_hasher_dir "$arch"
    [ -d "$current_hasherdir/chroot" ] && hsh-rmchroot ${hashernumber:+--number $hashernumber} "$current_hasherdir"
}

arch_mainrepo_unmets()
{
    local arch outfile
    arch=$1
    outfile=$2
    local WORKDIR
    WORKDIR=`mktemp -d --tmpdir autorepo.XXXXXXXXXX`
    # TODO: trap here (not in bash)
    #cleanup()
    #{
    #	rm -rf "$WORKDIR"
    #}
    #trap cleanup ERR
    get_apt_conf_for_arch $arch check
    mkaptbox --apt-config="$aptconf_for_arch" $WORKDIR
    $WORKDIR/aptbox/apt-cache unmet | LANG=C autorepo-buildhelper-sort-unmets > "$outfile"
    #cleanup
    rm -rf "$WORKDIR"
    #trap - ERR
    [ -s "$outfile" ] || rm -f "$outfile"
}

arch_create_mainrepo_hashercache()
{
    local arch number
    arch=$1
    number=$2
    [ -n "$number" ] || number=0
    set_hashercache_dir "$arch"
    if [ -n "$keep_hashercache" -a -d "$hashercache_dir"/aptbox -a -d "$hashercache_dir"/cache -a ! -d "$hashercache_dir"/chroot ]; then
	return 0
    fi
    rm -rf "$hashercache_dir"/repo
    aptconf_for_arch=
    get_apt_conf_for_arch $arch build
    mkdir -p "$hashercache_dir"
    echo $arch hsh "$hashercache_dir" ${AR_BUILDER_HASHER_OPTS:-} --target=$arch --with-stuff --number $number --apt-config=$aptconf_for_arch --initroot-only
    if ! $arch hsh "$hashercache_dir" ${AR_BUILDER_HASHER_OPTS:-} --target=$arch --with-stuff --number $number --apt-config=$aptconf_for_arch --initroot-only >/dev/null 2>&1; then
	echo "$arch: init parallel hasher cache failed"
	exit 1
    fi
    if [ ! -d "$hashercache_dir"/aptbox -o ! -d "$hashercache_dir"/cache ]; then
	echo "arch_create_mainrepo_hashercache: $arch: invalid cache detected at $hashercache_dir"
	exit 1
    fi
    echo "$hashercache_dir"/aptbox/apt-cache unmet
    if ! "$hashercache_dir"/aptbox/apt-cache unmet | LANG=C autorepo-buildhelper-sort-unmets > "$hashercache_dir"/unmets.buildrepo.$arch; then
       echo "arch_create_mainrepo_hashercache: $arch: unmets cache failed"
       exit 1
    fi
    hsh-rmchroot --number 0 "$hashercache_dir"
}


arch_autorepo_build()
{
    local arch exit_build_status
    exit_build_status=0
    arch=$1
    shift;
    aptconf_for_arch=
    get_apt_conf_for_arch $arch build
    autorepo_set_logdir
    set_hasher_dir "$arch"
    set_hashercache_dir "$arch"
    local firstfile
    firstfile="$1"
    if ! [ -f "$firstfile" ]; then
	echo "Fatal: Internal error: arch_autorepo_build: expected list of files, got" "$@"
	exit 1
    # unfortunately, test will fail for *.tar -- we need spec extraction
    elif [ "${firstfile%%.rpm}" != "$firstfile" ]; then
	local ExcludeArch ExclusiveArch
	ExcludeArch=`rpmquery --qf '[%{ExcludeArch}\n]' -p "$firstfile"`
	if [ -n "$ExcludeArch" ] && rpmquery --qf '[%{ExcludeArch}\n]' -p "$firstfile" | grep -E "^$arch$" >/dev/null; then
	    touch "$logdir"/exclude.$arch
	    return $exit_build_status
	fi
	ExclusiveArch=`rpmquery --qf '[%{ExclusiveArch}\n]' -p "$firstfile"`
	if [ -n "$ExclusiveArch" ] && ! rpmquery --qf '[%{ExclusiveArch}\n]' -p "$firstfile" | grep -E "^$arch$" >/dev/null; then
	    touch "$logdir"/exclude.$arch
	    return $exit_build_status
	fi
    fi
    if [ -n "$use_hashercache" -a -d "$hashercache_dir" ]; then
	hsh-clone-workdir "$hashercache_dir" "$current_hasherdir"
	$arch hsh-clone-initroot "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${hashernumber:+--number $hashernumber} >"$logdir"/hsh.log.$arch 2>&1
	echo $arch hsh-rebuild "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${AR_BUILDER_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch "$@"
	$arch hsh-rebuild "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${AR_BUILDER_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch "$@" >>"$logdir"/hsh.log.$arch 2>&1
    else
	mkdir -p "$current_hasherdir"
	echo $arch hsh "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${AR_BUILDER_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch --apt-config=$aptconf_for_arch "$@"
	$arch hsh "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${AR_BUILDER_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch --apt-config=$aptconf_for_arch "$@" >"$logdir"/hsh.log.$arch 2>&1
    fi
    if [ "$?" -gt 0 ]; then
	exit_build_status=3
	# in case we have a *.tar -- can't rpmquery it above
	if grep -E "^error: Architecture is (not in|ex)cluded: $arch" "$logdir"/hsh.log.$arch >/dev/null; then
	    touch "$logdir"/exclude.$arch
	    arch_cleanup_chroot $arch
	    return $exit_build_status
	fi
    fi
    if [ "$exit_build_status" -eq 0 ]; then
	echo "$arch: build success"
	touch "$logdir"/build.success.$arch
    fi
    # hsh-rebuild does not cleanup
    if [ -d "$current_hasherdir"/chroot ]; then
	arch_cleanup_chroot $arch
    fi
    return $exit_build_status
}

arch_autorepo_init_chroot()
{
    local arch logfile
    arch="$1"
    logfile="$2"
    shift; shift
    local init_status
    init_status=0
    aptconf_for_arch=
    get_apt_conf_for_arch $arch build
    set_hasher_dir "$arch"
    if [ -n "$use_hashercache" -a -d "$hashercache_dir" ]; then
	hsh-clone-workdir "$hashercache_dir" "$current_hasherdir"
	echo $arch hsh-clone-initroot "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${hashernumber:+--number $hashernumber}
	$arch hsh-clone-initroot "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} ${hashernumber:+--number $hashernumber} >"$logfile" 2>&1 || init_status=$?
    else
	mkdir -p "$current_hasherdir"
	echo $arch hsh "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} --target=$arch --with-stuff ${hashernumber:+--number $hashernumber} --apt-config=$aptconf_for_arch --initroot-only
	$arch hsh "$current_hasherdir" ${AR_BUILDER_HASHER_OPTS:-} --target=$arch --with-stuff ${hashernumber:+--number $hashernumber} --apt-config=$aptconf_for_arch --initroot-only > "$logfile" 2>&1 || init_status=$?
    fi
    if [ "$init_status" -ne 0 ]; then
	echo "Internal error: initroot failed"
	exit 1
    else
	set_initroot_stamp $arch
	touch "$initroot_stamp"
    fi
    return $init_status
}

arch_autorepo_calculate_unmets()
{
    local arch
    arch=$1
    autorepo_set_logdir
    mkdir -p "$logdir"
    local difffile
    difffile="$logdir"/unmets.diff.$arch
    rm -f "$difffile"
    if [ -e "$logdir"/exclude.$arch ]; then
	return 0
    fi
    set_hashercache_dir "$arch"
    set_hasher_dir "$arch"
    if ! [ -x $current_hasherdir/aptbox/apt-cache ]; then
	arch_autorepo_init_chroot $arch "$logdir"/unmets-init.$arch.log || return 1
    fi
    $current_hasherdir/aptbox/apt-cache unmet | LANG=C autorepo-buildhelper-sort-unmets > "$logdir"/unmets.$arch.new || return 1
    if [ -s "$logdir"/unmets.$arch.new ]; then
	if [ -n "$use_hashercache" -a -e "$hashercache_dir"/unmets.buildrepo.$arch ]; then
	    cat "$hashercache_dir"/unmets.buildrepo.$arch > "$logdir"/unmets.$arch.old
	else
	    mv $current_hasherdir/aptbox/etc/apt/sources.list{,.orig}
	    # not good; what if we add rpm-dir ourselves?
	    #sed -i '/^rpm-dir file:/d $current_hasherdir/aptbox/etc/apt/sources.list
	    local srclistremote
	    srclistremote="$AR_BUILDER_LOCAL_APT_DIR/sources.list.$arch"
	    cp "$srclistremote" $current_hasherdir/aptbox/etc/apt/sources.list
	    $current_hasherdir/aptbox/apt-cache unmet | LANG=C autorepo-buildhelper-sort-unmets > "$logdir"/unmets.$arch.old || return 1
	    mv -f $current_hasherdir/aptbox/etc/apt/sources.list{.orig,}
	fi
	diff -U0 "$logdir"/unmets.$arch.{old,new} > "$difffile" ||:
	if ! grep -E -v '^(\+\+\+|@@|-)' $difffile; then
	    # unmets are purely removed, not added
	    rm -f "$difffile"
	    touch "$difffile"
	fi
    else
	rm -f "$difffile"
	touch "$difffile"
    fi
    rm -f "$logdir"/unmets.$arch.{old,new} "$logdir"/unmets-init.$arch.log
    if [ -s "$difffile" ]; then
	arch_cleanup_chroot $arch
	return 4
    fi
    touch "$logdir"/unmets.success.$arch
    echo "$arch: unmets success"
    return 0
}

arch_autorepo_install_test_install_file()
{
    local arch rpm logfile
    arch="$1"
    rpm="$2"
    logfile="$3"
    shift; shift; shift
    local install_status initroot_created
    install_status=0
    initroot_created=
    set_hasher_dir "$arch"
    mkdir -p "$current_hasherdir"
    set_initroot_stamp $arch
    if ! [ -f "$initroot_stamp" ]; then
	# trying to reuse existing chroot; not doing --initroot-only if already done
	arch_autorepo_init_chroot $arch "$logfile"
	initroot_created=yes
    fi
    echo $arch hsh-install ${AR_BUILDER_HASHER_OPTS:-} "$current_hasherdir" "$rpm"
    $arch hsh-install ${AR_BUILDER_HASHER_OPTS:-} "$current_hasherdir" "$rpm" >> "$logfile" 2>&1
    if [ "$?" -gt 0 ]; then
	if [ -n "$initroot_created" ]; then
	    install_status=5
	else
	    # reinitializing chroot as installation failed in shared chroot
	    arch_autorepo_init_chroot $arch "$logfile"
	    echo $arch hsh-install ${AR_BUILDER_HASHER_OPTS:-} "$current_hasherdir" "$rpm"
	    $arch hsh-install ${AR_BUILDER_HASHER_OPTS:-} "$current_hasherdir" "$rpm" >> "$logfile" 2>&1
	    if [ "$?" -gt 0 ]; then
		install_status=5
	    fi
	fi
    fi
    return $install_status
}

arch_autorepo_install_test()
{
    local arch exit_install_status
    arch=$1
    exit_install_status=0
    autorepo_set_logdir
    if ! [ -e "$logdir"/exclude.$arch ]; then
	set_hasher_dir "$arch"
	for rpm in "$current_hasherdir"/repo/$arch/RPMS.hasher/*.rpm; do
	    if [ -e "$rpm" ]; then # -e because w/o nullglob
		logfile="$logdir"/`basename $rpm`-${arch}-install.log
		if arch_autorepo_install_test_install_file $arch "$rpm" "$logfile"; then
		    rm -f "$logfile"
		else
		    echo "$rpm: install test failed"
		    exit_install_status=5
		fi
	    fi
	done
	if [ "$exit_install_status" -eq 0 ]; then
	    touch "$logdir"/install.success.$arch
	    set_initroot_stamp $arch
	    rm -f "$initroot_stamp"
	    echo "$arch: install success"
	fi
    fi
    arch_cleanup_chroot $arch
    if [ -n "$use_hashercache" -a -d "$hashercache_dir" ]; then
	rm -rf "$current_hasherdir"/{aptbox,cache}
    fi
    return $exit_install_status
}

arch_autorepo_prepare_build_for_move()
{
    local arch
    arch=$1
    autorepo_set_logdir
    mkdir -p "$logdir"/SRPMS.hasher
    set_hasher_dir "$arch"
    repo="$current_hasherdir"/repo
    if [ -d $repo/SRPMS.hasher ]; then
	mv -f -t "$logdir"/SRPMS.hasher $repo/SRPMS.hasher/* 2>/dev/null ||:
	rmdir $repo/SRPMS.hasher ||:
    fi
    if [ -d $repo/$arch ]; then
	if true "in future noarch compare test passed"; then
	    mkdir -p "$logdir"/noarch/RPMS.hasher
	    mv -f -t "$logdir"/noarch/RPMS.hasher $repo/$arch/RPMS.hasher/*.noarch.rpm 2>/dev/null ||:
	fi
	mv $repo/$arch "$logdir"
    fi
    [ -d $repo ] && rmdir $repo ||:
    rmdir -p "$logdir"/{$arch,noarch}/RPMS.hasher 2>/dev/null ||:
}

autorepo_merge_arch_success()
{
    autorepo_set_logdir
    local build_status unmets_status install_status
    build_status=0
    unmets_status=0
    install_status=0
    for arch in $GB_ARCH; do
	if ! [ -e "$logdir"/exclude.$arch ]; then
	    [ -e "$logdir"/build.success.$arch ] || build_status=1
	    [ -e "$logdir"/unmets.success.$arch ] || unmets_status=1
	    [ -e "$logdir"/install.success.$arch ] || install_status=1
	fi
    done

    if [ "$build_status" -gt 0 ]; then
	echo "build test failed."
	return $build_status
    fi
    touch "$logdir"/build.success
    rm -f "$logdir"/build.success.*

    if [ "$unmets_status" -gt 0 ]; then
	echo "unmets test failed."
	return $unmets_status
    fi
    touch "$logdir"/unmets.success
    rm -f "$logdir"/unmets.success.* "$logdir"/unmets.diff.*

    if [ "$install_status" -gt 0 ]; then
	echo "install test failed."
	return $install_status
    fi
    touch "$logdir"/install.success
    rm -f "$logdir"/install.success.*
    return 0
}
