#!/bin/sh

#----------------------------------------------------------------------------
# Automated build and test for Valgrind.  Compares Valgrind from 24 hours
# ago with the current one.  See the README.txt on how to run it.
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# Helper functions
#----------------------------------------------------------------------------

runcmd () {
   logfile=$1
   str=$2
   shift 2

   # Header in short logfile.
   # We use "printf" to avoid printing a newline;  "echo -n" isn't POSIX and
   # so isn't supported on all systems.
   printf "   $str  ... " >> $logfile.short

   # Header and command in verbose logfile
   printf "   $str  ... " >> $logfile.verbose
   echo "$*" >> $logfile.verbose

   # Run the command
   ("${ABT_EVAL}" "$*") >> $logfile.verbose 2>&1
   res=$?

   # Write result to the short logfile
   if [ $res = 0 ]
   then
      echo "done"   >> $logfile.short
   else
      echo "failed" >> $logfile.short
   fi

   return $res
}

#----------------------------------------------------------------------------
# Startup
#----------------------------------------------------------------------------

valgrind_git_repo="git://sourceware.org/git/valgrind.git/"

# Must have exactly two arguments
if [ $# -ne 2 ] ; then
    echo "usage: $0 /path/to/valgrind/nightly <tag>"
    exit 1
fi

# Get args from command line
DIR=$1
TAG=$2

# Get times and date
START=`date "+%F %H:%M:%S %Z"`

cd $DIR

# Clean up output files produced by a previous run.
rm -rf diffs diffs.txt diff.short final new.short new.verbose old.short old.verbose
rm -rf sendmail.log unchanged.log valgrind-repo valgrind-old valgrind-new

# Setup any relevant environment variables from conf/<tag>.conf.
. conf/$TAG.conf
if [ "${ABT_JOBS}" = "" ]; then
  ABT_JOBS=1
fi
if [ "${ABT_EVAL}" = "" ]; then
  ABT_EVAL="eval"
fi
if [ "${ABT_RUN_REGTEST}" = "" ]; then
  ABT_RUN_REGTEST="make regtest"
fi

if [ "${ABT_PERF_TOOLS}" = "" ]; then
  ABT_PERF_TOOLS="--tools=none,memcheck,callgrind,helgrind,cachegrind,drd,massif"
fi
if [ "${ABT_PERF_REPS}" = "" ]; then
  ABT_PERF_REPS="--reps=3"
fi


#----------------------------------------------------------------------------
# Check out, build, test
#----------------------------------------------------------------------------

# Clone the GIT repository first. We will query it and copy it afterwards.
git clone --quiet $valgrind_git_repo valgrind-repo

cd valgrind-repo
commits=$( git rev-list --after="24 hours ago" master )
if [ -z "$commits" ]; then
  cd ..
  echo "There are no new commits since yesterday -- skipping nightly build." > unchanged.log
  exit 0
fi
cd ..

# Do everything twice -- once for the 24 hours old Valgrind, and once 
# for the current one.
for logfile in old new ; do

   # Remove old short and verbose log files, and start the new ones
   for ext in short verbose ; do
      echo > $logfile.$ext
   done

   # Choose the current Valgrind, or one from 24 hours ago
   if [ $logfile = "old" ] ; then
      git_commit=$( echo $commits | awk '{print $NF}' )
   else
      git_commit="HEAD"
   fi

   # Check out, build, run tests
   runcmd $logfile \
          "Checking out Valgrind source tree" \
          "cp -r --preserve=links valgrind-repo valgrind-$logfile && cd valgrind-$logfile && git checkout ${git_commit}" && \
   \
   runcmd $logfile \
          "Configuring valgrind             " \
          "cd valgrind-$logfile && ./autogen.sh  && ./configure --prefix=`pwd`/valgrind-$logfile/Inst ${ABT_CONFIGURE_OPTIONS}" && \
   \
   runcmd $logfile \
          "Building valgrind                " \
          "cd valgrind-$logfile && make -j ${ABT_JOBS} && make -j ${ABT_JOBS} check && make install" && \
   \
   runcmd $logfile \
          "Running regression tests         " \
          "cd valgrind-$logfile && ${ABT_RUN_REGTEST}"

   # Stash away the return code of the regression run
   regrun_rc=$?

   # Grab some indicative text for the short log file -- if the regtests
   # succeeded, show their results.  If we didn't make it that far, show the
   # last 20 lines.
   egrep -q '^== [0-9]+ tests' $logfile.verbose && (
      echo >> $logfile.short
      echo "Regression test results follow" >> $logfile.short
      echo >> $logfile.short
      awk '/^== [0-9]+ tests/, /^$/ { print }' $logfile.verbose >> $logfile.short
   # Check the return code of the regression run; we might have successfully
   # run all tests but still failed in the post-regtest checks.
      if [ $regrun_rc != "0" ]; then
         echo >> $logfile.short
         echo "Last 20 lines of verbose log follow" >> $logfile.short \
         echo >> $logfile.short
         tail -20 $logfile.verbose >> $logfile.short
      fi
   ) || (
      echo >> $logfile.short
      echo "Last 20 lines of verbose log follow" >> $logfile.short \
      echo >> $logfile.short
      tail -20 $logfile.verbose >> $logfile.short
   )
done

# if requested, run regression tests and produce results in perflogfile.out
if [ "${ABT_PERF}" != "" ]; then
   cd valgrind-new
   echo ${ABT_PERF_TOOLS} ${ABT_PERF_REPS} ${ABT_PERF} > ../perflogfile
   (time perl perf/vg_perf ${ABT_PERF_TOOLS} ${ABT_PERF_REPS} ${ABT_PERF} perf) >> ../perflogfile 2>&1
   cd ..
fi


#----------------------------------------------------------------------------
# Prepare results and send
#----------------------------------------------------------------------------

# Get times and date
END=`date "+%F %H:%M:%S %Z"`

# Gather some information about this run and its environment
valgrind_revision=$( ./valgrind-new/vg-in-place -v --version )
gcc_version="`gcc --version 2> /dev/null | head -1`"
gdb_version="`gdb --version 2> /dev/null | head -1`"
as_version="`as --version 2> /dev/null | head -1`"
if [ `uname -o` = "Solaris" ]; then
  libc="Solaris libc"
else
  libc_so="`ls -1 /lib/libc.so.* /lib64/libc.so.* /lib32/libc.so.* /lib/*-linux-gnu/libc.so.* 2>/dev/null | tail -1`"
  libc="unknown"
  if [ "x$libc_so" != "x" ]; then
    if [ -e "$libc_so" -a -r "$libc_so" ]; then
      libc="`$libc_so | head -1`"
    fi
  fi
  libc=`echo $libc | sed "s/, by Roland.*//"`
fi
uname_stuff="`uname -mrs`"
if [ -r /etc/os-release ]; then
  vendor_stuff="`. /etc/os-release; echo ${NAME} ${VERSION}`"
elif which lsb_release >/dev/null 2>&1; then
  vendor_stuff="`lsb_release -sicr | xargs echo`"
elif [ -e "/etc/issue.net" -a -r "/etc/issue.net" ]; then
  vendor_stuff="`cat /etc/issue.net | head -1`"
elif [ -r /etc/release ]; then
  vendor_stuff="`cat /etc/release | head -1`"
else
  vendor_stuff="unknown"
fi

echo "valgrind revision: $valgrind_revision" >  final
echo "C compiler:        $gcc_version"       >> final
echo "GDB:               $gdb_version"       >> final
echo "Assembler:         $as_version"        >> final
echo "C library:         $libc"              >> final
echo "uname -mrs:        $uname_stuff"       >> final
echo "Vendor version:    $vendor_stuff"      >> final

# 'final' shows the difference between the old and new results
echo                                              >> final
echo "Nightly build on" $TAG "(" $ABT_DETAILS ")" >> final
echo "Started at" $START                          >> final
echo "Ended   at" $END                            >> final

# If the results differ from 24 hours ago, print extra stuff.
diff -C1 old.short new.short > diff.short
changed=$?

if [ $changed != 0 ] ; then
   echo "Results differ from 24 hours ago"      >> final
   changed_str=""
else
   echo "Results unchanged from 24 hours ago"   >> final
   changed_str="(unchanged) "
fi

# Always show the current results.
cat new.short >> final

if [ $changed != 0 ] ; then
   echo "=================================================" >> final
   echo "== Results from 24 hours ago                   ==" >> final
   echo "=================================================" >> final
   cat old.short                                            >> final

   echo                                                     >> final
   echo "=================================================" >> final
   echo "== Difference between 24 hours ago and now     ==" >> final
   echo "=================================================" >> final
   echo                                                     >> final
   cat diff.short                                           >> final
   echo                                                     >> final
fi

# add perf results if requested
if [ "${ABT_PERF}" != "" ]; then
   cat perflogfile                                             >> final
fi

# Gather up the diffs (at most the first 100 lines for each one) into a
# single file.
MAX_LINES=100
diff_files=`find . -name '*.diff*' | sort`
if [ z"$diff_files" = z ] ; then
   echo "Congratulations, all tests passed!" >> diffs
else
   for i in $diff_files ; do
      echo "=================================================" >> diffs
      echo $i                                                  >> diffs 
      echo "=================================================" >> diffs
      if [ `wc -l < $i` -le $MAX_LINES ] ; then
         cat $i                                                >> diffs
      else
         head -n $MAX_LINES $i                                 >> diffs
         echo "<truncated beyond $MAX_LINES lines>"            >> diffs
      fi
   done
fi

# Rename diffs into diffs.txt such that it can be viewed easily with an
# e-mail client.
mv diffs diffs.txt

# Use the conf/<tag>.sendmail script to email the results.
conf/$TAG.sendmail \
   "$changed_str$START nightly build ($TAG, $ABT_DETAILS)" \
   final \
   diffs.txt > sendmail.log 2>&1
