#!/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.
#
# The current releases are  configured  and   build  using  CMake.  As a
# result, this README only applies for   building the PREREQUISITES. See
# CMAKE.md and/or scripts/make-distribution for configuring and building
# SWI-Prolog.
#
# USE DOCKER INSTEAD
#
# These instructions are only required if  you   want  to  build in your
# local (Linux) environment. We have created   a  Docker image for doing
# the build which is a lot easier to manage. See:
#
#   https://github.com/SWI-Prolog/docker-swipl-build-mingw.git
#
# USAGE
#
# You have been warned! Make sure  to  have   a  lot  of time and coffee
# before proceeding.
#
# The remainder of the  file  is   an  executable  specification  of the
# installation process. The setup is designed to create ~/mingw32 and/or
# ~/mingw64 for building and installing   the prerequisites. We describe
# the setup for Win64. Simply replace  64   with  32 to build for 32-bit
# Windows. First, install the requires tools from the Linux packages:
#
#    % sudo apt-get install wine wine-binfmt mingw-w64
#    % sudo apt-get install cmake ninja-build nsis
#
# Next, create and prepare the prerequisite location (the script changes
# your prompt to `W64`
#
#    % cd ~
#    % mkdir -p mingw64/src
#    % cd mingw64/src
#    % source <path/to/swipl-devel/README.mingw win64
#    W64 download_prerequisites
#
# Unfortunately some of the download locations  tend to be unreliable or
# require some registration or acceptance  of   license.  So,  check for
# empty .tar.gz files and try  to  get   the  archive  by  hand from the
# internet. Than re-run ``download_<package>`` to extract the archive.
#
# Once all is there, get the MinGW   runtime  libs in `bin`. The details
# may depend on exact versions of MinGW, so please check.
#
#    % mkdir -p ~/mingw64/bin
#    % cd ~/mingw64/bin
#    % cp /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll .
#    % cp /usr/lib/gcc/x86_64-w64-mingw32/9.2-win32/libgcc_s_seh-1.dll .
#
# Now, build the prerequisites:
#
#    W64 build_prerequisites
#
# If this worked successfully, build SWI-Prolog.   Below  is the default
# command, see CMAKE.md for various options:
#
#    % cd /path/to/swipl-devel
#    % mkdir build.win64
#    % cd build.win64
#    % cmake -DCMAKE_BUILD_TYPE=Release \
#         -DCMAKE_TOOLCHAIN_FILE=../cmake/cross/linux_win64.cmake \
#         -DJAVA_COMPATIBILITY=ON \
#         -G Ninja ..
#    % ninja
#
# If this succeeds you should be   able  to run `./src/swipl-win.exe` or
# `./src/swipl.exe`
#
# To build an installer for installation on other systems, run
#
#    % cpack
#
#
# 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)

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.2
PTHREADS_VERSION=2-9-1-release
LIBDWARF_VERSION=20120410
SSL_VERSION=1.1.1d
JPEG_VERSION=9b
ZLIB_VERSION=1.2.12
ARCHIVE_VERSION=3.4.0
UUID_VERSION=1.6.2
BDB_VERSION=6.1.26
PCRE_VERSION=8.45  # TODO: update for PCRE2
FFI_VERSION=3.3
YAML_VERSION=0.2.2
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 --with-zlib \
    --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


# TODO: update for PCRE2
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
  )
}

################################
# Download and install libffi

download_libffi()
{ FFI_FILE=libffi-$FFI_VERSION.tar.gz
  [ -f $FFI_FILE ] || \
  curl ftp://sourceware.org/pub/libffi/$FFI_FILE > $FFI_FILE
  tar zxvf $FFI_FILE
}

build_libffi()
{ ( cd libffi-$FFI_VERSION
    ./configure --host=$CROSS --prefix=$install
    make
    make install
    # Bit strange location for the headers
    cp $install/lib/libffi-$FFI_VERSION/include/*.h $install/include
  )
}


################################
# Download and install libyaml

download_libyaml()
{ #tested 01f3a8786127748b5bbd4614880c4484570bbd44
  git clone https://github.com/yaml/libyaml
}

build_libyaml()
{ ( cd libyaml
    ./bootstrap
    ./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 )
  ( cd ffi-$FFI_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
  download_libffi
  download_libyaml
}

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
  build_libffi
  build_libyaml
}
