#!/bin/sh
#
# Copyright (C) 2011 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.
#
# Rebuild all host toolchains and programs
#

PROGDIR=$(dirname $0)
. $PROGDIR/prebuilt-common.sh

NDK_DIR=$ANDROID_NDK_ROOT
register_var_option "--ndk-dir=<path>" NDK_DIR "NDK installation directory"

SYSTEMS=$HOST_TAG
# Replace x86_64 by x86 at the end of SYSTEMS since we will produce
# 32-bit toolchains by default, i.e. unless you use the --try-64 flag
if [ "${SYSTEMS%%x86_64}" != "$SYSTEMS" ]; then
    SYSTEMS=${SYSTEMS%%x86_64}x86
fi
# If we are on Linux, we are able to generate the Windows binaries
# with the mingw32 cross-toolchain.
if [ "$SYSTEMS" = "linux-x86" ]; then
    SYSTEMS=$SYSTEMS",windows"
    # If darwin toolchain exist, build darwin too
    if [ -f "${DARWIN_TOOLCHAIN}-gcc" ]; then
        SYSTEMS=$SYSTEMS",darwin-x86"
    fi
fi
CUSTOM_SYSTEMS=
register_option "--systems=<names>" do_SYSTEMS "List of host systems to build for"
do_SYSTEMS () { CUSTOM_SYSTEMS=true; SYSTEMS=$1; }

ARCHS=$(find_ndk_unknown_archs)
ARCHS="$DEFAULT_ARCHS $ARCHS"
register_var_option "--arch=<list>" ARCHS "List of target archs to build for"

PACKAGE_DIR=
register_var_option "--package-dir=<path>" PACKAGE_DIR "Package toolchain into this directory"

DARWIN_SSH=
register_var_option "--darwin-ssh=<hostname>" DARWIN_SSH "Generate darwin packages on remote host"

NO_GEN_PLATFORMS=
register_var_option "--no-gen-platforms" NO_GEN_PLATFORMS "Don't generate platforms/ directory, use existing one"

GCC_VERSION_LIST="default" # it's arch defined by default so use default keyword
register_var_option "--gcc-version-list=<vers>" GCC_VERSION_LIST "List of GCC release versions"

LLVM_VERSION_LIST=$DEFAULT_LLVM_VERSION_LIST
register_var_option "--llvm-version-list=<vers>" LLVM_VERSION_LIST "List of LLVM release versions"

CHECK_FLAG=
do_check_option () { CHECK_FLAG="--check"; }
register_option "--check" do_check_option "Check host prebuilts"

register_try64_option

PROGRAM_PARAMETERS="<toolchain-src-dir>"
PROGRAM_DESCRIPTION=\
"This script can be used to rebuild all the host NDK toolchains at once.
You need to give it the path to the toolchain source directory, as
downloaded by the 'download-toolchain-sources.sh' dev-script."

extract_parameters "$@"

# Check toolchain source path
SRC_DIR="$PARAMETERS"
if [ -z "$SRC_DIR" ]; then
    echo "ERROR: Please provide the path to the toolchain source tree. See --help"
    exit 1
fi

if [ ! -d "$SRC_DIR" ]; then
    echo "ERROR: Not a directory: '$SRC_DIR'"
    exit 1
fi

if [ ! -f "$SRC_DIR/build/configure" -o ! -d "$SRC_DIR/gcc/gcc-$DEFAULT_GCC32_VERSION" -o ! -d "$SRC_DIR/gcc/gcc-$DEFAULT_GCC64_VERSION" ]; then
    echo "ERROR: The file $SRC_DIR/build/configure or"
    echo "       the directory $SRC_DIR/gcc/gcc-$DEFAULT_GCC32_VERSION or"
    echo "       $SRC_DIR/gcc/gcc-$DEFAULT_GCC64_VERSION does not exist"
    echo "This is not the top of a toolchain tree: $SRC_DIR"
    echo "You must give the path to a copy of the toolchain source directories"
    echo "created by 'download-toolchain-sources.sh."
    exit 1
fi

# Now we can do the build
BUILDTOOLS=$ANDROID_NDK_ROOT/build/tools

if [ -z "$NO_GEN_PLATFORMS" ]; then
    echo "Preparing the build..."
    run $BUILDTOOLS/gen-platforms.sh --minimal --dst-dir=$NDK_DIR --ndk-dir=$NDK_DIR --arch=$(spaces_to_commas $ARCHS)
    fail_panic "Could not generate minimal sysroot!"
else
    if [ ! -d "$NDK_DIR/platforms" ]; then
        echo "ERROR: --no-gen-platforms used but directory missing: $NDK_DIR/platforms"
        exit 1
    fi
fi

SYSTEMS=$(commas_to_spaces $SYSTEMS)
ARCHS=$(commas_to_spaces $ARCHS)

# Detect unknown arch
UNKNOWN_ARCH=$(filter_out "$DEFAULT_ARCHS" "$ARCHS")
if [ ! -z "$UNKNOWN_ARCH" ]; then
    ARCHS=$(filter_out "$UNKNOWN_ARCH" "$ARCHS")
fi

LLVM_VERSION_LIST=$(commas_to_spaces $LLVM_VERSION_LIST)

if [ "$DARWIN_SSH" -a -z "$CUSTOM_SYSTEMS" ]; then
    SYSTEMS=" darwin-x86"
fi

FLAGS=
if [ "$VERBOSE" = "yes" ]; then
    FLAGS=$FLAGS" --verbose"
fi
if [ "$VERBOSE2" = "yes" ]; then
    FLAGS=$FLAGS" --verbose"
fi
if [ "$TRY64" = "yes" ]; then
    FLAGS=$FLAGS" --try-64"
else
    force_32bit_binaries
fi
if [ "$PACKAGE_DIR" ]; then
    mkdir -p "$PACKAGE_DIR"
    fail_panic "Could not create package directory: $PACKAGE_DIR"
    FLAGS=$FLAGS" --package-dir=$PACKAGE_DIR"
fi

do_remote_host_build ()
{
    local SYSTEM="$1"
    local ARCH="$2"
    local REMOTE_HOST="$3"

    # 1/ Copy the NDK toolchain build scripts
    # 2/ Copy the toolchain sources/package
    # 3/ Ssh to unpack the build scripts, and run them
    # 4/ Copy back the generated prebuilt binaries
    #
    dump "Preparing remote build on $REMOTE_HOST..."

    # First try to create a remote temp directory on the remote host
    # Do it first so we can fail fast, not after spending time preparing
    # large tarballs.
    dump "Creating remote temp directory"
    TMPREMOTE=/tmp/ndk-$USER/darwin-prebuild
    run ssh $REMOTE_HOST "mkdir -p $TMPREMOTE && rm -rf $TMPREMOTE/*"
    fail_panic "Could not create temporary build directory on $REMOTE_HOST"

    # Now, package all the stuff we're going to send in a temporary
    # directory here on the machine, except toolchain sources
    TMPDARWIN=$NDK_TMPDIR/darwin  # Where we're going to package stuff
    log "Using temporary work directory: $TMPDARWIN"
    mkdir -p "$TMPDARWIN"
    dump "Prepare NDK build scripts"
    copy_directory "$ANDROID_NDK_ROOT/build" "$TMPDARWIN/ndk/build"
    copy_file_list "$ANDROID_NDK_ROOT" "$TMPDARWIN/ndk" sources/android/libthread_db
    copy_file_list "$ANDROID_NDK_ROOT" "$TMPDARWIN/ndk" "$STLPORT_SUBDIR"
    copy_file_list "$ANDROID_NDK_ROOT" "$TMPDARWIN/ndk" "$GABIXX_SUBDIR"
    copy_file_list "$ANDROID_NDK_ROOT" "$TMPDARWIN/ndk" sources/host-tools
    dump "Prepare platforms files"
    copy_directory "$NDK_DIR/platforms" "$TMPDARWIN/ndk/platforms"
    dump "Copying NDK build scripts and platform files to remote..."
    (cd "$TMPDARWIN" && tar czf - ndk) | (ssh $REMOTE_HOST tar xzf - -C $TMPREMOTE)
    fail_panic "Could not copy!"
    rm -rf $TMPDARWIN

    # Copy the toolchain sources
    dump "Copy toolchain sources to remote"
    ssh $REMOTE_HOST mkdir -p $TMPREMOTE/toolchain &&
    (cd "$SRC_DIR" && tar czf - .) | (ssh $REMOTE_HOST tar xzf - -C $TMPREMOTE/toolchain)
    fail_panic "Could not copy toolchain!"

    # Time to run the show :-)
    for ARCH in $(commas_to_spaces $ARCHS $UNKNOWN_ARCH); do
        dump "Running remote $ARCH toolchain build..."
        SYSROOT=$TMPREMOTE/ndk/platforms/android-$(get_default_api_level_for_arch $ARCH)/arch-$ARCH
        run ssh $REMOTE_HOST "$TMPREMOTE/ndk/build/tools/build-host-prebuilts.sh $TMPREMOTE/toolchain --package-dir=$TMPREMOTE/packages --arch=$ARCH --ndk-dir=$TMPREMOTE/ndk --no-gen-platforms"
        fail_panic "Could not build prebuilt $ARCH toolchain on Darwin!"
    done
    # Get the results
    dump "Copying back Darwin prebuilt packages..."
    mkdir -p $TMPDARWIN/packages && rm -rf $TMPDARWIN/packages/*
    run scp $REMOTE_HOST:$TMPREMOTE/packages/*-darwin-* $TMPDARWIN/packages
    fail_panic "Could not grab Darwin packages!"

    for PACK in $TMPDARWIN/packages/*; do
        if [ "$PACKAGE_DIR" ]; then
            echo "Copying $(basename $PACK) to $PACKAGE_DIR..."
            cp $PACK $PACKAGE_DIR/
        else
            echo "Unpacking $(basename $PACK) into $NDK_DIR..."
            unpack_archive $PACK $NDK_DIR
        fi
        fail_panic ""
    done

    dump "Cleaning up remote machine..."
    run ssh $REMOTE_HOST rm -rf $TMPREMOTE
}

for SYSTEM in $SYSTEMS; do

    # Add --mingw/--darwin flag
    TOOLCHAIN_FLAGS=$FLAGS
    CANADIAN_BUILD=no
    if [ "$HOST_TAG32" = "linux-x86" ]; then
        case "$SYSTEM" in
            windows)
                TOOLCHAIN_FLAGS=$TOOLCHAIN_FLAGS" --mingw"
                CANADIAN_BUILD=yes
                ;;
            darwin-x86)
                TOOLCHAIN_FLAGS=$TOOLCHAIN_FLAGS" --darwin"
                CANADIAN_BUILD=yes
                ;;
        esac
    fi

    # Should we do a remote build?
    if [ "$SYSTEM" != "$HOST_TAG32" -a "$CANADIAN_BUILD" != "yes" ]; then
        case $SYSTEM in
            darwin-*)
                if [ "$DARWIN_SSH" ]; then
                    do_remote_host_build "$SYSTEM" "$ARCH" "$DARWIN_SSH"
                else
                    echo "WARNING: Can't build $SYSTEM binaries on this system!"
                    echo "Consider using the --darwin-ssh option."
                fi
                continue
        esac
    fi

    # Determin the display system name
    SYSNAME=$SYSTEM
    if [ "$TRY64" = "yes" ]; then
        case $SYSTEM in
            darwin-x86|linux-x86)
                SYSNAME=${SYSTEM%%x86}x86_64
                ;;
            windows)
                SYSNAME=windows-x86_64
                ;;
        esac
    fi

    # First, ndk-stack
    echo "Building $SYSNAME ndk-stack"
    run $BUILDTOOLS/build-ndk-stack.sh $TOOLCHAIN_FLAGS --with-libbfd --src-dir=$SRC_DIR
    fail_panic "ndk-stack build failure!"

    echo "Building $SYSNAME ndk-depends"
    run $BUILDTOOLS/build-ndk-depends.sh $TOOLCHAIN_FLAGS
    fail_panic "ndk-depends build failure!"

    echo "Building $SYSNAME ndk-make"
    run $BUILDTOOLS/build-host-make.sh $TOOLCHAIN_FLAGS
    fail_panic "make build failure!"

    echo "Building $SYSNAME ndk-awk"
    run $BUILDTOOLS/build-host-awk.sh $TOOLCHAIN_FLAGS
    fail_panic "awk build failure!"

    echo "Building $SYSNAME ndk-sed"
    run $BUILDTOOLS/build-host-sed.sh $TOOLCHAIN_FLAGS
    fail_panic "sed build failure!"

    # ToDo: perl in windows/darwin cross.
    MAKE_PERL=no
    case $SYSTEM in
        linux*)
            MAKE_PERL=yes
        ;;
        darwin*)
            # Only works if not cross compiling.
            if [ "$CANADIAN_BUILD" = "no" ] ; then
                MAKE_PERL=yes
            fi
        ;;
        *)
        ;;
    esac

    if [ "$MAKE_PERL" = "yes" ] ; then
        echo "Building $SYSNAME ndk-perl"
        run $BUILDTOOLS/build-host-perl.sh $TOOLCHAIN_FLAGS "$SRC_DIR"
        fail_panic "perl build failure!"
    fi

    echo "Building $SYSNAME ndk-python"
    run $BUILDTOOLS/build-host-python.sh $TOOLCHAIN_FLAGS "--toolchain-src-dir=$SRC_DIR" "--systems=$SYSTEM" "--force"
    fail_panic "python build failure!"

    echo "Building $SYSNAME ndk-yasm"
    run $BUILDTOOLS/build-host-yasm.sh "$SRC_DIR" "$NDK_DIR" $TOOLCHAIN_FLAGS
    fail_panic "yasm build failure!"

    if [ "$SYSTEM" = "windows" ]; then
        echo "Building $SYSNAME toolbox"
        run $BUILDTOOLS/build-host-toolbox.sh $FLAGS
        fail_panic "Windows toolbox build failure!"
    fi

    # Then the toolchains
    for ARCH in $ARCHS; do
        TOOLCHAIN_NAMES=$(get_toolchain_name_list_for_arch $ARCH)
        if [ "$GCC_VERSION_LIST" != "default" ]; then
           TOOLCHAINS=
           for VERSION in $(commas_to_spaces $GCC_VERSION_LIST); do
              for TOOLCHAIN in $TOOLCHAIN_NAMES; do
                 if [ $TOOLCHAIN != ${TOOLCHAIN%%$VERSION} ]; then
                    TOOLCHAINS="$TOOLCHAIN $TOOLCHAINS"
                 fi
              done
           done
           TOOLCHAIN_NAMES=$TOOLCHAINS
        fi
        if [ -z "$TOOLCHAIN_NAMES" ]; then
            echo "ERROR: Toolchains: "$(spaces_to_commas $GCC_VERSION_LIST)" are not available for arch: $ARCH"
            exit 1
        fi

        for TOOLCHAIN_NAME in $TOOLCHAIN_NAMES; do
            echo "Building $SYSNAME toolchain for $ARCH architecture: $TOOLCHAIN_NAME"
            run $BUILDTOOLS/build-gcc.sh "$SRC_DIR" "$NDK_DIR" $TOOLCHAIN_NAME $TOOLCHAIN_FLAGS --with-python=prebuilt -j$BUILD_NUM_CPUS
            fail_panic "Could not build $TOOLCHAIN_NAME-$SYSNAME!"
        done
    done

    # Build llvm and clang
    POLLY_FLAGS=
    if [ "$TRY64" != "yes" -a "$SYSTEM" != "windows" ]; then
        POLLY_FLAGS="--with-polly"
    fi
    for LLVM_VERSION in $LLVM_VERSION_LIST; do
        echo "Building $SYSNAME clang/llvm-$LLVM_VERSION"
        run $BUILDTOOLS/build-llvm.sh "$SRC_DIR" "$NDK_DIR" "llvm-$LLVM_VERSION" $TOOLCHAIN_FLAGS $POLLY_FLAGS $CHECK_FLAG -j$BUILD_NUM_CPUS
        fail_panic "Could not build llvm for $SYSNAME"
    done

    # Deploy ld.mcld
    $PROGDIR/deploy-host-mcld.sh --package-dir=$PACKAGE_DIR --systems=$SYSNAME
    fail_panic "Could not deploy ld.mcld for $SYSNAME"

    # We're done for this system
done

# Build tools common to all system
run $BUILDTOOLS/build-analyzer.sh "$SRC_DIR" "$NDK_DIR" "llvm-$DEFAULT_LLVM_VERSION" --package-dir="$PACKAGE_DIR"

if [ "$PACKAGE_DIR" ]; then
    echo "Done, please look at $PACKAGE_DIR"
else
    echo "Done"
fi

exit 0