#! /usr/bin/bash
#
# Master controller for building GMP on Windows using the WRICC and WRILD
# compiler & linker wrappers.

# This must be run from the top directory of the GMP sources,
# where "configure" is.  (We could probably fix this so it finds
# the directory automatically.)

#
# The typical invocation for this script is:
#    cd gmp-4.0
#    ../wri-build-gmp -O2 i486         for an optimized version (typical x86)
# or
#    ../wri-build-gmp -g i486          for a debug version (typical x86)
#
# Those commands build an i486-pc-cygwin version of GMP, which is the
# best generic 80x86 target.
# For an all-C no-assembler version, use none instead of i486.
#

# Print usage message and terminate.
function Fail_Usage
{
    local usage
    local ublanks

    usage="Usage: `basename $scriptname`"
    ublanks="${usage//?/ }"  # blanks the same length as usage
    echo   "$usage [-c] [-g] [-h] [-O[#]] [-V] [ENVVAR=value ...]" >&2
    echo "$ublanks [--configure-option ...] hostCPU" >&2
    echo >&2
    echo "Enter \"`basename $scriptname` -h\" for help." >&2
    exit 1;
}

# See if the $op_configure_arguments variable -- the list of all the
# --foo type arguments -- contains $1 in it.  Return 0 or 1 (false or
# true) in $contains.
function Check_Args_Contains
{
    if [ -n "`echo $op_configure_arguments | grep -e $1`" ]; then
        contains=1
    else
        contains=0
    fi
}

function Print_Help_And_Exit
{
    echo -n "`basename $scriptname` "
cat <<EOF
builds GMP on Cygwin using the wricc and wrild wrappers
for Microsoft CL and LINK.  It invokes the GMP configure script, then
does a complete rebuild using "make clean" and then a full make.
Because it uses wricc and wrild, it requires that either the Visual C
tools be installed with environment variables set up to allow them to
run from the command line, or that VCVARS32.BAT be run beforehand.

EOF
echo "Usage: `basename $scriptname` [ options ]"
cat <<EOF
OPTIONS
  -c              Run "configure" only.  Do not compile and build GMP.

  -g              Produce a debug version.  Optimization is turned off
                  (unless -O# is specified), a debug version of the
                  runtime library is linked, and the -g flag is passed
                  to wri-make and the linker to produce symbol table
                  information (.PDB file).

  -h              Print this help message.

  -O[...]         Pass optimization flags on to the compiler.
                  -O2 builds a speed-optimized version of GMP.

  -V              Verbose mode.  Show the command lines given to the
                  underlying tools.

  ENVVAR=value    Environment variables may be set up for configure.
                  For instance: CFLAGS="-Z -MT"

  --configure-option
                  GMP's configure script has many user-controllable
                  build settings.  To get a full listing, enter
EOF
echo "                  `basename $scriptname` --help"
cat <<EOF
                  Some useful ones are:
                  --enable-alloca={alloca,malloc-reentrant,malloc-notreentrant}
                  --enable-assert                        (add assert checking)
                  --disable-shared --enable-static       (builds a static .LIB)

  hostCPU         The target host CPU type for the GMP library.
                  This can be a configuration triplet: i486-pc-cygwin
                  Since the last two elements are always -pc-cygwin, only
                  the first element needs to be given:  i486, pentium2, none
                  "none" selects an all-C no-assembler version.  Slow but safe.

SOME TYPICAL INVOCATIONS
EOF
echo "  `basename $scriptname` -O2 i486  : build a generic 80x86 optimized GMP"
echo "  `basename $scriptname` -g none   : build a C-only debugging GMP"

exit 0;
}


# "nmmunged" runs nm (actually the 64-bit nm replacement wri-nm-64) and
# then modifies the output so it appears that external symbols in the
# read-only data section (marked R) are actually in the text section (T).
#
# It only does this when the file is called "conftest.o" and the symbol
# is a valid C identifier, thus excluding section names.
#
# This is, of course, a hack.  Why is it done?
#
# * "configure" insists on running NM to look for symbol names, and it
#   mistakenly believes function names will be in the text section, and
#   preceded by either a _ character or nothing.
#
#   On Win64, function names are preceded by a . character in the text
#   section, and nothing in the read-only data section.  (There are
#   therefore two entries for each function.)
#
#   Since "configure" doesn't look in the R section (at least on Cygwin,
#   since it thinks Cygwin==Win32) we need to "munge" the NM output to
#   put the un-dotted names somewhere it will look for them!
#
# * In a 64-bit build, we export this function and have "configure" use
#   it as the platform-specific version of NM.  (See below, "export -f".)
#
# The end result is that "configure" sees the function names when it is
# performing its test entitled "checking command to parse NM output from
# wricc object..."  With luck, everything will just work.
#
function nmmunged {
    # If the filename is conftest.o then munge the NM output.
    if [ "${*/conftest.o/}" != "$*" ]; then
       wri-nm-64 $@ | sed 's/ R\(  *[A-za-z0-9_]\)/ T\1/'
    else
       wri-nm-64 $@
    fi
}

# Set defaults
op_configure_arguments=		# Standard configure arguments like --foo
op_configure_envvars=		# Environment variable settings like VAR=val
op_configure_cflags=		# Value of CFLAGS environment variable
op_configure_cppflags=		# Value of CPPFLAGS environment variable
op_debug=0			# Debugging version?
op_optimize=			# Optimization flags
op_config_only=0		# Run configure without running make afterwards
op_host=			# Target host, e.g. i386, pentium2, none
op_verbose=0			# Verbose mode: echo commands invoked

# Default constants
MALLOC=malloc-reentrant	# Malloc type.  --enable-alloca option.
BUILD_DLL="--enable-shared --disable-static"	# Flags to select a DLL build.

# Get the directory that this script is located in.  We require
# that the other WRI tools (wricc, wrild, ln, etc.) are in the same
# directory.
script_dir=`dirname "${0}"`
script_dir=`cd "${script_dir}"; pwd`

# Find the absolute pathname of the wri-include header that will be
# force-included in every GMP file built.  Convert its backslashes
# to forward slashes.
include_file="`cygpath -w $script_dir/wri-include.h`"
# The following technique works in both bash 2 and bash 3.
backslash='\\'
include_file="${include_file//$backslash//}"

scriptname="$0"

# If no arguments, print a usage message and quit.
if [ $# -eq 0 ]; then
    Fail_Usage
fi

# Work through all the command-line arguments
while [ $# -ne 0 ]; do
    arg="$1"

    case "$arg" in
	--* ) op_configure_arguments="$op_configure_arguments$arg " ;;

	-g )  op_debug=1 ;;

	-O* ) op_optimize="$arg" ;;

	-c )  op_config_only=1 ;;

	-h )  Print_Help_And_Exit ;;

	-V )  op_verbose=1 ;;

	-* )  echo "Invalid option: $arg" >&2
	      Fail_Usage ;;

	CFLAGS=* ) cf="${arg#CFLAGS=}"			# the flags themselves
	           op_configure_cflags="$cf " ;;

	CPPFLAGS=* ) cf="${arg#CPPFLAGS=}"		# the flags themselves
	           op_configure_cppflags="$cf " ;;

	*=* ) op_configure_envvars="$op_configure_envvars$arg " ;;

	# Take any other word as the intended host; e.g. i386
	* ) op_host="$arg" ;;
    esac

    shift
done

# Work out the host type, if any.
if [ -n "$op_host" ]; then
    case "$op_host" in
	# Build a fat binary.  Host is irrelevant.
	fat ) op_configure_arguments="$top_configure_arguments--enable-fat "
	      op_host="" ;;
	# If there's more than one dash-separated element, assume that
	# this is a configuration triplet and leave it alone.
	*-* ) op_host="--host=$op_host " ;;
	# If there's only one element (no dashes), it's the first part
	# of a configuration triplet.  Complete the triplet.
	* ) op_host="--host=$op_host-pc-cygwin" ;;
    esac
else
    echo "Must supply a host CPU." >&2
    Fail_Usage
fi

# Set debugging flag & optimization level.  It is actually possible
# to have debugging (symbols, debug C runtime library) with optimization.
# (Sometimes it's the only way to track down an optimizer bug.)
if [ $op_debug = 1 ]; then
    op_configure_cflags="$op_configure_cflags-g "
fi
if [ -n "$op_optimize" ]; then
    op_configure_cflags="$op_configure_cflags$op_optimize "
fi

# Choose default settings for several configure --options.
# Check first to see if user is overriding them, though.

Check_Args_Contains "--enable-static"
es_contains=$contains
Check_Args_Contains "--disable-shared"
if [ $es_contains = 0 -a $contains = 0 ]; then
    op_configure_arguments="$op_configure_arguments$BUILD_DLL "
    op_configure_cppflags="${op_configure_cppflags}-DDLL_EXPORT "
fi

# The --help command just makes configure print out help.  So don't run make.
Check_Args_Contains "--help"
if [ $contains = 1 ]; then
    op_config_only=1
fi

# If we're in verbose mode, pass it on.
if [ $op_verbose = 1 ]; then
    op_configure_verbose="-V "
else
    op_configure_verbose=
fi

# Determine if this is a 32-bit or 64-bit build, by looking at the
# header of CL.  Snip off the last bit of the first line, case-flatten it,
# and use it as a platform determiner.  (80x86, ia-64, amd64).  Set bit64 if
# this is a 64-bit build (the platform name ends in "64"), and set it to
# the platform name if so.
clplatform=`cl 2>&1 | dos2unix | head -1 | grep -E -o '[^[:space:]]+$' | tr [A-Z] [a-z]`
if [ x"${clplatform%64}" != x"$clplatform" ]; then
    bit64="$clplatform"
else
    bit64=
fi

# Add 64-bit relevant environment variables
if [ -n "$bit64" ]; then
    export -f nmmunged
    op_configure_envvars="${op_configure_envvars}NM=nmmunged ABI=64 "
else
    op_configure_envvars="${op_configure_envvars}ABI=32 "
fi

# Set appropriate -mcpu flag, indicating best guess at CPU type.
# (Can be overridden by user's own CFLAGS.)

# For x86s, we ALWAYS force to pentiumpro (/G6, the best Visual C
# optimization), unless pentium4 (for which use /G7).  The result
# will work on any CPU but is best on Ppro and above (which is
# everybody these days).
if [ -z "`echo $op_configure_cflags | grep -E -e '-mcpu|-march'`" ]; then
    if [ x"$bit64" = x"ia-64" ]; then
        mcpu="ia64"
    elif [[ (x"$bit64" = x"amd64") || (x"$bit64" = x"x64") ]]; then
        mcpu="x86_64"
    else
        # Snip off beginning of host name.
	mcpu=${op_host#--host=}
        mcpu=${mcpu%%-*}
        case "$mcpu" in
            p4|pentium4 ) ;;
            * ) mcpu="pentiumpro" ;;
        esac
    fi

    op_configure_cflags="${op_configure_cflags}-mcpu=$mcpu "
fi

# Make the CFLAGS argument, if any.
if [ -n "$op_configure_cflags" ]; then
    op_configure_cflags="CFLAGS=\"$op_configure_cflags\""
fi

# Make the CPPFLAGS
op_configure_cppflags="CPPFLAGS=\"$op_configure_cppflags-Yc,/FI$include_file\""

# Now invoke "configure"

PATH="$script_dir":$PATH

cmdline="bash ./configure $op_host $op_configure_arguments$op_configure_envvars$op_configure_cflags CC=wricc LD=wrild $op_configure_cppflags"
# cmdline="bash ./configure $op_host $op_configure_arguments$op_configure_envvars$op_configure_cflags CC=wricc LD=wrild CPPFLAGS=\"$op_configure_verbose-Yc,/FI$include_file\""

if [ $op_verbose = 1 ]; then
    echo "$cmdline"
fi
eval "$cmdline" 	# Perform the configure
retval=$?		# Save exit status
if [ $retval != "0" ]; then
    exit $retval	# configure failed!
fi

# If -c option passed, so we only want a configure, stop now before make.
if [ $op_config_only = 1 ]; then
    exit 0;
fi

# Time to make!
# First we must "make clean" because dependencies don't work.
make clean
rm -f *.exe		# These aren't always caught by make clean.

# Now we invoke wri-make.  Use -g if a debugging version is being built.
makeargs=
if [ $op_debug = 1 ]; then
    makeargs=-g
fi

if [ $op_verbose = 1 ]; then
        echo "wri-make $makeargs"
    fi
wri-make $makeargs
retval=$?		# Save exit status

exit $retval

# Copyright 2012 Wolfram Research, Inc.
# 
# This file is part of the build scripts for building the GNU MP Library
# for Mathematica on Windows (32-bit).
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at your
# option) any later version.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
# License for more details.
# 
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see http://www.gnu.org/licenses/.
