#!/bin/sh
#
# Copyright (C) 2009 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 shell script is used to rebuild the Android NDK's prebuilt binaries.
#
#  The source tarballs must be located in $ANDROID_NDK_ROOT/build/archive
#  They will be located in $ANDROID_NDK_ROOT/build/toolchain after compilation
#

# include common function and variable definitions
. `dirname $0`/../core/ndk-common.sh

# number of jobs to run in parallel when running make
JOBS=$HOST_NUM_CPUS

TOOLCHAIN_NAME=arm-eabi-4.2.1
PLATFORM=android-3
ABI=arm
GCC_VERSION=4.2.1
GDB_VERSION=6.6

OPTION_HELP=no
OPTION_PLATFORM=
OPTION_FORCE_32=no
OPTION_REBUILD=no
OPTION_GCC_VERSION=
OPTION_GDB_VERSION=
OPTION_TOOLCHAIN_NAME=
OPTION_PACKAGE=

VERBOSE=no
for opt do
    optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
    case "$opt" in
    --help|-h|-\?) OPTION_HELP=yes
    ;;
    --verbose)
        if [ "$VERBOSE" = "yes" ] ; then
            VERBOSE2=yes
        else
            VERBOSE=yes
        fi
        ;;
    --gcc-version=*)
        OPTION_GCC_VERSION=$optarg
        ;;
    --gdb-version=*)
        OPTION_GDB_VERSION=$optarg
        ;;
    --toolchain=*)
        OPTION_TOOLCHAIN_NAME=$optarg
        ;;
    --package=*)
        OPTION_PACKAGE="$optarg"
        ;;
    --platform=*)
        PLATFORM=$optarg
        ;;
    --abi=*)
        ABI=$optarg
        ;;
    --force-download)
        OPTION_FORCE_DOWNLOAD=yes
        OPTION_FORCE_BUILD=yes
        ;;
    --force-build)
        OPTION_FORCE_BUILD=yes
        ;;
    --verbose)
        VERBOSE=yes
        ;;
    *)
        echo "unknown option '$opt', use --help"
        exit 1
    esac
done

if [ $OPTION_HELP = "yes" ] ; then
    echo "Rebuild the prebuilt binaries for the Android NDK toolchain."
    echo "This script will automatically download the sources from the"
    echo "Internet, unless you use the --package=<file> option to specify"
    echo "the exact source package to use."
    echo ""
    echo "See build/tools/download-toolchain-sources.sh for a tool that"
    echo "can create a compatible source package from the current"
    echo "git repositories."
    echo ""
    echo "options (defaults are within brackets):"
    echo ""
    echo "  --help                   print this message"
    echo "  --gcc-version=<version>  select GCC version [$GCC_VERSION]"
    echo "  --gdb-version=<version>  select GDB version [$GDB_VERSION]"
    echo "  --toolchain=<name>       toolchain name (default is $TOOLCHAIN_NAME)"
    echo "  --package=<file>         specify download source package"
    echo "  --platform=<name>        generate toolchain from platform <name> (default is $PLATFORM)"
    echo "  --abi=<name>             generate toolchain from abi <name> (default is $ABI)"
    echo "  --build-out=<path>       set Android build out directory"
    echo "  --force-download         force a download and unpacking of the toolchain sources"
    echo "  --force-build            force a rebuild of the sources"
    echo ""
    exit 0
fi

# Force generation of 32-bit binaries on 64-bit systems
case $HOST_TAG in
    *-x86_64)
        HOST_CFLAGS="$HOST_CFLAGS -m32"
        HOST_LDFLAGS="$HOST_LDFLAGS -m32"
        force_32bit_binaries  # to modify HOST_TAG and others
        ;;
esac

TMPLOG=/tmp/android-toolchain-build-$$.log
rm -rf $TMPLOG

if [ $VERBOSE = yes ] ; then
    run ()
    {
        echo "##### NEW COMMAND"
        echo "$@"
        $@ 2>&1
    }
    log ()
    {
        echo "LOG: $@"
    }
else
    echo "To follow build, please use in another terminal: tail -F $TMPLOG"
    run ()
    {
        echo "##### NEW COMMAND" >> $TMPLOG
        echo "$@" >> $TMPLOG
        $@ >>$TMPLOG 2>&1
    }
    log ()
    {
        echo "$@" > /dev/null
    }
fi

if [ -n "$OPTION_GCC_VERSION" ] ; then
    GCC_VERSION="$OPTION_GCC_VERSION"
    log "Using gcc version $GCC_VERSION"
else
    log "Using default gcc version $GCC_VERSION"
fi

if [ -n "$OPTION_GDB_VERSION" ] ; then
    GDB_VERSION="$OPTION_GDB_VERSION"
    log "Using gdb version $GDB_VERSION"
else
    log "Using default gdb version $GDB_VERSION"
fi

if [ -n "$OPTION_TOOLCHAIN_NAME" ] ; then
    TOOLCHAIN_NAME="$OPTION_TOOLCHAIN_NAME"
    log "Using toolchain name '$TOOLCHAIN_NAME'"
else
    TOOLCHAIN_NAME=arm-eabi-$GCC_VERSION
    log "Using default toolchain name '$TOOLCHAIN_NAME'"
fi

if [ -n "$OPTION_PACKAGE" ] ; then
    if [ ! -f "$OPTION_PACKAGE" ] ; then
        echo "Package is not a file: $OPTION_PACKAGE"
        exit 1
    fi
fi

ANDROID_NDK_ROOT=`cd $ANDROID_NDK_ROOT && pwd`
ANDROID_NDK_ARCHIVE=$ANDROID_NDK_ROOT/build/toolchains/archive
ANDROID_PLATFORMS_ROOT=$ANDROID_NDK_ROOT/build/platforms

# where all generated files will be placed
OUT=$ANDROID_NDK_ROOT/out/$TOOLCHAIN_NAME
PACKAGE_OUT=$OUT/packages
TIMESTAMP_OUT=$OUT/timestamps

# where the sysroot is located
ANDROID_SYSROOT=$ANDROID_NDK_ROOT/build/platforms/$PLATFORM/arch-$ABI

# where the toolchain binaries will be placed
ANDROID_TOOLCHAIN_OUT=$OUT/toolchain
ANDROID_TOOLCHAIN_SRC=$ANDROID_TOOLCHAIN_OUT/src
ANDROID_TOOLCHAIN_BUILD=$ANDROID_TOOLCHAIN_OUT/build

# where the gdbserver binaries will be placed
ANDROID_GDBSERVER_OUT=$OUT/gdbserver
ANDROID_GDBSERVER_BUILD=$ANDROID_GDBSERVER_OUT/build
ANDROID_GDBSERVER_DEST=$ANDROID_SYSROOT/usr/bin

# Let's check that we have a working md5sum here
A_MD5=`echo "A" | md5sum | cut -d' ' -f1`
if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then
    echo "Please install md5sum on this machine"
    exit 2
fi

# And wget too
WGET=`which wget`
CURL=`which curl`
SCP=`which scp`

# download a file with either 'curl', 'wget' or 'scp'
# $1: source
# $2: target
download_file ()
{
    # is this HTTP, HTTPS or FTP ?
    echo $1 | grep -q -e "^\(http\|https\):.*"
    if [ $? = 0 ] ; then
        if [ -n "$WGET" ] ; then
            $WGET -O $2 $1 
        elif [ -n "$CURL" ] ; then
            $CURL -o $2 $1
        else
            echo "Please install wget or curl on this machine"
            exit 1
        fi
        return
    fi

    # is this SSH ?
    echo $1 | grep -q -e "^ssh:.*"
    if [ $? = 0 ] ; then
        if [ -n "$SCP" ] ; then
            scp_src=`echo $1 | sed -e s%ssh://%%g`
            $SCP $scp_src $2
        else
            echo "Please install scp on this machine"
            exit 1
        fi
        return
    fi

    echo $1 | grep -q -e "^/.*"
    if [ $? = 0 ] ; then
        cp -f $1 $2
    fi
}

TOOLCHAIN_SRC=$ANDROID_TOOLCHAIN_SRC
TOOLCHAIN_BUILD=$ANDROID_TOOLCHAIN_BUILD
TOOLCHAIN_PREFIX=$ANDROID_NDK_ROOT/build/prebuilt/$HOST_TAG/$TOOLCHAIN_NAME
TOOLCHAIN_LICENSES=$ANDROID_NDK_ROOT/build/tools/toolchain-licenses

GDBSERVER_BUILD=$ANDROID_GDBSERVER_BUILD

timestamp_check ()
{
    [ -f $TIMESTAMP_OUT/$1/timestamp-$2 ]
}

timestamp_set ()
{
    mkdir -p $TIMESTAMP_OUT/$1
    touch $TIMESTAMP_OUT/$1/timestamp-$2
}

timestamp_clear ()
{
    rm -f $TIMESTAMP_OUT/$1/timestamp-*
}

timestamp_force ()
{
    rm -f $TIMESTAMP_OUT/$1/timestamp-$2
}

# this function will be used to download and verify a toolchain
# package 
# $1: directory name under build/archive  (e.g. 'android-toolchain')
#
download_package ()
{
    WORKSPACE=$ANDROID_NDK_ARCHIVE/$1
    if [ ! -d $WORKSPACE ] ; then
        echo "No directory named $1 under $ANDROID_NDK_ARCHIVE"
        exit 2
    fi
    SOURCES=$WORKSPACE/sources.txt
    if [ ! -f $SOURCES ] ; then
        echo "Missing sources.txt in $WORKSPACE"
        exit 2
    fi
    # First line must be file name
    PKGNAME=`cat $SOURCES | sed 1q`
    # Second line must be md5sum
    PKGSUM=`cat $SOURCES | sed 1d | sed 1q`
    if [ -z "$PKGNAME" -o -z "$PKGSUM" ] ; then
        echo "Corrupted file: $SOURCES"
        exit 2
    fi

    # Try to download the package if it is not there
    # the Third line of sources.txt, and all others behind
    # must be wget urls or something.
    PACKAGE_TARBALL=$PACKAGE_OUT/$PKGNAME
    if [ ! -f $PACKAGE_TARBALL ] ; then
        cat $SOURCES | sed 1,2d | while read src; do
            echo $src | grep -q -e "^/.*"
            if [ $? = 0 ] ; then
                if [ -f $src ] ; then
                    echo "Copy    : $PKGNAME"
                    echo "          from `dirname $src`"
                    echo "          into $PACKAGE_TARBALL"
                    run cp -f $src $PACKAGE_TARBALL
                    if [ $? = 0 ] ; then
                        break
                    fi
                    echo "Copy    : Problem copying from $src"
                else
                    echo "Copy    : Can't find $src (skipping)"
                fi
                continue
            fi
            echo $src | grep -q -e "^\(http\|https\|ftp\|ssh\):.*"
            if [ $? = 0 ] ; then
                echo "Download: $PKGNAME"
                echo "          from $src"
                echo "          into $PACKAGE_TARBALL"
                download_file $src $PACKAGE_TARBALL
                if [ $? = 0 ] ; then
                    break
                fi
                continue
            else
                "Copy    : Unknown method in $src"
            fi
        done
        if [ ! -f $PACKAGE_TARBALL ] ; then
            echo "ERROR: Could not copy or download $PKGNAME !"
            echo "Your probably need to edit $WORKSPACE/sources.txt"
            exit 1
        fi
    fi

    if ! timestamp_check $1 verify ; then
        SUM=`md5sum $PACKAGE_TARBALL | cut -d " " -f 1`
        if [ "$SUM" != "$PKGSUM" ] ; then
            echo "ERROR: Invalid MD5 Sum for $PACKAGE_TARBALL"
            echo "    Expected $PKGSUM"
            echo "    Computed $SUM"
            echo "You might want to use the --force-download option."
            exit 2
        fi

        echo "Verified: $PACKAGE_TARBALL"
        timestamp_set   $1 verify
        timestamp_force $1 unpack
    fi
    eval PKG_$1=$PACKAGE_TARBALL
}

# Unpack a given package in a target location
# $1: package name
# $2: target directory
#
unpack_package ()
{
    WORKSPACE=$ANDROID_NDK_ARCHIVE/$1
    SRCDIR=$2
    SRCPKG=`var_value PKG_$1`
    if ! timestamp_check $1 unpack; then
        echo "Unpack  : $1 sources"
        echo "          from $SRCPKG"
        echo "          into $SRCDIR"
        run rm -rf $SRCDIR
        run mkdir -p $SRCDIR
        TARFLAGS=xjf
        if [ $VERBOSE2 = yes ]; then
          TARFLAGS="v$TARFLAGS"
        fi
        run tar $TARFLAGS $SRCPKG -C $SRCDIR
        if [ $? != 0 ] ; then
            echo "ERROR: Could not unpack $1, See $TMPLOG"
            exit 1
        fi
        timestamp_set   $1 unpack
        timestamp_force $1 configure
    fi
}

if [ $OPTION_FORCE_DOWNLOAD ] ; then
    rm -rf $PACKAGE_OUT $ANDROID_TOOLCHAIN_SRC
    timestamp_force toolchain unpack
    timestamp_force toolchain verify
fi

if [ $OPTION_FORCE_BUILD ] ; then
    rm -rf $ANDROID_TOOLCHAIN_BUILD
    timestamp_clear toolchain
    timestamp_clear gdbserver
fi

# checks, we need more checks..
mkdir -p $PACKAGE_OUT
if [ $? != 0 ] ; then
    echo "Can't create download/archive directory for toolchain tarballs"
    exit 2
fi

if [ -n "$OPTION_PACKAGE" ] ; then
    PKG_toolchain="$OPTION_PACKAGE"
else
    download_package toolchain
fi

unpack_package   toolchain $ANDROID_TOOLCHAIN_SRC

# remove all info files from the unpacked toolchain sources
# they create countless little problems during the build
# if you don't have exactly the configuration expected by
# the scripts.
#
find $ANDROID_TOOLCHAIN_SRC -type f -a -name "*.info" -print0 | xargs -0 rm -f

# configure the toolchain
if ! timestamp_check toolchain configure; then
    echo "Configure: toolchain build"
    BUILD_SRCDIR=$TOOLCHAIN_SRC/build
	if [ ! -d $BUILD_SRCDIR ] ; then
        BUILD_SRCDIR=$TOOLCHAIN_SRC
    fi
    mkdir -p $TOOLCHAIN_BUILD &&
    cd $TOOLCHAIN_BUILD &&
    export ABI="32" &&  # needed to build a 32-bit gmp
    export CFLAGS="$HOST_CFLAGS" &&
    export LDFLAGS="$HOST_LDFLAGS" && run \
    $BUILD_SRCDIR/configure --target=arm-eabi \
                             --disable-nls \
                             --prefix=$TOOLCHAIN_PREFIX \
                             --with-sysroot=$ANDROID_SYSROOT \
                             --with-gcc-version=$GCC_VERSION \
                             --with-gdb-version=$GDB_VERSION
    if [ $? != 0 ] ; then
        echo "Error while trying to configure toolchain build. See $TMPLOG"
        exit 1
    fi
    timestamp_set   toolchain configure
    timestamp_force toolchain build
fi

# build the toolchain
if ! timestamp_check toolchain build ; then
    echo "Building : toolchain [this can take a long time]."
    cd $TOOLCHAIN_BUILD &&
    export CFLAGS="$HOST_CFLAGS" &&
    export LDFLAGS="$HOST_LDFLAGS" &&
    run make -j$JOBS
    if [ $? != 0 ] ; then
        echo "Error while building toolchain. See $TMPLOG"
        exit 1
    fi
    timestamp_set   toolchain build
    timestamp_force toolchain install
fi

# install the toolchain to its final location
if ! timestamp_check toolchain install ; then
    echo "Install  : toolchain binaries."
    cd $TOOLCHAIN_BUILD &&
    run make install
    if [ $? != 0 ] ; then
        echo "Error while installing toolchain. See $TMPLOG"
        exit 1
    fi
    # don't forget to copy the GPL and LGPL license files
    cp -f $TOOLCHAIN_LICENSES/COPYING $TOOLCHAIN_LICENSES/COPYING.LIB $TOOLCHAIN_PREFIX
    # remove some unneeded files
    rm -f $TOOLCHAIN_PREFIX/bin/*-gccbug
    rm -rf $TOOLCHAIN_PREFIX/man $TOOLCHAIN_PREFIX/info
    # strip binaries to reduce final package size
    strip $TOOLCHAIN_PREFIX/bin/*
    strip $TOOLCHAIN_PREFIX/arm-eabi/bin/*
    strip $TOOLCHAIN_PREFIX/libexec/gcc/*/*/cc1
    strip $TOOLCHAIN_PREFIX/libexec/gcc/*/*/cc1plus
    strip $TOOLCHAIN_PREFIX/libexec/gcc/*/*/collect2
    timestamp_set   toolchain install
    timestamp_force gdbserver configure
fi

# configure the gdbserver build now
if ! timestamp_check gdbserver configure; then
    echo "Configure: gdbserver build."
	if [ -d $TOOLCHAIN_SRC/gdb ] ; then
		GDB_SRCDIR=$TOOLCHAIN_SRC/gdb/gdb-$GDB_VERSION
    else
        GDB_SRCDIR=$TOOLCHAIN_SRC/gdb-$GDB_VERSION
    fi
    mkdir -p $GDBSERVER_BUILD
    cd $GDBSERVER_BUILD &&
    export CC="$TOOLCHAIN_PREFIX/bin/arm-eabi-gcc" &&
    export CFLAGS="-g -O2 -static -mandroid"  &&
    export LDFLAGS= &&
    run $GDB_SRCDIR/gdb/gdbserver/configure \
    --host=arm-eabi-linux \
    --with-sysroot=$ANDROID_SYSROOT
    if [ $? != 0 ] ; then
        echo "Could not configure gdbserver build. See $TMPLOG"
        exit 1
    fi
    timestamp_set   gdbserver configure
    timestamp_force gdbserver build
fi

# build gdbserver
if ! timestamp_check gdbserver build; then
    echo "Building : gdbserver."
    cd $GDBSERVER_BUILD &&
    run make -j$JOBS
    if [ $? != 0 ] ; then
        echo "Could not build gdbserver. See $TMPLOG"
        exit 1
    fi
    timestamp_set   gdbserver build
    timestamp_force gdbserver install
fi

# install gdbserver
#
# note that we install it in the toolchain bin directory
# not in $SYSROOT/usr/bin
#
if ! timestamp_check gdbserver install; then
    echo "Install  : gdbserver."
    DEST=$TOOLCHAIN_PREFIX/bin
    mkdir -p $DEST &&
    $TOOLCHAIN_PREFIX/bin/arm-eabi-strip $GDBSERVER_BUILD/gdbserver &&
    run cp -f $GDBSERVER_BUILD/gdbserver $DEST/gdbserver
    if [ $? != 0 ] ; then
        echo "Could not install gdbserver. See $TMPLOG"
        exit 1
    fi
    timestamp_set   gdbserver install
    timestamp_force package toolchain
fi

# package the toolchain
TOOLCHAIN_TARBALL=/tmp/android-ndk-prebuilt-$TOOLCHAIN_NAME-$HOST_TAG.tar.bz2
if ! timestamp_check package toolchain; then
    echo "Package  : $HOST_ARCH toolchain binaries"
    echo "           into $TOOLCHAIN_TARBALL"
    cd $ANDROID_NDK_ROOT &&
    TARFLAGS="cjf"
    if [ $VERBOSE = yes ] ; then
      TARFLAGS="v$TARFLAGS"
    fi
    run tar $TARFLAGS $TOOLCHAIN_TARBALL build/prebuilt/$HOST_TAG/$TOOLCHAIN_NAME
    if [ $? != 0 ] ; then
        echo "ERROR: Cannot package prebuilt toolchain binaries. See $TMPLOG"
        exit 1
    fi
    timestamp_set package toolchain
    echo "prebuilt toolchain is in $TOOLCHAIN_TARBALL"
else
    echo "prebuilt toolchain is in $TOOLCHAIN_TARBALL"
fi

echo "Done."
rm -f $TMPLOG