#!/bin/sh

# An MSYS shell script which helps to build GMP 6.0.0 for Windows
# It must be run from Build/Windows{-x86-64}/mingw/build.sh.

# --------------------------------------------------------------

if [ -z "$bitness" ]; then
    echo WRONG!
    echo This script is only meant to be run by one of the specific build.sh
    echo scripts in the Build/Windows/mingw subdirectories.  Go down there,
    echo read the README file, and run build.sh as directed.
    exit 1
fi

# --------------------------------------------------------------

echo Building GMP for Windows ${bitness}-bit

# make *.X expand to nothing if there are no .X files in the directory
# useful for for-loop copies, below
shopt -s nullglob

if $use_microsoft_LIB ; then
	type -P LIB.EXE &> /dev/null
	if [ $? != 0 ] ; then
		echo ----------------------------------------------------------
		echo ERROR: You need to have Microsoft\'s LIB.EXE on the path
		echo before running this script.  You do this by running a
		echo Visual Studio batch file like \"vcvarsall\" or \"vcvars32\"
		echo to set the VS environment variables in a Command Prompt,
		echo then running \"MSYS.BAT\" to start MSYS in the same
		echo session.
		exit 1
	fi
fi

cwd=`pwd`
outputDir=$cwd/../../../Windows${output_dir_suffix}/mingw

echo Removing pre-existing working build directories
if [ -e ./Source ]; then
	rm -rf ./Source
fi
mkdir ./Source

if [ -e ./install ] ; then
	rm -rf ./install
fi

if [ -e ./modified-files ] ; then
	rm -rf ./modified-files
fi
mkdir ./modified-files


echo Copying the GMP Source directory to a local subdirectory
cp -R ../../../Source/* Source
cd Source

# Fix a bug in memory.c where it uses "%lu" (long int) to print a size_t

sed -e '/%lu/{ s/%lu/%zu/g; s/(long)//g; }' < ../../../../Source/memory.c > memory.c

echo Converting Windows newlines to Unix newlines in Source -- may be slow
# MSYS tools are incapable of dealing with \r\n newlines; they require \n
# dos2unix-all-files is a script that runs the MSYS command "dos2unix" on a
# list of files.
find . -type f | xargs ../dos2unix-all-files

# Switch the mp_size_t, mp_exp_t, and mp_bitcnt_t types to be "long long int"
# rather than "long int", so they will be properly 64 bits wide on Win64.
# (Be sure to exclude any that are already long long.)
if [ $bitness -eq 64 ]; then
    sed -i -e '/long long/!{/^\s*typedef.*mp_\(size\|exp\|bitcnt\)_t;\s*$/s/\slong\s/ long long /}' gmp-h.in
fi

# CFLAGS SETTINGS
# -Wno-attributes silences annoying complaints about declarations with no
#    meaningful __dllexport__ attribute.
# -m32/-m64 indicate whether this is a 32-bit or 64-bit compile.  I force them
#    into the GCC arguments because a bug in GMP's configure script omits them.
# -posix turns on C99 stdio functions (not MSVCRT), so printf can use %z, etc.
# -O3 is full optimization.

cflags="-Wno-attributes -posix -O3 -m${bitness}"

# OPTIONS TO CONFIGURE
# --enable-shared --disable-static builds a DLL
# --enable-alloca=malloc-reentrant uses the safest form of malloc
#     (testing shows it is as fast as alloca, so it is entirely adequate)
# --enable-fat produces a library that adapts to different CPU types

# LIBGMP_LDFLAGS
# MinGW GCC produces a DLL with a dependency on GPL-licensed "libgcc_s_dw-1.dll"
# unless -static-libgcc is passed to GCC when it's being used to link the DLL.
# That could go in LDFLAGS, but "libtool" strips out any flags it doesn't know.
# But it knows the special fake flag "-XCClinker -foo" means to use -foo anyway.
# "-XCClinker -foo" can't go in LDFLAGS because "configure" passes LDFLAGS to
# GCC when it builds its little tests.  LIBGMP_LDFLAGS is combined with LDFLAGS
# everywhere in the Makefile, and configure copies it there but doesn't use it.

./configure  ABI=${bitness}  CFLAGS="$cflags" LIBGMP_LDFLAGS="-XCClinker -static-libgcc" --prefix=$cwd/install  --enable-shared --enable-alloca=malloc-reentrant --enable-fat --disable-static

make
make install

if [ ! -e "$outputDir" ] ; then
	mkdir -p $outputDir
else
	pushd $outputDir
	rm *.h *.dll *.lib
	popd
fi

cd ..

echo ======================================================================


echo Rewrite config.h and gmp-impl.h headers so they can be handled by MSVC.

# HAVE_ATTRIBUTE_* refers to GCC attributes
# HAVE_INTTYPES_H refers to GCC header "inttypes.h"
# Microsoft has neither, so set their #define macros to 0.
sed -e '/HAVE_ATTRIBUTE_/s/1/0/' -e '/HAVE_INTTYPES_H/s/1/0/' < Source/config.h > modified-files/config.h

# Declaration of struct cpuvec_t has fields declared __declspec(dllimport),
# which is meaningless and an error, due to the __GMP_DECLSPEC macro.
# Undefine the macro, but only for the duration of the struct type declaration.
sed -e '/^struct cpuvec_t {/,/^};$/ {
/^struct cpuvec_t {/i\
#pragma push_macro("__GMP_DECLSPEC")\
#undef __GMP_DECLSPEC\
#define __GMP_DECLSPEC
/^};$/a\
#pragma pop_macro("__GMP_DECLSPEC")
}' < Source/gmp-impl.h > modified-files/gmp-impl.h


echo Copy files to final output directory.

cd install/bin
cp *.dll $outputDir

# Convert .h files to Windows newline format using MSYS command "unix2dos"

cd ../include
for f in *.h
do
	unix2dos -q -n $f $outputDir/$f
done

cd ../../Source
for f in *.h
do
	unix2dos -q -n $f $outputDir/$f
done

cd ../modified-files
for f in *.h
do
	unix2dos -q -n $f $outputDir/$f
done

cd ..

# Bug workaround:  The so-called import library (whose name ends in .a)
# generated by MINGW's linker is defective.  When it is linked with the kernel
# using the Microsoft LINK option /OPT:REF, the Microsoft linker discards all
# import references to GMP, replacing them with references to some other DLL!
#   Bug report:  http://sourceware.org/bugzilla/show_bug.cgi?id=12633
# Luckily, we can build a correct import library given Microsoft LIB.EXE and
# the DLL's .DEF file, which MINGW's linker creates.
# (Note: that bug report claims this is fixed in VS2012 and later, so this all
#  may be removable once we move past VS2010 for Mathematica 10.)

if $use_microsoft_LIB ; then
	echo Construct import library .lib using Microsoft LIB.EXE.
	cd Source/.libs
	deffile=(*.def)
	dllfile=(*.dll)
	defcount=${#deffile[@]}
	dllcount=${#dllfile[@]}
	cd ../..
	if [ $defcount != 1 -o $dllcount != 1 ]; then
		echo ERROR: More than one .DEF or .DLL file in Source/.libs directory:
		echo .DEF files: $deffile
		echo .DLL files: $dllfile
		echo Don\'t know which one is the GMP library .DEF or .DLL file.
		exit 1
	fi
	dllname=${dllfile%.dll}
	cp Source/.libs/${deffile} modified-files/${dllname}.def
	cd modified-files
	LIB /OUT:${dllname}.lib /DEF:${dllname}.def /MACHINE:${lib_bitness}
	cp ${dllname}.lib $outputDir
	cd ..
else
	echo Copy dlltool-built import library .lib -- dont link with /OPT:REF.
	pushd ${outputDir} &>/dev/null
	dllfile=(*.dll)
	dllname=${dllfile%.dll}
	popd &>/dev/null
	cp install/lib/libgmp.dll.a ${outputDir}/${dllname}.lib
fi

echo ------------------------------------------------------------------------
echo Finished building GMP.  All files are now in
pushd $outputDir &>/dev/null
pwd
popd &>/dev/null
echo When committing the files, remember to \"cvs add -kb\" the .dll and .lib

