Пример #1
0
    def add_selecting_implying_rst(type_str, expr):
        # Writes a link for each symbol that selects the symbol (if 'expr' is
        # sym.rev_dep) or each symbol that imply's the symbol (if 'expr' is
        # sym.weak_rev_dep). Also adds a heading at the top derived from
        # type_str ("select"/"imply"), if there are any selecting/implying
        # symbols.

        nonlocal rst

        if expr is not sym.kconfig.n:
            heading = "Symbols that {} this symbol".format(type_str)
            rst += "{}\n{}\n\n".format(heading, len(heading)*"=")

            # The reverse dependencies from each select/imply are ORed together
            for select in kconfiglib.split_expr(expr, kconfiglib.OR):
                # - 'select/imply A if B' turns into A && B
                # - 'select/imply A' just turns into A
                #
                # In both cases, we can split on AND and pick the first
                # operand.

                rst += "- {}\n".format(rst_link(
                    kconfiglib.split_expr(select, kconfiglib.AND)[0]))

            rst += "\n"
Пример #2
0
    def add_selecting_implying_rst(type_str, expr):
        # Writes a link for each symbol that selects the symbol (if 'expr' is
        # sym.rev_dep) or each symbol that imply's the symbol (if 'expr' is
        # sym.weak_rev_dep). Also adds a heading at the top derived from
        # type_str ("select"/"imply"), if there are any selecting/implying
        # symbols.

        nonlocal rst

        if expr is not sym.kconfig.n:
            heading = "Symbols that {} this symbol".format(type_str)
            rst += "{}\n{}\n\n".format(heading, len(heading) * "=")

            # The reverse dependencies from each select/imply are ORed together
            for select in kconfiglib.split_expr(expr, kconfiglib.OR):
                # - 'select/imply A if B' turns into A && B
                # - 'select/imply A' just turns into A
                #
                # In both cases, we can split on AND and pick the first
                # operand.

                rst += "- {}\n".format(
                    rst_link(kconfiglib.split_expr(select, kconfiglib.AND)[0]))

            rst += "\n"
Пример #3
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
Пример #4
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.')
Пример #5
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.')
Пример #6
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"
Пример #7
0
        def write_select_imply_rst(expr):
            # Writes a link for each selecting symbol (if 'expr' is
            # sym.rev_dep) or each implying symbol (if 'expr' is
            # sym.weak_rev_dep)

            # The reverse dependencies from each select/imply are ORed together
            for select in kconfiglib.split_expr(expr, kconfiglib.OR):
                # - 'select/imply A if B' turns into A && B
                # - 'select/imply A' just turns into A
                #
                # In both cases, we can split on AND and pick the first
                # operand.
                sym_rst.write(" - :option:`CONFIG_{}`\n".format(
                    kconfiglib.split_expr(select, kconfiglib.AND)[0].name))

            sym_rst.write("\n")
Пример #8
0
    def add_select_imply_rst(type_str, expr):
        # Writes a link for each selecting symbol (if 'expr' is sym.rev_dep) or
        # each implying symbol (if 'expr' is sym.weak_rev_dep). Also adds a
        # heading at the top, derived from type_str ("select"/"imply").

        nonlocal sym_rst

        heading = "Symbols that ``{}`` this symbol".format(type_str)
        sym_rst += "{}\n{}\n\n".format(heading, len(heading) * "=")

        # The reverse dependencies from each select/imply are ORed together
        for select in kconfiglib.split_expr(expr, kconfiglib.OR):
            # - 'select/imply A if B' turns into A && B
            # - 'select/imply A' just turns into A
            #
            # In both cases, we can split on AND and pick the first
            # operand.
            sym_rst += " - :option:`CONFIG_{}`\n".format(
                kconfiglib.split_expr(select, kconfiglib.AND)[0].name)

        sym_rst += "\n"
Пример #9
0
    def add_select_imply_rst(type_str, expr):
        # Writes a link for each selecting symbol (if 'expr' is sym.rev_dep) or
        # each implying symbol (if 'expr' is sym.weak_rev_dep). Also adds a
        # heading at the top, derived from type_str ("select"/"imply").

        nonlocal rst

        heading = "Symbols that ``{}`` this symbol".format(type_str)
        rst += "{}\n{}\n\n".format(heading, len(heading)*"=")

        # The reverse dependencies from each select/imply are ORed together
        for select in kconfiglib.split_expr(expr, kconfiglib.OR):
            # - 'select/imply A if B' turns into A && B
            # - 'select/imply A' just turns into A
            #
            # In both cases, we can split on AND and pick the first
            # operand.

            # kconfiglib.expr_str() generates a link
            rst += "- {}\n".format(kconfiglib.expr_str(
                kconfiglib.split_expr(select, kconfiglib.AND)[0]))

        rst += "\n"
Пример #10
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]
Пример #11
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
Пример #12
0
def kconfig_build_resources(app: Sphinx) -> None:
    """Build the Kconfig database and install HTML resources."""

    if not app.config.kconfig_generate_db:
        return

    with progress_message("Building Kconfig database..."):
        kconfig, module_paths = kconfig_load(app)
        db = list()

        for sc in chain(kconfig.unique_defined_syms, kconfig.unique_choices):
            # skip nameless symbols
            if not sc.name:
                continue

            # store alternative defaults (from defconfig files)
            alt_defaults = list()
            for node in sc.nodes:
                if "defconfig" not in node.filename:
                    continue

                for value, cond in node.orig_defaults:
                    fmt = kconfiglib.expr_str(value, sc_fmt)
                    if cond is not sc.kconfig.y:
                        fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
                    alt_defaults.append([fmt, node.filename])

            # build list of symbols that select/imply the current one
            # note: all reverse dependencies are ORed together, and conditionals
            # (e.g. select/imply A if B) turns into A && B. So we first split
            # by OR to include all entries, and we split each one by AND to just
            # take the first entry.
            selected_by = list()
            if isinstance(sc,
                          kconfiglib.Symbol) and sc.rev_dep != sc.kconfig.n:
                for select in kconfiglib.split_expr(sc.rev_dep, kconfiglib.OR):
                    sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
                    selected_by.append(f"CONFIG_{sym.name}")

            implied_by = list()
            if isinstance(
                    sc, kconfiglib.Symbol) and sc.weak_rev_dep != sc.kconfig.n:
                for select in kconfiglib.split_expr(sc.weak_rev_dep,
                                                    kconfiglib.OR):
                    sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
                    implied_by.append(f"CONFIG_{sym.name}")

            # only process nodes with prompt or help
            nodes = [node for node in sc.nodes if node.prompt or node.help]

            inserted_paths = list()
            for node in nodes:
                # avoid duplicate symbols by forcing unique paths. this can
                # happen due to dependencies on 0, a trick used by some modules
                path = f"{node.filename}:{node.linenr}"
                if path in inserted_paths:
                    continue
                inserted_paths.append(path)

                dependencies = None
                if node.dep is not sc.kconfig.y:
                    dependencies = kconfiglib.expr_str(node.dep, sc_fmt)

                defaults = list()
                for value, cond in node.orig_defaults:
                    fmt = kconfiglib.expr_str(value, sc_fmt)
                    if cond is not sc.kconfig.y:
                        fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
                    defaults.append(fmt)

                selects = list()
                for value, cond in node.orig_selects:
                    fmt = kconfiglib.expr_str(value, sc_fmt)
                    if cond is not sc.kconfig.y:
                        fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
                    selects.append(fmt)

                implies = list()
                for value, cond in node.orig_implies:
                    fmt = kconfiglib.expr_str(value, sc_fmt)
                    if cond is not sc.kconfig.y:
                        fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
                    implies.append(fmt)

                ranges = list()
                for min, max, cond in node.orig_ranges:
                    fmt = (f"[{kconfiglib.expr_str(min, sc_fmt)}, "
                           f"{kconfiglib.expr_str(max, sc_fmt)}]")
                    if cond is not sc.kconfig.y:
                        fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
                    ranges.append(fmt)

                choices = list()
                if isinstance(sc, kconfiglib.Choice):
                    for sym in sc.syms:
                        choices.append(kconfiglib.expr_str(sym, sc_fmt))

                menupath = ""
                iternode = node
                while iternode.parent is not iternode.kconfig.top_node:
                    iternode = iternode.parent
                    menupath = f" > {iternode.prompt[0]}" + menupath

                menupath = "(Top)" + menupath

                filename = node.filename
                for name, path in module_paths.items():
                    if node.filename.startswith(path):
                        filename = node.filename.replace(
                            path, f"<module:{name}>")
                        break

                db.append({
                    "name": f"CONFIG_{sc.name}",
                    "prompt": node.prompt[0] if node.prompt else None,
                    "type": kconfiglib.TYPE_TO_STR[sc.type],
                    "help": node.help,
                    "dependencies": dependencies,
                    "defaults": defaults,
                    "alt_defaults": alt_defaults,
                    "selects": selects,
                    "selected_by": selected_by,
                    "implies": implies,
                    "implied_by": implied_by,
                    "ranges": ranges,
                    "choices": choices,
                    "filename": filename,
                    "linenr": node.linenr,
                    "menupath": menupath,
                })

        app.env.kconfig_db = db  # type: ignore

        outdir = Path(app.outdir) / "kconfig"
        outdir.mkdir(exist_ok=True)

        kconfig_db_file = outdir / "kconfig.json"

        with open(kconfig_db_file, "w") as f:
            json.dump(db, f)

    app.config.html_extra_path.append(kconfig_db_file.as_posix())
    app.config.html_static_path.append(RESOURCES_DIR.as_posix())
Пример #13
0
def try_fix_deps(symbols):
    while symbols:
        check = set()

        for sym in symbols:
            if deps_met(sym):
                continue

            deps = K.split_expr(sym.direct_dep, K.AND)
            for dep in deps:
                if isinstance(dep, tuple):
                    if dep[0] == K.EQUAL and (dep[1].is_constant
                                              or dep[2].is_constant):
                        if dep[1].is_constant:
                            tgt, src = dep[2], dep[1]
                        else:
                            tgt, src = dep[1], dep[2]

                        v_old = K.TRI_TO_STR[user_value(tgt)]
                        v_new = K.TRI_TO_STR[user_value(src)]
                        eprint(
                            f"warning: Changing symbol value: {tgt.name} {v_old} -> {v_new}"
                        )

                        tgt.set_value(user_value(src))
                        check.add(tgt)

                        continue

                    if dep[0] == K.UNEQUAL and (dep[1].is_constant
                                                or dep[2].is_constant):
                        if dep[1].is_constant:
                            tgt, src = dep[2], dep[1]
                        else:
                            tgt, src = dep[1], dep[2]

                        values = set([1,
                                      2])  # assume that 'n' is not an option
                        values.remove(user_value(src))
                        value = (*values, )[0]

                        v_old = K.TRI_TO_STR[user_value(tgt)]
                        v_new = K.TRI_TO_STR[value]
                        eprint(
                            f"warning: Changing symbol value: {tgt.name} {v_old} -> {v_new}"
                        )

                        tgt.set_value(value)
                        check.add(tgt)

                        continue

                    eprint(
                        "error: Cannot fix dependencies: Complex dependency statements not suppoted"
                    )
                    eprint(f"       On symbol: {sym.name}")
                    exit(1)

                if dep_satisfied(sym, dep):
                    continue

                if dep.orig_type not in K._BOOL_TRISTATE:
                    eprint(
                        "error: Cannot fix dependencies: Non-boolean and non-tristate dependency"
                    )

                if dep.orig_type == K.TRISTATE and sym.orig_type == K.TRISTATE:
                    value = user_value(sym)  # copy tristate value
                elif dep.orig_type == K.TRISTATE:
                    value = 1  # prefer M for bool dependents
                else:
                    value = 2  # set bool dependency to Y

                if value > user_value(dep):
                    v_old = K.TRI_TO_STR[user_value(dep)]
                    v_new = K.TRI_TO_STR[value]
                    eprint(
                        f"warning: Changing symbol value: {dep.name} {v_old} -> {v_new}"
                    )

                    dep.set_value(value)
                    check.add(dep)

        symbols = check