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
+