Ejemplo n.º 1
0
def _defaults_info(sc):
    # Returns a string describing the defaults of 'sc' (Symbol or Choice)

    if not sc.defaults:
        return ""

    s = "Default"
    if len(sc.defaults) > 1:
        s += "s"
    s += ":\n"

    for val, cond in sc.orig_defaults:
        s += "  - "
        if isinstance(sc, Symbol):
            s += _expr_str(val)

            # Skip the tristate value hint if the expression is just a single
            # symbol. _expr_str() already shows its value as a string.
            #
            # This also avoids showing the tristate value for string/int/hex
            # defaults, which wouldn't make any sense.
            if isinstance(val, tuple):
                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
        else:
            # Don't print the value next to the symbol name for choice
            # defaults, as it looks a bit confusing
            s += val.name
        s += "\n"

        if cond is not _kconfig.y:
            s += "    Condition (={}):\n{}" \
                .format(TRI_TO_STR[expr_value(cond)],
                        _split_expr_info(cond, 4))

    return s + "\n"
Ejemplo n.º 2
0
def _minimize_expr(expr, visibility):
    def expr_nodes_invisible(e):
        return hasattr(e, 'nodes') and len(e.nodes) > 0 and all(not visibility.visible(i) for i in e.nodes)

    if isinstance(expr, tuple):
        if expr[0] == kconfiglib.NOT:
            new_expr = _minimize_expr(expr[1], visibility)
            return kconfiglib.Kconfig.y if new_expr == kconfiglib.Kconfig.n else new_expr
        else:
            new_expr1 = _minimize_expr(expr[1], visibility)
            new_expr2 = _minimize_expr(expr[2], visibility)
            if expr[0] == kconfiglib.AND:
                if new_expr1 == kconfiglib.Kconfig.n or new_expr2 == kconfiglib.Kconfig.n:
                    return kconfiglib.Kconfig.n
                if new_expr1 == kconfiglib.Kconfig.y:
                    return new_expr2
                if new_expr2 == kconfiglib.Kconfig.y:
                    return new_expr1
            elif expr[0] == kconfiglib.OR:
                if new_expr1 == kconfiglib.Kconfig.y or new_expr2 == kconfiglib.Kconfig.y:
                    return kconfiglib.Kconfig.y
                if new_expr1 == kconfiglib.Kconfig.n:
                    return new_expr2
                if new_expr2 == kconfiglib.Kconfig.n:
                    return new_expr1
            elif expr[0] == kconfiglib.EQUAL:
                if not isinstance(new_expr1, type(new_expr2)):
                    return kconfiglib.Kconfig.n
                if new_expr1 == new_expr2:
                    return kconfiglib.Kconfig.y
            elif expr[0] == kconfiglib.UNEQUAL:
                if not isinstance(new_expr1, type(new_expr2)):
                    return kconfiglib.Kconfig.y
                if new_expr1 != new_expr2:
                    return kconfiglib.Kconfig.n
            else:  # <, <=, >, >=
                if not isinstance(new_expr1, type(new_expr2)):
                    return kconfiglib.Kconfig.n  # e.g "True < 2"

                if expr_nodes_invisible(new_expr1) or expr_nodes_invisible(new_expr2):
                    return kconfiglib.Kconfig.y if kconfiglib.expr_value(expr) else kconfiglib.Kconfig.n

            return (expr[0], new_expr1, new_expr2)

    if (not kconfiglib.expr_value(expr) and len(expr.config_string) == 0 and expr_nodes_invisible(expr)):
        # nodes which are invisible
        # len(expr.nodes) > 0 avoids constant symbols without actual node definitions, e.g. integer constants
        # len(expr.config_string) == 0 avoids hidden configs which reflects the values of choices
        return kconfiglib.Kconfig.n

    if (kconfiglib.expr_value(expr) and len(expr.config_string) > 0 and expr_nodes_invisible(expr)):
        # hidden config dependencies which will be written to sdkconfig as enabled ones.
        return kconfiglib.Kconfig.y

    if any(node.item.name.startswith(visibility.target_env_var) for node in expr.nodes):
        # We know the actual values for IDF_TARGETs
        return kconfiglib.Kconfig.y if kconfiglib.expr_value(expr) else kconfiglib.Kconfig.n

    return expr
Ejemplo n.º 3
0
 def select(self):
     """
     Called when <Return> key is pressed and SELECT action is selected
     """
     item = self.node.item
     # - Menu: dive into submenu
     # - INT, HEX, STRING symbol: raise prompt to enter symbol value
     # - BOOL, TRISTATE symbol inside 'y'-valued Choice: set 'y' value
     if (item is kconfiglib.MENU
         or isinstance(item, kconfiglib.Symbol) and self.node.is_menuconfig
         or isinstance(item, kconfiglib.Choice)):
         # Dive into submenu
         self.menuconfig.show_submenu(self.node)
     elif (isinstance(item, kconfiglib.Symbol) and item.type in
           (kconfiglib.INT, kconfiglib.HEX, kconfiglib.STRING)):
         # Raise prompt to enter symbol value
         ident = self.node.prompt[0] if self.node.prompt is not None else None
         title = 'Symbol: {}'.format(item.name)
         if item.type is kconfiglib.INT:
             # Find enabled ranges
             ranges = [
                 (int(start.str_value), int(end.str_value))
                 for start, end, expr in item.ranges
                 if kconfiglib.expr_value(expr) > 0
             ]
             # Raise prompt
             self.set_str_value(str(self.menuconfig.ask_for_int(
                 ident=ident,
                 title=title,
                 value=item.str_value,
                 ranges=ranges
             )))
         elif item.type is kconfiglib.HEX:
             # Find enabled ranges
             ranges = [
                 (int(start.str_value, base=16), int(end.str_value, base=16))
                 for start, end, expr in item.ranges
                 if kconfiglib.expr_value(expr) > 0
             ]
             # Raise prompt
             self.set_str_value(hex(self.menuconfig.ask_for_hex(
                 ident=ident,
                 title=title,
                 value=item.str_value,
                 ranges=ranges
             )))
         elif item.type is kconfiglib.STRING:
             # Raise prompt
             self.set_str_value(self.menuconfig.ask_for_string(
                 ident=ident,
                 title=title,
                 value=item.str_value
             ))
     elif (isinstance(item, kconfiglib.Symbol)
           and item.choice is not None and item.choice.tri_value == 2):
         # Symbol inside choice -> set symbol value to 'y'
         self.set_tristate_value(2)
Ejemplo n.º 4
0
    def _implies_invisibility(self, item):
        if isinstance(item, tuple):
            if item[0] == kconfiglib.NOT:
                (invisibility, source) = self._implies_invisibility(item[1])
                if source is not None and source.startswith(self.target_env_var):
                    return (not invisibility, source)
                else:
                    # we want to be visible all configs which are not dependent on target variables,
                    # e.g. "depends on XY" and "depends on !XY" as well
                    return (False, None)
            elif item[0] == kconfiglib.AND:
                (invisibility, source) = self._implies_invisibility(item[1])
                if invisibility:
                    return (True, source)
                (invisibility, source) = self._implies_invisibility(item[2])
                if invisibility:
                    return (True, source)
                return (False, None)
            elif item[0] == kconfiglib.OR:
                implication_list = [self._implies_invisibility(item[1]), self._implies_invisibility(item[2])]
                if all([implies for (implies, _) in implication_list]):
                    source_list = [s for (_, s) in implication_list if s.startswith(self.target_env_var)]
                    if len(set(source_list)) != 1:  # set removes the duplicates
                        print('[WARNING] list contains targets: {}'.format(source_list))
                    return (True, source_list[0])
                return (False, None)
            elif item[0] in self.direct_eval_set:
                def node_is_invisible(item):
                    return all([node.prompt is None for node in item.nodes])
                if node_is_invisible(item[1]) or node_is_invisible(item[1]):
                    # it makes no sense to call self._implies_invisibility() here because it won't generate any useful
                    # "source"
                    return (not kconfiglib.expr_value(item), None)
                else:
                    # expressions with visible configs can be changed to make the item visible
                    return (False, None)
            else:
                raise RuntimeError('Unimplemented operation in {}'.format(item))
        else:  # Symbol or Choice
            vis_list = [self._visible(node) for node in item.nodes]
            if len(vis_list) > 0 and all([not visible for (visible, _) in vis_list]):
                source_list = [s for (_, s) in vis_list if s is not None and s.startswith(self.target_env_var)]
                if len(set(source_list)) != 1:  # set removes the duplicates
                    print('[WARNING] list contains targets: {}'.format(source_list))
                return (True, source_list[0])

            if item.name.startswith(self.target_env_var):
                return (not kconfiglib.expr_value(item), item.name)

            if len(vis_list) == 1:
                (visible, source) = vis_list[0]
                if visible:
                    return (False, item.name)  # item.name is important here in case the result will be inverted: if
                    # the dependency is on another config then it can be still visible

            return (False, None)
Ejemplo n.º 5
0
 def _check_is_visible(self, node):
     v = True
     v = v and node.prompt is not None
     # It should be enough to check if prompt expression is not false and
     # for menu nodes whether 'visible if' is not false
     v = v and kconfiglib.expr_value(node.prompt[1]) > 0
     if node.item == kconfiglib.MENU:
         v = v and kconfiglib.expr_value(node.visibility) > 0
     # If node references Symbol, then we also account for symbol visibility
     # TODO: need to re-think whether this is needed
     if isinstance(node.item, kconfiglib.Symbol):
         if node.item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
             v = v and len(node.item.assignable) > 0
         else:
             v = v and node.item.visibility > 0
     return v
Ejemplo n.º 6
0
def _split_expr_info(expr, indent):
    # Returns a string with 'expr' split into its top-level && or || operands,
    # with one operand per line, together with the operand's value. This is
    # usually enough to get something readable for long expressions. A fancier
    # recursive thingy would be possible too.
    #
    # indent:
    #   Number of leading spaces to add before the split expression.

    if len(split_expr(expr, AND)) > 1:
        split_op = AND
        op_str = "&&"
    else:
        split_op = OR
        op_str = "||"

    s = ""
    for i, term in enumerate(split_expr(expr, split_op)):
        s += "{}{} {}".format(indent * " ", "  " if i == 0 else op_str,
                              _expr_str(term))

        # Don't bother showing the value hint if the expression is just a
        # single symbol. _expr_str() already shows its value.
        if isinstance(term, tuple):
            s += "  (={})".format(TRI_TO_STR[expr_value(term)])

        s += "\n"

    return s
Ejemplo n.º 7
0
def node2str(node):
    prompt, cond = node.prompt
    if not kconfiglib.expr_value(cond):
        return None, None

    if node.item == kconfiglib.MENU:
        return f'=== {prompt} ===', None

    if node.item == kconfiglib.COMMENT:
        return f'*** {prompt} ***', None

    # Now node.item is a symbol or a choice
    if node.item.type == kconfiglib.UNKNOWN:
        return None, None

    res = f'{value_str(node.item):3} {prompt}'

    if node.item.name is not None:
        res += f' ({node.item.name})'

    if hasattr(node, 'help') and node.help is not None:
        help_lines = [l for l in node.help.split('\n') if l.strip()]
        return res, help_lines

    return res, None
Ejemplo n.º 8
0
def check_deprecated(kconf):
    deprecated = kconf.syms['DEPRECATED']
    dep_expr = deprecated.rev_dep

    if dep_expr is not kconf.n:
        selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
        for selector in selectors:
            selector_name = split_expr(selector, AND)[0].name
            warn(f'Deprecated symbol {selector_name} is enabled.')
Ejemplo n.º 9
0
def check_experimental(kconf):
    experimental = kconf.syms['EXPERIMENTAL']
    dep_expr = experimental.rev_dep

    if dep_expr is not kconf.n:
        selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
        for selector in selectors:
            selector_name = split_expr(selector, AND)[0].name
            warn(f'Experimental symbol {selector_name} is enabled.')
Ejemplo n.º 10
0
    def run_exploration_from_file(self, config_file, with_initial_config=True):
        kconfig_file = f"{self.cwd}/{self.kconfig}"
        kconfig_hash = self.file_hash(kconfig_file)
        with cd(self.cwd):
            kconf = kconfiglib.Kconfig(kconfig_file)
        kconf.load_config(config_file)

        if with_initial_config:
            experiment = ExploreConfig()
            shutil.copyfile(config_file, f"{self.cwd}/.config")
            experiment([
                "--config_hash",
                self.file_hash(config_file),
                "--kconfig_hash",
                kconfig_hash,
                "--project_version",
                self.git_commit_id(),
                "--project_root",
                self.cwd,
                "--clean_command",
                self.clean_command,
                "--build_command",
                self.build_command,
                "--attr_command",
                self.attribute_command,
            ])

        for symbol in kconf.syms.values():
            if kconfiglib.TYPE_TO_STR[symbol.type] == "bool":
                if symbol.tri_value == 0 and 2 in symbol.assignable:
                    logger.debug(f"Set {symbol.name} to y")
                    symbol.set_value(2)
                    self._run_explore_experiment(kconf, kconfig_hash,
                                                 config_file)
                elif symbol.tri_value == 2 and 0 in symbol.assignable:
                    logger.debug(f"Set {symbol.name} to n")
                    symbol.set_value(0)
                    self._run_explore_experiment(kconf, kconfig_hash,
                                                 config_file)
            elif (kconfiglib.TYPE_TO_STR[symbol.type] == "int"
                  and symbol.visibility and symbol.ranges):
                for min_val, max_val, condition in symbol.ranges:
                    if kconfiglib.expr_value(condition):
                        min_val = int(min_val.str_value, 0)
                        max_val = int(max_val.str_value, 0)
                        step_size = (max_val - min_val) // 5
                        if step_size == 0:
                            step_size = 1
                        for val in range(min_val, max_val + 1, step_size):
                            print(f"Set {symbol.name} to {val}")
                            symbol.set_value(str(val))
                            self._run_explore_experiment(
                                kconf, kconfig_hash, config_file)
                        break
            else:
                continue
Ejemplo n.º 11
0
def _direct_dep_info(sc):
    # Returns a string describing the direct dependencies of 'sc' (Symbol or
    # Choice). The direct dependencies are the OR of the dependencies from each
    # definition location. The dependencies at each definition location come
    # from 'depends on' and dependencies inherited from parent items.

    return "" if sc.direct_dep is _kconfig.y else \
        'Direct dependencies (={}):\n{}\n' \
        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
                _split_expr_info(sc.direct_dep, 2))
Ejemplo n.º 12
0
    def sis(expr, val, title):
        # sis = selects/implies
        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
        if not sis:
            return ""

        res = title
        for si in sis:
            res += "  - {}\n".format(split_expr(si, AND)[0].name)
        return res + "\n"
Ejemplo n.º 13
0
    def node_str(node, indent):
        """
        Returns the complete menu entry text for a menu node, or "" for
        invisible menu nodes. Invisible menu nodes are those that lack a prompt
        or that do not have a satisfied prompt condition.

        Example return value: "[*] Bool symbol (BOOL)"

        The symbol name is printed in parentheses to the right of the prompt.
        This is so that symbols can easily be referred to in the configuration
        interface.
        """
        if not node.prompt:
            return ""

        # Even for menu nodes for symbols and choices, it's wrong to check
        # Symbol.visibility / Choice.visibility here. The reason is that a
        # symbol (and a choice, in theory) can be defined in multiple
        # locations, giving it multiple menu nodes, which do not necessarily
        # all have the same prompt visibility. Symbol.visibility /
        # Choice.visibility is calculated as the OR of the visibility of all
        # the prompts.
        prompt, prompt_cond = node.prompt
        if not expr_value(prompt_cond):
            return ""

        if node.item == MENU:
            return "    " + indent * " " + prompt + "  --->"

        if type(node.item) == Choice:
            return "    " + indent * " " + prompt

        if node.item == COMMENT:
            return "    " + indent * " " + "*** {} ***".format(prompt)

        # Symbol
        sym = node.item

        if sym.type == UNKNOWN:
            return ""

        # {:3} sets the field width to three. Gives nice alignment for empty
        # string values.
        res = "{:3} {}{}".format(Menuconfig.value_str(sym), indent * " ",
                                 prompt)

        # Append a sub-menu arrow if menuconfig and enabled
        if node.is_menuconfig:
            res += "  ---" + (">" if sym.tri_value > 0 else "-")

        return res
Ejemplo n.º 14
0
def node_str(node):
    """
    Returns the complete menu entry text for a menu node, or "" for invisible
    menu nodes. Invisible menu nodes are those that lack a prompt or that do
    not have a satisfied prompt condition.

    Example return value: "[*] Bool symbol (BOOL)"

    The symbol name is printed in parentheses to the right of the prompt.
    """
    if not node.prompt:
        return ""

    # Even for menu nodes for symbols and choices, it's wrong to check
    # Symbol.visibility / Choice.visibility here. The reason is that a symbol
    # (and a choice, in theory) can be defined in multiple locations, giving it
    # multiple menu nodes, which do not necessarily all have the same prompt
    # visibility. Symbol.visibility / Choice.visibility is calculated as the OR
    # of the visibility of all the prompts.
    prompt, prompt_cond = node.prompt
    if not expr_value(prompt_cond):
        return ""

    if node.item == MENU:
        return "    " + prompt

    if node.item == COMMENT:
        return "    *** {} ***".format(prompt)

    # Symbol or Choice

    sc = node.item

    if sc.type == UNKNOWN:
        # Skip symbols defined without a type (these are obscure and generate
        # a warning)
        return ""

    # Add help text
    if WITH_HELP_DESC:
        prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', '')

    # {:3} sets the field width to three. Gives nice alignment for empty string
    # values.
    res = "{:3} {}".format(value_str(sc), prompt)

    # Don't print the name for unnamed choices (the normal kind)
    if sc.name is not None:
        res += " ({})".format(sc.name)

    return res
Ejemplo n.º 15
0
def node_str(node):
    """
    Returns the complete menu entry text for a menu node, or "" for invisible
    menu nodes. Invisible menu nodes are those that lack a prompt or that do
    not have a satisfied prompt condition.

    Example return value: "[*] Bool symbol (BOOL)"

    The symbol name is printed in parentheses to the right of the prompt.
    """
    if not node.prompt:
        return ""

    # Even for menu nodes for symbols and choices, it's wrong to check
    # Symbol.visibility / Choice.visibility here. The reason is that a symbol
    # (and a choice, in theory) can be defined in multiple locations, giving it
    # multiple menu nodes, which do not necessarily all have the same prompt
    # visibility. Symbol.visibility / Choice.visibility is calculated as the OR
    # of the visibility of all the prompts.
    prompt, prompt_cond = node.prompt
    if not expr_value(prompt_cond):
        return ""

    if node.item == MENU:
        return "    " + prompt

    if node.item == COMMENT:
        return "    *** {} ***".format(prompt)

    # Symbol or Choice

    sc = node.item

    if sc.type == UNKNOWN:
        # Skip symbols defined without a type (these are obscure and generate
        # a warning)
        return ""

    # Add help text
    if WITH_HELP_DESC:
        prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', '')

    # {:3} sets the field width to three. Gives nice alignment for empty string
    # values.
    res = "{:3} {}".format(value_str(sc), prompt)

    # Don't print the name for unnamed choices (the normal kind)
    if sc.name is not None:
        res += " ({})".format(sc.name)

    return res
Ejemplo n.º 16
0
def missing_deps(sym):
    # check_assigned_sym_values() helper for finding unsatisfied dependencies.
    #
    # Given direct dependencies
    #
    #     depends on <expr> && <expr> && ... && <expr>
    #
    # on 'sym' (which can also come from e.g. a surrounding 'if'), returns a
    # list of all <expr>s with a value less than the value 'sym' was assigned
    # ("less" instead of "not equal" just to be general and handle tristates,
    # even though Zephyr doesn't use them).
    #
    # For string/int/hex symbols, just looks for <expr> = n.
    #
    # Note that <expr>s can be something more complicated than just a symbol,
    # like 'FOO || BAR' or 'FOO = "string"'.

    deps = split_expr(sym.direct_dep, AND)

    if sym.type in (BOOL, TRISTATE):
        return [dep for dep in deps if expr_value(dep) < sym.user_value]
    # string/int/hex
    return [dep for dep in deps if expr_value(dep) == 0]
Ejemplo n.º 17
0
def get_sym_missing_deps(sym):
    """
    Returns an array of strings, where each element is the string representation
    of the expressions on which `sym` depends and are missing.
    """
    # this splits the top expressions that are connected via AND (&&)
    # this will be the case, for example, for expressions that are defined in
    # multiple lines
    top_deps = kconfiglib.split_expr(sym.direct_dep, kconfiglib.AND)

    # we only need the expressions that are not met
    expr = [dep for dep in top_deps if kconfiglib.expr_value(dep) == 0]

    # convert each expression to strings and add the value for a friendlier
    # output message
    expr_str = []
    for expr in expr:
        s = kconfiglib.expr_str(expr)
        if isinstance(expr, tuple):
            s = "({})".format(s)
        expr_str.append("{} (={})".format(s, kconfiglib.TRI_TO_STR[kconfiglib.expr_value(expr)]))

    return expr_str
Ejemplo n.º 18
0
    def get_active_range(sym):
        """
        Returns a tuple of (low, high) integer values if a range
        limit is active for this symbol, or (None, None) if no range
        limit exists.
        """
        base = kconfiglib._TYPE_TO_BASE[sym.orig_type] if sym.orig_type in kconfiglib._TYPE_TO_BASE else 0

        try:
            for low_expr, high_expr, cond in sym.ranges:
                if kconfiglib.expr_value(cond):
                    low = int(low_expr.str_value, base) if is_base_n(low_expr.str_value, base) else 0
                    high = int(high_expr.str_value, base) if is_base_n(high_expr.str_value, base) else 0
                    return (low, high)
        except ValueError:
            pass
        return (None, None)
Ejemplo n.º 19
0
def get_sym_applying_range(sym):
    """
    Returns the first range that applies to a symbol (the active range) and the
    condition when it is not a constant.
    The return value is a tuple holding string representations of the range like
    so:
        (low, high, condition)
    When the condition is a constant (e.g. 'y' when there is no condition) it
    will be an empty string.
    """
    # multiple ranges could apply to a symbol
    for rng in sym.ranges:
        (low, high, cond) = rng
        # get the first active one
        if kconfiglib.expr_value(cond):
            return (low.str_value, high.str_value, "(if {})".format(cond.name)
                    if not cond.is_constant else "")
    return None
Ejemplo n.º 20
0
    def _add_symbol_if_nontrivial(self, sym, trivialize=True):
        if sym.__class__ is ExprSymbol and not symbol_can_be_user_assigned(
                sym.sym):
            return sympy.true if kconfiglib.expr_value(
                sym.sym) else sympy.false

        # If the symbol is aleady satisfied in the current config,
        # skip it.
        if trivialize and sym.is_satisfied():
            return sympy.true

        # Return existing symbol if possible
        for s, sympy_s in self.symbols:
            if s.__class__ is sym.__class__ is ExprSymbol:
                if s.sym == sym.sym:
                    return sympy_s

        # Create new symbol
        i = len(self.symbols)
        s = sympy.Symbol(str(i))
        self.symbols.append((sym, s))
        return s
Ejemplo n.º 21
0
    def save_config(self, filename):
        kas_includes = []
        kas_targets = []
        kas_build_system = None
        kas_vars = {}
        menu_configuration = {}

        for symname in self.kconf.syms:
            if symname == 'MODULES':
                continue

            sym = self.kconf.syms[symname]
            symvalue = sym.str_value

            if expr_value(sym.direct_dep) < 2:
                continue

            if sym.visibility == 2:
                if sym.type == BOOL:
                    menu_configuration[symname] = symvalue == 'y'
                elif sym.type == STRING:
                    menu_configuration[symname] = symvalue
                elif sym.type == INT:
                    menu_configuration[symname] = int(symvalue)
                elif sym.type == HEX:
                    menu_configuration[symname] = int(symvalue, 16)
                else:
                    logging.error(
                        'Configuration variable %s uses unsupported type',
                        symname)
                    sys.exit(1)

            if symname.startswith('KAS_INCLUDE_'):
                check_sym_is_string(sym)
                if symvalue != '':
                    kas_includes.append(symvalue)
            elif symname.startswith('KAS_TARGET_'):
                check_sym_is_string(sym)
                if symvalue != '':
                    kas_targets.append(symvalue)
            elif symname == 'KAS_BUILD_SYSTEM':
                check_sym_is_string(sym)
                if symvalue != '':
                    kas_build_system = symvalue
            elif sym.type in (STRING, INT, HEX):
                kas_vars[symname] = symvalue

        config = {
            'header': {
                'version': __file_version__,
                'includes': kas_includes
            },
            'menu_configuration': menu_configuration
        }
        if kas_build_system:
            config['build_system'] = kas_build_system
        if len(kas_targets) > 0:
            config['target'] = kas_targets
        if len(kas_vars) > 0:
            config['local_conf_header'] = {
                '__menu_config_vars':
                '\n'.join([
                    '{} = "{}"'.format(key, value)
                    for key, value in kas_vars.items()
                ])
            }

        logging.debug('Menu configuration:\n%s', pprint.pformat(config))

        if config != self.orig_config:
            logging.info('Saving configuration as %s', filename)

            # format multi-line strings more nicely
            yaml.add_representer(str, str_representer)

            try:
                os.rename(filename, filename + '.old')
            except FileNotFoundError:
                pass

            with open(filename, 'w') as config_file:
                config_file.write('#\n'
                                  '# Automatically generated by kas {}\n'
                                  '#\n'.format(__version__))
                yaml.dump(config, config_file)
Ejemplo n.º 22
0
 def isVisible(node):
     """Return the visibility state of the node passed as an argument."""
     return node.prompt and expr_value(node.prompt[1]) and not \
         (node.item == MENU and not expr_value(node.visibility))
Ejemplo n.º 23
0
    def write_node(node):
        try:
            json_parent = node_lookup[node.parent]['children']
        except KeyError:
            assert node.parent not in node_lookup  # if fails, we have a parent node with no "children" entity (ie a bug)
            json_parent = result  # root level node

        # node.kconfig.y means node has no dependency,
        if node.dep is node.kconfig.y:
            depends = None
        else:
            depends = kconfiglib.expr_str(node.dep)

        try:
            # node.is_menuconfig is True in newer kconfiglibs for menus and choices as well
            is_menuconfig = node.is_menuconfig and isinstance(node.item, kconfiglib.Symbol)
        except AttributeError:
            is_menuconfig = False

        new_json = None
        if node.item == kconfiglib.MENU or is_menuconfig:
            new_json = {'type': 'menu',
                        'title': node.prompt[0],
                        'depends_on': depends,
                        'children': [],
                        }
            if is_menuconfig:
                sym = node.item
                new_json['name'] = sym.name
                new_json['help'] = node.help
                new_json['is_menuconfig'] = is_menuconfig
                greatest_range = None
                if len(sym.ranges) > 0:
                    # Note: Evaluating the condition using kconfiglib's expr_value
                    # should have one condition which is true
                    for min_range, max_range, cond_expr in sym.ranges:
                        if kconfiglib.expr_value(cond_expr):
                            greatest_range = [min_range, max_range]
                new_json['range'] = greatest_range

        elif isinstance(node.item, kconfiglib.Symbol):
            sym = node.item
            greatest_range = None
            if len(sym.ranges) > 0:
                # Note: Evaluating the condition using kconfiglib's expr_value
                # should have one condition which is true
                for min_range, max_range, cond_expr in sym.ranges:
                    if kconfiglib.expr_value(cond_expr):
                        base = 16 if sym.type == kconfiglib.HEX else 10
                        greatest_range = [int(min_range.str_value, base), int(max_range.str_value, base)]
                        break

            new_json = {
                'type': kconfiglib.TYPE_TO_STR[sym.type],
                'name': sym.name,
                'title': node.prompt[0] if node.prompt else None,
                'depends_on': depends,
                'help': node.help,
                'range': greatest_range,
                'children': [],
            }
        elif isinstance(node.item, kconfiglib.Choice):
            choice = node.item
            new_json = {
                'type': 'choice',
                'title': node.prompt[0],
                'name': choice.name,
                'depends_on': depends,
                'help': node.help,
                'children': []
            }

        if new_json:
            node_id = get_menu_node_id(node)
            if node_id in existing_ids:
                raise RuntimeError('Config file contains two items with the same id: %s (%s). ' +
                                   'Please rename one of these items to avoid ambiguity.' % (node_id, node.prompt[0]))
            new_json['id'] = node_id

            json_parent.append(new_json)
            node_lookup[node] = new_json
Ejemplo n.º 24
0
    def write_node(node):
        try:
            json_parent = node_lookup[node.parent]["children"]
        except KeyError:
            assert node.parent not in node_lookup  # if fails, we have a parent node with no "children" entity (ie a bug)
            json_parent = result  # root level node

        # node.kconfig.y means node has no dependency,
        if node.dep is node.kconfig.y:
            depends = None
        else:
            depends = kconfiglib.expr_str(node.dep)

        try:
            is_menuconfig = node.is_menuconfig
        except AttributeError:
            is_menuconfig = False

        new_json = None
        if node.item == kconfiglib.MENU or is_menuconfig:
            new_json = {
                "type": "menu",
                "title": node.prompt[0],
                "depends_on": depends,
                "children": []
            }
            if is_menuconfig:
                sym = node.item
                new_json["name"] = sym.name
                new_json["help"] = node.help
                new_json["is_menuconfig"] = is_menuconfig
                greatest_range = None
                if len(sym.ranges) > 0:
                    # Note: Evaluating the condition using kconfiglib's expr_value
                    # should have one result different from value 0 ("n").
                    for min_range, max_range, cond_expr in sym.ranges:
                        if kconfiglib.expr_value(cond_expr) != "n":
                            greatest_range = [min_range, max_range]
                new_json["range"] = greatest_range

        elif isinstance(node.item, kconfiglib.Symbol):
            sym = node.item
            greatest_range = None
            if len(sym.ranges) > 0:
                # Note: Evaluating the condition using kconfiglib's expr_value
                # should have one result different from value 0 ("n").
                for min_range, max_range, cond_expr in sym.ranges:
                    if kconfiglib.expr_value(cond_expr) != "n":
                        greatest_range = [
                            int(min_range.str_value),
                            int(max_range.str_value)
                        ]

            new_json = {
                "type": kconfiglib.TYPE_TO_STR[sym.type],
                "name": sym.name,
                "title": node.prompt[0] if node.prompt else None,
                "depends_on": depends,
                "help": node.help,
                "range": greatest_range,
                "children": [],
            }
        elif isinstance(node.item, kconfiglib.Choice):
            choice = node.item
            new_json = {
                "type": "choice",
                "title": node.prompt[0],
                "name": choice.name,
                "depends_on": depends,
                "help": node.help,
                "children": []
            }

        if new_json:
            json_parent.append(new_json)
            node_lookup[node] = new_json
Ejemplo n.º 25
0
def write_menu_item(f, node, visibility):
    def is_choice(node):
        """ Skip choice nodes, they are handled as part of the parent (see below) """
        return isinstance(node.parent.item, kconfiglib.Choice)

    if is_choice(node) or not visibility.visible(node):
        return

    try:
        name = node.item.name
    except AttributeError:
        name = None

    is_menu = node_is_menu(node)

    # Heading
    if name:
        title = 'CONFIG_%s' % name
    else:
        # if no symbol name, use the prompt as the heading
        title = node.prompt[0]

    f.write('.. _%s:\n\n' % get_link_anchor(node))
    f.write('%s\n' % title)
    f.write(HEADING_SYMBOLS[get_heading_level(node)] * len(title))
    f.write('\n\n')

    if name:
        f.write('%s%s\n\n' % (INDENT, node.prompt[0]))
        f.write('%s:emphasis:`Found in:` %s\n\n' %
                (INDENT, get_breadcrumbs(node)))

    try:
        if node.help:
            # Help text normally contains newlines, but spaces at the beginning of
            # each line are stripped by kconfiglib. We need to re-indent the text
            # to produce valid ReST.
            f.write(format_rest_text(node.help, INDENT))
            f.write('\n')
    except AttributeError:
        pass  # No help

    if isinstance(node.item, kconfiglib.Choice):
        f.write('%sAvailable options:\n' % INDENT)
        choice_node = node.list
        while choice_node:
            # Format available options as a list
            f.write('%s- %-20s (%s)\n' %
                    (INDENT * 2, choice_node.prompt[0], choice_node.item.name))
            if choice_node.help:
                HELP_INDENT = INDENT * 2
                fmt_help = format_rest_text(choice_node.help,
                                            '  ' + HELP_INDENT)
                f.write('%s  \n%s\n' % (HELP_INDENT, fmt_help))
            choice_node = choice_node.next

        f.write('\n\n')

    if isinstance(node.item, kconfiglib.Symbol):

        def _expr_str(sc):
            if sc.is_constant or not sc.nodes or sc.choice:
                return '{}'.format(sc.name)
            return ':ref:`%s%s`' % (sc.kconfig.config_prefix, sc.name)

        range_strs = []
        for low, high, cond in node.item.ranges:
            cond = _minimize_expr(cond, visibility)
            if cond == kconfiglib.Kconfig.n:
                continue
            if not isinstance(cond, tuple) and cond != kconfiglib.Kconfig.y:
                if len(cond.nodes) > 0 and all(not visibility.visible(i)
                                               for i in cond.nodes):
                    if not kconfiglib.expr_value(cond):
                        continue
            range_str = '%s- from %s to %s' % (INDENT * 2, low.str_value,
                                               high.str_value)
            if cond != kconfiglib.Kconfig.y and not kconfiglib.expr_value(
                    cond):
                range_str += ' if %s' % kconfiglib.expr_str(cond, _expr_str)
            range_strs.append(range_str)
        if len(range_strs) > 0:
            f.write('%sRange:\n' % INDENT)
            f.write('\n'.join(range_strs))
            f.write('\n\n')

        default_strs = []
        for default, cond in node.item.defaults:
            cond = _minimize_expr(cond, visibility)
            if cond == kconfiglib.Kconfig.n:
                continue
            if not isinstance(cond, tuple) and cond != kconfiglib.Kconfig.y:
                if len(cond.nodes) > 0 and all(not visibility.visible(i)
                                               for i in cond.nodes):
                    if not kconfiglib.expr_value(cond):
                        continue
            # default.type is mostly UNKNOWN so it cannot be used reliably for detecting the type
            d = default.str_value
            if d in ['y', 'Y']:
                d = 'Yes (enabled)'
            elif d in ['n', 'N']:
                d = 'No (disabled)'
            elif re.search(
                    r'[^0-9a-fA-F]',
                    d):  # simple string detection: if it not a valid number
                d = '"%s"' % d
            default_str = '%s- %s' % (INDENT * 2, d)
            if cond != kconfiglib.Kconfig.y and not kconfiglib.expr_value(
                    cond):
                default_str += ' if %s' % kconfiglib.expr_str(cond, _expr_str)
            default_strs.append(default_str)
        if len(default_strs) > 0:
            f.write('%sDefault value:\n' % INDENT)
            f.write('\n'.join(default_strs))
            f.write('\n\n')

    if is_menu:
        # enumerate links to child items
        child_list = []
        child = node.list
        while child:
            if not is_choice(child) and child.prompt and visibility.visible(
                    child):
                child_list.append((child.prompt[0], get_link_anchor(child)))
            child = child.next
        if len(child_list) > 0:
            f.write('Contains:\n\n')
            sorted_child_list = sorted(child_list,
                                       key=lambda pair: pair[0].lower())
            ref_list = [
                '- :ref:`{}`'.format(anchor) for _, anchor in sorted_child_list
            ]
            f.write('\n'.join(ref_list))
            f.write('\n\n')
Ejemplo n.º 26
0
def is_constant_y(sym):
    return sym.is_constant and (kconfiglib.expr_value(sym) == 2)
Ejemplo n.º 27
0
def expr_value_bool(expr):
    """
    Evaluates the given expression using kconfiglib.expr_value(expr) and converts
    the result to a boolean value using tri_to_bool().
    """
    return tri_to_bool(kconfiglib.expr_value(expr))
Ejemplo n.º 28
0
    def write_node(node):
        try:
            json_parent = node_lookup[node.parent]["children"]
        except KeyError:
            assert node.parent not in node_lookup  # if fails, we have a parent node with no "children" entity (ie a bug)
            json_parent = result  # root level node

        # node.kconfig.y means node has no dependency,
        if node.dep is node.kconfig.y:
            depends = None
        else:
            depends = kconfiglib.expr_str(node.dep)

        try:
            is_menuconfig = node.is_menuconfig
        except AttributeError:
            is_menuconfig = False

        new_json = None
        if node.item == kconfiglib.MENU or is_menuconfig:
            new_json = {
                "type": "menu",
                "title": node.prompt[0],
                "depends_on": depends,
                "children": [],
            }
            if is_menuconfig:
                sym = node.item
                new_json["name"] = sym.name
                new_json["help"] = node.help
                new_json["is_menuconfig"] = is_menuconfig
                greatest_range = None
                if len(sym.ranges) > 0:
                    # Note: Evaluating the condition using kconfiglib's expr_value
                    # should have one condition which is true
                    for min_range, max_range, cond_expr in sym.ranges:
                        if kconfiglib.expr_value(cond_expr):
                            greatest_range = [min_range, max_range]
                new_json["range"] = greatest_range

        elif isinstance(node.item, kconfiglib.Symbol):
            sym = node.item
            greatest_range = None
            if len(sym.ranges) > 0:
                # Note: Evaluating the condition using kconfiglib's expr_value
                # should have one condition which is true
                for min_range, max_range, cond_expr in sym.ranges:
                    if kconfiglib.expr_value(cond_expr):
                        base = 16 if sym.type == kconfiglib.HEX else 10
                        greatest_range = [
                            int(min_range.str_value, base),
                            int(max_range.str_value, base)
                        ]
                        break

            new_json = {
                "type": kconfiglib.TYPE_TO_STR[sym.type],
                "name": sym.name,
                "title": node.prompt[0] if node.prompt else None,
                "depends_on": depends,
                "help": node.help,
                "range": greatest_range,
                "children": [],
            }
        elif isinstance(node.item, kconfiglib.Choice):
            choice = node.item
            new_json = {
                "type": "choice",
                "title": node.prompt[0],
                "name": choice.name,
                "depends_on": depends,
                "help": node.help,
                "children": []
            }

        if new_json:
            node_id = get_menu_node_id(node)
            if node_id in existing_ids:
                raise RuntimeError(
                    "Config file contains two items with the same id: %s (%s). "
                    + "Please rename one of these items to avoid ambiguity." %
                    (node_id, node.prompt[0]))
            new_json["id"] = node_id

            json_parent.append(new_json)
            node_lookup[node] = new_json
Ejemplo n.º 29
0
    def write_node(node):
        try:
            json_parent = node_lookup[node.parent]["children"]
        except KeyError:
            assert node.parent not in node_lookup  # if fails, we have a parent node with no "children" entity (ie a bug)
            json_parent = result  # root level node

        # node.kconfig.y means node has no dependency,
        if node.dep is node.kconfig.y:
            depends = None
        else:
            depends = kconfiglib.expr_str(node.dep)

        try:
            is_menuconfig = node.is_menuconfig
        except AttributeError:
            is_menuconfig = False

        new_json = None
        if node.item == kconfiglib.MENU or is_menuconfig:
            new_json = {"type": "menu",
                        "title": node.prompt[0],
                        "depends_on": depends,
                        "children": [],
                        }
            if is_menuconfig:
                sym = node.item
                new_json["name"] = sym.name
                new_json["help"] = node.help
                new_json["is_menuconfig"] = is_menuconfig
                greatest_range = None
                if len(sym.ranges) > 0:
                    # Note: Evaluating the condition using kconfiglib's expr_value
                    # should have one condition which is true
                    for min_range, max_range, cond_expr in sym.ranges:
                        if kconfiglib.expr_value(cond_expr):
                            greatest_range = [min_range, max_range]
                new_json["range"] = greatest_range

        elif isinstance(node.item, kconfiglib.Symbol):
            sym = node.item
            greatest_range = None
            if len(sym.ranges) > 0:
                # Note: Evaluating the condition using kconfiglib's expr_value
                # should have one condition which is true
                for min_range, max_range, cond_expr in sym.ranges:
                    if kconfiglib.expr_value(cond_expr):
                        greatest_range = [int(min_range.str_value), int(max_range.str_value)]

            new_json = {
                "type": kconfiglib.TYPE_TO_STR[sym.type],
                "name": sym.name,
                "title": node.prompt[0] if node.prompt else None,
                "depends_on": depends,
                "help": node.help,
                "range": greatest_range,
                "children": [],
            }
        elif isinstance(node.item, kconfiglib.Choice):
            choice = node.item
            new_json = {
                "type": "choice",
                "title": node.prompt[0],
                "name": choice.name,
                "depends_on": depends,
                "help": node.help,
                "children": []
            }

        if new_json:
            node_id = get_menu_node_id(node)
            if node_id in existing_ids:
                raise RuntimeError("Config file contains two items with the same id: %s (%s). " +
                                   "Please rename one of these items to avoid ambiguity." % (node_id, node.prompt[0]))
            new_json["id"] = node_id

            json_parent.append(new_json)
            node_lookup[node] = new_json