#!/bin/sh
#
# vim: ts=4 sw=4 et
#
# This script finds miscompiled procedures in a module after the binary script
# has identified the module.
#
# Given
#
# - a stage2 directory that works (stage2.ok), which must have
#   its object files,
# - a stage2 directory that does not work (stage2.bad), which must have
#   its Mmake.params file,
# - a directory name (library, compiler etc),
# - the name of the bad module in that directory (list, hlds_data, etc), and
# - a limit number
#
# this script compiles the bad module with the parameters from
# stage2.bad/Mmake.params plus --experiment=N, varying N from 1 to the limit,
# and finds out which values of N cause a problem.
#
# The test for the cobbled-up stage2 compiler is either bootstrap checking
# (the default), some initial part of bootstrap checking (making dependencies,
# compiling the library), the successful execution of all the test cases in
# one or more subdirectories of the tests directory, or the successful
# execution of a single command.

usage="\
Usage: $0 [options] dirname modulename merfilename limit
Options:
    -b-, --no-bootcheck
        Do not perform a bootcheck; check only the tests directory
        or the single command.
    -c, --compile-only
        Check the successful creation of the stage3 .c files,
        but do not compare stage2.ok and stage3.
    -C <mmc_command>
        Use the given string as the command to invoke the Mercury compiler.
        The string may include options.
    -d, --dependency-only
        Make dependencies for stage3 only. Do not compile stage3.
    -h, --help
        Display this usage message.
    -j <num-jobs>, --jobs <num-jobs>
        Run using <num-jobs> different parallel processes.
    -l, --library-only
        Check the successful creation of the stage3 .c files in the
        library, but do not compile the compiler directory.
    -m <mmake-args>, --mmake-args <mmake-args>
        Pass <mmake-args> as options to \`mmake'.
    -o <filename>, --output-file <filename>
        Output results to <filename>.
    -r, --copy-runtime
        Copy the runtime directory instead of linking it.
        This is necessary if the test uses a compilation model option
        that differs from the one used in the main runtime directory.
    -s <command>, --single-command <command>
        Execute the given command using each constructed compiler.
    -t <testdir>, --test-dir <testdir>
        Execute runtests from the named subdirectory of tests.
"

# If you change this, you will also need to change the files indicated
# in scripts/c2init.in.
STD_LIB_NAME=mer_std

set -x

bootcheck=""
compile_only=""
mmc_command="mmc"
dependency_only=""
library_only=""
jfactor=
mmake_opts=""
outfile=""
copy_runtime=false
single_command=""
testdirs=""
alldirs=""
compare_to_bad=""
basis="ok"

while [ $# -gt 0 ]; do
    case "$1" in

    -b-|--no-bootcheck)
        bootcheck="-b-" ;;

    -c|--compile-only)
        compile_only="-c" ;;

    -C)
        mmc_command="$2"; shift ;;

    -d|--dependency-only)
        dependency_only="-d" ;;

    -h|--help)
        echo "$usage"
        exit 0 ;;

    -j|--jobs)
        jfactor="-j$2"; shift ;;
    -j*)
        jfactor="-j` expr $1 : '-j\(.*\)' `" ;;
    --jobs*)
        jfactor="--jobs` expr $1 : '--jobs\(.*\)' `" ;;

    -l|--library-only)
        library_only="-l" ;;

    -m|--mmake)
        mmake_opts="$mmake_opts $2"; shift ;;

    -o|--output-file)
        outfile="-o $2"; shift ;;
    -o*)
        outfile="-o ` expr $1 : '-o\(.*\)' `"; ;;

    -r|--copy-runtime)
        copy_runtime=true ;;

    -s|--single-command)
        single_command="$2"; shift ;;
    -s*)
        single_command=` expr "$1" : '-s\(.*\)' ` ;;
    --single-command*)
        single_command=` expr "$1" : '--single-command\(.*\)' ` ;;

    -t|--test-dir)
        testdirs="$testdirs -t$2"; shift ;;
    -t*)
        testdirs="$testdirs ` expr $1 : '-t\(.*\)' `" ;;

    -*)
        echo "$0: unknown option \`$1'" 1>&2
        echo "$usage" 1>&2
        exit 1 ;;

    *)
        break
        ;;
    esac
    shift
done

case "$#" in
    4)
        tested_dir_name="$1"
        tested_module_name="$2"
        tested_file_name="$3"
        limit="$4"
        ;;
    *)
        echo "$usage" 1>&2
        exit 1 ;;
esac

base=ok
trial=bad
expected=success

if test -d stage2.ok -a -d stage2.bad
then
    echo "stage2.ok and stage2.bad both present"
else
    echo "at least one of stage2.ok and stage2.bad is missing"
    exit 1
fi

if test ! -d stage2.ok/${tested_dir_name}
then
    echo stage2.ok/${tested_dir_name} does not exist
    exit 1
fi

if test ! -f stage2.ok/${tested_dir_name}/${tested_file_name}.m
then
    echo stage2.ok/${tested_dir_name}/${tested_file_name}.m does not exist
    exit 1
fi

if test ! -f stage2.ok/${tested_dir_name}/${tested_module_name}.c
then
    echo stage2.ok/${tested_dir_name}/${tested_module_name}.c does not exist
    exit 1
fi

root=`/bin/pwd`
trueroot=`echo $root | sed '
s:/mount/munkora/mercury:/home/mercury:
s:/mount/munkora/home/mercury:/home/mercury:
s:/mount/munkora/clp/mercury:/home/mercury:'`
PATH=$root/tools:$PATH
export PATH

if test "$RMSTAGECMD" = ""
then
    RMSTAGECMD="/bin/rm -fr"
fi

echo "starting at `date`"

for dir in $base $trial
do
    for subdir in library mdbcomp compiler
    do
        case $subdir in
            library)    example_o=builtin.o ;;
            mdbcomp)    example_o=mdbcomp.prim_data.o ;;
            compiler)   example_o=check_hlds.cse_detection.o ;;
        esac

        if test -f stage2.$dir/$subdir/$example_o
        then
            echo "stage2.$dir/$subdir seems to have its object files"
        else
            echo "reconstructing object files in stage2.$dir/$subdir"
            ( cd stage2.$dir/$subdir; mmake )
        fi
    done
done

echo "starting unary at `date`"

set +x
[ -d stage2 ] || mkdir stage2
$RMSTAGECMD $trueroot/stage2/compiler < /dev/null &
$RMSTAGECMD $trueroot/stage2/library < /dev/null &
wait
$RMSTAGECMD $trueroot/stage2/* < /dev/null
echo linking stage 2... 1>&2
cd stage2
ln -s $root/main.c .
mkdir compiler
cd compiler
ln -s $root/compiler/[a-h]*.m .
ln -s $root/compiler/[i-o]*.m .
ln -s $root/compiler/[p-s]*.m .
ln -s $root/compiler/[t-z]*.m .
cp $root/compiler/Mmake* .
cp $root/compiler/Mercury* .
cp $root/compiler/*FLAGS* .
cp $root/compiler/.mgnuc* .
cd $root/stage2
mkdir library
cd library
ln -s $root/library/[a-l]*.m .
ln -s $root/library/[m-z]*.m .
ln -s $root/library/*.init .
cp $root/library/print_extra_inits .
cp $root/library/Mmake* .
cp $root/library/Mercury* .
cp $root/library/*FLAGS* .
cp $root/library/mer_std.trans_opt_deps_spec .
cp $root/library/.mgnuc* .
cd $root/stage2
mkdir mdbcomp
cd mdbcomp
ln -s $root/mdbcomp/*.m .
ln -s $root/mdbcomp/*.init .
cp $root/mdbcomp/Mmake* .
cp $root/mdbcomp/Mercury* .
cp $root/mdbcomp/*FLAGS* .
cp $root/mdbcomp/.mgnuc* .
cd $root/stage2
if "$copy_runtime"
then
    mkdir runtime
    cd runtime
    ln -s $root/runtime/*.h .
    ln -s $root/runtime/*.c .
    ln -s $root/runtime/*.in .
    ln -s $root/runtime/machdeps .
    cp $root/runtime/Mmake* .
    cd $root/stage2
else
    # $root/runtime may be in a different grade from the stage2 directories.
    ln -s $root/stage2.ok/runtime .
fi
ln -s $root/boehm_gc .
ln -s $root/browser .
ln -s $root/ssdb .
ln -s $root/trace .
ln -s $root/robdd .
ln -s $root/doc .
ln -s $root/scripts .
ln -s $root/util .
ln -s $root/profiler .
ln -s $root/deep_profiler .
ln -s $root/tools .
ln -s $root/conf* .
ln -s $root/aclocal.m4 .
ln -s $root/VERSION .
ln -s $root/Mercury.options .
ln -s $root/.*.in .
rm -f config*.log
cp $root/stage2.ok/Mmake* .
cd $root

# We don't copy the .d files. This prevents mmake from trying to remake any
# of the .c and .o files, which we provide in the form they should be used.

# cp stage2.ok/library/*.d stage2/library
cp stage2.ok/library/*.dep stage2/library
cp stage2.ok/library/*.dv stage2/library
cp stage2.ok/library/*.int0 stage2/library
cp stage2.ok/library/*.int3 stage2/library
cp stage2.ok/library/*.date3 stage2/library
cp stage2.ok/library/*.int stage2/library
cp stage2.ok/library/*.int2 stage2/library
cp stage2.ok/library/*.date stage2/library
cp stage2.ok/library/*.opt stage2/library
cp stage2.ok/library/*.optdate stage2/library
cp stage2.ok/library/*.trans_opt stage2/library
cp stage2.ok/library/*.trans_opt_date stage2/library
cp stage2.ok/library/*.mh stage2/library
cp stage2.ok/library/*.mih stage2/library
# cp stage2.ok/mdbcomp/*.d stage2/mdbcomp
cp stage2.ok/mdbcomp/*.dep stage2/mdbcomp
cp stage2.ok/mdbcomp/*.dv stage2/mdbcomp
cp stage2.ok/mdbcomp/*.int0 stage2/mdbcomp
cp stage2.ok/mdbcomp/*.int3 stage2/mdbcomp
cp stage2.ok/mdbcomp/*.date3 stage2/mdbcomp
cp stage2.ok/mdbcomp/*.int stage2/mdbcomp
cp stage2.ok/mdbcomp/*.int2 stage2/mdbcomp
cp stage2.ok/mdbcomp/*.date stage2/mdbcomp
cp stage2.ok/mdbcomp/*.mh stage2/mdbcomp
cp stage2.ok/mdbcomp/*.mih stage2/mdbcomp
# cp stage2.ok/compiler/*.d stage2/compiler
cp stage2.ok/compiler/*.dep stage2/compiler
cp stage2.ok/compiler/*.dv stage2/compiler
cp stage2.ok/compiler/*.int0 stage2/compiler
cp stage2.ok/compiler/*.int3 stage2/compiler
cp stage2.ok/compiler/*.date3 stage2/compiler
cp stage2.ok/compiler/*.int stage2/compiler
cp stage2.ok/compiler/*.int2 stage2/compiler
cp stage2.ok/compiler/*.date stage2/compiler
cp stage2.ok/compiler/*.mh stage2/compiler
cp stage2.ok/compiler/*.mih stage2/compiler

if test "$bootcheck" = ""
then
    cd $root
    [ -d stage3 ] || mkdir stage3
    $RMSTAGECMD $trueroot/stage3/compiler < /dev/null &
    $RMSTAGECMD $trueroot/stage3/library < /dev/null &
    wait
    $RMSTAGECMD $trueroot/stage3/* < /dev/null
    echo linking stage 3... 1>&2
    cd stage3
    ln -s $root/main.c
    mkdir compiler
    cd compiler
    # Break up the links into several chunks.
    # This is needed to cope with small limits
    # on the size of argument vectors.
    ln -s $root/compiler/[a-h]*.m .
    ln -s $root/compiler/[i-o]*.m .
    ln -s $root/compiler/[p-s]*.m .
    ln -s $root/compiler/[t-z]*.m .
    cp $root/compiler/Mmake* .
    cp $root/compiler/Mercury* .
    cp $root/compiler/*FLAGS* .
    cp $root/compiler/.mgnuc* .
    cd $root/stage3
    mkdir library
    cd library
    ln -s $root/library/[a-l]*.m .
    ln -s $root/library/[m-z]*.m .
    ln -s $root/library/*.init .
    cp $root/library/Mercury* .
    cp $root/library/*FLAGS* .
    cp $root/library/mer_std.trans_opt_deps_spec .
    cp $root/library/.mgnuc* .
    cp $root/library/Mmake* .
    cd $root/stage3
    mkdir mdbcomp
    cd mdbcomp
    ln -s $root/mdbcomp/*.m .
    ln -s $root/mdbcomp/*.init .
    cp $root/mdbcomp/Mercury* .
    cp $root/mdbcomp/*FLAGS* .
    cp $root/mdbcomp/.mgnuc* .
    cp $root/mdbcomp/Mmake* .
    cd $root/stage3
    ln -s $root/boehm_gc .
    ln -s $root/browser .
    ln -s $root/ssdb .
    ln -s $root/trace .
    ln -s $root/doc .
    ln -s $root/scripts .
    ln -s $root/util .
    ln -s $root/profiler .
    ln -s $root/deep_profiler .
    ln -s $root/runtime .
    ln -s $root/tools .
    ln -s $root/conf* .
    ln -s $root/aclocal.m4 .
    ln -s $root/VERSION .
    ln -s $root/Mercury.options .
    ln -s $root/.*.in .
    rm -f config*.log
    /bin/rm -f Mmake*
    cp $root/stage2.$basis/Mmake* .
    # cp $root/stage2.ok/so_locations .
    cd $root
fi

set -x

if "$copy_runtime"
then
    if (cd stage2 ; mmake $mmake_opts $jfactor runtime)
    then
        echo "building of stage 2 runtime successful"
    else
        echo "building of stage 2 runtime not successful"
        exit 1
    fi
fi

cp stage2.ok/main.o stage2

for subdir in library mdbcomp
do
    echo linking stage2/$subdir from stage2.ok/$subdir 1>&2
    cp stage2.ok/$subdir/*.[co] stage2/$subdir
    cp stage2.ok/$subdir/*.pic_o stage2/$subdir
done

for subdir in compiler
do
    echo linking stage2/$subdir from stage2.ok/$subdir 1>&2
    cp stage2.ok/$subdir/*.[co] stage2/$subdir
done

# start out with all files in stage2 coming from stage2.$base

set +x
echo linking stage2 from stage2.$base 1>&2
cp stage2.$base/library/*.[co] stage2/library
cp stage2.$base/library/*.pic_o stage2/library
cp stage2.$base/compiler/*.[co] stage2/compiler
set -x

# find the set of candidate modules

samen=""
goodn=""
badn=""
/bin/rm -fr $root/SAME_C > /dev/null 2>&1
/bin/rm -fr $root/GOOD_C > /dev/null 2>&1
/bin/rm -fr $root/BAD_C  > /dev/null 2>&1
mkdir $root/SAME_C
mkdir $root/GOOD_C
mkdir $root/BAD_C

/bin/rm $root/.unary.progress

n=1
while test "$n" -le "$limit"
do
    # at this point, all the files in stage2 should be from stage2.$base
    echo "testing $n"
    echo $n > $root/.unary.checkpoint
    echo $n >> $root/.unary.progress
    date >> $root/.unary.progress

    cd $root/stage2/${tested_dir_name}
    cp $root/stage2.ok/${tested_dir_name}/${tested_module_name}.d .
    $mmc_command --experiment=$n -C ${tested_file_name}.m
    /bin/rm ${tested_module_name}.d

    if cmp $root/stage2.ok/${tested_dir_name}/${tested_module_name}.c ${tested_module_name}.c
    then
        echo "test $n generated same .c file"
        samen="$samen $n"
        cp $root/stage2/${tested_dir_name}/${tested_module_name}.c $root/SAME_C/$n.c
    else
        mmake ${tested_module_name}.o
        if test "${tested_dir_name}" = "library"
        then
            mmake ${tested_module_name}.pic_o
        fi

        cd $root

        if binary_step $bootcheck $compile_only $compare_to_bad $dependency_only $library_only $jfactor -m "$mmake_opts" $outfile $testdirs -s "$single_command"
        then
            echo "test $n succeeded"
            goodn="$goodn $n"
            cp $root/stage2/${tested_dir_name}/${tested_module_name}.c $root/GOOD_C/$n.c
        else
            echo "test $n failed"
            badn="$badn $n"
            cp $root/stage2/${tested_dir_name}/${tested_module_name}.c $root/BAD_C/$n.c
        fi
    fi
    n=`expr $n + 1`
done

echo "the parameter values for same .c are: $samen"
echo "the parameter values for success are: $goodn"
echo "the parameter values for failure are: $badn"
echo

echo
echo "finishing at `date`"
exit 0
