#!/bin/sh
#/****************************************************************************
#**  SCALASCA    http://www.scalasca.org/                                   **
#**  KOJAK       http://www.fz-juelich.de/jsc/kojak/                        **
#*****************************************************************************
#**  Copyright (c) 1998-2009                                                **
#**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
#**                                                                         **
#**  See the file COPYRIGHT in the package base directory for details       **
#****************************************************************************/

if [ $# -eq 0 ]
then
  echo "usage: $0 <compile-or-link-command>"
  exit 1
fi

# INITIALIZATION
OCC="gcc"
OOPTS=""

OBIN="/usr/bin/opari"
OINC="-I/usr/include"
OOINC=${OINC}

PDTBIN="/usr/bin"
PDTPARSER=""
PDTF90PARSER="gfparse"
PDTPARSEOPTS=""
PDTCOPTS="-I/usr/lib/openmpi/include"
PDTCXXOPTS="-I/usr/lib/openmpi/include"
PDTF95OPTS=""
TAUINSTOPTS="-spec /usr/doc/scalasca.inst"

DELETE=rm

PREC=${OBJECT_MODE:-32}
PFLAG=""

FPP=" "
FTR="tr f F"

SEQELGLIB=-lelg.ser
OMPELGLIB=-lelg.omp
MPIELGLIB=-lelg.mpi
HYBELGLIB=-lelg.ompi

ADDLIBS=no
COMPONLY=no
USEOPARI=no
USESOMP=no
USESMPI=no
USESOTF=no
USETAU=no
CPPARG=no
COMP=""
FLIB=""
ULIB="-lcubew3 -lbfd -liberty -lz"

MFILESsrc=""
MFILESobj=""

#V=yes

# PROCESS COMMAND LINE
# - collect source files for opari instrumentation (OFILES)
# - collect compiler output files so they can correctly renamed (MFILESobj)
# - collect opari output files so they can deleted after use (MFILESsrc)
# - make compiler to compile instrumented files (NEWFILE)
# - add opari include directory before first file (OINC)
# - collect special arguments to opari (everything up to "--")
for origarg in "$@"
do
  arg=`echo "x$origarg" | sed -e 's/^x//' -e 's/"/\\\"/g' \
                              -e 's/'\''/\\\'\''/g' -e 's/ /\\\ /g'`
  if [ ${CPPARG} = "yes" ]
  then
    CPPARG=no
    PDTPARSEOPTS="${PDTPARSEOPTS}${arg}"
  fi

  case ${arg} in
  *.c|*.C|*.cc|*.CC|*.cpp|*.CPP|*.cxx|*.CXX|*.f|*.F|*.f90|*.F90)
         if [ ${USEOPARI} = "yes" -o ${USETAU} = "yes" ]
         then
           BASE=`echo ${arg} | sed -e 's/\.[^\.]*$//'`
           SUF=`echo ${arg} | sed -e 's/.*\./\./' | ${FTR}`
           NEWFILE=${BASE}.mod${SUF}
           TAUFILE=${BASE}.tau${SUF}
           PDBFILE=${BASE}.mod.pdb
           OFILES="${OFILES} $arg"
           case ${arg} in
           *.f|*.F|*.f90|*.F90)
             MFILESsrc="${MFILESsrc} ${NEWFILE} ${TAUFILE} ${PDBFILE}"
             ;;
           *)
             MFILESsrc="${MFILESsrc} ${NEWFILE} ${TAUFILE} ${PDBFILE} ${arg}.opari.inc"
             ;;
           esac
           if [ ${USETAU} = "yes" ]
           then
             ARGS="${ARGS} ${OINC} ${TAUFILE}"
             MFILESobj="${MFILESobj} `basename ${BASE}.tau.o`"
           else
             ARGS="${ARGS} ${OINC} ${NEWFILE}"
             MFILESobj="${MFILESobj} `basename ${BASE}.mod.o`"
           fi
           OINC=""
         else
           ARGS="${ARGS} ${arg}"
         fi
         ;;
  --)    OOPTS="${OOPTS} ${COMP} ${ARGS}"
         ARGS=""
         COMP=""
         ;;
  -tau|-opt*)  if [ -z "/usr/bin" ]
         then
           echo "ERROR: TAU instrumentation not supported by this installation of KOJAK/SCALASCA"
           exit 43
         else
           USETAU=yes
           FTR="cat"

           case ${arg} in
           -tau)
             ;;
           -optVerbose*)
             V=yes
             ;;
           -optPdtCOpts=*)
             PDTOPTS=`echo $arg | sed -e 's/-optPdtCOpts=//'`
             PDTCOPTS="${PDTCOPTS} ${PDTOPTS}"
             unset PDTOPTS
             ;;
           -optPdtCxxOpts=*)
             PDTOPTS=`echo $arg | sed -e 's/-optPdtCxxOpts=//'`
             PDTCXXOPTS="${PDTCXXOPTS} ${PDTOPTS}"
             unset PDTOPTS
             ;;
           -optPdtF95Opts=*)
             PDTOPTS=`echo $arg | sed -e 's/-optPdtf95Opts=//'`
             PDTF95OPTS="${PDTF95OPTS} ${PDTOPTS}"
             unset PDTOPTS
             ;;
           -optPdtUser=*)
             OPTPDTUSER=`echo $arg | sed -e 's/-optPdtUser=//'`
             PDTPARSEOPTS="${PDTPARSEOPTS} ${OPTPDTUSER}"
             unset OPTPDTUSER
             ;;
           -optPdtF90Parser=*)
             PDTF90PARSER=`echo $arg | sed -e 's/-optPdtF90Parser=//'`
             ;;
           -optPdtGnuFortranParser*)
             PDTF90PARSER="gfparse"
             ;;
           -optPdtCleanscapeParser*)
             PDTF90PARSER="f95parse"
             ;;
           -optTauSelectFile=*)
             TAUSELECTFILE=`echo $arg | sed -e 's/-optTauSelectFile=/-f /'`
             TAUINSTOPTS="${TAUINSTOPTS} ${TAUSELECTFILE}"
             unset TAUSELECTFILE
             ;;
           -optTau=*)
             TAUOPTS=`echo $arg | sed -e 's/-optTau=//'`
             TAUINSTOPTS="${TAUINSTOPTS} ${TAUOPTS}"
             unset TAUOPTS
             ;;
           -optKeepFiles*)
             # -- do not remove file by replacing "rm" with "/bin/true"
             DELETE=true
             ;;
           *) echo "INFO: Ignoring TAU instrumentation option ${arg}."
             ;;
           esac
         fi
         ;;
  -nosrc) OOPTS="${OOPTS} -nosrc"
         FPP=""
         FTR="cat"
         ;;
  -otf)  if [ -z "-L/usr/lib -lotf -lz" ]
         then
           echo "ERROR: OTF generation not supported by this installation of KOJAK/SCALASCA"
           exit 42
         else
           SEQELGLIB=-lelg.otf
           OMPELGLIB=-lelg.otf.omp
           MPIELGLIB=-lelg.otf.mpi
           HYBELGLIB=-lelg.otf.ompi
           USESOTF=yes
         fi
         ;;
  -pomp) USEOPARI=yes
         ;;
  -fopenmp) USESOMP=yes USEOPARI=yes
         ARGS="${ARGS} ${arg}"
         ;;
  -lhpmpi*|-lmpi*) USESMPI=yes
         ULIB="${ULIB} ${arg}"
         ADDLIBS=yes
         ;;
  -l*)   if [ ${ADDLIBS} = "yes" ]
         then
           ULIB="${ULIB} ${arg}"
         else
           ARGS="${ARGS} ${arg}"
         fi
         ;;
  -I|-D|-U)
         CPPARG=yes
         PDTPARSEOPTS="${PDTPARSEOPTS} ${arg}"
         ARGS="${ARGS} ${arg}"
         ;;
  -I*|-D*|-U*)
         PDTPARSEOPTS="${PDTPARSEOPTS} ${arg}"
         ARGS="${ARGS} ${arg}"
         ;;
  -ffixed-line-length-132)
         PDTF95OPTS="${PDTF95OPTS} -ffixed-line-length-132"
         ARGS="${ARGS} ${arg}"
         ;;
  -ffree-form)
         PDTF95OPTS="${PDTF95OPTS} -R free"
         ARGS="${ARGS} ${arg}"
         ;;
  -ffixed-form)
         PDTF95OPTS="${PDTF95OPTS} -R fixed"
         ARGS="${ARGS} ${arg}"
         ;;
  -ffixed-line-length-132)
         PDTF95OPTS="${PDTF95OPTS} -ffixed-line-length-132"
         ARGS="${ARGS} ${arg}"
         ;;
  -ffree-form)
         PDTF95OPTS="${PDTF95OPTS} -R free"
         ARGS="${ARGS} ${arg}"
         ;;
  -ffixed-form)
         PDTF95OPTS="${PDTF95OPTS} -R fixed"
         ARGS="${ARGS} ${arg}"
         ;;
  -@)    V=yes
         ;;
  -c)    COMPONLY=yes
         ARGS="${ARGS} ${arg}"
         ;;
  -cc=*|-cxx=*|-CC=*|-fc=*|-f77=*|-f90=*) XCOMP=${arg}
         ;;
  -m64) PREC=64
         PFLAG=${arg}
         ARGS="${ARGS} ${arg}"
         ;;
  -m32) PREC=32
         PFLAG=${arg}
         ARGS="${ARGS} ${arg}"
         ;;
  *)     if [ -z "${COMP}" ]
         then
           COMP=${arg}
         else
           ARGS="${ARGS} ${arg}"
         fi
         ;;
  esac
done

# Determine plain compiler name and make sure Fortran can handle C preprocessor
if [ -z "${COMP}" ]
then
  echo "ERROR: No compiler or linker specified."
  exit 22
fi

PCOMP=`which ${COMP}`
BCOMP=`basename ${COMP}`
[ -n "${XCOMP}" ] && COMP="${COMP} ${XCOMP}"

# determine whether compiler supports GNU function instrumentation
CRAY=`${COMP} -V 2>&1 | grep -c ^Cray`
INTEL=`${COMP} -V 2>&1 | grep -c ^Intel`
if [ ${CRAY} -gt 0 ]; then
  echo "WARNING: CCE/Cray compiler function instrumentation not supported by this installation of Scalasca"
elif [ ${INTEL} -gt 0 ]; then
  echo "WARNING: Intel compiler function instrumentation not supported by this installation of Scalasca"
else
  PGIV=`${COMP} -V 2>&1 | grep ^pg | cut -d' ' -f2`
  case ${PGIV} in
    8.*)
        OOPTS="-nodecl ${OOPTS}"
        ILIB="-lpgnod_prof_inst"
        IOPT="-Minstrument=functions";;
    7.*)
        echo "ERROR: PGI ${PGIV} compiler function instrumentation not supported by this installation of Scalasca"
        exit;;
    *)
        # default for GCC and Pathscale compilers
        PATHSCALE=`${COMP} -v 2>&1 | grep -c ^PathScale`
        if [ ${PATHSCALE} -gt 0 ]; then
          OOPTS="-nodecl ${OOPTS}"
        fi
        IOPT="-g -finstrument-functions -fno-inline";;
  esac
fi

# Make sure Fortran can handle C preprocessor
if [ ${USEOPARI} = "yes" ]
then
  case ${BCOMP} in
  *f*) COMP="${COMP}${FPP}"
       ;;
  esac
fi

if [ "${PREC}" = "32" ]
then
  # -- note: "then" block needs to be marked with #COPY# for 32/64 combine
  if [ ${USESOTF} = "yes" ]; then ULIB="${ULIB} -L/usr/lib -lotf -lz"; fi  #COPY#
  ELGLIB="-L/usr/lib"                                          #COPY#
  EXTRALIBS="${ULIB} -L/usr/lib -Wl,-rpath /usr/lib -lpapi   -lsz0"            #COPY#
else
  echo "ERROR: ${PREC}bit-mode (${PFLAG}) not supported by this installation of KOJAK/SCALASCA"
  exit 32
fi

if [ ${USESOMP} = "no" ]
then
  # enable only POMP instrumentation for non-OpenMP
  OOPTS="-disable omp ${OOPTS}"
fi

# TAU instrumentor parsers cannot handle #line directives well
if [ ${USETAU} = "yes" ]
then
  OOPTS="${OOPTS} -nosrc"
  # disable automatic function instrumentation in conjunction with TAU instrumenter
  unset IOPT
fi

# INSTRUMENTATION
# - run opari on every collected source file
for ofile in ${OFILES}
do
  if [ ${USEOPARI} = "yes" ]
  then
    [ -n "${V}" ] && echo "+++ ${OBIN} ${OOPTS} $ofile"
    eval ${OBIN} ${OOPTS} $ofile || exit
  fi

  if [ ${USETAU} = "yes" ]
  then
    # Parse source file with PDT
    BASE=`echo ${ofile} | sed -e 's/\.[^\.]*$//'`
    SUF=`echo ${ofile} | sed -e 's/.*\./\./' | ${FTR}`
    if [ ${USEOPARI} = "yes" ]
    then
      NEWFILE=${BASE}.mod${SUF}
      PDBFILE=${BASE}.mod.pdb
    else
      NEWFILE=${ofile}
      PDBFILE=${BASE}.pdb
    fi
    TAUFILE=${BASE}.tau${SUF}
    case ${ofile} in
    *.f|*.F|*.f90|*.F90) PDTPARSER=${PDTF90PARSER}
      PDTPARSEOPTS="${PDTPARSEOPTS} ${PDTF95OPTS}" ;;
    *.c) PDTPARSER=cparse
      PDTPARSEOPTS="${PDTPARSEOPTS} ${PDTCOPTS}" ;;
    *) PDTPARSER=cxxparse
      PDTPARSEOPTS="${PDTPARSEOPTS} ${PDTCXXOPTS}" ;;
    esac
    [ -n "${V}" ] && echo "+++ ${PDTBIN}/${PDTPARSER} ${NEWFILE} ${OOINC} ${PDTPARSEOPTS}"
    eval ${PDTBIN}/${PDTPARSER} ${NEWFILE} ${OOINC} ${PDTPARSEOPTS} || exit

    # Instrument source with TAU
    [ -n "${V}" ] && echo "+++ ${PDTBIN}/tau_instrumentor ${PDBFILE} ${NEWFILE} -o ${TAUFILE} ${TAUINSTOPTS}"
    eval ${PDTBIN}/tau_instrumentor ${PDBFILE} ${NEWFILE} -o ${TAUFILE} ${TAUINSTOPTS} || exit
  fi
done


# EXECUTING MODIFIED COMMAND
if [ ${COMPONLY} = "yes" ]
then
  # COMPILATION ONLY
  [ -n "${V}" ] && echo "+++ ${COMP} ${IOPT} ${ARGS}"
  eval ${COMP} ${IOPT} ${ARGS} || exit
else
  # COMPILATION + LINKING
  # - determine whether MPI is used (by checking COMP)
  # - determine whether Fortran is used (by checking COMP)
  # - include OPARI processing when required or directed
  # - link the right EPILOG library depending on OpenMP/MPI usage
  case ${BCOMP} in
  mp*) USESMPI=yes
       ;;
  esac
  case ${BCOMP} in
  *f*) FLIB="-lmpi_f77 -lmpi_f90"
       ;;
  esac
  case ${PCOMP} in
    /opt/cray/*) USESMPI=yes # Cray compiler wrapper
    ;;
  esac

  if [ ${USESOMP} = "yes" ]
  then
    if [ ${USESMPI} = "yes" ]
    then
      VARIANT="OMP+MPI"
      ELIB="${ELGLIB} ${FLIB} ${HYBELGLIB} -lmpi -Wl,-R/usr/lib/openmpi/lib ${EXTRALIBS}"
    else
      VARIANT="OMP"
      ELIB="${ELGLIB} ${OMPELGLIB} ${EXTRALIBS}"
    fi
  else
    if [ ${USESMPI} = "yes" ]
    then
      VARIANT="MPI"
      ELIB="${ELGLIB} ${FLIB} ${MPIELGLIB} -lmpi -Wl,-R/usr/lib/openmpi/lib ${EXTRALIBS}"
    else
      VARIANT="serial"
      ELIB="${ELGLIB} ${SEQELGLIB} ${EXTRALIBS}"
    fi
  fi

  if [ ${USEOPARI} = "yes" ]
  then
    # Generate and compile opari table file
    OTAB=opari.tab.o
    [ -n "${V}" ] && echo "+++ ${OBIN} ${OOPTS} -table opari.tab.c"
    eval ${OBIN} ${OOPTS} -table opari.tab.c || exit
    [ -n "${V}" ] && echo "+++ ${OCC} ${PFLAG} ${OOINC} -c opari.tab.c"
    eval ${OCC} ${PFLAG} ${OOINC} -c opari.tab.c || exit
  fi

  # MODIFIED COMPILING/LINKING STEP
  [ -n "${V}" ] && echo "+++ ${COMP} ${IOPT} ${OTAB} ${ARGS} ${ILIB} ${ELIB}"
  eval ${COMP} ${IOPT} ${OTAB} ${ARGS} ${ILIB} ${ELIB} || exit

  # Report successful build of instrumented executable
  echo "INFO: Instrumented executable for ${VARIANT} measurement"

  if [ ${USEOPARI} = "yes" ]
  then
    # CLEANUP INTERMEDIATE FILES (in non-verbose mode)
    if [ -z "${V}" ]
    then
      INCFILES=`grep '\.opari\.inc\"$' opari.tab.c | sed -e 's,^#include ,,'`
      [ -n "${INCFILES}" ] &&  eval ${DELETE} ${INCFILES}
      ${DELETE} -f opari.tab.c opari.tab.o
    fi
  fi
fi

# RENAME COMPILER OUTPUT TO ORIGINAL FILE NAME
for mfile in ${MFILESobj}
do
  if [ -f ${mfile} ]
  then
    TARGET=`echo ${mfile} | sed -e 's/\.mod//' -e 's/\.tau//'`
    [ -n "${V}" ] && echo "+++ mv ${mfile} ${TARGET}"
    eval mv ${mfile} ${TARGET}
  fi
done

# DELETE INTERMEDIATE OPARI OUTPUT (in non-verbose mode)
for mfile in ${MFILESsrc}
do
  if [ -f ${mfile} ]
  then
    [ -z "${V}" ] && ${DELETE} ${mfile}
  fi
done
exit 0
