#!/usr/bin/env bash

# Copyright (c) 2015, Plume Design Inc. All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#    1. Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#    2. Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#    3. Neither the name of the Plume Design Inc. nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Plume Design Inc. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


# This script handles creating and expanding a minimized config
# that is a config with only changed values against the default config
# In addition it allows to include other "base" configs
# the include functionality is not part of kconfiglib so it is
# handled by this script plus the diffconf2.py helper
#
# To use the include for an existing config the simplest method is to
# - run make menuconfig
# - enter the name of the base config in Common/INCLUDE
# - upon exiting the minimized config will contain the include directive
#   and the file will contain just the delta against the base
# To dissasociate from the base config:
# - run make menuconfig
# - delete the Common/INCLUDE value
# - upon exiting the minimized config will be a delta against the default config.

[ -z "$KCONFIG_SEARCH_PATH" ] && KCONFIG_SEARCH_PATH="
.
kconfig
kconfig/targets
$PLATFORM_DIR/kconfig
$PLATFORM_DIR/kconfig/targets
platform/*/kconfig
platform/*/kconfig/targets
$VENDOR_DIR/kconfig
$VENDOR_DIR/kconfig/targets
"

ALL_INCF=""

usage()
{
    echo "Usage: `basename $0` [-m|-e] IN OUT"
    echo " temp path: KWORKDIR"
}

die()
{
    echo "$@" >&2
    exit 1
}

[ $# -lt 3 ] && usage
IN="$2"
OUT="$3"
[ -z "$INSTALL_PREFIX" ] && die "unset INSTALL_PREFIX"

debug()
{
    [ "$V" = 1 ] && echo "$@" >&2
    true
}

findinc()
{
    local f="$1"
    local srcdir=$(dirname "$2") # search relative to current file
    for D in $KCONFIG_SEARCH_PATH $srcdir; do
        if [ -e "$D/$f" ]; then
            debug "found: $D/$f"
            echo "$D/$f"
            return
        fi
    done
    exit 1
}

get_tmp()
{
    echo "$KWORKDIR/conf.$(basename $1 | tr / _)$2"
}

apply_inc()
{
    # apply fin on top of base
    local base="$1"
    local fin="$2"
    local level="$3"
    if [ $[level] -gt 10 ]; then
        echo "too many includes"
        exit 1
    fi
    if [ ! -e "$fin" ]; then
        die "include $fin: not found"
    fi
    local inc_list=$(sed -n '/^@include ".\+"$/{s/^.*"\(.\+\)"$/\1/p}' "$fin")
    local inc
    for inc in $inc_list; do
        debug "include: '$inc'"
        local finc=$(findinc $inc $fin)
        [ -z "$finc" ] && die "ERROR: include '$inc' not found"
        ALL_INCF="$ALL_INCF $finc"
        apply_inc $base $finc $[level+1]
    done
    kconfig/diffconf2.py -a $base $fin | grep -v '^CONFIG_INCLUDE=' > $base.tmp
    if [ -n "$inc_list" ]; then
        echo 'CONFIG_INCLUDE="'$inc_list'"' >> $base.tmp
    fi
    mv -f $base.tmp $base
}

expinc()
{
    local fin="$1"
    local fout="$2"
    > $fout
    apply_inc $fout $fin 1
    debug "wrote: $fout"
}

# expand includes
expand()
{
    [ -z "$KWORKDIR" ] && KWORKDIR=$(dirname $OUT)
    local tmpexp=$(get_tmp $OUT .exp)
    expinc $IN "$tmpexp"
    kconfig/diffconfig --unminimize --config "$tmpexp" --out "$OUT" kconfig/Kconfig || die "ERROR: kconfig/diffconfig failed"
    debug "wrote: $OUT"
    # dependencies
    echo $OUT: $ALL_INCF > $OUT.dep
}

# minimize against default config
minimize()
{
    kconfig/diffconfig --config "$1" --out "$1.tmp" kconfig/Kconfig
    grep -v '^# Generated by' "$1.tmp" | sed 's/^C/% C/' | LANG=C sort -k 2 | sed 's/^% //' > "$2"
    rm -f "$1.tmp"
}

minimize_inc()
{
    [ -z "$KWORKDIR" ] && KWORKDIR=$(dirname $IN)
    # expand and minimize included config
    local tmp_in_min=$(get_tmp $IN .min)
    local tmp_in_inc=$(get_tmp $IN .inc) # concatenated all included files
    minimize "$IN" "$tmp_in_min"
    local inc_list=$(sed -n '/^CONFIG_INCLUDE=".\+"$/{s/^.*="\(.\+\)"$/\1/p}' $IN)
    local found_inc=0
    local inc
    rm -f "$tmp_in_inc" $OUT.tmp
    for inc in $inc_list; do
        debug "include: '$inc'"
        local finc=$(findinc $inc $OUT)
        if [ -z "$finc" ]; then
            echo "WARNING: INCLUDE: '$inc' not found, ignoring"
            continue
        fi
        found_inc=1
        local tmpexp=$(get_tmp $inc .exp)
        expinc $finc $tmpexp
        # minimize expanded include file
        minimize "$tmpexp" "$tmpexp.min"
        cat "$tmpexp.min" >> "$tmp_in_inc"
        echo "@include \"$inc\"" >> $OUT.tmp
    done
    if [ "$found_inc" = 1 ]; then
        # get delta between included and actual
        kconfig/diffconf2.py -d "$tmp_in_inc" "$tmp_in_min" | grep -v '^CONFIG_INCLUDE=' >> $OUT.tmp
    else
        cp "$tmp_in_min" "$OUT.tmp"
    fi
    if cmp -s "$OUT.tmp" "$OUT"; then
        debug "no change to: $OUT"
    else
        cp "$OUT.tmp" "$OUT"
        debug "wrote: $OUT"
    fi
    rm "$OUT.tmp"
}


case "$1" in
    -m) minimize_inc ;;
    -e) expand ;;
    *) usage ;;
esac

true
