#!/bin/sh
FILE="$1"
test="${FILE%.*}"
ext="${FILE##*.}"
TIMEOUT="timeout --preserve-status 3h"
if test -n "$HTTP_LOGFILE"; then
exec 2>> "$HTTP_LOGFILE"
fi
# for debugging
# BASH_XTRACEFD=2
# set -x
if test -f $test.c; then
src=$test.c
elif test -f $test.m; then
src=$test.m
else
echo "No source file found for $test" >&2
exit 1
fi
case $ext in
ctst) log=clog; out=cout; ref=cref; ;;
*) log=log; out=out; ref=ref; ;;
esac
case $CC in
*-D_MPI=*) test -z "$EXEC" && EXEC="mpirun -np "`echo $CC | sed 's/.*-D_MPI=\([0-9]*\).*/\1/'`;
esac
for opt in $CFLAGS; do
case $opt in
-catch) test -z "$EXEC" && EXEC=" "; # turn off gdb
esac
done
case $OSTYPE in
*darwin*) ;; # gdb is (often) broken on Apple OSX
*)
if test -z "$GDB"; then
GDB=`which gdb`
fi
;;
esac
script()
{
rm -f $src display.html
if test -f fail; then
return 1
fi
test -s warn || rm -f warn
pass=true
rm -f $log log-* stencil stencil-* fine fine-* coarse coarse-*
tail -f --pid=$$ --retry display.html 2> /dev/null | grep -o 'http://.*:[0-9]*' &
tail -f --pid=$$ --retry $log 2> /dev/null | grep -E '.*:[0-9]*:.*(warning|error).*:' &
retail=$!
if test -n "$EXEC" -o -z "$GDB"; then
if ! $EXEC ./$test 2> $log > $out; then
pass=false
if test "$EXEC" = " " -a -f core -a -n "$GDB"; then
cat $log
xterm -e "gnuplot plot -" &
$GDB -q ./$test core
fi
fi
else
if ! stdbuf -oL $GDB -batch -return-child-result -ex "run 2> $log > $out" ./$test > gdb.log 2> gdb.err; then
pass=false
$AWK '
{
if (NF > 0)
a[nb++] = $0;
}
/ at .*:[0-9]*$/ {
for (i = 0; i < nb - 1; i++)
print $(NF) ":error: " a[i];
}
' < gdb.log >> $log
fi
rm -f gdb.log
fi
kill $retail
if test -f log-1; then
mv -f $log log-0
cat log-* > $log
fi
if $pass; then
if test -f $test.$ref; then
ref=$ref
else
ref=ref
fi
if test -f $test.$ref; then
echo diff $log $test.$ref > fail
diff $log $test.$ref >> fail && rm -f fail
rm -f $test.$ref
fi
else
cp -f $log fail
fi
if test -f fail; then
return 1
fi
touch pass
}
# run locally (C code)
locally()
{
echo \[$test.$ext\]
$BASILISK/qcc -autolink -disable-dimensions $CFLAGS -o $test/$test $src $LIBS -lm > $test/log 2>&1 \
|| mv -f $test/log $test/fail
cd $test
script
cd ..
}
# run locally (Octave code)
locally_octave()
{
echo \[$test.$ext\]
cd $test
if octave-cli --path .. -W -H -q $src 2> $log > $out && \
! grep -q '^error:' $log; then
touch pass
else
cp -f $log fail
fi
cd ..
}
# run untrusted code using the 'basilisk-untrusted' user if it exists
run_untrusted()
{
status=0
if test -n "$chksum"; then
if grep -q basilisk-untrusted /etc/passwd; then
if ! mkdir -m 777 ../$chksum; then
return 1
fi
if ! sudo -n -u basilisk-untrusted -- \
bash -c "cp -arf * ../$chksum && cd ../$chksum && $1"; then
status=1
fi
if ! cp -arf ../$chksum/* .; then
status=1
fi
sudo -n -u basilisk-untrusted -- rm -rf ../$chksum/*
rm -r -f ../$chksum
else
echo "Could not find the 'basilisk-untrusted' user" >2
status=1
fi
fi
return $status
}
doplots()
{
if test -f "$1".plot && ! run_untrusted 'gnuplot -e "batch=1; PNG=\"'$PNG'\"; set term '$PNG' enhanced font \",10\"; set output \"plot.png\"; set macros;" '$1'.plot > tmp 2>&1;'; then
cat tmp >> warn
fi
if ! run_untrusted "PNG=$PNG sh $BASILISK/gnuplot.sh $1 > tmp 2>&1"; then
cat tmp >> warn
fi
if ! run_untrusted "MPLBACKEND=Agg python3 plots.py > tmp 2>&1"; then
cat tmp >> warn
fi
rm -f tmp
}
# compile/run on a remote "sandbox"
# NOTE: update remotely_octave() when changing this
remotely()
{
rhost=$1
autolink=`$BASILISK/qcc -autolink -progress -source -disable-dimensions $CFLAGS $src`
chksum=`(cat $test.s && pwd && echo $test) | $GENSUM | cut -d' ' -f1`.$ext
mkdir $test/$chksum
cd $test
run=`echo $test | sed 's/^~//'`
if test "$run" != "$test"; then
cp -f $run.* $chksum 2> /dev/null || true
fi
cp -f $test.* $chksum 2> /dev/null || true
rm -f $chksum/$src.html
if test `echo "$test" | sed 's/^~//'` != "$test"; then
cd $chksum
for f in "$test".*; do
mv -f "$f" `echo "$f" | sed 's/^~//'`
done
cd ..
fi
mv -f ../_$src $chksum/$src
if grep -q '#define _GPU 1' $chksum/$src; then
TSP="TS_SOCKET=/tmp/tsp-gpu tsp"
TIMEOUT="OMP_NUM_THREADS=1 DISPLAY=:0 $TIMEOUT"
else
TSP=tsp
fi
PCC="\$CC99"
case "$CFLAGS" in
*-cadna*) PCC="\$CADNACC" ;;
esac
PCFLAGS=`echo $CFLAGS | sed -e 's/-grid=[^-]*//g' -e 's/-cadna//g' -e 's/-progress//g' -e 's/-Wdimensions//g' -e 's/-disable-dimensions//g' -e 's/-cpu//g' -e 's/-gpu//g'`
NCORES=`echo $EXEC | awk 'BEGIN{ np = 1 }{
if ($1 == "mpirun") np = $3; }END{ print np;}'`
case "$CFLAGS" in
*-fopenmp*)
NCORES=8
TIMEOUT="OMP_NUM_THREADS=8 $TIMEOUT"
;;
esac
cat <<EOF >$chksum/$chksum.sh
#!/bin/bash
run_untrusted()
{
status=0
notrust=/tmp/$chksum
if ! sudo -n -u basilisk-untrusted -- bash -c "source /home/basilisk/.bashrc-untrusted && mkdir \$notrust && cp -arf * \$notrust && cd \$notrust && rm -f completing && \$1 && touch completing"; then
status=1
fi
if ! cp -arf \$notrust/* .; then
status=1
fi
sudo -n -u basilisk-untrusted -- rm -rf \$notrust/
return \$status
}
$PCC $PCFLAGS -I\$HOME/include -o $chksum $src -L\$HOME/lib $LIBS $autolink -lm > log 2>&1 || mv -f log fail
rm -f $src
if ! test -f fail; then
pass=true
if test -n "$EXEC" -o -z "`which gdb`"; then
if ! run_untrusted "$TIMEOUT $EXEC ./$chksum 2> $log > $out"; then
pass=false
fi
else
if ! run_untrusted "$TIMEOUT gdb -batch -return-child-result -ex \"run 2>> $log > $out\" $chksum > gdb.log 2> gdb.err" > gdb.log 2> gdb.err; then
pass=false
cat gdb.err >> $log
awk '
{
if (NF > 0)
a[nb++] = \$0;
}
/ at .*:[0-9]*\$/ {
for (i = 0; i < nb - 1; i++)
print \$(NF) ":error: " a[i];
}
' < gdb.log >> $log
fi
rm -f gdb.log
fi
if test -f log-1; then
mv -f $log log-0
cat log-* > $log
fi
if \$pass; then
if test -f $test.$ref; then
ref=$ref
else
ref=ref
fi
if test -f $test.\$ref; then
echo diff $log $test.\$ref > fail
diff $log $test.\$ref >> fail && rm -f fail
rm -f $test.\$ref
fi
else
if ! test -s $log; then
echo "The code may have timed out." > $log
fi
sed "s/^$chksum: //g" $log > fail
fi
fi
rm -f $chksum $src $chksum.sh $test.*ref
tar czf \$HOME/$chksum.tgz *
rm -r -f \$HOME/$chksum
EOF
tar czf $chksum.tgz $chksum
rm -r -f $chksum/
if ! ( scp $chksum.tgz $rhost: && rm -f $chksum.tgz && \
ssh $rhost bash -c "\"tar xmzf $chksum.tgz && cd $chksum && $TSP -N \$(test \$($TSP -S) -le $NCORES && echo \$($TSP -S) || echo $NCORES) -L $test -n nice -19 bash $chksum.sh\" && sleep 0 && echo $chksum && echo $rhost && $TSP -w" > tspid.$ext && \
scp -q $rhost:$chksum.tgz $chksum.tgz && \
ssh $rhost rm -r -f $chksum.tgz && \
tar xmzf $chksum.tgz && \
rm -f $chksum.tgz $src $test.*ref progress) > /dev/null 2>> ssh.log;
then
cat ssh.log >> fail
fi
rm -f ssh.log
# generate graphics
doplots "$test"
if test -f fail; then
if test -f ../$test.$ext; then
mv -f ../$test.$ext fail.$ext
else
short=`echo $test | sed 's/^~//'`
test -f ../$short.$ext && mv -f ../$short.$ext fail.$ext
fi
echo
echo \[$test.$ext\]
cat fail
else
touch pass
fi
# Complete
rm -f *pid.$ext completing
}
octave_deps()
{
if test -z "$DOCUMENT_ROOT"; then
d=`pwd`
while ! test -d _darcs; do
cd ..
done
DOCUMENT_ROOT=`pwd`
cd "$d"
fi
path=`dirname $1`" "`grep --only-matching 'addpath[ \t]*( *"[^"]*" *)' $1 | sed -e 's/addpath[ \t]*( *"\([^"]*\)" *)/\1/g' -e "s|^~|$DOCUMENT_ROOT|g"`
for i in `grep -E --only-matching -h '[a-zA-Z_0-9]+[ \t]*\(' $1 | sort | uniq | sed 's/(//g'`; do
for p in $path; do
test -f "$p/$i.m" -a "$p/$i.m" != "$1" && echo "$p/$i.m" && octave_deps "$p/$i.m" && break;
done
done | sort | uniq
}
# compile/run on a remote "sandbox" (OCTAVE)
remotely_octave()
{
rhost=$1
chksum=`(cat $test.s && pwd && echo $test) | $GENSUM | cut -d' ' -f1`.$ext
mkdir $test/$chksum
mkdir $test/$chksum/lib/
cp -f $test.m `octave_deps ./$test.m` $test/$chksum/lib/
cd $test
run=`echo $test | sed 's/^~//'`
if test "$run" != "$test"; then
cp -f $run.* $chksum 2> /dev/null || true
fi
cp -f $test.* $chksum 2> /dev/null || true
rm -f $chksum/$src.html
if test `echo "$test" | sed 's/^~//'` != "$test"; then
cd $chksum
for f in "$test".*; do
mv -f "$f" `echo "$f" | sed 's/^~//'`
done
cd ..
fi
cp -f ../$src $chksum/$src
cat <<EOF >$chksum/$chksum.sh
#!/bin/bash
run_untrusted()
{
status=0
notrust=/tmp/$chksum
if ! sudo -n -u basilisk-untrusted -- bash -c "source /home/basilisk/.bashrc-untrusted && mkdir \$notrust && cp -arf * \$notrust && cd \$notrust && rm -f completing && \$1 && touch completing"; then
status=1
fi
if ! cp -arf \$notrust/* .; then
status=1
fi
sudo -n -u basilisk-untrusted -- rm -rf \$notrust/
return \$status
}
# workaround for octave bug with ~ in file names
src1="$src"
if [[ "$src" = ~* ]]; then
src1=_\${src1##\~};
ln -s -f "$src" "\$src1"
fi
pass=true
if ! run_untrusted "$TIMEOUT octave-cli --path lib -W -H -q \$src1 2> $log > $out" || grep -q '^error:' $log; then
pass=false
fi
if [[ "\$src1" != "$src" ]]; then
rm -f "\$src1"
fi
if \$pass; then
if test -f $test.$ref; then
ref=$ref
else
ref=ref
fi
if test -f $test.\$ref; then
echo diff $log $test.\$ref > fail
diff $log $test.\$ref >> fail && rm -f fail
rm -f $test.\$ref
fi
else
if ! test -s $log; then
echo "The code may have timed out." > $log
fi
cp -f $log fail
fi
if ! test -f fail; then
touch pass
fi
rm -r -f $chksum $src $chksum.sh $test.*ref lib/
tar czf \$HOME/$chksum.tgz *
rm -r -f \$HOME/$chksum
EOF
tar czf $chksum.tgz $chksum
rm -r -f $chksum/
if ! ( scp $chksum.tgz $rhost: && rm -f $chksum.tgz && \
ssh $rhost bash -c "\"tar xmzf $chksum.tgz && cd $chksum && tsp -n nice -19 bash $chksum.sh\" && sleep 0 && echo $chksum && echo $rhost && tsp -w" > tspid.$ext && \
scp -q $rhost:$chksum.tgz $chksum.tgz && \
ssh $rhost rm -r -f $chksum.tgz && \
tar xmzf $chksum.tgz && \
rm -f $chksum.tgz $src $test.*ref progress) > /dev/null 2>> ssh.log;
then
cat ssh.log >> fail
fi
rm -f ssh.log
if test -f fail; then
if test -f ../$test.$ext; then
mv -f ../$test.$ext fail.$ext
else
short=`echo $test | sed 's/^~//'`
test -f ../$short.$ext && mv -f ../$short.$ext fail.$ext
fi
echo
echo \[$test.$ext\]
cat fail
else
touch pass
fi
# Complete
rm -f *pid.$ext completing
}
checksum()
{
if grep $1 $2 | $CHECKSUM; then
return 0;
else
return 1;
fi
}
# check sum for $src
source_modified=false
if ! test -f $test.$ext || ! checksum $src $test.$ext; then
source_modified=true
fi
killchildren()
{
p=$1
c=`ps -o pid= --ppid $p`
kill $p
while test -n "$c"; do
p=$c
c=`ps -o pid= --ppid $p`
kill $p
done
}
# check sum for test.s
if test -f $test.$ext && checksum $test.s $test.$ext; then
touch $test.$ext
echo "make: '$test.$ext' is up to date."
if $source_modified; then
if test -n "$SANDBOX"; then
rm -f $test/warn
$AWK -f $BASILISK/gnuplot.awk < $src > $test/plots
$AWK -f $BASILISK/python.awk < $src > $test/plots.py
chksum=`(cat $test.s && pwd && echo $test) | $GENSUM | cut -d' ' -f1`.$ext
cd $test
doplots "$test"
cd ..
fi
$GENSUM $src $test.s > $test.$ext
fi
# check sum for test.s in fail.tst
elif ! test -f $test/fail.$ext || ! test -f $test/fail || ! checksum $test.s $test/fail.$ext; then
if test -n "$SANDBOX" -a -f $test/pid.$ext; then
killchildren `cat $test/pid.$ext`
if test -f $test/tspid.$ext; then
ssh $SANDBOX killtest `cat $test/tspid.$ext`
fi
rm -f $test/*pid.$ext
fi
rm -f $test.$ext $test/pass \
$test/fail $test/fail.* $test/plot.png $test/plots $test/core
$GENSUM $src $test.s > $test.$ext
mkdir -p $test && cp -f $src $test
$AWK -f $BASILISK/gnuplot.awk < $src > $test/plots
$AWK -f $BASILISK/python.awk < $src > $test/plots.py
cp -f $test.* $test 2> /dev/null || true
run=`echo $test | sed 's/^~//'`
if test "$run" != "$test"; then
cp -f $run.* $test 2> /dev/null || true
fi
rm -f $test/*.[sd] $test/*.*tst $test/$src.html 2> /dev/null || true
if test -n "$SANDBOX"; then
case "$src" in
*.c) ( remotely $SANDBOX ) & ;;
*.m) ( remotely_octave $SANDBOX ) & ;;
esac
echo $! > $test/pid.$ext
sleep 1
else
case "$src" in
*.c) locally ;;
*.m) locally_octave ;;
esac
rm -f $test/plots $test/plots.py
fi
fi
if test -f $test/fail; then
cat $test/fail
if test -f $test.$ext; then
if test -n "$SANDBOX"; then
mv -f $test.$ext $test/fail.tst
else
rm -f $test.$ext $test/fail.tst
fi
fi
exit 1
elif test -f $test/pid.$ext; then
echo \[$test.$ext on $SANDBOX \(`cat $test/pid.$ext`\)\]
echo " running..."
fi