#!/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.
#

# This script is used to rebuild the Android goldfish arm-based kernel from
# sources.

###
### Utilities
###

# Implementation of 'dirname' that works safely when paths contains spaces
# $1: output variable name
# $2: path (may contain spaces)
# Usage: set_dirname FOO "/path/to/foo with spaces"
set_dirname ()
{
    eval $1="`dirname \"$2\"`"
}

# Same as set_dirname, but for the 'basename' command
set_basename ()
{
    eval $1="`basename \"$2\"`"
}

# Check the status of the previous command, and print a message and exit
# if it didn't succeed.
fail_panic ()
{
    if [ $? != 0 ]; then
        echo "ERROR: $@"
        exit 1
    fi
}

# Send a message to the log. Does nothing if --verbose is not used
log ()
{
    if [ "$VERBOSE" = "yes" ] ; then
        echo "$@"
    fi
}

# Run a comment. Print it to the output first if --verbose is used
run ()
{
    if [ "$VERBOSE" = "yes" ] ; then
        echo "### COMMAND: $@"
        $@
    else
        $@ >/dev/null 2>&1
    fi
}

fail_ifnotdir ()
{
    local _dir="$1"
    if [ ! -d "$_dir" ]; then
        shift
        echo "ERROR: $@ ($_dir)"
        exit 1
    fi
}

# Check wether program '$2' is in your path, and set variable $1 to its
# full path, or to empty otherwise.
# Example: find_program FOO foo
#
# $1: Variable name
# $2: Program name
find_program ()
{
    local FINDPROG_=`which $2 2>/dev/null`
    if [ -n "$FINDPROG_" ]; then
        eval $1="$FINDPROG_";
    else
        eval $1=
    fi
}

set_dirname  PROGDIR  "$0"
set_basename PROGNAME "$0"

###
### Get defaults
###

# Location of prebuilt kernel images relative to Android top-level
PREBUILT_KERNEL_DIR="prebuilts/qemu-kernel/arm"

# Look for top-level Android build source tree if not in environment

if [ -n "$ANDROID_BUILD_TOP" ] ; then
    OUT_DIR="$ANDROID_BUILD_TOP/$PREBUILT_KERNEL_DIR"
else
    OUT_DIR=
fi

TOOLCHAIN_NAME=arm-eabi-4.4.0
TOOLCHAIN_PREFIX=arm-eabi
GCC="$TOOLCHAIN_PREFIX-gcc"

JOBS="`cat /proc/cpuinfo | grep -e "^processor" | wc -l`"

###
### Parse options
###
OPTION_HELP=no
VERBOSE=no
VERBOSE2=no
OPTION_OUT_DIR=
OPTION_CC=

PARAMS=""

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
            ;;
        --out-dir=*)
            OPTION_OUT_DIR="$optarg"
            ;;
        --cc=*)
            OPTION_CC="$optarg"
            ;;
        --toolchain=*)
            TOOLCHAIN_NAME="$optarg"
            ;;
        --jobs=*)
            JOBS="$optarg"
            ;;
        -*)
            echo "unknown option '$opt', use --help"
            exit 1
            ;;
        *)
            if [ -z "$PARAMS" ] ; then
                PARAMS="$opt"
            else
                PARAMS="$PARAMS $opt"
            fi
            ;;
    esac
done

if [ "$OPTION_HELP" = "yes" ] ; then
    echo "Usage: $PROGNAME <options> /path/to/kernel"
    echo ""
    echo "Rebuild the prebuilt Android goldfish-specific kernel from sources."
    echo ""
    echo "Options (default are inside brackets):"
    echo ""
    echo "    --help                Print this message."
    echo "    --verbose             Enable verbose output."
    echo "    --android=<path>      Set Android top-level directory [$ANDROID_BUILD_TOP]"
    echo "    --out-dir=<path>      Set output directory [$OUT_DIR]"
    echo "    --toolchain=<name>    Toolchain name [$TOOLCHAIN_NAME]"
    echo "    --cc=<path>           Path to C compiler [$GCC]"
    echo "    --jobs=<count>        Perform <count> parallel builds [$JOBS]"
    echo ""
    echo "The --toolchain option is ignored if you use the --cc one."
    exit 0
fi

###
### Parse parameters
###

set_parameters ()
{
    if [ -z "$1" ] ; then
        echo "ERROR: Please specify path of kernel sources directory. See --help for details."
        exit 1
    fi
    if [ -n "$2" ]; then
        echo "ERROR: Too many arguments. See --help for details."
        exit 1
    fi
    KERNEL_DIR="$1"
    fail_ifnotdir "$KERNEL_DIR" "Missing kernel directory"
    fail_ifnotdir "$KERNEL_DIR/kernel" "Missing kernel-specific directory"
    fail_ifnotdir "$KERNEL_DIR/arch"   "Missing kernel-specific directory"
    fail_ifnotdir "$KERNEL_DIR/arch/arm/mach-goldfish" "Missing goldfish-specific directory"
}

set_parameters $PARAMS

###
### Determine build configuration
###

if [ -n "$OPTION_OUT_DIR" ] ; then
    OUT_DIR="$OPTION_OUT_DIR"
fi

if [ -z "$ANDROID_BUILD_TOP" ] ; then
    # Assume that we are under $ANDROID_BUILD_TOP/prebuilt/android-arm/kernel
    ANDROID_BUILD_TOP="`cd \"$PROGDIR\"/../../.. && pwd`"
    if [ -d "$ANDROID_BUILD_TOP" ]; then
        echo "Probed Android top-level directory: $ANDROID_BUILD_TOP"
    else
        echo "ERROR: Can't find Android top-leveld directory. Please define ANDROID_BUILD_TOP"
        exit 1
    fi
fi

if [ -z "$OUT_DIR" ] ; then
    OUT_DIR="$ANDROID_BUILD_TOP/$PREBUILT_KERNEL_DIR"
    if [ ! -d "$OUT_DIR" ]; then
        echo "ERROR: Missing default output dir: $OUT_DIR"
        echo "Please use --out-dir=<path> to specify alternative output directory."
        exit 1
    fi
fi

echo "Using output directory: $OUT_DIR"
mkdir -p "$OUT_DIR"
fail_panic "Could not create directory: $OUT_DIR"

# Find toolchain

if [ -n "$OPTION_CC" ]; then
    GCC="$OPTION_CC"
else
    find_program GCC $TOOLCHAIN_PREFIX-gcc
fi

if [ -z "$GCC" ]; then
    TOOLCHAIN_DIR="$ANDROID_BUILD_TOP/prebuilt/linux-x86/toolchain/$TOOLCHAIN_NAME/bin"
    if [ ! -d "$TOOLCHAIN_DIR" ] ; then
        echo "ERROR: Missing directory: $TOOLCHAIN_DIR"
        exit 1
    fi
    GCC="$TOOLCHAIN_DIR/$TOOLCHAIN_PREFIX-gcc"
    if [ ! -f "$GCC" ] ; then
        echo "ERROR: Missing program file: $GCC"
        exit 1
    fi
fi

CROSS_COMPILE="`echo $GCC | sed -e 's!gcc$!!g' `"
echo "Using cross-toolchain prefix: $CROSS_COMPILE"

# Do we have ccache in our path?
find_program CCACHE ccache
if [ -n "$CCACHE" ]; then
    echo "Using ccache program: $CCACHE"
    CROSS_COMPILE="ccache $CROSS_COMPILE"
fi

###
### Do the build
###

cd "$KERNEL_DIR"
ARCH=arm
SUBARCH=arm

export CROSS_COMPILE ARCH SUBARCH

# $1: defconfig name (e.g. goldfish or goldfish_armv7)
# $2: output suffix (e.g. "" or "-armv7")
do_build ()
{
    MAKE_FLAGS="-j$JOBS"
    # Use --verbose --verbose to see build commands
    if [ "$VERBOSE2" = "yes" ] ; then
        MAKE_FLAGS="$MAKE_FLAGS V=1"
    fi
    echo "Cleaning up source tree."
    git ls-files -o | xargs rm -f
    echo "Setting up $1_defconfig..."
    run make $1_defconfig
    fail_panic "Could not setup $1_defconfig build!"
    echo "Building $1_deconfig..."
    run make $MAKE_FLAGS
    fail_panic "Could not build $1_defconfig!"
    echo "Copying $1_defconfig binaries to $OUT_DIR."
    cp -f arch/arm/boot/zImage "$OUT_DIR/kernel-qemu$2"
    cp -f vmlinux "$OUT_DIR/vmlinux-qemu$2"
}

do_build goldfish
if [ -f arch/arm/configs/goldfish_armv7_defconfig ]; then
    do_build goldfish_armv7 -armv7
fi

echo "Done."
exit 0