#!/bin/sh
# Convenience wrapper for easily viewing/setting options that
# the project's CMake scripts will recognize.

# check for `cmake` command
type cmake > /dev/null 2>&1 || {
    echo "\
This package requires CMake, please install it first, then you may
use this configure script to access CMake equivalent functionality.\
" >&2;
    exit 1;
}

command="$0 $*"
sourcedir="$( cd "$( dirname "$0" )" && pwd )"
usage="\
Usage: $0 [OPTION]... [VAR=VALUE]...

  Build Options:
    --generator=GENERATOR       CMake generator to use (see cmake --help)
    --build-type=DIR            CMake build type: [RelWithDebInfo]
                                  - Debug: debugging flags enabled
                                  - MinSizeRel: minimal output size
                                  - Release: optimizations on, debugging off
                                  - RelWithDebInfo: release flags plus debugging
    --build-dir=DIR             place build files in directory [build]
    --bin-dir=DIR               executable directory [build/bin]
    --lib-dir=DIR               library directory [build/lib]
    --with-clang=FILE           path to clang++ executable
    --with-gcc=FILE             path to g++ executable
    --dual-build                build both with gcc and clang
    --no-examples               build libcppa without examples
    --no-qt-examples            build libcppa without Qt examples
    --no-protobuf-examples      build libcppa without protobuf examples
    --no-unit-tests             build libcppa without unit tests
    --build-static              build libcppa as static and shared library
    --build-static-only         build libcppa as static library only
    --with-opencl               build libcppa with OpenCL support
    --without-memory-management build libcppa without memory management

  Installation Directories:
    --prefix=PREFIX             installation directory [/usr/local]

  Optional Features:
    --enable-debug              compile in debugging mode
                                (always sets --build-type=Debug)
    --enable-perftools          use Google perftools
    --with-cppa-log-level=LVL   sets the debugging output, possible values:
                                  - ERROR
                                  - WARNING
                                  - INFO
                                  - DEBUG
                                  - TRACE
                                (implicitly sets --enable-debug)

  Platform-Dependent Adjustments:
    --disable-context-switching compile libcppa without context-switching actors
                                even if Boost.Context is available

  Required Packages in Non-Standard Locations:
    --with-boost=PATH           path to Boost install root

  Influential Environment Variables (only on first invocation
  per build directory):
    CXX                         C++ compiler command
    CXXFLAGS                    C++ compiler flags
"


# Appends a CMake cache entry definition to the CMakeCacheEntries variable.
#   $1 is the cache entry variable name
#   $2 is the cache entry variable type
#   $3 is the cache entry variable value
append_cache_entry ()
{
    CMakeCacheEntries="$CMakeCacheEntries -D $1:$2=$3"
}

# Creates a build directory via CMake.
#   $1 is the path to a compiler executable.
#   $2 is the suffix of the build directory.
#   $3 is the executable output path.
#   $4 is the library output path.
#   $5 is the CMake generator.
configure ()
{
    CMakeCacheEntries=$CMakeDefaultCache

    if [ -n "$1" ]; then
        append_cache_entry CMAKE_CXX_COMPILER FILEPATH $1
    fi

    if [ -n "$2" ]; then
        workdir="$builddir-$2"
    else
        workdir=$builddir
    fi
    workdirs="$workdirs $workdir"

    if [ -n "$3" ]; then
        append_cache_entry EXECUTABLE_OUTPUT_PATH PATH $3
    else
        append_cache_entry EXECUTABLE_OUTPUT_PATH PATH "$workdir/bin"
    fi

    if [ -n "$4" ]; then
        append_cache_entry LIBRARY_OUTPUT_PATH PATH $4
    else
        append_cache_entry LIBRARY_OUTPUT_PATH PATH "$workdir/lib"
    fi

    if [ -d $workdir ]; then
        # If a build directory exists, check if it has a CMake cache.
        if [ -f $workdir/CMakeCache.txt ]; then
            # If the CMake cache exists, delete it so that this configuration
            # is not tainted by a previous one.
            rm -f $workdir/CMakeCache.txt
        fi
    else
        mkdir -p $workdir
    fi

    cd $workdir

    if [ -n "$5" ]; then
        cmake -G "$5" $CMakeCacheEntries $sourcedir
    else
        cmake $CMakeCacheEntries $sourcedir
    fi

    echo "# This is the command used to configure this build" > config.status
    echo $command >> config.status
    chmod u+x config.status
}

# Set defaults.
builddir="$sourcedir/build"
CMakeCacheEntries=""
append_cache_entry CMAKE_BUILD_TYPE            STRING RelWithDebInfo
append_cache_entry CMAKE_INSTALL_PREFIX        PATH   /usr/local
append_cache_entry ENABLE_DEBUG                BOOL   false
append_cache_entry DISABLE_CONTEXT_SWITCHING   BOOL   false

# Parse arguments.
while [ $# -ne 0 ]; do
    case "$1" in
        -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
        *) optarg= ;;
    esac

    case "$1" in
        --help|-h)
            echo "${usage}" 1>&2
            exit 1
            ;;
        --generator=*)
            CMakeGenerator="$optarg"
            ;;
        --prefix=*)
            append_cache_entry CMAKE_INSTALL_PREFIX PATH $optarg
            ;;
        --enable-debug)
            append_cache_entry ENABLE_DEBUG BOOL true
            ;;
        --with-opencl)
            append_cache_entry ENABLE_OPENCL BOOL true
            ;;
        --without-memory-management)
            append_cache_entry DISABLE_MEM_MANAGEMENT BOOL true
            ;;
        --with-cppa-log-level=*)
            level=$(echo "$optarg" | tr '[:lower:]' '[:upper:]')
            case $level in
                ERROR)
                    append_cache_entry CPPA_LOG_LEVEL STRING 0
                    ;;
                WARNING)
                    append_cache_entry CPPA_LOG_LEVEL STRING 1
                    ;;
                INFO)
                    append_cache_entry CPPA_LOG_LEVEL STRING 2
                    ;;
                DEBUG)
                    append_cache_entry CPPA_LOG_LEVEL STRING 3
                    ;;
                TRACE)
                    append_cache_entry CPPA_LOG_LEVEL STRING 4
                    ;;
                *)
                    echo "Invalid log level '$level'. Try '$0 --help' to see valid values."
                    exit 1
                    ;;
            esac
            append_cache_entry ENABLE_DEBUG BOOL true
            ;;
        --disable-context-switching)
            append_cache_entry DISABLE_CONTEXT_SWITCHING BOOL true
            ;;
        --with-boost=*)
            append_cache_entry BOOST_ROOT PATH $optarg
            ;;
        --with-clang=*)
            clang=$optarg
            ;;
        --with-gcc=*)
            gcc=$optarg
            ;;
        --build-type=*)
            append_cache_entry CMAKE_BUILD_TYPE STRING $optarg
            ;;
        --build-dir=*)
            builddir=$optarg
            ;;
        --bin-dir=*)
            bindir=$optarg
            ;;
        --lib-dir=*)
            libdir=$optarg
            ;;
        --dual-build)
            dualbuild=1
            ;;
        --no-examples)
            append_cache_entry CPPA_NO_EXAMPLES STRING yes
            ;;
        --no-qt-examples)
            append_cache_entry CPPA_NO_QT_EXAMPLES STRING yes
            ;;
        --no-protobuf-examples)
            append_cache_entry CPPA_NO_PROTOBUF_EXAMPLES STRING yes
            ;;
        --no-unit-tests)
            append_cache_entry CPPA_NO_UNIT_TESTS STRING yes
            ;;
        --build-static)
            append_cache_entry CPPA_BUILD_STATIC STRING yes
            ;;
        --build-static-only)
            append_cache_entry CPPA_BUILD_STATIC_ONLY STRING yes
            ;;
        *)
            echo "Invalid option '$1'.  Try $0 --help to see available options."
            exit 1
            ;;
    esac
    shift
done

# At this point we save the global CMake variables so that configure() can
# later use them.
CMakeDefaultCache=$CMakeCacheEntries

if [ -n "$dualbuild" ]; then
    # Use what we got in $PATH if --with-clang or --with-gcc is not specified.
    if [ -z "$clang" ]; then
        clang=clang++
    fi
    if [ -z "$gcc" ]; then
        gcc=g++
    fi

    for i in gcc clang; do
        compiler="$(eval echo \$$i)"
        configure $compiler $i "" "" $CMakeGenerator
    done
else
    # Prefer Clang to GCC.
    if [ -n "$clang" ]; then
        compiler=$clang
    elif [ -n "$gcc" ]; then
        compiler=$gcc
    fi

    configure $compiler "" $bindir $libdir $CMakeGenerator
fi

printf "DIRS := %s\n\n" "$workdirs" > $sourcedir/Makefile
makefile=$(cat <<'EOT'
all:
	@for i in $(DIRS); do $(MAKE) -C $$i $@ || exit; done

test:
	@for i in $(DIRS); do $(MAKE) -C $$i $@ || exit; done

install:
	@for i in $(DIRS); do $(MAKE) -C $$i $@ || exit; done

uninstall:
	@for i in $(DIRS); do $(MAKE) -C $$i $@ || exit; done

clean:
	@for i in $(DIRS); do $(MAKE) -C $$i $@; done

distclean:
	rm -rf $(DIRS) Makefile

doc:
	$(MAKE) -C $(firstword $(DIRS)) $@

.PHONY: all test install uninstall clean distclean
EOT
)

echo "$makefile" >> $sourcedir/Makefile
