#!/usr/bin/env bash
#
#  sage-spkg: install a Sage package
#
#  This script is typically invoked by giving the command
#      sage -i <options> <package name>...
#
#  Options can be:
#   -s: do not delete temporary build directory
#   -k: do not uninstall existing installation of this package before
#       installing; instead simply overwrite existing files.
#   -c: after installing, run the test suite for the spkg. This should
#       override the settings of SAGE_CHECK and SAGE_CHECK_PACKAGES.
#   -d: only download the package
#   -y: automatically reply "y" for all prompts regarding
#       experimental and old-style packages
#   -n: automatically reply "n" for all prompts regarding
#       experimental and old-style packages
#
#  A package may assume that the following environment
#  variables are defined:
#
#      SAGE_ROOT      -- root directory of sage install
#      SAGE_LOCAL     -- $SAGE_ROOT/local
#      SAGE_DISTFILES -- directory that stores upstream tarballs
#      SAGE_DESTDIR   -- temporary root the package will be installed to
#      PKG_BASE       -- the base name of the package itself (e.g. 'patch')
#      PKG_VER        -- the version number of the package
#      PKG_NAME       -- $PKG_BASE-$PKG_VER
#      LIBRARY_PATH, PYTHONPATH, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH
#      CC, CXX, CFLAGS, CXXFLAGS, LDFLAGS, MAKE
#
#  Your package script should try to build using the giving CC, CXX,
#  CFLAGS, MAKE, etc, via a file spkg-install in your script.
#
#  This script does the following:
#
#      1. Set environment variables (by calling sage-env)
#      2. Extract the metadata and upstream sources into a build directory
#      3. Run the script in the package called spkg-install
#      4. Return error 1 if anything goes wrong.
#
# AUTHORS:
#
# - Robert Bradshaw, R. Andrew Ohana (2013): #14480: extend functionality to
#   support the unified git repository.
#
# - Jeroen Demeyer (2012-02-27): #12602: refactor code to find packages,
#   download them and extract them.
#
# - Jeroen Demeyer (2012-02-27): #12479: big reorganization.
#
# - Volker Braun, Jeroen Demeyer (2012-01-18): #11073: remove the
#   spkg/base repository, move this file from local/bin/sage-spkg to
#   spkg/bin/sage-spkg.
#
# - William Stein, John Palmieri and others (Sage 4.8 and earlier).
#
#*****************************************************************************
#  Distributed under the terms of the GNU General Public License (GPL)
#  as published by the Free Software Foundation; either version 2 of
#  the License, or (at your option) any later version.
#                  http://www.gnu.org/licenses/
#*****************************************************************************

# Avoid surprises with character ranges [a-z] in regular expressions
export LC_ALL=C

usage()
{
cat <<EOF
Usage: sage -p <options> <package name>

If <package name> is a URL, download and install it. If it is a file
name, install it. Otherwise, search Sage's list of packages (see
'sage --package list') for a matching package, and if a match is
found, install it.

Options:
   -s: do not delete the temporary build directory
   -c: after installing, run the test suite for the package
   -d: only download the package
   -y: automatically reply "y" for all prompts regarding
       experimental and old-style packages; warning: there
       is no guarantee that these packages will build correctly;
       use at your own risk"
   -n: automatically reply "n" for all prompts regarding
       experimental and old-style packages
EOF
}

# error_msg(header, command)
# This is for printing an error message if something went wrong.
# The first argument is the header to print. If given, the second
# argument should be some proposed command to run in the subshell
# such as "make".
error_msg()
{
cat >&2 <<MESSAGE
************************************************************************
$1
************************************************************************
Please email sage-devel (http://groups.google.com/group/sage-devel)
explaining the problem and including the log file
  $SAGE_LOGS/$PKG_NAME.log
Describe your computer, operating system, etc.
MESSAGE

if [ -n "$2" ]; then
cat >&2 <<MESSAGE
If you want to try to fix the problem yourself, *don't* just cd to
`pwd` and type '$2' or whatever is appropriate.
Instead, the following commands setup all environment variables
correctly and load a subshell for you to debug the error:
  (cd '`pwd`' && '$SAGE_ROOT/sage' --sh)
When you are done debugging, you can type "exit" to leave the subshell.
MESSAGE
fi

cat >&2 <<MESSAGE
************************************************************************
MESSAGE
}

lookup_param()
{
    local param=$1
    local file=$2
    sed -n "s/^${param} *= *//p" $file
}

write_to_tty()
{
    # Try writing to terminal.  Suppress the possible error message
    if ! ( cat > /dev/tty ) 2> /dev/null ; then
        # Fall back to writing to stdout
        cat
        return 1
    fi
}

# Handle -n, -t, -q options for recursive make
# See Trac #12016.
if echo "$MAKE $MAKEFLAGS -$MAKEFLAGS" |grep '[ ]-[A-Za-z]*[qnt]' >/dev/null; then
    if echo "$MAKE $MAKEFLAGS -$MAKEFLAGS" |grep '[ ]-[A-Za-z]*q' >/dev/null; then
        # Pretend the target is *not* up-to-date
        exit 1
    else
        exit 0
    fi
fi

##################################################################
# Set environment variables
##################################################################

# The following sets environment variables for building packages.
# Since this is sourced, it returns a non-zero value on errors rather
# than exiting.  Using dot suggested by W. Cheung.
. sage-env


if [ $? -ne 0 ]; then
    error_msg "Error setting environment variables by sourcing sage-env"
    exit 1
fi

if [ -z "$SAGE_BUILD_DIR" ]; then
    export SAGE_BUILD_DIR="$SAGE_LOCAL/var/tmp/sage/build"
fi


# Remove '.' from PYTHONPATH, to avoid trouble with setuptools / easy_install
# (cf. #10192, #10176):
if [ -n "$PYTHONPATH" ]; then
    # We also collapse multiple slashs into a single one (first substitution),
    # remove leading './'s and trailing '/.'s (second and third), and
    # remove leading, trailing and redundant ':'s (last three substitutions):
    new_pp=`echo ":$PYTHONPATH:" \
        | sed \
        -e 's|//*|/|g' \
        -e 's|:\(\./\)\{1,\}|:|g' \
        -e 's|\(/\.\)\{1,\}:|:|g' \
        -e 's|\(:\.\)\{1,\}:|:|g' \
        -e 's|::*|:|g' -e 's|^::*||' -e 's|::*$||'`

    if [ "$PYTHONPATH" != "$new_pp" ]; then
        echo "Cleaning up PYTHONPATH:"
        echo "  Old: \"$PYTHONPATH\""
        echo "  New: \"$new_pp\""
        PYTHONPATH=$new_pp
        export PYTHONPATH # maybe redundant, but in any case safe
    fi
fi

##################################################################
# Handle special command-line options
##################################################################
if [ $# -eq 0 ]; then
   usage
   exit 0
fi

# Parse options
INFO=0
YES=0
KEEP_EXISTING=0
while true; do
    case "$1" in
        --info)
            INFO=1;;
        -y)
            YES=1;;
        -n)
            YES=-1;;
        -d)
            SAGE_INSTALL_FETCH_ONLY=1;;
        -s)
            export SAGE_KEEP_BUILT_SPKGS=yes;;
        -c|--check)
            SAGE_CHECK_PACKAGES=x  # nonempty, so not set to default later
            export SAGE_CHECK=yes;;
        -k|--keep-existing)
            KEEP_EXISTING=yes;;
        -*)
            echo >&2 "Error: unknown option '$1'"
            exit 2;;
        *) break;;
    esac
    shift
done


##################################################################
# Figure out the package filename, download it if needed.
##################################################################
# One should be able to install a package using
# sage -i <package-name> where <package-name> can be any of the
# following values:
#
# 1a. /path/to/<package>-x.y.z.spkg, i.e. the package is found somewhere
#     in your file system and you're giving an absolute path.
# 1b. relative/path/to/<package>-x.y.z.spkg, the same with a relative
#     path.
# 2a. <package>-x.y.z, i.e. the name of the package plus the package's
#     version numbers.
# 2b. <package>-x.y.z.spkg, i.e. the name of the package in addition to
#     the version numbers and the ".spkg" extension.
# 3.  <package>, i.e. the name of the package without a version number.
# 4.  <URL>/<package>-x.y.z.spkg, i.e. the full URL where the package
#     is hosted.  Any local packages matching <package> are ignored.
#
# In cases 2a, 2b and 3 we first look locally inside spkg/* for a
# matching package.  Otherwise, we try to download it.  In all cases,
# we reduce to case 1a.
#
# See #7544 and #12602.
#

PKG_SRC="$1"
# Does PKG_SRC contain a slash?
if echo "$PKG_SRC" | grep / >/dev/null; then
    PKG_HAS_PATH=yes
fi
# PKG_NAME is the last path component without .spkg
# This already reduces case 2b to case 2a.
PKG_NAME=`basename "$PKG_SRC" | sed 's/\.spkg$//'`
PKG_BASE=`echo "$PKG_NAME" | sed 's/-.*//'`

# USE_LOCAL_SCRIPTS is a flag that if non-empty will cause
# this script to try to install the package using local metadata
# i.e. use upstream tarballs (vs spkgs) and scripts located in build/pkgs/$PKG_BASE
# the value of this flag is set in the next codeblock
USE_LOCAL_SCRIPTS=

if [ -f "$PKG_SRC" ]; then
    # PKG_SRC is a file.  If it is given by a relative path, prepend `pwd`
    # (reduce case 1b to 1a)
    if ! echo "$PKG_SRC" | grep '^/' >/dev/null; then
        PKG_SRC="`pwd`/$PKG_SRC"
    fi
elif [ -z "$PKG_HAS_PATH" ]; then
    # If PKG_SRC is not an existing file and doesn't contain a slash,
    # we are in case 2a or 3.  If version in 2a matches the version in
    # build/pkgs or we are in case 3 use the local scripts, otherwise
    # we try to find a package in upstream
    PKG_VER="${PKG_NAME#${PKG_BASE}}"
    PKG_VER="${PKG_VER#-}"
    PKG_SCRIPTS="$SAGE_ROOT/build/pkgs/$PKG_BASE"
    LOCAL_PKG_VER=`cat $PKG_SCRIPTS/package-version.txt 2>/dev/null`
    if [ -n "$LOCAL_PKG_VER" ] && [ -z "$PKG_VER" -o "$PKG_VER" = "$LOCAL_PKG_VER" ]; then
        PKG_VER="$LOCAL_PKG_VER"
        if [ -z "$PKG_VER" ]; then
            PKG_NAME="${PKG_BASE}"
        else
            PKG_NAME="${PKG_BASE}-${PKG_VER}"
        fi
        USE_LOCAL_SCRIPTS=yes
        PKG_BASE_VER=`echo $PKG_VER | sed 's/\.p[0-9][0-9]*$//'`
        PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini" | sed "s/VERSION/$PKG_BASE_VER/"`
        echo "Found local metadata for $PKG_NAME"

        # Warning for experimental packages
        if [ x`cat "$PKG_SCRIPTS/type"` = x"experimental" -a $INFO = 0 ]; then
            if [ $YES != 1 ]; then
                # We use /dev/tty here because our output may be redirected
                # to a logfile, or line-buffered.
                write_to_tty <<EOF
=========================== WARNING ===========================
You are about to download and install the experimental package
$PKG_NAME.  This probably won't work at all for you! There
is no guarantee that it will build correctly, or behave as
expected. Use at your own risk!
===============================================================
EOF
                if [ $? -ne 0 ]; then
                    echo "Terminal not available for prompting.  Use 'sage -i -y $PKG_BASE'"
                    echo "to install experimental packages in non-interactive mode."
                    YES=-1
                fi
                if [ $YES != -1 ]; then
                    read -p "Are you sure you want to continue [Y/n]? " answer < /dev/tty > /dev/tty 2>&1
                else
                    answer=n
                fi
                case "$answer" in
                    n*|N*) exit 1;;
                esac
                # Confirm the user's input.  (This gives important
                # feedback to the user when output is redirected to a logfile.)
                echo > /dev/tty "OK, installing $PKG_NAME now..."
            fi
        fi

    else
        cd "$SAGE_DISTFILES"
        for spkg in `ls -1t ${PKG_NAME}.spkg ${PKG_NAME}-*.spkg 2>/dev/null`; do
            if [ -f "$spkg" ]; then
                # Found a good package
                echo "Found package $PKG_NAME in $SAGE_DISTFILES/$spkg"
                PKG_SRC="`pwd`/$spkg"
                PKG_NAME=`basename "$spkg" | sed 's/\.spkg$//'`
                break
            fi
        done
    fi
fi

if [ $INFO -ne 0 -a "$USE_LOCAL_SCRIPTS" = yes ]; then
    cat "$PKG_SCRIPTS/SPKG.txt"
    if [ -r "$PKG_SCRIPTS/type" ] ; then
        echo "== Type =="
        echo
        cat "$PKG_SCRIPTS/type"
    fi
    exit 0
fi

# If we haven't found the package yet, we must download it
if [ ! -f "$PKG_SRC" ]; then
    if [ -n "$PKG_NAME_UPSTREAM" ]; then
        # This is the new-style package way of downloading the tarball
        if ! sage-download-file "$PKG_NAME_UPSTREAM"; then
            error_msg "Error downloading $PKG_NAME_UPSTREAM"
            exit 1
        fi
        PKG_SRC="$SAGE_DISTFILES/$PKG_NAME_UPSTREAM"
    else
        # Handle all the legacy cruft. This branch can be deleted once
        # we get rid of old-style spkgs
        if [ $YES = -1 ]; then
            # User provided -n option, so don't even try to download the package"
            echo "Old-style packages disabled by use of '-n' option"
            exit 1
        fi
        if [ $INFO -eq 0 ]; then
            echo "Attempting to download package $PKG_NAME"
        else
            echo "Attempting to get on-line info for package $PKG_NAME"
        fi

        # Reduce everything to case 4: full URL.
        if [ -n "$PKG_HAS_PATH" ]; then
            PKG_URL="$PKG_SRC"
        else
            # Handle cases 2a and 3, where the package name is something
            # like "foo" or "foo-1.2.3".
            MIRROR=$(sage-download-file --print-fastest-mirror)/spkg
            if [ $? -ne 0 ]; then
                error_msg "Error downloading list of packages"
                exit 1
            fi
            for repo in optional experimental huge; do
                # Download the list of packages.
                echo ">>> Checking online list of $repo packages."
                # File inside DOT_SAGE should be writable
                repolist="${DOT_SAGE}/${repo}.list"
                sage-download-file --quiet "$MIRROR/$repo/list" $repolist
                if [ $? -ne 0 ]; then
                    rm -f $repolist
                    error_msg "Error downloading $MIRROR/$repo/list"
                    exit 1
                fi

                # The contrived sed commands print out either ${PKG_NAME} if
                # it appears as a complete line or some string starting with
                # ${PKG_NAME}- whichever occurs first.
                # Tested with GNU sed, BSD sed (on OS X) and Solaris sed.
                pkg=`sed -n -f <( echo "/^${PKG_NAME}\$/{p;q;}" && echo "/^${PKG_NAME}-/{p;q;}" ) $repolist`
                rm -f $repolist
                if [ -n "$pkg" ]; then
                    echo ">>> Found $pkg"
                    PKG_NAME=$pkg

                    # If INFO is set, try downloading only the .txt file
                    if [ $INFO -eq 1 ]; then
                        PKG_URL="$MIRROR/$repo/$pkg.txt"
                        sage-download-file --quiet "$PKG_URL" && exit 0
                        # If the download failed (for whatever reason),
                        # fall through and use the .spkg file.
                    else
                        if [ $YES != 1 ]; then
                            # Warn and ask the user if downloading an
                            # experimental package.
                            # Add a deprecation note for other packages,
                            # since old-style packages are deprecated.
                            if [ $repo = experimental ]; then
                                write_to_tty <<EOF
================================ WARNING =================================
You are about to download and install an unmaintained experimental
package. This probably won't work at all for you! There is no guarantee
that it will build correctly, or behave as expected. Use at your own risk!

This package will be removed in future versions of SageMath. If you care
about this package, you should make a proper new-style package instead.
For more information about making Sage packages, see
http://doc.sagemath.org/html/en/developer/packaging.html
==========================================================================
EOF
                                if [ $? -ne 0 ]; then
                                    echo "Terminal not available for prompting.  Use 'sage -i -y $PKG_BASE'"
                                    echo "to install experimental packages in non-interactive mode."
                                    YES=-1
                                fi
                                if [ $YES != -1 ]; then
                                    read -p "Are you sure you want to continue [Y/n]? " answer < /dev/tty > /dev/tty 2>&1
                                else
                                    answer=n
                                fi
                                case "$answer" in
                                    n*|N*) exit 1;;
                                esac
                            else
                                # Deprecated since Sage 6.9, Trac #19158
                                write_to_tty <<EOF
================================== NOTE ==================================
You are about to download and install an old-style package. While this
might still work fine, old-style packages are unmaintained and deprecated.

This package will be removed in future versions of SageMath. If you care
about this package, you should make a proper new-style package instead.
For more information about making Sage packages, see
http://doc.sagemath.org/html/en/developer/packaging.html
==========================================================================
EOF
                                if [ $? = 0 -a $YES != -1 ]; then
                                   read -t 30 -p "Are you sure (automatically continuing in 30 seconds) [Y/n]? " answer < /dev/tty > /dev/tty 2>&1
                                elif [ $YES = -1 ]; then
                                    answer=n
                                else
                                    answer=y
                                fi
                                case "$answer" in
                                    n*|N*) exit 1;;
                                esac
                            fi
                            # Confirm the user's input.  (This gives important
                            # feedback to the user when output is redirected to a logfile.)
                            echo > /dev/tty "OK, installing $PKG_NAME now..."
                        fi
                    fi
                    PKG_URL="$MIRROR/$repo/$pkg.spkg"
                    break
                fi
            done

            if [ -z "$PKG_URL" ]; then
                echo >&2 "Error: could not find a package matching $PKG_NAME"
                echo >&2 "       Try 'sage --package list' to see the available packages"
                echo >&2 "       $(sage-package apropos $PKG_NAME)"
                exit 1
            fi
        fi

        # Trac #5852: check write permissions
        mkdir -p "$SAGE_DISTFILES"
        if [ ! -w "$SAGE_DISTFILES" ]; then
            error_msg "Error: no write access to packages directory $SAGE_PACKAGES"
            exit 1
        fi
        cd "$SAGE_DISTFILES" || exit $?

        # Download to a temporary file (such that we don't end up with a
        # corrupted .spkg file).
        PKG_TMP="${PKG_URL##*/}.tmp"
        echo ">>> Trying to download $PKG_URL"
        sage-download-file "$PKG_URL" "$PKG_TMP"
        if [ $? -ne 0 ]; then
            # Delete failed download
            rm -f "$PKG_TMP"
            error_msg "Error downloading $PKG_URL"
            exit 1
        fi

        PKG_SRC="`pwd`/${PKG_URL##*/}"
        mv -f "$PKG_TMP" "$PKG_SRC"
    fi
fi

# Do a final check that PKG_SRC is a file with an absolute path
cd /
if [ ! -f "$PKG_SRC" ]; then
    echo >&2 "Error: spkg file '$PKG_SRC' not found."
    echo >&2 "This shouldn't happen, it is a bug in the sage-spkg script."
    exit 1
fi

# Go back to SAGE_ROOT where we have less chance of completely messing
# up the system if we do something wrong.
cd "$SAGE_ROOT" || exit $?

# If SAGE_SPKG_COPY_UPSTREAM is set, it should be the name of a directory
# to which all upstream files are copied. This is used in sage-sdist.
if [ -n "$SAGE_SPKG_COPY_UPSTREAM" ]; then
    mkdir -p "$SAGE_SPKG_COPY_UPSTREAM" && cp -p "$PKG_SRC" "$SAGE_SPKG_COPY_UPSTREAM"
    if [ $? -ne 0 ]; then
        error_msg "Error copying upstream tarball to directory '$SAGE_SPKG_COPY_UPSTREAM'"
        exit 1
    fi
fi
if [ -n "$SAGE_INSTALL_FETCH_ONLY" ]; then
    exit 0
fi

##################################################################
# Handle --info
##################################################################

if [ $INFO -ne 0 -a -z "$USE_LOCAL_SCRIPTS" ]; then
        cat "$PKG_SCRIPTS/SPKG.txt"
    sage-uncompress-spkg "$PKG_SRC" "$PKG_NAME/SPKG.txt"
    if [ $? -ne 0 ]; then
        error_msg "Error: no file SPKG.txt in $PKG_NAME"
        exit 1
    fi
    exit 0
fi

##################################################################
# Setup directories
##################################################################

for dir in "$SAGE_SPKG_INST" "$SAGE_SPKG_SCRIPTS" "$SAGE_BUILD_DIR"; do
    mkdir -p "$dir"
    if [ $? -ne 0 ]; then
        error_msg "Error creating directory $dir"
        exit 1
    fi
done

# Trac #5852: check write permissions
if [ ! -w "$SAGE_BUILD_DIR" ]; then
    error_msg "Error: no write access to build directory $SAGE_BUILD_DIR"
    exit 1
fi
if [ ! -d "$SAGE_LOCAL" ]; then
    # If you just unpack Sage and run "sage -p <pkg>" then local does not yet exist
    mkdir "$SAGE_LOCAL"
fi
if [ ! -w "$SAGE_LOCAL" ]; then
    error_msg "Error: no write access to installation directory $SAGE_LOCAL"
    exit 1
fi

echo "$PKG_NAME"
echo "===================================================="

# Make absolutely sure that we are in the build directory before doing
# a scary "rm -rf" below.
cd "$SAGE_BUILD_DIR" || exit $?


if [ "x$SAGE_KEEP_BUILT_SPKGS" != "xyes" ]; then
    rm -rf "$PKG_NAME"
else
    if [ -e "$PKG_NAME" ]; then
        echo "Moving old directory $PKG_NAME to $SAGE_BUILD_DIR/old..."
        mkdir -p old
        if [ $? -ne 0 ]; then
            error_msg "Error creating directory $SAGE_BUILD_DIR/old"
            exit 1
        fi
        rm -rf old/"$PKG_NAME"
        mv "$PKG_NAME" old/
    fi
fi

if [ -e "$PKG_NAME" ]; then
    error_msg "Error (re)moving $PKG_NAME"
    exit 1
fi

##################################################################
# Extract the package
##################################################################

if [ "$USE_LOCAL_SCRIPTS" = yes ]; then
    # New-style package
    echo "Setting up build directory for $PKG_NAME"
    cp -RLp "$PKG_SCRIPTS" "$PKG_NAME"
    cd "$PKG_NAME" || exit $?

    sage-uncompress-spkg -d src "$PKG_SRC"
    if [ $? -ne 0 ]; then
        error_msg "Error: failed to extract $PKG_SRC"
        exit 1
    fi
else
    # Old-style package (deprecated)
    echo "Extracting package $PKG_SRC"
    ls -l "$PKG_SRC"

    sage-uncompress-spkg "$PKG_SRC"
    if [ $? -ne 0 ]; then
        error_msg "Error: failed to extract $PKG_SRC"
        exit 1
    fi

    cd "$PKG_NAME"
    if [ $? -ne 0 ]; then
        error_msg "Error: after extracting, the directory '$PKG_NAME' does not exist"
        exit 1
    fi
fi

echo "Finished extraction"

cd src
if ! sage-apply-patches; then
    error_msg "Error applying patches"
    exit 1
fi
cd ..

##################################################################
# The package has been extracted, prepare for installation
##################################################################

# Rewrites the given bash pseudo-script with a boilerplate header that includes
# the shebang line and sourcing sage-env.  Make sure the name of the script is
# passed in as an absolute path.
write_script_wrapper() {
    local script="$1"
    local script_dir="$2"

    trap "echo >&2 Error: Unexpected error writing wrapper script for $script; exit \$_" ERR

    if head -1 "$script" | grep '^#!.*$' >/dev/null; then
        echo >&2 "Error: ${script##*/} should not contain a shebang line; it will be prepended automatically."
        exit 1
    fi

    local tmpscript="$(dirname "$script")/.tmp-${script##*/}"

    cat > "$tmpscript" <<__EOF__
#!/usr/bin/env bash

export SAGE_ROOT="$SAGE_ROOT"
export SAGE_SRC="$SAGE_SRC"
export SAGE_PKG_DIR="$script_dir"
export SAGE_SPKG_SCRIPTS="$SAGE_SPKG_SCRIPTS"

export PKG_NAME="$PKG_NAME"
export PKG_BASE="$PKG_BASE"
export PKG_VER="$PKG_VER"

for lib in "\$SAGE_ROOT/build/bin/sage-dist-helpers" "\$SAGE_SRC/bin/sage-env" ; do
    source "\$lib"
    if [ \$? -ne 0 ]; then
        echo >&2 "Error: failed to source \$lib"
        echo >&2 "Is \$SAGE_ROOT the correct SAGE_ROOT?"
        exit 1
    fi
done

sdh_guard
if [ \$? -ne 0 ]; then
    echo >&2 "Error: sdh_guard not found; Sage environment was not set up properly"
    exit 1
fi

cd "\$SAGE_PKG_DIR"
if [ \$? -ne 0 ]; then
    echo >&2 "Error: could not cd to the package build directory \$SAGE_PKG_DIR"
    exit 1
fi

__EOF__

    cat "$script" >> "$tmpscript"
    mv "$tmpscript" "$script"
    chmod +x "$script"

    trap - ERR
}


WRAPPED_SCRIPTS="build install check preinst postinst legacy-uninstall prerm postrm"
INSTALLED_SCRIPTS="prerm postrm"


for script in $WRAPPED_SCRIPTS; do
    # 'Installed' scripts are not run immediately out of the package build
    # directory, and may be run later, so set their root directory to
    # $SAGE_ROOT
    if echo "$INSTALLED_SCRIPTS" | grep -w "$script" > /dev/null; then
        script_dir="$SAGE_ROOT"
    else
        script_dir="$(pwd)"
    fi

    script="spkg-$script"

    if [ -f "$script" ]; then
        if [ "$USE_LOCAL_SCRIPTS" = "yes" ]; then
            if [ -x "$script" ]; then
                msg="$script should not be marked executable in the build/pkgs directory"
                if [ "$UNAME" = "CYGWIN" ]; then
                    # On Cygwin we can't necessarily rely on file permissions
                    # being sane, so just issue a warning; on other platforms
                    # this should be enforced as an error
                    echo >&2 "WARNING: $msg"
                else
                    error_msg "$msg"
                    exit 1
                fi
            fi

            write_script_wrapper "$(pwd)/$script" "$script_dir"
        else
            if [ ! -x "$script" ]; then
                echo >&2 "WARNING: $script is not executable, making it executable"
                chmod +x "$script"
            fi
        fi
    fi
done


# When there is no spkg-install, assume the "spkg" is a tarball not
# specifically made for Sage.  Since we want it to be as easy as
# possible to install such a package, we "guess" spkg-install.
if [ ! -f spkg-install ]; then
    echo '#!/usr/bin/env bash' > spkg-install
    if [ -x configure ]; then
        echo './configure --prefix="$SAGE_LOCAL" && make && $SAGE_SUDO make install' >> spkg-install
    elif [ -f setup.py ]; then
        echo 'python setup.py install' >> spkg-install
    else
        echo >&2 "Error: There is no spkg-install script, no setup.py, and no configure"
        echo >&2 "script, so I do not know how to install $PKG_SRC."
        exit 1
    fi
    chmod +x spkg-install
fi


# SAGE_CHECK_PACKAGES: if this contains "!pkg", skip tests for "pkg",
# so set SAGE_CHECK=no.  If this contains "pkg", run tests, so set
# SAGE_CHECK=yes.  Check this now and export SAGE_CHECK so that the
# package's spkg-install script can use this variable.
#
# Since Python's self-tests seem to fail on all platforms, we disable
# its test suite by default.
if [ -z "$SAGE_CHECK_PACKAGES" ]; then
    SAGE_CHECK_PACKAGES='!python2,!python3'
fi
# Allow spaces, commas, or colons as separator (the documentation suggests commas).
if echo ",$SAGE_CHECK_PACKAGES," | grep -i "[ ,:]\!$PKG_BASE[ ,:]" > /dev/null ; then
    export SAGE_CHECK=no
elif echo ",$SAGE_CHECK_PACKAGES," | grep -i "[ ,:]$PKG_BASE[ ,:]" > /dev/null ; then
    export SAGE_CHECK=yes
fi


echo "****************************************************"
echo "Host system:"
uname -a
echo "****************************************************"
echo "C compiler: $CC"
echo "C compiler version:"
$CC -v
echo "****************************************************"

##################################################################
# Poison the proxy variable to forbid downloads in spkg-install
##################################################################
export http_proxy=http://192.0.2.0:5187/
export https_proxy=$http_proxy
export ftp_proxy=$http_proxy
export rsync_proxy=$http_proxy

##################################################################
# We need to run sage-rebase.sh for each package installed, but it
# can be dangerous to do this while other packages are installing
# so we need to use a lock to manage when rebase is allowed to
# run.  Because of this, if multiple sage-spkg runs are waiting on
# the rebase lock, we can end up with multiple consecutive rebase
# calls that are redundant, but I don't see an obvious way around
# that.  This also unfortunately slows down parallel builds since
# all packages will eventually need to wait for this lock, but
# again there's no simple way around that.
##################################################################

if [ "$UNAME" = "CYGWIN" ]; then
    if [ ! -d "$SAGE_LOCAL/var/lock" ]; then
        mkdir -p "$SAGE_LOCAL/var/lock"
    fi
    exec 200>"$SAGE_LOCAL/var/lock/rebase.lock"
    sage-flock -s $lock_type 200
fi

##################################################################
# Actually install
##################################################################

# Set the $SAGE_DESTDIR variable to be passed to the spkg-install
# script (the script itself could set this, but better to standardize
# this in one place)
export SAGE_DESTDIR="${SAGE_BUILD_DIR}/${PKG_NAME}/inst"

# The actual prefix where the installation will be staged. This is the
# directory that you need to work in if you want to change the staged
# installation tree (before final installation to $SAGE_LOCAL) at the
# end of spkg-install.
export SAGE_DESTDIR_LOCAL="${SAGE_DESTDIR}${SAGE_LOCAL}"

# First uninstall the previous version of this package, if any
if [ "$KEEP_EXISTING" != "yes" ]; then
    sage-spkg-uninstall "$PKG_BASE"
fi

# Run the pre-install script, if any
if [ -f spkg-preinst ]; then
    echo "Running pre-install script for $PKG_NAME."
    time $SAGE_SUDO ./spkg-preinst
    if [ $? -ne 0 ]; then
        error_msg "Error running the preinst script for $PKG_NAME."
        exit 1
    fi
fi

if [ -f spkg-build ]; then
    # Package has both spkg-build and spkg-install; execute the latter with SAGE_SUDO
    time ./spkg-build
    if [ $? -ne 0 ]; then
        error_msg "Error building package $PKG_NAME" "make"
        exit 1
    fi
    time $SAGE_SUDO ./spkg-install
    if [ $? -ne 0 ]; then
        error_msg "Error installing package $PKG_NAME" "make"
        exit 1
    fi
else
    # Package only has spkg-install
    time ./spkg-install
    if [ $? -ne 0 ]; then
        error_msg "Error installing package $PKG_NAME" "make"
        exit 1
    fi
fi

# All spkgs should eventually support this, but fall back on old behavior in
# case DESTDIR=$SAGE_DESTDIR installation was not used
echo "Copying package files from temporary location $SAGE_DESTDIR to $SAGE_LOCAL"
if [ -d "$SAGE_DESTDIR" ]; then
    # Some `find` implementations will put superfluous slashes in the
    # output if we give them a directory name with a slash; so make sure
    # any trailing slash is removed; https://trac.sagemath.org/ticket/26013
    PREFIX="${SAGE_DESTDIR_LOCAL%/}"

    rm -f "$PREFIX"/lib/*.la
    if [ $? -ne 0 ]; then
        error_msg "Error deleting unnecessary libtool archive files"
        exit 1
    fi

    # Generate installed file manifest
    FILE_LIST="$(cd "$PREFIX" && find . -type f -o -type l | sed 's|^\./||' | sort)"

    # Copy files into $SAGE_LOCAL
    $SAGE_SUDO cp -Rp "$PREFIX/." "$SAGE_LOCAL"
    if [ $? -ne 0 ]; then
        error_msg "Error copying files for $PKG_NAME."
        exit 1
    fi

    # Remove the $SAGE_DESTDIR entirely once all files have been moved to their
    # final location.
    rm -rf "$SAGE_DESTDIR"
fi


# At this stage the path in $SAGE_DESTDIR no longer exists, so the variable
# should be unset
unset SAGE_DESTDIR
unset SAGE_DESTDIR_LOCAL


# Some spkg scripts, if they exist, should also be installed to
# $SAGE_SPKG_SCRIPTS; they are not included in the package's manifest, but are
# removed by sage-spkg-uninstall
INSTALLED_SCRIPTS_DEST="$SAGE_SPKG_SCRIPTS/$PKG_BASE"
for script in $INSTALLED_SCRIPTS; do
    script="spkg-$script"

    if [ -f "$script" ]; then
        mkdir -p "$INSTALLED_SCRIPTS_DEST"
        if [ $? -ne 0 ]; then
            error_msg "Error creating the spkg scripts directory $INSTALLED_SCRIPTS_DEST."
            exit 1
        fi

        cp -a "$script" "$INSTALLED_SCRIPTS_DEST"
        if [ $? -ne 0 ]; then
            error_msg "Error copying the $script script to $INSTALLED_SCRIPTS_DEST."
            exit 1
        fi
    fi
done


# Run the post-install script, if any
if [ -f spkg-postinst ]; then
    echo "Running post-install script for $PKG_NAME."
    time $SAGE_SUDO ./spkg-postinst
    if [ $? -ne 0 ]; then
        error_msg "Error running the postinst script for $PKG_NAME."
        exit 1
    fi
fi

if [ "$UNAME" = "CYGWIN" ]; then
    # Drop our sage-spkg's shared lock, and try to call sage-rebase.sh
    # under an exclusive lock
    sage-flock -u 200

    # Rebase after installing each package--in case any packages load this
    # package at build time we need to ensure during the build that no binaries
    # have conflicting address spaces
    echo "Waiting for rebase lock"
    sage-flock -x "$SAGE_LOCAL/var/lock/rebase.lock" \
        sage-rebase.sh "$SAGE_LOCAL" 2>/dev/null
fi


# Note: spkg-check tests are run after the package has been copied into
# SAGE_LOCAL.  It might make more sense to run the tests before, but the
# spkg-check scripts were written before use of DESTDIR installs, and so
# fail in many cases.  This might be good to change later.

if [ "$SAGE_CHECK" = "yes" ]; then
    if [ -f spkg-check ]; then
        echo "Running the test suite for $PKG_NAME..."
        time ./spkg-check
        if [ $? -ne 0 ]; then
            error_msg "Error testing package $PKG_NAME" "make check"
            exit 1
        fi
        TEST_SUITE_RESULT="passed"
    else
        echo "Package $PKG_NAME has no test suite."
        TEST_SUITE_RESULT="not available"
    fi
fi

# For each line in $FILE_LIST, enclose in double quotes:
NEW_LIST=""
for f in $FILE_LIST; do
         NEW_LIST+="\"$f\"
"
done
# Now remove the last line (it's blank), indent each line (skipping
# the first) and append a comma (skipping the last).
FILE_LIST="$(echo "$NEW_LIST" | sed '$d' | sed '2,$s/^/        /; $!s/$/,/')"

# Mark that the new package has been installed (and tested, if
# applicable).
PKG_NAME_INSTALLED="$SAGE_SPKG_INST/$PKG_NAME"
cat > "$PKG_NAME_INSTALLED" << __EOF__
{
    "package_name": "$PKG_BASE",
    "package_version": "$PKG_VER",
    "install_date": "$(date)",
    "system_uname": "$(uname -a)",
    "sage_version": "$(cat "${SAGE_ROOT}/VERSION.txt")",
    "test_result": "$TEST_SUITE_RESULT",
    "files": [
        $FILE_LIST
    ]
}
__EOF__


echo "Successfully installed $PKG_NAME"


##################################################################
# Delete the temporary build directory if required
##################################################################
if [ "x$SAGE_KEEP_BUILT_SPKGS" != "xyes" ]; then
    echo "Deleting temporary build directory"
    echo "$SAGE_BUILD_DIR/$PKG_NAME"
    # On Solaris, the current working directory cannot be deleted,
    # so we "cd" out of $SAGE_BUILD_DIR/$PKG_NAME. See #12637.
    cd "$SAGE_BUILD_DIR"
    rm -rf "$SAGE_BUILD_DIR/$PKG_NAME"
else
    echo "You can safely delete the temporary build directory"
    echo "$SAGE_BUILD_DIR/$PKG_NAME"
fi


# Touch this file to force a sage-location run next time Sage is
# started.  This is needed to make some of the installed libraries and
# scripts independent of the SAGE_ROOT path.
touch "$SAGE_LOCAL/lib/sage-force-relocate.txt"


echo "Finished installing $PKG_NAME.spkg"
