#!/bin/bash
# vim: tw=0
set -o errexit

########################################################################
##
## This script is meant to be first symlinked to under a file name
## indicating the host scheme and architecture, and then invoked in a
## context with the following environment variables set: TRACK DARTLOG
##
## Here is a sample simple wrapper script meant to demonstrate how 
## to first invoke this script, and then send the resulting dart log
## to the dart server (uighur.ccs.neu.edu in this case)
# 
# LOGFILE=/home/henchman/build.`date +%F`.log
# export TRACK="Nightly"
# export DARTLOG="/home/henchman/larcenytest/dart-native-sub.xml"
# /home/henchman/bin/checkout-larceny-linux86-native >> $LOGFILE 2>&1
# java -jar /home/henchman/jars/DartClient.jar -s uighur.ccs.neu.edu dart $DARTLOG >> $LOGFILE 2>&1
# cat $LOGFILE | mail -s "Nightly build log" pnkfelix@pnkfx.org
#
########################################################################

# Override this variable to submit to other Dart tracks.
TRACK=${TRACK:-"Experimental"}

# We should not have to do 'always-source here, but Larceny is
# currently not smart enough to back off when encountering a fasl file
# generated for a target that differs from the host.
SETUP_ARGS="'exit-on-error 'quiet-load 'always-source"

WARNINGNUM=1
# SVNTRUNK="http://larceny.ccs.neu.edu/svn/trunk/larceny_src"
SVNTRUNK="http://uighur.ccs.neu.edu:3457/larceny-svn/trunk/larceny_src"

FINAL_LARCENY_SCRIPT=larceny
FIXPATH_CMD=echo
FINAL_LARCENY_BIN=larceny.bin
FINAL_TWOBIT_BIN=larceny.bin

# Setup options (based on script's name)
case "`basename "$0"`" in

    checkout-larceny-solaris-native)
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'solaris 'native 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/sparc-HHH-heap.sch
	DIRNAME=native
	MAKETEXTSAFE=native2ascii
    ;;
    checkout-mzscheme-solaris-native)
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'solaris 'native 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/sparc-HHH-heap.sch
	DIRNAME=mznative
	MAKETEXTSAFE=native2ascii
    ;;
    checkout-larceny-solaris-petit)
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'solaris"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=petit
	MAKETEXTSAFE=native2ascii
    ;;
    checkout-mzscheme-solaris-petit)
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'solaris"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=mzpetit
	MAKETEXTSAFE=native2ascii
    ;;

    checkout-larceny-linux86-petit)
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'linux86"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=petit
	MAKETEXTSAFE="iconv -t utf8 -c"
    ;;
    checkout-larceny-linux86-petit-nasm)
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'linux86 'nasm"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=petit
    ;;
    checkout-mzscheme-linux86-petit)
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'linux86"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=mzpetit
	MAKETEXTSAFE="iconv -t utf8 -c"
    ;;
    checkout-mzscheme-linux86-petit-nasm)
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'linux86 'nasm"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=mzpetit
    ;;
#    checkout-larceny-linux86-dotnet)
#	SCHEME_PGM=larceny
#	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'linux86 'dotnet"
#	FINAL_LARCENY_BIN=dotnet.heap.exe
#	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
#	DIRNAME=dotnet
#    ;;
#    checkout-mzscheme-linux86-dotnet)
#	SCHEME_PGM=mzscheme
#	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'linux86 'dotnet"
#	FINAL_LARCENY_BIN=dotnet.heap.exe
#	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
#	DIRNAME=mzdotnet
#    ;;
    checkout-larceny-linux86-native)
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'linux86 'sassy 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=native
	MAKETEXTSAFE="iconv -t utf8 -c"
    ;;
    checkout-mzscheme-linux86-native)
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'linux86 'sassy 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=mznative
	MAKETEXTSAFE="iconv -t utf8 -c"
    ;;

    checkout-larceny-macosx-petit)   # PPC!
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'macosx"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=petit
	MAKETEXTSAFE=native2ascii
    ;;
    checkout-mzscheme-macosx-petit)  # PPC!
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'macosx"
	FINAL_LARCENY_BIN=petit-larceny.bin
	FINAL_TWOBIT_BIN=twobit.bin
	HEAP_DUMP_SCRIPT=src/Build/petit-HHH-heap.sch
	DIRNAME=mzpetit
	MAKETEXTSAFE=native2ascii
    ;;
    checkout-larceny-macosx-native)  # IA32!
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'macosx-el 'sassy 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=native
	MAKETEXTSAFE=native2ascii
    ;;
    checkout-mzscheme-macosx-native) # IA32!
	SCHEME_PGM="mzscheme -f "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'mzscheme 'host: 'macosx-el 'sassy 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=mznative
	MAKETEXTSAFE=native2ascii
    ;;


    *)
    case "`uname`" in
	SunOS)
	echo "Setup arguments not specified; assuming Solaris native build."
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'solaris 'native 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/sparc-HHH-heap.sch
	DIRNAME=default
	MAKETEXTSAFE=native2ascii
	;;
	Linux)
	echo "Setup arguments not specified; assuming linux86 native build."
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'linux86 'sassy 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=default
	MAKETEXTSAFE="iconv -t utf8 -c"
	;;
	Darwin)
	echo "Setup arguments not specified; assuming macosx86 native build."	
	SCHEME_PGM="larceny -- "
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'macosx-el 'sassy 'string-rep: 'flat4"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=default
	MAKETEXTSAFE=native2ascii
	;;
        CYGWIN*)
	echo "Setup arguments not specified; assuming win32 native build."	
	HOME="`pwd`"
	SCHEME_PGM="`pwd`/HostLarceny/larceny.bat --"
	SETUP_ARGS="${SETUP_ARGS} 'scheme: 'larceny  'host: 'win32 'sassy 'string-rep: 'flat4"
	FINAL_LARCENY_BIN=larceny.bin.exe
	FINAL_TWOBIT_BIN=larceny.bin.exe
	FINAL_LARCENY_SCRIPT=larceny.bat
	FIXPATH_CMD="cygpath -w"
	HEAP_DUMP_SCRIPT=src/Build/iasn-HHH-heap.sch
	DIRNAME=default
	### XXX This is bad:
	MAKETEXTSAFE="cat"
        ;;
	*)
	echo "Setup arguments not specified; unknown target architecture."
	exit 1
	;;
    esac
    ;;
esac

# Perhaps add more to this, like the hour and minute?
TODAY=`date +%Y-%m-%d` 
DIR=${HOME}/larcenytest/larceny-${DIRNAME}-${TRACK}-${TODAY}
DARTLOG=${DARTLOG:-"${HOME}/larcenytest/dart-sub.xml"}
TEMPLOG=${TEMPLOG:-"${HOME}/larcenytest/temp.log"}
TEMPSCM=${TEMPSCM:-"${HOME}/larcenytest/temp.scm"}
REALSCM="`${FIXPATH_CMD} "${TEMPSCM}" | sed 's@\\\\@\\\\\\\\@g'`"
CALCDATEDART="date +%Y-%m-%dT%H:%M:%S%z"
LARCENY_HEAP_DUMP_SCRIPT="`echo ${HEAP_DUMP_SCRIPT} | sed 's/HHH/larceny/'`"
TWOBIT_HEAP_DUMP_SCRIPT="`echo ${HEAP_DUMP_SCRIPT} | sed 's/HHH/twobit/'`"

# Initialize the Dart log
echo '<?xml version="1.0" encoding="utf-8"?>'            >  $DARTLOG

mkdir -p ${DIR}

function dartlog {
   echo $@ >> $DARTLOG
}

function cmdsetstatus {
    if eval "$1" ; then
	STATUS="passed" 
    else
	STATUS="failed"
    fi
}

##A trick for outputting stdout, stderr _and_ stdout&stderr to three separate files
##with the appropriate ordering on messages.  Does not preserve the status code of
##the argument command (given as i$1)
#function cmdlog {
#    ((($1 | tee ${TEMPOUT}) 3>&1 1>&2 2>&3 | tee ${TEMPERR}) 3>&1 1>&2 2>&3) > ${TEMPLOG} 2>&1
#}

function quotetextfile_cdata { # doesn't work for some reason...
   dartlog '<![CDATA['
   cat $1 >> ${DARTLOG}
   dartlog ']]>'
}

function quotetextfile { # esc_html
  # On CCIS Sun, iconv doesn't have a working iconv with the -c option. 
  # On non CCIS Sun, we don't have native2ascii.
  cat $1 | ${MAKETEXTSAFE} | sed -e 's/\&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' >> ${DARTLOG}
}

dartlog '<DartSubmission version="2.0" createdby="' $0 '">'
dartlog '<Site>'`hostname`'</Site>'
dartlog '<BuildName>'`uname` $DIRNAME'</BuildName>'
dartlog '<Track>'${TRACK}'</Track>'
dartlog '<DateTimeStamp>'`$CALCDATEDART`'</DateTimeStamp>'

# args: DartStageName HumanStageName WorkingDir CmdString
function eval_stage {
   dartlog '<Test>'
   dartlog '<Name>'.Build.Stage$1'</Name>'
   dartlog '<Measurement name="StageName" type="text/string">'$2'</Measurement>'
   dartlog '<Measurement name="StartDateTime" type="text/string">'`$CALCDATEDART`'</Measurement>'

   pushd "$3" > /dev/null
   SECS_BEGIN=`date +%s`
   cmdsetstatus "$4" > ${TEMPLOG} 2>&1
   # cmdsetstatus "$4" 2>&1 | tee ${TEMPLOG}
   SECS_FINIS=`date +%s`
   popd     > /dev/null

   dartlog '<Measurement name="ElapsedTime" type="numeric/float">'
   dartlog `echo "(($SECS_FINIS - $SECS_BEGIN) * 0.0166666)" | bc`
   dartlog '</Measurement>'
   dartlog '<Measurement name="Execution Time" type="numeric/float">'
   dartlog `echo "(($SECS_FINIS - $SECS_BEGIN) * 0.0166666)" | bc`
   dartlog '</Measurement>'
   dartlog '<Status>'
   dartlog ${STATUS}
   dartlog '</Status>'
   dartlog '<Measurement name="EndDateTime"  type="text/string">'`$CALCDATEDART`'</Measurement>'
   dartlog '<Measurement name="BuildStatus"  type="text/string">'${STATUS}'</Measurement>'
   dartlog '<Measurement name="BuildCommand" type="text/string">'$4'</Measurement>'
   dartlog '<Measurement name="Log" type="text/xml">' 
   quotetextfile ${TEMPLOG}
   dartlog '</Measurement>'
 
   # Warnings and such have to be _separate_ tests; if they appear as a 
   # subtree within this <Test>, the Dart server will ignore it.  :(
   dartlog '</Test>'

   if grep -qi warning ${TEMPLOG} ; then
      grep -n -i warning ${TEMPLOG} | while read WARNINGLINE ; do
	  WARNINGLINENUM=`echo $WARNINGLINE | sed -e 's/\([^:]*\):\(.*\)/\1/'`
	  WARNINGLINETXT=`echo $WARNINGLINE | sed -e 's/\([^:]*\):\(.*\)/\2/'`
	  cat >> ${DARTLOG} <<EOF
<Test>
<Name>.Build.Stage${1}.Warning${WARNINGNUM}</Name>
<Measurement name="BuildLogLine" type="text/string">${WARNINGLINENUM}</Measurement>
<Measurement name="Text" type="text/string">${WARNINGLINETXT}</Measurement>
</Test>
EOF
          # Each Warning needs a unique id in the dart submission. :(
	  let WARNINGNUM+=1
      done
   fi 
}

function host_scm_cmd {
   cat > ${TEMPSCM} <<EOF
(load "setup.sch")
(setup ${SETUP_ARGS} )
(build-config-files)
(load-compiler)
;(set! *make-verbose* #f)
$4
(exit)
EOF
   eval_stage "$1" "$2" "$3" "${SCHEME_PGM} ${REALSCM}"
}

eval_stage   bSvnCheckout  "1. svn checkout"       ${DIR}             "svn checkout -q ${SVNTRUNK}"
host_scm_cmd dBuildHeap    "2. bootstrap heap"     ${DIR}/larceny_src "(build-heap)"
host_scm_cmd eRuntime      "3. larceny runtime"    ${DIR}/larceny_src "(build-runtime)"
host_scm_cmd fExecutable   "4. larceny executable" ${DIR}/larceny_src "(build-executable)"
host_scm_cmd gLarcenyFasl  "5. larceny fasl files" ${DIR}/larceny_src "(build-larceny-files)"
eval_stage   hLarcenyHeap  "6. larceny heap"       ${DIR}/larceny_src "echo | ./${FINAL_LARCENY_BIN} -stopcopy -- ${LARCENY_HEAP_DUMP_SCRIPT}"
host_scm_cmd iLarcenyFasl  "7. twobit fasl files"  ${DIR}/larceny_src "(build-twobit)"
eval_stage   jTwobitHeap   "8. twobit heap"        ${DIR}/larceny_src "echo | ./${FINAL_TWOBIT_BIN} -stopcopy -- ${TWOBIT_HEAP_DUMP_SCRIPT}"
eval_stage   kChkDiskUsage "9. measure disk usage" ${DIR}             "du -skh larceny_src; df -kh"

BUILD_STATUS=${STATUS}

function dart_post_test {
    dartlog '<Status>'
    dartlog ${STATUS}
    dartlog '</Status>'
    dartlog '<Measurement name="Output" type="text/text">'
    quotetextfile ${TEMPLOG}
    dartlog '</Measurement>'
}

function library_test {
    dartlog '<Test>'
    dartlog '<Name>'.Test.Lib.$1.$2'</Name>'
    pushd ${DIR}/larceny_src/test/Lib > /dev/null
    cat > ${TEMPSCM} <<EOF
(error-handler (lambda l (display l) (newline) (exit 115)))
(compile-file "test.sch")
(compile-file "$1.sch")
(load "test.fasl")
(load "$1.fasl")
(let ((num-fails 0))
  (test-reporter (lambda (id got expected) (set! num-fails (+ 1 num-fails))))
  (run-$2-tests)
  (exit num-fails))
EOF
    SECS_BEGIN=`date +%s`
    cmdsetstatus "../../${FINAL_LARCENY_SCRIPT} -- ${REALSCM}" > ${TEMPLOG} 2>&1
    SECS_FINIS=`date +%s`
    dartlog '<Measurement name="Execution Time" type="numeric/float">'
    dartlog `echo "(($SECS_FINIS - $SECS_BEGIN) * 0.0166666)" | bc`
    dartlog '</Measurement>'
    dart_post_test
    dartlog '</Test>'
    popd > /dev/null
}

## Library tests
library_test bool       boolean
library_test char       char
library_test string     string
library_test normalization normalization
## library_test complex
library_test bytevector bytevector
library_test io         io
library_test hashtable  hashtable
library_test pred       predicate
library_test number     number
library_test fact       fact 
library_test fib        fib
library_test ctak       ctak
## library_test env       env
library_test dynamic-wind dynamic-wind
library_test regression regression
library_test fixnums    fixnum
library_test wcm        wcm
library_test record     record
library_test condition  condition
library_test enum       enumset

function compiler_tests {
    dartlog '<Test>'
    dartlog '<Name>'.Test.Compiler.$1'</Name>'
    pushd ${DIR}/larceny_src/test/Compiler > /dev/null
    cat > ${TEMPSCM} <<EOF
(error-handler (lambda l (display l) (newline) (exit 116)))
(load "run-tests.sch")
(let ((num-fails 0))
  (test-reporter (lambda (id got expected) (set! num-fails (+ 1 num-fails))))
  (run-compiler-tests $1)
  (exit num-fails))
EOF
    SECS_BEGIN=`date +%s`
    cmdsetstatus "../../${FINAL_LARCENY_SCRIPT} -- ${REALSCM}" > ${TEMPLOG} 2>&1
    SECS_FINIS=`date +%s`
    dartlog '<Measurement name="Execution Time" type="numeric/float">'
    dartlog `echo "(($SECS_FINIS - $SECS_BEGIN) * 0.0166666)" | bc`
    dartlog '</Measurement>'
    dart_post_test
    dartlog '<Status>'
    dartlog ${STATUS}
    dartlog '</Status>'

    dartlog '</Test>'
    popd > /dev/null
}

## Compiler tests (sanity switches only, but we could add extra passes...)
compiler_tests sanity-switches
## compiler_tests basic-switches
## compiler_tests optimization-switches
## compiler_tests backend-switches

# Benchmarks
pushd ${DIR}/larceny_src/test/Benchmarking/CrossPlatform > /dev/null
LARCENY=`pwd`/../../../${FINAL_LARCENY_SCRIPT} ./bench -r 3 larceny all > ${TEMPLOG} 2>&1
cat > ${TEMPSCM} <<EOF
(error-handler (lambda l (display l) (newline) (exit 117)))
(load "summarize.sch")
(let ((os (open-output-string)))
    (with-output-to-port os
      (lambda () 
	((summarize larceny-results) "results.Larceny-r5rs")))
    (let* ((str (get-output-string os))
	   (is (open-input-string str))
	   (decoded (decode-summary is))
	   (lines (caddr decoded))
	   (format-measurement (lambda (name type val)
                                 (format #t "<Measurement name=~s type=~s>~s</Measurement>" 
                                         name type val)
                                 (newline))))
      (for-each 
       (lambda (line)
         (cond ((eq? (list-ref line 1) 'Error:)
                (let ((name (list-ref line 0)))
                  (format #t "<Test><Name>.DynamicAnalysis.~s.~s</Name>" name name)
                  (format #t "<Status>failed</Status>")
                  (newline)
                  (format #t "<Measurement name=\"Output\" type=\"text/text\">~a ~a</Measurement>"
                          (list-ref line 1)
                          (list-ref line 2))
                  (format #t "</Test>")
                  (newline)))
               (else
                  (let ((name (list-ref line 0))
                        (cpu  (list-ref line 1))
                        (real (list-ref line 2))
                        (gc   (list-ref line 3))
                        (numt "numeric/integer"))
                    (format #t "<Test><Name>.DynamicAnalysis.~s.~s</Name>" name name)
                    (format #t "<Status>passed</Status>")
                    (newline)
                    (format-measurement "Execution Time" "numeric/float" 
                                        (exact->inexact (/ real 60000)))
                    (for-each format-measurement
                              (list "cpu time" "real time" "gc time")
                              (list numt       numt        numt)
                              (list cpu        real        gc))
                    (format #t "</Test>")
                    (newline)))))
       lines)))
(exit)
EOF
../../../${FINAL_LARCENY_SCRIPT} -nobanner -- -e '(herald #f)' ${REALSCM} >> ${DARTLOG}

cat results.Larceny-r5rs >> Results/results.Larceny
rm results.Larceny-r5rs
popd > /dev/null

dartlog '</DartSubmission>'

if [ $BUILD_STATUS == "failed" ] 
then exit 1
fi
