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:
parent
d3a8fd40b9
commit
ba2f95ab39
1 changed files with 80 additions and 33 deletions
|
@ -5,7 +5,10 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
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),
|
# 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.
|
# output and also assigns promptless symbols.
|
||||||
check_no_promptless_assign(kconf)
|
check_no_promptless_assign(kconf)
|
||||||
|
|
||||||
# Print warnings for symbols whose actual value doesn't match the assigned
|
# Print warnings for symbols that didn't get the assigned value. Only
|
||||||
# value
|
# do this for handwritten input too, to avoid likely unhelpful warnings
|
||||||
for sym in kconf.unique_defined_syms:
|
# when using an old configuration and updating Kconfig files.
|
||||||
# Was the symbol assigned to? Choice symbols are checked separately.
|
check_assigned_sym_values(kconf)
|
||||||
if sym.user_value is not None and not sym.choice:
|
check_assigned_choice_values(kconf)
|
||||||
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)
|
|
||||||
|
|
||||||
# Hack: Force all symbols to be evaluated, to catch warnings generated
|
# Hack: Force all symbols to be evaluated, to catch warnings generated
|
||||||
# during evaluation. Wait till the end to write the actual output files, so
|
# during evaluation. Wait till the end to write the actual output files, so
|
||||||
|
@ -119,27 +115,75 @@ other symbols. \
|
||||||
""" + SYM_INFO_HINT).format(sym))
|
""" + SYM_INFO_HINT).format(sym))
|
||||||
|
|
||||||
|
|
||||||
def check_assigned_sym_value(sym):
|
def check_assigned_sym_values(kconf):
|
||||||
# Verifies that the value assigned to 'sym' "took" (matches the value the
|
# Verifies that the values assigned to symbols "took" (matches the value
|
||||||
# symbol actually got), printing a warning otherwise
|
# 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
|
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:
|
if user_value != sym.str_value:
|
||||||
warn(("""\
|
msg = f"{sym.name_and_loc} was assigned the value '{user_value}' " \
|
||||||
{0.name_and_loc} was assigned the value '{1}' but got the value
|
f"but got the value '{sym.str_value}'. "
|
||||||
'{0.str_value}'. Check its dependencies. \
|
|
||||||
""" + SYM_INFO_HINT).format(sym, user_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):
|
def missing_deps(sym):
|
||||||
# Verifies that the choice symbol that was selected (by setting it to y)
|
# check_assigned_sym_values() helper for finding unsatisfied dependencies.
|
||||||
# ended up as the selection, printing a warning otherwise.
|
#
|
||||||
|
# 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
|
# 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
|
# choice symbols within the same choice are set to y. This might happen if
|
||||||
|
@ -150,7 +194,10 @@ def check_assigned_choice_value(choice):
|
||||||
# Without special-casing choices, we'd detect that the first symbol set to
|
# Without special-casing choices, we'd detect that the first symbol set to
|
||||||
# y ended up as n, and print a spurious warning.
|
# y ended up as n, and print a spurious warning.
|
||||||
|
|
||||||
if choice.user_selection is not choice.selection:
|
for choice in kconf.unique_choices:
|
||||||
|
if choice.user_selection and \
|
||||||
|
choice.user_selection is not choice.selection:
|
||||||
|
|
||||||
warn(("""\
|
warn(("""\
|
||||||
the choice symbol {0.name_and_loc} was selected (set =y), but {1} ended up as
|
the choice symbol {0.name_and_loc} was selected (set =y), but {1} ended up as
|
||||||
the choice selection. \
|
the choice selection. \
|
||||||
|
@ -159,8 +206,8 @@ the choice selection. \
|
||||||
choice.selection.name_and_loc if choice.selection else "no symbol"))
|
choice.selection.name_and_loc if choice.selection else "no symbol"))
|
||||||
|
|
||||||
|
|
||||||
# Hint on where to find symbol information. Expects the first argument of
|
# Hint on where to find symbol information. Used like
|
||||||
# format() to be the symbol.
|
# SYM_INFO_HINT.format(sym).
|
||||||
SYM_INFO_HINT = """\
|
SYM_INFO_HINT = """\
|
||||||
See http://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_{0.name}.html
|
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
|
and/or look up {0.name} in the menuconfig/guiconfig interface. The Application
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue