#!/usr/bin/env bash

###########################################
## Singular
###########################################

SRC=`pwd`/src/latest
SHARED=`pwd`/src/shared
PATCHES=`pwd`/patches

if [ "$SAGE_LOCAL" = "" ]; then
    echo >&2 "SAGE_LOCAL undefined ... exiting";
    echo >&2 "Maybe run 'sage -sh'?"
    exit 1
fi

# disable the dynamic kernel, except on Linux
if [ "$UNAME" != "Linux" ]; then
    SINGULAR_CONFIGURE="--without-dynamic-kernel $SINGULAR_CONFIGURE"
fi

if [ "$UNAME" = "Darwin" ]; then
    # Singular needs $LD set to "libtool", not "ld" on Darwin.
    # If we unset it, Singular will figure it out correctly.
    unset LD
fi

# On SPARC, we need 8-byte alignment for 64-bit integers
if uname -p | grep sparc >/dev/null; then
    SINGULAR_CONFIGURE="--with-align=8 $SINGULAR_CONFIGURE"
fi


if [ "x$SAGE_DEBUG" = "xyes" ]; then
    WITH_DEBUG="--with-debug"
    ENABLE_DEBUGOUTPUT="--enable-debugoutput"

    CFLAGS="-O0 -g $CFLAGS"
    CXXFLAGS="-O0 -g $CXXFLAGS"
else
    WITH_DEBUG="--without-debug"
    ENABLE_DEBUGOUTPUT="--disable-debugoutput"

    # By default, parts of Singular are compiled with -O2 and parts
    # with -O3.  If we do set any CFLAGS, this always overrides the
    # default CFLAGS set by upstream.  In order to be compatible, we
    # set the optimization to -O2.
    # Increasing the optimization level to -O3 has caused various
    # problems in the past either with specific compilers or on specific
    # platforms.
    OPTIMIZATION_FLAGS="-O2"

    CFLAGS="$OPTIMIZATION_FLAGS -g $CFLAGS"
    CXXFLAGS="$OPTIMIZATION_FLAGS -g $CXXFLAGS"
fi

if [ "x$SAGE64" = xyes ]; then
    echo "Building a 64-bit version of Singular"
    CFLAGS="$CFLAGS -m64 "
    CXXFLAGS="$CXXFLAGS -m64"
    CPPFLAGS="$CPPFLAGS -m64"
    LDFLAGS="$LDFLAGS -m64 "; export LDFLAGS
    if [ "x`uname`" = xSunOS ] ; then
        export CC="$CC -m64"
        export CXX="$CXX -m64"
    fi
fi

export CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS"

# we are building everything fPIC, this might impose a slight
# performance hit, need to evaluate:
export CXXFLAGS="$CXXFLAGS -fPIC"
export CFLAGS="$CFLAGS -fPIC"

# Singular does not respect LDFLAGS; Ugly hack:
# Used on Linux and Darwin (see singular-3.1.7-use_cxx_for_linking.patch)
export CXX="$CXX $LDFLAGS"

# The Sun assembler has problems with -pipe, so disable it.
# This only affects compile time, not the compiled programs/libraries.
if [ "$UNAME" = "SunOS" ]; then
    MAKE="$MAKE PIPE="
fi


# There is a race condition in the Singular Makefiles, building in
# parallel sometimes fails (Trac #17774)
export MAKE="$MAKE -j1"

# Workaround for GCC6: https://trac.sagemath.org/ticket/20926
export CXXFLAGS="$CXXFLAGS -fno-delete-null-pointer-checks -fno-strict-overflow"

choose_patches()
{
    cd "$PATCHES" || return $?

    if [ "x$SAGE_DEBUG" = "xyes" ]; then
        mv conditional/sage_debug.patch . || return $?
        # Remove old omalloc files, except for configure
        mv "$SRC/omalloc/configure" configure_omalloc
        rm -r "$SRC/omalloc"/*
        mv configure_omalloc "$SRC/omalloc/configure"
        mv conditional/singular_xalloc.patch . || return $?
        mv conditional/cygwin64_debug.patch ./cygwin64.patch || return $?
    fi
}

apply_patches()
{
    # cd down to the root of src/
    cd ..

    # Apply all patches
    for patch in ../patches/*.patch; do
        [ -r "$patch" ] || continue  # Skip non-existing or non-readable patches
        echo "Applying $patch"
        patch -p1 <"$patch"
        if [ $? -ne 0 ]; then
            echo >&2 "Error applying '$patch'"
            return 1
        fi
    done
}

remove_old_version()
{
    rm -f "$SAGE_LOCAL"/bin/Singular*
    rm -f "$SAGE_LOCAL/include/factory.h"
    rm -f "$SAGE_LOCAL/include/factoryconf.h"
    rm -rf "$SAGE_LOCAL/include/factory"
    rm -rf "$SAGE_LOCAL/include/singular"
    rm -rf "$SAGE_LOCAL"/include/omalloc*
    rm -f "$SAGE_LOCAL"/lib/*omalloc*
}

config()
{
    # configure notes:
    # 1) We really need to add --exec-prefix and --bindir as Singular
    #    uses some wierd defaults.
    # 2) configure calls other configure scripts (for example
    #    omalloc/configure).  Not all of these configure scripts
    #    support all options given here, leading to warnings which
    #    may be ignored.
    # 3) --without-debug doesn't work:
    #    http://www.singular.uni-kl.de:8002/trac/ticket/438
    #    Replace --with-debug by $WITH_DEBUG if this is fixed.
    ./configure --prefix="$SAGE_LOCAL" \
                --exec-prefix="$SAGE_LOCAL" \
                --bindir="$SAGE_LOCAL"/bin \
                --libdir="$SAGE_LOCAL"/lib \
                --with-apint=gmp \
                --with-malloc=system \
                --with-NTL \
                --with-flint="$SAGE_LOCAL" \
                --without-MP \
                --without-lex \
                --without-Boost \
                --enable-Singular \
                --enable-factory \
                --enable-libfac \
                --enable-IntegerProgramming \
                --disable-doc \
                $SINGULAR_CONFIGURE \
                --with-debug

    if [ $? -ne 0 ]; then
        echo >&2 "Unable to configure Singular."
        return 1
    fi
}


build_singular()
{
    # Singular doesn't have separate build and install steps. So we
    # directly need to install. The "-nolns" means that we will really
    # copy files, not symlink them (for some crazy reason, Singular's
    # default is the create symlinks when installing).
    $MAKE install-nolns
    if [ $? -ne 0 ]; then
        echo >&2 "Unable to build and install Singular"
        return 1
    fi

}


create_singular_script()
{
    cd "$SAGE_LOCAL/bin" || return $?

    # The Singular build annoyingly puts an absolute symlink to the
    # Singular executable in $SAGE_LOCAL/bin/.  So we delete it and
    # replace it by a relative symlink.
    rm -f Singular singular

    singular_executable=`ls Singular-* | tail -1`
    if [ -z "$singular_executable" ]; then
        echo >&2 "The Singular executable was not installed in $SAGE_LOCAL/bin/ as it should have been."
        return 1
    fi

    ln -s "$singular_executable" Singular || return $?

    # Lower case version is convenient (this can fail on
    # case-insensitive systems, we ignore the error).
    ln -s "$singular_executable" singular 2>/dev/null

    return 0
}

build_libsingular()
{
    # we really need DLIBSINGULAR, so we have to rebuild
    OLD_CFLAGS=$CFLAGS
    export CFLAGS="$CFLAGS -DLIBSINGULAR"
    OLD_CXXFLAGS=$CXXFLAGS
    export CXXFLAGS="$CXXFLAGS -DLIBSINGULAR"

    if [ "$UNAME" = "Darwin" ]; then
        # on darwin we need to adjust the install name of the library
	export LIBSINGULAR_LDFLAGS="-install_name ${SAGE_LOCAL}/lib/libsingular.dylib"
    fi

    config || return $?

    $MAKE clean
    $MAKE install-libsingular

    if [ $? -ne 0 ]; then
        echo >&2 "Unable to build and install libsingular."
        return 1
    fi

    # singular does not install this header
    cp Singular/sing_dbm.h $SAGE_LOCAL/include/singular/

    export CFLAGS="$OLD_CFLAGS"
    export CXXFLAGS="$OLD_CXXFLAGS"
}

build_factory()
{
    cd factory || return $?

    $MAKE distclean

    ./configure --prefix="$SAGE_LOCAL" \
                --includedir="$SAGE_LOCAL/include/factory" \
                --exec-prefix="$SAGE_LOCAL" \
                --bindir="$SAGE_LOCAL"/bin \
                --libdir="$SAGE_LOCAL"/lib \
                --enable-gmp \
                --with-gmp="$SAGE_LOCAL" \
                --enable-NTL \
                --with-NTL="$SAGE_LOCAL" \
                --with-flint="$SAGE_LOCAL" \
                --with-gmp="$SAGE_LOCAL" \
                $ENABLE_DEBUGOUTPUT

    if [ $? -ne 0 ]; then
        echo >&2 "Error configuring the standalone factory."
        return 1
    fi

    $MAKE all
    if [ $? -ne 0 ]; then
        echo >&2 "Error building the standalone factory."
        return 1
    fi

    $MAKE install
    if [ $? -ne 0 ]; then
        echo >&2 "Error installing the standalone factory."
        return 1
    fi

    $CP -p factoryconf.h "$SAGE_LOCAL"/include/
    if [ $? -ne 0 ]; then
        echo >&2 "Error copying factory/factoryconf.h to include directory."
        return 1
    fi
}

build_libfac()
{
    cd libfac || return $?

    $MAKE distclean

    ./configure --prefix="$SAGE_LOCAL" \
                --exec-prefix="$SAGE_LOCAL" \
                --bindir="$SAGE_LOCAL"/bin \
                --libdir="$SAGE_LOCAL"/lib \
                $WITH_DEBUG

    if [ $? -ne 0 ]; then
        echo >&2 "Error configuring libfac."
        return 1
    fi

    $MAKE all
    if [ $? -ne 0 ]; then
        echo >&2 "Error building libfac."
        return 1
    fi

    $MAKE install
    if [ $? -ne 0 ]; then
        echo >&2 "Error installing libfac."
        return 1
    fi
}

install_docs()
{
    cd "$SHARED" || return $?
    cp -p singular.hlp singular.idx "$SAGE_LOCAL/share/singular/"
}


# Actually run all the functions defined above
for i in choose_patches apply_patches remove_old_version config \
  build_singular build_libsingular build_factory build_libfac \
  create_singular_script install_docs ; do
    echo "### Singular spkg-install: $i ###"
    cd "$SRC" && $i
    if [ $? -ne 0 ]; then
        echo >&2 "Error building Singular (error in $i)."
        exit 1
    fi
done
