#!/bin/bash -e

#
# Global config
#

set -o pipefail

COMPILER_ROOT="/home/projects/x86-64"

GCC_BUILD_LIST="OpenMP,Pthread,Serial,OpenMP_Serial,Pthread_Serial"
INTEL_BUILD_LIST="OpenMP,Pthread,Serial,OpenMP_Serial,Pthread_Serial"
CLANG_BUILD_LIST="Pthread,Serial,Pthread_Serial"
CUDA_BUILD_LIST="Cuda_OpenMP,Cuda_Pthread,Cuda_Serial"

GCC_WARNING_FLAGS="-Wall,-Wshadow,-pedantic,-Werror,-Wsign-compare,-Wtype-limits,-Wignored-qualifiers,-Wempty-body,-Wclobbered,-Wuninitialized"
CLANG_WARNING_FLAGS="-Wall,-Wshadow,-pedantic,-Werror,-Wsign-compare,-Wtype-limits,-Wuninitialized"
INTEL_WARNING_FLAGS="-Wall,-Wshadow,-pedantic,-Werror,-Wsign-compare,-Wtype-limits,-Wuninitialized"
CUDA_WARNING_FLAGS=""

# Format: (compiler module-list build-list exe-name warning-flag)
COMPILERS=("gcc/4.7.2 gcc/4.7.2/base,hwloc/1.10.0/host/gnu/4.7.2 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
           "gcc/4.8.4 gcc/4.9.2/base,hwloc/1.10.0/host/gnu/4.9.2 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
           "gcc/4.9.2 gcc/4.9.2/base,hwloc/1.10.0/host/gnu/4.9.2 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
           "gcc/5.1.0 gcc/5.1.0/base,hwloc/1.10.0/host/gnu/5.1.0 $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
           "intel/14.0.4 intel/14.0.4/base,hwloc/1.10.0/host/gnu/4.7.2 $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS"
           "intel/15.0.2 intel/15.0.2/base,hwloc/1.10.0/host/gnu/4.7.2 $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS"
           "clang/3.5.2 clang/3.5.2/base $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS"
           "clang/3.6.1 clang/3.6.1/base $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS"
           "cuda/6.5.14 cuda/6.5.14,nvcc-wrapper/gnu,gcc/4.7.2/base $CUDA_BUILD_LIST nvcc_wrapper $CUDA_WARNING_FLAGS"
           "cuda/7.0.28 cuda/7.0.18,nvcc-wrapper/gnu,gcc/4.7.2/base $CUDA_BUILD_LIST nvcc_wrapper $CUDA_WARNING_FLAGS"
           )

export OMP_NUM_THREADS=4

export SEMS_MODULE_ROOT=/projects/modulefiles
module use /home/projects/modulefiles
module use /projects/modulefiles/rhel6-x86_64/sems/compiler

SCRIPT_KOKKOS_ROOT=$( cd "$( dirname "$0" )" && cd .. && pwd )

#
# Handle arguments
#

DEBUG=False
ARGS=""
CUSTOM_BUILD_LIST=""
DRYRUN=False

while [[ $# > 0 ]]
do
key="$1"
case $key in
--kokkos-path*)
KOKKOS_PATH="${key#*=}"
;;
--build-list*)
CUSTOM_BUILD_LIST="${key#*=}"
;;
--debug*)
DEBUG=True
;;
--dry-run*)
DRYRUN=True
;;
--help)
echo "test_all_sandia <ARGS> <OPTIONS>:"
echo "--kokkos-path=/Path/To/Kokkos: Path to the Kokkos root directory"
echo "    Defaults to root repo containing this script"
echo "--debug: Run tests in debug. Defaults to False"
echo "--dry-run: Just print what would be executed"
echo "--build-list=BUILD,BUILD,BUILD..."
echo "    Provide a comma-separated list of builds instead of running all builds"
echo "    Valid items:"
echo "      OpenMP, Pthread, Serial, OpenMP_Serial, Pthread_Serial"
echo "      Cuda_OpenMP, Cuda_Pthread, Cuda_Serial"
echo ""
echo "ARGS: list of expressions matching compilers to test"
echo ""
echo "Examples:"
echo "  Run all tests"
echo "  % test_all_sandia"
echo ""
echo "  Run all gcc tests"
echo "  % test_all_sandia gcc"
echo ""
echo "  Run all gcc/4.7.2 and all intel tests"
echo "  % test_all_sandia gcc/4.7.2 intel"
echo ""
echo "  Run all tests in debug"
echo "  % test_all_sandia --debug"
echo ""
echo "  Run gcc/4.7.2 and only do OpenMP and OpenMP_Serial builds"
echo "  % test_all_sandia gcc/4.7.2 --build-list=OpenMP,OpenMP_Serial"
echo
exit 0
;;
*)
# args, just append
ARGS="$ARGS $1"
;;
esac
shift
done


# set kokkos path
if [ -z "$KOKKOS_PATH" ]; then
    KOKKOS_PATH=$SCRIPT_KOKKOS_ROOT
else
    # Ensure KOKKOS_PATH is abs path
    KOKKOS_PATH=$( cd $KOKKOS_PATH && pwd )
fi

# set build type
if [ "$DEBUG" = "True" ]; then
    BUILD_TYPE=debug
else
    BUILD_TYPE=release
fi

# If no args provided, do all compilers
if [ -z "$ARGS" ]; then
    ARGS='?'
fi

# Process args to figure out which compilers to test
COMPILERS_TO_TEST=""
for ARG in $ARGS; do
    for COMPILER_DATA in "${COMPILERS[@]}"; do
        arr=($COMPILER_DATA)
        COMPILER=${arr[0]}
        if [[ "$COMPILER" = $ARG* ]]; then
            if [[ "$COMPILERS_TO_TEST" != *${COMPILER}* ]]; then
                COMPILERS_TO_TEST="$COMPILERS_TO_TEST $COMPILER"
            else
                echo "Tried to add $COMPILER twice"
            fi
        fi
    done
done

#
# Functions
#

# Do not call directly
get_compiler_data() {
    compiler=$1
    item=$2

    for compiler_data in "${COMPILERS[@]}" ; do
        arr=($compiler_data)
        if [ "$compiler" = "${arr[0]}" ]; then
            echo "${arr[$item]}" | tr , ' '
            return 0
        fi
    done

    # Not found
    echo "Unreconized compiler $compiler" >&2
    exit 1
}

#
# For all getters, usage: <GETTER> <COMPILER>
#

get_compiler_modules() {
    get_compiler_data $1 1
}

get_compiler_build_list() {
    get_compiler_data $1 2
}

get_compiler_exe_name() {
    get_compiler_data $1 3
}

get_compiler_warning_flags() {
    get_compiler_data $1 4
}

run_cmd() {
    echo "RUNNING: $*"
    if [ "$DRYRUN" != "True" ]; then
	eval "$*"
    fi
}

report_and_log_test_result() {
    if [ "$1" = "0" ]; then
	echo "PASSED $2"
	TEST_RESULTS="${TEST_RESULTS}\nPASSED $2"
    else
	echo "FAILED $2" >&2
	TEST_RESULTS="${TEST_RESULTS}\nFAILED $2 ($3)"
	NUM_FAILED+=1
    fi
}

# single_build_and_test <COMPILER> <BUILD> <BUILD_TYPE>
single_build_and_test() {
    # Use sane var names
    local compiler=$1; local build=$2; local build_type=$3;

    cd $ROOT_DIR/$compiler

    local compiler_warning_flags=$(get_compiler_warning_flags $compiler)
    local compiler_exe=$(get_compiler_exe_name $compiler)

    if [[ "$build_type" = hwloc* ]]; then
        local extra_args="--with-hwloc=$HWLOC_ROOT"
    fi

    if [[ "$build_type" = *debug* ]]; then
        local extra_args="$extra_args --debug"
        local cxxflags="-g $compiler_warning_flags"
    else
        local cxxflags="-O3 $compiler_warning_flags"
    fi

    local desc=$(echo "${compiler}-${build}-${build_type}" | sed 's:/:-:g')
    echo "  Doing build: $desc"

    mkdir "${build}-$build_type"
    cd "${build}-$build_type"

    # cxxflags="-DKOKKOS_USING_EXPERIMENTAL_VIEW $cxxflags"

    run_cmd ${KOKKOS_PATH}/generate_makefile.bash --with-devices=$build --compiler=$(which $compiler_exe) --cxxflags=\"$cxxflags\" \"$extra_args\" 2>&1 | tee ${desc}.configure.log || { report_and_log_test_result 1 ${desc} configure && return 0; }
    run_cmd make build-test 2>&1 | tee ${desc}.build.log || { report_and_log_test_result 1 ${desc} build && return 0; }
    run_cmd make test 2>&1 | tee ${desc}.test.log || { report_and_log_test_result 1 ${desc} test && return 0; }
    report_and_log_test_result 0 $desc
    return 0
}

setup_env() {
    local compiler=$1
    local compiler_modules=$(get_compiler_modules $compiler)

    module purge

    for mod in $compiler_modules; do
	module load $mod
        # It is ridiculously hard to check for the success of a loaded
        # module. Module does not return error codes and piping to grep
        # causes module to run in a subshell.
        module list 2>&1 | grep "$mod"
    done
}

# build_and_test_all <COMPILER>
build_and_test_all() {
    # Get compiler data
    local compiler=$1
    if [ -z "$CUSTOM_BUILD_LIST" ]; then
	local compiler_build_list=$(get_compiler_build_list $compiler)
    else
	local compiler_build_list=$(echo "$CUSTOM_BUILD_LIST" | tr , ' ')
    fi

    # set up env
    cd $ROOT_DIR
    mkdir -p $compiler
    setup_env $compiler

    # do builds
    for build in $compiler_build_list
    do
	single_build_and_test $compiler $build $BUILD_TYPE

        # If not cuda, do a hwloc test too
        if [[ "$compiler" != cuda* ]]; then
            single_build_and_test $compiler $build "hwloc-$BUILD_TYPE"
        fi
    done

    return 0
}

#
# Main
#

/bin/rm -rf TestAll
mkdir TestAll
cd TestAll

TEST_RESULTS=""
declare -i NUM_FAILED=0
ROOT_DIR=$(pwd)
for COMPILER in $COMPILERS_TO_TEST; do
    echo "Testing compiler $COMPILER"
    build_and_test_all $COMPILER
done

echo "#######################################################"
echo "RESULT SUMMARY"
echo "#######################################################"
echo -e $TEST_RESULTS

exit $NUM_FAILED
