# $MirOS: contrib/hosted/tg/deb/mksh/debian/meat,v 1.41 2012/05/05 21:45:26 tg Exp $
#-
# Design: this thing is intended to build two mksh binaries,
# mksh-full and mksh-static (with the latter being statical-
# ly linked) according to DEB_BUILD_OPTIONS set:
# - nocheck: do not run mksh's regression test suite
# - noopt: build mksh with -O0, for eglibc builds only
# - nodbg: do not build with debugging info
# - nostrip: handled by dh_strip, so ignored here
# - parallel=n: currently ignored, maybe -flto=jobserver could use it
# - mksh-firstbuilt: do not run a simple test for operational
#   functionality, but use the first built binary (e.g. cross-builds)
# - mksh-static=x: use library x (comma-separated list) for this,
#   values for x are: klibc dietlibc eglibc
# - nomksh-small: do not build mksh-static with -DMKSH_SMALL
#
# Default values are:
# - mksh-static=klibc,dietlibc,eglibc [alpha amd64 hppa ia64 m32r ppc64 sparc sparc64]
# - mksh-static=dietlibc,eglibc [others]
# if mksh-firstbuilt is set:
# - mksh-static=dietlibc,eglibc on certain targets (see below for details)
# - mksh-static=eglibc on certain targets (see below for details)
#
# If we are cross-building, mksh-firstbuilt will be set.


# --- functions ---
buildok=0
normalise() {
	varname=$1
	eval varvalue=\$$varname
	newvalue=
	unset newvalue
	for tmpvalue in $varvalue; do
		newvalue=$newvalue${newvalue+ }$tmpvalue
	done
	eval $varname=\$newvalue
}
logbuildinfo() {
	where=$1
	{
		echo "Build information for $where mksh"
		fgrep 'set -A check_categories' builddir/$where/test.sh
		echo "From: $startdate"
		$CC --version 2>&1 | grep ^gcc
		echo "Result: $buildinfo (broken<firstbuilt<checked<regressed)"
		if test x"$buildinfo" = x"regressed"; then
			echo "Regression test results:"
			echo "$resultest" | sed 's/^/| /'
		fi
		echo "Variables used:"
		for v in CC CFLAGS CPPFLAGS LDFLAGS LDSTATIC LIBS; do
			eval x=\$$v
			echo "| $v='$x'"
		done
		echo "Actual compilation options (except combine, if used):"
		fgrep main builddir/$where/Rebuild.sh | sed 's/^/] /'
		echo "Resulting binary:"
		size builddir/$where/mksh 2>&1 | sed 's/^/| /'
		echo "Date: $(date -u)"
	} >builddir/buildinfo.$where
}
trybuild() {
	where=$1
	normalise CC
	normalise CFLAGS
	normalise CPPFLAGS
	normalise LDFLAGS
	# do not normalise LDSTATIC
	normalise LIBS
	cd builddir/$where
	echo "Attempting compilation of mksh in $where with CC='$CC'"
	echo "CFLAGS='$CFLAGS' CPPFLAGS='$CPPFLAGS'"
	echo "LDFLAGS='$LDFLAGS' LDSTATIC='$LDSTATIC' LIBS='$LIBS'"

	buildok=0
	arg='-r -c lto'
	buildinfo=broken
	gotbin=0
	startdate=$(date -u)
	set -x
	CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" \
	    LDSTATIC="$LDSTATIC" LIBS="$LIBS" sh ../../Build.sh $arg && \
	    test -f mksh && gotbin=1
	set +x
	test -f Rebuild.sh && test x"$HAVE_CAN_COMBINE" != x"0" && \
	    if test $gotbin = 0; then
		echo 'Build with combine *FAILED*, retrying without.'
		echo '=== note: for better optimisation, fix your gcc! ==='
		sh Rebuild.sh && test -f mksh && gotbin=1
	fi
	if test $gotbin = 0; then
		echo "Build attempt failed."
		cd ../..
		return
	fi
	if test $firstbuilt = 1; then
		echo "Got a binary, using first built."
		buildinfo=firstbuilt
		cd ../..
		buildok=1
		logbuildinfo $where
		return
	fi
	echo "Running simple checks on the binary."
	perl ../../check.pl -s ../../debian/mtest.t -p ./mksh -v 2>&1 | \
	    tee mtest.log
	if grep '^Total failed: 0$' mtest.log >/dev/null; then
		echo "Simple tests okay."
	else
		echo "Simple tests failed."
		cd ../..
		return
	fi
	buildinfo=checked
	if test $nocheck = 0; then
		echo "Running mksh regression test suite."
		if test x"$(uname -s)" = x"GNU"; then
			# script(1) fails on Debian GNU/Hurd in pbuilder
			if test -e ../../../attended; then
				./test.sh -v
			else
				./test.sh -v -C regress:no-ctty
			fi 2>&1 | tee utest.log
		else
			echo "This needs /dev/tty and /dev/ptmx in the chroot."
			echo >test.wait
			script -qc './test.sh -v 2>&1 | tee utest.log; x=$?; rm -f test.wait; exit $x'
			maxwait=0
			while test -e test.wait; do
				sleep 1
				maxwait=$(expr $maxwait + 1)
				test $maxwait -lt 900 || break
			done
		fi
		if grep '^pass .*:KSH_VERSION' utest.log >/dev/null 2>&1; then
			echo "Regression test suite run."
		else
			echo "Regression tests apparently not run."
			echo "Failing this build."
			cd ../..
			return
		fi
		buildinfo=regressed
		resultest=$(grep -a '^[FPT]' utest.log 2>&1 | fgrep -a -v \
		    -e 'Testing mksh for conformance' \
		    -e 'This shell is actually')
	fi
	cd ../..
	buildok=1
	logbuildinfo $where
}

# --- main code ---

LC_ALL=C; export LC_ALL
topdir=$(pwd)
# get maximum of hardening flags
DEB_BUILD_MAINT_OPTIONS="hardening=+all"
export DEB_BUILD_MAINT_OPTIONS

# pull environment configuration
eval $(dpkg-architecture -s)

rm -rf builddir
mkdir builddir builddir/full builddir/static

echo "Building the package 'mksh' on '$DEB_BUILD_ARCH' for '$DEB_HOST_ARCH'" \
    "with DEB_BUILD_OPTIONS '$DEB_BUILD_OPTIONS'"
echo "Values (not used) from environment: CFLAGS='$CFLAGS' CPPFLAGS='$CPPFLAGS' LDFLAGS='$LDFLAGS'"

# parse options
nocheck=0
noopt=0
nodbg=0
firstbuilt=0
static=unset
small=1

if test x"$DEB_HOST_ARCH" = x"$DEB_BUILD_ARCH"; then
	firstbuilt=0
	iscross=0
else
	echo Using first built binary because we are cross-compiling
	echo "from $DEB_BUILD_GNU_TYPE to $DEB_HOST_GNU_TYPE here."
	firstbuilt=1
	iscross=1
fi

for i in $DEB_BUILD_OPTIONS; do
	case $i in
	(nocheck) nocheck=1 ;;
	(noopt) noopt=1 ;;
	(nodbg) nodbg=1 ;;
	(mksh-firstbuilt) firstbuilt=1 ;;
	(mksh-static=*) static=${i#*=} ;;
	(nomksh-small) small=0 ;;
	esac
done

# set default values for CC, CFLAGS, CPPFLAGS, LDFLAGS
test -n "$CC" || CC=gcc; dCC=$CC
echo "Using compiler: '$dCC'"
if test $noopt = 1; then
	x=-O0
else
	x=-O2
fi
test $nodbg = 1 || x="$x -g"
dCFLAGS=$(dpkg-buildflags --get CFLAGS 2>/dev/null) || dCFLAGS=$x
dCPPFLAGS=$(dpkg-buildflags --get CPPFLAGS 2>/dev/null)
dLDFLAGS=$(dpkg-buildflags --get LDFLAGS 2>/dev/null)
echo "Values from dpkg-buildflags: CFLAGS='$dCFLAGS' CPPFLAGS='$dCPPFLAGS' LDFLAGS='$dLDFLAGS'"
dCFLAGS="$dCFLAGS -Wall -Wextra"
dLDFLAGS="$dLDFLAGS -Wl,--as-needed"
HAVE_CAN_WALL=0; export HAVE_CAN_WALL
# Debian #499139
dCPPFLAGS="$dCPPFLAGS -DMKSH_BINSHREDUCED"
# Debian #532343, #539158
USE_PRINTF_BUILTIN=1; export USE_PRINTF_BUILTIN
# avoid needing a Build-Conflicts on libbsd-dev
HAVE_LIBUTIL_H=0; export HAVE_LIBUTIL_H

# avr32 currently has no locales
if test x"$DEB_HOST_ARCH" = x"avr32"; then
	HAVE_SETLOCALE_CTYPE=0; export HAVE_SETLOCALE_CTYPE
fi

# set mksh-static default values if none given
if test x"$static" = x"unset"; then
	static=eglibc
	case $firstbuilt$static:$DEB_HOST_ARCH in
	(1*:hppa)
		# revisit once dietlibc and lsb-release are built
		# and this arch has caught up (meat,v 1.32 e.g.)
		# or of course if klibc has gotten usable on hppa
		static=eglibc
		;;
	(0*:hppa)
		# let's see whether...
		static=klibc,eglibc
		;;
	(1*)
		# revisit once klibc results are in
		static=dietlibc,eglibc
		;;
	(0*:alpha|0*:amd64|0*:hppa|0*:ia64|0*:m32r|0*:ppc64|0*:sparc|0*:sparc64)
		static=klibc,dietlibc,eglibc
		;;
	(*)
		# klibc sigsuspend is broken
		static=dietlibc,eglibc
		;;
	esac
fi

# validate mksh-static arguments
static=$(echo x,"$static" | sed 's/^x,//' | tr , ' ')
for x in $static; do
	case $x in
	(klibc|dietlibc|eglibc) ;;
	(*)
		echo "Error: invalid library '$x' in DEB_BUILD_OPTIONS"
		exit 1
		;;
	esac
done

# create a locale if we are to run the testsuite
test x"$HAVE_SETLOCALE_CTYPE" != x"0" && if test $nocheck = 0; then
	echo Creating locale for the regression tests
	mkdir builddir/tloc
	# cf. Debian #522776
	localedef -i en_US -c -f UTF-8 builddir/tloc/en_US.UTF-8 || \
	    echo Warning: testsuite failures may occur
	LOCPATH=$topdir/builddir/tloc; export LOCPATH
fi

# build mksh-full
CC=$dCC
CFLAGS=$dCFLAGS
CPPFLAGS=$dCPPFLAGS
LDFLAGS=$dLDFLAGS
unset LDSTATIC
LIBS=
echo Building mksh-full...
trybuild full
if test $buildok = 0; then
	echo Build of mksh-full failed.
	exit 1
fi

# build mksh-static
sCC=$dCC
sCFLAGS=
# drop optimisation, debugging and PIC flags for mksh-static
for x in $dCFLAGS; do
	case $x in
	(-O*|-g*|-fPIE) ;;
	(*) sCFLAGS="$sCFLAGS $x" ;;
	esac
done
sCPPFLAGS=$dCPPFLAGS
sLDFLAGS=
for x in $dLDFLAGS; do
	case $x in
	(-pie|-fPIE) ;;
	(*) sLDFLAGS="$sLDFLAGS $x" ;;
	esac
done
sLIBS=
test $small = 0 || sCPPFLAGS="$sCPPFLAGS -DMKSH_SMALL"
# no locale support in mksh-static needed
HAVE_SETLOCALE_CTYPE=0; export HAVE_SETLOCALE_CTYPE

buildok=0
for x in $static; do echo Building mksh-static with $x; case $x in
(klibc)
	if test -z "$(which klcc 2>/dev/null)"; then
		echo ... skipping, klcc not found
		continue
	fi
	CC=klcc
	CFLAGS="$sCFLAGS -fno-stack-protector -Os"
	test $nodbg = 1 || CFLAGS="$CFLAGS -g"
	CPPFLAGS="$sCPPFLAGS -DMKSH_NO_LIMITS"
	LDFLAGS="$sLDFLAGS"
	LDSTATIC="-static"
	LIBS=$sLIBS
	HAVE_CAN_COMBINE=0; export HAVE_CAN_COMBINE
	trybuild static
	unset HAVE_CAN_COMBINE
	;;
(dietlibc)
	if test -z "$(which diet 2>/dev/null)"; then
		echo ... skipping, diet not found
		continue
	fi
	CC="diet -v -Os $sCC"
	CFLAGS=$sCFLAGS
	# debug builds are too fragile here, IIRC
	CPPFLAGS=$sCPPFLAGS
	LDFLAGS=$sLDFLAGS
	LDSTATIC=" "
	LIBS=$sLIBS
	echo "Note: all warning: dereferencing pointer 'p' does break" \
	    "strict-aliasing rules come from dietlibc, not mksh!"
	trybuild static
	;;
(eglibc)
	CC=$sCC
	CFLAGS="-Os $sCFLAGS"
	test $nodbg = 1 || CFLAGS="$CFLAGS -g"
	CPPFLAGS=$sCPPFLAGS
	LDFLAGS="$sLDFLAGS"
	LDSTATIC="-static"
	LIBS=$sLIBS
	trybuild static
	;;
esac; test $buildok = 0 || break; done
if test $buildok = 0; then
	echo Build of mksh-static failed.
	exit 1
fi

echo Logging build information...
{
	cat debian/README.Debian
	echo Build information for mksh${1}:
	for v in DEB_BUILD_GNU_TYPE DEB_HOST_GNU_TYPE DEB_BUILD_OPTIONS; do
		eval x=\$$v
		echo "| $v='$x'"
	done
	echo Dependencies:
	dpkg-query -W $(for wasn in ./usr/lib/libc.so ./usr/lib/*/libc.so \
	    locales .=diet .=klcc .=as .=ld .=strip; do
		case $wasn in
		(.=*) wasn=.$(which ${wasn#.=} 2>/dev/null) ;;
		esac
		case $wasn in
		(./*)
			for wasn in ${wasn#.}; do
				test -e $wasn && dpkg -S $wasn | sed 's/:.*$//'
			done
			;;
		(.*)	;;
		(*)
			echo $wasn
			;;
		esac
	done | sort -u) | column -t | sed 's/^/| /'
	echo ----
	cat builddir/buildinfo.full
	echo ----
	cat builddir/buildinfo.static
	echo ----
	dpkg-parsechangelog -n1 | grep '^Version:'
	date -R
} | gzip -n9 >builddir/README.Debian.gz
echo All builds complete.
exit 0
