kconfig: Show unsatisfied deps. when assignments don't take

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>
This commit is contained in:
Ulf Magnusson 2020-01-20 15:27:09 +01:00 committed by Carles Cufí
commit ba2f95ab39

View file

@ -5,7 +5,10 @@ import os
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),
@ -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
@ -119,27 +115,75 @@ other symbols. \
""" + 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
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))
# 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 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 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]
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
@ -150,8 +194,11 @@ 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(
@ -159,8 +206,8 @@ the choice 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