#!/bin/sh
#/****************************************************************************
#**  SCALASCA    http://www.scalasca.org/                                   **
#**  KOJAK       http://www.fz-juelich.de/jsc/kojak/                        **
#*****************************************************************************
#**  Copyright (c) 1998-2010                                                **
#**  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
# -- Variables marked #FE# are relevant on frontend (used in Makefile.cross)
OCC="gcc -g"
OOPTS=""

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

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

DELETE=rm

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

INSTMODE=${SKIN_MODE:-"AUTO"}
ADDLIBS=no
COMPONLY=no
USESOMP=no
USESMPI=no
USESOTF=no
USEAPI=no
USETAU=no
CPPARG=no
EXEARG=no
COMP=""
FLIB=""
ULIB="-lcubew3 -lbfd -lz -liberty"

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 "-mode=${INSTMODE}" "$@"
do
  # Escape special characters in arguments
  #   -e 's/^x//'              Removes the 'x' at the beginning
  #   -e 's/\\\\/\\\\\\\\/g'   Replaces '\' by '\\'
  #   -e 's/"/\\\"/g'          Replaces '"' by '\"'
  #   -e 's/'\''/\\\'\''/g'    Replaces ''' by '\''
  #   -e 's/ /\\\ /g'          Replaces ' ' by '\ '
  #   -e 's/(/\\\(/g'          Replaces '(' by '\('
  #   -e 's/)/\\\)/g'          Replaces ')' by '\)'
  arg=`echo "x$origarg" | sed -e 's/^x//' \
                              -e 's/\\\\/\\\\\\\\/g' \
                              -e 's/"/\\\"/g' \
                              -e 's/'\''/\\\'\''/g' \
                              -e 's/ /\\\ /g' \
                              -e 's/(/\\\(/g' \
                              -e 's/)/\\\)/g'`
  if [ ${CPPARG} = "yes" ]
  then
    CPPARG=no
    PDTPARSEOPTS="${PDTPARSEOPTS}${arg}"
  fi

  if [ ${EXEARG} = "yes" ]
  then
    EXEARG=no
    ARGS="${ARGS} ${arg}"
  else
    case ${arg} in
    *.c|*.C|*.cc|*.CC|*.cpp|*.CPP|*.cxx|*.CXX|*.f|*.F|*.for|*.FOR|*.ftn|*.FTN|*.f90|*.F90|*.f95|*.F95)
           case ${arg} in
           -*) ARGS="${ARGS} ${arg}"
               ;;
           *)  if [ ${INSTMODE} != "NONE" ]
               then
                 BASE=`echo ${arg} | sed -e 's/\.[^\.]*$//'`
                 SUF=`echo ${arg} | sed -e 's/.*\./\./' | ${FTR}`
                 NEWFILE=${BASE}.mod${SUF}
                 TAUFILE=${BASE}.tau${SUF}
                 OFILES="${OFILES} $arg"
                 case ${arg} in
                 *.f|*.F|*.for|*.FOR|*.ftn|*.FTN|*.f90|*.F90|*.f95|*.F95)
                   MFILESsrc="${MFILESsrc} ${NEWFILE} ${TAUFILE}"
                   ;;
                 *)
                   MFILESsrc="${MFILESsrc} ${NEWFILE} ${TAUFILE} ${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
               ;;
           esac
           ;;
    --)    OOPTS="${OOPTS} ${COMP} ${ARGS}"
           ARGS=""
           COMP=""
           ;;
    -comp=*)
           echo "Warning: Ignoring unsupported option: $arg"
           ;;
    -mode=*)
           MODE=`echo $arg | sed -e 's/-mode=//' | tr a-z A-Z`
           case ${MODE} in
           AUTO|AUTOMATIC) # default: determine variant automatically
              ;;
           NONE) # disable all instrumentation and measurement preparation
              INSTMODE="NONE"
              ;;
           MPI)
              INSTMODE="MPI"
              USESMPI="yes"
              USESOMP="no"
              ;;
           OMP|OPENMP)
              INSTMODE="OMP"
              USESMPI="no"
              USESOMP="yes"
              USEOPARI="yes"
              ;;
           OMPI|OMP[+,/]MPI|OPENMP[+,/]MPI|MPI[+,/]OPENMP|MPI[+,/]OMP|HYB|HYBRID)
              INSTMODE="OMP+MPI"
              USESMPI="yes"
              USESOMP="yes"
              USEOPARI="yes"
              ;;
           SER|SERIAL|SEQ|SEQUENTIAL)
              INSTMODE="serial"
              USESMPI="no"
              USESOMP="no"
              ;;
           *)
              echo "ERROR: Unsupported instrumentation measurement mode: $MODE"
              exit 44
              ;;
           esac
           ;;
    -pdt|-tau|-opt[A-Z]*)  if [ -z "${PDTBIN}" ]
           then
             echo "ERROR: PDT/TAU instrumentation not supported by this installation of KOJAK/SCALASCA"
             exit 43
           else
             USETAU=yes
             FTR="cat"
  
             case ${arg} in
             -pdt|-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 PDT/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
           ;;
    -user) USEAPI=yes
           ;;
    -OpenMPnotAvailable)
           echo "INFO: OpenMP not supported by this installation of KOJAK/SCALASCA"
           exit 0
           ;;
    -fopenmp) #USESOMP#
           if [ "${INSTMODE}" = "AUTO" ]; then
             USESOMP=yes
           fi
           ARGS="${ARGS} ${arg}"
           ;;
    -lhpmpi*|-lscmpi*|-lmpi*) #USESMPI#
           if [ "${INSTMODE}" = "AUTO" ]; then
             USESMPI=yes
             ULIB="${ULIB} ${arg}"
             ADDLIBS=yes
           fi
           ;;
    -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
           ;;
    -o)    EXEARG=yes;
           ARGS="${ARGS} ${arg}"
           ;;
    -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}"
           ;;
    -E|-P|-EP) INSTMODE="NONE"
           USETAU=no
           USEOPARI=no
           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
  fi
done

# No instrumentation, execute "original" command line
if [ "${INSTMODE}" = "NONE" ]
then
  [ -n "${V}" ] && echo "+++ ${COMP} ${ARGS}"
  eval ${COMP} ${ARGS} || exit
  exit 0
fi

# Adjust PDT parser options to include '_OPENMP' if OpenMP is enabled
if [ "${USESOMP}" = "yes" ]
then
  PDTCOPTS="${PDTCOPTS} -D_OPENMP"
  PDTCXXOPTS="${PDTCXXOPTS} -D_OPENMP"
  PDTF95OPTS="${PDTF95OPTS} -D_OPENMP"
fi

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
PGIV=`${COMP} -V 2>&1 | grep ^pg | cut -d' ' -f2`
case "${PGIV}" in
  [78].*) OOPTS="-nodecl ${OOPTS}"
      ;;
esac

# 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=`basename ${BASE}.mod.pdb`
    MFILESsrc="${MFILESsrc} ${PDBFILE}"
    case ${ofile} in
    *.f|*.F|*.for|*.FOR|*.ftn|*.FTN|*.f90|*.F90|*.f95|*.F95) 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

# enable manual user instrumentation macros
if [ ${USEAPI} = "yes" ]
then
  DOPT="-D"
  case ${BCOMP} in
  *f*) DOPT="-D" ;;
  esac
  IOPT="${IOPT} ${DOPT}EPIK ${OINC}"
fi

# 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

  if [ "${INSTMODE}" = "AUTO" ]; then
    # - determine whether MPI is used (by checking COMP)
    case ${BCOMP} in
    mp*) USESMPI=yes
       ;;
    esac
    # - determine whether Fortran is used (by checking COMP)
    case ${BCOMP} in
    *f*) FLIB="-lmpi_f77 -lmpi_f90"
       ;;
    esac
    case ${PCOMP} in
      /opt/cray/*) USESMPI=yes # Cray compiler wrapper
      ;;
    esac
  fi

  # - link the right EPILOG library depending on OpenMP/MPI usage
  if [ ${USESOMP} = "yes" ]
  then
    if [ ${USESMPI} = "yes" ]
    then
      VARIANT="OMP+MPI"
      ELIB="${ELGLIB} ${FLIB} ${HYBELGLIB}  ${EXTRALIBS}"
    else
      VARIANT="OMP"
      ELIB="${ELGLIB} ${OMPELGLIB} ${EXTRALIBS}"
    fi
  else
    if [ ${USESMPI} = "yes" ]
    then
      VARIANT="MPI"
      ELIB="${ELGLIB} ${FLIB} ${MPIELGLIB}  ${EXTRALIBS}"
    else
      VARIANT="serial"
      ELIB="${ELGLIB} ${SEQELGLIB} ${EXTRALIBS}"
    fi
  fi

  # Generate and 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

  if [ "${INSTMODE}" = "AUTO" ]; then
    [ -n "${V}" ] && echo "INFO: Linking executable for ${VARIANT} measurement ..."
  fi

  # 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
