#! /usr/bin/env bash

########################################################################
#
# File:   gnu-test
# Author: Mark Mitchell
# Date:   2005-04-21
#
# Contents:
#   Script to run DejaGNU tests on CodeSourcery GNU Toolchain releases.
#
########################################################################

########################################################################
# License
########################################################################

# Copyright (C) 2005, CodeSourcery LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
# 02110-1301, USA.

########################################################################
# Initialization
########################################################################

# All errors are fatal.
set -e

# Make sure that any attempts to read from standard input do not go to
# the terminal; otherwise, TeX may fail to terminate when confronted
# with invalid input.
exec < /dev/null

# Check that $CSL_SCRIPTDIR has been initialized.
if [ ! "$CSL_SCRIPTDIR" ]; then
    echo "error: CSL_SCRIPTDIR not set" >& 2
    exit 1
fi

########################################################################
# Subroutines
########################################################################

# Load common subroutines.
. ${CSL_SCRIPTDIR}/gnu-common

# Issue an error message and exit.

usage() {
    echo "gnu-test -h host -t target [-S board] [-o flags]" >& 2
    echo "    [-s srcdir] [-c config]" >& 2
    echo "    [-b binutils_srcdir] [-g gcc_srcdir] [-n newlib_srcdir]" >& 2
    echo "    [-G gdb_srcdir] [-j parallelism] [-m]" >& 2 
    echo "    testsuite ..." >& 2
    exit 1
}

# Transform $test_flags (which is in the format given by gcc
# -print-multi-lib) into the format (including curly braces) used by
# DejaGNU.

multilib_options() {
    local first
    local line
    local ml_switches
    local switches=""

    first=true 
    for line in $test_flags; do
	ml_switches=$(echo "$line" \
	    | sed -e 's|^.*;||g' -e 's|@|-|1' -e 's|@|/-|g')
	if $first; then
	    switches="$ml_switches"
	    first=false
	else
	    switches="${switches},$ml_switches"
	fi
    done
    if [ -n "$switches" ]; then
	switches="{$switches}"
    fi
    echo "$switches"
}

# Add code to test the tool $1 to $makefile.  Update $tools to include
# $1.

run_testsuite() {
    local tool=$1
    local testdir
    local board_spec
    local extracmd=""

    case $tool in
	binutils)
	    testdir='${binutils_srcdir}/binutils/testsuite'
	    ;;
	gas)
	    testdir='${binutils_srcdir}/gas/testsuite'
	    ;;
	ld)
	    testdir='${binutils_srcdir}/ld/testsuite'
	    ;;
	gcc)
	    testdir='${gcc_srcdir}/gcc/testsuite'
	    ;;
	g++)
	    testdir='${gcc_srcdir}/gcc/testsuite'
	    ;;
	libstdc++)
	    testdir='${gcc_srcdir}/libstdc++-v3/testsuite'
	    ;;
	newlib)
	    testdir='${newlib_srcdir}/newlib/testsuite'
	    ;;
	gdb)
	    testdir='${gdb_srcdir}/gdb/testsuite'
	    # Work around lack of support for installed GDB testing.
	    extracmd="$testdir/configure --host=$host_alias --build=$host_alias --target=$target_alias && "
	    ;;
	*)
	    error "Testsuite $tool unknown"
	    ;;
    esac

    if ! eval test -d "$testdir" ; then
	eval error "${testdir} does not exist"
    fi

    board_spec="${target_board}"
    # Tell DejaGNU to use additional compiler flags when running
    # pertinent tests.
    case "$tool" in
	gcc|g++|libstdc++|newlib|gdb)
	    board_spec="${target_board}"$(multilib_options $test_flags)
	    ;;
    esac

    cat >> "$makefile" <<EOF
all::check-$tool
.PHONY: check-$tool 
check-$tool: $tool/site.exp
	-cd $tool && \
	${extracmd}runtest --tool "$tool" --srcdir "$testdir" --target_board="$board_spec"

EOF
}

# Run the Plum-Hall testsuite for language "$1".

run_ph() {
    local language="$1"
    local multilib
    local subdir
    local flags
    
    for multilib in $ph_test_flags; do
	subdir=ph/$(echo "$multilib" | sed -e 's|;.*||g')/$language
	flags=$(echo "$multilib" | sed -e 's|.*;||g' -e 's|@| -|g')

	mkdir -p "$subdir"

	# Create a QMTest database.
	qmtest -D "$subdir" create-tdb \
	    -a srcdir=/home/gcc/dev/PlumHall \
	    -c ph_database.PHDatabase

	# Create a context file.
	cat > "$subdir/context" <<-EOF
	# The test database contains both C and C++ tests.
	CompilerTable.languages=c cplusplus

	# Information about the C compiler.
	CompilerTable.c_path=$target-gcc
	CompilerTable.c_options=-pedantic -std=iso9899:1999 $flags
	CompilerTable.c_ldflags=-lm
	CompilerTable.c_kind=GCC

	# Information about the C++ compiler.
	CompilerTable.cplusplus_path=$target-g++
	CompilerTable.cplusplus_options=-ansi -pedantic -w $flags
	CompilerTable.cplusplus_ldflags=-lm
	CompilerTable.cplusplus_kind=GCC
	EOF

	local ph_target
	ph_target=$(echo "$subdir" | sed -e 's|/|-|g')
    
        # Add to the makefile.
	cat >> "$makefile" <<EOF
all:: $ph_target
.PHONY: $ph_target
$ph_target:
	-cd "$subdir" && qmtest run -fnone --result-stream ph_result_stream.PHResultStream $language | tee ph-$language.sum

EOF

    done
}

# If a log directory was specified, copy the log files there.  The
# naming conventions used here are a historical artifact, but the
# "journeyman" autobuilder depends upon this particular naming scheme.

save_logs() {
    if [ "$logdir" ]; then
	for tool in $tools; do
	    local logsubdir
	    case "$tool" in 
		binutils | gas | ld)
		    logsubdir=test_binutils_dg
		    ;;
		gcc | g++ | libstdc++)
		    logsubdir=test_gcc_dg
		    ;;
		newlib)
		    logsubdir=test_newlib_dg
		    ;;
		gdb)
		    logsubdir=test_gdb_dg
		    ;;
	    esac
	    mkdir -p "$logdir/$logsubdir"
	    cp $tool/*.{sum,log} "$logdir/$logsubdir"
	done
	if $mail_logs && [ -d "$logdir/test_gcc_dg" ]; then
	    $CSL_SCRIPTDIR/gnu-mail-logs -s "$srcdir" \
		-l "$logdir/test_gcc_dg" "$release_config"
	fi
    fi
}

########################################################################
# Main Program
########################################################################

# Any error in this script is fatal.
set -e

# Check that $CSL_SCRIPTDIR has been initialized.
if [ -z "$CSL_SCRIPTDIR" ]; then
    error "CSL_SCRIPTDIR is not set."
fi

# The GNU triplets for the host and target machines.
host_alias=""
target_alias=""
host=""
target=""

# The directory containing the binutils sources.
binutils_srcdir=""
gcc_srcdir=""
newlib_srcdir=""
gdb_srcdir=""

# See gnu-common for documentation for these variables.
test_flags=
ph_test_flags=

# The number of testsuites that should be run simultaneously.
test_parallelism=1

# The DejaGNU target board on which the tests should be run.
target_board="unix"

# The directory to which logs should be copied, or the empty string if
# no copying is required.
logdir=

# The set of tools the user has requested be tested.
tools=

# The configuration file loaded.
release_config=

# Whether to mail GCC logs out.
mail_logs=false

while getopts b:c:g:h:j:l:n:o:s:S:t:m ARG; do
    case $ARG in
	b)
	    binutils_srcdir=$OPTARG
	    ;;
	c)
	    release_config="$OPTARG"
	    read_config "$release_config"
	    ;;
	g)
	    gcc_srcdir=$OPTARG
	    ;;
	G)
	    gdb_srcdir=$OPTARG
	    ;;
	h)
	    host=$OPTARG
	    ;;
	j)
	    test_parallelism=$OPTARG
	    ;;
	l)
	    logdir=$OPTARG
	    ;;
	m)
	    mail_logs=true
	    ;;
	n)
	    newlib_srcdir=$OPTARG
	    ;;
	o)
	    test_flags=$OPTARG
	    ;;
	s)
	    srcdir=$OPTARG
	    ;;
	S)
	    target_board=$OPTARG
	    ;;
	t)
	    target=$OPTARG
	    ;;
	\?)
	    usage
	    ;;
    esac
done

if [ -z "$host" ]; then
    error "Host machine is not set."
elif [ -z "$target" ]; then
    error "Target machine is not set."
fi
# Get the canonical names of the host and target triplets, if possible.
host_alias=$host
target_alias=$target
if [ "$gcc_srcdir" ]; then
    host=$("$gcc_srcdir/config.sub" "$host_alias")
    target=$("$gcc_srcdir/config.sub" "$target_alias")
fi

# Set up the QMTEST_CLASS_PATH to include Plum-Hall support.
if [ "$ph_test_flags" ]; then
    QMTEST_CLASS_PATH=$(python <<-EOF 
	import qmtest_ph
	import os.path
	print os.path.dirname(qmtest_ph.__file__)
	EOF
    ):$QMTEST_CLASS_PATH
    export QMTEST_CLASS_PATH
fi

# The name of the Makefile generated.
makefile="Make.gnu-test"

# Create the makefile.
rm -f $makefile
cat > $makefile <<EOF
####################################################################
#
# Contents: 
#   Makefile for testing $target toolchain
#
# Automatically generated with:
#   gnu-test $@
#
####################################################################

# Make sure that the commands in this Makefile are interpreted
# consistently on all systems.
SHELL = bash

###############################
# Host and target configuration
###############################

host=$host
host_alias=$host_alias
target=$target
target_alias=$target_alias

####################
# Source directories
####################

binutils_srcdir=$binutils_srcdir
gcc_srcdir=$gcc_srcdir
newlib_srcdir=$newlib_srcdir
gdb_srcdir=$gdb_srcdir

##########
# Programs
##########
  
# Save the PATH so that manual use of "make" is more likely to get the 
# same results as are obtained when running with gnu-test.
export PATH=${PATH}
# Similarly for QMTEST_CLASS_PATH.
export QMTEST_CLASS_PATH=${QMTEST_CLASS_PATH}

##########
# Targets
##########

# Run all of the tests.
.PHONY: all
all:: 

# We run each testsuite in its own subdirectory to avoid the various
# DejaGNU instances from overwriting files.  So, we need a copy of
# site.exp in each subdirectory -- but with tmpdir set to that
# subdirectory.

# We set CC and CXX because DejaGNU's logic for finding these
# automatically is not entirely robust.  In particular, DejaGNU's
# find_g++ procedure will look for a file named "../g++" relative to
# the tool_root_dir, which deafults to the current directory.  Since
# the name of the directory in which we run the G++ testsuite is
# "g++", DejaGNU incorrectly intuits that this directory is in fact
# the G++ driver!

# We ensure that CodeSourcery's board definitions are used in
# preference to the ones supplied with DejaGNU itself.  We also define
# HOSTCC and HOSTCFLAGS for gcc.dg/struct-layout-1.exp test makes uses
# of these variables.  For now, just use default values; later we may
# need to add a command-line option to gnu-test.  It should be
# possible to use any C compiler as HOSTCC.  We also define libiconv, 
# to the empty string, as that works on most systems.
%/site.exp: $makefile
	@ test -d \$* || mkdir \$*
	@ rm -f \$@
	@ echo "set CC \$(target)-gcc" >> \$@ 
	@ echo "set CXX \$(target)-g++" >> \$@ 
	@ echo "set host_triplet \$(host)" >> \$@
	@ echo "set host_alias \$(host_alias)" >> \$@
	@ echo "set target_triplet \$(target)" >> \$@ 
	@ echo "set target_alias \$(target_alias)" >> \$@
	@ echo "set boards_dir ${CSL_SCRIPTDIR}/dejagnu/boards" >> \$@
	@ echo "set HOSTCC \"\$(host)-gcc\"" >> \$@
	@ echo "set HOSTCFLAGS \"\"" >> \$@
	@ echo "set libiconv \"\"" >> \$@
	@ echo "set tmpdir \$\$(pwd)/\$*" >> \$@

EOF

# We wait to shift the arguments until we've recorded them in $makefile above.
shift $(expr $OPTIND - 1)

# Figure out what tools the user wishes to test.
for tool; do
    if [ "$tool" = "all" ]; then
	if [ "$binutils_srcdir" ]; then
	    tools="$tools binutils gas ld"
	fi
	if [ "$gcc_srcdir" ]; then
	    tools="$tools gcc g++ libstdc++"
	fi
	if [ "$newlib_srcdir" ]; then
	    tools="$tools newlib"
	fi
	if [ "$gdb_srcdir" ]; then
	    tools="$tools gdb"
	fi
    else
	tools="$tools $tool"
    fi
done

if [ ! "$tools" ] ; then
    error "No testsuites specified."
fi

# Build the makefile.
for tool in $tools; do
    run_testsuite $tool
    if [ "$tool" == "gcc" ]; then
	run_ph "c"
    elif [ "$tool" == "g++" ]; then
	run_ph "c++"
    fi
done

# Run the tests.
make -j $test_parallelism -f "$makefile" all

# Save the logfiles, if so requested.
save_logs
