#!/usr/bin/env bash
#
# Copyright 2000-2008 Daniel F. Savarese
# Copyright 2009-2018 Savarese Software Research Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.savarese.com/software/ApacheLicense-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------
#
# This script bootstraps the automatic Makefile generation process.
# It runs the necessary autotools to generate Makefile.in files,
# libtool, configure, and other support files.  It then passes
# any arguments on to configure, running it from a build directory.
#
# ------------------------------------------------------------------------

PATH=$PATH:/bin:/usr/bin
export PATH

declare -r VERSION=1.1.12
declare -r BUILD="2021-01-04 17:29:30 CET"
declare -r COPYRIGHT='
Copyright 2000-2008 Daniel F. Savarese
Copyright 2009-2018 Savarese Software Research Corporation
'
declare BOOTSTRAP_CONF="bootstrap.conf"
declare -r PROGRAM=$0
declare -r PROGRAM_NAME=$(basename $PROGRAM)
declare TOP_SRCDIR="$(pwd)"
declare -r CONFIG_DIR="config"
declare -r CONFIG_GUESS="$CONFIG_DIR/config.guess"
declare -r CONFIG_FILES="compile config.guess config.sub depcomp install-sh ltmain.sh ltconfig missing mkinstalldirs test-driver"
declare -r TOPLEVEL_FILES="config.h.in aclocal.m4 stamp-h.in configure autom4te.cache"

declare -r ACLOCAL_FLAGS="-I $CONFIG_DIR"
declare -r AUTOHEADER_FLAGS="--force"
declare -r LIBTOOLIZE_FLAGS="--force --copy"
declare -r AUTOMAKE_FLAGS="--foreign --add-missing --copy"
declare GMAKE=$(type -p gmake || echo "")
declare -r MAKE=${GMAKE:-make}
declare -r GLIBTOOLIZE=$(type -p glibtoolize)

unset GMAKE

# Exporting MAKE is necessary to ensure that configure and any other
# scripts/tools use GNU make.

export MAKE

if [ -n "$GLIBTOOLIZE" ]; then
    function libtoolize() {
        "$GLIBTOOLIZE" "$@"
    }
fi

#
# Default build directory.  Is overridden by mkbuild_dir.
#
declare BUILD_DIR="${TOP_SRCDIR}/build"
declare BUILD_DIR_SET=0

#
# Configuration name.  Is set by set_config_args.
#
declare CONFIG_NAME=""

#
# Exit code, set command switch.
#
declare EXIT_CODE=0

function usage() {
echo "
usage: $0
          [-build-dir dir] [-config-file file] [-src-dir dir]
          [-help | -init [topdir] |
           -build [configure-options | config-name [make-args]]
           -clean [config-name] | -config [configure-options] |
           -configonly [configure-options] | -install [configure-options] |
           -mkdir config-name | -rebuild [configure-options] |
           -reconfig [configure-options] | -version]

OPTIONS
    -build-dir dir

       Sets its argument as the build directory where the
       configuration-specific build directories will be created.
       Overrides the default of \"build/\" in the top source
       directory.  This option must precede any command options such
       as -config or -build.

    -config-file file

       Specifies the path name of the configuration file to use.  If
       not specified, the default value is \"bootstrap.conf\".  If
       -src-dir is not specified, the parent directory of the config
       file is used as the location of the source tree.  This option
       must precede any command options such as -config or -build.

    -src-dir dir

       Sets the location of the source tree.  This allows you to run
       $PROGRAM_NAME without installing it in the top of the source
       tree with -init.  By default, the location of the source tree
       is the current working directory or the parent directory of the
       config file when -config-file is specified.  The value of the
       -src-dir option takes precedence.  This option must precede any
       command options such as -config or -build.

    -help

       Displays this help message.

    -build [configure-options | config-name [make-args]]

       Performs the same operations as -config, but additionally
       builds the source.  You can pass configure options after
       -build just as you would with -config.  Additionally, if
       you specify a configuration name (see configure-options below)
       any arguments after the configuration name are passed along
       to the make command for building the source.

    -clean [config-name}

       Removes all of the configure support files created by
       autotools.  Specifying a configuration name will remove
       the build directory for that configuration; otherwise the
       default build directory is removed.

    -config [configure-options]

       After prepping the source tree by running the autotools,
       runs configure with the specified configure options,
       readying the source tree for building.

    -configonly [configure-options]

       Runs configure with the specified configure options without
       readying the source tree for building.

    -install [configure-options]

       Performs the same operations as -build, but in addition
       installs the resulting build.

    -mkdir config-name

       Preps the source tree by running the autotools and then
       creates an architecture-specific build directory.  Specifying
       a configuration name will create a build directory with a
       suffix matching the configuration name.

    -rebuild [configure-options]

       Like -build, but does a -reconfig before building.
       You can pass configure options after -build just as
       you would with -config.

    -reconfig [configure-options]

       Performs the same operations as:
           $0 -clean
           $0 -config [configure-options]

    -version

       Print $PROGRAM_NAME version number.

SPECIAL ARGUMENTS
    configure-options  Argument may be options to pass to configure or the
                       name of a configuration in the bootstrap config file
                       that predefines the arguments to pass to configure.

DEPRECATED OPTIONS

    -init [topdir]

       Initializes the source tree for use with $PROGRAM_NAME.  This
       consists merely of making a symbolic link to the $PROGRAM_NAME
       script in the specified directory (or the current directory
       if none is specified).  $PROGRAM_NAME must be run from the link
       for all other operations, or it will fail to find the
       source tree unless -src-dir or -config-file are specified.
"
}


function version() {
echo "
$PROGRAM_NAME
  version: $VERSION
  build: $BUILD
$COPYRIGHT
"
}

function init() {
  local directory="$1"

  printf "\nWarning: -init is deprecated and should no longer be used.\n\n" 1>&2

  if [ -n "$directory" -a ! -d "$directory" ]; then
      echo "Cannot find $directory"
      return 1
  fi

  if [ -z "$directory" ]; then
      directory="."
  fi

  local program_dir=$(echo $(cd $(dirname $PROGRAM); pwd))
  local dest="$directory/$PROGRAM_NAME"
  local src="${program_dir}/${PROGRAM_NAME}"

  if [ -e "$dest" -o -h "$dest" ]; then
      echo "$dest already exists."
      return 1
  fi

  if ln -s "$src" "$dest"; then
      echo "Created $dest -> $src"
      return 0
  else
      echo "Failed to create $dest -> $src"
      return 1
  fi
}

#
# Can't assume mkdir accepts the -p flag, so we use this function instead.
#

function mkdirs() {
    local old_ifs
    local directory="$1"
    local root_dir="."

    if [ "${directory:0:1}" = "/" ]; then
        root_dir="/"
    fi

    old_ifs="$IFS"
    IFS=/
    set $directory

    directory="$root_dir"
    while [ $# -gt 0 ]; do
	directory="$directory/$1"
	if [ ! -d "$directory" ]; then
	    mkdir "$directory"
	fi
	shift 1
    done
    IFS="$old_ifs"
}


#
# As a side effect sets BUILD_DIR.
#

function set_build_dir() {
    local build_dir

    if [ $BUILD_DIR_SET -ne 0 ]; then
        return 0
    fi

    pushd "$TOP_SRCDIR" > /dev/null 2>&1
    if [ ! -x "$CONFIG_GUESS" ]; then
	popd  > /dev/null 2>&1
	return 1
    fi

    build_dir="$BUILD_DIR"/$("$CONFIG_GUESS")

    if [ -n "$CONFIG_NAME" ]; then
	build_dir="${build_dir}.${CONFIG_NAME}"
    fi

    popd > /dev/null 2>&1
    BUILD_DIR="$build_dir"
    BUILD_DIR_SET=1
}

function mkbuild_dir() {
    if set_build_dir; then
	pushd "$TOP_SRCDIR" > /dev/null 2>&1
	if [ ! -d "$BUILD_DIR" ]; then
	    mkdirs "$BUILD_DIR"
	fi
	popd  > /dev/null 2>&1
        return 0
    fi

    return 1
}

function bootstrap() {
    local exit_code=1

    pushd "$TOP_SRCDIR" > /dev/null 2>&1
    if [ ! -d "$CONFIG_DIR" ]; then
	mkdirs "$CONFIG_DIR"
    fi

    if [ "$(type -t pre_bootstrap)" = "function" ]; then
        pre_bootstrap
    fi

    set -x
    aclocal $ACLOCAL_FLAGS && autoheader $AUTOHEADER_FLAGS && libtoolize $LIBTOOLIZE_FLAGS && automake $AUTOMAKE_FLAGS && autoconf
    exit_code=$?
    set +x

    if [ "$(type -t post_bootstrap)" = "function" ]; then
        post_bootstrap
    fi

    popd > /dev/null 2>&1

    return $exit_code
}

function configure() {
    local exit_code=1
    local build_dir

    pushd "$TOP_SRCDIR" > /dev/null 2>&1

    if ! mkbuild_dir || [ ! -x configure ]; then
	popd  > /dev/null 2>&1
	return 1
    fi

    build_dir="$BUILD_DIR"

    set -x
    (cd "$build_dir"; "$TOP_SRCDIR"/configure "$@";)
    exit_code=$?
    set +x
    popd > /dev/null 2>&1

    return $exit_code
}

function build() {
    local exit_code=1
    local build_dir

    pushd "$TOP_SRCDIR" > /dev/null 2>&1

    if ! set_build_dir || [ ! -f "$BUILD_DIR/Makefile" ]; then
	popd  > /dev/null 2>&1
	return 1
    fi

    build_dir="$BUILD_DIR"

    set -x
    (cd "$build_dir"; $MAKE "$@")
    exit_code=$?
    set +x
    popd > /dev/null 2>&1

    return $exit_code
}

function clean() {
    pushd "$TOP_SRCDIR" > /dev/null 2>&1
    if set_build_dir ; then
	if [ -d "$BUILD_DIR" ]; then
	    set -x
	    rm -fr "$BUILD_DIR"
	    set +x
	fi
    fi
    set -x
    rm -fr $TOPLEVEL_FILES
    find . -name Makefile.in -print | xargs rm -f
    (cd "$CONFIG_DIR"; rm -f $CONFIG_FILES;)
    set +x
    popd > /dev/null 2>&1

    return 0
}


#
# This is a helper that obtains the package name and version
# from the AC_INIT call in configure.ac.  If successful,
# sets PACKAGE_NAME and PACKAGE_VERSION as a side effect.
#

function get_package_info() {
    local ac_init

    if [ -f configure.ac ]; then
	ac_init="$(fgrep AC_INIT configure.ac)"
	PACKAGE_NAME="${ac_init#*(}"
	PACKAGE_NAME="${PACKAGE_NAME%,*}"
	PACKAGE_NAME="${PACKAGE_NAME// /}"
	PACKAGE_VERSION="${ac_init#*,}"
	PACKAGE_VERSION="${PACKAGE_VERSION%)*}"
	PACKAGE_VERSION="${PACKAGE_VERSION// /}"
    fi
}

#
# Looks for bootstrap config file, sources it if it exists, and tries
# to match the stated configuration against a sourced variable name.
# If successful, sets CONFIG_FLAGS as a side effect.
#

function set_config_args() {
    local bootstrap_conf="$1"
    local config_name="$2"
    local config_args

    pushd "$TOP_SRCDIR" > /dev/null 2>&1

    get_package_info

    if [ -f "$bootstrap_conf" ]; then
	. "$bootstrap_conf"
    else
	popd > /dev/null 2>&1
	return 1
    fi

    config_args=conf_${config_name}

#   We really do mean ${!config_args-x} and NOT ${!config_args:-x}

    if [ "${!config_args-x}" = "x" ]; then
	if [ -z "$conf_default" ]; then
	    popd > /dev/null 2>&1
	    return 1
	else
	    config_args="$conf_default"
	fi
    else
	config_args=${!config_args}
	CONFIG_NAME="$config_name"
    fi

    CONFIG_ARGS="$config_args"

    popd > /dev/null 2>&1
}

function detect_config_name() {
    local config_name="$1"

    if ! set_config_args "$BOOTSTRAP_CONF" "$config_name"; then
	CONFIG_ARGS="$@"
    fi
}

function configureonly() {
    local exit_code=1
    local old_ifs="$IFS"
    IFS="
"
    if ! configure $CONFIG_ARGS; then
	echo "
  Error.  Cannot run configure.  Make sure config.guess exists so
  that the architecture-specific build directory can be created.
"
    else
        exit_code=0
    fi
    IFS="$old_ifs"

    return $exit_code
}

function configonly() {
    local exit_code=1

    pushd "$TOP_SRCDIR" > /dev/null 2>&1

    if set_build_dir && [ -d "$BUILD_DIR" ]; then
	configureonly
        exit_code=$?
    else
	echo "
  Error.  $BUILD_DIR does not exist."
    fi

    popd > /dev/null 2>&1

    return $exit_code
}

function config() {
    bootstrap && configureonly
}

stop=1
srcdir_set=0
builddir_set=0

until [[ $stop -eq 0 ]]; do
    command="$1"
    case "$command" in
        -build-dir)
            BUILD_DIR="$2"
            builddir_set=1
            shift 2
            ;;
        -config-file)
            BOOTSTRAP_CONF="$2"
            # Force an absolute path name in case a parent path is relative.
            _TOP_SRCDIR="$(echo $(cd $(dirname $BOOTSTRAP_CONF); pwd))"
            BOOTSTRAP_CONF="${_TOP_SRCDIR}/$(basename $BOOTSTRAP_CONF)"
            shift 2
            if [[ $srcdir_set -eq 0 ]]; then
                TOP_SRCDIR="${_TOP_SRCDIR}"
            fi
            unset _TOP_SRCDIR
            ;;
        -src-dir)
            TOP_SRCDIR="$2"
            # Force an absolute path name in case a parent path is relative.
            TOP_SRCDIR="$(echo $(cd $TOP_SRCDIR; pwd))"
            srcdir_set=1
            shift 2
            ;;
        *)
            stop=0
            ;;
    esac
done

if [[ $builddir_set -eq 0 ]]; then
    BUILD_DIR="${TOP_SRCDIR}/build"
fi

unset stop
unset srcdir_set
unset builddir_set

command="$1"
num_arguments="$#"

shift
detect_config_name "$@"

if [ -n "$CONFIG_NAME" ]; then
    shift
    build_args="$@"
else
    build_args=""
fi

case "$command" in
    -help)
	usage
        EXIT_CODE=1
	;;
    -init)
	init "$@"
        EXIT_CODE=$?
	;;
    -build | -install)
        if [ "$command" = "-install" ]; then
	    build_args="install $build_args"
	fi
        config && build $build_args
        EXIT_CODE=$?
        ;;
    -clean)
	clean
	;;
    -config | -configure)
	config
        EXIT_CODE=$?
	;;
    -configonly | -configureonly)
	configonly
        EXIT_CODE=$?
	;;
    -rebuild)
        clean && config && build
        EXIT_CODE=$?
        ;;
    -reconfig)
	clean && config
        EXIT_CODE=$?
	;;
    -mkdir)
	bootstrap && mkbuild_dir
        EXIT_CODE=$?
	;;
    -version)
	version
	;;
    *)
	if [ "$num_arguments" -eq 0 ]; then
	    bootstrap
            EXIT_CODE=$?
	else
	    usage 1>&2
            EXIT_CODE=1
	fi
	;;
esac

exit $EXIT_CODE
