Skip to content

Commit f79dc03

Browse files
committed
kconfig: refactor choice value calculation
Handling choices has always been in a PITA in Kconfig. For example, fixes and reverts were repeated for randconfig with KCONFIG_ALLCONFIG: - 422c809 ("kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG") - 23a5dfd ("Revert "kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG"") - 8357b48 ("kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG") - 490f161 ("Revert "kconfig: fix randomising choice entries in presence 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=1 randconfig The output is always as follows: CONFIG_A=y # CONFIG_B is not set Not only randconfig, but other all*config variants are also broken with KCONFIG_ALLCONFIG. With the same Kconfig, $ echo '# CONFIG_A is not set' > all.config $ make KCONFIG_ALLCONFIG=1 allyesconfig You will get this: CONFIG_A=y # 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=y 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 five symbols: A, B, C, D, and E. Initially, the linked list looks like this: A(=?) --> B(=?) --> C(=?) --> D(=?) --> E(=?) Suppose randconfig is executed with the following KCONFIG_ALLCONFIG: CONFIG_C=y # CONFIG_A is not set CONFIG_D=y First, CONFIG_C=y is read. C is set to 'y' and moved to the top. C(=y) --> A(=?) --> B(=?) --> D(=?) --> E(=?) Next, '# CONFIG_A is not set' is read. A is set to 'n' and moved to the top. A(=n) --> C(=y) --> B(=?) --> D(=?) --> E(=?) Then, 'CONFIG_D=y' is read. D is set to 'y' and moved to the top. D(=y) --> A(=n) --> C(=y) --> B(=?) --> E(=?) Lastly, randconfig shuffles the order of the remaining symbols, resulting in: D(=y) --> A(=n) --> C(=y) --> B(=y) --> E(=y) or D(=y) --> A(=n) --> C(=y) --> E(=y) --> B(=y) When calculating the output, the linked list is traversed and the first visible symbol with 'y' is taken. In this case, it is D if visible. If D is hidden by 'depends on', the next node, A, is examined. Since it is already specified as 'n', it is skipped. Next, C is checked, and selected if it is visible. If C is also invisible, either B or E is chosen as a result of the randomization. If B and E are also invisible, the linked list is traversed in the reverse order, and the least prioritized 'n' symbol is chosen. It is A in this case. Now, Kconfig remembers all user values. This is a big difference from the previous implementation, where Kconfig would forget CONFIG_C=y when CONFIG_D=y appeared in the same input file. The new appaorch respects user-specified values as much as possible. Signed-off-by: Masahiro Yamada <[email protected]>
1 parent ee29e62 commit f79dc03

File tree

7 files changed

+187
-197
lines changed

7 files changed

+187
-197
lines changed

scripts/kconfig/conf.c

Lines changed: 62 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -114,41 +114,54 @@ static void set_randconfig_seed(void)
114114
srand(seed);
115115
}
116116

117-
static void randomize_choice_values(struct symbol *csym)
117+
/**
118+
* randomize_choice_values - randomize choice block
119+
*
120+
* @choice: menu entry for the choice
121+
*/
122+
static void randomize_choice_values(struct menu *choice)
118123
{
119-
struct property *prop;
120-
struct symbol *sym;
121-
struct expr *e;
122-
int cnt, def;
123-
124-
prop = sym_get_choice_prop(csym);
125-
126-
/* count entries in choice block */
127-
cnt = 0;
128-
expr_list_for_each_sym(prop->expr, e, sym)
129-
cnt++;
124+
struct menu *menu;
125+
int x;
126+
int cnt = 0;
130127

131128
/*
132-
* find a random value and set it to yes,
133-
* set the rest to no so we have only one set
129+
* First, count the number of symbols to randomize. If sym_has_value()
130+
* is true, it was specified by KCONFIG_ALLCONFIG. It needs to be
131+
* respected.
134132
*/
135-
def = rand() % cnt;
136-
137-
cnt = 0;
138-
expr_list_for_each_sym(prop->expr, e, sym) {
139-
if (def == cnt++) {
140-
sym->def[S_DEF_USER].tri = yes;
141-
csym->def[S_DEF_USER].val = sym;
142-
} else {
143-
sym->def[S_DEF_USER].tri = no;
133+
menu_for_each_sub_entry(menu, choice) {
134+
struct symbol *sym = menu->sym;
135+
136+
if (sym && !sym_has_value(sym))
137+
cnt++;
138+
}
139+
140+
while (cnt > 0) {
141+
x = rand() % cnt;
142+
143+
menu_for_each_sub_entry(menu, choice) {
144+
struct symbol *sym = menu->sym;
145+
146+
if (sym && !sym_has_value(sym))
147+
x--;
148+
149+
if (x < 0) {
150+
sym->def[S_DEF_USER].tri = yes;
151+
sym->flags |= SYMBOL_DEF_USER;
152+
/*
153+
* Move the selected item to the _tail_ because
154+
* this needs to have a lower priority than the
155+
* user input from KCONFIG_ALLCONFIG.
156+
*/
157+
list_move_tail(&sym->choice_link,
158+
&choice->choice_members);
159+
160+
break;
161+
}
144162
}
145-
sym->flags |= SYMBOL_DEF_USER;
146-
/* clear VALID to get value calculated */
147-
sym->flags &= ~SYMBOL_VALID;
163+
cnt--;
148164
}
149-
csym->flags |= SYMBOL_DEF_USER;
150-
/* clear VALID to get value calculated */
151-
csym->flags &= ~SYMBOL_VALID;
152165
}
153166

154167
enum conf_def_mode {
@@ -159,9 +172,9 @@ enum conf_def_mode {
159172
def_random
160173
};
161174

162-
static bool conf_set_all_new_symbols(enum conf_def_mode mode)
175+
static void conf_set_all_new_symbols(enum conf_def_mode mode)
163176
{
164-
struct symbol *sym, *csym;
177+
struct menu *menu;
165178
int cnt;
166179
/*
167180
* 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)
170183
int pby = 50; /* probability of bool = y */
171184
int pty = 33; /* probability of tristate = y */
172185
int ptm = 33; /* probability of tristate = m */
173-
bool has_changed = false;
174186

175187
if (mode == def_random) {
176188
int n, p[3];
@@ -217,14 +229,21 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)
217229
}
218230
}
219231

220-
for_all_symbols(sym) {
232+
menu_for_each_entry(menu) {
233+
struct symbol *sym = menu->sym;
221234
tristate val;
222235

223-
if (sym_has_value(sym) || sym->flags & SYMBOL_VALID ||
224-
(sym->type != S_BOOLEAN && sym->type != S_TRISTATE))
236+
if (!sym || !menu->prompt || sym_has_value(sym) ||
237+
(sym->type != S_BOOLEAN && sym->type != S_TRISTATE) ||
238+
sym_is_choice_value(sym))
225239
continue;
226240

227-
has_changed = true;
241+
if (sym_is_choice(sym)) {
242+
if (mode == def_random)
243+
randomize_choice_values(menu);
244+
continue;
245+
}
246+
228247
switch (mode) {
229248
case def_yes:
230249
val = yes;
@@ -251,34 +270,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)
251270
continue;
252271
}
253272
sym->def[S_DEF_USER].tri = val;
254-
255-
if (!(sym_is_choice(sym) && mode == def_random))
256-
sym->flags |= SYMBOL_DEF_USER;
273+
sym->flags |= SYMBOL_DEF_USER;
257274
}
258275

259276
sym_clear_all_valid();
260-
261-
if (mode != def_random) {
262-
for_all_symbols(csym) {
263-
if ((sym_is_choice(csym) && !sym_has_value(csym)) ||
264-
sym_is_choice_value(csym))
265-
csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES;
266-
}
267-
}
268-
269-
for_all_symbols(csym) {
270-
if (sym_has_value(csym) || !sym_is_choice(csym))
271-
continue;
272-
273-
sym_calc_value(csym);
274-
if (mode == def_random)
275-
randomize_choice_values(csym);
276-
else
277-
set_all_choice_values(csym);
278-
has_changed = true;
279-
}
280-
281-
return has_changed;
282277
}
283278

284279
static void conf_rewrite_tristates(tristate old_val, tristate new_val)
@@ -429,10 +424,9 @@ static void conf_choice(struct menu *menu)
429424
{
430425
struct symbol *sym, *def_sym;
431426
struct menu *child;
432-
bool is_new;
427+
bool is_new = false;
433428

434429
sym = menu->sym;
435-
is_new = !sym_has_value(sym);
436430

437431
while (1) {
438432
int cnt, def;
@@ -456,8 +450,10 @@ static void conf_choice(struct menu *menu)
456450
printf("%*c", indent, ' ');
457451
printf(" %d. %s (%s)", cnt, menu_get_prompt(child),
458452
child->sym->name);
459-
if (!sym_has_value(child->sym))
453+
if (!sym_has_value(child->sym)) {
454+
is_new = true;
460455
printf(" (NEW)");
456+
}
461457
printf("\n");
462458
}
463459
printf("%*schoice", indent - 1, "");
@@ -586,9 +582,7 @@ static void check_conf(struct menu *menu)
586582
return;
587583

588584
sym = menu->sym;
589-
if (sym && !sym_has_value(sym) &&
590-
(sym_is_changeable(sym) || sym_is_choice(sym))) {
591-
585+
if (sym && !sym_has_value(sym) && sym_is_changeable(sym)) {
592586
switch (input_mode) {
593587
case listnewconfig:
594588
if (sym->name)
@@ -804,8 +798,7 @@ int main(int ac, char **av)
804798
conf_set_all_new_symbols(def_default);
805799
break;
806800
case randconfig:
807-
/* Really nothing to do in this loop */
808-
while (conf_set_all_new_symbols(def_random)) ;
801+
conf_set_all_new_symbols(def_random);
809802
break;
810803
case defconfig:
811804
conf_set_all_new_symbols(def_default);

scripts/kconfig/confdata.c

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,7 @@ int conf_read_simple(const char *name, int def)
382382

383383
def_flags = SYMBOL_DEF << def;
384384
for_all_symbols(sym) {
385-
sym->flags |= SYMBOL_CHANGED;
386385
sym->flags &= ~(def_flags|SYMBOL_VALID);
387-
if (sym_is_choice(sym))
388-
sym->flags |= def_flags;
389386
switch (sym->type) {
390387
case S_INT:
391388
case S_HEX:
@@ -399,6 +396,8 @@ int conf_read_simple(const char *name, int def)
399396
}
400397

401398
while (getline_stripped(&line, &line_asize, in) != -1) {
399+
struct menu *choice;
400+
402401
conf_lineno++;
403402

404403
if (!line[0]) /* blank line */
@@ -460,15 +459,14 @@ int conf_read_simple(const char *name, int def)
460459
if (conf_set_sym_val(sym, def, def_flags, val))
461460
continue;
462461

463-
if (sym && sym_is_choice_value(sym)) {
464-
struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
465-
if (sym->def[def].tri == yes) {
466-
if (cs->def[def].tri != no)
467-
conf_warning("override: %s changes choice state", sym->name);
468-
cs->def[def].val = sym;
469-
cs->def[def].tri = yes;
470-
}
471-
}
462+
/*
463+
* If this is a choice member, give it the highest priority.
464+
* If conflicting CONFIG options are given from an input file,
465+
* the last one wins.
466+
*/
467+
choice = sym_get_choice_menu(sym);
468+
if (choice)
469+
list_move(&sym->choice_link, &choice->choice_members);
472470
}
473471
free(line);
474472
fclose(in);
@@ -514,18 +512,6 @@ int conf_read(const char *name)
514512
/* maybe print value in verbose mode... */
515513
}
516514

517-
for_all_symbols(sym) {
518-
if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
519-
/* Reset values of generates values, so they'll appear
520-
* as new, if they should become visible, but that
521-
* doesn't quite work if the Kconfig and the saved
522-
* configuration disagree.
523-
*/
524-
if (sym->visible == no && !conf_unsaved)
525-
sym->flags &= ~SYMBOL_DEF_USER;
526-
}
527-
}
528-
529515
if (conf_warnings || conf_unsaved)
530516
conf_set_changed(true);
531517

@@ -1146,23 +1132,3 @@ void conf_set_changed_callback(void (*fn)(bool))
11461132
{
11471133
conf_changed_callback = fn;
11481134
}
1149-
1150-
void set_all_choice_values(struct symbol *csym)
1151-
{
1152-
struct property *prop;
1153-
struct symbol *sym;
1154-
struct expr *e;
1155-
1156-
prop = sym_get_choice_prop(csym);
1157-
1158-
/*
1159-
* Set all non-assinged choice values to no
1160-
*/
1161-
expr_list_for_each_sym(prop->expr, e, sym) {
1162-
if (!sym_has_value(sym))
1163-
sym->def[S_DEF_USER].tri = no;
1164-
}
1165-
csym->flags |= SYMBOL_DEF_USER;
1166-
/* clear VALID to get value calculated */
1167-
csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES);
1168-
}

scripts/kconfig/expr.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ enum {
7373
* Represents a configuration symbol.
7474
*
7575
* Choices are represented as a special kind of symbol with null name.
76+
*
77+
* @choice_link: linked to menu::choice_members
7678
*/
7779
struct symbol {
7880
/* link node for the hash table */
@@ -110,6 +112,8 @@ struct symbol {
110112
/* config entries associated with this symbol */
111113
struct list_head menus;
112114

115+
struct list_head choice_link;
116+
113117
/* SYMBOL_* flags */
114118
int flags;
115119

@@ -133,7 +137,6 @@ struct symbol {
133137
#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */
134138
#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
135139
#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
136-
#define SYMBOL_CHANGED 0x0400 /* ? */
137140
#define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */
138141
#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
139142
#define SYMBOL_WARNED 0x8000 /* warning has been issued */
@@ -145,9 +148,6 @@ struct symbol {
145148
#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */
146149
#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */
147150

148-
/* choice values need to be set before calculating this symbol value */
149-
#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000
150-
151151
#define SYMBOL_MAXLENGTH 256
152152

153153
/* A property represent the config options that can be associated
@@ -204,6 +204,8 @@ struct property {
204204
* for all front ends). Each symbol, menu, etc. defined in the Kconfig files
205205
* gets a node. A symbol defined in multiple locations gets one node at each
206206
* location.
207+
*
208+
* @choice_members: list of choice members with priority.
207209
*/
208210
struct menu {
209211
/* The next menu node at the same level */
@@ -223,6 +225,8 @@ struct menu {
223225

224226
struct list_head link; /* link to symbol::menus */
225227

228+
struct list_head choice_members;
229+
226230
/*
227231
* The prompt associated with the node. This holds the prompt for a
228232
* symbol as well as the text for a menu or comment, along with the

scripts/kconfig/lkc.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ void zconf_nextfile(const char *name);
4040
/* confdata.c */
4141
extern struct gstr autoconf_cmd;
4242
const char *conf_get_configname(void);
43-
void set_all_choice_values(struct symbol *csym);
4443

4544
/* confdata.c and expr.c */
4645
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 symbol *sym)
121120
return sym->curr.tri;
122121
}
123122

124-
125-
static inline struct symbol *sym_get_choice_value(struct symbol *sym)
126-
{
127-
return (struct symbol *)sym->curr.val;
128-
}
123+
struct symbol *sym_get_choice_value(struct symbol *sym);
129124

130125
static inline bool sym_is_choice(struct symbol *sym)
131126
{

0 commit comments

Comments
 (0)