Add (alternate) build script that supports building linux->fuchsia and
fuchsia->fuchsia tools.

Change-Id: Ie0edb55bde4653e1823bdbb0f37dafb5164485bb
diff --git a/do-build b/do-build
new file mode 100755
index 0000000..63c66d9
--- /dev/null
+++ b/do-build
@@ -0,0 +1,661 @@
+#!/bin/bash
+
+SCRIPT_NAME="$(basename "$0")"
+SCRIPT_DIR="$(cd $(dirname "$0"); pwd)"
+INVOCATION_DIR="$(pwd)"
+
+# Things we want to build
+HOST_LIBS="gmp mpfr mpc"
+TOOLS="binutils gcc"
+
+ALL_GNU_TOOLS="ar as gcc ld nm objcopy objdump ranlib readelf"
+
+# Extra tools to build when targeting "*-none" platforms
+TOOLS_none="gdb"
+SUPPORTED_OSES="none linux fuchsia darwin"
+SUPPORTED_ARCHES="arm aarch64 x86_64"
+
+### Start of configuration options
+## In the configuration options below, all arch/os suffixes relate to the
+## target. e.g., "CONFIG_GCC_fuchsia" provides options that should be
+## used when building executables that will run on fuchsia, regardless
+## of the architecture. "CONFIG_GCC_aarch64_fuchsia" would give options that
+## should be used when building executables that will run on aarch64-fuchsia.
+## Uses ':' as an option delimiter, to facilitate the use of spaces inside
+## some options (e.g., CFLAGS).
+# GMP
+CONFIG_GMP_fuchsia="--with-pic=yes"
+
+# MPFR
+CONFIG_MPFR="--with-pic=yes: \
+             --with-gmp="
+
+# MPC
+CONFIG_MPC="--with-gmp=: \
+            --with-mpfr="
+
+# BINUTILS
+CONFIG_BINUTILS="--with-included-gettext: \
+                 --disable-werror: \
+                 --enable-initfini-array"
+CONFIG_BINUTILS_none="--enable-gold"
+CONFIG_BINUTILS_x86_64_fuchsia="--enable-gold=default"
+CONFIG_BINUTILS_aarch64_fuchsia="--enable-gold"
+CONFIG_BINUTILS_fuchsia="--enable-plugins: \
+                         --enable-relro:"
+CONFIG_BINUTILS_x86_64_none="--enable-targets=x86_64-pep"
+
+# GDB
+CONFIG_GDB="--with-included-gettext:--disable-werror"
+CONFIG_GDB_aarch64_none="--enable-targets=arm-eabi"
+
+# GCC
+CONFIG_GCC="--with-included-gettext: \
+            --disable-werror: \
+            --enable-languages=c,c++:\
+            --with-gmp=: \
+            --with-mpfr=: \
+            --with-mpc="
+CONFIG_GCC_none="--disable-libstdcxx: \
+                 --disable-libssp: \
+                 --disable-libquadmath"
+CONFIG_GCC_fuchsia="--enable-default-pie: \
+                    --disable-multilib"
+CONFIG_GCC_arm="--with-cpu=arm926ej-s: \
+                --with-fpu=vfp"
+CONFIG_GCC_TOOLS_llvm="CXXFLAGS=-fbracket-depth=1024"
+### End of configuration options
+
+
+usage()
+{
+  echo "Usage: $SCRIPT_NAME [OPTION]..."
+  echo "Build a set of compiler tools."
+  echo "Options:"
+  echo "  --force             Build even if configuration is not supported."
+  echo "  --help              Produce this message"
+  echo "  --host <ARCH-OS>    Specify type of host where the tools will run."
+  echo "                      (default is to use the current build machine)"
+  echo "  -j <#>              Specify number of parallel build instances"
+  echo "  --list-supported    List all supported configurations and exit"
+  echo "  --strip             Strip all binaries"
+  echo "  --sysroot <DIR>     Specify the location of the sysroot to be"
+  echo "                      used for non-native builds" 
+  echo "  --target <ARCH-OS>  Specify type of target the tools will generate"
+  echo "                      code for"
+}
+
+die()
+{
+  echo "$SCRIPT_NAME: $1" >&2
+  shift
+  while [ ! -z "$1" ]
+  do
+    echo "$1" >&2
+    shift
+  done
+  exit 1
+}
+
+gather_host_info()
+{
+  # Determine build machine attributes
+  BUILD_ARCH="$(uname -m)"
+  BUILD_OS="$(uname | tr '[:upper:]' '[:lower:]')"
+  BUILD="$BUILD_ARCH-$BUILD_OS"
+
+  if [ "$BUILD_OS" == "linux" ]
+  then
+    local num_cpus=$(grep processor /proc/cpuinfo | wc -l)
+    PARALLEL_BUILDS="$num_cpus"
+  fi
+
+  if cc --version | grep -q "LLVM"
+  then
+    HOST_TOOLS="llvm"
+  fi
+}
+
+verify_arch_os_tuple()
+{
+  tuple_to_check="$1"
+
+  for arch in $SUPPORTED_ARCHES
+  do
+    for os in $SUPPORTED_OSES
+    do
+      if [ "$arch-$os" == "$tuple_to_check" ]
+      then
+        return
+      fi
+    done
+  done
+
+  die "Unrecognized arch-os pair '$tuple_to_check'" \
+      "Use --list-supported to show all supported configurations"
+}
+
+split_arch_os()
+{
+  local tuple="$1"
+
+  RESULT_ARCH="$(echo "$tuple" | sed -E -e 's/([^-]+)-[^-]+$/\1/')"
+  RESULT_OS="$(echo "$tuple" | sed -E -e 's/[^-]+-([^-]+)$/\1/')"
+}
+
+is_configuration_supported()
+{
+  split_arch_os "$TARGET"
+  local target_arch="$RESULT_ARCH"
+  local target_os="$RESULT_OS"
+
+  if [ "$BUILD" == "$HOST" ]
+  then
+    # Simple (not Canadian) cross-compiler
+    if [ "$target_os" == "none" ] || [ "$target_os" == "fuchsia" ]
+    then
+      return 0
+    fi
+  elif [ "$HOST" == "$TARGET" ] && [ "$target_os" == "fuchsia" ]
+  then
+    # Build a natively-running fuchsia compiler
+    if [ "$target_arch" == "arm" ]
+    then
+      # ARM native builds don't work at the moment -- we need
+      # synchronization primitive support for pre-v6 architectures.
+      return 1
+    fi
+    return 0
+  fi
+  return 1
+}
+
+list_all_supported()
+{
+  local host_arch
+  local host_os
+  local target_arch
+  local target_os
+
+  for host_arch in $SUPPORTED_ARCHES
+  do
+    for host_os in $SUPPORTED_OSES
+    do
+      for target_arch in $SUPPORTED_ARCHES
+      do
+        for target_os in $SUPPORTED_OSES
+        do
+          HOST="$host_arch-$host_os"
+          TARGET="$target_arch-$target_os"
+          if is_configuration_supported
+          then
+            echo "  $HOST -> $TARGET"
+          fi
+        done
+      done
+    done
+  done
+}
+
+get_argument()
+{
+  if echo "$1" | grep -q "="
+  then
+    RESULT=$(echo "$1" | sed -E -e 's/^[^=]+*=//')
+  else
+    if [ -z "$2" ]
+    then
+      die "Argument required for option '$1'"
+    fi
+    SKIP_EXTRA_ARG="yes"
+    RESULT="$2"
+  fi
+}
+
+process_opts()
+{
+  local force=""
+
+  # Set defaults
+  OUT_DIR="$INVOCATION_DIR"
+  HOST="$BUILD"
+  STRIP=""
+
+  while [ ! -z "$1" ]
+  do
+    SKIP_EXTRA_ARG=""
+    case "$1" in
+      --force)
+        force="yes"
+        ;;
+      --help)
+        usage
+        exit 0
+        ;;
+      --host | --host=*)
+        get_argument $*
+        HOST="$RESULT"
+        verify_arch_os_tuple "$HOST"
+        ;;
+      -j | -j=*)
+        get_argument $*
+        NUM_PARALLEL_BUILDS="$RESULT"
+        ;;
+      --list-supported)
+        echo "All supported configurations are (host -> target):"
+        list_all_supported
+        exit 0
+        ;;
+      --outdir | --outdir=*)
+        get_argument $*
+        OUT_DIR="$RESULT"
+        ;;
+      --strip)
+        STRIP="yes"
+        ;;
+      --sysroot | --sysroot=*)
+        get_argument $*
+        SYSROOT="$RESULT"
+        ;;
+      --target | --target=*)
+        get_argument $*
+        TARGET="$RESULT"
+        verify_arch_os_tuple "$TARGET"
+        ;;
+      *)
+        die "Unrecognized option '$1'. Use '--help' for usage."
+        ;;
+    esac
+    shift
+    if [ -n "$SKIP_EXTRA_ARG" ]
+    then
+      shift
+    fi
+  done
+
+  # Post-processing
+  if [ -z "$TARGET" ]
+  then
+    die "--target required"
+  fi
+
+  if ! is_configuration_supported && [ -z "$force" ]
+  then
+    die "Configuration not supported. Use --force to try anyway."
+  fi
+
+  ARCHIVE_DIR="$OUT_DIR/archives"
+}
+
+get_tool_verinfo()
+{
+  . toolvers
+  GNU_MIRROR="https://mirrors.kernel.org/gnu"
+  BINUTILS_REPO="$GNU_MIRROR/binutils/binutils-$BINUTILS_VER.tar.bz2"
+  GCC_REPO="$GNU_MIRROR/gcc/gcc-$GCC_VER/gcc-$GCC_VER.tar.bz2"
+  GDB_REPO="$GNU_MIRROR/gdb/gdb-$GDB_VER.tar.xz"
+  GMP_REPO="$GNU_MIRROR/gmp/gmp-$GMP_VER.tar.bz2"
+  MPC_REPO="$GNU_MIRROR/mpc/mpc-$MPC_VER.tar.gz"
+  MPFR_REPO="$GNU_MIRROR/mpfr/mpfr-$MPFR_VER.tar.bz2"
+}
+
+get_component_attribute()
+{
+  local component="$1"
+  local suffix="$2"
+  local component_uppercase="$(echo "$component" \
+                             | tr '[:lower:]' '[:upper:]')"
+  local var_name="${component_uppercase}_${suffix}"
+
+  RESULT="${!var_name}"
+}
+
+# See if there are any additional tools specified with TOOLS_<arch> or
+# TOOLS_<os>
+build_tool_list()
+{
+  split_arch_os "$TARGET"
+  local target_arch="$RESULT_ARCH"
+  local target_os="$RESULT_OS"
+
+  RESULT="$TOOLS"
+  local arch_specific_var_name="TOOLS_${target_arch}"
+  if [ ! -z "${!arch_specific_var_name}" ]
+  then
+    RESULT="$RESULT ${!arch_specific_var_name}"
+  fi
+  local os_specific_var_name="TOOLS_${target_os}"
+  if [ ! -z "${!os_specific_var_name}" ]
+  then
+    RESULT="$RESULT ${!os_specific_var_name}"
+  fi
+}
+
+maybe_download_sources()
+{
+  build_tool_list
+  local all_tools="$RESULT"
+  local component
+  for component in $all_tools $HOST_LIBS
+  do
+    get_component_attribute "$component" "REPO"
+    local component_repo="$RESULT"
+    local component_tarfile="$(basename "$component_repo")"
+    get_component_attribute "$component" "VER"
+    local component_ver="$RESULT"
+    local component_source_dir="$OUT_DIR/$component-$component_ver"
+    if [ ! -f "$component_source_dir/.extracted" ]
+    then
+      # Download tarfile
+      if [ ! -f "$ARCHIVE_DIR/$component_tarfile" ]
+      then
+        echo "Fetching $component-$component_ver"
+        wget -P "$ARCHIVE_DIR" -N "$component_repo" \
+          || die "Failed to retrieve from $component_repo"
+      fi
+
+      echo "Checking $component_tarfile integrity"
+      get_component_attribute "$component" "HASH"
+      local component_hash="$RESULT"
+      [ "$(shasum -a 256 -b "$ARCHIVE_DIR/$component_tarfile" \
+           | cut -f1 -d' ')" \
+        == "$component_hash" ] || \
+        die "$component_tarfile failed integrity check"
+
+      echo "Extracting $component_tarfile"
+      mkdir -p "$OUT_DIR" || die "Unable to create directory $OUT_DIR"
+      pushd "$OUT_DIR" > /dev/null || die "Unable to change to $OUT_DIR"
+      rm -rf "$component_source_dir" \
+        || die "Failed removing $component_source_dir"
+      tar xf "$ARCHIVE_DIR/$component_tarfile" \
+        || die "Failed extracting $component_tarfile"
+
+      local patch_filename="$SCRIPT_DIR/patches/${component}-patch.txt"
+      if [ -f "$patch_filename" ]
+      then
+        echo "Patching $component"
+        local src_dir="${component}-${component_ver}"
+        patch -d "${src_dir}" -p1 < "$patch_filename" \
+          || die "Failed to patch '${src_dir}'"
+      fi
+
+      touch "${component_source_dir}/.extracted" \
+        || die "Failed to create ${component_source_dir}/.extracted"
+      popd > /dev/null || die "popd failed"
+    fi
+  done
+}
+
+get_build_dir()
+{
+  local component="$1"
+
+  get_component_attribute "$component" "VER"
+  local component_ver="$RESULT"
+  if [ "$HOST" == "$TARGET" ]
+  then
+    RESULT="$OUT_DIR/build/${component}-${component_ver}_${HOST}"
+  else
+    RESULT="$OUT_DIR/build/${component}-${component_ver}_${HOST}_to_${TARGET}"
+  fi
+}
+
+get_install_dir()
+{
+  get_component_attribute "gcc" "VER"
+  local gcc_ver="$RESULT"
+  if [ "$HOST" == "$TARGET" ]
+  then
+    RESULT="$OUT_DIR/gcc-${gcc_ver}_${HOST}"
+  else
+    RESULT="$OUT_DIR/gcc-${gcc_ver}_${HOST}_to_${TARGET}"
+  fi
+}
+
+get_host_lib_install_dir()
+{
+  local lib_name="$1"
+
+  get_component_attribute "$lib_name" "VER"
+  local lib_ver="$RESULT"
+  RESULT="$OUT_DIR/host_libs/${HOST}/${lib_name}-${lib_ver}"
+}
+
+init_config_options()
+{
+  CONFIG_OPTIONS="$(echo "$*" | sed -E -e 's/ +/ /g')"
+}
+
+add_config_options()
+{
+  local config_var_name="CONFIG_$1"
+  if [ ! -z "${!config_var_name}" ]
+  then
+    if [ -z "$CONFIG_OPTIONS" ]
+    then
+      CONFIG_OPTIONS="${!config_var_name}"
+    else
+      CONFIG_OPTIONS="${CONFIG_OPTIONS}:${!config_var_name}"
+    fi
+    CONFIG_OPTIONS=$(echo "${CONFIG_OPTIONS}" \
+                     | sed -E -e "s/[ \t\n]*:[ \t\n]*/:/g")
+  fi
+}
+
+fix_lib_configs()
+{
+  for host_lib in $HOST_LIBS
+  do
+    get_host_lib_install_dir "$host_lib"
+    local install_dir="$RESULT"
+    local escaped_dir="$(echo "$install_dir" | sed -E -e 's/\//\\\//g')"
+    CONFIG_OPTIONS="$(echo $CONFIG_OPTIONS \
+                    | sed -E -e "s/(--with-${host_lib}=)/\1${escaped_dir}/g")"
+  done
+}
+
+# Add a flag into the configuration options. If the flag is already present
+# in the configuration option string, inserts the value into the 
+add_to_flags()
+{
+  local flagname="$1"
+  local flagvalue="$2"
+
+  # This test assumes that the configuration option will not be at the start
+  # of the string.
+  if $(echo $CONFIG_OPTIONS | grep -q ":${flagname}=")
+  then
+    CONFIG_OPTIONS=$(echo $CONFIG_OPTIONS \
+                     | sed -E -e "s/${flagname}=/${flagname}=${flagvalue} /")
+  else
+    CONFIG_OPTIONS="$CONFIG_OPTIONS:${flagname}=${flagvalue}"
+  fi
+}
+
+# Add extra flags needed to build as needed for the build/host/target
+# combo (e.g., CFLAGS, CFLAGS_FOR_TARGET...)
+add_cross_build_options()
+{
+  local tool
+  local uppercase_tool
+  if [ "$BUILD" != "$HOST" ]
+  then
+    add_to_flags "CC_FOR_BUILD" "gcc"
+  fi
+
+  if [ "$BUILD" != "$TARGET" ]
+  then
+    if ! [ -z "$SYSROOT" ]
+    then
+      CONFIG_OPTIONS="$CONFIG_OPTIONS:--with-sysroot=${SYSROOT}"
+    fi
+    # Specify target-specific tools. Surprisingly, when gcc builds
+    # libgcc if these aren't specified it will use the unprefixed
+    # tools even if the prefixed tools are in the PATH.
+    local tool
+    for tool in $ALL_GNU_TOOLS
+    do
+      uppercase_tool="$(echo "$tool" | tr '[:lower:]' '[:upper:]')"
+      add_to_flags "${uppercase_tool}_FOR_TARGET" "${TARGET}-${tool}"
+    done
+    add_to_flags "CC_FOR_TARGET" "${TARGET}-gcc"
+    add_to_flags "CXX_FOR_TARGET" "${TARGET}-g++"
+  fi
+}
+
+# Build the configuration options for a specific module, including relevant
+# environment variables of the forms:
+#   CONFIG_<module>
+#   CONFIG_<module>_<arch>
+#   CONFIG_<module>_<os>
+#   CONFIG_<module>_<arch>_<os>
+#   CONFIG_<module>_TOOLS_<host-tools>
+build_config_options()
+{
+  local component="$1"
+  local is_host_lib="$2"
+
+  local uppercase_component="$(echo "$component" \
+                               | tr '[:lower:]' '[:upper:]')"
+
+  split_arch_os "$TARGET"
+  local target_arch="$RESULT_ARCH"
+  local target_os="$RESULT_OS"
+
+  init_config_options "--prefix=$install_dir"
+  if [ "$HOST" != "$BUILD" ]
+  then
+    canonicalize_target_name "$HOST"
+    CONFIG_OPTIONS="$CONFIG_OPTIONS:--host=${RESULT}"
+  fi
+  if [ "$TARGET" != "$HOST" ] && [ "$is_host_lib" == '0' ]
+  then
+    canonicalize_target_name "$TARGET"
+    CONFIG_OPTIONS="$CONFIG_OPTIONS:--target=${RESULT}"
+  fi
+  add_config_options "$uppercase_component"
+  add_config_options "${uppercase_component}_${target_arch}"
+  add_config_options "${uppercase_component}_${target_os}"
+  add_config_options "${uppercase_component}_${target_arch}_${target_os}"
+
+  # Apply options that are specific to the host compiler
+  if [ ! -z "$HOST_TOOLS" ]
+  then
+    add_config_options "${uppercase_component}_TOOLS_${HOST_TOOLS}"
+  fi
+
+  # Fix references to libraries (gmp, etc.)
+  fix_lib_configs
+
+  # Indicate location of sysroot
+  if [ "$is_host_lib" == "0" ] # || [ "$BUILD" != "$HOST" ]
+  then
+    add_cross_build_options
+  fi
+}
+
+canonicalize_target_name()
+{
+  RESULT="$(echo "$1" \
+            | sed -E -e 's/arm-none/arm-eabi/' \
+            | sed -E -e 's/([a-zA-Z0-9_]+)-none/\1-elf/')"
+}
+
+do_configure()
+{
+  build_config_options "$component" "$is_host_lib"
+  (IFS=':' && "$OUT_DIR/${component}-${component_ver}/configure" \
+              $CONFIG_OPTIONS)
+  if [ "$?" != '0' ]
+  then
+    die "Failed to configure $component"
+  fi
+}
+
+build_component()
+{
+  local component="$1"
+  local is_host_lib="$2"
+
+  get_component_attribute "$component" "VER"
+  local component_ver="$RESULT"
+  get_build_dir "$component"
+  local build_dir="$RESULT"
+  if [ "$is_host_lib" == "0" ]
+  then
+    get_install_dir
+  else
+    get_host_lib_install_dir "$component"
+  fi
+  local install_dir="$RESULT"
+  if [ ! -f $build_dir/built.txt ]
+  then
+    mkdir -p "$build_dir" || die "Unable to create directory $build_dir"
+    pushd "$build_dir" > /dev/null || die "Unable to pushd $build_dir"
+    echo "Configuring $component"
+    do_configure
+    echo "Building $component"
+    make -j ${NUM_PARALLEL_BUILDS} || die "Failed to build $component"
+    echo "Installing $component"
+    make -j ${NUM_PARALLEL_BUILDS} install || die "Failed to install $component"
+    touch built.txt || die "Failed to create $build_dir/built.txt"
+    popd > /dev/null || die "Unable to popd"
+  fi
+}
+
+build_components()
+{
+  maybe_download_sources
+  get_install_dir
+  local install_dir="$RESULT"
+
+  # Mucking with the path makes configuration significantly easier.
+  if [ "$BUILD" == "$HOST" ]
+  then
+    export PATH="$PATH:${install_dir}/bin"
+  fi
+
+  local lib
+  for lib in $HOST_LIBS
+  do
+    build_component $lib 1
+  done
+  build_tool_list
+  local all_tools="$RESULT"
+  for tool in $all_tools
+  do
+    build_component $tool 0
+  done
+
+  if [ -n "$STRIP" ]
+  then
+    if [ "$BUILD" == "$HOST" ]
+    then
+      local strip_util="strip"
+    else
+      local strip_util="${HOST}-strip"
+    fi
+    local filename
+    for filename in $(find "${install_dir}/bin" -type f) \
+                    $(find "${install_dir}/libexec" -type f)
+    do
+      (file "${filename}" | grep -q ELF) && ${strip_util} "${filename}"
+    done
+  fi
+}
+
+gather_host_info
+process_opts $*
+get_tool_verinfo
+if [ "$BUILD" != "$HOST" ]
+then
+  # First, build a BUILD->HOST cross-compiler
+  ORIG_TARGET="$TARGET"
+  TARGET="$HOST"
+  HOST="$BUILD"
+  build_components
+  HOST="$TARGET"
+  TARGET="$ORIG_TARGET"
+fi
+build_components
+