diff --git a/scripts/kconfig/kconfig.py b/scripts/kconfig/kconfig.py index 2753a80b36e727..132e3ec46e512e 100755 --- a/scripts/kconfig/kconfig.py +++ b/scripts/kconfig/kconfig.py @@ -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), @@ -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 @@ 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 && && ... && + # + # on 'sym' (which can also come from e.g. a surrounding 'if'), returns a + # list of all 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 = n. + # + # Note that 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 @@ -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 @@ def check_assigned_choice_value(choice): 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