kconfig: list all definitions of a symbol in help text
authorThomas Hebb <tommyhebb@gmail.com>
Tue, 17 Dec 2019 16:15:43 +0000 (08:15 -0800)
committerChristian Lamparter <chunkeey@gmail.com>
Fri, 5 Feb 2021 10:51:23 +0000 (11:51 +0100)
In Kconfig, each symbol (representing a config option) can be defined in
multiple places. Each definition may or may not have a prompt, which
allows the option to be set via an interface like menuconfig. Each
definition has a set of dependencies, which determine whether its prompt
is visible and whether other pieces of the definition, like a default
value, take effect.

Historically, a symbol's help text (i.e. what's shown when a user
presses '?' in menuconfig) contained some symbol-wide information not
tied to any particular definition (e.g. what other symbols it selects)
as well as the location (file name and line number) and dependencies of
each prompt. Notably, the help text did not show the location or
dependencies of definitions without prompts.

Because this made it hard to reason about symbols that had no prompts,
commit bcdedcc1afd6 ("menuconfig: print more info for symbol without
prompts") changed the help text so that, instead of containing the
location and dependencies of each prompt, it contained the location and
dependencies of the symbol's first definition, regardless of whether or
not that definition had a prompt.

For symbols with only one definition, that change makes sense. However,
it breaks down for symbols with multiple definitions: each definition
has its own set of dependencies (the `dep` field of `struct menu`), and
those dependencies are ORed together to get the symbol's dependency list
(the `dir_dep` field of `struct symbol`). By printing only the
dependencies of the first definition, the help text misleads users into
believing that an option is more narrowly-applicable than it actually
is.

For an extreme example of this, we can look at the SYS_TEXT_BASE symbol
in the Das U-Boot project (version 2019.10), which also uses Kconfig. (I
unfortunately could not find an illustrative example in Linux.) This
config option specifies the load address of the built binary and, as
such, is applicable to basically every configuration possible. And yet,
without this patch, its help text is as follows:

  Symbol: SYS_TEXT_BASE [=]
  Type  : hex
  Prompt: U-Boot base address
    Location:
      -> ARM architecture
  Prompt: Text Base
    Location:
      -> Boot images
    Defined at arch/arm/mach-aspeed/Kconfig:9
    Depends on: ARM [=n] && ARCH_ASPEED [=n]

The help text indicates that the option is applicable only for a
specific unselected architecture (aspeed), because that architecture's
promptless definition (which just sets a default value), happens to be
the first one seen. No definition or dependency information is printed
for either of the two prompts listed.

Because source locations and dependencies are fundamentally properties
of definitions and not of symbols, we should treat them as such. This
patch brings back the pre-bcdedcc1afd6 behavior for definitions with
prompts but also separately prints the location and dependencies of
those without prompts, solving the original problem in a different way.
With this change, our SYS_TEXT_BASE example becomes

   Symbol: SYS_TEXT_BASE [=]
   Type  : hex
   Defined at arch/arm/mach-stm32mp/Kconfig:83
     Prompt: U-Boot base address
     Depends on: ARM [=n] && ARCH_STM32MP [=n]
     Location:
       -> ARM architecture
   Defined at Kconfig:532
     Prompt: Text Base
     Depends on: !NIOS2 [=n] && !XTENSA [=n] && !EFI_APP [=n]
     Location:
       -> Boot images
   Defined at arch/arm/mach-aspeed/Kconfig:9
     Depends on: ARM [=n] && ARCH_ASPEED [=n]
   Defined  at arch/arm/mach-socfpga/Kconfig:25
     Depends on: ARM [=n] && ARCH_SOCFPGA [=n]
   <snip>
   Defined at board/sifive/fu540/Kconfig:15
     Depends on: RISCV [=n] && TARGET_SIFIVE_FU540 [=n]

which is a much more accurate representation.

Note that there is one notable difference between what gets printed for
prompts after this change and what got printed before bcdedcc1afd6: the
"Depends on" line now accurately represents the prompt's dependencies
instead of conflating those with the prompt's visibility (which can
include extra conditions). See the patch later in this series titled
"kconfig: distinguish between dependencies and visibility in help text"
for more details and better handling of that nuance.

Signed-off-by: Thomas Hebb <tommyhebb@gmail.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
config/menu.c

index 8b772ced755dd382cef4b184499627c582aaab21..b4fb0c88b6e0e0672b03053a71a98aec96a38e3b 100644 (file)
@@ -711,6 +711,21 @@ const char *menu_get_help(struct menu *menu)
                return "";
 }
 
+static void get_def_str(struct gstr *r, struct menu *menu)
+{
+       str_printf(r, "Defined at %s:%d\n",
+                  menu->file->name, menu->lineno);
+}
+
+static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix)
+{
+       if (!expr_is_yes(expr)) {
+               str_append(r, prefix);
+               expr_gstr_print(expr, r);
+               str_append(r, "\n");
+       }
+}
+
 static void get_prompt_str(struct gstr *r, struct property *prop,
                           struct list_head *head)
 {
@@ -718,7 +733,9 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
        struct menu *submenu[8], *menu, *location = NULL;
        struct jump_key *jump = NULL;
 
-       str_printf(r, "Prompt: %s\n", prop->text);
+       str_printf(r, "  Prompt: %s\n", prop->text);
+
+       get_dep_str(r, prop->menu->dep, "  Depends on: ");
        menu = prop->menu->parent;
        for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) {
                bool accessible = menu_is_visible(menu);
@@ -768,18 +785,6 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
        }
 }
 
-/*
- * get property of type P_SYMBOL
- */
-static struct property *get_symbol_prop(struct symbol *sym)
-{
-       struct property *prop = NULL;
-
-       for_all_properties(sym, prop, P_SYMBOL)
-               break;
-       return prop;
-}
-
 static void get_symbol_props_str(struct gstr *r, struct symbol *sym,
                                 enum prop_type tok, const char *prefix)
 {
@@ -819,17 +824,19 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,
                        }
                }
        }
-       for_all_prompts(sym, prop)
-               get_prompt_str(r, prop, head);
-
-       prop = get_symbol_prop(sym);
-       if (prop) {
-               str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
-                       prop->menu->lineno);
-               if (!expr_is_yes(prop->visible.expr)) {
-                       str_append(r, "  Depends on: ");
-                       expr_gstr_print(prop->visible.expr, r);
-                       str_append(r, "\n");
+
+       /* Print the definitions with prompts before the ones without */
+       for_all_properties(sym, prop, P_SYMBOL) {
+               if (prop->menu->prompt) {
+                       get_def_str(r, prop->menu);
+                       get_prompt_str(r, prop->menu->prompt, head);
+               }
+       }
+
+       for_all_properties(sym, prop, P_SYMBOL) {
+               if (!prop->menu->prompt) {
+                       get_def_str(r, prop->menu);
+                       get_dep_str(r, prop->menu->dep, "  Depends on: ");
                }
        }