#!/bin/bash
#
# Copyright 2000-2008 Daniel F. Savarese
#
# 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.5
declare -r BUILD="2013-07-22 20:59:39 JST"
declare -r COPYRIGHT='Copyright 2000-2008 Daniel F. Savarese'
declare -r BOOTSTRAP_CONF="bootstrap.conf"
declare -r PROGRAM=$0
declare -r PROGRAM_NAME=$(basename $PROGRAM)
declare -r PROGRAM_DIR=$(echo $(cd $(dirname $PROGRAM); pwd))
declare -r CONFIG_DIR="config"
declare -r CONFIG_GUESS="$CONFIG_DIR/config.guess"
declare -r CONFIG_FILES="config.guess config.sub depcomp install-sh ltmain.sh ltconfig missing mkinstalldirs"
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"

#
# Default build directory.  Is overridden by mkbuild_dir.
#
declare BUILD_DIR="build"

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

function usage() {
echo "
usage: $0 -help | -init [topdir] | -build [configure-options] | -clean |
          -config [configure-options] | -rebuild [configure-options] |
          -reconfig [configure-options]

OPTIONS
    -help     Displays this help message.

    -init     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.

    -build    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    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   After prepping the source tree by running the autotools,
              runs configure with the specified configure options,
              readying the source tree for building.

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

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

    -mkdir    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.

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

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

    -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.
"
}


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

$COPYRIGHT
"
}

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

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

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

  local dest="$directory/$PROGRAM_NAME"
  local src="${PROGRAM_DIR}/${PROGRAM_NAME}"

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

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

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

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

    old_ifs="$IFS"
    IFS=/
    set $directory
    directory="."
    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;

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

    build_dir=build/$("$CONFIG_GUESS")

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

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

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

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

    pushd "$PROGRAM_DIR" > /dev/null 2>&1
    if [ ! -d "$CONFIG_DIR" ]; then
	mkdirs "$CONFIG_DIR"
    fi
    set -x
    aclocal $ACLOCAL_FLAGS
    autoheader $AUTOHEADER_FLAGS
    libtoolize $LIBTOOLIZE_FLAGS
    automake $AUTOMAKE_FLAGS
    autoconf
    set +x
    popd > /dev/null 2>&1

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

function configure() {
    local build_dir;

    pushd "$PROGRAM_DIR" > /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"; ../../configure $@;)
    set +x
    popd > /dev/null 2>&1
}

function build() {
    local make_args="$@"
    local build_dir;

    pushd "$PROGRAM_DIR" > /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 $make_args;)
    set +x
    popd > /dev/null 2>&1
}

function clean() {
    pushd "$PROGRAM_DIR" > /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
}


#
# 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 "$PROGRAM_DIR" > /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 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.
"
    fi
    IFS="$old_ifs"
}

function configonly() {
    pushd "$PROGRAM_DIR" > /dev/null 2>&1

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

    popd > /dev/null 2>&1
}

function config() {
    bootstrap
    configureonly
}

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 1
	;;
    -init)
	init $@
	;;
    -build | -install)
        if [ "$command" = "-install" ]; then
	    build_args="install $build_args"
	fi
        config
        build $build_args
        ;;
    -clean)
	clean
	;;
    -config | -configure)
	config
	;;
    -configonly | -configureonly)
	configonly
	;;
    -rebuild)
        clean
        config
        build
        ;;
    -reconfig)
	clean
	config
	;;
    -mkdir)
	bootstrap
	mkbuild_dir
	;;
    -version)
	version
	;;
    *)
	if [ "$num_arguments" -eq 0 ]; then
	    bootstrap
	else
	    usage 1>&2
	    exit 1
	fi
	;;
esac

exit 0
