#!/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="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
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)
         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=""
         ;;
  --)    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
         ;;
  -OpenMPnotAvailable)
         echo "INFO: OpenMP not supported by this installation of KOJAK/SCALASCA"
         exit 0
         ;;
  -fopenmp) USESOMP=yes
         ARGS="${ARGS} ${arg}"
         ;;
  -lhpmpi*|-lscmpi*|-lmpi*) USESMPI=yes
         ULIB="${ULIB} ${arg}"
         ADDLIBS=yes
         ;;
  -l@FLIBINDICATOR@) FLIB="-lmpi_f77 -lmpi_f90"
         if [ ${ADDLIBS} = "yes" ]
         then
           ULIB="${ULIB} ${arg}"
         else
           ARGS="${ARGS} ${arg}"
         fi
         ;;
  -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}"
         ;;
  -@)    V=yes
         ;;
  -c)    COMPONLY=yes
         ARGS="${ARGS} ${arg}"
         ;;
  -m64|-q64|-xarch=*64|-xarch=amd64a|-xtarget=*64) PREC=64
         PFLAG=${arg}
         ARGS="${ARGS} ${arg}"
         ;;
  -m32|-q32|-xarch=*|-xtarget=*) PREC=32
         PFLAG=${arg}
         ARGS="${ARGS} ${arg}"
         ;;
  *)     if [ -z "${COMP}" ]
         then
           COMP=${arg}
         else
           ARGS="${ARGS} ${arg}"
         fi
         ;;
  esac
done

if [ -z "${COMP}" ]
then
  echo "ERROR: No compiler or linker specified."
  exit 22
fi

PCOMP=`which ${COMP}`
BCOMP=`basename ${COMP}`

# Disable OPARI declarations for broken compilers
PATHSCALE=`${COMP} -v 2>&1 | grep -c ^PathScale`
if [ ${PATHSCALE} -gt 0 ]; then
  OOPTS="-nodecl ${OOPTS}"
fi
PGI=`${COMP} -V 2>&1 | grep -c ^pg`
if [ ${PGI} -gt 0 ]; then
  OOPTS="-nodecl ${OOPTS}"
fi

# Make sure Fortran can handle C preprocessor
case ${BCOMP} in
*f*) COMP="${COMP}${FPP}"
     ;;
esac

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"
fi

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

  if [ ${USETAU} = "yes" ]
  then
    # Parse source file with PDT
    BASE=`echo ${ofile} | sed -e 's/\.[^\.]*$//'`
    SUF=`echo ${ofile} | sed -e 's/.*\./\./' | ${FTR}`
    NEWFILE=${BASE}.mod${SUF}
    TAUFILE=${BASE}.tau${SUF}
    PDBFILE=${BASE}.mod.pdb
    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

# AUTOMATIC FUNCTION INSTRUMENTATION
IOPT=""

# 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)
  # - 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

  # Compile opari table file
  [ -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

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

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

  # 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

# 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
