#!/bin/bash
#
# Copyright (C) 2006-2007, Princeton University
# All rights reserved.
# Author: Christian Bienia
#
# bldconfadd - Add a build configuration to the PARSEC benchmark suite
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Princeton University nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY CHRISTIAN BIENIA ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL CHRISTIAN BIENIA BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# CHANGELOG
#
# From PARSEC 2.0 to PARSEC 2.1:
#  - Made PARSECPLAT string detection more robust



#################################################################
#                                                               #
#                         CONFIGURATION                         #
#                                                               #
#################################################################

# Define directory of PARSEC benchmark suite
# If $PARSECDIR is not set, the script tries to autodetect the path
#export PARSECDIR=""

# Define platform name to use
# If $PARSECPLAT is not set, the script will automaticaly determine a name
#export PARSECPLAT=""



##### There should be no need to touch anything below here #####

# Output prefix to use
oprefix="[PARSEC]"



#################################################################
#                                                               #
#                           FUNCTIONS                           #
#                                                               #
#################################################################

# parsec_init
#
# Initialize the script
#
# Arguments: none
function parsec_init {
  # We need to hard-wire a few commands because we need them for the path detection
  PWD="pwd"
  BASENAME="basename"
  DIRNAME="dirname"

  # Try to find path
  uniquefile=".parsec_uniquefile"
  parsecdir=""
  if [ ! -z "${PARSECDIR}" ]; then
    # User defined PARSECDIR, check it
    parsecdir="${PARSECDIR}"
    if [ ! -f "${parsecdir}/${uniquefile}" ]; then
      echo "${oprefix} Error: Variable PARSECDIR points to '${PARSECDIR}', but this does not seem to be the PARSEC directory. Either unset PARSECDIR to make me try to autodetect the path or set it to the correct value."
      exit 1
    fi
  else
    # Try to autodetect path by looking at path used to invoke this script

    # Try to extract absoute or relative path
    if [ "${0:0:1}" == "/" ]; then
      # Absolute path given
      eval parsecdir=$(${DIRNAME} $(${DIRNAME} $0))
      # Check
      if [ -f "${parsecdir}/${uniquefile}" ]; then
        PARSECDIR=${parsecdir}
      fi
    else
      # No absolute path, maybe relative path?
      eval parsecdir=$(${PWD})/$(${DIRNAME} $(${DIRNAME} $0))
      # Check
      if [ -f "${parsecdir}/${uniquefile}" ]; then
        PARSECDIR=${parsecdir}
      fi
    fi

    # If PARSECDIR is still undefined, we try to guess the path
    if [ -z "${PARSECDIR}" ]; then
      # Check current directory
      if [ -f "./${uniquefile}" ]; then
        eval "parsecdir=\$(${PWD})"
        PARSECDIR=${parsecdir}
      fi
    fi
    if [ -z "${PARSECDIR}" ]; then
      # Check next-higher directory
      if [ -f "../${uniquefile}" ]; then
        eval "parsecdir=\$(${PWD})/.."
        PARSECDIR=${parsecdir}
      fi
    fi
  fi

  # Make sure PARSECDIR is defined and exported
  if [ -z "${PARSECDIR}" ]; then
    echo "${oprefix} Error: Unable to autodetect path to the PARSEC benchmark suite. Either define an environment variable PARSECDIR, or edit ${me} and set PARSECDIR to the correct value at the beginning of the file."
    exit 1
  fi
  export PARSECDIR

  # Eliminate trailing `/.' from PARSECDIR
  PARSECDIR=${PARSECDIR/%\/./}

  # Determine OS name to use for automatically determined PARSECPLAT
  case "${OSTYPE}" in
  *linux*)   ostype="linux";;
  *solaris*) ostype="solaris";;
  *bsd*)     ostype="bsd";;
  *aix*)     ostype="aix";;
  *hpux*)    ostype="hpux";;
  *irix*)    ostype="irix";;
  *amigaos*) ostype="amigaos";;
  *beos*)    ostype="beos";;
  *bsdi*)    ostype="bsdi";;
  *cygwin*)  ostype="windows";;
  *darwin*)  ostype="darwin";;
  *interix*) ostype="interix";;
  *os2*)     ostype="os2";;
  *osf*)     ostype="osf";;
  *sunos*)   ostype="sunos";;
  *sysv*)    ostype="sysv";;
  *sco*)     ostype="sco";;
  *)         ostype="${OSTYPE}";;
  esac

  # Determine HOST name to use for automatically determined PARSECPLAT
  case "${HOSTTYPE}" in
  *i386*)    hosttype="i386";;
  *x86_64*)  hosttype="amd64";;
  *amd64*)   hosttype="amd64";;
  *i486*)    hosttype="amd64";;
  *sparc*)   hosttype="sparc";;
  *sun*)     hosttype="sparc";;
  *ia64*)    hosttype="ia64";;
  *itanium*) hosttype="ia64";;
  *powerpc*) hosttype="powerpc";;
  *ppc*)     hosttype="powerpc";;
  *alpha*)   hosttype="alpha";;
  *mips*)    hosttype="mips";;
  *arm*)     hosttype="arm";;
  *)         hosttype="${HOSTTYPE}";;
  esac

  # Determine first part of value to use for PARSECPLAT environment variable if not defined by user
  # Note: We will append the compiler configuration to that to get the final value for PARSECPLAT
  hostostype="${hosttype}-${ostype}"

  # Define some global directories
  benchdir=${parsecdir}/pkgs
  logdir=${parsecdir}/log

  # Source global configuration file with alias definitions, package dependencies etc.
  parsecconfig="${PARSECDIR}/config/parsec.conf"
  if [ -f "${parsecconfig}" ]; then
    source ${parsecconfig}
  else
    echo "${oprefix} Error: Cannot load global configuration file '${parsecconfig}'."
    exit 1
  fi

  # Try to load OS-specific configuration to get binaries and correct arguments
  sysconfig="${PARSECDIR}/config/${ostype}.sysconf"
  if [ -f "${sysconfig}" ]; then
    source ${sysconfig}
  else
    echo "${oprefix} Error: Cannot load system configuration file '${sysconfig}' for OS type '${ostype}'. Please create a new system configuration file."
    exit 1
  fi

  # Setup environment so PARSEC tools are usable by other programs
  if [ -z "${PATH}" ]; then
    export PATH="${PARSECDIR}/bin"
  else
    export PATH="${PARSECDIR}/bin:${PATH}"
  fi
}

# process_args
#
# Process args and setup argument-dependent environment
#
# Arguments: all arguments given to the script (i.e. "$@")
function process_args {
  # Usage
  eval me=$(${BASENAME} $0)
  usage="\
Usage: $me -n NAME [OPTION]...

Add a new configuration to the PARSEC benchmark suite.

Options:
    -n NAME          Name of the configuration to add.
    -c CONFIG        Create a hard copy of configuration CONFIG (The resulting
                     configuration will be a full, independent copy of CONFIG).
    -s CONFIG        Create a soft copy of configuration CONFIG (The resulting
                     configuration will load CONFIG without modifications).
    -f               Overwrite any existing files.
    -h               Displays this help message.

Examples:
    - Create an empty configuration 'my-config' which does nothing:
        $me -n my-config
    - Create a copy 'my-config' of configuration 'gcc', overwrite files:
        $me -n my-config -c gcc -f
    - Create a new configuration 'my-config' which merely points to 'gcc':
        $me -n my-config -s gcc

Notes:
    If you would like to create a completely new configuration, you can use
    $me to create a template. An empty configuration will contain all
    variables which are recognized by PARSEC in a cleared form. A hard copy is a
    full copy of an existing configuration. A soft copy is merely a wrapper for
    an existing configuration. If you would like to modify an existing
    configuration only slightly, you can use a soft copy."

  # Parse arguments
  parsemode="none"
  need_arg_for=""
  config_name=""
  config_soft=""
  config_hard=""
  config_overwrite=""
  while [ ! -z "$1" ]; do
    arg="$1"
    case "${arg}" in
      "-n" )
        if [ ! -z "${need_arg_for}" ]; then
          echo "${oprefix} Error: ${parsemode} expected between '${need_arg_for}' and '-n'"
          echo "${usage}"
          exit 1
        fi
        if [ ! -z "${config_name}" ]; then
          echo "${oprefix} Error: Two configuration names specified"
          exit 1
        fi
        need_arg_for="-n"
        parsemode="NAME";;
      "-c" )
        if [ ! -z "${need_arg_for}" ]; then
          echo "${oprefix} Error: ${parsemode} expected between '${need_arg_for}' and '-c'"
          echo "${usage}"
          exit 1
        fi
        if [ ! -z "${config_hard}" ]; then
          echo "${oprefix} Error: Two hard copy names specified"
          exit 1
        fi
        need_arg_for="-n"
        parsemode="HCONFIG";;
      "-s" )
        if [ ! -z "${need_arg_for}" ]; then
          echo "${oprefix} Error: ${parsemode} expected between '${need_arg_for}' and '-s'"
          echo "${usage}"
          exit 1
        fi
        if [ ! -z "${config_soft}" ]; then
          echo "${oprefix} Error: Two soft copy names specified"
          exit 1
        fi
        need_arg_for="-n"
        parsemode="SCONFIG";;
      "-f" )
        if [ ! -z "${need_arg_for}" ]; then
          echo "${oprefix} Error: ${parsemode} expected between '${need_arg_for}' and '-f'"
          echo "${usage}"
          exit 1
        fi
        config_overwrite="TRUE";;
      "-h" )
        echo "${usage}"
        exit 0;;
      *    )
        if [ ${arg:0:1} == "-" ]; then
          echo "${oprefix} Error: Unknown argument '${arg}'"
          echo "${usage}"
          exit 1
        fi
        need_arg_for=""
        case "${parsemode}" in
          "NAME"   )
            parsemode="none"
            config_name="${arg}";;
          "HCONFIG"   )
            parsemode="none"
            config_hard="${arg}";;
          "SCONFIG"   )
            parsemode="none"
            config_soft="${arg}";;
          *        )
            echo "${oprefix} Error: Unknown argument '${arg}'"
            echo "${usage}"
            exit 1;;
        esac;;
    esac

    shift
  done
  if [ ! -z "${need_arg_for}" ]; then
    echo "${oprefix} Error: ${parsemode} expected after '${need_arg_for}'"
    echo "${usage}"
    exit 1
  fi

  # Make sure a configuration name was given
  if [ -z "${config_name}" ]; then
    echo "${oprefix} Error: A name for the new configuration must be given with option '-n'"
    exit 1
  fi

  # Check that hard and soft copy aren't requested at the same time
  if [ ! -z "${config_hard}" -a ! -z "${config_soft}" ]; then
    echo "${oprefix} Error: Cannot create hard copy ('-c') and soft copy ('-s') at the same time"
    exit 1
  fi
  if [ ! -z "${config_hard}" ]; then
    # Check whether configuration exists
    if [ ! -f "${PARSECDIR}/config/${config_hard}.bldconf" ]; then
      echo "${oprefix} Error: Configuration '${config_hard}' does not exist"
      exit 1
    fi
    # Make sure it's not the same configuration
    if [ "${config_name}" == "${config_hard}" ]; then
      echo "${oprefix} Error: Cannot create copy with same name '${config_name}'"
      exit 1
    fi
  fi
  if [ ! -z "${config_soft}" ]; then
    # Check whether configuration exists
    if [ ! -f "${PARSECDIR}/config/${config_soft}.bldconf" ]; then
      echo "${oprefix} Error: Configuration '${config_soft}' does not exist"
      exit 1
    fi
    # Make sure it's not the same configuration
    if [ "${config_name}" == "${config_soft}" ]; then
      echo "${oprefix} Error: Cannot create copy with same name '${config_soft}'"
      exit 1
    fi
  fi
}

# create_global_config_empty
#
# Create an empty, global configuration file
#
# Arguments: the configuration file to create
function create_global_config_empty {
  if [ -z "$1" ]; then
    echo "${oprefix} Error: create_global_config_empty called with no arguments."
    exit 1
  fi
  local configfile="${PARSECDIR}/$1"

  # Check whether file already exists
  if [ -z "${config_overwrite}" ]; then
    if [ -e "${configfile}" ]; then
      echo "${oprefix} Error: File '${configfile}' already exists, use option '-f' to overwrite."
      exit 1
    fi
  fi

  # Create empty, global configuration
  touch "${configfile}"
  if [ ! "$?" -eq "0" ]; then
    echo "${oprefix} Error: Cannot create file '${configfile}'."
    exit 1
  fi
  echo "#!/bin/bash" > ${configfile}
  echo "" >> ${configfile}
  echo "# $(${BASENAME} ${configfile}) - empty configuration file for PARSEC" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Compilation and linking options" >> ${configfile}
  echo "# These are the global options we'll use to build the benchmark suite." >> ${configfile}
  echo "# Each package also has a local build configuration file defining the" >> ${configfile}
  echo "# exact arguments and environment to use." >> ${configfile}
  echo "" >> ${configfile}
  echo "# Programs to use" >> ${configfile}
  echo "export CC=\"\"" >> ${configfile}
  echo "export CXX=\"\"" >> ${configfile}
  echo "export CPP=\"\"" >> ${configfile}
  echo "export CXXCPP=\"\"" >> ${configfile}
  echo "export LD=\"\"" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Arguments to use" >> ${configfile}
  echo "export CFLAGS=\"\"" >> ${configfile}
  echo "export CXXFLAGS=\"\"" >> ${configfile}
  echo "export CPPFLAGS=\"\"" >> ${configfile}
  echo "export CXXCPPFLAGS=\"\"" >> ${configfile}
  echo "export LDFLAGS=\"\"" >> ${configfile}
  echo "export LIBS=\"\"" >> ${configfile}
  echo "export EXTRA_LIBS=\"\"" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Java" >> ${configfile}
  echo "export JAVAC=\"\"" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Version numbers" >> ${configfile}
  echo "CC_ver=\$(\${CC} --version)" >> ${configfile}
  echo "CXX_ver=\$(\${CXX} --version)" >> ${configfile}
  echo "LD_ver=\$(\${LD} --version)" >> ${configfile}
  echo "JAVAC_ver=\$(\${JAVAC} --version)" >> ${configfile}
}

# create_config_empty
#
# Create an empty, local configuration file
#
# Arguments: name of the configuration file
function create_config_empty {
  if [ -z "$1" ]; then
    echo "${oprefix} Error: create_config_empty called with no arguments."
    exit 1
  fi
  local configfile="${PARSECDIR}/$1"

  # Check whether file already exists
  if [ -z "${config_overwrite}" ]; then
    if [ -e "${configfile}" ]; then
      echo "${oprefix} Error: File '${configfile}' already exists, use option '-f' to overwrite."
      exit 1
    fi
  fi

  # Create empty configuration
  touch "${configfile}"
  if [ ! "$?" -eq "0" ]; then
    echo "${oprefix} Error: Cannot create file '${configfile}'."
    exit 1
  fi
  echo "#!/bin/bash" > ${configfile}
  echo "" >> ${configfile}
  echo "# $(${BASENAME} ${configfile}) - empty configuration file for PARSEC" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Compilation and linking options" >> ${configfile}
  echo "# This is the configuration we'll use to build the program. Each package of the" >> ${configfile}
  echo "# benchmark suite has such a file with information that will be added to the" >> ${configfile}
  echo "# global configuration. All variables set in the global configuration files can" >> ${configfile}
  echo "# be referenced here." >> ${configfile}
  echo "" >> ${configfile}
  echo "# Environment to use for configure script and Makefile" >> ${configfile}
  echo "build_env=\"\"" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Whether the build system supports only in-place compilation" >> ${configfile}
  echo "# If TRUE, then all sources will be copied to the build directory before we" >> ${configfile}
  echo "# start building the package. Required for older build systems which don't" >> ${configfile}
  echo "# support VPATH" >> ${configfile}
  echo "build_inplace=\"TRUE\"" >> ${configfile}
  echo "" >> ${configfile}
  echo "# Arguments to pass to the configure script, if it exists" >> ${configfile}
  echo "build_conf=\"\"" >> ${configfile}
}

# create_config_hard
#
# Create a hard copy of a configuration file
#
# Arguments: name of the new configuration file, name of the existing configuration file
function create_config_hard {
  if [ -z "$1" ]; then
    echo "${oprefix} Error: create_config_hard called with no arguments."
    exit 1
  fi
  local configfile="${PARSECDIR}/$1"
  if [ -z "$2" ]; then
    echo "${oprefix} Error: create_config_hard called with too few arguments."
    exit 1
  fi
  local sourcefile="${PARSECDIR}/$2"

  # Check whether file already exists
  if [ -z "${config_overwrite}" ]; then
    if [ -e "${configfile}" ]; then
      echo "${oprefix} Error: File '${configfile}' already exists, use option '-f' to overwrite."
      exit 1
    fi
  fi
  # Make sure source file exists
  if [ ! -f "${sourcefile}" ]; then
    echo "${oprefix} Configuration file '${sourcefile}' does not exist, skipping."
    return
  fi

  # Create hard copy of configuration
  touch "${configfile}"
  if [ ! "$?" -eq "0" ]; then
    echo "${oprefix} Error: Cannot create file '${configfile}'."
    exit 1
  fi
  cp ${sourcefile} ${configfile}
}

# create_config_soft
#
# Create a soft copy of a configuration file
#
# Arguments: name of the new configuration file, name of the existing configuration file
function create_config_soft {
  if [ -z "$1" ]; then
    echo "${oprefix} Error: create_config_soft called with no arguments."
    exit 1
  fi
  local configfile="${PARSECDIR}/$1"
  if [ -z "$2" ]; then
    echo "${oprefix} Error: create_config_soft called with too few arguments."
    exit 1
  fi
  local sourcefile="${PARSECDIR}/$2"
  local pathsuffix="$2"

  # Check whether file already exists
  if [ -z "${config_overwrite}" ]; then
    if [ -e "${configfile}" ]; then
      echo "${oprefix} Error: File '${configfile}' already exists, use option '-f' to overwrite."
      exit 1
    fi
  fi
  # Make sure source file exists
  if [ ! -f "${sourcefile}" ]; then
    echo "${oprefix} Configuration file '${sourcefile}' does not exist, skipping."
    return
  fi

  # Create soft copy of configuration
  touch "${configfile}"
  if [ ! "$?" -eq "0" ]; then
    echo "${oprefix} Error: Cannot create file '${configfile}'."
    exit 1
  fi
  echo "#!/bin/bash" > ${configfile}
  echo "" >> ${configfile}
  echo "# $(${BASENAME} ${configfile}) - configuration file for PARSEC" >> ${configfile}
  echo "" >> ${configfile}
  echo "source \${PARSECDIR}/${pathsuffix}" >> ${configfile}
}



#################################################################
#                                                               #
#                             MAIN                              #
#                                                               #
#################################################################

# Check version
if [ ${BASH_VERSINFO[0]} -lt 3 ]; then
  # We need certain Bash 3 features. e.g. PIPESTATUS (which was available but broken in earlier versions)
  echo "${oprefix} Warning: At least bash version 3 is recommended. Earlier versions might not function properly. Current version is $BASH_VERSION."
fi

# Execute functions to setup environment
parsec_init
process_args "$@"

# Create empty configuration?
if [ -z "${config_hard}" -a -z "${config_soft}" ]; then
  create_global_config_empty "config/${config_name}.bldconf"
  for pkg in ${alias_all}; do
    eval pkggroup="\${group_${pkg}}"
    create_config_empty "pkgs/${pkggroup}/${pkg}/parsec/${config_name}.bldconf"
  done
fi

# Create hard copy of configuration?
if [ ! -z "${config_hard}" ]; then
  create_config_hard "config/${config_name}.bldconf" "config/${config_hard}.bldconf"
  for pkg in ${alias_all}; do
    eval pkggroup="\${group_${pkg}}"
    create_config_hard "pkgs/${pkggroup}/${pkg}/parsec/${config_name}.bldconf" "pkgs/${pkggroup}/${pkg}/parsec/${config_hard}.bldconf"
  done
fi

# Create soft copy of configuration?
if [ ! -z "${config_soft}" ]; then
  create_config_soft "config/${config_name}.bldconf" "config/${config_soft}.bldconf"
  for pkg in ${alias_all}; do
    eval pkggroup="\${group_${pkg}}"
    create_config_soft "pkgs/${pkggroup}/${pkg}/parsec/${config_name}.bldconf" "pkgs/${pkggroup}/${pkg}/parsec/${config_soft}.bldconf"
  done
fi
