#! /usr/bin/env bash

########################################################################
#
# File:   gnu-release
# Author: Mark Mitchell
# Date:   2004-08-13
#
# Contents:
#   Script to build CodeSourcery GNU Toolchain releases.
#
########################################################################

########################################################################
# License
########################################################################

# Copyright (C) 2005, CodeSourcery LLC
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
# 02110-1301, USA.

########################################################################
# Notes
########################################################################

# The documentation for this script is maintained at:
#
#   https://intranet.codesourcery.com/GNUToolchainReleaseScript
#
# Please update that page when making changes to this script. 

########################################################################
# Initialization
########################################################################

# All errors are fatal.
set -e

# Make sure that any attempts to read from standard input do not go to
# the terminal; otherwise, TeX may fail to terminate when confronted
# with invalid input.
exec < /dev/null

# Check that $CSL_SCRIPTDIR has been initialized.
test -n "$CSL_SCRIPTDIR" 

########################################################################
# Subroutines
########################################################################

# Load common subroutines.
. ${CSL_SCRIPTDIR}/gnu-common

# Issue usage information and exit.

usage() {
    error "gnu-release [-n] [-s srcdir] [-o objdir] [-p pkgdir] [-i installdir] [-l logdir] [-T testlogdir] [-t] [-m] config-file"
}

# Print the path to use for a log file for building $1.

log_path() {
    local log=$1
    echo "$logdir/$log-$full_version".log
}

# Print the path to use for a build directory for the tool $1.

objdir_path() {
    local tool=$1
    echo "$objdir/$tool-$full_version"
}

# Copy the directory given by "$1" to "$2".

copy_dir() {
    mkdir -p "$2"
    # Use tar, rather than cp -r, so that symlinks are preserved.
    (cd "$1" && tar cf - .) | (cd $2 && tar xf -)
}

# Save the config.status file from the objdir for the tool $1, if
# requested.  This functionality is required by the journeyman nightly
# builds.

save_config_status() {
    local tool="$1"
    if [ "$testlogdir" ] && [ $build == $host ]; then
	# The subdirectory into which config.status should match
	# the subdirectory in which gnu-test (when invoked with "-t")
	# will place the logs for $tool.
	local subdir="$testlogdir/test_${tool}_dg"
	mkdir -p "$subdir"
	cp $(objdir_path "$tool")/config.status "$subdir"
    fi
}

# Remove detritus from previous builds.

remove_previous_builds() {
    # Remove previous installations.
    rm -rf "$prefix"/*

    # Remove previous build directories.
    rm -rf $objdir/*-$full_version

    # Remove log files.
    rm -rf "$build_summary" \
	${logdir}/*-$full_version.{log,sum}
}

# Build a source package "$1" containing the files in "$2", which is
# the root of the source tree.

build_source_package() {
    if $build_source_packages; then
	source_package=$1

	# If the source directory is a symbolic link, then tar will
	# create an archive containing only the symbolic link.  Using
	# the "-h" option to tar will dereference all symbolic links
	# in the source directory, which is not desirable either.  So,
	# we require that the source directory be an actual file.
	if test -h "$2"; then
	    error "source directory cannot be a symbolic link"
	fi

	# Remove any previous versions of the source package.
	rm -f "$source_package"
	pushd $(dirname $2) > /dev/null
	tar cf "$1" --bzip2 $tar_opts \
	    --exclude=CVS --exclude=.svn \
	    --exclude="*~" --exclude=".#*" \
	    --exclude="*.orig" --exclude="*.rej" \
	    `basename $2`
	popd > /dev/null
	source_packages="$source_packages $source_package"
    fi
}

# Build a package using the usual "configure; make; make install"
# method.  $1 is the human-readable name of the package; $2 is the
# prefix used to name variables that apply to the package.  Remaining
# arguments are passed to the package "configure" script.

build_standard_package() {
    local package="$1"
    local pkg_prefix="$2"
    shift 2
 
    local src_package=$(eval echo "\$${pkg_prefix}_src_package")
    local pkg_srcdir=$(eval echo "\$${pkg_prefix}_srcdir")

    inform "Building $package..." 
	
    build_source_package "$src_package" "$pkg_srcdir"
    
    local pkg_objdir=$(objdir_path "$pkg_prefix")
    rm -rf "$pkg_objdir"
    execute mkdir -p "$pkg_objdir"
    execute_pushd "$pkg_objdir"
    local saved_PATH="$PATH"
    export PATH="$build_tool_path:$PATH"
    execute "$pkg_srcdir"/configure \
	--build="$build" --host="$host" --target="$target" \
	--prefix="$installdir" \
	"$@"
    execute make -j"${parallelism}"
    execute make prefix="$prefix" install
    PATH="$saved_PATH"
    execute_popd

    save_config_status "$pkg_prefix"

    inform "$package build complete."
}

# Build and install GNU Binutils.

build_binutils () {
    local sysroot_opt=
    if [ "$sysroot" ]; then
	sysroot_opt="--with-sysroot=$sysroot"
    fi
    build_standard_package "GNU Binutils" "binutils" \
	$nls_configure_opts $sysroot_opt "$@"
    pretidy_installation

    # Make the installation look like native if required.
    if $binutils_force_native; then
	pushd "$prefix/bin" > /dev/null
	local f
	local nf
	local tpf="$target-"
	for f in ${tpf}*; do
	    nf=${f#$tpf}
	    if [ ! -f "$nf" ]; then
		ln "$f" "$nf"
	    fi
	done
	popd > /dev/null
    fi
}

# Build elf2flt.

build_elf2flt () {
    if [ "$elf2flt_version" ]; then
	local elf2flt_build_log=$(log_path "elf2flt")
	build_standard_package "ELF2FLT" "elf2flt" \
	    $nls_configure_opts $elf2flt_configure_opts \
	    --with-bfd-include-dir=$prefix/$host/$target/include \
	    --with-libbfd=$prefix/$host/$target/lib/libbfd.a \
	    --with-libiberty=$prefix/lib/libiberty.a \
	    >> "$elf2flt_build_log" 2>&1
    fi
}

# Build and install the GNU Compiler Collection.

build_gcc () {
    inform "Building GNU Compiler Collection..."

    local gcc_configure_opts=" \
       --build="$build" --host="$host" --target="$target" \
       $*"
    if [ "$sysroot" ]; then
	gcc_configure_opts="$gcc_configure_opts \
	  --with-sysroot=$sysroot"
    fi
    if [ "$build_sysroot" ]; then 
	gcc_configure_opts="$gcc_configure_opts \
	  --with-build-sysroot=$prefix/$build_sysroot"
        # Unfortunately, --with-build-sysroot does not (yet?) set 
        # CPPFLAGS_FOR_TARGET or LDFLAGS_FOR_TARGET automatically, so we do 
	# it manually here.
	gcc_make_opts="$gcc_make_opts \
	  LDFLAGS_FOR_TARGET=--sysroot=$prefix/$build_sysroot \
          CPPFLAGS_FOR_TARGET=--sysroot=$prefix/$build_sysroot \
          build_tooldir=$prefix/$target"
    fi

    local build_target=""
    local install_target="install"
    # When building a Canadian cross compiler, build only the
    # compilers.  The target libraries will be copied from the
    # original build.
    if [ "$host" != "$build" ]; then
	build_target="all-gcc"
	install_target="install-gcc"
    fi

    local gcc_objdir=$(objdir_path "gcc")
    rm -rf "$gcc_objdir"
    execute mkdir -p "$gcc_objdir/gcc"

    # The compiler will look for the assembler as "as", even when
    # building a cross toolchain.  It will search an appropriate
    # subdirectory of $installdir, but we have actually installed the
    # tools in $prefix.  Thus, the compiler will not find the
    # assembler and linker when building the runtime libraries.
    # Because the compiler uses -B./ when building these libraries, we
    # can work around this issue by placing a symbolic link in the GCC
    # build directory.  However, we only want the link when using the
    # GNU assembler.  We also don't need the link when building a
    # native toolchain, because we put $build_tool_path in the PATH, 
    # and the assembler will be named "as" in that case.  Precisely
    # the same considerations apply to the linker. 
    if [ "$host" != "$target" ]; then 
	local tool
	for tool in as ld; do
	    # If the configure options do not include
            # --with-gnu-$tool, or includes a specific path, then we 
	    # do not need the link.
	    if ! expr match "$gcc_configure_opts" \
                   ".*--with-gnu-$tool.*" > /dev/null \
		|| expr match "$gcc_configure_opts" \
                   ".*--with-$tool=.*" > /dev/null; then
		continue
	    fi
	    ln -s "$build_tool_path/$target-$tool" "$gcc_objdir/gcc/$tool"
	done
    fi

    # Build the compiler.  We put the binary utilities in the PATH so 
    # that the compiler can find them.
    local saved_PATH="$PATH"
    export PATH="$build_tool_path:$PATH"
    local saved_CC="$CC"
    if [ -n "$gcc_cc" ]; then
	export CC="$gcc_cc"
    fi
    execute_pushd "$gcc_objdir"
    execute "$gcc_srcdir"/configure $nls_configure_opts $gcc_configure_opts
    execute make -j"${parallelism}" $gcc_make_opts $build_target
    execute make prefix="$prefix" $install_target
    execute_popd
    CC="$saved_CC"
    PATH="$saved_PATH"

    pretidy_installation

    if [ "$host" != "$build" ]; then 
        # Copy libraries that were built previously.
	copy_libs \
            "lib/gcc/$target" \
	    "$target/lib" \
	    "include/c++"
    fi

    save_config_status "gcc"

    inform "GNU Compiler Collection build complete."
}

# Build and install the GNU Debugger.

build_gdb () {
    if [ "$gdb_version" ]; then
	local gdb_build_log=$(log_path "gdb")
	build_standard_package "GDB" "gdb" \
	    $nls_configure_opts $gdb_configure_opts >> "$gdb_build_log" 2>&1
    fi
}

# Build and install GNU Make, on Windows only.

build_make () {
    if [ "$host" = i686-mingw32 ] && [ "$make_version" ]; then
	local make_build_log=$(log_path "make")
	build_standard_package "Make" "make" \
	    $nls_configure_opts $make_configure_opts >> "$make_build_log" 2>&1
    fi
}

# Build and install the GDB-to-RDI stub.

build_rdi_stub () {
    if [ "$rdi_stub_version" ]; then
	local rdi_stub_build_log=$(log_path "rdi-stub")
	build_standard_package "RDI stub" "rdi_stub" \
	    $rdi_stub_configure_opts >> "$rdi_stub_build_log" 2>&1
    fi
}

# Build RedBoot.

build_redboot () {
    inform "Building RedBoot..."
    build_source_package "$redboot_src_package" "$redboot_srcdir"
    local redboot_objdir=$(objdir_path "redboot")
    mkdir -p "$redboot_objdir"
    execute_pushd "$redboot_objdir"
    export ECOS_REPOSITORY="$redboot_srcdir"
    ecosconfig new "$redboot_board" redboot
    ecosconfig import "$redboot_srcdir/hal/$redboot_arch/$redboot_board/$redboot_hal_version/misc/$redboot_cfg.ecm"
    ecosconfig tree
    local saved_PATH="$PATH"
    export PATH="$build_tool_path:$PATH"
    make -j"${parallelism}"
    PATH="$saved_PATH"
    mkdir -p "$prefix/lib/redboot"
    cp install/bin/redboot.* "$prefix/lib/redboot"
    execute_popd
    inform "RedBoot build complete."
}

# Generate an Eclipse plug-in for this toolchain.  The plug-in is
# generated as a ZIP file since that is the format conventionally used
# for distributing Eclipse plug-ins.

build_eclipse_plugin () {
    local eclipse_objdir
    local plugin_name

    inform "Building Eclipse plug-in..."    
    plugin_name=$(eclipse_plugin_name)
    eclipse_objdir="$objdir/$plugin_name"
    rm -rf "$eclipse_objdir"
    execute mkdir -p "$eclipse_objdir"
    execute "$CSL_SCRIPTDIR/gnu-eclipse" "$release_config" \
        > "$eclipse_objdir/plugin.xml"
    execute_pushd "$objdir"
    execute mkdir -p "$prefix/share"
    execute zip -q -r "$prefix/share/$plugin_name".zip "$plugin_name"
    execute_popd
    inform "Eclipse plug-in build complete."
}
 
# Install the Windows runtime libraries.

unpack_windows_runtime () {
    if [ "$mingw_version" ]; then
	build_source_package "$mingw_src_package" "$mingw_srcdir"
	copy_dir "$mingw_srcdir" "$prefix/$target"
    fi
    if [ "$w32api_version" ]; then
	build_source_package "$w32api_src_package" "$w32api_srcdir"
	copy_dir "$w32api_srcdir" "$prefix/$target"
    fi
    # The native MinGW configuration expects the headers in
    # /mingw/include.
    if [ "$mingw_version" ] || [ "$w32api_version" ]; then
	mkdir -p "$prefix/$target/mingw"
	mv "$prefix/$target/include" "$prefix/$target/mingw/include"
    fi
}

# Remove $remove_files from the installation.  This is done separately
# from tidy_installation because some of these files may be broken and
# cause problems for the rest of the build.

pretidy_installation () {
    pushd $prefix > /dev/null
    for f in $remove_files; do
	rm -f $f
    done
    popd > /dev/null
}

# Perform post-installation cleanups.

tidy_installation () {
    inform "Tidying installation..."

    pretidy_installation
    # Libtool archives contain paths that do not get relocated
    # appropriately.  In general, anyone trying to use these archives
    # is more likely to be confused than to have success, so we remove them. 
    find ${prefix} -name '*.la' -exec rm {} \;

    # Strip binaries.

    # The strip program. 
    local host_strip=$strip
    # When building a Canadian cross, we must use the strip program
    # for host binaries.
    if [ $host != $build ]; then
	host_strip="$host-strip"
    fi

    # The actual compiler binaries (cc1, etc.) are in libexec/gcc when
    # using GCC 3.4.x, but are in lib/gcc-lib in older versions of the 
    # toolchain.
    for x in \
	${prefix}/bin/* \
	${prefix}/$target/bin/* \
	${prefix}/lib/gcc-lib/$target/$gcc_version/{cc1,cc1plus,collect2} \
	${prefix}/libexec/gcc/$target/$gcc_version/{cc1,cc1plus,collect2}; do
	if [ -x $x ]; then 
	    if ! file $x |egrep 'script|text' >/dev/null; then
		$host_strip $x
	    fi
	fi
    done
    
    inform "Installation tidying complete."
}

# We have already done a standard installation of Binutils and GCC.
# Perform post-installation steps.
 
build_package () {
    inform "Building package ..."

    tidy_installation

    # Create the actual binary package.
    rm -f "$package"
    local tar_opts="$tar_opts"
    # Add any host-specific options.
    case "$host" in
	# Resolve symbolic links in Windows packages.  MinGW tar
	# cannot unpack symbolic links at all.  Cygwin tar creates
	# Windows shortcuts, which are not recognized by MinGW
	# programs as links.
	*-mingw32)
	    tar_opts="$tar_opts -h"
	    ;;
    esac
    pushd $prefix > /dev/null
    tar cjf "$package" $tar_opts .
    popd > /dev/null

    inform "Package build complete."
}

# Create the ${uninstall_script}.

generate_uninstall_script() {
    inform "Generating uninstall script..."
    
    (cat | \
	sed -e 's|${target}|'"$target"'|g' \
            -e 's|${version}|'"$version"'|g' \
	> "$uninstall_script") << 'EOF'
#! /bin/sh

########################################################################
#
# NAME
#
#    ${target}-uninstall
#
# SYNOPSIS
#
#    ${target}-uninstall DIRECTORY
#
# DESCRIPTION
#
#    Uninstall a CodeSourcery GNU Toolchain release.  DIRECTORY is the
#    path to the "codesourcery" directory in which the toolchain has
#    been installed.  For example, if you installed the GNU Toolchain
#    release in "/opt/codesourcery", then you should invoke this 
#    script as:
#
#      ${target}-uinstall /opt/codesourcery
#
########################################################################

########################################################################
# Subroutines
########################################################################

# Issue an error message and exit.

error () {
    echo "error: $@" >& 2
    exit 1
}

########################################################################
# Main Program
########################################################################

# Any error in this script is fatal.
set -e

# Make sure that this script was invoked appropriately.
if test $# -ne 1; then
    error "should be inovked as ${target}-uninstall DIRECTORY"
fi

# Make sure that the user really wants to remove the installation.
ok=""
while test "$ok" != "y"; do
  echo "About to remove CodeSourcery GNU Toolchain ${version} for ${target}."
  echo "Proceed (y/n)?"
  read ok
  # If the user does not want to proceed, exit immediately.
  if test "$ok" = "n"; then
      exit 0
  fi
done

# The root of the installation.
install_dir="$1/gnu-${version}"

# The directory in which installation manifests are placed.
manifests_dir="$install_dir/share/manifests"

# If the manifests directory does not exist, this script was
# misinvoked.
if test ! -d "$manifests_dir"; then
    error "$manifests_dir does not exist"
fi

# The manifest for this release.
manifest="$manifests_dir/${target}.mft"

# If the manifest file does not exist, this script was misinvoked.
if test ! -f "$manifest"; then
    error "$manifest does not exist"
fi

# Concatenate all of the manifest files.
other_files=`mktemp /tmp/${target}-uninstall-all.XXXXXX`
target_files=`mktemp /tmp/${target}-uninstall-target.XXXXXX`
for f in "$manifests_dir"/*.mft; do
    if test "$f" != "$manifest"; then
	cat "$f"
    fi
done | awk '{ print $2; }' | sort | uniq > "$other_files"
cat "$manifest" | awk '{ print $2; }' | sort > "$target_files"

# Remove only those files that are specific to this target.
comm -2 -3 "$target_files" "$other_files" | \
    while read f; do
      f="$install_dir/$f"
      if test ! -d "$f"; then
	  rm "$f"
       fi
    done

# Remove the temporary files.
rm "$other_files" "$target_files"

# Remove any directories that are empty.
find "$install_dir" -depth -type d -print | \
    while read d; do
      # This command will fail for directories that are not empty.
      rmdir $d 2>/dev/null || true
    done

# All done.
echo "${target}-uninstall: Uninstallation complete."
exit 0

EOF
    if [ "$?" -ne 0 ]; then 
	return 1
    fi
    chmod +x "$uninstall_script"
    inform "Uninstall script generation complete."
}

# Produce a description of this build.

generate_build_summary () {
    inform "Generating build summary..."

cat > "$build_summary" <<EOF
Version Information
===================

Version:           $version
Host(s):           $hosts
Target:            $target

Build Information
=================

Build date:             $build_date
Build machine:          $build_machine
Build operating system: $build_operating_system
Build uname:            $build_uname
Build user:             $build_user

EOF

    inform "Build summary generation complete..."
}

# Create the manifest file.

generate_manifest() {
    inform "Generating manifest..."

    mkdir -p "$manifests_dir"
    # Because the shell will create the manifest before the loop below
    # begins, it will appear in the manifest -- but the checksum will
    # be incorrect.  There is no way to get a correct checksum for the
    # manifest file, since the checksum depends on the value of the
    # checksum.  So, we ignore those inaccuracies.
    pushd "$prefix" > /dev/null
    find . -type f -o -type l | xargs md5sum $f > "$manifest"
    popd > /dev/null

    inform "Manifest generation complete."
}

# Build the package containing information necessary to rebuild this
# release.

build_backup_package() {
    inform "Building backup package..."

    for file in $build_summary $source_packages; do
	if [ -f "$file" ]; then
	    cp "$file" "$backupdir"
	fi
    done

    # Copy all of the release-script machinery into a "scripts"
    # directory, so that, after unpacking the backup package, users
    # can set CSL_SCRIPTDIR to the "scripts" directory.
    local backup_scriptdir="$backupdir/scripts"
    rm -rf "$backup_scriptdir"
    mkdir "$backup_scriptdir"
    local scripts="$release_script \
	${CSL_SCRIPTDIR}/gnu-common \
	${CSL_SCRIPTDIR}/gnu-test \
        ${CSL_SCRIPTDIR}/COPYING \
        $release_config_includes"
    local script
    for script in $scripts; do
	local script_basename=$(echo "$script" | sed -e "s|$CSL_SCRIPTDIR/||g")
	mkdir -p "$backup_scriptdir"/$(dirname "$script_basename")
	cp "$script" "$backup_scriptdir/$script_basename"
    done

    # Create the final package.
    pushd $(dirname "$backup_package") > /dev/null
    tar cjf $(basename "$backup_package") $(basename "$backupdir")
    popd > /dev/null

    inform "Backup package complete."
}

# Build packages for Binutils and GCC. 

package_binutils_gcc () {
    local binutils_build_log=$(log_path "binutils")
    local gcc_build_log=$(log_path "gcc")
    local sysroot=

    if [ "$build_sysroot" ]; then
	sysroot="$installdir/$build_sysroot"
    elif [ "$cross_sysroot" ] && [ "$target" != "$host" ]; then
	sysroot="$cross_sysroot"
    fi

    build_binutils $binutils_configure_opts > "$binutils_build_log" 2>&1
    build_source_package "$gcc_src_package" "$gcc_srcdir"
    build_gcc $gcc_configure_opts >> "$gcc_build_log" 2>&1
    build_package
}

# Print a series of lines describing the multilibs that should be used
# when building GLIBC, in the same format used by "gcc
# -print-multi-lib".  This function can be overridden by the release
# configuration file.

print_glibc_multilibs () {
    # By default there are no multilibs.
    cat <<EOF
.;
EOF
}

# Install the GLIBC header files.  $1 is the subdirectory of the
# installation root in which the GLIBC headers should be placed.  The
# remaining command-line arguments are the command-line options that
# should be provided to GCC when building GLIBC.  This function also
# (a) builds and installs the crt*.o files provided by GLIBC, and (b)
# creates a dummy version of libc.so that GCC can link against when
# creating shared versions of libgcc.

install_glibc_headers () {
    local subdir="/$1"
    shift
    local cflags="$@"
    local glibc_objdir="$glibc_objdir$subdir/"
    local glibc_sysroot="$prefix/$build_sysroot$subdir"
    local glibc_cflags="$glibc_cflags $cflags"

    mkdir -p \
        "$glibc_sysroot/lib" \
        "$glibc_sysroot/usr/lib"

    rm -rf "$glibc_objdir"
    mkdir -p "$glibc_objdir"
    execute_pushd "$glibc_objdir"

    # The GLIBC configure script tries to link programs to determine
    # whether or not forced unwinding is available.  However, the
    # compiler build above cannot yet link programs, since there is
    # no C library installed.  This dirty trick keeps configure from
    # trying to run these tests.
    local config_cache="$(pwd)/config.cache"
    cat > "$config_cache" <<EOF
libc_cv_forced_unwind=yes
libc_cv_c_cleanup=yes
EOF

    # Make sure GLIBC knows where to find the host and target tools.   
    # PERL should be set to a value appropriate to the target, not just
    # for the build system, as it is encoded in the mtrace script.
    BUILD_CC=gcc \
    CC="${target_prefix}gcc $glibc_cflags" \
    AR="${target_prefix}ar" \
    RANLIB="${target_prefix}ranlib" \
    PERL=/usr/bin/perl \
    execute "$glibc_srcdir"/configure \
       --prefix=/usr \
       --with-headers="$usr_include" \
       --build="$host" \
       --host="$target" \
       --cache-file="$config_cache" \
       $glibc_configure_opts
    # Install header files.
    execute make install_root="$glibc_sysroot" install-headers
    # The <bits/stdio_lim.h> and <gnu/stubs.h> headers are not
    # installed by install-headers.  An empty version of the latter is
    # OK for now.
    execute cp bits/stdio_lim.h "$usr_include"/bits/stdio_lim.h
    execute cp /dev/null "$usr_include"/gnu/stubs.h
    # Build and install crt*.o.
    execute make csu/subdir_lib
    execute cp csu/crt[1in].o "$glibc_sysroot"/lib
    execute_popd

    # Build a dummy version of libc.so so that libgcc.so can link against
    # it.
    "${target_prefix}"gcc $cflags \
	-o "$glibc_sysroot/usr/lib/libc.so" \
	-nostdlib -nostartfiles -shared -x c \
	/dev/null
}

# Copy the libraries built when building the ordinary cross compiler
# into this toolchains.  (This function is used when building a
# Canadian cross or Canadian native compiler.)

copy_libs () {
    local src
    for src in "$@"; do
	# Normally, we just copy $src to the same place in the 
	# target toolchain.
	local dest=$src
	# As a special case, libraries placed in $target/lib/ on a 
	# cross compiler go into lib/ in a native compiler.
	if [ "$host" = "$target" ] && [ "$src" = "$target/lib" ]; then
	    dest=lib
	fi
	copy_dir "$objdir/$build/$src" "$prefix/$dest"
	# But, as a special case on top of the special case, the
	# ldscripts/ directory always goes in $target/lib/.  It will
	# already have been put there when we installed binutils,
	# so all we need to do is remove it from lib/.
	if [ "$host" = "$target" ] && [ "$src" = "$target/lib" ]; then
	    rm -rf "$prefix/lib/ldscripts"
	fi
    done
}

# Build and install GLIBC in the $1 subdirectory.

build_glibc () {
    local subdir="/$1"
    local glibc_objdir="$glibc_objdir$subdir/"
    local glibc_sysroot="$prefix/$build_sysroot$subdir"

    execute_pushd $glibc_objdir
    execute make -j${parallelism}
    execute make install_root="$glibc_sysroot" install
    # The pt_chown program is only required for kernels that are not
    # configured to use /dev/pts.  All modern kernels can use
    # /dev/pts, and it is better to use that technique, so we believe
    # that shipping pt_chown is unncessary.  Furthermore, since it is
    # supposed to be an suid binary, it's confusing to ship it
    # not-suid.  Shipping it suid does not work if the the user
    # unpacking the file is not root.  So, we just remove the file.
    execute rm -f $glibc_sysroot/usr/libexec/pt_chown
    execute_popd
}

# When this function is called, $prefix/$target/libc contains the C
# library.  Make changes necessary to permit that directory to be
# directly mounted on a target system.

finalize_libc_installation () {
    local libdir="$prefix/$target/lib"
    local sysroot="$prefix/$target/libc"

    local subdir
    print_glibc_multilibs | while read multilib; do
	subdir=$(echo "$multilib" | sed -e 's|;.*||g')
	# If this is not the default multilib, remove usr/include; we
	# only need one set of headers.
	if [ "$subdir" != "." ]; then
	    rm -rf "$sysroot/$subdir/usr/include"
	fi
	# Copy libgcc into place.  In older versions of GCC, all 
	# multilibs of libgcc were installed in $target/lib and
	# had distinguishing names.  In newer versions, they are 
	# installed in appropriate subdirectories.
	for f in "$libdir"/libgcc*.so*; do
	    if [ "$f" != "$libdir/libgcc*.so*" ]; then
		cp -d $f "$sysroot/$subdir/lib/"
	    fi
	done
	# pthread_cancel expects the library to be libgcc_s.so.1, so
	# replace this by an appropriate link for non-default
	# multilibs.
	if [ "$subdir" != "." ]; then
	    sublibgcc=libgcc_s_$(echo "$subdir" | sed -e 's,/,_,g').so.1
	    if [ -e "$sysroot/$subdir/lib/$sublibgcc" ]; then
		rm -f "$sysroot/$subdir/lib/libgcc_s.so.1"
		ln -s "$sublibgcc" "$sysroot/$subdir/lib/libgcc_s.so.1"
	    fi
	fi
	# It is OK if there are no files in the multilib subdir.
	for f in "$libdir/$subdir"/libgcc*.so*; do
	    if [ -e "$f" ]; then
		cp -d "$f" "$sysroot/$subdir/lib/"
	    fi
	done
	# Copy libstdc++ into place.
	for f in "$libdir/$subdir"/libstdc++.so*; do
	    if [ "$f" != "$libdir/$subdir/libstdc++.so*" ]; then
		cp -d $f "$sysroot/$subdir/usr/lib/"
	    fi
	done
    done
}

# Build packages for Binutils, GCC, and GLIBC.

package_glibc_toolchain () {
    local sysroot="$installdir/$target/libc"
    local build_sysroot="$target/libc"
    local usr_include="$prefix/$build_sysroot/usr/include"

    # Build the binary utilities.
    local binutils_build_log=$(log_path "binutils")
    build_binutils $binutils_configure_opts \
	 > "$binutils_build_log" 2>&1

    # Build the GCC and Linux kernel source packages.
    build_source_package "$gcc_src_package" "$gcc_srcdir"
    build_source_package "$linux_src_package" "$linux_srcdir"

    # Add additional options appropriate for GLIBC.  
    local glibc_gcc_configure_opts="$gcc_configure_opts \
	--enable-symvers=gnu --enable-__cxa_atexit"
    local gcc_build_log=$(log_path "gcc")

    # Some platforms support NPTL, while others do not.
    local glibc_gcc_thread_configure_opts=""
    if $enable_nptl; then
	glibc_gcc_thread_configure_opts="--enable-threads"
    fi

    if [ "$host" != "$build" ]; then 
	# Copy the libraries we have already built.  
	copy_libs \
            "$target/lib" \
            "$target/libc" \
            "include/c++" \
            "lib/gcc/$target"
    else
	build_source_package "$glibc_src_package" "$glibc_srcdir"

	local glibc_build_log=$(log_path "glibc")

	# Create the sysroot directory.
	mkdir -p "$usr_include" "$usr_include/bits" "$usr_include/gnu" \
	    >> "$glibc_build_log" 2>&1

        install_kernel_headers "$usr_include" >> "$glibc_build_log" 2>&1

	# Build the C compiler so that we can use it to build crt*.o.
	# We use --disable-shared as building a shared object requires
	# crt*.o, which do not yet exist.  We use --disable-threads because
	# threads do not work without shared libraries.  We use
	# --without-headers for obvious reasons.  The use of --with-newlib,
	# however, is a hack.  GCC 3.4.3's configure script does not honor 
	# --without-headers by itself.  (That is a bug.)
	build_gcc \
	    $glibc_gcc_configure_opts \
	    --disable-shared --disable-threads --disable-libssp \
	    --without-headers --with-newlib \
	    --enable-languages=c \
	    >> "$gcc_build_log" 2>&1

	local glibc_objdir=$(objdir_path "glibc")

	# The GLIBC configure script assumes that assert.h will be
	# installed by GCC, which is not the case.  (It appears that 
	# this is a bug in autoconf.) This ugly hack works around the
	# problem.
	touch "$usr_include/assert.h"

	# Install enough of GLIBC to build a compiler that can support
	# TLS.
	local subdir
	local cflags
	local multilib
	print_glibc_multilibs | while read multilib; do
	    subdir=$(echo "$multilib" | sed -e 's|;.*||g')
	    cflags=$(echo "$multilib" | sed -e 's|.*;||g' -e 's|@| -|g')
	    install_glibc_headers "$subdir" "$cflags" \
		>> $glibc_build_log 2>&1
	done
	check_status

	# Now build a compiler with a shared libgcc and with support for 
	# threads.  That will allow us to build GLIBC with NPTL
	# support.
	build_gcc \
	    $glibc_gcc_configure_opts \
	    $glibc_gcc_thread_configure_opts \
	    --enable-shared \
	    --enable-languages=c \
	    >> "$gcc_build_log" 2>&1

	# Build the C library itself.
	inform "Building C library..."
	print_glibc_multilibs | while read multilib; do
	    subdir=$(echo "$multilib" | sed -e 's|;.*||g')
	    build_glibc "$subdir" >> "$glibc_build_log" 2>&1
	done
	check_status
    fi

    # Now build the final version of the compiler, using whatever 
    # languages were requested for this configuration.
    build_gcc \
	$glibc_gcc_configure_opts \
	$glibc_gcc_thread_configure_opts \
	--enable-shared \
	>> "$gcc_build_log" 2>&1

    # Now that everything has been built, do any post-processing
    # required on the GLIBC installation. 
    finalize_libc_installation

    build_package
}

# Install the uClibc header files.  $1 is the subdirectory of the
# installation root in which the uClibc headers should be placed.  The
# remaining command-line arguments are the command-line options that
# should be provided to GCC when building uClibc.
# TODO: Do we also need to build/install crt* and a dummy libc.so?

install_uclibc_headers () {
    local subdir="/$1"
    shift
    local cflags="$@"
    local uclibc_objdir="$uclibc_objdir$subdir/"
    local uclibc_sysroot="$prefix/$build_sysroot$subdir"
    local uclibc_cflags="$uclibc_cflags $cflags"

    mkdir -p \
        "$uclibc_sysroot/lib" \
        "$uclibc_sysroot/usr/lib"

    rm -rf "$uclibc_objdir"
    mkdir -p "$uclibc_objdir"
    execute_pushd "$uclibc_objdir"

    # uClibc doesn't currently build with srcdir != objdir.  To hack round 
    # this copy the contents of srcdir to objdir.
    (cd $uclibc_srcdir; tar cf - .) | tar xf -

    # Create the .config
    {
      echo "KERNEL_SOURCE=\"$prefix/$build_sysroot/usr\""
      echo "CROSS_COMPILER_PREFIX=\"${target_prefix}\""
      cat ${CSL_SCRIPTDIR}/release-configs/${uclibc_config_file} \
	| sed -e '/^KERNEL_SOURCE=/d;/^CROSS_COMPILER_PREFIX=/d'
    } > .config
    # Fill in any holes in the .config
    yes "" | make oldconfig
    #  Install the header files
    execute make ARCH_CFLAGS="$uclibc_cflags" headers
    execute make ARCH_CFLAGS="$uclibc_cflags" PREFIX="$uclibc_sysroot" \
      install_headers
}

# Build and install uClibc in the $1 subdirectory.

build_uclibc () {
    local subdir="/$1"
    shift
    local cflags="$@"
    local uclibc_objdir="$uclibc_objdir$subdir/"
    local uclibc_sysroot="$prefix/$build_sysroot$subdir"

    execute_pushd $uclibc_objdir
    execute make ARCH_CFLAGS="$cflags"
    execute make PREFIX="$uclibc_sysroot" install
    execute_popd
}

# Install linux kenrel headers to $1
install_kernel_headers () {
    # Figure out what name the Linux kernel uses for this architecture. 
    local linux_arch
    local linux_make_targets="include/asm include/linux/version.h" 
    local extra_includes
    local linux_headers="$1"
    # Order is important.  uclinux target take precedence.
    case "$target" in
	arm*uclinux)
	    linux_arch=armnommu
	    linux_make_targets="$linux_make_targets include/asm-arm/.arch"
	    extra_includes=asm-arm
	    ;;
	arm*-*-*)
	    linux_arch=arm
	    linux_make_targets="$linux_make_targets include/asm-arm/.arch"
	    ;;
	i?86-*-*)
	    linux_arch=i386
	    ;;
	m68k*uclinux)
	    linux_arch=m68knommu
	    extra_includes=asm-m68k
	    ;;
	mips-*-*)
	    linux_arch=mips
	    ;;
	powerpc*-*-*)
	    linux_arch=ppc
	    ;;
	*)
	    error "Unknown kernel architecture"
	    ;;
    esac

    # We do not actually need a working compiler at this point,
    # but we pass in the correct value for CROSS_COMPILE, so that
    # if, in future, we do need such a compiler, we'll be sure to
    # use the right one.
    local linux_make="make ARCH=$linux_arch CROSS_COMPILE=$target_prefix"
    # Populate the system with the kernel headers.
    pushd "$linux_srcdir" > /dev/null
    yes "" | $linux_make oldconfig
    $linux_make $linux_make_targets
    copy_dir include/asm-"$linux_arch" "$linux_headers/asm"
    local d
    for d in linux asm-generic $extra_includes; do
	copy_dir include/$d "$linux_headers/$d"
    done
    # If the Linux kernel is stored in CVS, the copy_dir commands
    # will have copied CVS directories as well; remove them now.
    for d in $(find "$usr_include" -name CVS); do
	rm -rf $d
    done
    popd > /dev/null
}

# Build packages for Binutils, GCC, and uClibc.

package_uclibc_toolchain () {
    local sysroot="$installdir/$target/libc"
    local build_sysroot="$target/libc"
    local usr_include="$prefix/$build_sysroot/usr/include"

    # Build the binary utilities.
    local binutils_build_log=$(log_path "binutils")
    build_binutils $binutils_configure_opts \
	 > "$binutils_build_log" 2>&1

    # Build ELF2FLT.
    build_elf2flt

    # Build the GCC and Linux kernel source packages.
    build_source_package "$gcc_src_package" "$gcc_srcdir"
    build_source_package "$linux_src_package" "$linux_srcdir"

    local gcc_build_log=$(log_path "gcc")

    if [ "$host" != "$build" ]; then 
	# Copy the libraries we have already built.  
	copy_libs \
            "$target/lib" \
            "$target/libc" \
            "include/c++" \
            "lib/gcc/$target"
    else
	build_source_package "$uclibc_src_package" "$uclibc_srcdir"

	local uclibc_build_log=$(log_path "uclibc")

	# Create the sysroot directory.
	mkdir -p "$usr_include" "$usr_include/bits" "$usr_include/gnu" \
	    >> "$uclibc_build_log" 2>&1

	install_kernel_headers "$usr_include" >> "$uclibc_build_log" 2>&1

	# Build the C compiler so that we can use it to build crt*.o.
	# We use --disable-shared as building a shared object requires
	# crt*.o, which do not yet exist.  We use --disable-threads because
	# threads do not work without shared libraries.  We use
	# --without-headers for obvious reasons.  The use of --with-newlib,
	# however, is a hack.  GCC 3.4.3's configure script does not honor 
	# --without-headers by itself.  (That is a bug.)
	build_gcc \
	    $gcc_configure_opts \
	    --disable-shared --disable-threads --disable-libssp \
	    --without-headers --with-newlib \
	    --enable-languages=c \
	    >> "$gcc_build_log" 2>&1

	local uclibc_objdir=$(objdir_path "uclibc")

	# Build uClibc
	local subdir
	local cflags
	local multilib
	print_glibc_multilibs | while read multilib; do
	    subdir=$(echo "$multilib" | sed -e 's|;.*||g')
	    cflags=$(echo "$multilib" | sed -e 's|.*;||g' -e 's|@| -|g')
	    install_uclibc_headers "$subdir" "$cflags" \
		>> $uclibc_build_log 2>&1
	    build_uclibc "$subdir" "$cflags" \
		>> $uclibc_build_log 2>&1
	done
	check_status
    fi

    # Now build the final version of the compiler, using whatever 
    # languages were requested for this configuration.
    build_gcc \
	$gcc_configure_opts \
	>> "$gcc_build_log" 2>&1

    # Now that everything has been built, do any post-processing
    # required on the uClibc installation. 
    finalize_libc_installation

    release_config_includes="$release_config_includes $CSL_SCRIPTDIR/release-configs/$uclibc_config_file"
    build_package
}

# Build packages for Binutils, GCC, and newlib.

package_newlib_toolchain() {
    local sysroot="$installdir/$target"
    local build_sysroot="$target"

    # Build the binary utilities.
    local binutils_build_log=$(log_path "binutils")
    build_binutils $binutils_configure_opts > "$binutils_build_log" 2>&1

    local gcc_build_log=$(log_path "gcc")

    local redboot_build_log=$(log_path "redboot")

    if [ "$host" != "$build" ]; then 
	# Copy the libc we have already built so that GCC's
	# configury will work correctly.
	copy_libs \
	    "$target/lib" \
	    "$target/include" \
            "include/c++" \
            "lib/gcc/$target"
    else
	# Add --disable-shared to the initial build because linking
	# libgcc_s.so requires crt0.o, which will not be built until we
	# build newlib.
	build_gcc \
	    --with-newlib \
	    $gcc_configure_opts \
	    --disable-shared --disable-threads --disable-libssp \
	    --without-headers --enable-languages=c \
	    >> "$gcc_build_log" 2>&1

	# Build newlib.
	local newlib_build_log=$(log_path "newlib")
	build_standard_package "Newlib" "newlib" $newlib_configure_opts \
	    >> "$newlib_build_log" 2>&1
    fi

    # Build the GCC source package.
    build_source_package "$gcc_src_package" "$gcc_srcdir"

    # Build the final version of the compiler.
    build_gcc --with-newlib \
        $gcc_configure_opts >> "$gcc_build_log" 2>&1

    # Build RedBoot.
    if [ "$host" = "$build" ] && [ "$redboot_version" ]; then
	build_redboot >>"$redboot_build_log" 2>&1
    fi

    build_package
}

# Build packages for Binutils, GCC, and the Dinkum C++ library.

package_dinkum_toolchain() {
    # Build the binary utilities.
    local binutils_build_log=$(log_path "binutils")
    build_binutils $binutils_configure_opts > "$binutils_build_log" 2>&1

    # Build the compiler.
    local gcc_build_log=$(log_path "gcc")
    local gcc_configure_opts="$gcc_configure_opts --disable-hosted-libstdcxx --with-cxxabi=$dinkum_srcdir/include"
    build_gcc $gcc_configure_opts >> "$gcc_build_log" 2>&1

    if [ "$host" != "$build" ]; then 
	# Copy the libraries we have already built.
	copy_libs "$target/lib" "include"
    else
	local subdir
	local cflags
	local multilib
	local dinkum_subdir
	local dinkum_ml_srcdir
	local dinkum_ml_objdir
	local dinkum_ml_libdir

	inform "Building Dinkum C++ library..."
	build_source_package "$dinkum_src_package" "$dinkum_srcdir"
	# Create the top-level Dinkum object directory.  Multilibs
	# will be created under this directory.
	local dinkum_objdir=$(objdir_path "dinkum")
	rm -rf "$dinkum_objdir"
	execute mkdir -p "$dinkum_objdir"
	cat > "$dinkum_objdir/dinkum.exclude" <<-EOF
		c
		embedded/CVS
		export
	EOF
	# Dinkum's build script invokes "gcc", not "$target-gcc".
	ln -s "$prefix/bin/$target-gcc" "$dinkum_objdir/gcc"
	local saved_PATH="$PATH"
	export PATH="$build_tool_path:$dinkum_objdir:$PATH"
	# Loop over the multilibs.
	"$prefix/bin/$target-gcc" -print-multi-lib | \
	    while read multilib; do
		subdir=$(echo "$multilib" | sed -e 's|;.*||g')
		cflags=$(echo "$multilib" | sed -e 's|.*;||g' -e 's|@| -|g')
		case "$cflags" in
		    *-mrtp*) setup=native ;;
		    *) setup=kernel ;;
		esac
		case "$cflags" in
		    *-mc++-abr*) setup="$setup abridged" ;;
		    *) setup="$setup standard" ;;
		esac
		dinkum_subdir="$dinkum_objdir/$subdir"
		# Populate the Dinkum source directory.
		dinkum_ml_srcdir="$dinkum_subdir/source"
		mkdir -p $dinkum_ml_srcdir
		ln -s "$dinkum_srcdir/source/"*.{c,cpp,h,cx,cx0} \
		    "$dinkum_ml_srcdir"
		ln -s "$dinkum_srcdir/"{include,tests} \
		    "$dinkum_subdir"
		# Build the Dinkumware library.
		dinkum_ml_objdir="$dinkum_subdir"/lib
		execute mkdir -p "$dinkum_ml_objdir"
		execute_pushd "$dinkum_subdir"
		MULTILIB_OPTIONS="$cflags" execute \
		    "$dinkum_srcdir/setup/Gcc" "v${gcc_version}" $setup build
		execute_popd
		# Combine the compiler-support routines from GCC with
		# the C++ runtime from Dinkumware.
		dinkum_ml_libdir="$dinkum_subdir"/libstdc++
		execute mkdir -p "$dinkum_ml_libdir"
		execute_pushd "$dinkum_ml_libdir"
		execute "$target-ar" x "$dinkum_ml_objdir/libdinkum.a"
		execute "$target-ar" x "$(objdir_path gcc)/$target/$subdir/libstdc++-v3/libsupc++/.libs/libabic++convenience.a"
		rm -f "libstdc++.a"
		execute "$target-ar" cr "libstdc++.a" *.o
		execute install -d "$prefix/$target/lib/$subdir"
		execute install -m 644 "libstdc++.a" \
			"$prefix/$target/lib/$subdir"
		(cd "$dinkum_srcdir/include" &&\
		    tar cfX - "$dinkum_objdir/dinkum.exclude" [a-z]*) | \
			tar xfC - "$prefix/include/c++/$gcc_version"
		execute_popd
   	    done
	check_status
	PATH="$saved_PATH"
	inform "Dinkum C++ library complete."
    fi

    build_package
}

# Run the DejaGNU tests against the toolchain in $installdir.

test_toolchain() {
    local testsuite
    local testsuites
    local gnu_test
    local gnu_test_opts
    local ld_library_path

    inform "Running tests..."

    pushd $objdir > /dev/null

    # Form the gnu-test command-line.
    gnu_test="${CSL_SCRIPTDIR}/gnu-test"

    # Figure out which testsuites to use.
    testsuites="binutils gas ld gcc g++"
    if [ "$gdb_version" ]; then
	testsuites="$testsuites gdb"
    fi
    if [ "$newlib_version" ]; then
	testsuites="$testsuites newlib"
    fi
    if [ ! "$dinkum_version" ]; then
	testsuites="$testsuites libstdc++"
    fi

    # If the -T option was present, pass it along.
    gnu_test_opts="-h $host -s $srcdir -c $release_config"
    if [ "$testlogdir" ]; then
	gnu_test_opts="$gnu_test_opts -l $testlogdir"
    fi
    if $mail_logs; then
	gnu_test_opts="$gnu_test_opts -m"
    fi

    # For native testing, set up LD_LIBRARY_PATH to run binaries
    # compiled with this toolchain.
    ld_library_path=""
    if [ "$host" = "$target" ]; then
	local d
	for d in $shlib_dirs; do
	    local p
	    p="$prefix/$d"
	    if [ "$ld_library_path" ]; then
		ld_library_path="$ld_library_path:$p"
	    else
		ld_library_path="$p"
	    fi
	done
	if [ "$LD_LIBRARY_PATH" ]; then
	    ld_library_path="$ld_library_path:$LD_LIBRARY_PATH"
	fi
    fi

    # Run the testsuites.  They generate a lot of output, which will
    # be interleaved due to the way that gnu-test works, and all the
    # data is in the generated log files, so we make this command
    # silent.
    if [ "$ld_library_path" ]; then
	PATH="$prefix/bin:$PATH" LD_LIBRARY_PATH="$ld_library_path" \
	    $gnu_test $gnu_test_opts $testsuites > /dev/null 2>&1
    else
	PATH="$prefix/bin:$PATH" \
	    $gnu_test $gnu_test_opts $testsuites > /dev/null 2>&1
    fi

    # Save the logs, so that they are bundled as part of the backup
    # package.
    for testsuite in $testsuites; do
	local log
	if [ ! -r "$testsuite/$testsuite.sum" ]; then
	    continue
	fi
	local dest=$backupdir/tests/$host/$testsuite
	mkdir -p "$dest"
	cp "$testsuite/$testsuite".{log,sum} "$dest"
	# Generate an HTML report for use with Roundup.
	local is_combined=false
	if [ "$testsuite" = "gcc" ] \
	    || [ "$testsuite" = "g++" ] \
	    || [ "$testsuite" = "libstdc++" ]; then
	    is_combined=true
	fi
	# Figure out where the csl_qmtest package has been placed.
	local cslqmtestdir=$(python <<-EOF 
		import csl_qmtest 
		import os.path
		print os.path.dirname(csl_qmtest.__file__)
		EOF
	)
	# Run QMTest, comparing the actual DejaGNU outcomes against
	# the expected outcomes, with output in the format we use for
	# display from Roundup.  QMTest will exit with a non-zero exit
	# code if there are unexpected failures, but we don't want
	# that to stop the build, so we use an explicit "|| true".
	QMTEST_CLASS_PATH="$cslqmtestdir" \
	    qmtest summarize \
	    -f none \
	    -O "dejagnu_stream.DejaGNUReader(filename = '$dest/$testsuite.log', expectations = 'true', is_combined = '$is_combined')" \
	    --result-stream="csl_report.CSLGNUReportStream(filename = '$dest/$testsuite.html', host = '$host')" \
	    "dejagnu_stream.DejaGNUReader(filename = '$dest/$testsuite.log', is_combined = '$is_combined')" || true 
    done

    popd > /dev/null

    inform "Tests complete."
}

# Build all packages required for this release.

build_all_packages() {
    # Remove the backup package; if the build fails, we don't want to 
    # accidentally use the old package.
    rm -f "$backup_package"
    # Create the directory into which things will be placed for inclusion
    # in the backup package.
    local backupdir=$(dirname "$backup_package")/$(basename "$backup_package" .src.tar.bz2)
    rm -rf "$backupdir"
    mkdir -p "$backupdir"

    inform "Building toolchain for $target..."

    # Tell configure scripts what compiler to use on the build machine. 
    if $use_canonical_gcc; then 
	export CC_FOR_BUILD="$build-gcc"
    fi

    for host in $hosts; do
	inform "Building packages for $host..."
	# Configuration depending on the individual host.

	# Tell configure scripts what compiler to use on the host machine.
	if $use_canonical_gcc; then
	    export CC="$host-gcc"
	fi
	# The string to prepend to the names of tools (like "as" and "gcc")
	# to obtain the path to them.
	if [ "$host" != "$target" ]; then
	    target_prefix="$prefix/bin/${target}-"
	else
	    target_prefix="$prefix/bin/"
	fi

	# Use "make bootstrap" to build GCC on native targets, but
	# plain "make" on cross targets.
	local gcc_make_opts
	if [ "$host" = "$build" ] && [ "$build" = "$target" ]; then
	    gcc_make_opts="bootstrap"
	else
	    gcc_make_opts=""
	fi

	local build_tool_path=""
	if [ "$host" = "$build" ]; then
	    # If the tools we're building will run on the build system,
	    # then we can just use them. 
	    build_tool_path="$prefix/bin"
	else
	    # Use the tools that we built earlier for the build
	    # system.  
	    local build_pkg=$(binary_package "$build")
	    rm -rf "$objdir/$build"
	    mkdir "$objdir/$build"
	    pushd "$objdir/$build" > /dev/null
	    tar xf "$build_pkg" --bzip2
	    popd > /dev/null
	    build_tool_path="$objdir/$build/bin" 
	fi

	local full_version="$version-$target-$host"

	# Send the command log to a file.
	eval "exec $command_fd>$logdir/$pkg_prefix-${full_version}.sh"

        # The file that the user will eventually download.
	package=$(binary_package "$host")
	rm -f "$package"

	remove_previous_builds
	
	if $eclipse_plugin; then
	    build_eclipse_plugin
	fi
	build_rdi_stub
	build_gdb
	build_make
	unpack_windows_runtime
	if [ "$glibc_version" ]; then
	    package_glibc_toolchain
	elif [ "$uclibc_version" ]; then
	    package_uclibc_toolchain
	elif [ "$newlib_version" ]; then
	    package_newlib_toolchain
	elif [ "$dinkum_version" ]; then
	    package_dinkum_toolchain
	else
	    package_binutils_gcc
	fi

	# Run the DejaGNU tests, if requested.
	if $run_tests && [ "$host" = "$build" ]; then 
	    test_toolchain
	fi

	# There is no reason to build additional copies of the source
	# packages for hosts other than the first.
	build_source_packages=false
    done

    generate_build_summary
    build_backup_package
}

########################################################################
# Main Program
########################################################################

# If LANGUAGES is set, the GCC configure script uses its contents in
# preference to the --enable-languages option.  Therefore, we make sure
# this variable is not set.
unset LANGUAGES

# The directory containing the source subdirectories.
srcdir="/scratch/$USER/src"

# The directory into which build subdirectories and output files will
# be placed.
objdir="/scratch/$USER/obj"

# The directory into which the source and binary packages are placed.
pkgdir="/scratch/$USER/pkg"

# The directory into which the logs are placed.
logdir="/scratch/$USER/logs"

# The directory in which the tools will be installed during this build
# process.
prefix="/scratch/$USER/install"

# True if source packages should be built.
build_source_packages=true

# The source packages that have been built thus far.
source_packages=""

# True if tests should be run after building.
run_tests=false

# If not empty, the directory into which test logs should be placed.
# The config.status files for the various components are also placed
# here.
testlogdir=

# True if GCC test logs should be mailed out.
mail_logs=false

# Parse options.
while getopts "i:kno:p:s:l:tT:m" arg; do
    case $arg in
	i)
	    prefix=$OPTARG
	    ;;
	n)
	    build_source_packages=false
	    ;;
	o)
	    objdir=$OPTARG
	    ;;
	p)
	    pkgdir=$OPTARG
	    ;;
	s)
	    srcdir=$OPTARG
	    ;;
	l)
	    logdir=$OPTARG
	    ;;
	t)
	    run_tests=true
	    ;;
	T)
	    testlogdir=$OPTARG
	    ;;
	m)
	    mail_logs=true
	    ;;
	\?) 
	    usage
	    ;;
    esac
done
shift $(expr $OPTIND - 1)
if [ $# -ne 1 ]; then
    usage
fi

# This script.
release_script="$CSL_SCRIPTDIR/gnu-release"

# The release configuration file.
release_config="$1"

# Files included by the release configuration.
release_config_includes=""

# Read the release configuration.
read_config "$release_config"

# Add the build tool directory to the PATH.
build_tool_dir="/usr/local/tools/gcc-$build_tool_version"
if [ ! -d "$build_tool_dir/bin" ]; then
    error "Build tool version $build_tool_version does not exist"
fi
prepend_path PATH "$build_tool_dir/bin"
prepend_path LD_LIBRARY_PATH "$build_tool_dir/lib"

# But testsuite logfiles in 
if [ ! "$testlogdir" ]; then
    testlogdir="$logdir"
fi

# Make sure that the output directories exist.
for x in $objdir $prefix $pkgdir $logdir; do
    [ -d "$x" ] || mkdir -p "$x"
done

build_all_packages
