#!/usr/bin/env bash
# 
# Copyright (C) 2012-2013 Brian Aker
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
#     * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 
#     * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# 
#     * The names of its contributors may not be used to endorse or
# promote products derived from this software without specific prior
# written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


# Environment Variables that will influence the build:
#   AUTOMAKE
#   AUTORECONF
#   LIBTOOLIZE
#   MAKE
#   PREFIX
#   LOG_COMPILER
#   VERBOSE
#   WARNINGS
#

use_banner ()
{
  echo "#####################################################################################"
  echo "#"
  echo "#"
  echo "#"
  echo "#    TARGET:$1"
  echo "#"
  echo "#"
  echo "#"
  echo "#####################################################################################"
}

command_not_found_handle ()
{
  warn "$*" ": command not found"

  #if $DEBUG; then
    echo ""
    echo "Stack trace:"
    local frame=0
    while caller $frame; do
      ((frame++));
    done
    echo ""
  #fi

  return 127
}

error ()
{
  echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: " "$*" >&2
}

die ()
{
  echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: " "$*" >&2
  exit 1;
}

warn ()
{
  echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: " "$*"
  #echo "$BASH_SOURCE:${BASH_LINENO[0]}: $@" >&1
}

nassert ()
{
  local param_name=\$"$1"
  local param_value
  param_value="$(eval "expr \"$param_name\" ")"

  if [ -n "$param_value" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($param_name) had value of $param_value" >&2
    exit 1
  fi
}

assert ()
{
  local param_name=\$"$1"
  local param_value
  param_value="$(eval "expr \"$param_name\" ")"

  if [ -z "$param_value" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($param_name)" >&2
    exit 1
  fi
}

assert_file ()
{
  if [ ! -f "$1" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($1) does not exist: $2" >&2
    exit 1; 
  fi
}

assert_no_file ()
{
  if [ -f "$1" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($1) file exists: $2" >&2
    exit 1;
  fi
}

assert_no_directory ()
{
  if [ -d "$1" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($1) directory exists: $2" >&2
    exit 1;
  fi
}

assert_exec_file ()
{
  if [ ! -f "$1" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($1) does not exist: $2" >&2
    exit 1;
  fi

  if [ ! -x "$1" ]; then
    echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]}: assert($1) exists but is not executable: $2" >&2
    exit 1;
  fi
}

command_exists ()
{
  type "$1" &> /dev/null ;
}

rebuild_host_os ()
{
  HOST_OS="${UNAME_MACHINE_ARCH}-${VENDOR}-${VENDOR_DISTRIBUTION}-${VENDOR_RELEASE}-${UNAME_KERNEL}-${UNAME_KERNEL_RELEASE}"
  if [ -z "$1" ]; then
    if $verbose; then
      echo "HOST_OS=$HOST_OS"
    fi
  fi
}

# Validate the distribution name, or toss an erro
#  values: darwin,fedora,rhel,ubuntu,debian,opensuse
set_VENDOR_DISTRIBUTION ()
{
  local dist
  dist="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
  case "$dist" in
    darwin)
      VENDOR_DISTRIBUTION='darwin'
      ;;
    fedora)
      VENDOR_DISTRIBUTION='fedora'
      ;;
    rhel)
      VENDOR_DISTRIBUTION='rhel'
      ;;
    debian)
      VENDOR_DISTRIBUTION='debian'
      ;;
    ubuntu)
      VENDOR_DISTRIBUTION='ubuntu'
      ;;
    suse)
      VENDOR_DISTRIBUTION='opensuse'
      ;;
    opensuse)
      VENDOR_DISTRIBUTION='opensuse'
      ;;
    *)
      die "attempt to set an invalid VENDOR_DISTRIBUTION=$dist"
      ;;
  esac
}

# Validate a Vendor's release name/number 
set_VENDOR_RELEASE ()
{
  local release
  release="$(echo "$1" | tr '[:upper:]' '[:lower:]')"

  if $verbose; then
    echo "VENDOR_DISTRIBUTION:$VENDOR_DISTRIBUTION"
    echo "VENDOR_RELEASE:$release"
  fi

  case $VENDOR_DISTRIBUTION in
    darwin)
      case $release in
        10.6*)
          VENDOR_RELEASE='snow_leopard'
          ;;
        10.7*)
          VENDOR_RELEASE='mountain'
          ;;
        mountain)
          VENDOR_RELEASE='mountain'
          ;;
        10.8.*)
          echo "mountain_lion"
          VENDOR_RELEASE='mountain_lion'
          ;;
        10.9)
          echo "mavericks"
          VENDOR_RELEASE='mavericks'
          ;;
        10.9.*)
          echo "mavericks"
          VENDOR_RELEASE='mavericks'
          ;;
        *)
          echo "$release"
          VENDOR_RELEASE='unknown'
          ;;
      esac
      ;;
    fedora)
      VENDOR_RELEASE="$release"
      ;;
    rhel)
      VENDOR_RELEASE="$release"
      ;;
    debian)
      VENDOR_RELEASE="$release"
      ;;
    ubuntu)
      VENDOR_RELEASE="$release"
      if [[ "x$VENDOR_RELEASE" == 'x12.04' ]]; then
        VENDOR_RELEASE="precise"
      elif [[ "x$VENDOR_RELEASE" == 'x12.10' ]]; then
        VENDOR_RELEASE="quantal"
      elif [[ "x$VENDOR_RELEASE" == 'x13.04' ]]; then
        VENDOR_RELEASE="raring"
      fi
      ;;
    opensuse)
      VENDOR_RELEASE="$release"
      ;;
    unknown)
      die "attempt to set VENDOR_RELEASE without setting VENDOR_DISTRIBUTION"
      ;;
    *)
      die "attempt to set with an invalid VENDOR_DISTRIBUTION=$VENDOR_DISTRIBUTION"
      ;;
  esac
}


#  Valid values are: apple, redhat, centos, canonical, oracle, suse
set_VENDOR ()
{
  local vendor
  vendor="$(echo "$1" | tr '[:upper:]' '[:lower:]')"

  case $vendor in
    apple)
      VENDOR='apple'
      ;;
    redhat)
      VENDOR='redhat'
      ;;
    fedora)
      VENDOR='redhat'
      ;;
    redhat-release-server-*)
      VENDOR='redhat'
      ;;
    enterprise-release-*)
      VENDOR='oracle'
      ;;
    centos)
      VENDOR='centos'
      ;;
    canonical)
      VENDOR='canonical'
      ;;
    ubuntu)
      VENDOR='canonical'
      ;;
    debian)
      VENDOR='debian'
      ;;
    opensuse)
      VENDOR='suse'
      ;;
    suse)
      VENDOR='suse'
      ;;
    *)
      die "An attempt was made to set an invalid VENDOR=$vendor"
      ;;
  esac

  set_VENDOR_DISTRIBUTION "$2"
  set_VENDOR_RELEASE "$3"
}

determine_target_platform ()
{
  UNAME_MACHINE_ARCH="$(uname -m 2>/dev/null)" || UNAME_MACHINE_ARCH=unknown
  UNAME_KERNEL="$(uname -s 2>/dev/null)"  || UNAME_KERNEL=unknown
  UNAME_KERNEL_RELEASE="$(uname -r 2>/dev/null)" || UNAME_KERNEL_RELEASE=unknown

  if [[ -x '/usr/bin/sw_vers' ]]; then
    local _VERSION
    _VERSION="$(/usr/bin/sw_vers -productVersion)"
    set_VENDOR 'apple' 'darwin' "$_VERSION"
  elif [[ $(uname) == 'Darwin' ]]; then
    set_VENDOR 'apple' 'darwin' 'mountain'
  elif [[ -f '/etc/fedora-release' ]]; then
    local fedora_version
    fedora_version="$(awk ' { print $3 } ' < /etc/fedora-release)"
    set_VENDOR 'redhat' 'fedora' "$fedora_version"
  elif [[ -f '/etc/centos-release' ]]; then
    local centos_version
    centos_version="$(awk ' { print $7 } ' < /etc/centos-release)"
    set_VENDOR 'centos' 'rhel' "$centos_version"
  elif [[ -f '/etc/SuSE-release' ]]; then
    local suse_distribution
    suse_distribution="$(head -1 /etc/SuSE-release | awk ' { print $1 } ')"
    local suse_version
    suse_version="$(head -1 /etc/SuSE-release | awk ' { print $2 } ')"
    set_VENDOR 'suse' "$suse_distribution" "$suse_version"
  elif [[ -f '/etc/redhat-release' ]]; then
    local rhel_version
    rhel_version="$(awk ' { print $7 } ' < /etc/redhat-release)"
    local _vendor
    _vendor="$(rpm -qf /etc/redhat-release)"
    set_VENDOR "$_vendor" 'rhel' "$rhel_version"
  elif [[ -f '/etc/os-release' ]]; then
    # shellcheck disable=SC1091
    source '/etc/os-release'
    set_VENDOR "$ID" "$ID" "$VERSION_ID"
  elif [[ -x '/usr/bin/lsb_release' ]]; then
    local _ID
    _ID="$(/usr/bin/lsb_release -s -i)"
    local _VERSION
    _VERSION="$(/usr/bin/lsb_release -s -r)"
    set_VENDOR "$_ID" "$_ID" "$_VERSION_ID"
  elif [[ -f '/etc/lsb-release' ]]; then
    # shellcheck disable=SC1091
    source '/etc/lsb-release'
    set_VENDOR 'canonical' "$DISTRIB_ID" "$DISTRIB_CODENAME"
  fi

  rebuild_host_os
}

run_configure ()
{
  # We will run autoreconf if we are required
  run_autoreconf_if_required

  # We always begin at the root of our build
  if [ ! $? ]; then
    die "Programmer error, we entered run_configure with a stacked directory"
  fi

  if ! command_exists "$CONFIGURE"; then
    die "$CONFIGURE does not exist"
  fi

  # Arguments for configure
  local BUILD_CONFIGURE_ARG=''

  # If debug is set we enable both debug and asssert, otherwise we see if this is a VCS checkout and if so enable assert
  # Set ENV ASSERT in order to enable assert.
  # If we are doing a valgrind run, we always compile with assert disabled
  if $valgrind_run; then
    BUILD_CONFIGURE_ARG="--enable-assert=no $BUILD_CONFIGURE_ARG"
  else
    if $debug; then 
      BUILD_CONFIGURE_ARG="--enable-debug --enable-assert $BUILD_CONFIGURE_ARG"
    elif [[ -n "$VCS_CHECKOUT" ]]; then
      BUILD_CONFIGURE_ARG="--enable-assert $BUILD_CONFIGURE_ARG"
    fi
  fi

  if [[ -n "$CONFIGURE_ARG" ]]; then 
    BUILD_CONFIGURE_ARG="$CONFIGURE_ARG $BUILD_CONFIGURE_ARG"
  fi

  if [[ -n "$PREFIX_ARG" ]]; then 
    BUILD_CONFIGURE_ARG="$PREFIX_ARG $BUILD_CONFIGURE_ARG"
  fi

  ret=1;
  # If we are executing on OSX use CLANG, otherwise only use it if we find it in the ENV
  case $HOST_OS in
    rhel-5*)
      command_exists 'gcc44' || die "Could not locate gcc44"
      run CC=gcc44 CXX=gcc44 "$top_srcdir/configure" "$BUILD_CONFIGURE_ARG" || die "Cannot execute CC=gcc44 CXX=gcc44 configure $BUILD_CONFIGURE_ARG"
      ret=$?
      ;;
    *)
      run "$CONFIGURE" "$BUILD_CONFIGURE_ARG"
      ret=$?
      ;;
  esac

  if [ $ret -ne 0 ]; then
    die "Could not execute $CONFIGURE $BUILD_CONFIGURE_ARG"
  fi

  if [ ! -f 'Makefile' ]; then
    die "Programmer error, configure was run but no Makefile existed after $CONFIGURE was run"
  fi
}

setup_gdb_command ()
{
  GDB_TMPFILE=$(mktemp /tmp/gdb.XXXXXXXXXX)
  echo 'set logging overwrite on' > "$GDB_TMPFILE"
  {
  echo 'set logging on'
  echo 'set environment LIBTEST_IN_GDB=1'
  echo 'run'
  echo 'thread apply all bt'
  echo 'quit'
  } >> "$GDB_TMPFILE"

  GDB_COMMAND="gdb -f -batch -x $GDB_TMPFILE"
}

setup_valgrind_command ()
{
  VALGRIND_PROGRAM="$(type -p valgrind)"
  if [[ -n "$VALGRIND_PROGRAM" ]]; then
    VALGRIND_COMMAND="$VALGRIND_PROGRAM --error-exitcode=1 --leak-check=yes --malloc-fill=A5 --free-fill=DE --xml=yes --xml-file=\"valgrind-%p.xml\""
  fi
}

save_BUILD ()
{
  if [[ -n "$OLD_CONFIGURE" ]]; then
    die "OLD_CONFIGURE($OLD_CONFIGURE) was set on push, programmer error!"
  fi

  if [[ -n "$OLD_CONFIGURE_ARG" ]]; then
    die "OLD_CONFIGURE_ARG($OLD_CONFIGURE_ARG) was set on push, programmer error!"
  fi

  if [[ -n "$OLD_PREFIX" ]]; then
    die "OLD_PREFIX($OLD_PREFIX) was set on push, programmer error!"
  fi

  if [[ -n "$OLD_MAKE" ]]; then
    die "OLD_MAKE($OLD_MAKE) was set on push, programmer error!"
  fi

  if [[ -n "$OLD_LOG_COMPILER" ]]; then
    die "OLD_LOG_COMPILER($OLD_LOG_COMPILER) was set on push, programmer error!"
  fi

  if [[ -n "$CONFIGURE" ]]; then
    OLD_CONFIGURE=$CONFIGURE
  fi

  if [[ -n "$CONFIGURE_ARG" ]]; then
    OLD_CONFIGURE_ARG=$CONFIGURE_ARG
  fi

  if [[ -n "$MAKE" ]]; then
    OLD_MAKE=$MAKE
  fi

  if [[ -n "$LOG_COMPILER" ]]; then
    OLD_LOG_COMPILER=$LOG_COMPILER
  fi
}

restore_BUILD ()
{
  if [[ -n "$OLD_CONFIGURE" ]]; then
    CONFIGURE=$OLD_CONFIGURE
  fi

  if [[ -n "$OLD_CONFIGURE_ARG" ]]; then
    CONFIGURE_ARG=$OLD_CONFIGURE_ARG
  fi

  if [[ -n "$OLD_PREFIX" ]]; then
    PREFIX_ARG=$OLD_PREFIX
  fi

  if [[ -n "$OLD_MAKE" ]]; then
    MAKE=$OLD_MAKE
  fi

  if [[ -n "$OLD_LOG_COMPILER" ]]; then
    LOG_COMPILER=$OLD_LOG_COMPILER
  fi

  OLD_CONFIGURE=
  OLD_CONFIGURE_ARG=
  OLD_PREFIX=
  OLD_MAKE=
  OLD_LOG_COMPILER=

  export -n CC CXX
}

make_valgrind ()
{
  # If the env VALGRIND_COMMAND is set then we assume it is valid
  local valgrind_was_set=false
  if [[ -z "$VALGRIND_COMMAND" ]]; then
    setup_valgrind_command
    if [[ -n "$VALGRIND_COMMAND" ]]; then
      valgrind_was_set=true
    fi
  else
    valgrind_was_set=true
  fi

  # If valgrind_was_set is set to no we bail
  if ! $valgrind_was_set; then
    echo 'valgrind was not present'
    return 1
  fi

  save_BUILD

  valgrind_run=true

  # If we are required to run configure, do so now
  run_configure

  # If we don't have a configure, then most likely we will be missing libtool
  assert_file 'configure'
  if [[ -x 'libtool' ]]; then
    LOG_COMPILER="./libtool --mode=execute $VALGRIND_COMMAND"
  else
    LOG_COMPILER="$VALGRIND_COMMAND"
  fi

  make_target 'all'
  make_target 'check'
  ret=$?

  valgrind_run=false

  restore_BUILD

  if [ "$ret" -ne 0 ]; then
    return 1
  fi
}

make_install_system ()
{
  local INSTALL_LOCATION
  INSTALL_LOCATION="$(mktemp -d /tmp/XXXXXXXXXX)"

  save_BUILD
  PREFIX_ARG="--prefix=$INSTALL_LOCATION"

  if [ ! -d "$INSTALL_LOCATION" ] ; then
    die "ASSERT temp directory not found '$INSTALL_LOCATION'"
  fi

  run_configure #install_buid_dir

  make_target 'install'

  make_target 'installcheck'

  make_target 'uninstall'

  rm -r -f "$INSTALL_LOCATION"
  make 'distclean'

  if [ -f 'Makefile' ]; then
    die "ASSERT Makefile should not exist"
  fi

  restore_BUILD
}

make_darwin_malloc ()
{
  run_configure_if_required

  old_MallocGuardEdges=$MallocGuardEdges
  MallocGuardEdges=1
  old_MallocErrorAbort=$MallocErrorAbort
  MallocErrorAbort=1
  old_MallocScribble=$MallocScribble
  MallocScribble=1

  make_check

  MallocGuardEdges=$old_MallocGuardEdges
  MallocErrorAbort=$old_MallocErrorAbort
  MallocScribble=$old_MallocScribble
}

# This will reset our environment, and make sure built files are available.
make_for_snapshot ()
{
  # Lets make sure we have a clean environment
  assert_no_file 'Makefile'
  assert_no_file 'configure'
  assert_no_directory 'autom4te.cache'

  run_configure
  make_target 'all'
  make_target 'distclean'

  # We should have a configure, but no Makefile at the end of this exercise
  assert_no_file 'Makefile'
  assert_exec_file 'configure'
}

check_mingw ()
{
  command_exists 'mingw64-configure'
  ret=$?
  if [ "$ret" -ne 0 ]; then
    return 1
  fi

  command_exists 'mingw64-make'
  ret=$?
  if [ "$ret" -ne 0 ]; then
    return 1
  fi

  return 0
}

check_clang ()
{
  command_exists 'clang'
  ret=$?
  if [ "$ret" -ne 0 ]; then
    return 1
  fi

  return 0
}

check_clang_analyzer ()
{
  command_exists 'scan-build'
  ret=$?
  if [ "$ret" -ne 0 ]; then
    return 1
  fi

  return 0
}

make_skeleton ()
{
  run_configure
  ret=$?

  if [ $ret -eq 0 ]; then
    assert_file 'Makefile'

    make_target 'all' 'warn'
    ret=$?
    if [ $ret -ne 0 ]; then
      warn "$MAKE failed"
    else
      if [[ -n "$DISPLAY" ]]; then
        if command_exists 'wine'; then
          LOG_COMPILER='wine'
        fi
      fi

      if [[ -n "$LOG_COMPILER" ]]; then
        make_target 'check' 'warn' || warn "$MAKE check failed"
        ret=$?
      fi
    fi

    if $jenkins_build_environment; then
      make_target 'clean' 'warn'
    fi
  fi

  return $ret
}

make_for_mingw ()
{
  if ! check_mingw; then
    return 1
  fi

  # Make sure it is clean
  if [ -f Makefile ] || [ -f configure ]; then
    make_maintainer_clean
  fi

  run_autoreconf

  save_BUILD

  CONFIGURE='mingw64-configure'
  MAKE='mingw64-make'
  CONFIGURE_ARGS='--enable-static --disable-shared'

  make_skeleton
  ret=$?

  restore_BUILD

  return $ret
}

make_for_clang ()
{
  if ! check_clang; then
    return 1
  fi

  # Make sure it is clean
  if [ -f Makefile ] || [ -f configure ]; then
    make_maintainer_clean
  fi

  run_autoreconf

  save_BUILD

  CC=clang CXX=clang++
  export CC CXX

  make_skeleton
  ret=$?

  make_target 'check'

  restore_BUILD

  return $ret
}

make_for_clang_analyzer ()
{
  if ! check_clang; then
    return 1
  fi

  if ! check_clang_analyzer; then
    die 'clang-analyzer was not found'
  fi

  # Make sure it is clean
  if [ -f Makefile ] || [ -f configure ]; then
    make_maintainer_clean
  fi

  run_autoreconf

  save_BUILD

  CC=clang CXX=clang++
  export CC CXX
  CONFIGURE='scan-build ./configure'
  CONFIGURE_ARGS='--enable-debug'
  export CONFIGURE_ARGS

  run_configure

  scan-build -o clang-html make -j4 -k

  restore_BUILD
}

# If we are locally testing, we should make sure the environment is setup correctly
check_for_jenkins ()
{
  if ! $jenkins_build_environment; then
    echo "Not inside of jenkins, simulating environment"

    if [ -f 'configure' ]; then
      make_maintainer_clean
    fi

    if $BOOTSTRAP_SNAPSHOT; then
      make_for_snapshot
    fi
  fi
}

make_universe ()
{
  use_banner 'make maintainer-clean'
  make_maintainer_clean

  use_banner 'snapshot'
  make_for_snapshot

  use_banner 'valgrind'
  make_valgrind

  use_banner 'gdb'
  make_gdb

  use_banner 'rpm'
  make_rpm

  use_banner 'clang'
  make_for_clang

  use_banner 'clang analyzer'
  make_for_clang_analyzer

  use_banner 'mingw'

  if !  check_mingw
  then
    make_for_mingw
  fi

  use_banner 'make distcheck'
  make_distcheck

  use_banner 'make install'
  make_install_system
}

check_snapshot ()
{
  if [ -n "$BOOTSTRAP_SNAPSHOT_CHECK" ]; then
    assert_file "$BOOTSTRAP_SNAPSHOT_CHECK" 'snapshot check failed'
  fi
}

make_for_continuus_integration ()
{
  # Setup the environment if we are local
  check_for_jenkins

  # No matter then evironment, we should not have a Makefile at this point
  assert_no_file 'Makefile'

  # Platforms which require bootstrap should have some setup done before we hit this stage.
  # If we are building locally, skip this step, unless we are just testing locally. 
  if $BOOTSTRAP_SNAPSHOT; then
    if $BOOTSTRAP_SNAPSHOT; then
      assert_file 'configure'
    fi

    check_snapshot
  else
    # If we didn't require a snapshot, then we should not have a configure
    assert_no_file 'configure'

    run_autoreconf
  fi

  assert_no_file 'Makefile' 'Programmer error, Makefile existed where build state should have been clean'

  case $HOST_OS in
    *)
      make_jenkins_default
      ;;
  esac

  make_maintainer_clean
}

# The point to this test is to test bootstrap.sh itself
self_test ()
{
  # We start off with a clean env
  make_maintainer_clean

#  eval "./bootstrap.sh jenkins" || die "failed 'jenkins'"
#  eval "./bootstrap.sh all" || die "failed 'all'"
#  eval "./bootstrap.sh gdb" || die "failed 'gdb'"
#  eval "./bootstrap.sh maintainer-clean" || die "failed 'maintainer-clean'"
}

make_install_html ()
{
  run_configure_if_required
  assert_file 'configure'

  make_target 'install-html'
}

make_gdb ()
{
  save_BUILD

  if command_exists 'gdb'; then
    run_configure_if_required

    # Set ENV GDB_COMMAND
    if [[ -z "$GDB_COMMAND" ]]; then
      setup_gdb_command
    fi

    # If we don't have a configure, then most likely we will be missing libtool
    assert_file 'configure'
    if [[ -f 'libtool' ]]; then
      LOG_COMPILER="./libtool --mode=execute $GDB_COMMAND"
    else
      LOG_COMPILER="$GDB_COMMAND"
    fi

    make_target 'check'

    if [ -f 'gdb.txt' ]; then
      rm 'gdb.txt'
    fi

    if [ -f '.gdb_history' ]; then
      rm '.gdb_history'
    fi

    if $jenkins_build_environment; then
      make_target 'clean'
    fi
  else
    echo 'gdb was not present'
    return 1
  fi

  restore_BUILD
}

# $1 target to compile
# $2 to die, or not to die, based on contents
make_target ()
{
  if [ -z "$1" ]; then
    die "Programmer error, no target provided for make"
  fi

  if [ ! -f 'Makefile' ]; then
    die "Programmer error, make was called before configure"
    run_configure
  fi

  if [ -n "$LOG_COMPILER" ]; then
    if $verbose; then
      echo "LOG_COMPILER=$LOG_COMPILER"
    fi
  fi

  if [ -z "$MAKE" ]; then
    die "MAKE was not set"
  fi

  # $2 represents error or warn
  run "$MAKE" "$1"
  ret=$?

  if [ $ret -ne 0 ]; then
    if [ -n "$2" ]; then
      warn "Failed to execute $MAKE $1: $ret"
    elif [ $ret -eq 2 ]; then
      die "Failed to execute $MAKE $1"
    else
      die "Failed to execute $MAKE $1: $ret"
    fi
  fi

  return $ret
}

make_distcheck ()
{
  make_target 'distcheck'
}

make_rpm ()
{
  if command_exists 'rpmbuild'; then
    if [ -f 'rpm.am' ] || [ -d 'rpm' ]; then
      run_configure_if_required
      make_target 'dist-rpm'

      return $?
    fi
  fi

  return 1
}

make_maintainer_clean ()
{
  run_configure_if_required
  make_target 'maintainer-clean' 'no_error'

  # Lets make sure we really cleaned up the environment
  assert_no_file 'Makefile'
  assert_no_file 'configure'
  assert_no_directory 'autom4te.cache'
}

make_distclean ()
{
  run_configure_if_required
  make_target 'distclean' 'no_error'

  # Lets make sure we really cleaned up the environment
  assert_no_file 'Makefile'
  assert_file 'configure'
}

make_check ()
{
  make_target 'check'
}

make_jenkins_default ()
{
  run_configure
  make_target 'all'
}

make_default ()
{
  run_configure_if_required
  make_target 'all'
}

run_configure_if_required () 
{
  run_autoreconf_if_required

  if [ ! -f 'Makefile' ]; then
    run_configure
  fi

  assert_file 'Makefile' 'configure did not produce a Makefile'
}

run_make_maintainer_clean_if_possible () 
{
  if [ -f 'Makefile' ]; then
    make_maintainer_clean
  fi
}

run_autoreconf_if_required () 
{
  if [ ! -x 'configure' ]; then
    run_autoreconf
  fi

  assert_exec_file 'configure'
  bash -n configure
}

run_autoreconf () 
{
  if [[ -z "$AUTORECONF" ]]; then
    die "Programmer error, tried to call run_autoreconf () but AUTORECONF was not set"
  fi

  if $use_libtool; then
    assert "$BOOTSTRAP_LIBTOOLIZE"
    if $jenkins_build_environment; then
      run "$BOOTSTRAP_LIBTOOLIZE" '--copy' '--install' || die "Cannot execute $BOOTSTRAP_LIBTOOLIZE"
    else
      run "$BOOTSTRAP_LIBTOOLIZE" '--copy' '--install' '--force' || die "Cannot execute $BOOTSTRAP_LIBTOOLIZE"
    fi
  fi

  run "$AUTORECONF" "$AUTORECONF_ARGS" || die "Cannot execute $AUTORECONF"

  eval 'bash -n configure' || die "autoreconf generated a malformed configure"
}

run ()
{
  if $verbose; then
    echo "\`$*'" "$ARGS"
  fi

  if [ -z "$1" ]; then
    return 127;
  fi

  eval "$@" "$ARGS"
} 

parse_command_line_options ()
{
  local SHORTOPTS=':apcmt:dvho:'

  nassert OPT_TARGET

  while getopts "$SHORTOPTS" opt; do
    case $opt in
      a) #--autoreconf
        AUTORECONF_OPTION=true
        OPT_TARGET+='autoreconf'
        ;;
      p) #--print-env
        print_setup_opt=true
        ;;
      c) # --configure
        CONFIGURE_OPTION=true
        OPT_TARGET+='configure'
        ;;
      m) # maintainer-clean
        CLEAN_OPTION=true
        ;;
      o) # target
        CONFIGURE_ARG="$OPTARG"
        ;;
      t) # target
        TARGET_OPTION=true
        TARGET_OPTION_ARG="$OPTARG"
        OPT_TARGET+="$OPTARG"
        ;;
      d) # debug
        opt_debug=true
        enable_debug
        ;;
      h) # help
        echo "bootstrap.sh [options] optional_target ..."
        echo "  -a # Just run autoreconf";
        echo "  -p # Print ENV";
        echo "  -c # Just run configure";
        echo "  -m # Just run maintainer-clean";
        echo "  -o # Specify configure arguments";
        echo "  -t # Make target";
        echo "  -d # Enable debug";
        echo "  -h # Show help";
        echo "  -v # Be more verbose in output";
        exit
        ;;
      v) # verbose
        opt_verbose=true
        verbose=true
        ;;
      :)
        echo "Option -$OPTARG requires an argument." >&2
        exit 1
        ;;
      *)
        echo "$0: error - unrecognized option $1" 1>&2
        exit 1
        ;;
    esac
  done

  shift $((OPTIND-1))

  if [ -n "$1" ]; then
    OPT_TARGET="$*"
  fi
}

determine_vcs ()
{
  if [[ -d '.git' ]]; then
    VCS_CHECKOUT=git
  elif [[ -d '.bzr' ]]; then
    VCS_CHECKOUT=bzr
  elif [[ -d '.svn' ]]; then
    VCS_CHECKOUT=svn
  elif [[ -d '.hg' ]]; then
    VCS_CHECKOUT=hg
  else
    VCS_CHECKOUT=
  fi

  if [[ -n "$VCS_CHECKOUT" ]]; then
    verbose=true
  fi
}

require_libtoolise ()
{
  grep '^[         ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \
    && use_libtool=true
  grep '^[         ]*LT_INIT' configure.ac >/dev/null \
    && use_libtool=true
}

autoreconf_setup ()
{
  # Set ENV MAKE in order to override "make"
  if [[ -z "$MAKE" ]]; then 
    if command_exists 'gmake'; then
      MAKE="$(type -p gmake)"
    else
      if command_exists 'make'; then
        MAKE="$(type -p make)"
      fi
    fi
    
    if [ "$VCS_CHECKOUT" ]; then
      if $debug; then
        MAKE="$MAKE --warn-undefined-variables"
      fi
    fi

    if $debug; then
      MAKE="$MAKE -d"
    fi
  fi

  if [[ -z "$GNU_BUILD_FLAGS" ]]; then
    if $jenkins_build_environment; then
      GNU_BUILD_FLAGS="--install"
    else
      GNU_BUILD_FLAGS="--install --force"
    fi
  fi

  if $verbose; then
    GNU_BUILD_FLAGS="$GNU_BUILD_FLAGS --verbose"
  fi

  if [ -z "$ACLOCAL_PATH" ]; then
    ACLOCAL_PATH="/usr/local/share/aclocal $ACLOCAL_PATH"
  fi

  if [[ -z "$WARNINGS" ]]; then
    if [[ -n "$VCS_CHECKOUT" ]]; then
      WARNINGS="all,error"
    else
      WARNINGS="all"
    fi
  fi

  if $use_libtool; then
    if [[ -n "$LIBTOOLIZE" ]]; then
      BOOTSTRAP_LIBTOOLIZE=$(type -p "$LIBTOOLIZE")

      if [[ -z "$BOOTSTRAP_LIBTOOLIZE" ]]; then
        echo "Couldn't find user supplied libtoolize, it is required"
        return 1
      fi
    else
      # If we are using OSX, we first check to see glibtoolize is available
      if [[ "$VENDOR_DISTRIBUTION" == "darwin" ]]; then
        BOOTSTRAP_LIBTOOLIZE="$(type -p glibtoolize)"

        if [[ -z "$BOOTSTRAP_LIBTOOLIZE" ]]; then
          echo "Couldn't find glibtoolize, it is required on OSX"
          return 1
        fi
      else
        BOOTSTRAP_LIBTOOLIZE="$(type -p libtoolize)"

        if [[ -z "$BOOTSTRAP_LIBTOOLIZE" ]]; then
          echo "Couldn't find libtoolize, it is required"
          return 1
        fi
      fi
    fi

    if $verbose; then
      LIBTOOLIZE_OPTIONS="--verbose $BOOTSTRAP_LIBTOOLIZE_OPTIONS"
    fi

    if $debug; then
      LIBTOOLIZE_OPTIONS="--debug $BOOTSTRAP_LIBTOOLIZE_OPTIONS"
    fi

    # Here we set LIBTOOLIZE to true since we are going to invoke it via BOOTSTRAP_LIBTOOLIZE
    LIBTOOLIZE=true
  fi

  # Test the ENV AUTOMAKE if it exists
  if [[ -n "$AUTOMAKE" ]]; then
    run "$AUTOMAKE" '--help'    &> /dev/null    || die "Failed to run AUTOMAKE:$AUTOMAKE"
  fi

  # Test the ENV AUTOCONF if it exists
  if [[ -n "$AUTOCONF" ]]; then
    run "$AUTOCONF" '--help'    &> /dev/null    || die "Failed to run AUTOCONF:$AUTOCONF"
  fi

  # Test the ENV AUTOHEADER if it exists
  if [[ -n "$AUTOHEADER" ]]; then
    run "$AUTOHEADER" '--help'  &> /dev/null    || die "Failed to run AUTOHEADER:$AUTOHEADER"
  fi

  # Test the ENV AUTOM4TE if it exists
  if [[ -n "$AUTOM4TE" ]]; then
    run "$AUTOM4TE" '--help'    &> /dev/null    || die "Failed to run AUTOM4TE:$AUTOM4TE"
  fi

  # Test the ENV AUTOHEADER if it exists, if not we add one and add --install
  if [[ -z "$ACLOCAL" ]]; then
    ACLOCAL="aclocal --install"
  fi
  run "$ACLOCAL" '--help'  &> /dev/null    || die "Failed to run ACLOCAL:$ACLOCAL"

  if [[ -z "$AUTORECONF" ]]; then
    AUTORECONF="$(type -p autoreconf)"

    if [[ -z "$AUTORECONF" ]]; then
      die "Couldn't find autoreconf"
    fi

    if [[ -n "$GNU_BUILD_FLAGS" ]]; then
      AUTORECONF_ARGS="$GNU_BUILD_FLAGS"
    fi
  fi

  run "$AUTORECONF" '--help'  &> /dev/null    || die "Failed to run AUTORECONF:$AUTORECONF"
}

print_setup ()
{
  local saved_debug_status=$debug
  if $debug; then
    disable_debug
  fi

  echo '----------------------------------------------' 
  echo 'BOOTSTRAP ENV' 
  echo "AUTORECONF=$AUTORECONF"
  echo "HOST_OS=$HOST_OS"
  echo "VENDOR=$VENDOR"
  echo "VENDOR_DISTRIBUTION=$VENDOR_DISTRIBUTION"
  echo "VENDOR_RELEASE=$VENDOR_RELEASE"

  echo "getopt()"
  if $AUTORECONF_OPTION; then
    echo "--autoreconf"
  fi

  if $CLEAN_OPTION; then
    echo "--clean"
  fi

  if $CONFIGURE_OPTION; then
    echo "--configure"
  fi

  if $opt_debug; then
    echo "--debug"
  fi

  if $print_setup_opt; then
    echo "--print-env"
  fi

  if $TARGET_OPTION; then
    echo "--target=$TARGET_OPTION_ARG"
  fi

  if $opt_verbose; then
    echo "--verbose"
  fi

  if [[ -n "$MAKE" ]]; then
    echo "MAKE=$MAKE"
  fi

  if [[ -n "$BOOTSTRAP_TARGET" ]]; then
    echo "BOOTSTRAP_TARGET=$BOOTSTRAP_TARGET"
  fi

  if [[ -n "$PREFIX" ]]; then
    echo "PREFIX=$PREFIX"
  fi

  if [[ -n "$LOG_COMPILER" ]]; then
    echo "LOG_COMPILER=$LOG_COMPILER"
  fi

  if [[ -n "$VCS_CHECKOUT" ]]; then
    echo "VCS_CHECKOUT=$VCS_CHECKOUT"
  fi

  if $debug; then
    echo "debug=true"
  fi

  if [[ -n "$WARNINGS" ]]; then
    echo "WARNINGS=$WARNINGS"
  fi

  if $saved_debug_status; then
    echo "DEBUG=true"
  fi

  echo '----------------------------------------------' 

  if $saved_debug_status; then
    enable_debug
  fi
}

make_clean_option ()
{
  run_configure_if_required

  make_maintainer_clean

  if [[ "$VCS_CHECKOUT" == 'git' ]]; then
    run "$VCS_CHECKOUT" status --ignored
  elif [[ -n "$VCS_CHECKOUT" ]]; then
    run "$VCS_CHECKOUT" status
  fi
}

make_for_autoreconf ()
{
  if [ -f 'Makefile' ]; then
    make_maintainer_clean
  fi

  run_autoreconf

  assert_no_file 'Makefile'
}

check_make_target()
{
  local ret=0
  case $1 in
    'self')
      ;;
    'rpm')
      ;;
    'gdb')
      ;;
    'clean_op')
      ;;
    'autoreconf')
      ;;
    'install-system')
      ;;
    'configure')
      ;;
    'distcheck')
      ;;
    'check')
      ;;
    'snapshot')
      ;;
    'mingw')
      ;;
    'universe')
      ;;
    'valgrind')
      ;;
    'jenkins')
      ;;
    'distclean')
      ;;
    'maintainer-clean')
      ;;
    'install')
      ;;
    'all')
      ;;
    'make_default')
      ;;
    'clang')
      ;;
    'clang-analyzer')
      ;;
    test-*)
      ;;
    valgrind-*)
      ;;
    gdb-*)
      ;;
    'dist')
      ;;
    *)
      echo "Matched default"
      ret=1
      ;;
  esac

  return $ret
}

execute_job ()
{
  # We should always have a target by this point
  assert BOOTSTRAP_TARGET

  determine_target_platform

  determine_vcs

  # Set up whatever we need to do to use autoreconf later
  use_libtool=false
  require_libtoolise
  if ! autoreconf_setup; then
    return 1
  fi

  if $print_setup_opt -o  "$debug"; then
    echo
    print_setup
    echo

    # Exit if all we were looking for were the currently used options
    if $print_setup_opt; then
      exit
    fi
  fi

  # Use OLD_LOG_COMPILER for tracking the state of the variable
  local OLD_LOG_COMPILER=

  # Set ENV PREFIX in order to set --prefix for ./configure
  if [[ -n "$PREFIX" ]]; then 
    PREFIX_ARG="--prefix=$PREFIX"
  fi

  if $CLEAN_OPTION; then
    make_maintainer_clean
  fi

  local BOOTSTRAP_TARGET_ARRAY
  BOOTSTRAP_TARGET_ARRAY=( "$BOOTSTRAP_TARGET" )

  for target in "${BOOTSTRAP_TARGET_ARRAY[@]}"
  do
    # If we are running inside of Jenkins, we want to only run some of the possible tests
    if $jenkins_build_environment; then
      check_make_target "$target"
      ret=$?
      if [ $ret -ne 0 ]; then
        warn "Unknown BOOTSTRAP_TARGET option: $target"
        target="jenkins"
      fi
    fi

    if $jenkins_build_environment; then
      use_banner $target
    fi

    local valgrind_run=false

    case $target in
      'self')
        self_test
        ;;
      'gdb')
        make_gdb
        ;;
      'install-html')
        make_install_html
        ;;
      'clean_op')
        make_clean_option
        ;;
      'autoreconf')
        make_for_autoreconf
        ;;
      'install-system')
        make_install_system
        ;;
      'configure')
        run_configure
        ;;
      'make_default')
        make_default
        ;;
      'clang')
        make_distclean
        if ! check_clang; then
          die "clang was not found"
        fi

        if ! make_for_clang; then
          die "Failed to build clang: $?"
        fi
        ;;
      'clang-analyzer')
        make_distclean
        if ! check_clang_analyzer; then
          die "clang-analyzer was not found"
        fi
        if ! check_clang; then
          die "clang was not found"
        fi

        if ! make_for_clang_analyzer; then
          die "Failed to build clang-analyzer: $?"
        fi
        ;;
      'mingw')
        make_distclean
        if ! make_for_mingw; then
          die "Failed to build mingw: $?"
        fi
        ;;
      'snapshot')
        make_for_snapshot
        check_snapshot
        ;;
      'rpm')
        make_rpm
        ;;
      'darwin_malloc')
        make_darwin_malloc
        ;;
      'valgrind')
        make_valgrind
        ;;
      'universe')
        make_universe
        ;;
      'jenkins')
        make_for_continuus_integration
        ;;
      *)
        run_configure_if_required
        make_target "$target"
        ;;
    esac
  done
}

main ()
{
  # Are we running inside of Jenkins?
  if [[ -n "$JENKINS_HOME" ]]; then 
    declare -r jenkins_build_environment=true
  else
    declare -r jenkins_build_environment=false
  fi

  # Variables we export
  declare -x VCS_CHECKOUT=

  # Variables we control globally
  local -a BOOTSTRAP_TARGET=
  local CONFIGURE=
  local use_libtool=false
  local verbose=false

  #getop variables
  local opt_debug=false
  local opt_verbose=false

  if [[ -n "$VERBOSE" ]]; then
    verbose=true
  fi

  # Options for getopt
  local AUTORECONF_OPTION=false
  local CLEAN_OPTION=false
  local CONFIGURE_OPTION=false
  local print_setup_opt=false
  local TARGET_OPTION=false
  local TARGET_OPTION_ARG=

  local OLD_CONFIGURE=
  local OLD_CONFIGURE_ARG=
  local OLD_PREFIX=
  local OLD_MAKE=
  local OLD_LOG_COMPILER=

  local -r top_srcdir="$(pwd)"

  # Default configure
  if [ -z "$CONFIGURE" ]; then
    CONFIGURE="$top_srcdir/configure"
  fi


  # Variables for determine_target_platform () and rebuild_host_os ()
  #   UNAME_MACHINE_ARCH= uname -m
  #   VENDOR= apple, redhat, centos, canonical
  #   VENDOR_RELEASE=  
  #                  RHEL{rhel,Tikanga,Santiago}
  #                  Ubuntu{ubuntu,Lucid,Maverick,Natty,Oneiric,Precise,Quantal}
  #                  Fedora{fedora,Verne,Beefy}
  #                  OSX{osx,lion,snow,mountain}
  #   VENDOR_DISTRIBUTION= darwin,fedora,rhel,ubuntu
  #   UNAME_KERNEL= Linux, Darwin,...
  #   UNAME_KERNEL_RELEASE= Linux, Darwin,...
  local UNAME_MACHINE_ARCH=unknown
  local VENDOR=unknown
  local VENDOR_RELEASE=unknown
  local VENDOR_DISTRIBUTION=unknown
  local UNAME_KERNEL=unknown
  local UNAME_KERNEL_RELEASE=unknown
  local HOST_OS=

  rebuild_host_os no_output

  local OPT_TARGET=
  parse_command_line_options "$@"

  nassert BOOTSTRAP_TARGET

  if [ -n "$OPT_TARGET" ]; then
    BOOTSTRAP_TARGET="$OPT_TARGET"
  fi

  if [ -z "$BOOTSTRAP_TARGET" ]; then
    BOOTSTRAP_TARGET="make_default"
  fi

  # We should always have a target by this point
  assert BOOTSTRAP_TARGET

  execute_job
  local ret=$?

  jobs -l
  wait

  exit $ret
}

set_branch ()
{
  if [ -z "$BRANCH" ]; then 
    if [ -z "$CI_PROJECT_TEAM" ]; then 
      die "Variable CI_PROJECT_TEAM has not been set"
    fi
    if [ -z "$PROJECT" ]; then 
      die "Variable PROJECT has not been set"
    fi
    if [ -z "$BUILD_TAG" ]; then 
      die "Variable BUILD_TAG has not been set"
    fi

    BRANCH="lp:~$CI_PROJECT_TEAM/$PROJECT/$BUILD_TAG"
    export BRANCH
  fi

  if [ -z "$BRANCH" ]; then 
    die "Missing values required to build BRANCH variable."
  fi
}

merge ()
{
  if [ -z "$VCS_CHECKOUT" ]; then
    die "Merges require VCS_CHECKOUT."
  fi

  set_branch

  if [[ "$VCS_CHECKOUT" == 'bzr' ]]; then
    if test -n "$BRANCH_TO_MERGE"; then
      bzr merge "$BRANCH_TO_MERGE"
      bzr commit --message="Merge $BRANCH_TO_MERGE Build: $BUILD_TAG" --unchanged
    fi

    bzr push "$BRANCH"
  elif [[ -n "$VCS_CHECKOUT" ]]; then
    die "Merge attempt occured, current VCS setup does not support this"
  fi
}

enable_debug ()
{
  if ! $debug; then
    local caller_loc
    caller_loc="$(caller)"
    if [[ -n "$1" ]]; then
      echo "$caller_loc Enabling debug: $1"
    else
      echo "$caller_loc Enabling debug"
    fi
    set -x
    debug=true
  fi
}

usage ()
{
  cat << EOF
  Usage: $program_name [OPTION]..

  Bootstrap this package from the checked-out sources, and optionally walk through CI run.

  Options:

EOF
}

disable_debug ()
{
  set +x
  debug=false
}

check_shell ()
{
  if [ -x '/usr/local/bin/shellcheck' ]; then
    /usr/local/bin/shellcheck "$1"
    local ret=$?

    if [ "$ret" -ne 0 ]; then
      die "$1 failed shellcheck"
    fi
  fi
}

bootstrap ()
{
  check_shell 'bootstrap.sh'
  local env_debug_enabled=false
  local debug=false

  export ACLOCAL
  export AUTOCONF
  export AUTOHEADER
  export AUTOM4TE
  export AUTOMAKE
  export AUTORECONF
  export CONFIGURE_ARG
  export DEBUG
  export GNU_BUILD_FLAGS
  export LIBTOOLIZE
  export LIBTOOLIZE_OPTIONS
  export MAKE
  export PREFIX_ARG
  export LOG_COMPILER
  export VERBOSE
  export WARNINGS

  case $OSTYPE in
    darwin*)
      export MallocGuardEdges
      export MallocErrorAbort
      export MallocScribble
      ;;
  esac

  # We check for DEBUG twice, once before we source the config file, and once afterward
  if [[ -n "$DEBUG" ]]; then
    env_debug_enabled=true
  fi

  # Variables which only can be set by .bootstrap
  BOOTSTRAP_SNAPSHOT=false
  BOOTSTRAP_SNAPSHOT_CHECK=

  if [ -f '.bootstrap' ]; then
    # shellcheck disable=SC1091
    source '.bootstrap'
  fi

  # We do this in order to protect the case where DEBUG that came from the ENV (i.e. it overrides what is found in .bootstrap
  if $env_debug_enabled; then
    enable_debug
  elif [[ -n "$DEBUG" ]]; then
    enable_debug "Enabling DEBUG from '.bootstrap'"
  fi

  if $env_debug_enabled; then
    print_setup
  fi


  main "$@"
}

# Script begins here
declare -r program_name="$0"
bootstrap "$@"
