#!/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-config, but those can be separated from AUTOREPO_
# $AUTOREPO_HASHER_PREFIX
# $AUTOREPO_HASHER_OPTS
# $AUTOREPO_HASHER_BUILD_OPTS
# $AUTOREPO_LOCAL_APT_DIR
# $AUTOREPO_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=${AUTOREPO_HASHER_PREFIX:-~/hasher}${hashernumber:-}.$arch
}

set_hashercache_dir()
{
    local arch
    arch=$1
    hashercache_dir=${AUTOREPO_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
    repobranch=$AUTOREPO_BRANCH
    aptconf_for_arch=
    for conffile in \
    "$AUTOREPO_LOCAL_APT_DIR/apt.conf.$repobranch.$arch" \
    "$AUTOREPO_LOCAL_APT_DIR/apt.conf.$arch" \
    "$AUTOREPO_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 $AUTOREPO_LOCAL_APT_DIR:$AUTOREPO_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 difffile
    arch=$1
    difffile=$2
    get_apt_conf_for_arch $arch
    TOPDIR=`mktemp -d --tmpdir autorepo.XXXXXXXXXX`
    WORKDIR=$TOPDIR/WD
    mkdir $WORKDIR
    mkaptbox --apt-config="$aptconf_for_arch" $WORKDIR
    $WORKDIR/aptbox/apt-cache unmet > "$difffile"
    rm -rf "$TOPDIR"
    [ -s "$difffile" ] || rm -f "$difffile"
}

arch_create_mainrepo_hashercache()
{
    arch=$1
    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
    mkdir -p "$hashercache_dir"
    echo $arch hsh "$hashercache_dir" ${AUTOREPO_HASHER_OPTS:-} --target=$arch --with-stuff --number 0 --apt-config=$aptconf_for_arch --initroot-only
    if ! $arch hsh "$hashercache_dir" ${AUTOREPO_HASHER_OPTS:-} --target=$arch --with-stuff --number 0 --apt-config=$aptconf_for_arch --initroot-only >/dev/null 2>&1; then
	echo "$arch: init parallel hasher cache failed"
	exit 1
    fi
    echo "$hashercache_dir"/aptbox/apt-cache unmet
    if ! "$hashercache_dir"/aptbox/apt-cache unmet > "$hashercache_dir"/unmets.buildrepo.$arch; then
       echo "$arch: unmets cache failed"
       exit 1
    fi
    hsh-rmchroot --number 0 "$hashercache_dir"
    rm -rf "$hashercache_dir"/aptbox
    if ! $arch mkaptbox "$hashercache_dir" ${AUTOREPO_HASHER_OPTS:-} --target=$arch --with-stuff --apt-config=$aptconf_for_arch >/dev/null 2>&1; then
	echo "$arch: init parallel hasher cache failed"
	exit 1
    fi
}


arch_autorepo_build()
{
    local arch exit_build_status
    exit_build_status=0
    arch=$1
    shift;
    aptconf_for_arch=
    get_apt_conf_for_arch $arch
    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" | egrep "^$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" | egrep "^$arch$" >/dev/null; then
	    touch "$logdir"/exclude.$arch
	    return $exit_build_status
	fi
    fi
    if [ -n "$use_hashercache" -a -d "$hashercache_dir" -a -d "$hashercache_dir"/cache  -a -d "$hashercache_dir"/aptbox -a ! -d "$hashercache_dir"/chroot ]; then
	if [ -d "$current_hasherdir"/chroot ]; then
	    arch_cleanup_chroot $arch
	fi
	if [ -d "$current_hasherdir" ]; then
	    rm -rf "$current_hasherdir"
	fi
	mkdir -p "$current_hasherdir"
	if ! cp -rl "$hashercache_dir"/{aptbox,cache} "$current_hasherdir"; then
	    echo "Internal error: cloning $hashercache_dir to $current_hasherdir failed."
	    exit 1
	fi
	rm -f \
	   "$current_hasherdir"/pid \
	   "$current_hasherdir"/aptbox/var/cache/apt/archives/lock \
	   "$current_hasherdir"/aptbox/var/lib/apt/lists/lock \
	   "$current_hasherdir"/aptbox/var/lib/rpm/.rpm.lock \
	   "$current_hasherdir"/aptbox/var/lib/rpm/.dbenv.lock
	touch \
	   "$current_hasherdir"/pid \
	   "$current_hasherdir"/aptbox/var/cache/apt/archives/lock \
	   "$current_hasherdir"/aptbox/var/lib/apt/lists/lock \
	   "$current_hasherdir"/aptbox/var/lib/rpm/.rpm.lock \
	   "$current_hasherdir"/aptbox/var/lib/rpm/.dbenv.lock
	# TODO: update "$current_hasherdir" apt config to be with-stuff
	sed -i "s,$hashercache_dir,$current_hasherdir,g" \
	    "$current_hasherdir"/aptbox/apt-cache \
	    "$current_hasherdir"/aptbox/apt-config \
	    "$current_hasherdir"/aptbox/apt-get \
	    "$current_hasherdir"/aptbox/regenbasedir \
	    "$current_hasherdir"/aptbox/setarch \
	    "$current_hasherdir"/aptbox/etc/apt/apt.conf \
	    "$current_hasherdir"/aptbox/etc/apt/sources.list
	hsh-mkchroot "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} ${hashernumber:+--number $hashernumber} >"$logdir"/hsh.log.$arch 2>&1
	hsh-initroot "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} ${hashernumber:+--number $hashernumber} >>"$logdir"/hsh.log.$arch 2>&1
	echo $arch hsh-rebuild "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} ${AUTOREPO_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch "$@"
	$arch hsh-rebuild "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} ${AUTOREPO_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch "$@" >>"$logdir"/hsh.log.$arch 2>&1
    else
	mkdir -p "$current_hasherdir"
	echo $arch hsh "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} ${AUTOREPO_HASHER_BUILD_OPTS:-} ${hashernumber:+--number $hashernumber} --target=$arch --apt-config=$aptconf_for_arch "$@"
	$arch hsh "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} ${AUTOREPO_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 egrep "^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
    aptconf_for_arch=
    get_apt_conf_for_arch $arch
    set_hasher_dir "$arch"
    mkdir -p "$current_hasherdir"
    echo $arch hsh "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} --target=$arch --with-stuff ${hashernumber:+--number $hashernumber} --apt-config=$aptconf_for_arch --initroot-only
    $arch hsh "$current_hasherdir" ${AUTOREPO_HASHER_OPTS:-} --target=$arch --with-stuff ${hashernumber:+--number $hashernumber} --apt-config=$aptconf_for_arch --initroot-only > "$logfile" 2>&1
    init_status=$?
    if [ "$init_status" -eq 0 ]; then
	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 > "$logdir"/unmets.$arch.new || return 1
    if [ -s "$logdir"/unmets.$arch.new ]; then
	if [ -n "$use_hashercache" -a -e "$hashercache_dir"/unmets.buildrepo.$arch -a -d "$hashercache_dir"/aptbox -a ! -d "$hashercache_dir"/chroot ]; 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="$AUTOREPO_LOCAL_APT_DIR/sources.list.$arch"
	    cp "$srclistremote" $current_hasherdir/aptbox/etc/apt/sources.list
	    $current_hasherdir/aptbox/apt-cache unmet > "$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 ! egrep -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 ${AUTOREPO_HASHER_OPTS:-} "$current_hasherdir" "$rpm"
    $arch hsh-install ${AUTOREPO_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 ${AUTOREPO_HASHER_OPTS:-} "$current_hasherdir" "$rpm"
	    $arch hsh-install ${AUTOREPO_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
    #
    # TODO: hack! use hsh-initchroot !
    #
    #
#    if [ -n "$use_hashercache" -a -d "$hashercache_dir" -a -d "$hashercache_dir"/cache  -a -d "$hashercache_dir"/aptbox -a ! -d "$hashercache_dir"/chroot ]; then
#	rm -rf "$current_hasherdir"
#    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
    mv -f -t "$logdir"/SRPMS.hasher $repo/SRPMS.hasher/* 2>/dev/null ||:
    rmdir $repo/SRPMS.hasher ||:
    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"
    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
}
