#!/bin/sh
#
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# This script is used to rebuild the Linux 32-bit cross-toolchain
# that allows you to generate 32-bit binaries that target Ubuntu 8.04
# (a.k.a. Hardy Heron) instead of the host system (which usually is 10.04,
# a.k.a. Lucid Lynx)
#

# You will need a working NDK install or working directory (which is used
# to download and patch the toolchain sources from android.git.kernel.org)
#
# Define the NDK variable in your environment before calling this script,
# or it will complain loudly. For example, you can invoke it as:
#
#  export NDK=/path/to/ndk
#  ./uild-hardy-toolchain.sh
#
# Then *wait* for a very long time (about 13minutes on an 8-core HP z600)
# A package file will be generated in /tmp, its name dumped by the script
# in case of success.
#
# There are no command-line options right now, modify the definitions under
# the "CONFIGURATION" section below if you want to change stuff.
#

###########################################################################
###########################################################################
#####
#####  C O N F I G U R A T I O N
#####
###########################################################################
###########################################################################

# We only support running this script on Linux
OS=`uname -s`
case "$OS" in
    Linux)
        OS=linux
        ;;
    *)
        echo "ERROR: This script can only run on Linux!"
        exit 1
        ;;
esac

# Set V to 1 to enable verbose mode
V=${V:-0}

# Location of temporary directory where everything will be downloaded
# configured, built and installed before we generate the final
# package archive
ROOT_DIR=/tmp/gcc-hardy32
mkdir -p $ROOT_DIR

# Location where we download packages from the Ubuntu servers
DOWNLOAD_DIR=$ROOT_DIR/download

# Location of the Android NDK
if [ -z "$NDK" ] ; then
    echo "ERROR: Please define the NDK variable to point to a valid"
    echo "       NDK directory (installation or git repository)."
    echo "       This is needed to download the toolchain sources."
    exit 1
fi

NDK_DOWNLOAD_TOOLCHAIN_SOURCES_SH="$NDK/build/tools/download-toolchain-sources.sh"
if [ ! -f "$NDK_DOWNLOAD_TOOLCHAIN_SOURCES_SH" ] ; then\
    echo "ERROR: Missing script: $NDK_DOWNLOAD_TOOLCHAIN_SOURCES_SH"
    echo "Your NDK variable probably points to an invalid directory!"
    exit 1
fi

# Versions of various toolchain components, do not touch unless you know
# what you're doing!
BINUTILS_VERSION=2.19
GMP_VERSION=4.2.4
MPFR_VERSION=2.4.1
GCC_VERSION=4.4.3
GCC_TARGET=i686-linux
GMP_TARGET=i386-linux

# Location where we will download the toolchain sources
TOOLCHAIN_SRC_DIR=$ROOT_DIR/toolchain-src

# Location of original sysroot
# You must create it with the "hardy-sysroot.sh" script
ORG_SYSROOT_DIR=$ROOT_DIR/sysroot

# Name of the final generated toolchain
TOOLCHAIN_NAME=$GCC_TARGET-glibc2.7-$GCC_VERSION

# Name of the final toolchain binary tarball that this script will create
TOOLCHAIN_ARCHIVE=/tmp/$TOOLCHAIN_NAME.tar.bz2

# Location where we're going to install the toolchain
INSTALL_DIR=$ROOT_DIR/$TOOLCHAIN_NAME

# Location of the final sysroot. This must be a sub-directory of INSTALL_DIR
# to ensure that the toolchain binaries are properly relocatable (i.e. can
# be used when moved to another directory).
SYSROOT_DIR=$INSTALL_DIR/sysroot

# Try to parallelize the build for faster performance.
NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l`
MAKE_FLAGS="-j$NUM_CPUS"

# The list of packages we need to download from the Ubuntu servers and
# extract into the original sysroot
UBUNTU_PACKAGES=

add_ubuntu_package ()
{
    UBUNTU_PACKAGES="$UBUNTU_PACKAGES $@"
}

# The package files containing kernel headers for Hardy and the C
# library headers and binaries
add_ubuntu_package \
    http://mirrors.us.kernel.org/ubuntu//pool/main/l/linux/linux-libc-dev_2.6.24-28.81_i386.deb \
    http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.7-10ubuntu7_i386.deb \
    http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6-dev_2.7-10ubuntu7_i386.deb

# The X11 headers and binaries (for the emulator)
add_ubuntu_package \
    http://mirrors.us.kernel.org/ubuntu//pool/main/libx/libx11/libx11-6_1.1.3-1ubuntu2_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/libx/libx11/libx11-dev_1.1.3-1ubuntu2_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/x/x11proto-core/x11proto-core-dev_7.0.11-1_all.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/x/x11proto-xext/x11proto-xext-dev_7.0.2-5ubuntu1_all.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/x/x11proto-input/x11proto-input-dev_1.4.2-1_all.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/x/x11proto-kb/x11proto-kb-dev_1.0.3-2ubuntu1_all.deb

# Audio libraries (required by the emulator)
add_ubuntu_package \
    http://mirrors.us.kernel.org/ubuntu//pool/main/a/alsa-lib/libasound2_1.0.15-3ubuntu4_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/a/alsa-lib/libasound2-dev_1.0.15-3ubuntu4_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/e/esound/libesd-alsa0_0.2.38-0ubuntu9_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/e/esound/libesd0-dev_0.2.38-0ubuntu9_i386.deb \
    http://security.ubuntu.com/ubuntu/pool/main/a/audiofile/libaudiofile-dev_0.2.6-7ubuntu1.8.04.1_i386.deb \
    http://security.ubuntu.com/ubuntu/pool/main/p/pulseaudio/libpulse0_0.9.10-1ubuntu1.1_i386.deb \
    http://security.ubuntu.com/ubuntu/pool/main/p/pulseaudio/libpulse-dev_0.9.10-1ubuntu1.1_i386.deb

# ZLib
add_ubuntu_package \
    http://mirrors.us.kernel.org/ubuntu//pool/main/z/zlib/zlib1g_1.2.3.3.dfsg-7ubuntu1_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/z/zlib/zlib1g-dev_1.2.3.3.dfsg-7ubuntu1_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/n/ncurses/libncurses5_5.6+20071124-1ubuntu2_i386.deb \
    http://mirrors.us.kernel.org/ubuntu//pool/main/n/ncurses/libncurses5-dev_5.6+20071124-1ubuntu2_i386.deb

###########################################################################
###########################################################################
#####
#####  E N D   O F   C O N F I G U R A T I O N
#####
###########################################################################
###########################################################################

panic ()
{
    echo "ERROR: $@"
    exit 1
}

fail_panic ()
{
    if [ $? != 0 ] ; then
        echo "ERROR: $@"
        exit 1
    fi
}

if [ "$V" != 0 ] ; then
    run () {
        echo "## COMMAND: $@"
        $@
    }
else
    run () {
        $@ >>$TMPLOG 2>&1
    }
fi

OLD_PATH="$PATH"
OLD_LD_LIBRARY_PATH="$LD_LIBRARY_PATH"

BUILD_DIR=$ROOT_DIR/build
mkdir -p $BUILD_DIR

TMPLOG=$BUILD_DIR/build.log
rm -rf $TMPLOG && touch $TMPLOG

BUILD_BINUTILS_DIR=$BUILD_DIR/binutils
BUILD_GMP_DIR=$BUILD_DIR/gmp
BUILD_MPFR_DIR=$BUILD_DIR/mpfr
BUILD_GCC_DIR=$BUILD_DIR/gcc

TIMESTAMPS_DIR=$BUILD_DIR/timestamps
mkdir -p $TIMESTAMPS_DIR

if [ "$V" = 0 ] ; then
    echo "To follow build, run: tail -F $TMPLOG"
fi

# returns 0 iff the string in $2 matches the pattern in $1
# $1: pattern
# $2: string
pattern_match ()
{
    echo "$2" | grep -q -E -e "$1"
}

# Find if a given shell program is available.
# We need to take care of the fact that the 'which <foo>' command
# may return either an empty string (Linux) or something like
# "no <foo> in ..." (Darwin). Also, we need to redirect stderr
# to /dev/null for Cygwin
#
# $1: variable name
# $2: program name
#
# Result: set $1 to the full path of the corresponding command
#         or to the empty/undefined string if not available
#
find_program ()
{
    local PROG
    PROG=`which $2 2>/dev/null`
    if [ -n "$PROG" ] ; then
        if pattern_match '^no ' "$PROG"; then
            PROG=
        fi
    fi
    eval $1="$PROG"
}

# Copy a directory, create target location if needed
#
# $1: source directory
# $2: target directory location
#
copy_directory ()
{
    local SRCDIR="$1"
    local DSTDIR="$2"
    if [ ! -d "$SRCDIR" ] ; then
        panic "Can't copy from non-directory: $SRCDIR"
    fi
    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar cf - *) | (tar xf - -C "$DSTDIR")
    fail_panic "Cannot copy to directory: $DSTDIR"
}

find_program CMD_WGET wget
find_program CMD_CURL curl
find_program CMD_SCRP scp

# Download a file with either 'curl', 'wget' or 'scp'
#
# $1: source URL (e.g. http://foo.com, ssh://blah, /some/path)
# $2: target file
download_file ()
{
    # Is this HTTP, HTTPS or FTP ?
    if pattern_match "^(http|https|ftp):.*" "$1"; then
        if [ -n "$CMD_WGET" ] ; then
            run $CMD_WGET -O $2 $1
        elif [ -n "$CMD_CURL" ] ; then
            run $CMD_CURL -o $2 $1
        else
            echo "Please install wget or curl on this machine"
            exit 1
        fi
        return
    fi

    # Is this SSH ?
    # Accept both ssh://<path> or <machine>:<path>
    #
    if pattern_match "^(ssh|[^:]+):.*" "$1"; then
        if [ -n "$CMD_SCP" ] ; then
            scp_src=`echo $1 | sed -e s%ssh://%%g`
            run $CMD_SCP $scp_src $2
        else
            echo "Please install scp on this machine"
            exit 1
        fi
        return
    fi

    # Is this a file copy ?
    # Accept both file://<path> or /<path>
    #
    if pattern_match "^(file://|/).*" "$1"; then
        cp_src=`echo $1 | sed -e s%^file://%%g`
        run cp -f $cp_src $2
        return
    fi

    # Unknown schema
    echo "ERROR: Unsupported source URI: $1"
    exit 1
}

# A variant of 'download_file' used to specify the target directory
# $1: source URL
# $2: target directory
download_file_to ()
{
    local URL="$1"
    local DIR="$2"
    local DST="$DIR/`basename $URL`"
    mkdir -p $DIR
    download_file "$URL" "$DST"
}

# Pack a given archive
#
# $1: archive file path (including extension)
# $2: source directory for archive content
# $3+: list of files (including patterns), all if empty
pack_archive ()
{
    local ARCHIVE="$1"
    local SRCDIR="$2"
    local SRCFILES
    local TARFLAGS ZIPFLAGS
    shift; shift;
    if [ -z "$1" ] ; then
        SRCFILES="*"
    else
        SRCFILES="$@"
    fi
    if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then
        ARCHIVE="`pwd`/$ARCHIVE"
    fi
    mkdir -p `dirname $ARCHIVE`
    if [ "$VERBOSE2" = "yes" ] ; then
        TARFLAGS="vcf"
        ZIPFLAGS="-9r"
    else
        TARFLAGS="cf"
        ZIPFLAGS="-9qr"
    fi
    case "$ARCHIVE" in
        *.zip)
            (cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES)
            ;;
        *.tar)
            (cd $SRCDIR && run tar $TARFLAGS "$ARCHIVE" $SRCFILES)
            ;;
        *.tar.gz)
            (cd $SRCDIR && run tar z$TARFLAGS "$ARCHIVE" $SRCFILES)
            ;;
        *.tar.bz2)
            (cd $SRCDIR && run tar j$TARFLAGS "$ARCHIVE" $SRCFILES)
            ;;
        *)
            panic "Unsupported archive format: $ARCHIVE"
            ;;
    esac
}

# Does the host compiler generate 32-bit machine code?
# If not, add the -m32 flag to the compiler name to ensure this.
#
compute_host_flags ()
{
    HOST_CC=${CC:-gcc}
    HOST_CXX=${CXX-g++}
    echo -n "Checking for ccache..."
    find_program CMD_CCACHE ccache
    if [ -n "$CMD_CCACHE" ] ; then
        echo "$HOST_CC" | tr ' ' '\n' | grep -q -e "ccache"
        if [ $? = 0 ] ; then
            echo "yes (ignored)"
        else
            echo "yes"
            HOST_CC="ccache $HOST_CC"
            HOST_CXX="ccache $HOST_CXX"
        fi
    else
        echo "no"
    fi
    echo -n "Checking whether the compiler generates 32-bit code... "
    cat > $BUILD_DIR/conftest.c << EOF
    /* This will fail to compile if void* is not 32-bit */
    int test_array[1 - 2*(sizeof(void*) != 4)];
EOF
    $HOST_CC -o $BUILD_DIR/conftest.o -c $BUILD_DIR/conftest.c > $BUILD_DIR/conftest.log 2>&1
    if [ $? != 0 ] ; then
        echo "no"
        HOST_CC="$HOST_CC -m32"
        HOST_CXX="$HOST_CXX -m32"
    else
        echo "yes"
    fi
    export CC="$HOST_CC"
    export CXX="$HOST_CXX"
}

compute_host_flags

# Return the value of a given named variable
# $1: variable name
#
# example:
#    FOO=BAR
#    BAR=ZOO
#    echo `var_value $FOO`
#    will print 'ZOO'
#
var_value ()
{
    # find a better way to do that ?
    eval echo "$`echo $1`"
}

var_list_append ()
{
    local VARNAME=$1
    local VARVAL=`var_value $VARNAME`
    shift
    if [ -z "$VARVAL" ] ; then
        eval $VARNAME=\"$@\"
    else
        eval $VARNAME=\"$VARVAL $@\"
    fi
}

var_list_prepend ()
{
    local VARNAME=$1
    local VARVAL=`var_value $VARNAME`
    shift
    if [ -z "$VARVAL" ] ; then
        eval $VARNAME=\"$@\"
    else
        eval $VARNAME=\"$@ $VARVAL\"
    fi
}

_list_first ()
{
    echo $1
}

_list_rest ()
{
    shift
    echo "$@"
}

var_list_pop_first ()
{
    local VARNAME=$1
    local VARVAL=`var_value $VARNAME`
    local FIRST=`_list_first $VARVAL`
    eval $VARNAME=\"`_list_rest $VARVAL`\"
    echo "$FIRST"
}

_list_first ()
{
    echo $1
}

_list_rest ()
{
    shift
    echo "$@"
}

var_list_first ()
{
    local VAL=`var_value $1`
    _list_first $VAL
}

var_list_rest ()
{
    local VAL=`var_value $1`
    _list_rest $VAL
}

ALL_TASKS=

# Define a new task for this build script
# $1: Task name (e.g. build_stuff)
# $2: Task description
# $3: Optional: command name (will be cmd_$1 by default)
#
task_define ()
{
    local TASK="$1"
    local DESCR="$2"
    local COMMAND="${3:-cmd_$1}"

    var_list_append ALL_TASKS $TASK
    task_set $TASK name "$TASK"
    task_set $TASK descr "$DESCR"
    task_set $TASK cmd "$COMMAND"
    task_set $TASK deps ""
}

task_set ()
{
    local TASK="$1"
    local FIELD="$2"
    shift; shift;
    eval TASK_${TASK}__${FIELD}=\"$@\"
}

task_get ()
{
    var_value TASK_$1__$2
}

# return the list of dependencies for a given task
task_get_deps ()
{
    task_get $1 deps
}

task_get_cmd ()
{
    task_get $1 cmd
}

task_get_descr ()
{
    task_get $1 descr
}

# $1: task name
# $2+: other tasks this task depends on.
task_depends ()
{
    local TASK="$1"
    shift;
    var_list_append TASK_${TASK}__deps $@
}

task_dump ()
{
    local TASK
    for TASK in $ALL_TASKS; do
        local DEPS="`task_get_deps $TASK`"
        local CMD="`task_get_cmd $TASK`"
        local DESCR="`task_get_descr $TASK`"
        echo "TASK $TASK: $DESCR: $CMD"
        echo ">  $DEPS"
    done
}

task_visit ()
{
    task_set $TASK visit 1
}

task_unvisit ()
{
    task_set $TASK visit 0
}

task_is_visited ()
{
    [ `task_get $TASK visit` = 1 ]
}

task_queue_reset ()
{
    TASK_QUEUE=
}

task_queue_push ()
{
    var_list_append TASK_QUEUE $1
}

task_queue_pop ()
{
    local FIRST=`var_list_first TASK_QUEUE`
    TASK_QUEUE=`var_list_rest TASK_QUEUE`
}

do_all_tasks ()
{
    local TASK
    local TASK_LIST=
    task_queue_reset
    # Clear visit flags
    for TASK in $ALL_TASKS; do
        task_unvisit $TASK
    done
    task_queue_push $1
    while [ -n "$TASK_QUEUE" ] ; do
        TASK=`task_queue_pop`
        if task_is_visited $TASK; then
            continue
        fi
        # Prepend the task to the list if its timestamp is not set
        if [ ! -f $TIMESTAMPS_DIR/$TASK ]; then
            var_list_prepend TASK_LIST $TASK
        fi
        # Add all dependencies to the work-queue
        local SUBTASK
        for SUBTASK in `task_get_deps $TASK`; do
            task_queue_push $SUBTASK
        done
        task_visit $TASK
    done

    # Now, TASK_LIST contains the
}

do_task ()
{
    local TASK="$1"
    local DEPS="`task_get_deps $TASK`"
    local DESCR="`task_get_descr $TASK`"
    local DEP
    #echo ">> $TASK: $DEPS"
    if [ -f "$TIMESTAMPS_DIR/$TASK" ] ; then
        echo "Skipping $1: already done."
        return 0
    fi

    # do_task for any dependents
    for DEP in $DEPS; do
        #echo "  ? $DEP"
        if [ ! -f "$TIMESTAMPS_DIR/$DEP" ] ; then
            do_task $DEP
        fi
    done

    echo "Running: $DESCR"
    if [ "$V" != 0 ] ; then
        eval `task_get_cmd $TASK`
    else
        eval `task_get_cmd $TASK` >> $TMPLOG 2>&1
    fi
    if [ $? != 0 ] ; then
        echo "ERROR: Cannot $DESCR"
        exit 1
    fi

    touch "$TIMESTAMPS_DIR/$TASK"
}

task_define download_toolchain_sources "Download toolchain sources from android.git.kernel.org"
cmd_download_toolchain_sources ()
{
    $NDK_DOWNLOAD_TOOLCHAIN_SOURCES_SH $TOOLCHAIN_SRC_DIR
}

task_define download_packages "Download Ubuntu packages"
cmd_download_packages ()
{
    local PACKAGE

    mkdir -p $DOWNLOAD_DIR &&
    for PACKAGE in $UBUNTU_PACKAGES; do
        echo "Downloading $PACKAGE"
        download_file_to $PACKAGE $DOWNLOAD_DIR
    done
}

task_define build_sysroot "Build sysroot"
task_depends build_sysroot download_packages

cmd_build_sysroot ()
{
    local PACKAGE
    for PACKAGE in $UBUNTU_PACKAGES; do
        local SRC_PKG=$DOWNLOAD_DIR/`basename $PACKAGE`
        echo "Extracting $SRC_PKG"
        dpkg -x $SRC_PKG $ORG_SYSROOT_DIR/
    done
}

# Now, we need to patch libc.so which is actually a linker script
# referencing /lib and /usr/lib. Do the same for libpthread.so
patch_library ()
{
    echo "Patching $1"
    sed -i -e "s! /lib/! !g" -e "s! /usr/lib/! !g" $1
}

task_define copy_sysroot "Fix and copy sysroot"
task_depends copy_sysroot build_sysroot
cmd_copy_sysroot ()
{
    local SL

    # Copy the content of $BUILD_DIR/lib to $SYSROOT_DIR/usr/lib
    copy_directory $ORG_SYSROOT_DIR/lib $SYSROOT_DIR/usr/lib
    copy_directory $ORG_SYSROOT_DIR/usr/lib $SYSROOT_DIR/usr/lib
    copy_directory $ORG_SYSROOT_DIR/usr/include $SYSROOT_DIR/usr/include

    # We need to fix the symlink like librt.so -> /lib/librt.so.1
    # in $SYSROOT_DIR/usr/lib, they should point to librt.so.1 instead now.
    SYMLINKS=`ls -l $SYSROOT_DIR/usr/lib | grep /lib/ | awk '{ print $11; }'`
    cd $SYSROOT_DIR/usr/lib
    for SL in $SYMLINKS; do
        # convert /lib/libfoo.so.<n> into 'libfoo.so.<n>' for the target
        local DST=`echo $SL | sed -e 's!^/lib/!!g'`
        # convery libfoo.so.<n> into libfoo.so for the source
        local SRC=`echo $DST | sed -e 's!\.[0-9]*$!!g'`
        echo "Fixing symlink $SRC --> $DST"
        ln -sf $DST $SRC
    done

    patch_library $SYSROOT_DIR/usr/lib/libc.so
    patch_library $SYSROOT_DIR/usr/lib/libpthread.so
}



task_define configure_binutils "Configure binutils-$BINUTILS_VERSION"
task_depends configure_binutils download_toolchain_sources copy_sysroot
cmd_configure_binutils ()
{
    OUT_DIR=$BUILD_BINUTILS_DIR
    mkdir -p $OUT_DIR && cd $OUT_DIR &&
    $TOOLCHAIN_SRC_DIR/binutils/binutils-$BINUTILS_VERSION/configure \
        --prefix=$INSTALL_DIR \
        --with-sysroot=$SYSROOT_DIR \
        --target=$GCC_TARGET
}

task_define build_binutils "Build binutils-$BINUTILS_VERSION"
task_depends build_binutils configure_binutils
cmd_build_binutils ()
{
    cd $BUILD_BINUTILS_DIR &&
    make $MAKE_FLAGS
}

task_define install_binutils "Install binutils-$BINUTILS_VERSION"
task_depends install_binutils build_binutils

cmd_install_binutils ()
{
    cd $BUILD_BINUTILS_DIR &&
    make install
}

task_define extract_gmp "Extract sources for gmp-$GMP_VERSION"
task_depends extract_gmp download_toolchain_sources
cmd_extract_gmp ()
{
    OUT_DIR=$BUILD_GMP_DIR
    mkdir -p $OUT_DIR && cd $OUT_DIR &&
    tar xjf $TOOLCHAIN_SRC_DIR/gmp/gmp-$GMP_VERSION.tar.bz2
}

task_define configure_gmp "Configure gmp-$GMP_VERSION"
task_depends configure_gmp extract_gmp install_binutils
cmd_configure_gmp ()
{
    export ABI=32 &&
    cd $BUILD_GMP_DIR && mkdir -p build && cd build &&
    ../gmp-$GMP_VERSION/configure --prefix=$INSTALL_DIR --host=$GMP_TARGET --disable-shared
}

task_define build_gmp "Build gmp-$GMP_VERSION"
task_depends build_gmp configure_gmp
cmd_build_gmp ()
{
    export ABI=32 &&
    cd $BUILD_GMP_DIR/build &&
    make $MAKE_FLAGS
}

task_define install_gmp "Install gmp-$GMP_VERSION"
task_depends install_gmp build_gmp
cmd_install_gmp ()
{
    cd $BUILD_GMP_DIR/build &&
    make install
}

# Third, build mpfr
task_define extract_mpfr "Extract sources from mpfr-$MPFR_VERSION"
task_depends extract_mpfr download_toolchain_sources
cmd_extract_mpfr ()
{
    OUT_DIR=$BUILD_MPFR_DIR
    mkdir -p $OUT_DIR && cd $OUT_DIR &&
    tar xjf $TOOLCHAIN_SRC_DIR/mpfr/mpfr-$MPFR_VERSION.tar.bz2
}

task_define configure_mpfr "Configure mpfr-$MPFR_VERSION"
task_depends configure_mpfr extract_mpfr install_gmp
cmd_configure_mpfr ()
{
    cd $BUILD_MPFR_DIR && mkdir -p build && cd build &&
    ../mpfr-$MPFR_VERSION/configure \
        --prefix=$INSTALL_DIR \
        --host=$GMP_TARGET \
        --with-gmp=$INSTALL_DIR \
        --with-sysroot=$SYSROOT_DIR \
        --disable-shared
}

task_define build_mpfr "Build mpfr-$MPFR_VERSION"
task_depends build_mpfr configure_mpfr
cmd_build_mpfr ()
{
    cd $BUILD_MPFR_DIR/build &&
    make $MAKE_FLAGS
}

task_define install_mpfr "Install mpfr-$MPFR_VERSION"
task_depends install_mpfr build_mpfr
cmd_install_mpfr ()
{
    cd $BUILD_MPFR_DIR/build &&
    make install
}


# Fourth, the compiler itself
task_define configure_gcc "Configure gcc-$GCC_VERSION"
task_depends configure_gcc download_toolchain_sources install_binutils install_gmp install_mpfr
cmd_configure_gcc ()
{
    OUT_DIR=$BUILD_GCC_DIR
    mkdir -p $OUT_DIR && cd $OUT_DIR &&
    export PATH=$INSTALL_DIR/bin:$OLD_PATH &&
    export CFLAGS="-m32" &&
    export CC_FOR_TARGET="$HOST_CC" &&
    export LD_LIBRARY_PATH=$INSTALL_DIR/lib:$OLD_LD_LIBRARY_PATH &&
    export LDFLAGS="-L$INSTALL_DIR/lib" &&
    $TOOLCHAIN_SRC_DIR/gcc/gcc-$GCC_VERSION/configure \
        --prefix=$INSTALL_DIR \
        --with-sysroot=$SYSROOT_DIR \
        --disable-nls \
        --with-gmp=$INSTALL_DIR \
        --with-mpfr=$INSTALL_DIR \
        --target=$GCC_TARGET \
        --disable-plugin \
        --enable-languages=c,c++
}

task_define build_gcc "Build gcc-$GCC_VERSION"
task_depends build_gcc configure_gcc
cmd_build_gcc ()
{
    export PATH=$INSTALL_DIR/bin:$OLD_PATH &&
    export LD_LIBRARY_PATH=$INSTALL_DIR/lib:$OLD_LD_LIBRARY_PATH &&
    cd $BUILD_GCC_DIR &&
    make $MAKE_FLAGS
}

task_define install_gcc "Install gcc-$GCC_VERSION"
task_depends install_gcc build_gcc
cmd_install_gcc ()
{
    export PATH=$INSTALL_DIR/bin:$OLD_PATH &&
    export LD_LIBRARY_PATH=$INSTALL_DIR/lib:$OLD_LD_LIBRARY_PATH &&
    cd $BUILD_GCC_DIR &&
    make install
}

task_define cleanup_toolchain "Cleanup toolchain"
task_depends cleanup_toolchain install_gcc
cmd_cleanup_toolchain ()
{
    # Remove un-needed directories and files
    rm -rf $INSTALL_DIR/share
    rm -rf $INSTALL_DIR/man
    rm -rf $INSTALL_DIR/info
    rm -rf $INSTALL_DIR/lib32
    rm -rf $INSTALL_DIR/libexec/*/*/install-tools

    (strip $INSTALL_DIR/bin/*)
    true
}

task_define package_toolchain "Package final toolchain"
task_depends package_toolchain cleanup_toolchain
cmd_package_toolchain ()
{
    pack_archive $TOOLCHAIN_ARCHIVE "`dirname $INSTALL_DIR`" "`basename $INSTALL_DIR`"
}

#do_task package_toolchain
do_task package_toolchain

echo "Done, see $TOOLCHAIN_ARCHIVE"