#!/bin/sh -efu
#
# Copyright (C) 2008  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2006-2008  Sergey Vlasov <vsu@altlinux.org>
#
# kernel-build-tools common shell functions.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#

PROG="${0##*/}"
PROG_VERSION='0.109'

LF='
'

MKAPTBOX=/usr/share/hasher/mkaptbox
[ -x "$MKAPTBOX" ] || MKAPTBOX=mkaptbox

verbose=

message()
{
	printf %s\\n "$PROG: $*" >&2
}

fatal()
{
	printf %s\\n "$PROG: $*" >&2
	exit 1
}

verbose=
verbose()
{
	[ -n "$verbose" ] || return 0
	message "$@"
}

show_usage()
{
        [ -z "$*" ] || message "$*"
        echo "Try '$PROG --help' for more information." >&2
        exit 1
}

workdir=
exit_handler()
{
	local rc=$?
	trap - EXIT
	[ -z "$workdir" ] || rm -rf "$workdir"
	exit $rc
}

signal_handler()
{
	echo "$PROG: Interrupted" >&2
	exit 1
}

trap exit_handler EXIT
trap signal_handler SIGHUP SIGPIPE SIGINT SIGTERM SIGQUIT

workdir=`mktemp -td kbuild.XXXXXXXXXX` ||
	fatal "failed to create working directory"

shell_quote_args()
{
	local arg
	for arg; do
		if [ -z "${arg##*[\"\$\`\\]*}" ]; then
			arg="$(printf %s "$arg" | sed -e 's/["$`\]/\\&/g')"
		fi
		printf '"%s" ' "$arg"
	done
}

# make_rpm_dirs
#
# Create directories for building RPM packages.
#
make_rpm_dirs()
{
	mkdir -p $verbose "$rpm_logdir"
	mkdir -p $verbose "$rpm_rpmdir"
	mkdir -p $verbose "$rpm_rpmdir/$rpm_target"
	mkdir -p $verbose "$rpm_rpmdir/noarch"
	mkdir -p $verbose "$rpm_srcrpmdir"
	mkdir -p $verbose "$rpm_builddir"
	mkdir -p $verbose "$rpm_tmppath"
	mkdir -p $verbose "$rpm_rootdir"
}

init_rpmdb()
{
	[ -d "$rpm_dbpath" ] && return ||:

	message "Initializing RPM database..."
	mkdir -p $verbose "$rpm_rootdir/var/lib"
	cp -a /var/lib/rpm "$rpm_rootdir/var/lib"
	local to_remove
	to_remove="$(
		rpmquery --dbpath "$rpm_dbpath" -qa |
			grep '^kernel' |
			grep -v kernel-build-tools
	)"
	if [ -n "$to_remove" ]; then
		rpm --dbpath "$rpm_dbpath" --define '__dbi_cdb nofsync' \
			--justdb -e --nodeps $to_remove
	fi
	message "RPM database initialized"
}

install_packages()
{
	local installed

	init_rpmdb

	installed="$(
		arch="$(printf %s "$rpm_base_target" | sed -e 's/^i?86$/i?86/')"
		find "$rpm_rpmdir" "$TOP/source/" -xtype f \
			\( \
				-name "*.noarch.rpm" \
				-o -name "*.$arch.rpm" \
				-o -name "*.$rpm_target.rpm" \
			\) \
			-not \( \
				-name "kernel-image-*" \
				-o -name "kernel-modules-*" \
			\) |
		while read -r pkg; do
			nvr="$(rpmquery --qf "%{NAME}-%{VERSION}-%{RELEASE}" -p "$pkg")"
			installed="$(rpmquery --dbpath "$rpm_dbpath" -q "$nvr" 2>/dev/null ||:)"
			if [ -z "$installed" ]; then
				cd "$rpm_rootdir"
				message "Unpacking $(basename -- "$pkg")"
				rpm2cpio "$pkg" | 
				cpio -idm --no-absolute-filenames --quiet
				printf %s\\n "$pkg"
			fi
		done
	)"
	if [ -n "$installed" ]; then
		message "Updating RPM database..."
		printf %s "$installed" |
		xargs -r rpm -Uvh --dbpath "$rpm_dbpath" \
			--define '__dbi_cdb nofsync' \
			--force --justdb --nodeps --
		message "RPM database updated"
	fi
}

get_modules_to_build()
{
	local branch="$1" && shift

	local result= module

	GIT_DIR="$TOP/kernel/.git" git cat-file blob "$branch:modules.build" \
			>"$workdir/modules.build" 2>/dev/null ||
		fatal "No modules.build found for $branch"
	while read -r module; do
		case "$module" in
			""|\#*)
				continue
				;;
		esac
		result="$result$LF$module"
	done <"$workdir/modules.build"

	printf %s "$result"
}

# make_aptbox [<mkaptbox-options>...]
#
# Create aptbox in $hsh_workdir.
make_aptbox()
{
	rm -rf -- "$hsh_workdir/aptbox" || \
		fatal "cannot remove $hsh_workdir/aptbox"
	eval "$(
		shell_quote_args "$MKAPTBOX" -f "$@"
	) $hsh_options $(
		shell_quote_args --target "$rpm_target" "$hsh_workdir"
	) >/dev/null" || \
		fatal "mkaptbox failed"
}

get_package_evr()
{
	rpmquery --qf '%|SERIAL?{%{SERIAL}:}|%{VERSION}-%{RELEASE}' -p "$1"
}

# latest_package_evr <package-name>
#
# Using aptbox created by make_aptbox, find latest version of the specified
# package.
latest_package_evr()
{
	"$apt_cache" show "$1" | \
	sed -ne 's/^Version: \(.*\)$/\1/p' | \
	(
		latest=
		while read evr; do
			if [ -z "$latest" ]; then
				latest="$evr"
			elif [ `rpmevrcmp "$evr" "$latest"` = 1 ]; then
				latest="$evr"
			fi
		done
		[ -n "$latest" ] && echo "$latest"
	)
}

kernel_version_code()
{
	local release="$1" && shift

	local kver="$(printf %s "$release" | cut -d- -f1)"
	local version="$(printf %s "$kver" | cut -d. -f1)"
	local patchlevel="$(printf %s "$kver" | cut -d. -f2)"
	local sublevel="$(printf %s "$kver" | cut -d. -f3)"

	# from kernel Makefile
	expr "$version" \* 65536 + "$patchlevel" \* 256 + "$sublevel"
}

# parse_kernel_evr <EVR>
#
# Parse [epoch:]version-release string and set global variables:
#  - KERNEL_EPOCH
#  - KERNEL_VERSION
#  - KERNEL_RELEASE
#  - KERNEL_BUILDRELEASE
#  - KERNEL_CODE
parse_kernel_evr()
{
	local evr="$1" && shift

	local version_release="${evr#*:}"
	if [ "$version_release" = "$evr" ]; then
		KERNEL_EPOCH=
	else
		KERNEL_EPOCH="${evr%%:*}"
	fi
	KERNEL_VERSION="${version_release%-*}"
	KERNEL_RELEASE="${version_release##*-}"
	KERNEL_BUILDRELEASE="${KERNEL_RELEASE#alt}"
	KERNEL_CODE="$(kernel_version_code "$KERNEL_VERSION")"
}

# subst_module_spec <spec>
#
# Substitute module spec template keywords (@kversion@, @krelease@, ...) with
# values specified in global variables $KERNEL_VERSION, $KERNEL_RELEASE, ...
#
subst_module_spec()
{
	local spec="$1" && shift

	subst "s,@kversion@,$KERNEL_VERSION," "$spec"
	subst "s,@krelease@,$KERNEL_RELEASE," "$spec"
	subst "s,@kflavour@,$KERNEL_FLAVOUR," "$spec"

	local arch
	[ "$KERNEL_FLAVOUR" = "std-pae" ] &&
		arch="%ix86" ||
		arch="%ix86 x86_64"
	subst "s,@kernelarch@,$arch," "$spec"

	if fgrep -qs "@kreleasebuild@" "$spec"; then
		subst "s,@kreleasebuild@,$KERNEL_CODE.$KERNEL_BUILDRELEASE," \
			"$spec"
	else
		subst "s,^release:.*$,&.$KERNEL_CODE.$KERNEL_BUILDRELEASE,1I" \
			"$spec"
		add_changelog \
			-e "- Build for kernel-image-$KERNEL_FLAVOUR-$KERNEL_VERSION-$KERNEL_RELEASE." \
			"$spec"
	fi
}

# Default values for configuration variables.  Can be overridden in config.sh.
#
rpm_target="$(uname -m | sed 's/^\(i.86\|pentium4\|athlon\|k6\)$/i586/')"
rpm_builddir="$TOP/tmp/BUILD"
rpm_tmppath="$TOP/tmp/TMP"
rpm_rootdir="$TOP/tmp/root"
rpm_outdir="$TOP/out"
rpm_logdir="$rpm_outdir/logs"
rpm_srcrpmdir="$rpm_outdir/SRPMS"
rpm_rpmdir="$rpm_outdir/RPMS"
hsh_options=
rpmbuild_options=
hsh_workdir=

if [ -s "config.sh" ]; then
	. config.sh
fi

#
# hasher_config_param <option>
#
# try to read value of an <option> from ~/.hasher/config
#
hasher_config_param()
{
	if [ -n "$1" -a -r "$HOME/.hasher/config" ]; then
		eval local "$1="
		( . "$HOME/.hasher/config"; eval printf '%s' "\$$1" )
	fi
}

# Set final values of configuration variables after processing command line
# options.
#
finish_setup()
{
	# If hsh_workdir was not set, try to find it automatically
	[ -n "$hsh_workdir" ] ||
		hsh_workdir=$(hasher_config_param 'workdir')
	if [ -z "$hsh_workdir" ]; then
		local prefix
		prefix="$(
			. /etc/hasher-priv/system
			if [ -r /etc/hasher-priv/user.d/"$USER" ]; then
				. /etc/hasher-priv/user.d/"$USER"
			fi
			printf %s "$prefix"
		)" ||
			fatal "Failed to find hasher prefix"
		if [ "${prefix%%:*}" = "$HOME" ]; then
			hsh_workdir="$HOME/hasher"
		else
			hsh_workdir="$prefix/$USER/hasher"
		fi
	fi

	apt_cache="$hsh_workdir/aptbox/apt-cache"

	RPMBUILD="$(
		shell_quote_args rpmbuild \
			--define "_builddir $rpm_builddir" \
			--define "_tmppath $rpm_tmppath" \
			--define "_srcrpmdir $rpm_srcrpmdir" \
			--define "_rpmdir $rpm_rpmdir" \
			--target "$rpm_target"
	) $rpmbuild_options"

	rpm_dbpath="$rpm_rootdir/var/lib/rpm"

	rpm_base_target="$(
		printf %s "$rpm_target" |
		sed 's/^\(i.86\|pentium[234]\|athlon\(_xp\)?\|k6\)$/i586/'
	)"
	kernel_arch="$(printf %s "$rpm_base_target" | sed 's/^i586$/i386/')"
}
