#!/bin/sh

# Silly shorthand, used for source reorganization
src="src/arch"

more=0

syntax() {
  echo ''
  echo 'Usage: build <target> <version> <options> [charmc-options ...]'
  echo ''
  echo '<targets>: converse charm++ LIBS AMPI FEM bluegene pose'
  echo '<targets>: doc ps-doc pdf-doc html-doc'
  if test $more = 1
  then
  echo ''
  echo '  charm++         compile Charm++ core only'
  echo '  AMPI            compile Adaptive MPI on top of Charm++'
  echo '  FEM             compile FEM framework'
  echo '  LIBS            compile additional parallel libraries with Charm++ core'
  echo '  bluegene        build additional BigSim libraries'
  echo '  pose            build POSE parallel discrete event simulator'
  echo ''
  fi
  echo '<versions>: ' 
  ( cd $src ; ls -1 | egrep -v '(^CVS)|(^shmem$)|(^mpi$)|(^sim$)|(^net$)|(^elan$)|(^util$)|(^common$)|(^uth$)|(^conv-mach-fix.sh$)|(^win32$)|(^paragon$)|(^vmi$)' | pr -3 -t )
  echo ''
  echo '<options>: compiler and platform specific options'
  echo 'cc cc64 cxx kcc pgcc acc icc ecc gcc3 mpcc'
  echo 'help smp gm tcp vmi scyld clustermatic opteron bluegene ooc syncft'
  echo '--incdir --libdir --basedir --no-build-shared -j'
  if test $more = 1
  then
  echo ''
  echo 'For platform specific options, use help option:'
  echo '  help		  platform specific help, e.g. ./build charm++ net-linux help'
  echo ''
  echo 'Choose a compiler (only one option is allowed from this section):'
  echo '  cc, cc64        For Sun WorkShop C++ 32/64 bit compilers'
  echo '  cxx             DIGITAL C++ compiler (DEC Alpha)'
  echo '  kcc             KAI C++ compiler'
  echo "  pgcc            Portland Group's C++ compiler"
  echo '  acc             HP aCC compiler'
  echo '  icc             Intel C/C++ compiler for Linux IA32'
  echo '  ecc             Intel C/C++ compiler for Linux IA64'
  echo '  gcc3            use gcc3 - GNU GCC/G++ version 3'
  echo '  mpcc            SUN Solaris C++ compiler for MPI'
  echo ''
  echo 'Platform specific options (choose multiple if apply):'
  echo '  opteron         compile for AMD Opteron'
  echo '  smp             support for SMP, multithreaded charm on each node'
  echo '  gm              use Myrinet for communication'
  echo '  tcp             use TCP sockets for communication (ony for net version)'
  echo "  vmi             use NCSA's VMI for communication (only for mpi version)"
  echo '  scyld           compile for Scyld Beowulf cluster based on bproc'
  echo '  clustermatic    compile for Clustermatic (support version 3 and 4) '
  echo ''
  echo 'Advanced options:'
  echo '  bluegene	  compile for BigSim (Blue Gene) simulator'
  echo '  ooc		  compile with out of core support'
  echo '  syncft	  compile with Charm++ fault tolerance support'
  echo ''
  echo "Charm++ dynamic libraries:"
  echo "  --build-shared     build Charm++ dynamic libraries (.so) (default)"
  echo "  --no-build-shared  don't build Charm++'s shared libraries"
  echo ''
  echo 'Miscellaneous options:'
  echo '  --incdir=DIR	  specify additional include path for compiler'
  echo '  --libdir=DIR	  specify additional lib path for compiler'
  echo '  --basedir=DIR	  shortcut for the above two - DIR/include and DIR/lib'
  echo '  -j[N]           parallel make, N is the number of paralle make jobs'
  fi
  echo ''
  echo '<charmc-options>: normal compiler options e.g. -g -O -save -verbose'
  if test $more = 1
  then
  echo ''
  echo 'Examples:'
  echo "1. display all supported options for net-linux using 'help':"
  echo '  ./build charm++ net-linux help'
  echo '2. compile Charm++ on Linux with optimizations:'
  echo '  ./build charm++ net-linux -O -DCMK_OPTIMIZE'
  echo '3. compile Charm++ for Linux clustermatic Beowulf cluster with Intel compiler:'
  echo '  ./build charm++ net-linux clustermatic icc -O'
  echo '4. compile Charm++ for IA64 Linux cluster with MPICH-GM:'
  echo '  ./build charm++ mpi-linux-ia64 gm -O'
  echo '5. compile Charm++ for Windows with VC++ (Cygwin required):'
  echo '  ./build charm++ net-win32 -O'
  echo '6. compile on IBM SP with MPI using parallel gmake, 4 jobs simultaneously:'
  echo '  MAKE=gmake  ./build charm++ mpi-sp -j4 -O            # sh'
  echo '  setenv MAKE gmake;  ./build charm++ mpi-sp -j4 -O    # csh'
  echo '7. compile Charm++ with icc compiler and MPICH-GM, with gm lib at /opt/gm/lib:'
  echo '  ./build charm++ mpi-linux gm icc --libdir=/opt/gm/lib -O'
  echo ''
  echo 'Note: This script:'
  echo ' 1. Creates directories <version> and <version>/tmp';
  echo ' 2. Copies src/scripts/Makefile into <version>/tmp';
  echo ' 3. Does a "make <target> <version> OPTS=<charmc-options>" in <version>/tmp.'
  echo "That's all build does.  The rest is handled by the Makefile."
  echo ''
  echo 'You should register your copy of Charm++ with us by sending email'
  echo 'to tms@charm.cs.uiuc.edu containing the message "subscribe charm-users"'
  echo 'This will allow us to notify you of new releases of Charm++.'
  echo ''
  echo 'Thank you for using Charm++, please send questions or comments to '
  echo 'Parallel Programming Lab at University of Illinois at Urbana-Champaign'
  echo '(email: ppl@cs.uiuc.edu).'

  else
  echo ''
  echo "To get more detailed help, run ./build --help"
  fi
}

printOption() {
  for prefix in cc conv-mach
  do
    str="Supported compilers:"
    [ "$prefix" = "conv-mach" ] && str="Supported options:"
    opts=""
    for dir in $OPT_DIRS
    do
#         echo "Checking for $prefix in $dir"
          files=`cd $dir; ls $prefix-*.h 2>/dev/null`
          opts="$opts "`echo $files | sed 's/'$prefix'-\([^.]*\).h/\1/g'`
    done 
    echo $str $opts
  done
  exit 1
}

CheckDir() {
  for dir in $*
  do
    if test ! -d $dir
    then
      echo Error: cannot find $1!
      exit 1
    fi
  done
}

TestIfCompiler() {
  for dir in $OPT_DIRS
  do
    [ -f $dir/cc-$1.sh ] && return 1
  done  
  return 0
}

TestIfOption() {
  for dir in $OPT_DIRS
  do
    [ -f $dir/conv-mach-$1.h ] && return 1 
  done
  
  echo "Error> option: $1 is not supported in this version!"; 
  printOption
}


# start

MAKEOPTS=""
OPTS=""
BOPTS=""
MORE=""
COMPILER=""
BASEDIR=
LIBDIR=
INCDIR=
ARCH=
BUILD_SHARED=

[ "$1" = '--help' -o "$1" = '-h' ] && more=1 && syntax | more && exit 1
[ $# -lt 2 ] && syntax && exit 1
PROGRAM=$1
shift
VERSION=$1; BASEVERSION=$1; ARCH=`echo $1 | sed -e 's@-.*@@'`
shift

OPT_DIRS="$src/$BASEVERSION $src/$ARCH $src/common"

while [ ! $# -eq 0 ]
do
  case "$1" in
    	    "--basedir")
          	shift; CheckDir $1/include $1/lib
          	LIBDIR="$LIBDIR -L$1/lib";
          	INCDIR="$INCDIR -I$1/include"; shift
 	  	;;
    	    --basedir=*)
                basedir=`echo $1 | awk -F= '{print $2}'`
		CheckDir $basedir/include $basedir/lib
          	LIBDIR="$LIBDIR -L$basedir/lib";
          	INCDIR="$INCDIR -I$basedir/include"; shift
 	  	;;
    	    "--libdir")
          	shift; CheckDir $1
          	LIBDIR="$LIBDIR -L$1"; shift
 	  	;;
    	    --libdir=*)
                libdir=`echo $1 | awk -F= '{print $2}'`
		CheckDir $libdir
          	LIBDIR="$LIBDIR -L$libdir"; shift
 	  	;;
    	    "--incdir")
          	shift; CheckDir $1
          	INCDIR="$INCDIR -I$1"; shift
 	  	;;
    	    --incdir=*)
                incdir=`echo $1 | awk -F= '{print $2}'`
		CheckDir $incdir
          	INCDIR="$INCDIR -I$incdir"; shift
 	  	;;
	    --no-build-shared|-nobs|--no-shared)
		BUILD_SHARED="-nobs"; shift
		;;
	    --build-shared|-bs)
		# obsolete, do nothing
		shift
		;;
	    -j*)
                PMAKENUM=`echo $1 | awk -Fj '{print $2}'`
		MAKEOPTS="$MAKEOPTS -j $PMAKENUM"
		shift;
		;;
    -*) 
# We hit a real compiler option (like -g, or -Dfoo),
# so just copy over all remaining options
          while [ ! $# -eq 0 ]
          do
            OPTS="$OPTS $1"; shift
          done
	  ;;
    "help")
	printOption
	;;
    *)
# This has to be a build-time option (like "smp", "gcc3", or "kcc")
        TestIfCompiler $1
        if [ $? -eq 1 ] 
        then
# It specifies a compiler:
          if [ ! -z "$COMPILER" ] 
          then
              echo "Error> Tried to specify two compilers: $COMPILER and $1"
              printOption
          fi
          COMPILER=$1
        else
# It specifies some other option:
          TestIfOption $1
          BOPTS="$BOPTS $1"
        fi
        shift
	;;
  esac
done

[ "x$VERSION" = "x" ] && syntax && exit 1

[ -z "$MAKE" ] && MAKE='make'

if [ ! -f $src/$BASEVERSION/conv-mach.h ] 
then
  echo "Error> build can not find arch: $BASEVERSION!" 
  exit 1
fi

#generate VERSION name combining all the build-time options.
if [ -n "$BOPTS" -o -n "$COMPILER" ]
then
  echo "Selected Compiler: $COMPILER"
  echo "Selected Options: $BOPTS"
  SORTED=`echo $BOPTS | awk  '{ for (i = 1; i <= NF; ++i) print $i }' | sort`
  BOPTS_WITHCOMPILER=`echo $SORTED $COMPILER`
  for i in $BOPTS_WITHCOMPILER
  do
    VERSION=$VERSION-$i
  done
fi

[ -d $VERSION ] || echo "Creating dir: $VERSION" 
[ -d $VERSION ] || mkdir $VERSION
[ -d $VERSION/tmp ] || echo "Creating dir: $VERSION/tmp" 
[ -d $VERSION/tmp ] || mkdir $VERSION/tmp

echo "Copying src/scripts/Makefile to $VERSION/tmp"
rm -f $VERSION/tmp/Makefile
rm -f $VERSION/tmp/Make.depends

if [ "x_`echo $VERSION | awk -F- '{print $2}'`" = "x_win32" ]
then
#Win32 version needs special compilers and *copied* (not linked)
# source files.
	echo "Copying compilers for win32 into cygwin /bin directory"
	cp $src/win32/unix2nt* /bin
	cp $src/win32/system_ln $VERSION/tmp
  chmod +x $VERSION/tmp/system_ln
else
	cat > $VERSION/tmp/system_ln <<EOF
#!/bin/sh
ln -f -s \$@
EOF
  chmod +x $VERSION/tmp/system_ln
	for newdir in `echo bin lib lib_so include tmp`
	do
		echo "Soft-linking over $newdir"
    if [ -r $newdir ]
    then
      rm -f $newdir
    fi
		$VERSION/tmp/system_ln $VERSION/$newdir $newdir
	done
fi

$VERSION/tmp/system_ln "../../src/scripts/Makefile" $VERSION/tmp/Makefile
$VERSION/tmp/system_ln "../../src/scripts/Make.depends" $VERSION/tmp/Make.depends

ConvUsr="$VERSION/tmp/conv-mach-pre.sh"
echo "Generating $ConvUsr"
echo > $ConvUsr
test -n "$LIBDIR" && echo "CMK_LIBDIR=\"$LIBDIR\"" >> $ConvUsr
test -n "$INCDIR" && echo "CMK_INCDIR=\"$INCDIR\"" >> $ConvUsr
chmod +x $ConvUsr

# Create conv-mach-opt headers with special build-time options
ConvHeader="$VERSION/tmp/conv-mach-opt.h"
ConvSh="$VERSION/tmp/conv-mach-opt.sh"
if [ ! -f $ConvSh -o ! -f $ConvHeader ]
then
  echo "Generating $ConvHeader, conv-mach-opt.sh"
  echo '/* Build-time options header, automatically generated by charm/build*/'> $ConvHeader
  echo '# Built-time options header, automatically generated by charm/build'> $ConvSh
  echo '[ -z "$CHARMINC" ] && CHARMINC="."' >> $ConvSh
fi
if test -n "$COMPILER"
then
      i="$COMPILER"
      echo '#include "'cc-$i.h'"' >> $ConvHeader
      echo '. $CHARMINC/'"cc-$i.sh" >> $ConvSh
fi
if test -n "$BOPTS"
then
    for i in $BOPTS
    do
      echo '#include "'conv-mach-$i.h'"' >> $ConvHeader
      echo '. $CHARMINC/'"conv-mach-$i.sh" >> $ConvSh
    done
fi
if test  -n "$BUILD_SHARED" 
then
    echo "CMK_NO_BUILD_SHARED=true" >> $ConvSh
fi


echo $BASEVERSION > $VERSION/tmp/.vdir

echo "Performing '$MAKE $MAKEOPTS $PROGRAM OPTS="$OPTS"' in $VERSION/tmp"
cd $VERSION/tmp 
$MAKE $MAKEOPTS $PROGRAM OPTS="$OPTS $BUILD_SHARED"

MAKEEXIT=$?
if [ $MAKEEXIT -eq 0 ]
then
	echo "-------------------------------------------------"
	echo "$PROGRAM built successfully."
	echo "Next, try out a sample program like" \
	     "pgms/charm++/simplearrayhello"
else
	echo "-------------------------------------------------"
	echo "Charm++ NOT BUILT. Either cd into $VERSION/tmp and try"
	echo "to resolve the problems yourself, visit"
	echo "    http://charm.cs.uiuc.edu/"
	echo "for more information. Otherwise, email the developers at ppl@cs.uiuc.edu"
	exit $MAKEEXIT
fi
