#!/bin/bash

# Compiling SWI-Prolog using MinGW
# --------------------------------
#
# This file provides an overview for building SWI-Prolog using MinGW, in
# particular using the cross-compilers that   are  distributed with most
# todays Linux distributions. This  work  is   mostly  done  by  Roberto
# Bagnara (initial port of the Prolog   kernel), Abramo Bagnara and Keri
# Harris.
#
# USAGE
#
# The remainder of the  file  is   an  executable  specification  of the
# installation process. To use it, copy this   file to a directory above
# the location where you want to   build all components. I.e., typically
# the directory above this one and source it into the bash shell:
#
#   % . README.mingw win32|win64
#
# Next, execute the functions for the parts of the process you want. The
# function build_all does everything.
#
# This script has been tested for   cross-compilation  on a Linux system
# with the mingw cross-compiler and wine installed.
#
# PREREQUISITES
#
# The open source libraries on which   SWI-Prolog depends are downloaded
# and   built   using   the   functions   download_prerequisites()   and
# build_prerequisites().  In  addition,  the  following    need   to  be
# installed:
#
#   - The Oracle Java SDK for building JPL
#	- jdk-<version>-windows-i586.exe into
#	  /C/Program Files (x86)/... (default on 64-bit Windows)
#	- jdk-<version>-windows-x64.exe into
#	  /C/Program Files/... (default on 64-bit Windows)
#   - NSIS installer generator for generating the installer.
#       - nsis-<version>-setup.exe into /C/Program Files (x86)/...
#   - MSVC tools (lib.exe) for creating MSVC compatible .lib files
#       - Install Microsoft Visual C++ Express on your Windows partition
#       - Copy "c:/Program Files (x86)/Microsoft Visual Studio 9" into
#	  your ".wine/drive_c/Program Files (x86)"
#	- Use "wine regedit" to add ...\VC\bin and ...\Common7\IDE to
#	  your wine PATH.
#	- Run "wine lib.exe" to see whether it works
#	(See
#	http://issaris.blogspot.nl/2008/04/creating-dlls-using-visual-c-8-on.html)
#
# UBUNTU PACKAGES
#
# Tested on Ubuntu 12.04 on AMD64. Installing   wine  in this version is
# broken, but can be get  te  work   using  the  following  commands. In
# particular WINE-GECKO MUST BE INSTALLED FIRST.
#
#   - Remove all wine and wingw packages
#   - apt-get install wine-gecko1.5
#   - apt-get install wine
#   - apt-get install mingw-w64
#
# If you cannot run Win64 executables with   your  wine, .wine is out of
# date. The only working advice I found   sofar is to (re)move .wine and
# start wine to create a fresh installation.
#
# Linux must be configured such that Windows  .exe files can be executed
# directly    without    explicit    invocation      of     wine.    See
# https://www.kernel.org/doc/Documentation/binfmt_misc.txt
#
# Updated for Ubuntu 14.04. This  version   ships  with MinGW 4.8, which
# provides winpthread. If you upgrade, you  must remove pthreads-w32 and
# recompile the prerequisites to depend on MinGW's native pthreads:
#
#   - Remove pthreads-w32 and all traces from $install/bin, $install/lib
#     and $install/include
#   - Copy the new libgcc_s_sjlj-1.dll and libwinpthread-1.dll from the
#     GCC installation to $install/bin
#   - Run clean_prerequisites (function in this script)
#   - Run build_prerequisites (function in this script)

TARGETHOST="$1"
MAKE="make -j 4"

case "$TARGETHOST" in
    win64) BITS=64
	 ;;
    win32) BITS=32
	 ;;
    *)	   echo "Please run as . $BASH_SOURCE 'win32|win64'"
	   return
esac

PS1=W$BITS' (\W\[\e[36m\]$(__git_ps1 "; %s")\[\e[0m\]) \!_> '

export BUILDARCH=$TARGETHOST

# Versions of prerequisite packages. Used to  download the right release
# file.

GMP_VERSION=6.1.1
PTHREADS_VERSION=2-9-1-release
LIBDWARF_VERSION=20120410
SSL_VERSION=1.1.0f
JPEG_VERSION=9b
ZLIB_VERSION=1.2.8
ARCHIVE_VERSION=3.2.1
UUID_VERSION=1.6.2
BDB_VERSION=6.1.26
PCRE_VERSION=8.40
PLDIR=pl-devel

# installation prefix.  This path should not have spaces in one of the
# directory names.

install="${install-$HOME/mingw$BITS}"
src="$(pwd)"
PATH=$install/swipl/bin:$PATH

# Check whether we are cross-compiling or running under MSYS in Windows.
# Note that the MSYS version has not yet been tested.

case "$(uname)" in
    MINGW*)
	CROSSCOMPILING=no
	export PATH=$install/bin:$PATH
	DRIVEC="/C"
	MINGWGCC=gcc
	;;
    *)
	export CROSSCOMPILING=yes
	export WINEDEBUG=-all
	DRIVEC="$HOME/.wine/drive_c"
	export WINEPATH="$(winepath -w $install/bin)"
	;;
esac

if [ "$CROSSCOMPILING" = yes ]; then
  if [ -z "$CROSS" ]; then
    case $TARGETHOST in
      win64)
	CROSS=x86_64-w64-mingw32
	;;
      win32)
        if [ -r /usr/bin/i686-w64-mingw32-gcc ]; then
	  CROSS=i686-w64-mingw32
	else
	  CROSS=i586-mingw32msvc
	fi
	;;
    esac
  fi
  MINGWGCC=$CROSS-gcc
  export WINDRES=$CROSS-windres
fi

# See whether pthread is built-in

if [ -r "$($MINGWGCC -print-file-name=libwinpthread-1.dll)" ]; then
  PTHREADS_VERSION=
fi

# Java configuration
# Note that Win64 javac and javadoc crash under Wine (1.5.6).  But, we
# can use the native Linux versions

if [ -z "$JAVAROOT" ]; then
  case $TARGETHOST in
    win64)
      JAVAROOT="$DRIVEC/Program Files/Java"
      if [ "$CROSSCOMPILING" = yes ]; then
        export JAVAC=javac
	export JAVADOC=javadoc
      fi
      ;;
    win32)
      JAVAROOT="$DRIVEC/Program Files (x86)/Java"
      ;;
  esac
fi
JAVAPREFIX="$(echo "$JAVAROOT"/jdk*)/bin"
export JAVAPREFIX

# Set some variables to find the Windows libraries and include files

# export COFLAGS=-g
export CIFLAGS="-I$install/include"
export CPPFLAGS=-I$install/include
export LDFLAGS="-L$install/lib"
export PKGLDFLAGS=-I$install/lib


################################################################
# end of gathering info about the environment; the remainder are
# functions that perform steps of the build process
################################################################

################
# Handy for running autoconf from a directory

config()
{ if [ -r ./configure ]; then
    ./configure --host=$CROSS --prefix=$install
  elif  [ -r ../src/configure ]; then
    ./configure --host=$CROSS --prefix=$install
  fi
}


################
# Create target directories

make_mingw_dirs()
{ mkdir -p $install/include
  mkdir -p $install/lib
  mkdir -p $install/bin
}


###########################
# Download and install the GMP library.

download_gmp()
{ GMP_FILE=gmp-$GMP_VERSION.tar.bz2

  [ -f $GMP_FILE ] || \
    wget ftp://ftp.gmplib.org/pub/gmp-$GMP_VERSION/$GMP_FILE
  tar jxf $GMP_FILE
}

build_gmp()
{ ( cd gmp-$GMP_VERSION
    ./configure --host=$CROSS --prefix=$install \
       --enable-shared --disable-static
    make
    make install
  )
}

###########################
# Download and install the Pthreads-win32 library.  Only if PTHREADS_VERSION
# is set. Else we assume a recent MinGW64, which ships with
# libwinpthread

download_pthread_win32()
{ if [ ! -z "$PTHREADS_VERSION" ]; then
    PTHREADS_FILE=pthreads-w32-$PTHREADS_VERSION.tar.gz

    cd $src
    [ -f $PTHREADS_FILE ] || \
      wget ftp://sourceware.org/pub/pthreads-win32/$PTHREADS_FILE

    tar xzf $PTHREADS_FILE
  fi
}

build_pthread_win32()
{ if [ -d pthreads-w32-$PTHREADS_VERSION ]; then
    ( cd pthreads-w32-$PTHREADS_VERSION
      make -f GNUmakefile CROSS=$CROSS- \
	  LFLAGS="-lwsock32 -Wl,--out-implib=libpthreadGC2.dll.a" GC
      cp pthread.h semaphore.h sched.h $install/include/
      cp pthreadGC2.dll $install/bin/
      cp libpthreadGC2.dll.a $install/lib/
    )
  fi
}

###########################
# Download and install the DWARF debugging library.

download_libdwarf()
{ LIBDWARF_FILE=libdwarf-$LIBDWARF_VERSION.tar.gz

  [ -f $LIBDWARF_FILE ] || \
    wget http://www.prevanders.net/$LIBDWARF_FILE
  tar xzf $LIBDWARF_FILE
}

build_libdwarf()
{ ( cd dwarf-$LIBDWARF_VERSION/libdwarf
    ./configure --host=$CROSS --enable-shared
    sed -i -e "s|libdwarf.so|libdwarf.dll|" \
           -e "s|\$(CFLAGS) -shared|\$(LDFLAGS) -shared|" \
           -e "/_elf_/d" -e "/[ \t]pro_/d" -e "/dwarf_addr_finder/d" Makefile
    make PRELIBS="-Wl,--out-implib=libdwarf.dll.a"
    mkdir -p $install/bin
    cp libdwarf.dll $install/bin/
    mkdir -p $install/lib
    cp libdwarf.a libdwarf.dll.a $install/lib/
    mkdir -p $install/include
    cp dwarf.h libdwarf.h $install/include
  )
}

###########################
# Download and install ssl

download_ssl()
{ SSL_FILE=openssl-$SSL_VERSION.tar.gz
  [ -f $SSL_FILE ] || wget http://www.openssl.org/source/$SSL_FILE
  tar xzf $SSL_FILE
}

build_ssl()
{ ( cd openssl-$SSL_VERSION
    export CC=$MINGWGCC
    case $CROSS in
	i?86*)	 MINGW=mingw
		 ;;
	x86_64*) MINGW=mingw64
		 ;;
	*)	 echo "SSL: Unknown CROSS: $CROSS"
		 exit 1
		 ;;
    esac;
    ./Configure $MINGW --prefix=$install shared threads
    make depend
    make
    make install_sw
  )
}

###########################
# Download and install BerkeleyDB
# http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/overview/index.html

download_libdb()
{ BDB_FILE=db-$BDB_VERSION.tar.gz

  [ -f $BDB_FILE ] || \
  curl http://download.oracle.com/berkeley-db/$BDB_FILE > $BDB_FILE
  tar zxvf $BDB_FILE
}

build_libdb()
{ ( cd db-$BDB_VERSION/build_unix
    sed -i -e "s:WinIoCtl.h:winioctl.h:" ../src/dbinc/win_db.h
    ../dist/configure --enable-mingw --host=$CROSS --prefix=$install \
       --enable-shared --disable-static
    make library_build
    make install_lib install_include
    ( cd $install/lib
      [ -f libdb.dll.a ] || ln -s libdb-*.dll.a libdb.dll.a
      [ -f libdb.la ] || ln -s libdb-*.la libdb.la
    )
  )
}

###########################
# Download and install jpeg

download_jpeg()
{ JPEG_FILE=jpegsrc.v$JPEG_VERSION.tar.gz

  [ -f $JPEG_FILE ] || wget http://www.ijg.org/files/$JPEG_FILE
  tar xzf $JPEG_FILE
}

build_jpeg()
{ ( cd jpeg-$JPEG_VERSION
    ./configure --host=$CROSS --prefix=$install --enable-shared
    make
    make install
  )
}

###########################
# Download and install libXpm

download_libxpm()
{ if [ -d libXpm ]; then
     (cd libXpm && git pull)
  else
     git clone https://github.com/SWI-Prolog/libXpm.git
  fi
}

build_libxpm()
{ ( cd libXpm/lib
    autoconf
    ./configure --host=$CROSS --prefix=$install
    make -f Makefile.mingw
    make -f Makefile.mingw install
  )
}

###########################
# Download and install zlib

download_zlib()
{ ZLIB_FILE=zlib-$ZLIB_VERSION.tar.gz

  [ -f $ZLIB_FILE ] || wget http://zlib.net/$ZLIB_FILE
  tar xzf $ZLIB_FILE
}

build_zlib()
{ ( cd zlib-$ZLIB_VERSION
    make -f win32/Makefile.gcc PREFIX=$CROSS- BINARY_PATH=$install/bin INCLUDE_PATH=$install/include LIBRARY_PATH=$install/lib SHARED_MODE=1 IMPLIB=libz.dll.a install
  )
}

#################################
# Download and install libarchive

download_libarchive()
{ ARCHIVE_FILE=libarchive-$ARCHIVE_VERSION.tar.gz

  [ -f $ARCHIVE_FILE ] || \
    wget http://www.libarchive.org/downloads/$ARCHIVE_FILE
  tar xzf $ARCHIVE_FILE
}

# lt_cv_deplibs_check_method=pass_all works around a bug in libtool
# causing: "linker path does not have real file for library" error on MinGW
# See http://lists.cairographics.org/archives/cairo/2009-July/017686.html

build_libarchive()
{ ( cd libarchive-$ARCHIVE_VERSION
    export CFLAGS="-I$install/include"
    export LDFLAGS="-L$install/lib"
    export lt_cv_deplibs_check_method='pass_all'
    export ac_cv_func__localtime64_s='no'
    export ac_cv_func__ctime64_s='no'
    ./configure --host=$CROSS --prefix=$install --with-pic \
    --without-iconv --without-openssl --without-nettle --without-xml2 \
    --without-expat --without-libregex --without-bz2lib \
    --without-lzmadec --without-lzma --without-lzo2
    make
    make install
  )
}


#################################
# Download and install libpcre


download_libpcre()
{ PCRE_FILE=pcre-$PCRE_VERSION.tar.gz

  [ -f $PCRE_FILE ] || \
    wget https://ftp.pcre.org/pub/pcre/$PCRE_FILE
  tar xzf $PCRE_FILE
}


build_libpcre()
{ ( cd pcre-$PCRE_VERSION
    ./configure --host=$CROSS --prefix=$install \
	--disable-static --disable-cpp --enable-utf8 --enable-unicode-properties
    make pcre.dll
    make install
  )
}


#################################
# Download and install libuuid

download_libuuid()
{ UUID_FILE=uuid-$UUID_VERSION.tar.gz

  [ -f $UUID_FILE ] || \
  curl ftp://ftp.ossp.org/pkg/lib/uuid/$UUID_FILE > $UUID_FILE
  tar zxvf $UUID_FILE
}

build_libuuid()
{ ( cd uuid-$UUID_VERSION
    ./configure --host=$CROSS --prefix=$install
    make
    make install
  )
}


###########################
# Do the whole lot for all prerequisites

clean_prerequisites()
{ ( cd gmp-$GMP_VERSION && make distclean )
  ( cd openssl-$SSL_VERSION && make distclean )
  ( cd jpeg-$JPEG_VERSION && make distclean )
  ( cd libXpm/lib && make distclean )
  ( cd zlib-$ZLIB_VERSION && make distclean )
  ( cd libarchive-$ARCHIVE_VERSION && make distclean )
  ( cd uuid-$UUID_VERSION && make distclean )
  if [ -d pthreads-w32-$PTHREADS_VERSION ]; then
    ( cd pthreads-w32-$PTHREADS_VERSION && make distclean )
  fi
}


download_prerequisites()
{ download_gmp
  download_pthread_win32
  download_libdwarf
  download_ssl
  download_jpeg
  download_libxpm
  download_zlib
  download_libarchive
  download_libuuid
  download_libdb
  download_libpcre
}

build_prerequisites()
{ build_gmp
  build_pthread_win32
  build_libdwarf
  build_ssl
  build_jpeg
  build_libxpm
  build_zlib
  build_libarchive
  build_libuuid
  build_libdb
  build_libpcre
}

###########################
# Download and unpack a SWI-Prolog source distribution.

enter_topdir()
{ if [ ! -r build.templ ]; then
    if [ -r $PLDIR/build.templ ]; then
      cd $PLDIR
    else
      echo "ERROR: Cannot find SWI-Prolog sources"
      return 1
    fi
  fi
}


download_swipl()
{ if [ ! -d $PLDIR -a ! -r build.templ ]; then
    git clone git://www.swi-prolog.org/home/pl/git/$PLDIR.git
  fi

  ( enter_topdir
    git pull
    ./prepare --yes
  )
}

build_swipl_core()
{ export CIFLAGS="-I$install/include"
  export LDFLAGS="-L$install/lib"
  ( enter_topdir
    wineserver || true
    ./configure --host=$CROSS --prefix=$install \
        --disable-readline --disable-libdirversion \
        --enable-shared --disable-static
    $MAKE 2>&1 | tee make.out
    if [ "${PIPESTATUS[0]}" != 0 ]; then exit 1; fi
    make install 2>&1 | tee make-install.out
    if [ "${PIPESTATUS[0]}" != 0 ]; then exit 1; fi
  )
}

dist_dlls="libarchive-*.dll libgmp-*.dll libjpeg-*.dll "
dist_dlls+="*pthread*.dll zlib*.dll libdb-*.dll "
if [ "$BITS" = 64 ]; then
  dist_dlls+="libcrypto-1_1-x64.dll libssl-1_1-x64.dll "
else
  dist_dlls+="libcrypto-1_1.dll libssl-1_1.dll "
fi
dist_dlls+="libpcre-*.dll "
dist_dlls+="`(cd $install/bin && find . -name 'libgcc*.dll')` "
if [ -r "$install/bin/libdwarf.dll" ]; then
  dist_dlls+=" libdwarf.dll"
fi

# this assumes that the required GCC dlls are copied into $install

copy_dlls()
{ (cd $install/bin && cp $dist_dlls $install/swipl/bin)
  if [ -r $install/lib/libpthreadGC2.dll.a ]; then
    cp $install/lib/libpthreadGC2.dll.a $install/swipl/lib
  fi
}


# Usage: build_swipl_packages [pkg ...]

build_swipl_packages()
{ ( enter_topdir
    cd packages
    export CPPFLAGS=-I$install/include
    export PKGLDFLAGS=-I$install/lib
    [ -z "$1" ] || export PKG="$1"
    ./configure --host=$CROSS --prefix=$install
    $MAKE 2>&1 | tee -a ../make.out
    if [ "${PIPESTATUS[0]}" != 0 ]; then exit 1; fi
    make install 2>&1 | tee -a ../make-install.out
    if [ "${PIPESTATUS[0]}" != 0 ]; then exit 1; fi
    make html-install || exit 1
  )
}

check_build()
{ ( enter_topdir
    [ -f make.out ] && scripts/check_build_log.sh make.out
  )
}

build_swipl()
{ build_swipl_core
  copy_dlls
  build_swipl_packages
  check_build
}

clean_swipl()
{ ( enter_topdir
    make distclean
  )
}

uninstall_swipl()
{ rm -rf $install/swipl
}


build_installer()
{ ( enter_topdir
    cd ${BUILDARCH-src}
    make installer
  )
}

###########################
# Do the whole lot

build_all()
{ make_mingw_dirs
  build_prerequisites
  build_swipl
}

######################
# Create a new release

build_release()
{ clean_swipl
  uninstall_swipl
  download_swipl
  build_swipl
  build_installer
}
