#!/bin/bash
CHARM_VERSION=6.10.2

function error_syntax {
  echo ''
  echo 'This script will create a build directory for NAMD.'
  echo ''
  echo 'Usage: config [<build_dir>/]<arch>[.comment] [options]'
  echo ''
  echo 'Options (most defaults are set in arch subdirectory):'
  echo '  --charm-base <Charm++ base build directory containing src>'
  echo "      (defaults to ./charm-$CHARM_VERSION or ./charm)"
  echo '  --charm-arch <subdirectory of Charm++ base build directory>'
  echo '      (such as mpi-linux-x86_64-mpicxx or multicore-linux-x86_64)'
  echo '  --charm-opts <options to pass to charmc (quote list)>'
  echo '  --with-tcl (default)  --without-tcl'
  echo '  --tcl-prefix <directory containing Tcl lib and include>'
  echo '      (defaults to ./tcl or ~/tcl)'
  echo '  --without-python (default)  --with-python (requires Tcl)'
  echo '  --python-prefix <directory containing bin/python[23]-config>'
  echo '      (tries python3-config, python-config, and python2-config'
  echo '       in prefix/bin if specified, otherwise in regular path)'
  echo '  --with-fftw (default)  --without-fftw'
  echo '  --with-fftw3 (use fftw3 API, your fftw-prefix should match) '
  echo '  --fftw-prefix <directory containing FFTW lib and include>'
  echo '      (defaults to ./fftw or ~/fftw)'
  echo '  --with-mkl (use Intel Math Kernel Library via fftw3 API) '
  echo '  --mkl-prefix <directory containing Intel MKL lib and include>'
  echo '      (defaults to $MKLROOT)'
  echo '  --cxx <C++ compiler command>'
  echo '  --cxx-opts <C++ compiler options (quote list)>'
  echo '  --cxx-noalias-opts <C++ compiler options (quote list)>'
  echo '  --cxx-thread-opts <C++ compiler options (quote list)>'
  echo '  --cc <C compiler command>'
  echo '  --cc-opts <C compiler options (quote list)>'
  echo '  --with-debug  (sets all opts to -g)'
  echo '  --arch-suffix <suffix for NAMD platform printed at startup>'
  echo '  (the following are experimental features)'
  echo '  --without-memopt (default)  --with-memopt'
  echo '  --without-cuda (default)  --with-cuda'
  echo '      (do NOT use CUDA-enabled Charm++, NAMD does not need it)'
  echo '  --without-mic (default)  --with-mic'
  echo '      (do NOT use MIC-enabled Charm++, NAMD does not need it)'
  echo '  --cuda-prefix <directory containing CUDA bin, lib, and include>'
  echo '  --cuda-gencode arch=<arch>,code=<code> (may be repeated)'
  echo '  --cuda-dlink arch=<arch>,code=<code> (for cuFFT, may be repeated)'
  echo '  --with-cuda-profiling (enables CUDA profiling with NVTX)'
  echo ''
  if [ -n "${PRINT_ARCH_LIST+set}" ]; then
    ARCH_PAT=''
    ARCH_PAT2='XXX'
    case "$(uname -s)" in
      Linux)
        ARCH_PAT=Linux
        case "$(uname -p)" in
          i686)
            ARCH_PAT=Linux-x86
          ;;
          x86_64)
            ARCH_PAT=Linux-x86_64
            ARCH_PAT2=Linux-KNL
            if grep -q 'Xeon Phi' /proc/cpuinfo; then
              ARCH_PAT=Linux-KNL
            fi
            if [ -n "${CRAY_PRE_COMPILE_OPTS+set}" ]; then
              case "$CRAY_PRE_COMPILE_OPTS" in
              *seastar*)
                ARCH_PAT2=CRAY-XT
              ;;
              *gemini*)
                ARCH_PAT2=CRAY-XE
              ;;
              *aries*)
                ARCH_PAT2=CRAY-XC
              ;;
              *)
                ARCH_PAT2=CRAY
              ;;
              esac
            fi
          ;;
          ppc)
            ARCH_PAT=Linux-P
            ARCH_PAT2=BlueGene
          ;;
          ppc64)
            ARCH_PAT=Linux-P
            ARCH_PAT2=BlueGene
          ;;
        esac
      ;;
      AIX)
        ARCH_PAT=AIX
      ;;
      SunOS)
        ARCH_PAT=Solaris
        case "$(uname -p)" in
          sparc)
            ARCH_PAT=Solaris-Sparc
          ;;
        esac
      ;;
      Darwin)
        ARCH_PAT=MacOSX
      ;;
      CYGWIN*)
        ARCH_PAT=Win
        ARCH_PAT2=Cygwin
      ;;
    esac
    if [[ "$ARCH_PAT" == "$ARCH_PAT2" ]]; then ARCH_PAT=XXXXXX; fi
    echo 'Possible options for <arch> on this machine based on uname: ' 
    ( cd arch ; ls -1 $ARCH_PAT*.arch $ARCH_PAT2*.arch |sed -e 's/\.arch//' | egrep -v 'template' | pr -2 -t)
    if [ -n "$ARCH_PAT" ]; then
      echo ''
      echo 'ls arch/*.arch to see all known platforms.  Options such as'
      echo 'MPI are controlled by the choice of Charm++ architecture. '
      echo 'Please see .txt files for Charm++ and NAMD build instructions.'
    fi
  else
    echo 'config with no arguments lists available <arch> options.'
  fi
  echo ''
  exit 1
}

function error_exists {
  echo ''
  echo 'directory already exists'
  echo ''
  exit 1
}

  # defaults
  use_debug=0
  use_tcl=1
  use_python=0
  use_fftw=1
  use_fftw3=0
  use_mkl=0
  use_cuda=0
  use_cuda_prof=0
  use_memopt=0
  use_mic=0
  use_spec=0

  if (( $# < 1 )); then
    PRINT_ARCH_LIST=1
    error_syntax
  fi

  if [ "$1" == debug ]; then
    use_debug=1
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [ "$1" == tcl ]; then
    echo 'The "tcl" flag is not needed; use of tcl is assumed.'
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [ "$1" == fftw ]; then
    echo 'The "fftw" flag is not needed; use of fftw is assumed.'
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [ "$1" == plugins ]; then
    echo 'The "plugins" flag is not needed; plugins are built automatically.'
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [ "$1" == cuda ]; then
    use_cuda=1
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [ "$1" == memopt ]; then
    use_memopt=1
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [ "$1" == mic ]; then
    use_mic=1
    shift
  fi

  if (( $# < 1 )); then error_syntax; fi
  if [[ "$1" == --* ]]; then error_syntax; fi

  BUILD_DIR=$1 ; shift
  if [ -z "$BUILD_DIR" ]; then error_syntax; fi

  if [ -e "$BUILD_DIR" ]; then error_exists; fi
  if [ -h "$BUILD_DIR" ]; then error_exists; fi

  ARCH=${BUILD_DIR##*/}
  if [ ! -f "arch/$ARCH.arch" ]; then ARCH=${ARCH%.*}; fi
  if [ ! -f "arch/$ARCH.arch" ]; then
    echo "ERROR: Platform $ARCH not found in directory arch"
    PRINT_ARCH_LIST=1
    error_syntax
  else
    echo ''
    echo "Selected arch file arch/$ARCH.arch contains:"
    echo ''
    cat arch/$ARCH.arch
  fi

  ARCH_SUFFIX_ARG=""

  if [[ $# -gt 0 && "$1" != --* ]]; then
    ARCH_SUFFIX_ARG=$ARCH_SUFFIX_ARG-$1
    shift
  fi

  PYTHON_PREFIX=""
  CUDA_GENCODE=""
  CUDA_DLINK=""

  while (( $# > 0 )); do
    if [[ "$1" != --* ]]; then
      echo "ERROR: Expected an option beginning with -- but found $1"
      error_syntax
    fi
    case "$1" in

      --arch-suffix)
        shift
        ARCH_SUFFIX_ARG=$ARCH_SUFFIX_ARG-$1
      ;;

      --with-debug)
        use_debug=1
      ;;

      --with-tcl)
        use_tcl=1
      ;;
      --without-tcl)
        use_tcl=0
      ;;
      --tcl-prefix)
        shift
        if [ ! -d "$1" ]; then
          echo "ERROR: No such directory $1"
          error_syntax
        fi
        TCL_PREFIX=$1
      ;;

      --with-python)
        use_python=1
      ;;
      --without-python)
        use_python=0
      ;;
      --python-prefix)
        shift
        if [ ! -d "$1" ]; then
          echo "ERROR: No such directory $1"
          error_syntax
        fi
        PYTHON_PREFIX=$1
      ;;

      --with-fftw)
        use_fftw=1
      ;;
      --with-fftw3)
        use_fftw3=1
      ;;
      --without-fftw)
        use_fftw=0
        use_fftw3=0
      ;;
      --fftw-prefix)
        shift
        if [ ! -d "$1" ]; then
          echo "ERROR: No such directory $1"
          error_syntax
        fi
        FFTW_PREFIX=$1
      ;;
      --with-mkl)
        use_mkl=1
        use_fftw=0
        use_fftw3=0
      ;;
      --mkl-prefix)
        shift
        if [ ! -d "$1" ]; then
          echo "ERROR: No such directory $1"
          error_syntax
        fi
        MKL_PREFIX=$1
      ;;

      --with-cuda)
        use_cuda=1
      ;;
      --without-cuda)
        use_cuda=0
      ;;
      --cuda-prefix)
        if [ -n "${CRAY_CUDATOOLKIT_DIR+set}" ]; then
          echo "ERROR: Do not specify --cuda-prefix when CRAY cudatoolkit module is loaded"
          error_syntax
        fi
        shift
        if [ ! -d "$1" ]; then
          echo "ERROR: No such directory $1"
          error_syntax
        fi
        CUDA_PREFIX=$1
      ;;
      --cuda-gencode)
        shift
        if (( ! $# )); then
          echo "ERROR: --cuda-gencode requires an argument"
          error_syntax
        fi
        CUDA_GENCODE="$CUDA_GENCODE -gencode $1"
      ;;
      --cuda-dlink)
        shift
        if (( ! $# )); then
          echo "ERROR: --cuda-dlink requires an argument"
          error_syntax
        fi
        CUDA_DLINK="$CUDA_DLINK -gencode $1"
      ;;
      --with-cuda-profiling)
        shift
        use_cuda_prof=1
      ;;

      --with-memopt)
         use_memopt=1
      ;;

      --with-mic)
         use_mic=1
      ;;
      --without-mic)
         use_mic=0
      ;;

      --with-spec)
         use_spec=1
      ;;

      --charm-base)
        shift
        if [ ! -d "$1" ]; then
          echo "ERROR: No such directory $1"
          error_syntax
        fi
        CHARM_BASE=$1
      ;;
      --charm-arch)
        shift
        CHARM_ARCH=$1
      ;;
      --charm-opts)
        shift
        CHARM_OPTS=$1
      ;;

      --cxx)
        shift
        CXX_COMMAND=$1
      ;;
      --cxx-opts)
        shift
        CXX_OPTS=$1
      ;;
      --cxx-noalias-opts)
        shift
        CXX_NOALIAS_OPTS=$1
      ;;
      --cxx-thread-opts)
        shift
        CXX_THREAD_OPTS=$1
      ;;
      --cc)
        shift
        CC_COMMAND=$1
      ;;
      --cc-opts)
        shift
        CC_OPTS=$1
      ;;

      *)
        echo "ERROR: unknown option $1"
        error_syntax
      ;;
    esac

    shift
  done

  if [[ -n "$CHARM_BASE" && -n "$CHARM_ARCH" ]]; then
    if [ ! -d "$CHARM_BASE/$CHARM_ARCH" ]; then
      echo "ERROR: No such directory $CHARM_BASE/$CHARM_ARCH"
      error_syntax
    fi
  fi

  if (( $use_fftw && $use_mkl )); then
    echo "ERROR: Do not specify both FFTW and Intel MKL"
    error_syntax
  fi

  if (( $use_cuda || $use_mic )); then

    if [ -n "$CHARM_ARCH" ]; then
       CHARM_ARCH_TEST=$CHARM_ARCH
    else
       CHARM_ARCH_TEST=$(awk '/^CHARMARCH =/ {print $3}' arch/$ARCH.arch)
    fi

    if [[ "$CHARM_ARCH_TEST" == mpi-* ]]; then
      charm_arch_mpi=1
      if [[ "$CHARM_ARCH_TEST" == *-win32* || "$CHARM_ARCH_TEST" == *-win64* ]]; then
        charm_arch_mpi=0
      fi
    else
      charm_arch_mpi=0
    fi

    if [[ "$CHARM_ARCH_TEST" == *-smp* || "$CHARM_ARCH_TEST" == multicore-* ]]; then
      charm_arch_smp=1
    else
      charm_arch_smp=0
    fi

    if (( $use_cuda )); then ERRTYPE="CUDA"; fi
    if (( $use_mic )); then ERRTYPE="MIC"; fi

    if (( $charm_arch_mpi || ! $charm_arch_smp )); then
      echo ''
      if (( $charm_arch_mpi )); then
        echo "ERROR: MPI-based Charm++ arch $CHARM_ARCH is not compatible with $ERRTYPE NAMD."
      fi
      if (( ! $charm_arch_smp )); then
        echo "ERROR: Non-SMP Charm++ arch $CHARM_ARCH is not compatible with $ERRTYPE NAMD."
      fi
      echo "ERROR: $ERRTYPE builds require non-MPI SMP or multicore Charm++ arch for reasonable performance."
      echo ''
      echo "Consider ucx-smp or verbs-smp (InfiniBand), gni-smp (Cray), or multicore (single node)."
      echo ''
      exit 1
    fi

  fi

  if (( $use_python )); then
    if (( ! $use_tcl )); then
      echo ''
      echo "ERROR: Python interface requires Tcl."
      echo ''
      exit 1
    fi


    PYTHON_LDFLAGS=""
    PYTHON_INCFLAGS=""

    PYTHON_CONFIG=""

    if [ -n "$PYTHON_PREFIX" ]; then
      echo "Trying to detect Python installation in $PYTHON_PREFIX:"
      if [ -n "$(which $PYTHON_PREFIX/bin/python3-config)" ]; then
        PYTHON_CONFIG="$PYTHON_PREFIX/bin/python3-config"
      elif [ -n "$(which $PYTHON_PREFIX/bin/python-config)" ]; then
        PYTHON_CONFIG="$PYTHON_PREFIX/bin/python-config"
      elif [ -n "$(which $PYTHON_PREFIX/bin/python2-config)" ]; then
        PYTHON_CONFIG="$PYTHON_PREFIX/bin/python2-config"
      fi
    else
      echo "Trying to detect Python installation:"
      if [ -n "$(which python3-config)" ]; then
        PYTHON_CONFIG="python3-config"
      elif [ -n "$(which python-config)" ]; then
        PYTHON_CONFIG="python-config"
      elif [ -n "$(which python2-config)" ]; then
        PYTHON_CONFIG="python2-config"
      fi
    fi

    if [ -z "$PYTHON_CONFIG" ]; then
      echo "Unable to detect Python installation; please edit Make.config file."
    else
      echo "Using $PYTHON_CONFIG to configure Python."
      PYTHON_PREFIX=$($PYTHON_CONFIG --prefix)
      PYTHON_INCFLAGS=$($PYTHON_CONFIG --includes)
      PYTHON_LDFLAGS=$($PYTHON_CONFIG --ldflags)
      if ! echo "$PYTHON_LDFLAGS" | grep -q -- '-L'; then
        echo "$PYTHON_CONFIG did not return a -L flag: using Anaconda?"
        if [ -d "$PYTHON_PREFIX/lib" ]; then
          PYTHON_LDFLAGS="-L$PYTHON_PREFIX/lib $PYTHON_LDFLAGS"
        elif [ -d "$PYTHON_PREFIX/lib64" ]; then
          PYTHON_LDFLAGS="-L$PYTHON_PREFIX/lib64 $PYTHON_LDFLAGS"
        else
          echo "Unable to detect Python installation; please edit Make.config file."
        fi
      fi
    fi

  fi # end Python

  if [ -n "$CHARM_BASE" ]; then
    [[ "$CHARM_BASE" != /* ]] && CHARM_BASE=.rootdir/$CHARM_BASE
  fi

  DIR=$(pwd)

  BUILD_LINK=$BUILD_DIR
  # Environment variable NAMD_BUILD_BASE may point to scratch directory.
  if [[ "${BUILD_DIR##*/}" == "$BUILD_DIR" && -n "$NAMD_BUILD_BASE" ]]; then
    if [ -e "$BUILD_DIR" ]; then error_exists; fi
    UNIQ=$(date '+%Y-%b-%d')-$$
    BUILD_DIR="$NAMD_BUILD_BASE/${UNIQ}_$BUILD_DIR"
    echo "Creating link: $BUILD_DIR to $BUILD_LINK"
    ln -s "$BUILD_DIR" "$BUILD_LINK"
  fi

  if [ -e "$BUILD_DIR" ]; then error_exists; fi
  if [ -h "$BUILD_DIR" ]; then error_exists; fi
  echo "Creating directory: $BUILD_DIR"
  mkdir "$BUILD_DIR"
  cd "$BUILD_DIR"

  ROOTDIR=$DIR
  if [[ "${BUILD_DIR##*/}" == "$BUILD_DIR" ]]; then ROOTDIR='..'; fi
  if [[ "./${BUILD_DIR##*/}" == "$BUILD_DIR" ]]; then ROOTDIR='..'; fi

  echo "Creating link: $ROOTDIR to .rootdir"
  ln -s "$ROOTDIR" .rootdir

  if (( $use_fftw )); then
    if [ -z "$FFTW_PREFIX" ]; then
      if [[ -d .rootdir/fftw/lib && -d .rootdir/fftw/include ]]; then
        echo "Using FFTW build found in main build directory"
        FFTW_PREFIX=.rootdir/fftw
      elif [[ -d "$HOME/fftw/lib" && -d "$HOME/fftw/include" ]]; then
        echo "Using FFTW build found in $HOME/fftw"
        FFTW_PREFIX=$HOME/fftw
      fi
    fi
    if [[ -n "$FFTW_PREFIX" && $use_fftw3 -eq 0 ]]; then
      if [ -e "$FFTW_PREFIX/include/fftw3.h" ]; then
        echo "Using FFTW3 build found in $FFTW_PREFIX"
        use_fftw3=1
      fi
    fi
  fi

  echo "Writing build options to $BUILD_LINK/Make.config"

  set -o noclobber

  if [ -n "$CHARM_BASE" ]; then
    echo "CHARMBASE = $CHARM_BASE" > Make.config
  elif [ -d ".rootdir/charm-$CHARM_VERSION" ]; then
    CHARM_BASE=.rootdir/charm-$CHARM_VERSION
    echo "Using Charm++ $CHARM_VERSION build found in main build directory"
    echo "CHARMBASE = .rootdir/charm-$CHARM_VERSION" > Make.config
  elif [ -d ".rootdir/charm-$CHARM_VERSION-pre" ]; then
    CHARM_BASE=.rootdir/charm-$CHARM_VERSION-pre
    echo "Using Charm++ $CHARM_VERSION-pre build found in main build directory"
    echo "CHARMBASE = .rootdir/charm-$CHARM_VERSION-pre" > Make.config
  elif [ -d .rootdir/charm ]; then
    CHARM_BASE=.rootdir/charm
    echo "Using Charm++ build found in main build directory"
    echo 'CHARMBASE = .rootdir/charm' > Make.config
  else
    CHARM_BASE=$(awk '/^CHARMBASE =/ {print $3}' .rootdir/Make.charm)
    echo "Using Charm++ build found in Make.charm: $CHARM_BASE"
    echo include .rootdir/Make.charm > Make.config
  fi

  echo include .rootdir/arch/$ARCH.arch >> Make.config

  if [ -n "$CHARM_ARCH" ]; then
    echo "CHARMARCH = $CHARM_ARCH" >> Make.config
  else
     CHARM_ARCH=$(awk '/^CHARMARCH =/ {print $3}' .rootdir/arch/$ARCH.arch)
  fi
  if [ ! -d "$CHARM_BASE/$CHARM_ARCH" ]; then
    echo "Warning: No such directory $CHARM_BASE/$CHARM_ARCH"
  fi
  if [ -n "$CHARM_OPTS" ]; then
    echo "CHARMOPTS = $CHARM_OPTS" >> Make.config
  fi
  echo 'CHARM = $(CHARMBASE)/$(CHARMARCH)' >> Make.config
  ARCH_SUFFIX=""
  [[ "$CHARM_ARCH" == *-scyld* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-Scyld
  if [[ "$CHARM_ARCH" == *-clustermatic* ]]; then
    SUF="-Clustermatic"
    [[ "$ARCH_SUFFIX_ARG" == *-Clustermatic* ]] && SUF=""
    ARCH_SUFFIX=${ARCH_SUFFIX}${SUF}
  fi
  if [[ "$CHARM_ARCH" == mpi-* ]]; then
    SUF="-MPI"
    [[ "$ARCH_SUFFIX_ARG" == *-MVAPICH* ]] && SUF=""
    [[ "$ARCH_SUFFIX_ARG" == *-OpenMPI* ]] && SUF=""
    ARCH_SUFFIX=${ARCH_SUFFIX}${SUF}
  fi
  [[ "$CHARM_ARCH" == gemini_gni-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-ugni
  [[ "$CHARM_ARCH" == gni-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-ugni
  [[ "$CHARM_ARCH" == lapi-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-lapi
  [[ "$CHARM_ARCH" == pami-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-pami
  [[ "$CHARM_ARCH" == pamilrts-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-pamilrts
  [[ "$CHARM_ARCH" == netlrts-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-netlrts
  [[ "$CHARM_ARCH" == verbs-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-verbs
  [[ "$CHARM_ARCH" == ofi-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-ofi
  [[ "$CHARM_ARCH" == ucx-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-ucx
  [[ "$CHARM_ARCH" == *-vmi* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-vmi
  [[ "$CHARM_ARCH" == *-tcp* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-TCP
  [[ "$CHARM_ARCH" == *-gm* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-gm
  [[ "$CHARM_ARCH" == *-mx* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-mx
  [[ "$CHARM_ARCH" == *-elan* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-Elan
  [[ "$CHARM_ARCH" == *-ibverbs* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-ibverbs
  [[ "$CHARM_ARCH" == *-ofi* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-ofi
  [[ "$CHARM_ARCH" == *-smp* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-smp
  [[ "$CHARM_ARCH" == *multicore-* ]] && ARCH_SUFFIX=${ARCH_SUFFIX}-multicore
  ARCH_SUFFIX=${ARCH_SUFFIX}${ARCH_SUFFIX_ARG}
  if (( $use_cuda )); then
    SUF="-CUDA"
    [[ "$ARCH_SUFFIX_ARG" == *CUDA* ]] && SUF=""
    ARCH_SUFFIX=${ARCH_SUFFIX}${SUF}
  fi
  if (( $use_mic )); then
    SUF="-MIC"
    [[ "$ARCH_SUFFIX_ARG" == *MIC* ]] && SUF=""
    ARCH_SUFFIX=${ARCH_SUFFIX}${SUF}
  fi
  (( $use_memopt )) && ARCH_SUFFIX=${ARCH_SUFFIX}-memopt
  (( $use_spec )) && ARCH_SUFFIX=${ARCH_SUFFIX}-SPEC
  echo NAMD_PLATFORM = \$\(NAMD_ARCH\)${ARCH_SUFFIX} >> Make.config

  echo 'include .rootdir/arch/$(NAMD_ARCH).base' >> Make.config
  (( $use_tcl )) && echo 'include .rootdir/arch/$(NAMD_ARCH).tcl' >> Make.config
  if (( $use_mkl )); then
    echo 'include .rootdir/arch/$(NAMD_ARCH).mkl' >> Make.config
  elif (( $use_fftw3 )); then
    echo 'include .rootdir/arch/$(NAMD_ARCH).fftw3' >> Make.config
  elif (( $use_fftw )); then
    echo 'include .rootdir/arch/$(NAMD_ARCH).fftw' >> Make.config
  fi

  (( $use_memopt )) && echo 'MEMOPT=-DMEM_OPT_VERSION' >> Make.config

  (( $use_mic )) && echo 'include .rootdir/arch/$(NAMD_ARCH).mic' >> Make.config

  (( $use_spec )) && echo 'RELEASEFLAGS=-DSPEC_DISABLED_VERSION' >> Make.config

  threaded=0
  [[ "$CHARM_ARCH" == *-smp* ]] && threaded=1
  [[ "$CHARM_ARCH" == *multicore-* ]] && threaded=1

  if (( $use_tcl )); then
    if [ -n "$TCL_PREFIX" ]; then
      echo "TCLDIR = $TCL_PREFIX" >> Make.config
    elif [[ $threaded -ne 0 && -d .rootdir/tcl-threaded/lib && -d .rootdir/tcl-threaded/include ]]; then
      echo "Using Tcl build found in main build directory"
      echo 'TCLDIR = .rootdir/tcl-threaded' >> Make.config
    elif [[ $threaded -ne 0 && -d "$HOME/tcl-threaded/lib" && -d "$HOME/tcl-threaded/include" ]]; then
      echo "Using Tcl build found in $HOME/tcl-threaded"
      echo 'TCLDIR = $(HOME)/tcl-threaded' >> Make.config
    elif [[ -d .rootdir/tcl/lib && -d .rootdir/tcl/include ]]; then
      echo "Using Tcl build found in main build directory"
      echo 'TCLDIR = .rootdir/tcl' >> Make.config
    elif [[ -d "$HOME/tcl/lib" && -d "$HOME/tcl/include" ]]; then
      echo "Using Tcl build found in $HOME/tcl"
      echo 'TCLDIR = $(HOME)/tcl' >> Make.config
    fi
  fi

  if (( $use_python )); then
    echo "PYTHON = -DNAMD_PYTHON $PYTHON_INCFLAGS" >> Make.config
    echo "PYTHONLIB = $PYTHON_LDFLAGS" >> Make.config
  fi

  if (( $use_mkl )); then
    if [ -n "$MKL_PREFIX" ]; then
      echo "FFTDIR = $MKL_PREFIX" >> Make.config
    fi
  elif (( $use_fftw )); then
    if [ -n "$FFTW_PREFIX" ]; then
      echo "FFTDIR = $FFTW_PREFIX" >> Make.config
    elif [[ -d .rootdir/fftw/lib && -d .rootdir/fftw/include ]]; then
      echo "Using FFTW build found in main build directory"
      echo 'FFTDIR = .rootdir/fftw' >> Make.config
    elif [[ -d "$HOME/fftw/lib" && -d "$HOME/fftw/include" ]]; then
      echo "Using FFTW build found in $HOME/fftw"
      echo 'FFTDIR = $(HOME)/fftw' >> Make.config
    fi
  fi

  if (( $use_cuda )); then
    echo 'include .rootdir/arch/$(NAMD_ARCH).cuda' >> Make.config
    CUDART_SO_PAT='lib64/libcudart.so.[1-9]*'
    CUFFT_SO_PAT='lib64/libcufft.so.[1-9]*'
    case "$(uname -s)" in
      Darwin)
        CUDART_SO_PAT='lib/libcudart.dylib'
        CUFFT_SO_PAT='lib/libcufft.dylib'
      ;;
      CYGWIN*)
        CUDART_SO_PAT='bin/cudart64_*.dll'
        CUFFT_SO_PAT='bin/cufft64_*.dll'
      ;;
    esac
    if [ -n "$CUDA_PREFIX" ]; then
      if [[ "$(uname -s)" == CYGWIN* ]]; then
        CUDA_PREFIX=$(cygpath -d "$CUDA_PREFIX")
        CUDA_PREFIX=$(cygpath "$CUDA_PREFIX")
      fi
    elif [[ -d .rootdir/cuda/lib && -d .rootdir/cuda/include ]]; then
      echo "Using CUDA build found in main build directory"
      CUDA_PREFIX=.rootdir/cuda
    elif [[ -d "$HOME/cuda/lib" && -d "$HOME/cuda/include" ]]; then
      echo "Using CUDA build found in $HOME/cuda"
      CUDA_PREFIX=$HOME/cuda
    else
      # try to set CUDA_PREFIX based on nvcc in user's path
      path_to_nvcc=$(which nvcc)
      if [ -x "${path_to_nvcc}" ]; then
        cuda_bindir=${path_to_nvcc%/*}
        cuda_dir=${cuda_bindir%/*} 
        if [ -x "${cuda_dir}/bin/nvcc" ]; then
          echo "Using CUDA based on path to nvcc"
          echo "Found enclosing directory ${cuda_dir}"
          CUDA_PREFIX=${cuda_dir}
        fi
      else
        echo "Warning: CUDA installation not found"
      fi
    fi
    if [ -n "$CUDA_PREFIX" ]; then
      cuda_ver=$(${CUDA_PREFIX}/bin/nvcc --version | grep release | cut -d " " -f5 | cut -d "," -f1)
      NAMD_ARCH=$(awk '/^NAMD_ARCH =/ {print $3}' .rootdir/arch/$ARCH.arch)
      if [ -z "$cuda_ver" ]; then
        echo "Warning: Did not find CUDA installation in $CUDA_PREFIX"
      else 
        echo "CUDA detected: version $cuda_ver"
        cuda_major_ver_found=$(echo ${cuda_ver} | cut -d "." -f1)
        cuda_major_ver=${cuda_major_ver_found}
        cuda_major_ver_min=8
        while [ ${cuda_major_ver} -ge ${cuda_major_ver_min} ]; do
          if [ -f ".rootdir/arch/${NAMD_ARCH}.cuda${cuda_major_ver}" ]; then
            echo "using ${NAMD_ARCH}.cuda${cuda_major_ver}"
            # work around CUDA 9.0 code generation for MacOS
            if [[ "${NAMD_ARCH}" == MacOS* && "${cuda_ver}" == "9.0" ]]; then
              cuda_major_ver_using=$((cuda_major_ver-1))
              echo "CUDA ${cuda_ver} does not fully support ${NAMD_ARCH}"
              echo "Using code generation flags from previous major version ${cuda_major_ver_using} of CUDA"
            else
              cuda_major_ver_using=${cuda_major_ver}
            fi
            echo 'include .rootdir/arch/$(NAMD_ARCH).cuda'${cuda_major_ver_using} >> Make.config
            break
          fi
          cuda_major_ver=$((cuda_major_ver-1))
        done
        if [ ${cuda_major_ver} -lt ${cuda_major_ver_min} ]; then
          echo "Warning: No CUDA build found for version ${cuda_ver}"
        elif [ ${cuda_major_ver} -lt ${cuda_major_ver_found} ]; then
          echo "Warning: CUDA build found is less than version ${cuda_ver}"
        fi
      fi
      echo "CUDADIR = $CUDA_PREFIX" >> Make.config
    fi
    if [ -n "$CUDA_PREFIX" ]; then
      CUDART_SO_FULL=( "$CUDA_PREFIX"/$CUDART_SO_PAT )
      [[ -z "$CUDART_SO_FULL" || "$CUDART_SO_FULL" != */* ]] && unset CUDART_SO_FULL
      CUFFT_SO_FULL=( "$CUDA_PREFIX"/$CUFFT_SO_PAT )
      [[ -z "$CUFFT_SO_FULL" || "$CUFFT_SO_FULL" != */* ]] && unset CUFFT_SO_FULL
    fi
    if [ -n "$CUDART_SO_FULL" ]; then
      CUDART_SO_FULL="$CUDART_SO_FULL"
      cudasodir=${CUDART_SO_FULL%/*}
      if [[ ! -d ${cudasodir} ]]; then
        echo "Warning: Invalid directory for CUDA library ${cudasodir}"
      fi
      libcudartso=${CUDART_SO_FULL##*/}
      if [[ ! -f ${cudasodir}/${libcudartso} ]]; then
        echo "Warning: Did not find the cudart library"
      fi
      echo "CUDASODIR = ${cudasodir}" >> Make.config
      if [[ "${NAMD_ARCH}" == Linux* || "${NAMD_ARCH}" == MacOS* ]]; then
        # don't copy .so for platforms where we statically link cudart and cufft
        echo "LIBCUDARTSO =" >> Make.config
        echo "LIBCUFFTSO =" >> Make.config
      else
        echo "LIBCUDARTSO = ${libcudartso}" >> Make.config
        if [ -n "$CUFFT_SO_FULL" ]; then
          CUFFT_SO_FULL="$CUFFT_SO_FULL"
          cufftsodir=${CUFFT_SO_FULL%/*}
          if [[ "${cudasodir}" != "${cufftsodir}" ]]; then
            echo "Warning: Found $CUDART_SO_FULL and $CUFFT_SO_FULL in different directories"
          fi
          libcufftso=${CUFFT_SO_FULL##*/}
          if [[ ! -f ${cufftsodir}/${libcufftso} ]]; then
            echo "Warning: Did not find the cufft library"
          fi
          echo "LIBCUFFTSO = ${libcufftso}" >> Make.config
        else
          echo "Warning: Found $CUDART_SO_FULL but no cufft"
        fi
      fi
    fi
  fi
  if [[ $use_cuda -ne 0 && -n "$CUDA_GENCODE" ]]; then
    echo "CUDAGENCODE = $CUDA_GENCODE" >> Make.config
    if [[ -n "$CUDA_DLINK" ]]; then
      echo "CUDADLINKOPTS = $CUDA_DLINK" >> Make.config
    else
      echo "# runtime error if dlink architectures not available in libcufft_static" >> Make.config
      echo "CUDADLINKOPTS = $CUDA_GENCODE" >> Make.config
    fi
  fi

  if (( $use_cuda && $use_cuda_prof )); then
    if [ -f ".rootdir/arch/${NAMD_ARCH}.cudaprof" ]; then
      echo "Enabling NVTX profiling for CUDA ${NAMD_ARCH}"
      echo 'include .rootdir/arch/$(NAMD_ARCH).cudaprof' >> Make.config
    else
      echo "Warning: Cannot enable NVTX profiling for CUDA ${NAMD_ARCH}"
    fi
  fi

  if [ -n "$CXX_COMMAND" ]; then
    echo "CXX = $CXX_COMMAND" >> Make.config
  fi
  if [ -n "$CXX_OPTS" ]; then
    echo "CXXOPTS = $CXX_OPTS" >> Make.config
  fi
  if [ -n "$CXX_NOALIAS_OPTS" ]; then
    echo "CXXNOALIASOPTS = $CXX_NOALIAS_OPTS" >> Make.config
  fi
  if [ -n "$CXX_THREAD_OPTS" ]; then
    echo "CXXTHREADOPTS = $CXX_THREAD_OPTS" >> Make.config
  fi
  if [ -n "$CC_COMMAND" ]; then
    echo "CC = $CC_COMMAND" >> Make.config
  fi
  if [ -n "$CC_OPTS" ]; then
    echo "COPTS = $CC_OPTS" >> Make.config
  fi

  if (( $use_debug )); then
    echo 'CXXOPTS = -g' >> Make.config
    echo 'CXXTHREADOPTS = -g' >> Make.config
    echo 'CXXSIMPARAMOPTS = -g' >> Make.config
    echo 'CXXNOALIASOPTS = -g' >> Make.config
    echo 'COPTS = -g' >> Make.config
  fi

  echo "Linking Makefile"
  ln -s .rootdir/Makefile ./Makefile
  echo "Linking Make.depends"
  ln -s .rootdir/Make.depends ./Make.depends
  echo "Linking src directory"
  ln -s .rootdir/src ./src
  echo "Linking plugins directory"
  ln -s .rootdir/plugins ./plugins
  echo "Linking psfgen directory"
  ln -s .rootdir/psfgen ./sb
  echo "Linking colvars directory"
  ln -s .rootdir/colvars ./colvars
  echo "Linking lepton directory"
  ln -s .rootdir/lepton ./lepton

  echo ''
  echo "Generated $BUILD_LINK/Make.config contains the following:"
  echo ''
  cat Make.config

  echo ''
  echo "You are ready to run make in directory $BUILD_LINK now."
