#
# 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.
#
#  This file contains various shell function definitions that can be
#  used to either build a static and shared libraries from sources, or
#  generate a Makefile to do it in parallel.
#

_BUILD_TAB=$(echo " " | tr ' ' '\t')

builder_command ()
{
    if [ -z "$_BUILD_MK" ]; then
        echo "$@"
        "$@"
    else
        echo "${_BUILD_TAB}$@" >> $_BUILD_MK
    fi
}


builder_log ()
{
    if [ "$_BUILD_MK" ]; then
        echo "${_BUILD_TAB}echo $@" >> $_BUILD_MK
    else
        log "$@"
    fi
}

# $1: Build directory
# $2: Optional Makefile name
builder_begin ()
{
    _BUILD_DIR_NEW=
    _BUILD_DIR=$1
    if [ ! -d "$_BUILD_DIR" ]; then
        mkdir -p "$_BUILD_DIR"
        fail_panic "Can't create build directory: $_BUILD_DIR"
        _BUILD_DIR_NEW=true
    else
        rm -rf "$_BUILD_DIR/*"
        fail_panic "Can't cleanup build directory: $_BUILD_DIR"
    fi
    _BUILD_TARGETS=
    _BUILD_PREFIX=
    _BUILD_MK=$2
    if [ -n "$_BUILD_MK" ]; then
        log "Creating temporary build Makefile: $_BUILD_MK"
        rm -f $_BUILD_MK &&
        echo "# Auto-generated by $0 - do not edit!" > $_BUILD_MK
        echo ".PHONY: all" >> $_BUILD_MK
        echo "all:" >> $_BUILD_MK
    fi

    builder_begin_module
}

# $1: Variable name
# out: Variable value
_builder_varval ()
{
    eval echo "\$$1"
}

_builder_varadd ()
{
    local _varname="$1"
    local _varval="$(_builder_varval $_varname)"
    shift
    if [ -z "$_varval" ]; then
        eval $_varname=\"$@\"
    else
        eval $_varname=\$$_varname\" $@\"
    fi
}


builder_set_prefix ()
{
    _BUILD_PREFIX="$@"
}

builder_begin_module ()
{
    _BUILD_CC=
    _BUILD_CXX=
    _BUILD_AR=
    _BUILD_C_INCLUDES=
    _BUILD_CFLAGS=
    _BUILD_CXXFLAGS=
    _BUILD_LDFLAGS_BEGIN_SO=
    _BUILD_LDFLAGS_END_SO=
    _BUILD_LDFLAGS_BEGIN_EXE=
    _BUILD_LDFLAGS_END_EXE=
    _BUILD_LDFLAGS=
    _BUILD_BINPREFIX=
    _BUILD_DSTDIR=
    _BUILD_SRCDIR=.
    _BUILD_OBJECTS=
    _BUILD_STATIC_LIBRARIES=
    _BUILD_SHARED_LIBRARIES=
    _BUILD_COMPILER_RUNTIME_LDFLAGS=-lgcc
}

builder_set_binprefix ()
{
    _BUILD_BINPREFIX=$1
    _BUILD_CC=${1}gcc
    _BUILD_CXX=${1}g++
    _BUILD_AR=${1}ar
}

builder_set_builddir ()
{
    _BUILD_DIR=$1
}

builder_set_srcdir ()
{
    _BUILD_SRCDIR=$1
}

builder_set_dstdir ()
{
    _BUILD_DSTDIR=$1
}

builder_cflags ()
{
    _builder_varadd _BUILD_CFLAGS "$@"
}

builder_sources ()
{
    local src srcfull obj cc cflags text
    if [ -z "$_BUILD_DIR" ]; then
        panic "Build directory not set!"
    fi
    if [ -z "$_BUILD_CC" ]; then
        _BUILD_CC=${CC:-gcc}
    fi
    if [ -z "$_BUILD_CXX" ]; then
        _BUILD_CXX=${CXX:-g++}
    fi
    for src in "$@"; do
        srcfull=$_BUILD_SRCDIR/$src
        if [ ! -f "$srcfull" ]; then
            echo "ERROR: Missing source file: $srcfull"
            exit 1
        fi
        obj=$src
        cflags=""
        for inc in $_BUILD_C_INCLUDES; do
            cflags=$cflags" -I$inc"
        done
        cflags=$cflags" -I$_BUILD_SRCDIR"
        case $obj in
            *.c)
                obj=${obj%%.c}
                text="C"
                cc=$_BUILD_CC
                cflags="$cflags $_BUILD_CFLAGS"
                ;;
            *.cpp)
                obj=${obj%%.cpp}
                text="C++"
                cc=$_BUILD_CXX
                cflags="$cflags $_BUILD_CXXFLAGS"
                ;;
            *.cc)
                obj=${obj%%.cc}
                text="C++"
                cc=$_BUILD_CXX
                cflags="$cflags $_BUILD_CXXFLAGS"
                ;;
            *.S|*.s)
                obj=${obj%%.$obj}
                text="ASM"
                cc=$_BUILD_CC
                cflags="$cflags $_BUILD_CFLAGS"
                ;;
            *)
                echo "Unknown source file extension: $obj"
                exit 1
                ;;
        esac

        # Source file path can include ../ path items, ensure
        # that the generated object do not back up the output
        # directory by translating them to __/
        obj=$(echo "$obj" | tr '../' '__/')

        # Ensure we have unwind tables in the generated machine code
        # This is useful to get good stack traces
        cflags=$cflags" -funwind-tables"

        obj=$_BUILD_DIR/$obj.o
        if [ "$_BUILD_MK" ]; then
            echo "$obj: $srcfull" >> $_BUILD_MK
        fi
        builder_log "${_BUILD_PREFIX}$text: $src"
        builder_command mkdir -p $(dirname "$obj")
        builder_command $cc -c -o "$obj" "$srcfull" $cflags
        fail_panic "Could not compile ${_BUILD_PREFIX}$src"
        _BUILD_OBJECTS=$_BUILD_OBJECTS" $obj"
    done
}

builder_host_executable ()
{
    local exe exename
    exename=$1
    exe=$_BUILD_DSTDIR/$exename$HOST_EXE
    if [ "$_BUILD_MK" ]; then
        _BUILD_TARGETS=$_BUILD_TARGETS" $exe"
        echo "$exe: $_BUILD_OBJECTS" >> $_BUILD_MK
    fi
    builder_log "${_BUILD_PREFIX}Executable: $exename$HOST_EXE"

    if [ -z "$_BUILD_CXX" ]; then
        _BUILD_CXX=${CXX:-g++}
    fi

    # Important: -lgcc must appear after objects and static libraries,
    #            but before shared libraries for Android. It doesn't hurt
    #            for other platforms.
    builder_command ${_BUILD_CXX} \
        -s \
        $_BUILD_OBJECTS \
        $_BUILD_STATIC_LIBRARIES \
        $_BUILD_SHARED_LIBRARIES \
        $_BUILD_LDFLAGS \
        -o $exe
    fail_panic "Could not create ${_BUILD_PREFIX}executable $libname"
}


builder_end ()
{
    if [ "$_BUILD_MK" ]; then
        echo "all: $_BUILD_TARGETS" >> $_BUILD_MK
        run make -j$NUM_JOBS -f $_BUILD_MK
        fail_panic "Could not build project!"
    fi

    if [ "$_BUILD_DIR_NEW" ]; then
        log "Cleaning up build directory: $_BUILD_DIR"
        rm -rf "$_BUILD_DIR"
        _BUILD_DIR_NEW=
    fi
}

# $1: Build directory
# $2: Optional Makefile name
builder_begin_host ()
{
    prepare_host_build
    builder_begin "$1" "$2"
    builder_set_prefix "$HOST_TAG "
}
