#!/bin/bash

################################################################
#
# Copyright (c) 1995-2014 SUSE Linux Products GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
################################################################

build_host_arch() {
    : ${BUILD_HOST_ARCH:=`uname -m`}
    # the linux kernel only knows armv7l, armv7hl is a userland definition
    test armv8l == "$BUILD_HOST_ARCH" && BUILD_HOST_ARCH=armv8hl
    test armv7l == "$BUILD_HOST_ARCH" && BUILD_HOST_ARCH=armv7hl
    test armv6l == "$BUILD_HOST_ARCH" && BUILD_HOST_ARCH=armv6hl

    BUILD_INITVM_ARCH="$BUILD_HOST_ARCH"
    # avoid multiple initvm.* helpers for i586 and i686
    test i686 != "$BUILD_INITVM_ARCH" || BUILD_INITVM_ARCH=i586
}

extend_build_arch() {
    case $BUILD_ARCH in
      aarch64) BUILD_ARCH="aarch64:aarch64_ilp32:armv8l" ;;
      aarch64_ilp32) BUILD_ARCH="aarch64_ilp32:aarch64:armv8l" ;;
      armv8hl) BUILD_ARCH="armv8hl:armv8l:armv7hl:armv7l:armv6hl:armv6l:armv5tel" ;;
      armv8l) BUILD_ARCH="armv8l:armv7hl:armv7l:armv6hl:armv6l:armv5tel" ;;
      armv7hl) BUILD_ARCH="armv7hl:armv7l:armv6hl:armv6l:armv5tel" ;;
      armv7l) BUILD_ARCH="armv7l:armv6l:armv5tel" ;;
      armv6hl) BUILD_ARCH="armv6hl:armv6l:armv5tel" ;;
      armv6l) BUILD_ARCH="armv6l:armv5tel" ;;
      armv5tel) BUILD_ARCH="armv5tel" ;;
      loongarch64) BUILD_ARCH="loongarch64" ;;
      m68k) BUILD_ARCH="m68k" ;;
      mips64) BUILD_ARCH="mips64:mips" ;;
      mips) BUILD_ARCH="mips" ;;
      i686) BUILD_ARCH="i686:i586:i486:i386" ;;
      i586) BUILD_ARCH="i586:i486:i386" ;;
      i486) BUILD_ARCH="i486:i386" ;;
      i386) BUILD_ARCH="i386" ;;
      ia64) BUILD_ARCH="ia64" ;;
      parisc64) BUILD_ARCH="hppa64:hppa" ;;
      parisc) BUILD_ARCH="hppa" ;;
      ppc) BUILD_ARCH="ppc" ;;
      ppc64) BUILD_ARCH="ppc64:ppc" ;;
      ppc64le) BUILD_ARCH="ppc64le" ;;
      riscv64) BUILD_ARCH="riscv64" ;;
      s390x) BUILD_ARCH="s390x:s390" ;;
      s390) BUILD_ARCH="s390" ;;
      sparc64v) BUILD_ARCH="sparc64v:sparc64:sparcv9v:sparcv9:sparcv8:sparc" ;;
      sparc64) BUILD_ARCH="sparc64:sparcv9:sparcv8:sparc" ;;
      sparcv9v) BUILD_ARCH="sparcv9v:sparcv9:sparcv8:sparc" ;;
      sparcv9) BUILD_ARCH="sparcv9:sparcv8:sparc" ;;
      sparcv8) BUILD_ARCH="sparcv8:sparc" ;;
      sparc) BUILD_ARCH="sparc" ;;
      x86_64) BUILD_ARCH="x86_64:i686:i586:i486:i386" ;;
    esac
}

set_build_arch() {
    build_host_arch
    if test -z "$BUILD_ARCH" ; then
	BUILD_ARCH="$BUILD_HOST_ARCH"
    fi
    extend_build_arch
    if test "$BUILD_ARCH" != "${BUILD_ARCH#i686}" ; then
	cpuflags=`grep ^flags /proc/cpuinfo`
	cpuflags="$cpuflags "
	if test "$cpuflags" = "${cpuflags/ cx8 /}" -o "$cpuflags" = "${cpuflags/ cmov /}"; then
	    echo "Your cpu doesn't support i686 rpms. Exit."
	    cleanup_and_exit 1
	fi
    fi
}

check_exit() {
    if test -e $BUILD_ROOT/exit; then
	echo "exit ..."
	cleanup_and_exit 1
    fi
}

internal_set_sb2_settings() {
        SB2_TOOLCHAIN="/unknown"
        SB2_INSTALLMODE="obs-rpm-install"
        SB2_DEFAULTMODE="obs-rpm-build"
        SB2_SYSROOT="-C --sysroot=/ -L --sysroot=/"
        SB2_MAPPING_METHOD="both"
        SB2_DEBUG=""
        SB2_QEMU="/unknown"
        while test -n "$1" ; do
	    case "$1" in
		--toolchain)
		    shift
		    SB2_TOOLCHAIN=$1
		    shift
		    ;;
		--installmode)
		    shift
		    SB2_INSTALLMODE=$1
		    shift
		    ;;
		--defaultmode)
		    shift
		    SB2_DEFAULTMODE=$1
		    shift
		    ;;
		--mappingmethod)
		    shift
		    SB2_MAPPING_METHOD=$1
		    shift
		    ;;
		--bootstrap)
		    shift
		    SB2_SYSROOT=""
		    ;;
		--qemu)
		    shift
		    SB2_QEMU=$1
		    shift
		    ;;
		--infologs)
		    shift
		    SB2_DEBUG="-q -L info"
		    ;;
		--debug)
		    shift
		    SB2_DEBUG="-q -d"
		    ;;
		*)
		    break
		    ;;
	    esac
	done
	if [ "x$SB2_TOOLCHAIN" = "x/unknown" ]; then
		echo "Please provide a proper SB2 toolchain in SB2flags. exiting.."
		cleanup_and_exit 1
	fi
}


cleanup_sb2() {
	if [ ! -z "$BUILD_SB2FLAGS" ]; then
		if [ -e /home/abuild/sb2-session-needroot ]; then
			su -c "cd $PWD; /usr/bin/sb2 -t oscbuild -D /home/abuild/sb2-session-needroot" - abuild
		fi
		if [ -e /home/abuild/sb2-session-needuser ]; then
			su -c "cd $PWD; /usr/bin/sb2 -t oscbuild -D /home/abuild/sb2-session-needuser" - abuild
		fi
	fi
}

# chroot-like wrapper, enter_target <mode: needroot|user> $TARGET commands
# needroot - operations that need to be done as root, like unpacking into file system, etc
# needuser - operations that will be done as non-root user

enter_target() {
	if [ -z "$BUILD_SB2FLAGS" ]; then
		if [ x$1 == xneeduser ]; then
			shift
			chroot $BUILD_TARGET su -c "${*}" - $BUILD_USER
		elif [ x$1 == xneedscript ]; then
			shift
			SCRIPT=$1
			shift
			chroot $BUILD_TARGET su -s $SCRIPT $BUILD_USER -- ${*}
		else
			shift
			chroot $BUILD_TARGET ${*}
		fi
	else
		if [ -z "$VM_TYPE" ]; then
			echo "SB2 currently does not work with chroots.."
			cleanup_and_exit 1
		fi
		if [ -z "$SB2_TOOLCHAIN" ]; then
			internal_set_sb2_settings $BUILD_SB2FLAGS
		fi
		if [ ! -e $BUILD_TARGET/.sb2inited ]; then
			ORIG=$PWD
			cd $BUILD_TARGET
			su -c "cd $PWD; /usr/bin/sb2-init $SB2_SYSROOT -c $SB2_QEMU -n -N -m $SB2_DEFAULTMODE -t / oscbuild $SB2_TOOLCHAIN" - abuild
			su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv SBOX_MAPPING_METHOD $SB2_MAPPING_METHOD" - abuild

			cd $ORIG
			touch $BUILD_TARGET/.sb2inited
		fi
		if [ x$1 = xneedscript ]; then
			PARAM=needuser
		else
			PARAM=$1
		fi
        # Take path as set after su -l before entering
		case "$PARAM" in
			needroot)
				shift
				ORIG=$PWD
				cd /target
		                if [ ! -e /home/abuild/sb2-session-needroot ]; then
					SESSION="-S /home/abuild/sb2-session-needroot -t oscbuild"
			        else
					SESSION="-J /home/abuild/sb2-session-needroot"
			        fi
				su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv HOME /root" - abuild
				su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv USER root" - abuild
				su -c "cd $PWD; /usr/bin/sb2 $SESSION $SB2_DEBUG -m $SB2_INSTALLMODE -R ${*}" - abuild				    su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv PATH \'\$PATH\'" - abuild
				RET=$?
				cd $ORIG
				return $RET
				;;
			needuser)
				# Debugging
				shift
				ORIG=$PWD
				cd /target
		                if [ ! -e /home/abuild/sb2-session-needuser ]; then
					SESSION="-S /home/abuild/sb2-session-needuser -t oscbuild"
			        else
					SESSION="-J /home/abuild/sb2-session-needuser"
				fi
				su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv HOME /home/abuild" - abuild
				su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv USER abuild" - abuild
				su -c "cd $PWD; /usr/bin/sb2-config -t oscbuild setenv PATH \'\$PATH\'" - abuild
				su -c "cd $PWD; /usr/bin/sb2 $SESSION $SB2_DEBUG -m $SB2_DEFAULTMODE ${*}" - abuild
				RET=$?
				cd $ORIG
				return $RET
				;;
			*)
				echo "Unknown parameter $1" >&2
				;;
		esac
	fi
}


check_native_arch() {
    local arch="$1"
    local old_build_arch="$BUILD_ARCH"
    BUILD_ARCH="$BUILD_HOST_ARCH"
    extend_build_arch
    BUILD_ARCH=":$BUILD_ARCH:"
    if test "$BUILD_ARCH" != "${BUILD_ARCH/:$arch:/}" ; then
	BUILD_ARCH="$old_build_arch"
	return 0
    fi
    BUILD_ARCH="$old_build_arch"
    return 1
}

set_initvm_name_for_emulator_in() {
    local initvmdir="$1"
    INITVM_NAME=
    # check if the extended host arch contains the build arch
    if check_native_arch "${BUILD_ARCH%%:*}" ; then
	# native supported arch, no emulator needed
	return 1
    fi

    # to run the qemu initialization in the vm, we need to
    # register it with a static program or shell script
    INITVM_NAME="initvm.$BUILD_INITVM_ARCH"
    if test -e "$initvmdir/$INITVM_NAME" -a -e "$initvmdir/qemu-reg" ; then
        # it exists, assume we need to run it
    return 0
    elif test -n "$BUILD_SB2FLAGS" ; then
    return 0
    fi
    INITVM_NAME=

    # XXX: error?
    echo "Warning: cross compile not possible due to missing static binaries. please install build-initvm package for that purpose."
    echo "         check that the right architecture is available for your build host, you need $INITVM_NAME for this one."
    return 1
}

# usage:
# progress_setup LIST
# for I in $LIST; do
#    progress_step LIST
#    action $I 
# done

# $1 name of a textual list
progress_setup() {
    eval "$1__ARRAY__=(\$$1)"
    eval "$1__INDEX__=1"
    eval "$1__LENGTH__=\${#$1__ARRAY__[@]}"
}

# $1 name of a textual list
# $2 optional, printf format for 2 numeric arguments (current, total)
progress_step() {
    local IDX=$1__INDEX__
    local LEN=$1__LENGTH__
    printf "${2-[%d/%d] }" $(($IDX++)) ${!LEN}
}

# umount that does not follow symlinks
buildroot_umount() {
    local d="$1"
    local d2="/$d"
    while test -n "$d2" ; do
        test -L "$BUILD_ROOT$d2" && return
        test -d "$BUILD_ROOT$d2" || return
        d2="${d2%/*}"
    done
    # XXX: use stat -f /dev/pts/ -c %T  to check whether it's mounted and not suppress errors?
    umount -n "$BUILD_ROOT/$d" 2>/dev/null || true
}

# rm that makes sure the file is gone
buildroot_rm() {
    rm -rf "$BUILD_ROOT/$1"
    test -e "$BUILD_ROOT/$1" && cleanup_and_exit 1 "could not remove $BUILD_ROOT/$1"
}

assert_dirs() {
    local d rl
    if test -z "$1" ; then
       set usr sbin usr/bin usr/sbin etc .build .build.oldpackages .init_b_cache .init_b_cache/scripts .init_b_cache/rpms .preinstall_image proc proc/sys proc/sys/fs proc/sys/fs/binfmt_misc sys dev dev/pts dev/shm mnt
    fi
    for d in "$@" ; do
	if test -L "$BUILD_ROOT/$d" ; then
	    rl="$(readlink "$BUILD_ROOT/$d")"
	    test "$d" = sbin -a "x$rl" = "xusr/sbin" && continue
	    test "$d" = sbin -a "x$rl" = "xusr/bin" && continue
	    test "$d" = usr/sbin -a "x$rl" = "xbin" && continue
	    cleanup_and_exit 1 "$d: illegal symlink to $rl"
	else
	    test -e "$BUILD_ROOT/$d" -a ! -d "$BUILD_ROOT/$d" && cleanup_and_exit 1 "$d: not a directory"
	fi
    done
}

assert_dir_path() {
    test "$1" != "${1%/*}" && assert_dir_path "${1%/*}"
    assert_dirs "$1"
}

detect_cache_dir() {
    if test -w /; then
	CACHE_DIR=/var/cache/build
    else
	CACHE_DIR=${XDG_CACHE_HOME:-~/.cache}/opensuse.org/build/cache
    fi
}
