#!/bin/bash

# process command line arguments. By default build all configurations
# (aka. nightly build)
   #
   case "$1" in

      "")   # default: build all configurations
         configs="$configs standard"
         configs="$configs develop"
         configs="$configs tar"
       # configs="$configs git"
         configs="$configs libxcb"
         configs="$configs rational"
         configs="$configs libapl"
         configs="$configs parallel_bench"
         configs="$configs erlang"
         configs="$configs python"
         ;;

      "clean")   # clean up everything
         configs=""
         ;;

      "nightly")   # only refresh /var/www/html/nightly_build_results.html
         configs=""
         ;;

      *)
         configs="$*"
   esac

echo "configs requested: $configs"

# this script builds different GNU APL variants and tests some of them

# First of all, figure and normalize some directory names:
#
# START_dir:
#    the current directory from which this script was called.
#    Used to cd back when all is done. In a nightly build, START_dir is
#    probably /etc/cron.daily.
#
# TOP_BUILD_dir:
#    the top-level directory that contains this script. The different
#    configurations produced by this script are stored in corresponding
#    subdirectories, e.g. in builddir_standard for configuration "standard"
#
#    Example: /home/username/apl-1.9/build
#
# TOP_LOG_dir:
#    the directory where top-level logfiles produced by the builds are stored.
#    Normally TOP_LOG_dir is the same as TOP_BUILD_dir
#
# ROOT_dir:
#    the GNU APL top-level directory. This is the one and only
#    directory than contains the ./configure script for GNU APL.
#    Normally #    TOP_BUILD_dir is $ROOT_dir/build.
#
#    Example: /home/username/apl-1.9
#
# APL_VERSION:
#    the current version (including the 'apl-' prefix).
#
#    Example: apl-1.9
#
# VSRC_dir:
#    this is (despite of its name 'src' the destination directory for a single
#    configuration. For example: the VSRC_dir for configuration "standard" is:
#    $TOP_BUILD_dir/build/builddir_standard/src
#
#    Example: /home/username/apl-1.9/src
#
START_dir=$(pwd)      # e.g. /etc/cron.daily

cd $(dirname $0)      # e.g. /root/apl-1.9
TOP_BUILD_dir=$(pwd)
SVN_VERSION=$(svnversion)

# NOTE: -j8 fails with out of memory on small hosts (like those
# used for nightly builds). By setting J8="" we avoid that
J8="-j8"
J8=""

# TOP_LOG_dir="/tmp"       # where to store longer build logs
TOP_LOG_dir="$TOP_BUILD_dir"   # where to store longer build logs

ROOT_dir=$(dirname $TOP_BUILD_dir)
APL_VERSION=$(basename $ROOT_dir)

# some counters for what happens
declare -i build_errors=0
declare -i missing_files=0
declare -i configs_bad=0
declare -i configs_good=0
declare -i test_errors=0
bad_configs=""

# print the.html table row for one build artifact
# print nothing if the artifact does not exist
#
artifact()
{
local indent="      " # <TR> indentation
local htmlname="$1"   # description (TD1)
local filename="$2"   # anchor text
local linkname="$3"   # anchor href (name of a symbolic link to $2
                      # in /var/www/html/

local TD1="<TD><b>$htmlname</b> : </TD>"
local TD2="<TD><A href=\"$linkname\">$filename</A><$1</TD>"
local noTD2="<TD>missing (<b>$linkname</b>: No such file or directory)<$1</TD>"

if [ -f $linkname ]
then
      echo "
$indent <TR>
$indent   $TD1
$indent   $TD2
$indent </TR>
"
else
      echo "
$indent <TR>
$indent   $TD1
$indent   $noTD2
$indent </TR>
"
fi
}

write_nightly_build_results()
{
# produce a .html file with the nightly build result. We do not
# add the </body> and </html> end tags because we (and also the
# /etc/cron.daily/znightly_build_apl script) will append to the .html
# file later on
#
   if [ -d /var/www/html ]
   then
   { 
echo "<!DOCTYPE html>
<html>
<head>
  <title>Nightly Build Results for GNU APL</title>
  <meta charset="UTF-8"/>
</head>
<body>
  <H1><center>Nightly Build Results for GNU APL</center></H1>
  <HR>
  Build date:  $(date)<br><br>
  SVN version: $SVN_VERSION<br><br>
  $build_errors build errors<br>
  $missing_files missing files (build results)<br>
  $configs_bad configurations failed: $bad_configs<br>
  $configs_good configurations succeeded<br>
  $test_errors testcases failed<br>
"

echo "<H3>Nightly Build Artifacts (if any)</H3>
<TABLE>"
artifact "GNU APL interpreter" "apl" "apl"
artifact "libapl.so (shared library)" "build/xxx" "libapl.so"
artifact "libapl.a  (static library)" "build/xxx" "libapl.a"
echo "</TABLE>"
echo "<H3>Other</H3>"

} > /var/www/html/nightly_build_results.html
   fi
}
# refresh /var/www/html/nightly_build_results.html
if [ "x$1" = "xnightly" ]
then
   write_nightly_build_results
   exit 0
fi

# new run (takes a long timr)

# remove old build results and start a fresh summary file
#
rm -f "$TOP_BUILD_dir"/apl-*
rm -f "$TOP_BUILD_dir"/*.so
rm -f "$TOP_LOG_dir"/*.log
rm -f "$TOP_LOG_dir"/*.log2
rm -Rf builddir_*

# the user can clean this directory up by calling this script with
# argument 'clean'
#
[ "x$1" = "xclean" ] && exit 0

# check that the ROOT_dir is not ./configure'd. Otherwise ./configure would
# complain.
#
if [ -f "$ROOT_dir/config.status" ]
then
   echo "*** $ROOT_dir is not clean. Please run 'make VPATH_clean' there."
   exit 1
fi

###################################################################
# build one configuration
#
build_one()
{
   # $1: configuration name
   # $2: ./configure options
   # $3: expected result file (apl binary, libapl.so, etc.)
   #

   cd "$TOP_BUILD_dir"
   rm -f "$3"   # remove stale build results (should not exist)
   rm -Rf "builddir_$1"
   mkdir "builddir_$1"
   cd "builddir_$1"   # like  the top-level directory in a non-VPATH build
   VSRC_dir="$TOP_BUILD_dir/builddir_$1/src"

   local time_FROM=$(date +%s)
   local Logfile=$TOP_LOG_dir/configure-$1.log
   echo
   echo "running ../../configure for configuration: \"$1\"..."
   echo "  └─── see result in: $Logfile"
   {
     set -o xtrace
     ../../configure $2
     set +o xtrace
   } > "$Logfile" 2>&1

   Logfile="$TOP_LOG_dir/build-$1.log"
   echo "running make $J8 all for configuration \"$1\"..."
   echo "  └─── see result in: $Logfile"

   {
     echo "building \"$1\"..."

     make $J8 all
   } > "$Logfile" 2>&1

   local make_result=$?
   local time_TO=$(date +%s)
   local secs=$(( $time_TO - $time_FROM ))
   local summ="$TOP_LOG_dir/summary.log"
   local OK_failed="OK"
   if [ $make_result -eq 0 ] ; then   # make succeeded
      echo "    make OK for configuration: $1" >> $summ
      if [ -f "$VSRC_dir/apl" ] ; then
         cp -f "$VSRC_dir/apl" "/var/www/html/apl--$1"
         mv -f "$VSRC_dir/apl" "$TOP_BUILD_dir/apl--$1"
         configs_good=$((configs_good+1))
      elif [ -f "$VSRC_dir/.libs/libapl.so" ] ; then
         cp -f "$VSRC_dir/.libs/libapl.so" "/var/www/html/libapl.so"
         mv -f "$VSRC_dir/.libs/libapl.so" "$TOP_BUILD_dir/"
         configs_good=$((configs_good+1))
      elif [ -f "$VSRC_dir/.libs/lib_gnu_apl.so" ] ; then
         cp -f "$VSRC_dir/.libs/lib_gnu_apl.so" "/var/www/html/lib_gnu_apl.so"
         mv -f "$VSRC_dir/.libs/lib_gnu_apl.so" "$TOP_BUILD_dir/"
         configs_good=$((configs_good+1))
      elif [ ! -f $3 ] ; then
         echo "**** no file $3 after building $1" >> $summ
         missing_files=$((missing_files+1))
         configs_bad=$((configs_bad+1))
         bad_configs="$bad_configs $1"
         OK_failed="FAILED (make target missing)"
      fi
      echo "    build $1: OK ($secs seconds)" >> $summ
   else                        # make failed
       echo "make FAILED with exit code: $make_result" >> $summ
       echo "*** build $1: FAILED ($secs seconds)" >> $summ
       echo "!!! configure options were: $2"
       build_errors=$((build_errors+1))
       configs_bad=$((configs_bad+1))
       bad_configs="$bad_configs $1"
       OK_failed="FAILED (make failed)"
   fi
   echo "    configuration: \"$1\" done." >> $summ
   echo "  configuration: \"$1\" done ($OK_failed)"
}

###################################################################
# test one configuration
#
test_one()
{
   echo "testing configuration \"$1\"..."
   echo "  └─── see test results in: $VSRC_dir/testcases/summary.log"

   local time_FROM=$(date +%s)
   local summ="$TOP_LOG_dir/summary.log"
   cd $VSRC_dir
   cp $ROOT_dir/src/testcases/* testcases/        # copy testcases
   cp $ROOT_dir/src/workspaces/* workspaces/      # needed by testcases
   make test > /dev/null 2>&1
   local make_result=$?
   local time_TO=$(date +%s)
   local secs=$(( $time_TO - $time_FROM ))
   if [ $make_result -eq 0 ] ; then
      echo "    test $1: OK ($secs seconds)"     >> $summ
   else
      echo "*** test $1: FAILED ($secs seconds)" >> $summ
      test_errors=$((1+$test_errors))
   fi
}

cd $ROOT_dir
echo "running autoreconf in $ROOT_dir..."
autoreconf
if [ "$?" != 0 ]; then
    echo "*** cannot build all since autoreconf failed ***"
    exit 42
fi
echo "  ├─── autoreconf done (OK)."

# some shortcuts for various configure options
#
STD="--enable-maintainer-mode"
LIBAPL="--with-libapl"
PYTHON="--with-python"
ERLANG="--with-erlang"
DEV="DEVELOP_WANTED=yes"
RAT="RATIONAL_NUMBERS_WANTED=yes"

PERF="PERFORMANCE_COUNTERS_WANTED=yes"
DYLO="DYNAMIC_LOG_WANTED=yes"
XCB="--without-gtk3"
CC_3="CORE_COUNT_WANTED=-3"		# ⎕SYL controls the number of cores
noVC="VALUE_CHECK_WANTED=no"
noVH="VALUE_HISTORY_WANTED=no"
AL0="ASSERT_LEVEL_WANTED=0"             # no ASSERTions
FAST="$noVC $noVH $AL0"

################################################################################
# build the standard configuration
#
build_standard()
{
  build_one "standard" "$STD" "$VSRC_dir/apl"
  test_one  "standard"
}

# build the standard configuration from the GNU APL tar file
build_tar()
{
  echo "building configuration 'standard' from the GNU APL tar file"
  echo "the APL_VERSION is: $APL_VERSION"
  cd $TOP_BUILD_dir
  tar xf $ROOT_dir/$APL_VERSION.tar.gz 
  mv $APL_VERSION builddir_tar
  cd builddir_tar
  ./configure
  make $J8
  cd $TOP_BUILD_dir
}

# build the standard configuration from a git checkout
build_git()
{
  echo "building configuration 'standard' from the git repository"
  git clone https://git.savannah.gnu.org/git/apl.git builddir_git
  cd builddir_git
  ./configure
  make $J8
}

#
build_libxcb()
{
  build_one "libxcb" "$STD $XCB" "$VSRC_dir/apl"
  test_one  "standard"
}

################################################################################
# build the standard configuration with libxcb (even if GTK3 is present)
#
build_libxcb()
{
  build_one "libxcb" "$STD $XCB" "$VSRC_dir/apl"
  test_one  "standard"
}

# build the developer configuration with rational number support
#
build_rational()
{
  build_one "rational" "$STD $DEV $RAT" "$VSRC_dir/apl"
}
################################################################################
# build apl as a library
#
build_libapl()
{
  build_one "libapl" "$STD $LIBAPL" "$VSRC_dir/.libs/libapl.so"
}
################################################################################
# build the python interface to APL
#
build_python()
{
  build_one "python" "$STD $PYTHON" "$VSRC_dir/.libs/lib_gnu_apl.so"
}
################################################################################
# build apl for parallel benchmarking
#
build_parallel_bench()
{
 build_one "parallel_bench" "$STD $FAST $PERF $DYLO $CC_3" "$VSRC_dir/apl"
}
################################################################################
# build the erlang interface to APL
#
build_erlang()
{
  build_one "erlang" "$STD $ERLANG" "$VSRC_dir/.libs/erlang_APL_nif.so"
}
################################################################################
# build the developer configuration.
#
build_develop()
{
  build_one "develop" "$STD $DEV" "$VSRC_dir/apl"
  test_one "develop"
}

# build all relevant configurations
{
  # clear the summary files
  #
  echo "Build Summary:" > "$TOP_LOG_dir/summary.log"
  echo "Build Summary Errors:" 2> "$TOP_LOG_dir/summary.log2"

  for config in $configs
  do
        echo -n "  ├─── building configuration $config..." > /dev/stderr
        build_$config
        echo " done" > /dev/stderr
  done
  echo "  └─── DONE." > /dev/stderr

  echo
  echo  "$build_errors build errors"
  echo "$missing_files missing files (build results)"
  [ ! -z "$bad_configs" ] && echo "$configs_bad configurations failed: $bad_configs"
  echo  "$configs_good configurations succeeded"
  echo   "$test_errors testcases failed"
} >> "$TOP_LOG_dir/summary.log" 2>> "$TOP_LOG_dir/summary.log2"

cat "$TOP_LOG_dir/summary.log"

write_nightly_build_results

cd $START_dir     # undo all cds in this script
stty sane         # in case some testcase failed badly

