#!/bin/bash
# 2003-2007 Etersoft www.etersoft.ru
# Author: Vitaly Lipatov <lav@etersoft.ru>
# Public domain
# $Id: etersoft-build-functions,v 1.128 2008/06/12 20:43:39 lav Exp $

# 20.06.04, 15.01.05, 28.02.05, 07.03.05, 10.01.08
#
# You can copy this file in ~/.ebconfig for per user settings

### Gettext ###
if which gettext.sh &>/dev/null ; then
	. gettext.sh
else
	eval_gettext()
	{
		echo -n $@
	}
fi

TEXTDOMAIN=etersoft-build-utils
export TEXTDOMAIN
TEXTDOMAINDIR='/usr/share/locale'
export TEXTDOMAINDIR

echog()
{
	if [ "$1" = "-n" ] ; then
		shift
		eval_gettext "$@"
	else
		eval_gettext "$@"; echo
	fi
}
### Gettext ###

#  SUDO  
SUDO="sudo"
# for some shells? not for bash
test -z "$UID" && UID=`id -u`
if [ $UID = "0" ]; then
	SUDO=""
fi

#        
fatal()
{
	echog "Error: $@"
	exit 1
}

#   
warning()
{
	echog "Warning: $@"
}

#    ,    ALT-
is_alt()
{
	test -f /etc/altlinux-release
	return $?
}
# Debian
#is_debian()
#{
#	test -f /etc/debian_version
#	return $?
#}



#   ,  ccache  
export CC=gcc
export CXX=g++
#   - 
#export CPP=g++

if [ "$UID" = "0" ] ; then
	fatal "It is strict recommended do not use these scripts as root"
fi


if which ccache &>/dev/null ; then
	#   
	export GCC_USE_CCACHE=1
#else
#	echog "Note: you can install ccache for compiler cache and speedup compilation"
fi

# It can be overrides in config
DEFAULTARCH=i586
[ `uname -m` = "x86_64" ] && DEFAULTARCH=`uname -m`
NICE="nice"
RPMBUILD=rpmbuild

is_git()
{
	local DIR=$1
	[ -n "$DIR" ] && [ -d "$DIR/.git" ] && return 0
	[ -d ".git" ] && return 0
	return 1
}

# Universal rpmbuild function
# 1st: rpmbuild, hsh, buildreq
# 2nd: spec name(s) (only one if git, we will skip it)
# other parameters
uni_rpmbuild()
{
	local COMMAND=$1
	shift
	local SPECNAME=$1
	local SPECDIR=`dirname $SPECNAME`
	if is_git $SPECDIR ; then
		[ -f "$SPECNAME" ] || fatal "run uni_rpmbuild with spec as 2nd parameter"
		shift # skip spec name
		[ -n "$SPECDIR" ] && [ "$SPECDIR" != "." ] && warning "Spec is not in git dir"
		$NICE gear --commit --rpmbuild -- $COMMAND $@
	else
		$COMMAND $@
	fi
}

RPMDIR="$HOME/RPM"
[ -n "$APTCONF" ] || APTCONF=/etc/apt/apt.conf
#OWNERMAIL=`grep "^%packager" ~/.rpmmacros | head -n 1 | sed -e "s/%packager[ \t]*//g"`
OWNERMAIL=`rpm --eval %packager`

UPLOADDIR="$RPMDIR/upload"
LOGDIR="$RPMDIR/log"
REMOTERPMDIR=
HASHERDIR="$HOME/hasher"
HASHER_NOCHECK=nvr,gpg,packager,buildtime
HASHERARG="--eager-cleanup"
[ -n "$DEBUG" ] && HASHERARG="-v $HASHERARG"

RSYNCSISYPHUS=rsync.altlinux.org::ALTLinux/Sisyphus
RSYNCINCOMING=devel:/incoming
#SSH_KEYFILE=~/.ssh/id_dsa

BUILDSERVER=
#BUILDSERVERPATH="$HASHERDIR/repo/i586/RPMS.hasher"

PKGREPLBASE=/usr/share/etersoft-build-utils/pkgrepl
if [ ! -d "$PKGREPLBASE" ] ; then
	PKGREPLBASE=$(dirname $0)/../pkgrepl
	echo "Warning: Use local replacement dir $PKGREPLBASE"
fi

test -f /etc/rpm/etersoft-build-config && . /etc/rpm/etersoft-build-config
test -f ~/.ebconfig  && . ~/.ebconfig
#test -f ~/.rpmconfig  && { echog "Please rename ~/.rpmconfig to ./ebconfig"; exit 1; }

#eval BUILDROOT=$BUILDROOT
#if [ -n "$BUILDROOT" ] ; then
#	mkdir -p $BUILDROOT
#	ln -snf $BUILDROOT $RPMDIR/tmp
#else
#	BUILDROOT=~/tmp-eee
	#echog "Please set BUILDROOT in ~/.ebconfig"
#fi

#ln -sf $RPMDIR/BUILD/

get_var()
{
#	sed -r -n "s/^(.*)$1:(.*)$/\1/pi"
#	sed -r -n "s/^(.*)$1[ \t]*:[ \t]*(.*)$/\1/pi"
#	grep -i "^$1:" | head -n 1 | sed -e "s/^.*\?[ \t]*:[ \t]*//"
#	grep -i "^$1:" | head -n 1 | perl -pi -e "s/^.*?[ \t]*:[ \t]*//"
	grep -i "^$1:" | head -n 1 | sed -e "s/^[^:]*[ \t]*:[ \t]*//"

}

#         
eval_spec()
{
	# Hack: just print spec if -bE failed
	$RPMBUILD -bE --target $DEFAULTARCH $RPMBUILDARG $@ 2>/dev/null || cat $1
}

get_release()
{
	eval_spec $1 | get_var "Release"
}

set_var()
{
	subst "s|\($2:\).*\$|\1 $3|" $1
}

set_release()
{
	#subst "s|\(Release:\).*\$|\1 $2|" $1
	set_var $1 Release $2
}

set_version()
{
	set_var $1 Version $2
}

subst_namever()
{

	sed -e "s|%{name}|$BASENAME|g
			s|%{version}|$VERSION|g
			s|%name|$BASENAME/|g
			s|%version|$VERSION/|g"
}

# get correct BUILDROOT, run build_rpms_name before!
build_buildroot()
{
	test -z "$BASENAME" && fatal "Use build_buildroot with BASENAME defined"
	# ALT Only?
	BUILDROOT=`rpm --eval %buildroot | subst_namever`
	test -z "$BUILDROOT" && fatal "Fix rpm's buildroot"
	if [ "$BUILDROOT" = "%buildroot" ] ; then
		BUILDROOT="$HOME/tmp/$BASENAME-buildroot"
		warning "Can't get buildroot from RPM, set to $BUILDROOT"
		return 1
	fi
	return 0
}

#      .
#   !
#    Name:   
# TODO:     NAMESPEC,   SPECNAME

build_rpms_name()
{
	local tmprpm CATSPEC NAMESPEC
	#TODO change : to = and execute?
	NAMESPEC=$1
	shift
	test -f "$NAMESPEC" || fatal "Spec $NAMESPEC does not exist"
	# Check for broken mktemp
	if mktemp -V &>/dev/null ; then
		tmprpm=`mktemp || exit 1`
	else
		tmprpm=`mktemp /tmp/$NAME.XXXXXX || exit 1`
	fi
	is_alt && eval_spec $NAMESPEC $@ | grep ":"  >$tmprpm
	is_alt || cat $NAMESPEC >$tmprpm
	CATSPEC="cat $tmprpm"
	BASENAME=$($CATSPEC | get_var "Name")
	RELEASE=$($CATSPEC | get_var "Release")
	VERSION=$($CATSPEC | get_var "Version")
	TARBALLNAME=$($CATSPEC | get_var "Source.*")
	[ -n "$TARBALLNAME" ] && TARBALLNAME=$(basename $TARBALLNAME)
	rm -f $tmprpm
	NAMERPMIN=$BASENAME-$VERSION-$RELEASE.$DEFAULTARCH.rpm
	NAMESRPMIN=$BASENAME-$VERSION-$RELEASE.src.rpm

	#RPMSOURCEDIR=$RPMDIR/SOURCES
	RPMSOURCEDIR=`rpm --eval %_sourcedir | subst_namever`
	local BNS NSS
	BNS="$BASENAME".spec
	NSS=`basename $NAMESPEC`
	test "$NSS" != "$BNS" && warning "BASENAME is not the same as NAMESPEC: $BNS against $SS"

	build_buildroot
}

check_log()
{
	local LOGFILE i RES
	echo
	echo "-------------------------------------------------------"
	echog "Check log for..."
	LOGFILE=$1
	grep 'ld: warning: libstdc++\.so\.5, needed by [^ ]\+, may conflict with libstdc++\.so\.6' $LOGFILE
	RES=$?
	if [ $RES = 0 ] ; then
		echog "Error: libstdc++.so.5/6 conflicts"
		return 1
	fi
	grep 'python-strict' $LOGFILE && warning "python-strict used!!!"
	NC1="command not found"
	NC2="-march=athlon -mtune=athlon-xp"
	#for i in ${NC1} ; do
	grep -- '$NC1' $LOGFILE && warning "'$NC1' in build output (some errors in a scripts)"
	grep -- '$NC2' $LOGFILE && warning "'$NC2' in build output (forbidden) "
	#done
	return 0
}


check_locking()
{
	if [ -f $HASHERDIR/lockdir/lockfile ]
	then
		echo
		echog "Hasher $HASHERDIR in use at `date`"
		echo -n "Wait for unlocking"
		while test -f $HASHERDIR/lockdir/lockfile
		do
			echo -n "."
			sleep $(($RANDOM%5+3))
		done
		echo
	fi
}

# LISTNAMES, options in arg
pack_src_rpm()
{
local i
ARGS=$@
# NOTE: always nodeps
# enable --nodeps for other ENV
#test -n "$MENV" && 
ARGS="$ARGS --nodeps"
for i in $LISTNAMES
do
	
	if [ -z ${i/*rpm/} ] ; then
		# if rpm not spec, guess it is src.rpm
		NAMESRPMIN=$i
	else
		#     
		# BASENAME, RELEASE, VERSION, NAMESRPMIN, NAMERPMIN
		build_rpms_name $i $ARGS
		if [ -n "${SIGN}" ];	then
			echog "Try to packing \$NAMESRPMIN package for sign"
			add_changelog $i
			if [ $? = "0" ]; then
				echog "Spec file \$i had not ChangeLog entry. It have added now, check it and enter your command again."
				echog "Wait for ten seconds."
				sleep 10
				exit 1
			fi
			uni_rpmbuild $RPMBUILD $i -bs $ARGS $NODEPS --target $DEFAULTARCH
		else
			echog "Just packing \$NAMESRPMIN"
			uni_rpmbuild $RPMBUILD $i -bs $ARGS $NODEPS --target $DEFAULTARCH || fatal "Error with rpmbuild"
		fi
	fi
	if [ -f $RPMDIR/SRPMS/$NAMESRPMIN ] ; then
		LISTBUILT="$LISTBUILT$NAMESRPMIN "
	else
		fatal "Can't find '$NAMESRPMIN' in '$RPMDIR/SRPMS'"
	fi
done
test -z "$LISTBUILT" && fatal "Error: List for build is empty. Check if file is exist."
#LISTRPMARGS=`echo ${LISTRPMARGS} | sed -e "s/--nodeps//g"`

}

# Internal
set_target_type()
{
	case "$1" in
		("M23" | "M24" | "M30" | "M40" | "M41" | "DD" | "SS" | "EE")
			MENV="$1"
			return 0;
			;;
	esac
	return 1;
}

# Internal
# ,    :   -M23 -M24   ,   
detect_target_env()
{
local DISTRNAME=`distr_vendor -e`
MENVARG=""
TARGET=`distr_vendor -p`
VENDOR=`distr_vendor -s`

if [ "$VENDOR" = "alt" ] && [ -n "$MENV" ] ; then
	APTCONF=$APTCONF.$MENV
	echog "Target ALT Linux system: \$MENV, use \$APTCONF"
	MENVARG="-$MENV"
else
	echog "Distribution: \$DISTRNAME (\$VENDOR) (target package: \$TARGET)"
fi
}

#    LISTNAMES
#     MENV
#  LISTARGS   
# :
# -   (MENV)
# -  
# -     
parse_cmd_pre()
{
local i
if [ $# -lt 1 ]; then
    [ -n "$Usage" ] && echog "$Usage"
	fatal "Use -h for help."
fi

# Sisyphus by default
MENV=SS
set_target_type $(basename `pwd`)

LISTNAMES=""
LISTARGS=""
OPTINDEX=1
for i in "$@"
do
	#   ,  ,    .
	if [ -f $i ]; then
		LISTNAMES="$LISTNAMES $i"
	else
		set_target_type ${i/-/} || LISTARGS="$LISTARGS $i"
	fi
done
# parse MENV
detect_target_env
}

check_key()
{
	echog "Check access to SSH private key..."
	ssh-add -l || ssh-add $SSH_KEYFILE || return 1
	return 0
}

set_incoming()
{
	INCOMING="Sisyphus"
	case "$1" in
		("M24")
			INCOMING="backports/2.4"
			;;
		("M23")
			INCOMING="backports/2.3"
			;;
		("M30")
			INCOMING="backports/3.0"
			;;
		("M40")
			INCOMING="backports/4.0"
			;;
		("M41")
			INCOMING="backports/4.1"
			;;
		("DD")
			INCOMING="Daedalus"
			;;
		("SS")
			INCOMING="Sisyphus"
			;;
	esac
}

set_incoming_updates()
{
	INCOMING="Sisyphus"
	case "$1" in
		("M24")
			INCOMING="updates/2.4"
			;;
		("M23")
			INCOMING="updates/2.3"
			;;
		("M30")
			INCOMING="updates/3.0"
			;;
		("M40")
			INCOMING="updates/4.0"
			;;
		("M41")
			INCOMING="updates/4.1"
			;;
		("DD")
			INCOMING="Daedalus"
			;;
		("SS")
			INCOMING="Sisyphus"
			;;
	esac
}


add_changelog_helper()
{
	# don't work sometime?
	tty -s || { echo "skip changelog fixing without tty" ; return 1 ; }
	add_changelog -e "$@"
	R=$?
	[ -z "$EDITOR" ] && { echo "skip changelog editing without EDITOR var"; return 1 ; }
	# If changelog sucessfully added
	if [ "$R" = "0" ]; then
		shift
		for SPEC in "$@" ; do
			N=`grep -n '^%changelog' $SPEC | head -n 1 | sed s!:.*!!g`
			# +1 -- comment with date and packager name
			# +2 -- place for edit comments
			${EDITOR} +$(($N + 2)) $SPEC
		done
	fi
	return $R
}

############# Project build & publish section ##############################
# Use only it this order:

# Update from CVS
update_from_cvs()
{
	local REP ROT

	if [ -d CVS ] ; then
		REP=`cat CVS/Repository`
		ROT=`cat CVS/Root`
		echo "Update from CVS... $ROT/$REP"
		cvs -z3 update -dPR || fatal $"Fail during update from CVS..."
	else
		fatal $"CVS dir not found"
	fi
}


# Run inside project dir (named as name) (arg: local for noncvs build)
prepare_tarball()
{
	local LOCALBUILD WDPROJECT TMPWDIR REP ROT
	test -n "$SPECNAME" || SPECNAME=$(basename `pwd`).spec
	build_rpms_name $SPECNAME
	test -z "$BASENAME" && fatal "BASENAME is empty"
	# If someone overrides version
	test -n "$TARBALLVERSION" && VERSION=$TARBALLVERSION
	# project name-version
	NAMEVER=$BASENAME-$VERSION
	WDPROJECT=$(pwd)

	# If local build or configure no exists in orig work dir
	if [ "$1" = "local" ] ||  [ ! -f configure ]; then
		LOCALBUILD=1
	fi

	[ -f configure ] && ! [ -d CVS ] && fatal "Can't work with configure without CVS"
	
	if [ -d CVS ] ; then
		REP=`cat CVS/Repository`
		ROT=`cat CVS/Root`
	fi
	[ -z "$CVSTAGFLAG" ] &&	CVSTAGFLAG=`cvs status VERSION | grep "Sticky Tag" | sed -e "s|(.*||g" | sed -e "s|^.*:[ 	]*||g"`
	[ -n "$CVSTAGFLAG" ] && CVSTAGFLAG="-r $CVSTAGFLAG"
	#echo "Project work dir: $WDPROJECT"
	pushd ..
	# Set as name-version if not get from spec
	[ -z "$TARBALLNAME" ] && TARBALLNAME=$NAMEVER.tar.bz2

	# Prepare temp dir/files for build
	TMPWDIR=$(TMPDIR= mktemp -d -p $(readlink -f $WDPROJECT/../))
	test -z "$TMPWDIR" && fatal "TMPWDIR is empty"
	cd $TMPWDIR || fatal "$TMPWDIR is not accessible"

	# if configure present, do checkout
	if [ -z "$LOCALBUILD" ] ; then
		#test -d $TMPWDIR && fatal "$TMPWDIR is already exists"
		#mkdir -p $TMPWDIR
		echo "Checkout with cvs $CVSTAGFLAG $ROT/$REP into $TMPWDIR"
		CVSROOT=$ROT cvs checkout $CVSTAGFLAG $REP || fatal "Error during checkout"
		mv $REP $NAMEVER || fatal "Can't move to $NAMEVER"
		cd $NAMEVER
		if [ -e ./autogen.sh ] ; then
			 ./autogen.sh
		else
			autoreconf -fisv
		fi
		# TODO remove CVS dirs
		#find ./ -type d -name CVS -print 0 | xargs -0 rm
	else
		#test -L $NAMEVER && rm -f $NAMEVER
		#ln -s $WDPROJECT $NAMEVER || fatal "Can't ln"
		mkdir -p $NAMEVER && cd $NAMEVER || fatal "Can't cd"
		cp -rl $WDPROJECT/* ./ || fatal "Can't create hard links"
		test -e ./Makefile && make distclean

	fi
	# remove unneeded files
	rm -rf autom4te.cache/
	cd -

	echo "Make tarball $TARBALLNAME ... from $(pwd)/$NAMEVER"
	mkdir -p $RPMSOURCEDIR/
	if [ -z "$SKIPTARBALLINTERNALDIR" ] ; then
		$NICE tar cfj $RPMSOURCEDIR/$TARBALLNAME $NAMEVER/* $ETERTARPARAM || fatal "Can't create tarball"
	else
		echo "Skip internal dir in tarball"
		cd $NAMEVER >/dev/null
		$NICE tar cfj $RPMSOURCEDIR/$TARBALLNAME * $ETERTARPARAM || fatal "Can't create tarball"
		cd - >/dev/null
	fi

	# Remove temp. dirs/files from tmpdir
	test -d $NAMEVER && rm -rf $NAMEVER
	rmdir $TMPWDIR
	popd
}

# Publish tarball to Inet (need PUBLICSERVER/PUBLICPATH, TARNAME)
publish_tarball()
{
	echo "Publish $TARNAME to $PUBLICSERVER:$PUBLICPATH "
	if [ -n "$PUBLICSERVER" ] ; then
		ssh $PUBLICSERVER mkdir -p $PUBLICPATH/
		rsync --progress $RPMSOURCEDIR/$TARNAME $PUBLICSERVER:$PUBLICPATH/$TARNAME || fatal "Can't rsync"
		ssh $PUBLICSERVER ln -sf $TARNAME $PUBLICPATH/$BASENAME-current.tar.bz2
	fi
}

# Publish srpm (need ETERDESTSRPM)
publish_srpm()
{
	echo "Copying $NAMESRPMIN to $ETERDESTSRPM"
	export ETERDESTSRPM
	rpmbs -s $* $SPECNAME || fatal "Can't build SRPMS"
	# copied by rpmbs -s
	#cp -f $RPMDIR/SRPMS/$NAMESRPMIN $BUILDHOME/
	#rpm --addsign $BUILDHOME/$NAMESRPMIN || fatal "Error when signing"
	#echo "Make link $ETERDESTSRPM/$BASENAME.src.rpm to fullnamed file"
	#ln -sf $NAMESRPMIN $ETERDESTSRPM/$BASENAME.src.rpm
}

# Get replacement rule for ALT package to local in $1 (scan for files in $@)
# set ALTPKGNAME, TARGETPKGNAME variable
tolocal_anyrepl()
{
	local i REPLRULE WARULES
	local GREP=$1
	shift
	# TODO: fix space removing
	WARULES="s/^ *//g 
		s/ *\$//g 
		s/ *|/|/g 
		s/| */|/g"

	for i in $@ ; do
		REPLRULE=`grep -v "^#" "$i" 2>/dev/null | grep -- "^ *$GREP *|" | sed -e "$WARULES" | head -n1`
		# For broken rule
		echo $REPLRULE | grep "|" >/dev/null || REPLRULE=""
		#REPLRULE=`echo $REPLRULE | sed -r -e 's,|,!,g'`
		ALTPKGNAME=`echo $REPLRULE | cut -d"|" -f1`
		TARGETPKGNAME=`echo $REPLRULE | cut -d"|" -f2`
		# for compatibility
		REPLRULE1=$ALTPKGNAME
		REPLRULE2=$TARGETPKGNAME
		test -n "$REPLRULE" && return 0
	done
	return 1
}

# Clean require names from various stuffs
clean_pkgreq()
{
	local i VAR
	VAR=`cat`
	for i in $VAR ; do
		echo "$i" | grep "[()<=>]" >/dev/null && continue
		echo "$i" | grep "^ *[0-9]\.[0-9]" >/dev/null && continue
		echo -n "$i "
	done
}

# Print list of all build requires in ALT notation
print_buildreq()
{
	eval_spec ${1} | grep "Build.*Req" | sed -e "s|^.*:||g" | clean_pkgreq
}

# Print list of all pkg requires
print_pkgreq()
{
	eval_spec ${1} | grep "Requires" | sed -e "s|^.*:||g" | clean_pkgreq
}

print_pkgrepl_list()
{
	local REPLBASE=$PKGREPLBASE/pkgrepl.

	# VENDOR, TARGET is defined in detect_target_env() func
	[ -z "$DISTRVERSION" ] && DISTRVERSION=`distr_vendor -v`
	# Get list of replacement rules files
	echo -n "$REPLBASE$VENDOR.$DISTRVERSION $REPLBASE$VENDOR "
	[ "$VENDOR" != "alt" ] && echo -n "$REPLBASE$TARGET"
}

print_grprepl_list()
{
	local REPLBASE=$PKGREPLBASE/../grprepl/grprepl.

	# VENDOR, TARGET is defined in detect_target_env() func
	[ -z "$DISTRVERSION" ] && DISTRVERSION=`distr_vendor -v`
	# Get list of replacement rules files
	echo -n "$REPLBASE$VENDOR.$DISTRVERSION $REPLBASE$VENDOR "
	[ "$VENDOR" != "alt" ] && echo -n "$REPLBASE$TARGET"
}

# Prints out buildreqs in target notation for SPEC
print_target_buildreq()
{
	# Build list in target ($VENDOR) notation for package's buildreqs
	for i in `print_buildreq ${1}` ; do
		# get target name or just print out original one
		tolocal_anyrepl $i `print_pkgrepl_list` || echo -n "$i "
		echo -n "$TARGETPKGNAME "
	done
}
