#!/usr/bin/env python3

# This script functions similarly to scripts/kconfig/merge_config.sh from the
# kernel tree, merging multiple configurations fragments to produce a complete
# .config, with unspecified values filled in as for alldefconfig.
#
# The generated .config respects symbol dependencies, and a warning is printed
# if any symbol gets a different value from the assigned value.
#
# For a real-world merging example based on this script, see
# https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py.
#
# Here's a demo:
#
# Kconfig contents:
#
#     config FOO
#         bool "FOO"
#
#     config BAR
#         bool "BAR"
#
#     config BAZ
#         string "BAZ"
#
#     config QAZ
#         bool "QAZ" if n
#
#
# conf1 contents:
#
#     CONFIG_FOO=y
#
#
# conf2 contents:
#
#     CONFIG_BAR=y
#
#
# conf3 contents:
#
#     # Assigned twice (would generate warning if 'warn_assign_override' was
#     # True)
#     # CONFIG_FOO is not set
#
#     # Ops... this symbol doesn't exist
#     CONFIG_OPS=y
#
#     CONFIG_BAZ="baz string"
#
#
# conf4 contents:
#
#     CONFIG_QAZ=y
#
#
# Running:
#
#     $ python(3) merge_config.py Kconfig merged conf1 conf2 conf3 conf4
#     Merged configuration 'conf1'
#     Merged configuration 'conf2'
#     conf3:5: warning: attempt to assign the value 'y' to the undefined symbol OPS
#     Merged configuration 'conf3'
#     Merged configuration 'conf4'
#     Configuration saved to 'merged'
#     warning: QAZ (defined at Kconfig:10) was assigned the value 'y' but got the value 'n' -- check dependencies
#     $ cat merged
#     Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
#     # CONFIG_FOO is not set
#     CONFIG_BAR=y
#     CONFIG_BAZ="baz string"

from __future__ import print_function
import sys
import re
import pprint

def _re_match(regex):
    return re.compile(regex, re.ASCII).match

from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR

def main(kconfig_file, config1, config2):

    kconf = Kconfig(kconfig_file, suppress_traceback=True)

    # Enable warnings for assignments to undefined symbols
    kconf.warn_assign_undef = False

    # (This script uses alldefconfig as the base. Other starting states could be
    # set up here as well. The approach in examples/allnoconfig_simpler.py could
    # provide an allnoconfig starting state for example.)

    # Disable warnings generated for multiple assignments to the same symbol within
    # a (set of) configuration files. Assigning a symbol multiple times might be
    # done intentionally when merging configuration files.
    kconf.warn_assign_override = False
    kconf.warn_assign_redun = False

    # Create a merged configuration by loading the fragments with replace=False.
    # load_config() and write_config() returns a message to print.
    print(kconf.load_config(config1, replace=False))
    print(kconf.load_config(config2, replace=False))

    # Modification for PX4 unset all symbols (INT,HEX etc) from 2nd config

    f = open(config2, 'r')

    unset_match = re.compile(r"# {}([^ ]+) is not set".format("CONFIG_"), re.ASCII).match

    for line in f:
        match = unset_match(line)
        #pprint.pprint(line)
        if match is not None:
            sym_name = match.group(1)
            kconf.syms[sym_name].unset_value()

            if kconf.syms[sym_name].type is BOOL:
                for default, cond in kconf.syms[sym_name].orig_defaults:
                    if(cond.str_value == 'y'):
                        # Default is y, our diff is unset thus we've set it to no
                        kconf.syms[sym_name].set_value(0)

    f.close()

    # Print warnings for symbols whose actual value doesn't match the assigned
    # value
    for sym in kconf.defined_syms:
        # Was the symbol assigned to?
        if sym.user_value is not None:
            # Tristate values are represented as 0, 1, 2. Having them as
            # "n", "m", "y" is more convenient here, so convert.
            if sym.type in (BOOL, TRISTATE):
                user_value = TRI_TO_STR[sym.user_value]
            else:
                user_value = sym.user_value

            if user_value != sym.str_value:
                print("warning: {} was assigned the value '{}' but got the "
                    "value '{}' -- check dependencies".format(
                        sym.name_and_loc, user_value, sym.str_value),
                    file=sys.stderr)

    return kconf

if __name__ == '__main__':
    if len(sys.argv) < 4:
        sys.exit("usage: merge_config.py Kconfig merged_config config1 config2]")
    # Write the merged configuration
    print(main(sys.argv[1],sys.argv[3],sys.argv[4]).write_config(sys.argv[2]))
