#!/usr/bin/perl
# Reads a file from STDIN and replaces ###SHELLPACK macros with code

use strict;

my $shellpack;

while (<>) {
	my $line = $_;
	if ($_ !~ /###SHELLPACK/) {
		print $_;
		next;
	}

	$line =~ s/.*###SHELLPACK //;

	my ($command, @details) = split(/\s+/, $line);

	if ($command eq "preamble") {
		my ($name, $version) = @details;
		my $dummy;
		($shellpack, $dummy) = split(/-/, $name);
		$shellpack = substr($name, 0, rindex($name, "-"));
		print <<EOC
P=$name
DEFAULT_VERSION=$version
. \$SHELLPACK_INCLUDE/common.sh
TIME_CMD=`which time`
if [ "\$TIME_CMD" = "" ]; then
        TIMEFORMAT="%2Uuser %2Ssystem %Relapsed %P%%CPU"
        TIME_CMD="time"
fi
EOC
	} elsif ($command eq "parseargBegin") {
		print <<EOC
# Basic argument parser
TASKSET_SERVER=
TASKSET_CLIENT=
TASKSET_ALL=
SERVERSIDE_COMMAND=none
SERVERSIDE_NAME=`date +%Y%m%d-%H%M-%S`

while [ "\$1" != "" ]; do
	case "\$1" in
	-v)
		VERSION=\$2
		shift 2
		;;
	--serverside-command)
		SERVERSIDE_COMMAND=\$2
		shift 2
		;;
	--serverside-name)
		SERVERSIDE_NAME=\$2
		shift 2
		;;
EOC
	} elsif ($command eq "parseargInstall") {
		print <<EOC
	--install-only)
		INSTALL_ONLY=yes
		shift
		;;
	--install-force)
		INSTALL_FORCE=yes
		shift
		;;
EOC
	} elsif ($command eq "parseargParam") {
		my ($switch, $param) = @details;
		print <<EOC
	$switch)
		$param=\$2
		shift 2
		;;
EOC
	} elsif ($command eq "parseargYes") {
		my ($switch, $param) = @details;
		print <<EOC
	$switch)
		$param=yes
		shift
		;;
EOC
	} elsif ($command eq "parseargBindings") {
		print <<EOC
	--bind-pinned)
		CPUA=`numactl --hardware | grep ^node | grep cpus: | head -1 | awk '{print \$4}'`
		TASKSET_SERVER="taskset -c \$CPUA"
		TASKSET_CLIENT="taskset -c \$CPUA"
		TASKSET_ALL="taskset -c \$CPUA"
		shift
		;;
	--bind-cross-node)
		CPUA=`numactl --hardware | grep ^node | grep cpus: | head -1 | awk '{print \$4}'`
		CPUB=`numactl --hardware | grep ^node | grep cpus: | tail -1 | awk '{print \$NF}'`
		TASKSET_SERVER="taskset -c \$CPUA"
		TASKSET_CLIENT="taskset -c \$CPUB"
		TASKSET_ALL="taskset -c \$CPUA,\$CPUB"
		shift
		;;
	--bind-cross-socket)
		CPUA=`numactl --hardware | grep ^node | grep cpus: | head -1 | awk '{print \$4}'`
		CPUB=`list-cpu-siblings.pl \$CPUA cores 0 | awk -F , '{print \$1}'`
		TASKSET_SERVER="taskset -c \$CPUA"
		TASKSET_CLIENT="taskset -c \$CPUB"
		TASKSET_ALL="taskset -c \$CPUA,\$CPUB"
		shift
		;;
	--bind-cross-ht)
		CPUA=`numactl --hardware | grep ^node | grep cpus: | head -1 | awk '{print \$4}'`
		CPUB=`list-cpu-siblings.pl \$CPUA threads 0 | awk -F , '{print \$1}'`
		if [ "\$CPUB" = "" ]; then
			echo ERROR: Could not identify HT thread for CPU \$CPUA
			exit \$SHELLPACK_ERROR
		fi
		TASKSET_SERVER="taskset -c \$CPUA"
		TASKSET_CLIENT="taskset -c \$CPUB"
		TASKSET_ALL="taskset -c \$CPUA,\$CPUB"
		shift
		;;
EOC

	} elsif ($command eq "parseargEnd") {
		print <<EOC
	*)
		echo Unrecognised option: \$1
		shift
	esac
done
if [ "\$TASKSET_SERVER" != "" ]; then
	echo TASKSET_SERVER: \$TASKSET_SERVER
	echo TASKSET_CLIENT: \$TASKSET_CLIENT
fi
if [ -z "\$VERSION" ]; then
	VERSION=\$DEFAULT_VERSION
fi
EOC
	} elsif ($command eq "sources_fetch") {
		my ($tarfile, $srcdir) = @details;
		my $versionDir = "";
		if ($tarfile =~ /\//) {
			($versionDir,$tarfile) = split(/\//, $tarfile);
			$versionDir="$versionDir/";
		}
		
		print <<EOC
# Unconditionally fetch the tar to find out the real version number
TARFILE=$tarfile
sources_fetch \$WEB_LOCATION/$versionDir\$TARFILE \$MIRROR_LOCATION/\$TARFILE \$SHELLPACK_SOURCES/\$TARFILE \$WEB_LOCATION_ALT/$versionDir\$TARFILE
cd \$SHELLPACK_SOURCES
tar -xf \$TARFILE
if [ \$? -ne 0 ]; then
	error "\$P: tar xf $tarfile failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi

# Rename directory to something we expect.
DST_DIR=`tar tf \$TARFILE | head -n 1 | awk -F / '{print \$1}'`
mv \$DST_DIR $srcdir
pushd $srcdir > /dev/null || die Failed to rename tar
EOC
	} elsif ($command eq "sources_fetch_p0") {
		my ($tarfile, $srcdir) = @details;
		my $versionDir = "";
		if ($tarfile =~ /\//) {
			($versionDir,$tarfile) = split(/\//, $tarfile);
			$versionDir="$versionDir/";
		}

		print <<EOC
# Unconditionally fetch the tar to find out the real version number
TARFILE=$tarfile
sources_fetch \$WEB_LOCATION/$versionDir\$TARFILE \$MIRROR_LOCATION/\$TARFILE \$SHELLPACK_SOURCES/\$TARFILE
mkdir \$SHELLPACK_SOURCES/$srcdir
cd \$SHELLPACK_SOURCES/$srcdir
tar -xf ../\$TARFILE
if [ \$? -ne 0 ]; then
	error "\$P: tar xf $tarfile failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi
EOC
	} elsif ($command eq "git_fetch") {
		my ($tarfile, $srcdir) = @details;
		my $versionDir = "";
		if ($tarfile =~ /\//) {
			($versionDir,$tarfile) = split(/\//, $tarfile);
			$versionDir="$versionDir/";
		}
		
		print <<EOC
# Unconditionally fetch the tar to find out the real version number
TARFILE=$tarfile
git_fetch \$GIT_LOCATION $srcdir \$MIRROR_LOCATION/\$TARFILE \$SHELLPACK_SOURCES/\$TARFILE \$VERSION
cd \$SHELLPACK_SOURCES
tar -xf \$TARFILE
if [ \$? -ne 0 ]; then
	error "\$P: tar xf $tarfile failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi

# Rename directory to something we expect.
DST_DIR=`tar tf \$TARFILE | head -n 1 | awk -F / '{print \$1}'`
mv \$DST_DIR $srcdir
pushd $srcdir > /dev/null || die Failed to rename tar
EOC
	} elsif ($command eq "hg_fetch") {
		my ($tarfile, $srcdir) = @details;
		my $versionDir = "";
		if ($tarfile =~ /\//) {
			($versionDir,$tarfile) = split(/\//, $tarfile);
			$versionDir="$versionDir/";
		}

		print <<EOC
# Unconditionally fetch the tar to find out the real version number
TARFILE=$tarfile
hg_fetch \$HG_LOCATION $srcdir \$MIRROR_LOCATION/\$TARFILE \$SHELLPACK_SOURCES/\$TARFILE
cd \$SHELLPACK_SOURCES
tar -xf \$TARFILE
if [ \$? -ne 0 ]; then
	error "\$P: tar xf $tarfile failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi

# Rename directory to something we expect.
DST_DIR=`tar tf \$TARFILE | head -n 1 | awk -F / '{print \$1}'`
mv \$DST_DIR $srcdir
pushd $srcdir > /dev/null || die Failed to rename tar
EOC
	} elsif ($command eq "build_start") {
		my ($srcdir) = @details;
		print <<EOC
pushd \$SHELLPACK_SOURCES/$srcdir || die Failed to change to source directory
for FILE in `find -name "*"`; do
	touch \$FILE
done
EOC
	} elsif ($command eq "build_autogen") {
		print <<EOC
./autogen.sh || die Failed to run autogen
EOC
	} elsif ($command eq "build_autoreconf") {
		print <<EOC
touch NEWS
touch AUTHORS
autoreconf -f -i || die Failed to run autoreconf
EOC
	} elsif ($command eq "build_configure") {
		my $srcdir = @details[0];
		shift @details;
		print <<EOC
export CFLAGS="-O2 \$CFLAGS_MMTESTS_EXTRA"
eval ./configure --prefix=\$SHELLPACK_SOURCES/$srcdir-installed @details
if [ \$? -ne 0 ]; then
	cp /usr/share/automake*/config.guess .
	cp /usr/share/automake*/config.sub .
	eval ./configure --prefix=\$SHELLPACK_SOURCES/$srcdir-installed @details
	if [ \$? -ne 0 ]; then
		error "\$P: configure failed"
		popd > /dev/null
		exit \$SHELLPACK_ERROR
	fi
fi
unset CFLAGS
EOC
	} elsif ($command eq "self_extract") {
		my $selffile = @details[0];
		shift @details;
		print <<EOC
LINESTART=`grep -n "==== BEGIN $selffile" \$0 | tail -1 | awk -F : '{print \$1}'`
LINEEND=`grep -n "==== END $selffile" \$0 | tail -1 | awk -F : '{print \$1}'`
if [ "\$LINEEND" = "" ]; then
	LINECOUNT=`wc -l \$0 | awk '{print \$1}'`
fi
if [ "\$LINESTART" = "" ]; then
	die Failed to find start of file $selffile
fi
echo Extracting \$SHELLPACK_TEMP/$selffile
sed -n \$((LINESTART+1)),\$((LINEEND-1))p \$0 > \$SHELLPACK_TEMP/$selffile
EOC
	} elsif ($command eq "build_cmake") {
		my $srcdir = @details[0];
		shift @details;
		print <<EOC
cmake . -DCMAKE_INSTALL_PREFIX=\$SHELLPACK_SOURCES/$srcdir-installed @details
if [ \$? -ne 0 ]; then
	error "\$P: cmake failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi
EOC

	} elsif ($command eq "make") {
		my $opts = join(" ", @details);
		print <<EOC
make -j\$NUMCPUS $opts
if [ \$? -ne 0 ]; then
	error "\$P: make failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi
EOC
	} elsif ($command eq "make_retry") {
		my $opts = join(" ", @details);
		print <<EOC
make -j\$NUMCPUS $opts
if [ \$? -ne 0 ]; then
	make -j\$NUMCPUS $opts
	if [ \$? -ne 0 ]; then
		error "\$P: make failed"
		popd > /dev/null
		exit \$SHELLPACK_ERROR
	fi
fi
EOC

	} elsif ($command eq "make_clean") {
		print <<EOC
make clean
if [ \$? -ne 0 ]; then
	error "\$P: make clean failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi
EOC
	} elsif ($command eq "make_make_install") {
		my $opts = join(" ", @details);
		print <<EOC
make -j\$NUMCPUS $opts
if [ \$? -ne 0 ]; then
	if [ "\$MAKE_RETRY" = "yes" ]; then
		make -j\$NUMCPUS $opts
	fi
	error "\$P: make failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi
make install
if [ \$? -ne 0 ]; then
	error "\$P: make install failed"
	popd > /dev/null
	exit \$SHELLPACK_ERROR
fi
EOC
	} elsif ($command eq "check_install_required") {
		my ($srcdir, @other) = @details;
		my $dstdir = "$srcdir-installed";

		print <<EOC
if [ "\$INSTALL_FORCE" = "yes" ]; then
	rm -rf \$SHELLPACK_SOURCES/$srcdir
fi
if [ ! -d \$SHELLPACK_SOURCES/$dstdir ]; then
	mmtests_activity source-install
	\$SHELLPACK_INCLUDE/shellpack-install-$shellpack -v \${VERSION} @other || die $shellpack install script returned error
	mmtests_activity source-installed
fi
cd \$SHELLPACK_SOURCES/$shellpack-\${VERSION}-installed || die Failed to cd to $shellpack install directory
if [ "\$INSTALL_ONLY" = "yes" ]; then
	echo $shellpack installed only as requested.
	exit \$SHELLPACK_SUCCESS
fi
EOC
	} elsif ($command eq "check_install_required_continue") {
		my ($srcdir, @other) = @details;
		my $dstdir = "$srcdir-installed";

		print <<EOC
if [ "\$INSTALL_FORCE" = "yes" ]; then
	rm -rf \$SHELLPACK_SOURCES/$srcdir
fi
if [ ! -d \$SHELLPACK_SOURCES/$dstdir ]; then
	mmtests_activity source-install
	\$SHELLPACK_INCLUDE/shellpack-install-$shellpack -v \${VERSION} @other || die $shellpack install script returned error
	mmtests_activity source-installed
fi
cd \$SHELLPACK_SOURCES/$shellpack-\${VERSION}-installed || die Failed to cd to $shellpack install directory
EOC
	} elsif ($command eq "check_external_install_required") {
		my ($externalpack, $srcdir, $version, @other) = @details;
		my $dstdir = "$srcdir-installed";

		print <<EOC
if [ "\$INSTALL_FORCE" = "yes" ]; then
	rm -rf \$SHELLPACK_SOURCES/$srcdir
fi
if [ ! -d \$SHELLPACK_SOURCES/$dstdir ]; then
	mmtests_activity source-install
	if [ ! -e  \$SHELLPACK_INCLUDE/shellpack-install-$externalpack ]; then
		\$SHELLPACK_TOPLEVEL/shellpack_src/src/refresh.sh $externalpack
	fi
	\$SHELLPACK_INCLUDE/shellpack-install-$externalpack -v $version @other || die $externalpack install script returned error
	mmtests_activity source-installed
fi
cd \$SHELLPACK_SOURCES/$externalpack-$version-installed || die Failed to cd to $externalpack install directory
if [ "\$INSTALL_ONLY" = "yes" ]; then
	echo $externalpack installed only as requested.
	exit \$SHELLPACK_SUCCESS
fi
EOC
	} elsif ($command eq "check_external_install_required_continue") {
		my ($externalpack, $srcdir, $version, @other) = @details;
		my $dstdir = "$srcdir-installed";

		print <<EOC
if [ "\$INSTALL_FORCE" = "yes" ]; then
	rm -rf \$SHELLPACK_SOURCES/$srcdir
fi
if [ ! -d \$SHELLPACK_SOURCES/$dstdir ]; then
	mmtests_activity source-install
	if [ ! -e  \$SHELLPACK_INCLUDE/shellpack-install-$externalpack ]; then
		\$SHELLPACK_TOPLEVEL/shellpack_src/src/refresh.sh $externalpack
	fi
	\$SHELLPACK_INCLUDE/shellpack-install-$externalpack -v $version @other || die $externalpack install script returned error
	mmtests_activity source-installed
fi
cd \$SHELLPACK_SOURCES/$externalpack-$version-installed || die Failed to cd to $externalpack install directory
EOC
	} elsif ($command eq "init_only_start") {
	print <<EOC
if [ "\$INSTALL_ONLY" = "yes" ]; then
EOC
	} elsif ($command eq "init_only_end") {
		print <<EOC
	echo $shellpack installed only as requested.
	exit \$SHELLPACK_SUCCESS
fi
EOC
	} elsif ($command eq "init_only_continue") {
		print <<EOC
	echo Initialisation step complete, continuing
fi
EOC
	} elsif ($command eq "init_complete") {
		print <<EOC
if [ "\$INSTALL_ONLY" = "yes" ]; then
	echo $shellpack installed only as requested.
	exit \$SHELLPACK_SUCCESS
fi
EOC
	} elsif ($command eq "monitor_hooks") {
		print <<EOC
# Include monitor hooks
. \$SHELLPACK_INCLUDE/include-monitor.sh
EOC
	} elsif ($command eq "iteration_begin") {
		my ($iterations) = @details;
		print <<EOC
for ITERATION in `seq 1 $iterations`; do
	mmtests_activity iteration \$ITERATION
EOC
	} elsif ($command eq "iteration_end") {
		print "sync\n";
		print "done\n";
	} elsif ($command eq "threads_fib_begin") {
		my ($min_thread, $max_thread) = @details;
		print <<EOC
NR_THREADS=1
LAST_NR_THREADS=0
while [ \$NR_THREADS -lt $max_thread ]; do
	TMP_NR_THREADS=\$LAST_NR_THREADS
	LAST_NR_THREADS=\$NR_THREADS
	NR_THREADS=\$((NR_THREADS+TMP_NR_THREADS))
	if [ \$NR_THREADS -lt $min_thread ]; then
		continue
	fi
	if [ \$NR_THREADS -gt $max_thread ]; then
		NR_THREADS=$max_thread
	fi
EOC
	} elsif ($command eq "threads_fib_end") {
		print "done\n";
	} elsif ($command eq "threads_powertwo_begin") {
		my ($min_thread, $max_thread) = @details;
		my $max_thread_varname = $max_thread;
		$max_thread_varname =~ s/^\$//;
		print <<EOC
NR_THREADS=$min_thread
if [ "\$NR_THREADS" = "" ]; then
	NR_THREADS=1
fi
THREADS=\$NR_THREADS
NR_THREADS=\$((NR_THREADS*2))
if [ "\$MMTESTS_THREAD_CUTOFF" != "" ]; then
	echo Forcing $max_thread_varname to \$MMTESTS_THREAD_CUTOFF
	$max_thread_varname=\$MMTESTS_THREAD_CUTOFF
fi
while [ \$NR_THREADS -le $max_thread ]; do
	THREADS="\$THREADS \$NR_THREADS"
	NR_THREADS=\$((NR_THREADS*2))
done
if [ `echo \$THREADS | awk '{print \$NF}'` -ne $max_thread ]; then
	THREADS="\$THREADS $max_thread"
fi
for NR_THREADS in \$THREADS; do
	if [ \$NR_THREADS -gt $max_thread ]; then
		NR_THREADS=$max_thread
	fi
EOC
	} elsif ($command eq "threads_powertwo_end") {
		print <<EOC
done
EOC

	} elsif ($command eq "threads_iterate_begin") {
		my ($min_thread, $max_thread) = @details;
		print <<EOC
for NR_THREADS in `seq $min_thread $max_thread`; do
EOC
	} elsif ($command eq "threads_iterate_end") {
		print "done\n";
	} elsif ($command eq "threads_min_max_stride_begin") {
		my ($min_thread, $max_thread) = @details;
		print <<EOC
for NR_THREADS in $min_thread $max_thread; do
EOC

	} elsif ($command eq "threads_stride_begin") {
		my ($min_thread, $max_thread) = @details;
		print <<EOC
THREADS=
START_THREAD=$min_thread
END_THREAD=$max_thread
if [ \$END_THREAD -gt 32 ]; then
	THREADS=`seq \$START_THREAD 8`
	THREADS="\$THREADS `seq 12 4 32`"
	THREADS="\$THREADS `seq 48 16 \$END_THREAD`"
elif [ \$END_THREAD -gt 8 ]; then
	THREADS=`seq \$START_THREAD 8`
	THREADS="\$THREADS `seq 12 4 \$END_THREAD`"
else
	THREADS=`seq \$START_THREAD \$END_THREAD`
fi
for NR_THREADS in \$THREADS; do
	if [ \$NR_THREADS -lt $min_thread ]; then
		continue
	fi
EOC
	} elsif ($command eq "threads_large_stride_begin") {
		my ($min_thread, $max_thread) = @details;
		my $max_thread_varname = $max_thread;
		$max_thread_varname =~ s/^\$//;
		print <<EOC
THREADS=
START_THREAD=$min_thread
if [ "\$MMTESTS_THREAD_CUTOFF" != "" ]; then
	echo Forcing $max_thread_varname to \$MMTESTS_THREAD_CUTOFF
	$max_thread_varname=\$MMTESTS_THREAD_CUTOFF
fi
END_THREAD=$max_thread
if [ \$END_THREAD -gt 32 ]; then
	THREADS=`seq \$START_THREAD 3 8`
	THREADS="\$THREADS `seq 12 9 32`"
	THREADS="\$THREADS `seq 48 31 \$END_THREAD`"
elif [ \$END_THREAD -gt 8 ]; then
	THREADS=`seq \$START_THREAD 2 8`
	THREADS="\$THREADS `seq 12 6 \$END_THREAD`"
else
	THREADS=`seq \$START_THREAD 2 \$END_THREAD`
fi
if [ `echo \$THREADS | awk '{print \$NF}'` -ne \$END_THREAD ]; then
	THREADS="\$THREADS \$END_THREAD"
fi

for NR_THREADS in \$THREADS; do
	if [ \$NR_THREADS -lt $min_thread ]; then
		continue
	fi
EOC
	} elsif ($command eq "threads_huge_stride_begin") {
		my ($min_thread, $max_thread) = @details;
		print <<EOC
THREADS=
START_THREAD=$min_thread
END_THREAD=$max_thread
if [ \$END_THREAD -ge 32 ]; then
	THREADS=`seq \$START_THREAD 4 8`
	THREADS="\$THREADS `seq 12 9 32`"
	THREADS="\$THREADS `seq 48 31 \$END_THREAD`"
elif [ \$END_THREAD -ge 8 ]; then
	THREADS=`seq \$START_THREAD 3 8`
	THREADS="\$THREADS `seq 12 6 \$END_THREAD`"
else
	THREADS=`seq \$START_THREAD 2 \$END_THREAD`
fi
if [ `echo \$THREADS | awk '{print \$NF}'` -ne \$END_THREAD ]; then
	THREADS="\$THREADS \$END_THREAD"
fi
for NR_THREADS in \$THREADS; do
	if [ \$NR_THREADS -lt $min_thread ]; then
		continue
	fi
EOC
	} elsif ($command eq "threads_stride_fixed_begin") {
		my ($min_thread, $max_thread, $samples) = @details;
		print <<EOC
THREADS="$min_thread"
THREAD_DIFF=\$(($max_thread-$min_thread))
if [ "\$THREAD_DIFF\" -lt $samples ]; then
	THREADS=`seq $min_thread $max_thread`
else
	for ITER in `seq 2 \$(($samples-1))`; do
		THREADS="\$THREADS \$((THREAD_DIFF/$samples*\$ITER))"
	done
	THREADS="\$THREADS $max_thread"
fi
for NR_THREADS in \$THREADS; do
	if [ \$NR_THREADS -lt $min_thread ]; then
		continue
	fi
EOC
	} elsif ($command eq "threads_stride_alt_fixed_begin") {
		my ($min_thread, $max_thread, $samples) = @details;
		$samples--;
		print <<EOC
THREADS="$min_thread"
THREAD_DIFF=\$(($max_thread-$min_thread))
if [ "\$THREAD_DIFF\" -lt $samples ]; then
	THREADS=`seq $min_thread $max_thread`
else
	for ITER in `seq 1 \$(($samples-1))`; do
		THREADS="\$THREADS \$(($min_thread+THREAD_DIFF/$samples*\$ITER))"
	done
	THREADS="\$THREADS $max_thread"
fi
for NR_THREADS in \$THREADS; do
	if [ \$NR_THREADS -lt $min_thread ]; then
		continue
	fi
EOC
	} elsif ($command eq "threads_stride_end") {
		print "done\n";
	} else {
		print "# WARNING: UNRECOGNISED command $command\n";
		print $_;
	}
}
