From nobody Thu Feb 12 15:46:20 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1DB8B74079; Tue, 11 Jun 2024 17:56:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718128568; cv=none; b=fngyft65cVCnauaDcxH9RYDfs/G5JSA95jqjRGKThMYXXmKnUYSoBch99RX7hwJu9X8uaGibg8mwvvDczduGAkg4rYd/qjL0Vr3fz7EXx3PstHeRudnoUP1enMw/ynu1DLdoxKoAAU0wFtVV5AR/3hOm+Zu+UyoetDIX9hMb3So= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718128568; c=relaxed/simple; bh=zrTBUnF4HfSxUYrDoB9vKX6e+xSiccO/5kgVgT45GPo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AJgHba2NQvZgl6i5MmmiBYSW/VfJicBCa2bkm8BFyQtJAa4l1nN8PPnjlhGpj9khYivocdBIEAT4W2Z4m6wWQoU1rawmeFmyXtx3hw3AR71hx1cwboLWmZP3ETJ1QclA8xiLahIJLqsAK9e2xexqhP9Az2JV+cby0n3SoaEGNII= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YFvRCEjc; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YFvRCEjc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EEBE3C4AF1C; Tue, 11 Jun 2024 17:56:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1718128567; bh=zrTBUnF4HfSxUYrDoB9vKX6e+xSiccO/5kgVgT45GPo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YFvRCEjcmRvL0WbERtTBOMalKSNCNdZ2PWPg0KIWIO/0K+n7sXeXP7zFO35Y3TkpV ZFynYwVM8HGAlaZJj6RAKXFS2AiJv6rhAPxkS2LvKYp6kTYeIItCIFq+LSfOpqJ4wB R0JNry3YUg7wUHWXJUpalqbL3HEIQ1ST9/xXPfXEhhBsmxshb3IIP2SyEIaT9ZTOex 2J65ZTxRNc0khQ0qlngF1fToCaBpwQvNJvn88u9nBfjK3bu9OyBKxgftEr5RugWuFb XyNdJh/giGk7Iuy2U7SEVm331GV4PuqIHtyBEBZ50SUbiXDEUCi4urLdN16f9Otb7v wmCN+rlz06T7Q== From: Masahiro Yamada To: linux-kbuild@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Masahiro Yamada Subject: [PATCH 06/16] kconfig: refactor choice value calculation Date: Wed, 12 Jun 2024 02:55:15 +0900 Message-ID: <20240611175536.3518179-7-masahiroy@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240611175536.3518179-1-masahiroy@kernel.org> References: <20240611175536.3518179-1-masahiroy@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Handling choices has always been in a PITA in Kconfig. For example, fixes and reverts were repeated for randconfig with KCONFIG_ALLCONFIG: - 422c809f03f0 ("kconfig: fix randomising choice entries in presence of KC= ONFIG_ALLCONFIG") - 23a5dfdad22a ("Revert "kconfig: fix randomising choice entries in presen= ce of KCONFIG_ALLCONFIG"") - 8357b48549e1 ("kconfig: fix randomising choice entries in presence of KC= ONFIG_ALLCONFIG") - 490f16171119 ("Revert "kconfig: fix randomising choice entries in presen= ce of KCONFIG_ALLCONFIG"") As these commits pointed out, randconfig does not randomize choices when KCONFIG_ALLCONFIG is used. This issue still remains. [Test Case] choice prompt "choose" config A bool "A" config B bool "B" endchoice $ echo > all.config $ make KCONFIG_ALLCONFIG=3D1 randconfig The output is always as follows: CONFIG_A=3Dy # CONFIG_B is not set Not only randconfig, but other all*config variants are broken with KCONFIG_ALLCONFIG. With the same Kconfig, $ echo '# CONFIG_A is not set' > all.config $ make KCONFIG_ALLCONFIG=3D1 allyesconfig You will get this: CONFIG_A=3Dy # CONFIG_B is not set This is incorrect because it does not respect all.config. The correct output should be: # CONFIG_A is not set CONFIG_B=3Dy To handle user inputs more accurately, this commit refactors the code based on the following principles: - When a user value is given, Kconfig must set it immediately. Do not defer it by setting SYMBOL_NEED_SET_CHOICE_VALUES. - The SYMBOL_DEF_USER flag must not be cleared, unless a new config file is loaded. Kconfig must not forget user inputs. In addition, user values for choices must be managed with priority. If user inputs conflict within a choice block, the newest value wins. The values given by randconfig have lower priority than explicit user inputs. This commit implements it by using a linked list. Every time a choice block gets a new input, it is moved to the top of the list. Let me explain how it works. Let's say, we have a choice block that consists of three symbols: A, B, and C. Initially, the linked list looks like this: A(=3D?) --> B(=3D?) --> C(=3D?) Say, '# CONFIG_B is not set' is specified by KCONFIG_ALLCONFIG. B is set to 'n', and moved to the top of the linked list: B(=3Dn) --> A(=3D?) --> C(=3D?) The randconfig shuffles the symbols without a user value. So, you will get: B(=3Dn) --> A(=3Dy) --> C(=3Dy) or B(=3Dn) --> C(=3Dy) --> A(=3Dy) When calculating the output, the linked list is traversed. The first visible symbol with =3Dy is taken. You will get either CONFIG_A=3Dy or CONFIG_C=3Dy with equal probability. As another example, let's say the .config with the following content is loaded: CONFIG_B=3Dy CONFIG_C=3Dy The linked list will become: C(=3Dy) --> B(=3Dy) --> A(=3D?) Please note the last one is prioritized when a decision conflicts in the same file. This is reasonable behavior because merge_config.sh appends config fragments to the existing .config file. So, the output will be CONFIG_C=3Dy if C is visible, but otherwise CONFIG_B=3Dy. This is different from the former implementation; previously, Kconfig forgot CONFIG_B=3Dy when CONFIG_C=3Dy appeared later in the same file. In the new implementation, Kconfig remembers both CONFIG_B=3Dy and CONFIG_C=3Dy, prioritizing the former. If C is hidden due to unmet dependency, CONFIG_B=3Dy arises as the second best. Signed-off-by: Masahiro Yamada --- scripts/kconfig/conf.c | 131 +++++++++++++++----------------- scripts/kconfig/confdata.c | 54 +++----------- scripts/kconfig/expr.h | 12 ++- scripts/kconfig/lkc.h | 7 +- scripts/kconfig/menu.c | 17 +---- scripts/kconfig/parser.y | 4 + scripts/kconfig/symbol.c | 149 ++++++++++++++++++++++--------------- 7 files changed, 177 insertions(+), 197 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 5dbdd9459f21..1c59998a62f7 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -114,41 +114,54 @@ static void set_randconfig_seed(void) srand(seed); } =20 -static void randomize_choice_values(struct symbol *csym) +/** + * randomize_choice_values - randomize choice block + * + * @choice: menu entry for the choice + */ +static void randomize_choice_values(struct menu *choice) { - struct property *prop; - struct symbol *sym; - struct expr *e; - int cnt, def; - - prop =3D sym_get_choice_prop(csym); - - /* count entries in choice block */ - cnt =3D 0; - expr_list_for_each_sym(prop->expr, e, sym) - cnt++; + struct menu *menu; + int x; + int cnt =3D 0; =20 /* - * find a random value and set it to yes, - * set the rest to no so we have only one set + * First, count the number of symbols to randomize. If sym_has_value() + * is true, it was specified by KCONFIG_ALLCONFIG. It needs to be + * respected. */ - def =3D rand() % cnt; + menu_for_each_sub_entry(menu, choice) { + struct symbol *sym =3D menu->sym; =20 - cnt =3D 0; - expr_list_for_each_sym(prop->expr, e, sym) { - if (def =3D=3D cnt++) { - sym->def[S_DEF_USER].tri =3D yes; - csym->def[S_DEF_USER].val =3D sym; - } else { - sym->def[S_DEF_USER].tri =3D no; - } - sym->flags |=3D SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - sym->flags &=3D ~SYMBOL_VALID; + if (sym && !sym_has_value(sym)) + cnt++; + } + + while (cnt > 0) { + x =3D rand() % cnt; + + menu_for_each_sub_entry(menu, choice) { + struct symbol *sym =3D menu->sym; + + if (sym && !sym_has_value(sym)) + x--; + + if (x < 0) { + sym->def[S_DEF_USER].tri =3D yes; + sym->flags |=3D SYMBOL_DEF_USER; + /* + * Move the selected item to the _tail_ because + * this needs to have a lower priority than the + * user input from KCONFIG_ALLCONFIG. + */ + list_move_tail(&sym->choice_link, + &choice->choice_members); + + break; + } + } + cnt--; } - csym->flags |=3D SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - csym->flags &=3D ~SYMBOL_VALID; } =20 enum conf_def_mode { @@ -159,9 +172,9 @@ enum conf_def_mode { def_random }; =20 -static bool conf_set_all_new_symbols(enum conf_def_mode mode) +static void conf_set_all_new_symbols(enum conf_def_mode mode) { - struct symbol *sym, *csym; + struct menu *menu; int cnt; /* * can't go as the default in switch-case below, otherwise gcc whines @@ -170,7 +183,6 @@ static bool conf_set_all_new_symbols(enum conf_def_mode= mode) int pby =3D 50; /* probability of bool =3D y */ int pty =3D 33; /* probability of tristate =3D y */ int ptm =3D 33; /* probability of tristate =3D m */ - bool has_changed =3D false; =20 if (mode =3D=3D def_random) { int n, p[3]; @@ -217,14 +229,21 @@ static bool conf_set_all_new_symbols(enum conf_def_mo= de mode) } } =20 - for_all_symbols(sym) { + menu_for_each_entry(menu) { + struct symbol *sym =3D menu->sym; tristate val; =20 - if (sym_has_value(sym) || sym->flags & SYMBOL_VALID || - (sym->type !=3D S_BOOLEAN && sym->type !=3D S_TRISTATE)) + if (!sym || !menu->prompt || sym_has_value(sym) || + (sym->type !=3D S_BOOLEAN && sym->type !=3D S_TRISTATE) || + sym_is_choice_value(sym)) continue; =20 - has_changed =3D true; + if (sym_is_choice(sym)) { + if (mode =3D=3D def_random) + randomize_choice_values(menu); + continue; + } + switch (mode) { case def_yes: val =3D yes; @@ -251,34 +270,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mo= de mode) continue; } sym->def[S_DEF_USER].tri =3D val; - - if (!(sym_is_choice(sym) && mode =3D=3D def_random)) - sym->flags |=3D SYMBOL_DEF_USER; + sym->flags |=3D SYMBOL_DEF_USER; } =20 sym_clear_all_valid(); - - if (mode !=3D def_random) { - for_all_symbols(csym) { - if ((sym_is_choice(csym) && !sym_has_value(csym)) || - sym_is_choice_value(csym)) - csym->flags |=3D SYMBOL_NEED_SET_CHOICE_VALUES; - } - } - - for_all_symbols(csym) { - if (sym_has_value(csym) || !sym_is_choice(csym)) - continue; - - sym_calc_value(csym); - if (mode =3D=3D def_random) - randomize_choice_values(csym); - else - set_all_choice_values(csym); - has_changed =3D true; - } - - return has_changed; } =20 static void conf_rewrite_tristates(tristate old_val, tristate new_val) @@ -429,10 +424,9 @@ static void conf_choice(struct menu *menu) { struct symbol *sym, *def_sym; struct menu *child; - bool is_new; + bool is_new =3D false; =20 sym =3D menu->sym; - is_new =3D !sym_has_value(sym); =20 while (1) { int cnt, def; @@ -456,8 +450,10 @@ static void conf_choice(struct menu *menu) printf("%*c", indent, ' '); printf(" %d. %s (%s)", cnt, menu_get_prompt(child), child->sym->name); - if (!sym_has_value(child->sym)) + if (!sym_has_value(child->sym)) { + is_new =3D true; printf(" (NEW)"); + } printf("\n"); } printf("%*schoice", indent - 1, ""); @@ -586,9 +582,7 @@ static void check_conf(struct menu *menu) return; =20 sym =3D menu->sym; - if (sym && !sym_has_value(sym) && - (sym_is_changeable(sym) || sym_is_choice(sym))) { - + if (sym && !sym_has_value(sym) && sym_is_changeable(sym)) { switch (input_mode) { case listnewconfig: if (sym->name) @@ -804,8 +798,7 @@ int main(int ac, char **av) conf_set_all_new_symbols(def_default); break; case randconfig: - /* Really nothing to do in this loop */ - while (conf_set_all_new_symbols(def_random)) ; + conf_set_all_new_symbols(def_random); break; case defconfig: conf_set_all_new_symbols(def_default); diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 1ac7fc9ad756..05823f85402a 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -382,10 +382,7 @@ int conf_read_simple(const char *name, int def) =20 def_flags =3D SYMBOL_DEF << def; for_all_symbols(sym) { - sym->flags |=3D SYMBOL_CHANGED; sym->flags &=3D ~(def_flags|SYMBOL_VALID); - if (sym_is_choice(sym)) - sym->flags |=3D def_flags; switch (sym->type) { case S_INT: case S_HEX: @@ -399,6 +396,8 @@ int conf_read_simple(const char *name, int def) } =20 while (getline_stripped(&line, &line_asize, in) !=3D -1) { + struct menu *choice; + conf_lineno++; =20 if (!line[0]) /* blank line */ @@ -460,15 +459,14 @@ int conf_read_simple(const char *name, int def) if (conf_set_sym_val(sym, def, def_flags, val)) continue; =20 - if (sym && sym_is_choice_value(sym)) { - struct symbol *cs =3D prop_get_symbol(sym_get_choice_prop(sym)); - if (sym->def[def].tri =3D=3D yes) { - if (cs->def[def].tri !=3D no) - conf_warning("override: %s changes choice state", sym->name); - cs->def[def].val =3D sym; - cs->def[def].tri =3D yes; - } - } + /* + * If this is a choice member, give it the highest priority. + * If conflicting CONFIG options are given from an input file, + * the last one wins. + */ + choice =3D sym_get_choice_menu(sym); + if (choice) + list_move(&sym->choice_link, &choice->choice_members); } free(line); fclose(in); @@ -514,18 +512,6 @@ int conf_read(const char *name) /* maybe print value in verbose mode... */ } =20 - for_all_symbols(sym) { - if (sym_has_value(sym) && !sym_is_choice_value(sym)) { - /* Reset values of generates values, so they'll appear - * as new, if they should become visible, but that - * doesn't quite work if the Kconfig and the saved - * configuration disagree. - */ - if (sym->visible =3D=3D no && !conf_unsaved) - sym->flags &=3D ~SYMBOL_DEF_USER; - } - } - if (conf_warnings || conf_unsaved) conf_set_changed(true); =20 @@ -1146,23 +1132,3 @@ void conf_set_changed_callback(void (*fn)(bool)) { conf_changed_callback =3D fn; } - -void set_all_choice_values(struct symbol *csym) -{ - struct property *prop; - struct symbol *sym; - struct expr *e; - - prop =3D sym_get_choice_prop(csym); - - /* - * Set all non-assinged choice values to no - */ - expr_list_for_each_sym(prop->expr, e, sym) { - if (!sym_has_value(sym)) - sym->def[S_DEF_USER].tri =3D no; - } - csym->flags |=3D SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - csym->flags &=3D ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); -} diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 7c0c242318bc..7acf27a4f454 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -73,6 +73,8 @@ enum { * Represents a configuration symbol. * * Choices are represented as a special kind of symbol with null name. + * + * @choice_link: linked to menu::choice_members */ struct symbol { /* link node for the hash table */ @@ -110,6 +112,8 @@ struct symbol { /* config entries associated with this symbol */ struct list_head menus; =20 + struct list_head choice_link; + /* SYMBOL_* flags */ int flags; =20 @@ -133,7 +137,6 @@ struct symbol { #define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG)= */ -#define SYMBOL_CHANGED 0x0400 /* ? */ #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .= config */ #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ #define SYMBOL_WARNED 0x8000 /* warning has been issued */ @@ -145,9 +148,6 @@ struct symbol { #define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ #define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ =20 -/* choice values need to be set before calculating this symbol value */ -#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 - #define SYMBOL_MAXLENGTH 256 =20 /* A property represent the config options that can be associated @@ -204,6 +204,8 @@ struct property { * for all front ends). Each symbol, menu, etc. defined in the Kconfig fil= es * gets a node. A symbol defined in multiple locations gets one node at ea= ch * location. + * + * @choice_members: list of choice members with priority. */ struct menu { /* The next menu node at the same level */ @@ -223,6 +225,8 @@ struct menu { =20 struct list_head link; /* link to symbol::menus */ =20 + struct list_head choice_members; + /* * The prompt associated with the node. This holds the prompt for a * symbol as well as the text for a menu or comment, along with the diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 64dfc354dd5c..bdd37a16b040 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -40,7 +40,6 @@ void zconf_nextfile(const char *name); /* confdata.c */ extern struct gstr autoconf_cmd; const char *conf_get_configname(void); -void set_all_choice_values(struct symbol *csym); =20 /* confdata.c and expr.c */ static inline void xfwrite(const void *str, size_t len, size_t count, FILE= *out) @@ -121,11 +120,7 @@ static inline tristate sym_get_tristate_value(struct s= ymbol *sym) return sym->curr.tri; } =20 - -static inline struct symbol *sym_get_choice_value(struct symbol *sym) -{ - return (struct symbol *)sym->curr.val; -} +struct symbol *sym_get_choice_value(struct symbol *sym); =20 static inline bool sym_is_choice(struct symbol *sym) { diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index bf5dcc05350b..170a269a8d7c 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -591,7 +591,6 @@ bool menu_is_empty(struct menu *menu) =20 bool menu_is_visible(struct menu *menu) { - struct menu *child; struct symbol *sym; tristate visible; =20 @@ -610,21 +609,7 @@ bool menu_is_visible(struct menu *menu) } else visible =3D menu->prompt->visible.tri =3D expr_calc_value(menu->prompt->= visible.expr); =20 - if (visible !=3D no) - return true; - - if (!sym || sym_get_tristate_value(menu->sym) =3D=3D no) - return false; - - for (child =3D menu->list; child; child =3D child->next) { - if (menu_is_visible(child)) { - if (sym) - sym->flags |=3D SYMBOL_DEF_USER; - return true; - } - } - - return false; + return visible !=3D no; } =20 const char *menu_get_prompt(struct menu *menu) diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 20538e1d3788..9d58544b0255 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -157,6 +157,9 @@ config_stmt: config_entry_start config_option_list current_entry->filename, current_entry->lineno); yynerrs++; } + + list_add_tail(¤t_entry->sym->choice_link, + ¤t_choice->choice_members); } =20 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); @@ -240,6 +243,7 @@ choice: T_CHOICE T_EOL menu_add_entry(sym); menu_add_expr(P_CHOICE, NULL, NULL); menu_set_type(S_BOOLEAN); + INIT_LIST_HEAD(¤t_entry->choice_members); =20 printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); }; diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 8df0a75f40b9..e59f2d2ce4e6 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -188,7 +188,6 @@ static void sym_set_changed(struct symbol *sym) { struct menu *menu; =20 - sym->flags |=3D SYMBOL_CHANGED; list_for_each_entry(menu, &sym->menus, link) menu->flags |=3D MENU_CHANGED; } @@ -282,36 +281,90 @@ struct symbol *sym_choice_default(struct symbol *sym) return NULL; } =20 -static struct symbol *sym_calc_choice(struct symbol *sym) +/* + * sym_calc_choice - calculate symbol values in a choice + * + * @choice: a menu of the choice + * + * Return: a chosen symbol + */ +static struct symbol *sym_calc_choice(struct menu *choice) { - struct symbol *def_sym; - struct property *prop; - struct expr *e; - int flags; + struct symbol *res =3D NULL; + struct symbol *sym; + struct menu *menu; =20 - /* first calculate all choice values' visibilities */ - flags =3D sym->flags; - prop =3D sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, def_sym) { - sym_calc_visibility(def_sym); - if (def_sym->visible !=3D no) - flags &=3D def_sym->flags; + /* Traverse the list of choice members in the priority order. */ + list_for_each_entry(sym, &choice->choice_members, choice_link) { + sym_calc_visibility(sym); + if (sym->visible =3D=3D no) + continue; + + /* The first visible symble with the user value 'y'. */ + if (sym_has_value(sym) && sym->def[S_DEF_USER].tri =3D=3D yes) { + res =3D sym; + break; + } } =20 - sym->flags &=3D flags | ~SYMBOL_DEF_USER; + /* If 'y' is not found in the user input, try the default */ + if (!res) { + res =3D sym_choice_default(choice->sym); + if (res && sym_has_value(res) && res->def[S_DEF_USER].tri =3D=3D no) + res =3D NULL; + } =20 - /* is the user choice visible? */ - def_sym =3D sym->def[S_DEF_USER].val; - if (def_sym && def_sym->visible !=3D no) - return def_sym; + /* Still not found. Pick up the first visible, user-unspecified symbol. */ + if (!res) { + menu_for_each_sub_entry(menu, choice) { + sym =3D menu->sym; =20 - def_sym =3D sym_choice_default(sym); + if (!sym || sym->visible =3D=3D no || sym_has_value(sym)) + continue; =20 - if (def_sym =3D=3D NULL) - /* no choice? reset tristate value */ - sym->curr.tri =3D no; + res =3D sym; + break; + } + } =20 - return def_sym; + /* Still not found. Pick up the first visible symbol. */ + if (!res) { + menu_for_each_sub_entry(menu, choice) { + sym =3D menu->sym; + + if (!sym || sym->visible =3D=3D no) + continue; + + res =3D sym; + break; + } + } + + menu_for_each_sub_entry(menu, choice) { + tristate val; + + sym =3D menu->sym; + + if (!sym || sym->visible =3D=3D no) + continue; + + val =3D sym =3D=3D res ? yes : no; + + if (sym->curr.tri !=3D val) + sym_set_changed(sym); + + sym->curr.tri =3D val; + sym->flags |=3D SYMBOL_VALID | SYMBOL_WRITE; + } + + return res; +} + +struct symbol *sym_get_choice_value(struct symbol *sym) +{ + struct menu *menu =3D list_first_entry(&sym->menus, struct menu, link); + + return sym_calc_choice(menu); } =20 static void sym_warn_unmet_dep(struct symbol *sym) @@ -347,7 +400,6 @@ void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; struct property *prop; - struct expr *e; =20 if (!sym) return; @@ -355,13 +407,6 @@ void sym_calc_value(struct symbol *sym) if (sym->flags & SYMBOL_VALID) return; =20 - if (sym_is_choice_value(sym) && - sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) { - sym->flags &=3D ~SYMBOL_NEED_SET_CHOICE_VALUES; - prop =3D sym_get_choice_prop(sym); - sym_calc_value(prop_get_symbol(prop)); - } - sym->flags |=3D SYMBOL_VALID; =20 oldval =3D sym->curr; @@ -400,9 +445,11 @@ void sym_calc_value(struct symbol *sym) switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: - if (sym_is_choice_value(sym) && sym->visible =3D=3D yes) { - prop =3D sym_get_choice_prop(sym); - newval.tri =3D (prop_get_symbol(prop)->curr.val =3D=3D sym) ? yes : no; + struct menu *choice_menu =3D sym_get_choice_menu(sym); + + if (choice_menu) { + sym_calc_choice(choice_menu); + newval.tri =3D sym->curr.tri; } else { if (sym->visible !=3D no) { /* if the symbol is visible use the user value @@ -461,8 +508,6 @@ void sym_calc_value(struct symbol *sym) } =20 sym->curr =3D newval; - if (sym_is_choice(sym) && newval.tri =3D=3D yes) - sym->curr.val =3D sym_calc_choice(sym); sym_validate_range(sym); =20 if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { @@ -473,23 +518,8 @@ void sym_calc_value(struct symbol *sym) } } =20 - if (sym_is_choice(sym)) { - struct symbol *choice_sym; - - prop =3D sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, choice_sym) { - if ((sym->flags & SYMBOL_WRITE) && - choice_sym->visible !=3D no) - choice_sym->flags |=3D SYMBOL_WRITE; - if (sym->flags & SYMBOL_CHANGED) - sym_set_changed(choice_sym); - } - + if (sym_is_choice(sym)) sym->flags &=3D ~SYMBOL_WRITE; - } - - if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) - set_all_choice_values(sym); } =20 void sym_clear_all_valid(void) @@ -523,15 +553,15 @@ bool sym_set_tristate_value(struct symbol *sym, trist= ate val) { tristate oldval =3D sym_get_tristate_value(sym); =20 - if (oldval !=3D val && !sym_tristate_within_range(sym, val)) + if (!sym_tristate_within_range(sym, val)) return false; =20 - if (!(sym->flags & SYMBOL_DEF_USER)) { + if (!(sym->flags & SYMBOL_DEF_USER) || sym->def[S_DEF_USER].tri !=3D val)= { + sym->def[S_DEF_USER].tri =3D val; sym->flags |=3D SYMBOL_DEF_USER; sym_set_changed(sym); } =20 - sym->def[S_DEF_USER].tri =3D val; if (oldval !=3D val) sym_clear_all_valid(); =20 @@ -565,10 +595,13 @@ void choice_set_value(struct menu *choice, struct sym= bol *sym) =20 menu->sym->def[S_DEF_USER].tri =3D val; menu->sym->flags |=3D SYMBOL_DEF_USER; - } =20 - choice->sym->def[S_DEF_USER].val =3D sym; - choice->sym->flags |=3D SYMBOL_DEF_USER; + /* + * Now, the user has explicitly enabled or disabled this symbol, + * it should be given the highest priority + */ + list_move(&menu->sym->choice_link, &choice->choice_members); + } =20 if (changed) sym_clear_all_valid(); --=20 2.43.0