##################################################### -*-shell-script-*-
#
# File:   gnu-common
# Author: Mark Mitchell
# Date:   2005-06-15
#
# Contents:
#   Helper library for gnu-* scripts.
#
########################################################################

########################################################################
# 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
########################################################################

# This file is read into other scripts with:
#   . ${CSL_SCRIPTDIR}/gnu-commmon

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

# The directory in which release configurations are stored.
release_configdir="$CSL_SCRIPTDIR/release-configs"

# The file descriptor used for informative messages that should be
# visible to the user even when output is being redirected to a log
# file.
inform_fd=3
exec 3>&2

# The file descriptor used for printing commands as they are executed.
command_fd=4
exec 4>&2

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

# If the last command did not exit successfully, then this command
# will exit with the same status.  This function should be called
# immediately after complex shell constructs that do not cause the
# shell to exit, even with "set -e".

check_status() {
    local status="$?"
    if [ "$status" -ne 0 ]; then
	return "$status"
    fi
}

# Issue an error message and exit.

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

# Provide an informational or status message to the user.
 
inform () {
    echo "[$(date +%T)] $@" >& $inform_fd
}

# Run the command given by "$@".  Use this function for commands that
# users might want to run themselves, by hand.  If $show_cmds is true
# the command will also be printed to $command_fd.  If $execute_cmds
# is false, the command will not actually be executed.
 
execute() {
    if $show_cmds; then
	echo "$@" >& $command_fd
    fi
    if $execute_cmds; then
	"$@"
    fi
}

# Like pushd, but honors $show_cmds and $execute_cmds.

execute_pushd() {
    if $show_cmds; then
	echo "pushd $1" >& $command_fd
    fi
    if $execute_cmds; then
	pushd "$1" > /dev/null
    fi
}

# Like popd, but honors $show_cmds and $execute_cmds.

execute_popd() {
    if $show_cmds; then
	echo "popd" >& $command_fd
    fi
    if $execute_cmds; then
	popd > /dev/null
    fi
}

# Print the "$1" with the version "$2" appended.

version_path() {
    if [ -n "$2" ]; then
	echo "$1-$2"
    else
	echo "$1"
    fi
}

# Print the name of the binary package running on the "$1" host
# system.

binary_package() {
    local host=$1
    echo "$pkgdir/$pkg_prefix-$version-$target-$host".tar.bz2
}

# Print the name used to prefix identifiers in the Eclipse plug-in
# manifest.

eclipse_plugin_prefix() {
    echo "com.codesourcery.$(echo "$target" | sed -e 's|-|_|g')"
}

# Print the name of the Eclipse plug-in.

eclipse_plugin_name() {
    echo "$(eclipse_plugin_prefix).cdt.managedbuilder.gnu.ui"
}

# $1 is the name of an exported environment variable containing a
# colon-separated path, such as PATH or LD_LIBRARY_PATH.  Add $2 to
# the start of the path.

prepend_path() {
    if $(eval "test -n \"\$$1\""); then
	eval "export $1=$2:\$$1"
    else
	eval "export $1=$2"
    fi
}

# Print a conventional name of the system indicated by $1.  This name
# is used in descriptions of the generated packages.

system_name() {
    local name

    case "$target" in
	arm*-*-eabi*)
	    name="ARM"
	    ;;
	arm*-*-linux*)
	    name="ARM GNU/Linux"
	    ;;
	arm*-*-symbian*)
	    name="ARM SymbianOS"
	    ;;
	i?86-*-linux*)
	    name="IA32 GNU/Linux"
	    ;;
	i?86-mingw*)
	    name="Windows"
	    ;;
	mips*-*-linux*)
	    name="MIPS GNU/Linux"
	    ;;
	powerpc*-*-linux*)
	    name="PowerPC GNU/Linux"
	    ;;
	powerpc*-*-vxworks*)
	    name="PowerPC VxWorks"
	    ;;
	*)
	    name=$target
	    ;;
    esac

    echo "$name"
}

# Read the release configuration file $1 and set all appropriate
# global variables.

read_config() {
    #######################
    # Release Configuration
    #######################

    # The information in this section can be overridden by the
    # configuration file.  The release configuration file can 
    # rely upon $release_configdir being set, in addition to 
    # all variables set before the "source" command below. 

    # The prefix given the generated package files.
    pkg_prefix="gnu"

    # Our version names are based on FSF version numbers for GCC, followed 
    # by "-$csl_version".  Do not use spaces in these version names.
    csl_version=""

    # The FSF version number for GNU Binutils upon which this release is
    # based.
    binutils_version=""

    # Options to pass to "configure" when building binutils.
    binutils_configure_opts=""

    # True if a cross binutils configuration should be modified after
    # installation to look like native.  This creates links such as 'as'
    # to programs such as '$target-as'.
    binutils_force_native=false

    # The version of the Dinkum C++ runtime library to use.  Leave
    # this empty, if the GNU C++ runtime library should be used
    # instead.
    dinkum_version=""

    # True if an Eclipse plugin should be generated for this
    # toolchain.
    eclipse_plugin=true

    # The FSF version of GCC upon which this release is based.
    gcc_version=""

    # Options to pass to "configure" when building GCC.

    # We disable libstdc++ PCH files because (a) they are likely not
    # to work on user systems due to variations in the addresses at
    # which GCC is mapped, and (b) there is no way to build a PCH
    # file for a Canadian cross configuration, and (c) they are huge.
    gcc_configure_opts="\
	--enable-languages=c,c++ \
	--enable-shared --enable-threads \
        --disable-libmudflap \
        --disable-libssp \
        --disable-libstdcxx-pch
	"

    # Options to pass to "configure" when building GCC to specify what
    # internal checks to enable.
    gcc_checking_configure_opts="--disable-checking"

    # Options to pass to "configure" when building GCC to specify the
    # assembler and linker to use.
    # We do not pass --with-as= or --with-ld=.  If we do, then the
    # driver will not search other locations, which is a problem if the 
    # compiler driver has not been built to be relocatable.
    gcc_as_ld_configure_opts="--with-gnu-as --with-gnu-ld"

    # Options to pass to "configure" when building GLIBC. Disable
    # profile-feedback and "gd".
    glibc_configure_opts="--disable-profile --without-gd"

    # The flags to use when compiling GLIBC.
    glibc_cflags=""

    # True if NPTL should be enabled when building GLIBC.
    enable_nptl=true

    # The version of GDB to use.
    gdb_version=""

    # Options to pass to "configure" when building GDB.
    gdb_configure_opts=""

    # The version of GLIBC to use.
    glibc_version=""

    # The version of the Linux kernel to use.
    linux_version=""

    # The version of newlib to use.
    newlib_version=""

    # Options to pass to "configure" when building newlib.  By
    # default, we enable "long long" support for printf/scanf so that
    # users using "%lld" do not experience silent failures.
    newlib_configure_opts="--enable-newlib-io-long-long"

    # The version of uClibc to use.
    uclibc_version=""

    # The uClibc configuration file to use.
    uclibc_config_file=""

    # The version of ELF2FLT to use.
    elf2flt_version=""

    # Options to pass to "configure" when building ELF2FLT.
    elf2flt_configure_opts=""

    # The version of the RDI stub to use.
    rdi_stub_version=""

    # Options to pass to "configure" when building RDI stub.
    rdi_stub_configure_opts=""

    # The version of RedBoot to use.
    redboot_version=""

    # The RedBoot architecture.
    redboot_arch=""

    # The RedBoot board name.
    redboot_board=""

    # The RedBoot configuration, e.g. "redboot_RAM".
    redboot_cfg=""

    # The RedBoot HAL version.
    redboot_hal_version="current"

    # The version of Make to use (with Eclipse, on Windows hosts).
    make_version=""

    # Options to pass to "configure" when building Make.
    make_configure_opts=""

    # The MinGW runtime version.
    mingw_version=""

    # The W32 API package version.
    w32api_version=""

    # Files to remove after installation, whether because broken or
    # because not desired in the package.
    # Remove things that our customers will not need.
    remove_files="bin/*gccbug \
	info/configure.info* info/cppinternals.info* info/gccint.info* \
	info/standards.info* \
	"

    # The directory into which the installation will be placed.
    installdir="/opt/codesourcery"

    # The absolute path to the sysroot that should be used when
    # the compiler being built is a cross compiler.  This value
    # is not used when building a native compiler, even when
    # the native compiler will run on a machine different from 
    # $build.  Normally, a cross compiler built with this 
    # variable should not be shipped to customers because they
    # are unlikely to have the target headers in the same path
    # as is given here.
    cross_sysroot=""

    # The relative path to the sysroot, from the top of the installation 
    # directory.  It does not make sense to set both this option and
    # cross_sysroot.
    build_sysroot=""

    # The GNU triplet for the system on which the tools are being built.
    build=""

    # The GNU triplets for the systems on which the tools will run,
    # separated by spaces.
    hosts=""

    # The GNU triplet for the system on which the code generated by the tools
    # will run.
    target=""

    # The DejaGNU target board, if any, required for running tests.
    target_board="unix"

    # The sets of options that should be used when running compiler
    # tests, in the same format used by "gcc -print-multi-lib".
    test_flags=""

    # Like test_flags, but for the Plum-Hall C/C++ testsuites.  The
    # directory names are the subdirectoris in which the testsuites
    # will be run.
    ph_test_flags=""

    # The number of tasks to run in parallel during the build process.
    parallelism=4

    # The number of tasks to run in parallel when testing.  If empty,
    # the value of $parallelism will be used.
    test_parallelism=

    # True if we should use $build-gcc to build programs for the build
    # machine, and $host-gcc to build programs for the host machine.
    use_canonical_gcc=true

    # CC to use for configuring GCC.  The default is as specified by
    # use_canonical_gcc; if use_canonical_gcc is false, no value of CC
    # is specified unless gcc_cc is defined.
    gcc_cc=

    # The program to use when stripping binaries.  This must not be the
    # same strip program installed during this build, as strip cannot
    # strip itself.
    strip="/usr/bin/strip"

    # The version of the build tools to use when building this toolchain. 
    # (The directory /usr/local/tools/gcc-$build_tool_version/bin will be 
    # placed in the PATH when building the toolchain.)
    build_tool_version=""

    # We disable NLS in tools that users will run; error messages
    # produced by the toolchain will thus be exclusively in English.
    # The rationale for disabling NLS is that (a) we have had no
    # customer demand, (b) the quality of the translations is thought
    # to be low, at least for some languages, (c) when you
    # relocate the tools installation NLS stops working, and (d)
    # disabling NLS avoids configure-script bugs that sometimes make
    # it hard to build the toolchain.  This option is for host tools;
    # it does not affect programs that run on the target.
    nls_configure_opts="--disable-nls"

    # For native builds, a list of subdirectories of $prefix to put in
    # LD_LIBRARY_PATH for testing; for example, "lib lib64".
    shlib_dirs="lib"

    # Read the configuration file.
    if [ $# -ne 1 ]; then
	error "Usage: gnu-release CONFIG"
    fi

    # The release configuration file.
    local release_config="$1"
    # The list of configuration files included by the main configuration 
    # file.
    release_config_includes=""
    # Configuration files can use the "include" command to read other
    # configuration files.
    include () {
	local config="$release_configdir/$1"
	release_config_includes="$release_config_includes $config"
	builtin source $config
    }
    # Read the release configuration.
    include "$release_config"
    # Remove the include function.
    unset -f include

    # If no explicit list of hosts is provided, then just build for
    # the build system.
    if [ -z "$hosts" ]; then
	hosts="$build"
    fi

    # If no explicit test_parallelism value is provided, use the build
    # parallelism as a default.
    if [ ! "$test_parallelism" ]; then
	test_parallelism=$parallelism
    fi

    ###############################
    # Configuration File Validation
    ###############################

    local var
    for var in target build csl_version build_tool_version; do
	local value
	eval "value=\$$var"
	if [ -z "$value" ]; then
	    error "release configuration does not define \$$var"
	fi
    done

    ############################
    # Installation Configuration
    ############################

    # Nothing after this point can be overridden from the
    # release-configuration file.

    # The version number of this release as it will be presented to
    # customers.
    version="$gcc_version-$csl_version"

    # The path to the uninstall script.
    uninstall_script="$prefix/bin/$target-uninstall"

    # The directory containing manifest files.
    manifests_dir="$prefix/share/manifests"

    # The manifest file summarizing the release.
    manifest="$manifests_dir/${target}.mft"

    # The file that contains basic information about the build.
    build_summary=$(version_path "$objdir/gnu" "${version}").txt

    # The file that will contain the various source packages and the
    # manifest file.
    backup_package=$(version_path "$pkgdir/$pkg_prefix" \
                       "${version}-${target}").src.tar.bz2

    # Options to provide to GNU tar when building packages.
    tar_opts="--owner=0 --group=0"

    ########################
    # Binutils Configuration
    ########################

    # The directory containing the source tree for GNU Binutils.
    binutils_srcdir=$(version_path "$srcdir/binutils" "$binutils_version")

    # Options to pass to "configure" when building Binutils.
    binutils_configure_opts="--prefix=$installdir $binutils_configure_opts"

    # The package generated by this script containing the Binutils source
    # files.
    binutils_src_package=$(version_path \
                           "$pkgdir/binutils" "${version}").tar.bz2

    ###################
    # GCC Configuration
    ###################

    # The directory containing the source tree for the GNU Compiler
    # Collection.
    gcc_srcdir=$(version_path "$srcdir/gcc" "$gcc_version")

    # Options to pass to "configure" when building GCC.
    gcc_configure_opts="$gcc_configure_opts $gcc_as_ld_configure_opts \
	$gcc_checking_configure_opts --prefix=$installdir"

    # The package generated by this script containing the GCC source files.
    gcc_src_package=$(version_path "$pkgdir/gcc" "${version}").tar.bz2

    #####################
    # GLIBC Configuration
    #####################

    if [ "$glibc_version" ]; then
	# The directory containing the source tree for the GNU C Library.
	glibc_srcdir=$(version_path "$srcdir/glibc" "$glibc_version")

	# The directory containing the source tree for the Linux kernel.
	linux_srcdir=$(version_path "$srcdir/linux" "$linux_version")

	# The package generated by this script containing the GLIBC source files.
	glibc_src_package=$(version_path "$pkgdir/glibc" "${version}").tar.bz2

	# The package generated by this script containing the Linux source files.
	linux_src_package=$(version_path "$pkgdir/linux" "${version}").tar.bz2

	# Enabling add-ons will enable NPTL.
	if $enable_nptl; then
	    glibc_configure_opts="$glibc_configure_opts --enable-add-ons"
	else
	    glibc_configure_opts="$glibc_configure_opts \
	       --enable-add-ons=linuxthreads"
	fi
    fi

    ######################
    # uClibc Configuration
    ######################

    if [ "$uclibc_version" ]; then
	# The directory containing the source tree for uClibc.
	uclibc_srcdir=$(version_path "$srcdir/uclibc" "$uclibc_version")

	# The directory containing the source tree for the Linux kernel.
	linux_srcdir=$(version_path "$srcdir/linux" "$linux_version")

	# The package generated by this script containing the uClibc source files.
	uclibc_src_package=$(version_path "$pkgdir/uclibc" "${version}").tar.bz2

	# The package generated by this script containing the Linux source files.
	linux_src_package=$(version_path "$pkgdir/linux" "${version}").tar.bz2
    fi

    ######################
    # Newlib Configuration
    ######################

    if [ "$newlib_version" ]; then
	# The directory containing the source tree for newlib.
	newlib_srcdir=$(version_path "$srcdir/newlib" "$newlib_version")

	# The generated source package for newlib.
	newlib_src_package=$(version_path "$pkgdir/newlib" \
                              "$version").tar.bz2
    fi

    #######################
    # ELF2FLT Configuration
    #######################

    if [ "$elf2flt_version" ]; then
	# The directory containing the source tree for elf2flt.
	elf2flt_srcdir=$(version_path "$srcdir/elf2flt" "$elf2flt_version")

	# The generated source package for elf2flt.
	elf2flt_src_package=$(version_path "$pkgdir/elf2flt" \
                              "$version").tar.bz2
    fi

    ######################
    # Dinkum Configuration
    ######################

    if [ "$dinkum_version" ]; then
	# The directory containing the source tree for dinkum.
	dinkum_srcdir=$(version_path "$srcdir/dinkum" "$dinkum_version")

	# The generated source package for dinkum.
	dinkum_src_package=$(version_path "$pkgdir/dinkum" \
                              "$version").tar.bz2
    fi

    ###################
    # GDB Configuration
    ###################

    if [ "$gdb_version" ]; then
	# The directory containing the source tree for gdb.
	gdb_srcdir=$(version_path "$srcdir/gdb" "$gdb_version")

	# The generated source package for gdb.
	gdb_src_package=$(version_path "$pkgdir/gdb" \
                            "$version").tar.bz2
    fi

    ########################
    # RDI Stub Configuration
    ########################

    if [ "$rdi_stub_version" ]; then
	# The directory containing the source tree for rdi_stub.
	rdi_stub_srcdir=$(version_path "$srcdir/rdi-stub" \
                          "$rdi_stub_version")

	# The generated source package for gdb.
	rdi_stub_src_package=$(version_path "$pkgdir/rdi-stub" \
	                       "$version").tar.bz2
    fi

    #######################
    # RedBoot Configuration
    #######################

    if [ "$redboot_version" ]; then
	# The directory containing the source tree for redboot (the
	# eCos "packages" directory).
	redboot_srcdir=$(version_path "$srcdir/redboot" "$redboot_version")

	# The generated source package.
	redboot_src_package=$(version_path "$pkgdir/redboot" "$version").tar.bz2
    fi

    ####################
    # Make Configuration
    ####################

    if [ "$make_version" ]; then
	# The directory containing the source tree for make.
	make_srcdir=$(version_path "$srcdir/make" "$make_version")

	# The generated source package.
	make_src_package=$(version_path "$pkgdir/make" "$version").tar.bz2
    fi

    #############################
    # MinGW Runtime Configuration
    #############################

    if [ "$mingw_version" ]; then
	mingw_srcdir=$(version_path "$srcdir/mingw" "$mingw_version")
	mingw_src_package=$(version_path "$pkgdir/mingw" "$version").tar.bz2
    fi

    #########################
    # Win32 API Configuration
    #########################

    if [ "$w32api_version" ]; then
	w32api_srcdir=$(version_path "$srcdir/w32api" "$w32api_version")
	w32api_src_package=$(version_path "$pkgdir/w32api" "$version").tar.bz2
    fi

    ###################
    # Build Information
    ###################

    # The time at which this build took place.
    build_date=$(date "+%Y%m%d %T")

    # The name of the machine on which this build took place.
    build_machine=$(hostname)

    # The operating system release if known.
    if [ -f /etc/redhat-release ]; then
	build_operating_system=$(cat /etc/redhat-release)
    elif [ -f /etc/debian_version ]; then
	build_operating_system=$(cat /etc/debian_version)
    else
	build_operating_system=""
    fi

    # Additional information about the operating system installation.
    build_uname=$(uname -a)

    # The user running this build script.
    build_user=$(whoami)
}

########################################################################
# Variables
########################################################################

# True if commands should be executed.  If false, they will still be
# printed if $showcmds is true, but they will not actually be run.
execute_cmds=true
# True if commands should be printed before they are executed.
show_cmds=true

