#! /usr/bin/env bash
#
# This script builds Bro, then creates a tar file of the Bro installation which
# will be used by each broctl test case to have its own fresh install of Bro.
# This script must be run before attempting to run any of the broctl tests.
#
# Usage:  build-bro [--clean]
#
# If the "--clean" parameter is specified, then this script just removes
# the broctl test install directory and tar file.
#
# A user can modify the behavior of this script by setting any of these
# env. variables:
#   BROCTL_TEST_USEBUILD  - If this is set (value doesn't matter), then Bro
#                           will be built in the default Bro build directory
#                           instead of using a new directory.  This is intended
#                           to be used when Bro was previously built, to
#                           shorten the time it takes to do the rebuild.
#   BROCTL_TEST_BUILDARGS - Specify additional options that will be passed
#                           to Bro's "configure" script.


# This function builds Bro using a build directory specifically for broctl
# tests (if BROCTL_TEST_USEBUILD is defined, then the default Bro build
# directory is used instead).
build_bro() {
    cd "${BROSRCDIR}"

    if [ "${TRAVIS}" = "true" ]; then
        # This is needed for some Python versions on Travis CI in order to
        # build pysubnettree (without this, it could fail to find libpython).
        export LDFLAGS=-L`python-config --exec-prefix`/lib
    fi

    if [ -n "${BROCTL_TEST_USEBUILD}" ]; then
        # Reuse a previous build in the default Bro build directory.
        BUILDPREFIX=build
        # Rebuild using a new prefix (use BROCTL_TEST_BUILDARGS to specify
        # any other flags that were used for the previous build).
        ./configure --prefix="${INSTALLPREFIX}" ${BROCTL_TEST_BUILDARGS}
    else
        # Use a build directory specifically for broctl tests.
        BUILDPREFIX=${BROCTLBUILDDIR}/bro-build
        # Additional configure options are used here to reduce the build size.
        ./configure --builddir=${BUILDPREFIX} --prefix="${INSTALLPREFIX}" --build-type=Release --disable-auxtools ${BROCTL_TEST_BUILDARGS}
    fi

    test $? -ne 0 && return 1

    cd ${BUILDPREFIX} && make -j 2 install
}

# Replace the Bro install prefix directory path with the text "@PREFIX@".  This
# is needed so that each BroControl test script can use its own install prefix.
replaceprefix() {
    # First, fix the BROSCRIPTDIR path (this step is needed to avoid any
    # unintended difference between BROSCRIPTDIR and INSTALLPREFIX because on
    # FreeBSD /home is actually a symlink to /usr/home).
    oldpath=`grep '^BROSCRIPTDIR' lib/broctl/BroControl/version.py | awk -F \" '{ print $2 }'`
    newpath=`canonicalpath "$oldpath"`
    if [ "$newpath" != "$oldpath" ]; then
        for i in lib/broctl/BroControl/version.py ; do
            sed "s#$oldpath#$newpath#" $i > $i.new && cp $i.new $i && rm $i.new
        done
    fi

    for i in etc/broctl.cfg bin/broctl lib/broctl/BroControl/version.py ; do
        sed "s#${INSTALLPREFIX}#@PREFIX@#" $i > $i.new && cp $i.new $i && rm $i.new
        if [ $? -ne 0 ]; then
            return 1
        fi
    done
}

# Normalize the specified pathname by resolving any symlinks in the path.
canonicalpath() {
    newpath=`python -c "from __future__ import print_function; import os,sys; print(os.path.realpath(sys.argv[1]))" "$1"`
    test $? -ne 0 && exit 1
    echo $newpath
}

# Remove the tar file and all test-specific Bro installation directories.
clean() {
    definevars

    # Verify the path is a directory (and exists) before attempting to
    # remove.
    test -d "${BROCTL_TEST}" && rm -rf "${BROCTL_TEST}"

    # If the directory still exists, then something is wrong.
    if [ -d "${BROCTL_TEST}" ]; then
        exit 1
    fi

    exit 0
}


definevars() {
    # Path to the top-level BroControl source code directory.
    BROCTLSRCDIR=`dirname "$0"`/../..

    # Path to the Bro source code directory.
    BROSRCDIR=${BROCTLSRCDIR}/../..

    # Path to the BroControl "build" directory.
    BROCTLBUILDDIR=`canonicalpath "${BROCTLSRCDIR}/build"`

    # Absolute path of parent directory where Bro will be installed.
    BROCTL_TEST=${BROCTLBUILDDIR}/testing
    test -z "${BROCTL_TEST}" && exit 1
}


# Create a tar file of the Bro installation.  The tar file will be used by
# each broctl test script to create a test-specific Bro installation.
create_tar() {
    definevars

    # Exit if any command fails.
    set -e

    # Verify that the entire Bro git repo was cloned (not just the broctl repo).
    if [ ! -e "${BROSRCDIR}/configure" ]; then
        echo "Error: in order to run the BroControl tests, you must clone the bro repo (not just the broctl repo)" 1>&2
        exit 1
    fi

    # Remove the test directory if it exists.
    if [ -e "${BROCTL_TEST}" ]; then
        rm -rf "${BROCTL_TEST}"
    fi

    mkdir -p ${BROCTL_TEST}

    # The tar file that all broctl test cases will use.
    TARFILE=${BROCTL_TEST}/bro-test-install.tar

    LOG=${BROCTLBUILDDIR}/buildbro.log
    rm -f "$LOG"

    # Bro will be installed in this temporary directory.
    INSTALLPREFIX=${BROCTL_TEST}/bro-install

    # Don't exit if a command fails.
    set +e

    # Build and install Bro in the temporary directory.
    if [ "${TRAVIS}" = "true" ]; then
        # Show build output in Travis CI to prevent timeout of the build job.
        build_bro
        if [ $? -ne 0 ]; then
            echo "Error: Bro build failed" 1>&2
            exit 1
        fi
    else
        echo "Building Bro (log in $LOG) ..."
        build_bro >>$LOG 2>&1
        if [ $? -ne 0 ]; then
            echo "Error: Bro build failed:" 1>&2
            tail -n 20 $LOG 1>&2
            exit 1
        fi
    fi

    # Create a tar file of the installation so each test case can start with a
    # clean installation without needing to rebuild Bro.
    (cd "${INSTALLPREFIX}" && replaceprefix && tar cf "${TARFILE}" * )
    if [ $? -ne 0 ]; then
        rm -rf "${INSTALLPREFIX}"
        exit 1
    fi
}

if [ "$1" = "--clean" ]; then
    clean
else
    create_tar
fi
