Skip to content

Commit

Permalink
kconfig: Show unsatisfied deps. when assignments don't take
Browse files Browse the repository at this point in the history
Show which dependencies are unsatisfied when symbols don't get their
assigned value.

For example, assume that FOO below is assigned with CONFIG_FOO=y. Note
that BAR, BAZ, and STR = "hmm" are 'n'.

    config FOO
           bool "foo"
           depends on BAR && BAZ && QAZ && STR = "hmm"

    config BAR
           def_bool n

    config BAZ
           def_bool n

    config QAZ
           def_bool y

    config STR
           def_string "zmh"

This now prints this warning:

    warning: FOO (defined at /home/ulf/z/z/Kconfig:10) was assigned the
    value 'y' but got the value 'n'. Check these unsatisfied
    dependencies: BAR (=n), BAZ (=n), STR = "hmm" (=n). See ...

Fixes: #21888

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
  • Loading branch information
ulfalizer committed Jan 22, 2020
1 parent 937c9c2 commit f830cf8
Showing 1 changed file with 80 additions and 33 deletions.
113 changes: 80 additions & 33 deletions scripts/kconfig/kconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import sys
import textwrap

from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR
# Zephyr doesn't use tristate symbols. They're supported here just to make the
# script a bit more generic.
from kconfiglib import Kconfig, split_expr, expr_value, expr_str, BOOL, \
TRISTATE, TRI_TO_STR, AND


# Warnings that won't be turned into errors (but that will still be printed),
Expand Down Expand Up @@ -58,18 +61,11 @@ def main():
# output and also assigns promptless symbols.
check_no_promptless_assign(kconf)

# Print warnings for symbols whose actual value doesn't match the assigned
# value
for sym in kconf.unique_defined_syms:
# Was the symbol assigned to? Choice symbols are checked separately.
if sym.user_value is not None and not sym.choice:
check_assigned_sym_value(sym)

# Print warnings for choices whose actual selection doesn't match the user
# selection
for choice in kconf.unique_choices:
if choice.user_selection:
check_assigned_choice_value(choice)
# Print warnings for symbols that didn't get the assigned value. Only
# do this for handwritten input too, to avoid likely unhelpful warnings
# when using an old configuration and updating Kconfig files.
check_assigned_sym_values(kconf)
check_assigned_choice_values(kconf)

# Hack: Force all symbols to be evaluated, to catch warnings generated
# during evaluation. Wait till the end to write the actual output files, so
Expand Down Expand Up @@ -119,27 +115,75 @@ def check_no_promptless_assign(kconf):
""" + SYM_INFO_HINT).format(sym))


def check_assigned_sym_value(sym):
# Verifies that the value assigned to 'sym' "took" (matches the value the
# symbol actually got), printing a warning otherwise
def check_assigned_sym_values(kconf):
# Verifies that the values assigned to symbols "took" (matches the value
# the symbols actually got), printing warnings otherwise. Choice symbols
# are checked separately, in check_assigned_choice_values().

for sym in kconf.unique_defined_syms:
if sym.choice:
continue

# 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 is None:
continue

# 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[user_value]

if user_value != sym.str_value:
msg = f"{sym.name_and_loc} was assigned the value '{user_value}' " \
f"but got the value '{sym.str_value}'. "

# List any unsatisfied 'depends on' dependencies in the warning
mdeps = missing_deps(sym)
if mdeps:
expr_strs = []
for expr in mdeps:
estr = expr_str(expr)
if isinstance(expr, tuple):
# Add () around dependencies that aren't plain symbols.
# Gives '(FOO || BAR) (=n)' instead of
# 'FOO || BAR (=n)', which might be clearer.
estr = f"({estr})"
expr_strs.append(f"{estr} (={TRI_TO_STR[expr_value(expr)]})")

msg += "Check these unsatisfied dependencies: " + \
", ".join(expr_strs) + ". "

warn(msg + SYM_INFO_HINT.format(sym))


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"'.

if user_value != sym.str_value:
warn(("""\
{0.name_and_loc} was assigned the value '{1}' but got the value
'{0.str_value}'. Check its dependencies. \
""" + SYM_INFO_HINT).format(sym, user_value))
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]

def check_assigned_choice_value(choice):
# Verifies that the choice symbol that was selected (by setting it to y)
# ended up as the selection, printing a warning otherwise.

def check_assigned_choice_values(kconf):
# Verifies that any choice symbols that were selected (by setting them to
# y) ended up as the selection, printing warnings otherwise.
#
# We check choice symbols separately to avoid warnings when two different
# choice symbols within the same choice are set to y. This might happen if
Expand All @@ -150,17 +194,20 @@ def check_assigned_choice_value(choice):
# Without special-casing choices, we'd detect that the first symbol set to
# y ended up as n, and print a spurious warning.

if choice.user_selection is not choice.selection:
warn(("""\
for choice in kconf.unique_choices:
if choice.user_selection and \
choice.user_selection is not choice.selection:

warn(("""\
the choice symbol {0.name_and_loc} was selected (set =y), but {1} ended up as
the choice selection. \
""" + SYM_INFO_HINT).format(
choice.user_selection,
choice.selection.name_and_loc if choice.selection else "no symbol"))


# Hint on where to find symbol information. Expects the first argument of
# format() to be the symbol.
# Hint on where to find symbol information. Used like
# SYM_INFO_HINT.format(sym).
SYM_INFO_HINT = """\
See http://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_{0.name}.html
and/or look up {0.name} in the menuconfig/guiconfig interface. The Application
Expand Down

0 comments on commit f830cf8

Please sign in to comment.