Pull Kconfig updates from Masahiro Yamada: - improve checkpatch for more precise Kconfig code checking - clarify effective selects by grouping reverse dependencies in help - do not write out '# CONFIG_FOO is not set' from invisible symbols - make oldconfig as silent as it should be - rename 'silentoldconfig' to 'syncconfig' - add unit-test framework and several test cases - warn unmet dependency of tristate symbols - make unmet dependency warnings readable, removing false positives - improve recursive include detection - use yylineno to simplify the line number tracking - misc cleanups * tag 'kconfig-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild: (30 commits) kconfig: use yylineno option instead of manual lineno increments kconfig: detect recursive inclusion earlier kconfig: remove duplicated file name and lineno of recursive inclusion kconfig: do not include both curses.h and ncurses.h for nconfig kconfig: make unmet dependency warnings readable kconfig: warn unmet direct dependency of tristate symbols selected by y kconfig: tests: test if recursive inclusion is detected kconfig: tests: test if recursive dependencies are detected kconfig: tests: test randconfig for choice in choice kconfig: tests: test defconfig when two choices interact kconfig: tests: check visibility of tristate choice values in y choice kconfig: tests: check unneeded "is not set" with unmet dependency kconfig: tests: test if new symbols in choice are asked kconfig: tests: test automatic submenu creation kconfig: tests: add basic choice tests kconfig: tests: add framework for Kconfig unit testing kbuild: add PYTHON2 and PYTHON3 variables kconfig: remove redundant streamline_config.pl prerequisite kconfig: rename silentoldconfig to syncconfig kconfig: invoke oldconfig instead of silentoldconfig from local*config ...
Showing the changes to scripts (55 of 58 files). Show all
Unified · Split
| @@ -2797,7 +2797,10 @@ sub process { · 2796 unmodified lines | ||
| 2797 | 2797 | # Only applies when adding the entry originally, after that we do not have |
| 2798 | 2798 | # sufficient context to determine whether it is indeed long enough. |
| 2799 | 2799 | if ($realfile =~ /Kconfig/ && |
| 2800 | − $line =~ /^\+\s*config\s+/) { | |
| 2800 | + # 'choice' is usually the last thing on the line (though | |
| 2801 | + # Kconfig supports named choices), so use a word boundary | |
| 2802 | + # (\b) rather than a whitespace character (\s) | |
| 2803 | + $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { | |
| 2801 | 2804 | my $length = 0; |
| 2802 | 2805 | my $cnt = $realcnt; |
| 2803 | 2806 | my $ln = $linenr + 1; |
| @@ -2812,9 +2815,13 @@ sub process { · 8 unmodified lines | ||
| 2812 | 2815 | next if ($f =~ /^-/); |
| 2813 | 2816 | last if (!$file && $f =~ /^\@\@/); |
| 2814 | 2817 | |
| 2815 | − if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { | |
| 2818 | + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { | |
| 2816 | 2819 | $is_start = 1; |
| 2817 | − } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { | |
| 2820 | + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) { | |
| 2821 | + if ($lines[$ln - 1] =~ "---help---") { | |
| 2822 | + WARN("CONFIG_DESCRIPTION", | |
| 2823 | + "prefer 'help' over '---help---' for new help texts\n" . $herecurr); | |
| 2824 | + } | |
| 2818 | 2825 | $length = -1; |
| 2819 | 2826 | } |
| 2820 | 2827 | |
| @@ -2822,7 +2829,13 @@ sub process { · 1 unmodified line | ||
| 2822 | 2829 | $f =~ s/#.*//; |
| 2823 | 2830 | $f =~ s/^\s+//; |
| 2824 | 2831 | next if ($f =~ /^$/); |
| 2825 | − if ($f =~ /^\s*config\s/) { | |
| 2832 | + | |
| 2833 | + # This only checks context lines in the patch | |
| 2834 | + # and so hopefully shouldn't trigger false | |
| 2835 | + # positives, even though some of these are | |
| 2836 | + # common words in help texts | |
| 2837 | + if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| | |
| 2838 | + if|endif|menu|endmenu|source)\b/x) { | |
| 2826 | 2839 | $is_end = 1; |
| 2827 | 2840 | last; |
| 2828 | 2841 | } |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | # Kernel configuration targets |
| 4 | 4 | # These targets are used from top-level makefile |
| 5 | 5 | |
| 6 | −PHONY += xconfig gconfig menuconfig config silentoldconfig update-po-config \ | |
| 6 | +PHONY += xconfig gconfig menuconfig config syncconfig update-po-config \ | |
| 7 | 7 | localmodconfig localyesconfig |
| 8 | 8 | |
| 9 | 9 | ifdef KBUILD_KCONFIG |
| @@ -36,22 +36,22 @@ nconfig: $(obj)/nconf · 26 unmodified lines | ||
| 36 | 36 | |
| 37 | 37 | # This has become an internal implementation detail and is now deprecated |
| 38 | 38 | # for external use. |
| 39 | −silentoldconfig: $(obj)/conf | |
| 39 | +syncconfig: $(obj)/conf | |
| 40 | 40 | $(Q)mkdir -p include/config include/generated |
| 41 | 41 | $< $(silent) --$@ $(Kconfig) |
| 42 | 42 | |
| 43 | −localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf | |
| 43 | +localyesconfig localmodconfig: $(obj)/conf | |
| 44 | 44 | $(Q)mkdir -p include/config include/generated |
| 45 | − $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config | |
| 45 | + $(Q)perl $(srctree)/$(src)/streamline_config.pl --$@ $(srctree) $(Kconfig) > .tmp.config | |
| 46 | 46 | $(Q)if [ -f .config ]; then \ |
| 47 | 47 | cmp -s .tmp.config .config || \ |
| 48 | 48 | (mv -f .config .config.old.1; \ |
| 49 | 49 | mv -f .tmp.config .config; \ |
| 50 | − $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ | |
| 50 | + $< $(silent) --oldconfig $(Kconfig); \ | |
| 51 | 51 | mv -f .config.old.1 .config.old) \ |
| 52 | 52 | else \ |
| 53 | 53 | mv -f .tmp.config .config; \ |
| 54 | − $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ | |
| 54 | + $< $(silent) --oldconfig $(Kconfig); \ | |
| 55 | 55 | fi |
| 56 | 56 | $(Q)rm -f .tmp.config |
| 57 | 57 | |
| @@ -86,7 +86,7 @@ PHONY += $(simple-targets) · 28 unmodified lines | ||
| 86 | 86 | $(simple-targets): $(obj)/conf |
| 87 | 87 | $< $(silent) --$@ $(Kconfig) |
| 88 | 88 | |
| 89 | −PHONY += oldnoconfig savedefconfig defconfig | |
| 89 | +PHONY += oldnoconfig silentoldconfig savedefconfig defconfig | |
| 90 | 90 | |
| 91 | 91 | # oldnoconfig is an alias of olddefconfig, because people already are dependent |
| 92 | 92 | # on its behavior (sets new symbols to their default value but not 'n') with the |
| @@ -95,6 +95,13 @@ oldnoconfig: olddefconfig · 2 unmodified lines | ||
| 95 | 95 | @echo " WARNING: \"oldnoconfig\" target will be removed after Linux 4.19" |
| 96 | 96 | @echo " Please use \"olddefconfig\" instead, which is an alias." |
| 97 | 97 | |
| 98 | +# We do not expect manual invokcation of "silentoldcofig" (or "syncconfig"). | |
| 99 | +silentoldconfig: syncconfig | |
| 100 | + @echo " WARNING: \"silentoldconfig\" has been renamed to \"syncconfig\"" | |
| 101 | + @echo " and is now an internal implementation detail." | |
| 102 | + @echo " What you want is probably \"oldconfig\"." | |
| 103 | + @echo " \"silentoldconfig\" will be removed after Linux 4.19" | |
| 104 | + | |
| 98 | 105 | savedefconfig: $(obj)/conf |
| 99 | 106 | $< $(silent) --$@=defconfig $(Kconfig) |
| 100 | 107 | |
| @@ -133,6 +140,14 @@ PHONY += tinyconfig · 32 unmodified lines | ||
| 133 | 140 | tinyconfig: |
| 134 | 141 | $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config |
| 135 | 142 | |
| 143 | +# CHECK: -o cache_dir=<path> working? | |
| 144 | +PHONY += testconfig | |
| 145 | +testconfig: $(obj)/conf | |
| 146 | + $(PYTHON3) -B -m pytest $(srctree)/$(src)/tests \ | |
| 147 | + -o cache_dir=$(abspath $(obj)/tests/.cache) \ | |
| 148 | + $(if $(findstring 1,$(KBUILD_VERBOSE)),--capture=no) | |
| 149 | +clean-dirs += tests/.cache | |
| 150 | + | |
| 136 | 151 | # Help text used by make help |
| 137 | 152 | help: |
| 138 | 153 | @echo ' config - Update current config utilising a line-oriented program' |
| @@ -23,7 +23,7 @@ static void check_conf(struct menu *menu); · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | enum input_mode { |
| 25 | 25 | oldaskconfig, |
| 26 | − silentoldconfig, | |
| 26 | + syncconfig, | |
| 27 | 27 | oldconfig, |
| 28 | 28 | allnoconfig, |
| 29 | 29 | allyesconfig, |
| @@ -100,7 +100,7 @@ static int conf_askvalue(struct symbol *sym, const char *def) · 70 unmodified lines | ||
| 100 | 100 | |
| 101 | 101 | switch (input_mode) { |
| 102 | 102 | case oldconfig: |
| 103 | − case silentoldconfig: | |
| 103 | + case syncconfig: | |
| 104 | 104 | if (sym_has_value(sym)) { |
| 105 | 105 | printf("%s\n", def); |
| 106 | 106 | return 0; |
| @@ -293,7 +293,7 @@ static int conf_choice(struct menu *menu) · 186 unmodified lines | ||
| 293 | 293 | printf("[1-%d?]: ", cnt); |
| 294 | 294 | switch (input_mode) { |
| 295 | 295 | case oldconfig: |
| 296 | − case silentoldconfig: | |
| 296 | + case syncconfig: | |
| 297 | 297 | if (!is_new) { |
| 298 | 298 | cnt = def; |
| 299 | 299 | printf("%d\n", cnt); |
| @@ -358,10 +358,11 @@ static void conf(struct menu *menu) · 58 unmodified lines | ||
| 358 | 358 | |
| 359 | 359 | switch (prop->type) { |
| 360 | 360 | case P_MENU: |
| 361 | − if ((input_mode == silentoldconfig || | |
| 362 | − input_mode == listnewconfig || | |
| 363 | − input_mode == olddefconfig) && | |
| 364 | − rootEntry != menu) { | |
| 361 | + /* | |
| 362 | + * Except in oldaskconfig mode, we show only menus that | |
| 363 | + * contain new symbols. | |
| 364 | + */ | |
| 365 | + if (input_mode != oldaskconfig && rootEntry != menu) { | |
| 365 | 366 | check_conf(menu); |
| 366 | 367 | return; |
| 367 | 368 | } |
| @@ -424,7 +425,7 @@ static void check_conf(struct menu *menu) · 56 unmodified lines | ||
| 424 | 425 | if (sym->name && !sym_is_choice_value(sym)) { |
| 425 | 426 | printf("%s%s\n", CONFIG_, sym->name); |
| 426 | 427 | } |
| 427 | − } else if (input_mode != olddefconfig) { | |
| 428 | + } else { | |
| 428 | 429 | if (!conf_cnt++) |
| 429 | 430 | printf(_("*\n* Restart config...\n*\n")); |
| 430 | 431 | rootEntry = menu_get_parent_menu(menu); |
| @@ -440,7 +441,7 @@ static void check_conf(struct menu *menu) · 9 unmodified lines | ||
| 440 | 441 | static struct option long_opts[] = { |
| 441 | 442 | {"oldaskconfig", no_argument, NULL, oldaskconfig}, |
| 442 | 443 | {"oldconfig", no_argument, NULL, oldconfig}, |
| 443 | − {"silentoldconfig", no_argument, NULL, silentoldconfig}, | |
| 444 | + {"syncconfig", no_argument, NULL, syncconfig}, | |
| 444 | 445 | {"defconfig", optional_argument, NULL, defconfig}, |
| 445 | 446 | {"savedefconfig", required_argument, NULL, savedefconfig}, |
| 446 | 447 | {"allnoconfig", no_argument, NULL, allnoconfig}, |
| @@ -467,8 +468,8 @@ static void conf_usage(const char *progname) · 20 unmodified lines | ||
| 467 | 468 | printf(" --listnewconfig List new options\n"); |
| 468 | 469 | printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); |
| 469 | 470 | printf(" --oldconfig Update a configuration using a provided .config as base\n"); |
| 470 | − printf(" --silentoldconfig Similar to oldconfig but generates configuration in\n" | |
| 471 | − " include/{generated/,config/} (oldconfig used to be more verbose)\n"); | |
| 471 | + printf(" --syncconfig Similar to oldconfig but generates configuration in\n" | |
| 472 | + " include/{generated/,config/}\n"); | |
| 472 | 473 | printf(" --olddefconfig Same as oldconfig but sets new symbols to their default value\n"); |
| 473 | 474 | printf(" --oldnoconfig An alias of olddefconfig\n"); |
| 474 | 475 | printf(" --defconfig <file> New config with default defined in <file>\n"); |
| @@ -500,7 +501,7 @@ int main(int ac, char **av) · 25 unmodified lines | ||
| 500 | 501 | } |
| 501 | 502 | input_mode = (enum input_mode)opt; |
| 502 | 503 | switch (opt) { |
| 503 | − case silentoldconfig: | |
| 504 | + case syncconfig: | |
| 504 | 505 | sync_kconfig = 1; |
| 505 | 506 | break; |
| 506 | 507 | case defconfig: |
| @@ -582,7 +583,7 @@ int main(int ac, char **av) · 75 unmodified lines | ||
| 582 | 583 | } |
| 583 | 584 | break; |
| 584 | 585 | case savedefconfig: |
| 585 | − case silentoldconfig: | |
| 586 | + case syncconfig: | |
| 586 | 587 | case oldaskconfig: |
| 587 | 588 | case oldconfig: |
| 588 | 589 | case listnewconfig: |
| @@ -662,24 +663,24 @@ int main(int ac, char **av) · 73 unmodified lines | ||
| 662 | 663 | case oldaskconfig: |
| 663 | 664 | rootEntry = &rootmenu; |
| 664 | 665 | conf(&rootmenu); |
| 665 | − input_mode = silentoldconfig; | |
| 666 | + input_mode = oldconfig; | |
| 666 | 667 | /* fall through */ |
| 667 | 668 | case oldconfig: |
| 668 | 669 | case listnewconfig: |
| 669 | − case olddefconfig: | |
| 670 | − case silentoldconfig: | |
| 670 | + case syncconfig: | |
| 671 | 671 | /* Update until a loop caused no more changes */ |
| 672 | 672 | do { |
| 673 | 673 | conf_cnt = 0; |
| 674 | 674 | check_conf(&rootmenu); |
| 675 | − } while (conf_cnt && | |
| 676 | − (input_mode != listnewconfig && | |
| 677 | − input_mode != olddefconfig)); | |
| 675 | + } while (conf_cnt); | |
| 676 | + break; | |
| 677 | + case olddefconfig: | |
| 678 | + default: | |
| 678 | 679 | break; |
| 679 | 680 | } |
| 680 | 681 | |
| 681 | 682 | if (sync_kconfig) { |
| 682 | − /* silentoldconfig is used during the build so we shall update autoconf. | |
| 683 | + /* syncconfig is used during the build so we shall update autoconf. | |
| 683 | 684 | * All other commands are only used to generate a config. |
| 684 | 685 | */ |
| 685 | 686 | if (conf_get_changed() && conf_write(NULL)) { |
| @@ -1137,49 +1137,9 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) · 1136 unmodified lines | ||
| 1137 | 1137 | return 0; |
| 1138 | 1138 | } |
| 1139 | 1139 | |
| 1140 | −static inline struct expr * | |
| 1141 | −expr_get_leftmost_symbol(const struct expr *e) | |
| 1142 | −{ | |
| 1143 | − | |
| 1144 | − if (e == NULL) | |
| 1145 | − return NULL; | |
| 1146 | − | |
| 1147 | − while (e->type != E_SYMBOL) | |
| 1148 | − e = e->left.expr; | |
| 1149 | − | |
| 1150 | − return expr_copy(e); | |
| 1151 | −} | |
| 1152 | − | |
| 1153 | −/* | |
| 1154 | − * Given expression `e1' and `e2', returns the leaf of the longest | |
| 1155 | − * sub-expression of `e1' not containing 'e2. | |
| 1156 | − */ | |
| 1157 | −struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2) | |
| 1158 | −{ | |
| 1159 | − struct expr *ret; | |
| 1160 | − | |
| 1161 | − switch (e1->type) { | |
| 1162 | − case E_OR: | |
| 1163 | − return expr_alloc_and( | |
| 1164 | − expr_simplify_unmet_dep(e1->left.expr, e2), | |
| 1165 | − expr_simplify_unmet_dep(e1->right.expr, e2)); | |
| 1166 | − case E_AND: { | |
| 1167 | − struct expr *e; | |
| 1168 | − e = expr_alloc_and(expr_copy(e1), expr_copy(e2)); | |
| 1169 | − e = expr_eliminate_dups(e); | |
| 1170 | − ret = (!expr_eq(e, e1)) ? e1 : NULL; | |
| 1171 | − expr_free(e); | |
| 1172 | − break; | |
| 1173 | − } | |
| 1174 | − default: | |
| 1175 | − ret = e1; | |
| 1176 | − break; | |
| 1177 | − } | |
| 1178 | − | |
| 1179 | − return expr_get_leftmost_symbol(ret); | |
| 1180 | −} | |
| 1181 | − | |
| 1182 | −static void __expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken, bool revdep) | |
| 1140 | +void expr_print(struct expr *e, | |
| 1141 | + void (*fn)(void *, struct symbol *, const char *), | |
| 1142 | + void *data, int prevtoken) | |
| 1183 | 1143 | { |
| 1184 | 1144 | if (!e) { |
| 1185 | 1145 | fn(data, NULL, "y"); |
| @@ -1234,14 +1194,9 @@ static void __expr_print(struct expr *e, void (*fn)(void *, struct symbol *, con · 48 unmodified lines | ||
| 1234 | 1194 | fn(data, e->right.sym, e->right.sym->name); |
| 1235 | 1195 | break; |
| 1236 | 1196 | case E_OR: |
| 1237 | − if (revdep && e->left.expr->type != E_OR) | |
| 1238 | − fn(data, NULL, "\n - "); | |
| 1239 | − __expr_print(e->left.expr, fn, data, E_OR, revdep); | |
| 1240 | − if (revdep) | |
| 1241 | − fn(data, NULL, "\n - "); | |
| 1242 | − else | |
| 1243 | − fn(data, NULL, " || "); | |
| 1244 | − __expr_print(e->right.expr, fn, data, E_OR, revdep); | |
| 1197 | + expr_print(e->left.expr, fn, data, E_OR); | |
| 1198 | + fn(data, NULL, " || "); | |
| 1199 | + expr_print(e->right.expr, fn, data, E_OR); | |
| 1245 | 1200 | break; |
| 1246 | 1201 | case E_AND: |
| 1247 | 1202 | expr_print(e->left.expr, fn, data, E_AND); |
| @@ -1274,11 +1229,6 @@ static void __expr_print(struct expr *e, void (*fn)(void *, struct symbol *, con · 26 unmodified lines | ||
| 1274 | 1229 | fn(data, NULL, ")"); |
| 1275 | 1230 | } |
| 1276 | 1231 | |
| 1277 | −void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) | |
| 1278 | −{ | |
| 1279 | − __expr_print(e, fn, data, prevtoken, false); | |
| 1280 | −} | |
| 1281 | − | |
| 1282 | 1232 | static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) |
| 1283 | 1233 | { |
| 1284 | 1234 | xfwrite(str, strlen(str), 1, data); |
| @@ -1329,7 +1279,27 @@ void expr_gstr_print(struct expr *e, struct gstr *gs) · 44 unmodified lines | ||
| 1329 | 1279 | * line with a minus. This makes expressions much easier to read. |
| 1330 | 1280 | * Suitable for reverse dependency expressions. |
| 1331 | 1281 | */ |
| 1332 | −void expr_gstr_print_revdep(struct expr *e, struct gstr *gs) | |
| 1282 | +static void expr_print_revdep(struct expr *e, | |
| 1283 | + void (*fn)(void *, struct symbol *, const char *), | |
| 1284 | + void *data, tristate pr_type, const char **title) | |
| 1285 | +{ | |
| 1286 | + if (e->type == E_OR) { | |
| 1287 | + expr_print_revdep(e->left.expr, fn, data, pr_type, title); | |
| 1288 | + expr_print_revdep(e->right.expr, fn, data, pr_type, title); | |
| 1289 | + } else if (expr_calc_value(e) == pr_type) { | |
| 1290 | + if (*title) { | |
| 1291 | + fn(data, NULL, *title); | |
| 1292 | + *title = NULL; | |
| 1293 | + } | |
| 1294 | + | |
| 1295 | + fn(data, NULL, " - "); | |
| 1296 | + expr_print(e, fn, data, E_NONE); | |
| 1297 | + fn(data, NULL, "\n"); | |
| 1298 | + } | |
| 1299 | +} | |
| 1300 | + | |
| 1301 | +void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, | |
| 1302 | + tristate pr_type, const char *title) | |
| 1333 | 1303 | { |
| 1334 | − __expr_print(e, expr_print_gstr_helper, gs, E_NONE, true); | |
| 1304 | + expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title); | |
| 1335 | 1305 | } |
| @@ -305,12 +305,12 @@ struct expr *expr_transform(struct expr *e); · 304 unmodified lines | ||
| 305 | 305 | int expr_contains_symbol(struct expr *dep, struct symbol *sym); |
| 306 | 306 | bool expr_depends_symbol(struct expr *dep, struct symbol *sym); |
| 307 | 307 | struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); |
| 308 | −struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2); | |
| 309 | 308 | |
| 310 | 309 | void expr_fprint(struct expr *e, FILE *out); |
| 311 | 310 | struct gstr; /* forward */ |
| 312 | 311 | void expr_gstr_print(struct expr *e, struct gstr *gs); |
| 313 | −void expr_gstr_print_revdep(struct expr *e, struct gstr *gs); | |
| 312 | +void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, | |
| 313 | + tristate pr_type, const char *title); | |
| 314 | 314 | |
| 315 | 315 | static inline int expr_is_yes(struct expr *e) |
| 316 | 316 | { |
| @@ -68,6 +68,7 @@ struct kconf_id { · 67 unmodified lines | ||
| 68 | 68 | enum symbol_type stype; |
| 69 | 69 | }; |
| 70 | 70 | |
| 71 | +extern int yylineno; | |
| 71 | 72 | void zconfdump(FILE *out); |
| 72 | 73 | void zconf_starthelp(void); |
| 73 | 74 | FILE *zconf_fopen(const char *name); |
| @@ -828,16 +828,16 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym, · 827 unmodified lines | ||
| 828 | 828 | |
| 829 | 829 | get_symbol_props_str(r, sym, P_SELECT, _(" Selects: ")); |
| 830 | 830 | if (sym->rev_dep.expr) { |
| 831 | − str_append(r, _(" Selected by: ")); | |
| 832 | − expr_gstr_print_revdep(sym->rev_dep.expr, r); | |
| 833 | − str_append(r, "\n"); | |
| 831 | + expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, " Selected by [y]:\n"); | |
| 832 | + expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, " Selected by [m]:\n"); | |
| 833 | + expr_gstr_print_revdep(sym->rev_dep.expr, r, no, " Selected by [n]:\n"); | |
| 834 | 834 | } |
| 835 | 835 | |
| 836 | 836 | get_symbol_props_str(r, sym, P_IMPLY, _(" Implies: ")); |
| 837 | 837 | if (sym->implied.expr) { |
| 838 | − str_append(r, _(" Implied by: ")); | |
| 839 | − expr_gstr_print_revdep(sym->implied.expr, r); | |
| 840 | − str_append(r, "\n"); | |
| 838 | + expr_gstr_print_revdep(sym->implied.expr, r, yes, " Implied by [y]:\n"); | |
| 839 | + expr_gstr_print_revdep(sym->implied.expr, r, mod, " Implied by [m]:\n"); | |
| 840 | + expr_gstr_print_revdep(sym->implied.expr, r, no, " Implied by [n]:\n"); | |
| 841 | 841 | } |
| 842 | 842 | |
| 843 | 843 | str_append(r, "\n\n"); |
| @@ -15,7 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <string.h> |
| 16 | 16 | #include <unistd.h> |
| 17 | 17 | #include <locale.h> |
| 18 | −#include <curses.h> | |
| 18 | +#include <ncurses.h> | |
| 19 | 19 | #include <menu.h> |
| 20 | 20 | #include <panel.h> |
| 21 | 21 | #include <form.h> |
| @@ -24,8 +24,6 @@ · 2 unmodified lines | ||
| 24 | 24 | #include <time.h> |
| 25 | 25 | #include <sys/time.h> |
| 26 | 26 | |
| 27 | −#include "ncurses.h" | |
| 28 | − | |
| 29 | 27 | #define max(a, b) ({\ |
| 30 | 28 | typeof(a) _a = a;\ |
| 31 | 29 | typeof(b) _b = b;\ |
| @@ -243,7 +243,7 @@ static void sym_calc_visibility(struct symbol *sym) · 242 unmodified lines | ||
| 243 | 243 | tri = yes; |
| 244 | 244 | if (sym->dir_dep.expr) |
| 245 | 245 | tri = expr_calc_value(sym->dir_dep.expr); |
| 246 | − if (tri == mod) | |
| 246 | + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) | |
| 247 | 247 | tri = yes; |
| 248 | 248 | if (sym->dir_dep.tri != tri) { |
| 249 | 249 | sym->dir_dep.tri = tri; |
| @@ -333,6 +333,27 @@ static struct symbol *sym_calc_choice(struct symbol *sym) · 83 unmodified lines | ||
| 333 | 333 | return def_sym; |
| 334 | 334 | } |
| 335 | 335 | |
| 336 | +static void sym_warn_unmet_dep(struct symbol *sym) | |
| 337 | +{ | |
| 338 | + struct gstr gs = str_new(); | |
| 339 | + | |
| 340 | + str_printf(&gs, | |
| 341 | + "\nWARNING: unmet direct dependencies detected for %s\n", | |
| 342 | + sym->name); | |
| 343 | + str_printf(&gs, | |
| 344 | + " Depends on [%c]: ", | |
| 345 | + sym->dir_dep.tri == mod ? 'm' : 'n'); | |
| 346 | + expr_gstr_print(sym->dir_dep.expr, &gs); | |
| 347 | + str_printf(&gs, "\n"); | |
| 348 | + | |
| 349 | + expr_gstr_print_revdep(sym->rev_dep.expr, &gs, yes, | |
| 350 | + " Selected by [y]:\n"); | |
| 351 | + expr_gstr_print_revdep(sym->rev_dep.expr, &gs, mod, | |
| 352 | + " Selected by [m]:\n"); | |
| 353 | + | |
| 354 | + fputs(str_get(&gs), stderr); | |
| 355 | +} | |
| 356 | + | |
| 336 | 357 | void sym_calc_value(struct symbol *sym) |
| 337 | 358 | { |
| 338 | 359 | struct symbol_value newval, oldval; |
| @@ -403,9 +424,10 @@ void sym_calc_value(struct symbol *sym) · 64 unmodified lines | ||
| 403 | 424 | if (!sym_is_choice(sym)) { |
| 404 | 425 | prop = sym_get_default_prop(sym); |
| 405 | 426 | if (prop) { |
| 406 | − sym->flags |= SYMBOL_WRITE; | |
| 407 | 427 | newval.tri = EXPR_AND(expr_calc_value(prop->expr), |
| 408 | 428 | prop->visible.tri); |
| 429 | + if (newval.tri != no) | |
| 430 | + sym->flags |= SYMBOL_WRITE; | |
| 409 | 431 | } |
| 410 | 432 | if (sym->implied.tri != no) { |
| 411 | 433 | sym->flags |= SYMBOL_WRITE; |
| @@ -413,18 +435,8 @@ void sym_calc_value(struct symbol *sym) · 1 unmodified line | ||
| 413 | 435 | } |
| 414 | 436 | } |
| 415 | 437 | calc_newval: |
| 416 | − if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) { | |
| 417 | − struct expr *e; | |
| 418 | − e = expr_simplify_unmet_dep(sym->rev_dep.expr, | |
| 419 | − sym->dir_dep.expr); | |
| 420 | − fprintf(stderr, "warning: ("); | |
| 421 | − expr_fprint(e, stderr); | |
| 422 | − fprintf(stderr, ") selects %s which has unmet direct dependencies (", | |
| 423 | − sym->name); | |
| 424 | − expr_fprint(sym->dir_dep.expr, stderr); | |
| 425 | − fprintf(stderr, ")\n"); | |
| 426 | − expr_free(e); | |
| 427 | − } | |
| 438 | + if (sym->dir_dep.tri < sym->rev_dep.tri) | |
| 439 | + sym_warn_unmet_dep(sym); | |
| 428 | 440 | newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); |
| 429 | 441 | } |
| 430 | 442 | if (newval.tri == mod && |
| @@ -0,0 +1,50 @@ | ||
| 1 | +config A | |
| 2 | + bool "A" | |
| 3 | + default y | |
| 4 | + | |
| 5 | +config A0 | |
| 6 | + bool "A0" | |
| 7 | + depends on A | |
| 8 | + default y | |
| 9 | + help | |
| 10 | + This depends on A, so should be a submenu of A. | |
| 11 | + | |
| 12 | +config A0_0 | |
| 13 | + bool "A1_0" | |
| 14 | + depends on A0 | |
| 15 | + help | |
| 16 | + Submenus are created recursively. | |
| 17 | + This should be a submenu of A0. | |
| 18 | + | |
| 19 | +config A1 | |
| 20 | + bool "A1" | |
| 21 | + depends on A | |
| 22 | + default y | |
| 23 | + help | |
| 24 | + This should line up with A0. | |
| 25 | + | |
| 26 | +choice | |
| 27 | + prompt "choice" | |
| 28 | + depends on A1 | |
| 29 | + help | |
| 30 | + Choice should become a submenu as well. | |
| 31 | + | |
| 32 | +config A1_0 | |
| 33 | + bool "A1_0" | |
| 34 | + | |
| 35 | +config A1_1 | |
| 36 | + bool "A1_1" | |
| 37 | + | |
| 38 | +endchoice | |
| 39 | + | |
| 40 | +config B | |
| 41 | + bool "B" | |
| 42 | + help | |
| 43 | + This is independent of A. | |
| 44 | + | |
| 45 | +config C | |
| 46 | + bool "C" | |
| 47 | + depends on A | |
| 48 | + help | |
| 49 | + This depends on A, but not a consecutive item, so can/should not | |
| 50 | + be a submenu. | |
| @@ -0,0 +1,12 @@ | ||
| 1 | +""" | |
| 2 | +Create submenu for symbols that depend on the preceding one. | |
| 3 | + | |
| 4 | +If a symbols has dependency on the preceding symbol, the menu entry | |
| 5 | +should become the submenu of the preceding one, and displayed with | |
| 6 | +deeper indentation. | |
| 7 | +""" | |
| 8 | + | |
| 9 | + | |
| 10 | +def test(conf): | |
| 11 | + assert conf.oldaskconfig() == 0 | |
| 12 | + assert conf.stdout_contains('expected_stdout') | |
| @@ -0,0 +1,10 @@ | ||
| 1 | +A (A) [Y/n/?] (NEW) | |
| 2 | + A0 (A0) [Y/n/?] (NEW) | |
| 3 | + A1_0 (A0_0) [N/y/?] (NEW) | |
| 4 | + A1 (A1) [Y/n/?] (NEW) | |
| 5 | + choice | |
| 6 | + > 1. A1_0 (A1_0) (NEW) | |
| 7 | + 2. A1_1 (A1_1) (NEW) | |
| 8 | + choice[1-2?]: | |
| 9 | +B (B) [N/y/?] (NEW) | |
| 10 | +C (C) [N/y/?] (NEW) | |
| @@ -0,0 +1,54 @@ | ||
| 1 | +config MODULES | |
| 2 | + bool "Enable loadable module support" | |
| 3 | + option modules | |
| 4 | + default y | |
| 5 | + | |
| 6 | +choice | |
| 7 | + prompt "boolean choice" | |
| 8 | + default BOOL_CHOICE1 | |
| 9 | + | |
| 10 | +config BOOL_CHOICE0 | |
| 11 | + bool "choice 0" | |
| 12 | + | |
| 13 | +config BOOL_CHOICE1 | |
| 14 | + bool "choice 1" | |
| 15 | + | |
| 16 | +endchoice | |
| 17 | + | |
| 18 | +choice | |
| 19 | + prompt "optional boolean choice" | |
| 20 | + optional | |
| 21 | + default OPT_BOOL_CHOICE1 | |
| 22 | + | |
| 23 | +config OPT_BOOL_CHOICE0 | |
| 24 | + bool "choice 0" | |
| 25 | + | |
| 26 | +config OPT_BOOL_CHOICE1 | |
| 27 | + bool "choice 1" | |
| 28 | + | |
| 29 | +endchoice | |
| 30 | + | |
| 31 | +choice | |
| 32 | + prompt "tristate choice" | |
| 33 | + default TRI_CHOICE1 | |
| 34 | + | |
| 35 | +config TRI_CHOICE0 | |
| 36 | + tristate "choice 0" | |
| 37 | + | |
| 38 | +config TRI_CHOICE1 | |
| 39 | + tristate "choice 1" | |
| 40 | + | |
| 41 | +endchoice | |
| 42 | + | |
| 43 | +choice | |
| 44 | + prompt "optional tristate choice" | |
| 45 | + optional | |
| 46 | + default OPT_TRI_CHOICE1 | |
| 47 | + | |
| 48 | +config OPT_TRI_CHOICE0 | |
| 49 | + tristate "choice 0" | |
| 50 | + | |
| 51 | +config OPT_TRI_CHOICE1 | |
| 52 | + tristate "choice 1" | |
| 53 | + | |
| 54 | +endchoice | |
| @@ -0,0 +1,40 @@ | ||
| 1 | +""" | |
| 2 | +Basic choice tests. | |
| 3 | + | |
| 4 | +The handling of 'choice' is a bit complicated part in Kconfig. | |
| 5 | + | |
| 6 | +The behavior of 'y' choice is intuitive. If choice values are tristate, | |
| 7 | +the choice can be 'm' where each value can be enabled independently. | |
| 8 | +Also, if a choice is marked as 'optional', the whole choice can be | |
| 9 | +invisible. | |
| 10 | +""" | |
| 11 | + | |
| 12 | + | |
| 13 | +def test_oldask0(conf): | |
| 14 | + assert conf.oldaskconfig() == 0 | |
| 15 | + assert conf.stdout_contains('oldask0_expected_stdout') | |
| 16 | + | |
| 17 | + | |
| 18 | +def test_oldask1(conf): | |
| 19 | + assert conf.oldaskconfig('oldask1_config') == 0 | |
| 20 | + assert conf.stdout_contains('oldask1_expected_stdout') | |
| 21 | + | |
| 22 | + | |
| 23 | +def test_allyes(conf): | |
| 24 | + assert conf.allyesconfig() == 0 | |
| 25 | + assert conf.config_contains('allyes_expected_config') | |
| 26 | + | |
| 27 | + | |
| 28 | +def test_allmod(conf): | |
| 29 | + assert conf.allmodconfig() == 0 | |
| 30 | + assert conf.config_contains('allmod_expected_config') | |
| 31 | + | |
| 32 | + | |
| 33 | +def test_allno(conf): | |
| 34 | + assert conf.allnoconfig() == 0 | |
| 35 | + assert conf.config_contains('allno_expected_config') | |
| 36 | + | |
| 37 | + | |
| 38 | +def test_alldef(conf): | |
| 39 | + assert conf.alldefconfig() == 0 | |
| 40 | + assert conf.config_contains('alldef_expected_config') | |
| @@ -0,0 +1,5 @@ | ||
| 1 | +CONFIG_MODULES=y | |
| 2 | +# CONFIG_BOOL_CHOICE0 is not set | |
| 3 | +CONFIG_BOOL_CHOICE1=y | |
| 4 | +# CONFIG_TRI_CHOICE0 is not set | |
| 5 | +# CONFIG_TRI_CHOICE1 is not set | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +CONFIG_MODULES=y | |
| 2 | +# CONFIG_BOOL_CHOICE0 is not set | |
| 3 | +CONFIG_BOOL_CHOICE1=y | |
| 4 | +# CONFIG_OPT_BOOL_CHOICE0 is not set | |
| 5 | +CONFIG_OPT_BOOL_CHOICE1=y | |
| 6 | +CONFIG_TRI_CHOICE0=m | |
| 7 | +CONFIG_TRI_CHOICE1=m | |
| 8 | +CONFIG_OPT_TRI_CHOICE0=m | |
| 9 | +CONFIG_OPT_TRI_CHOICE1=m | |
| @@ -0,0 +1,5 @@ | ||
| 1 | +# CONFIG_MODULES is not set | |
| 2 | +# CONFIG_BOOL_CHOICE0 is not set | |
| 3 | +CONFIG_BOOL_CHOICE1=y | |
| 4 | +# CONFIG_TRI_CHOICE0 is not set | |
| 5 | +CONFIG_TRI_CHOICE1=y | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +CONFIG_MODULES=y | |
| 2 | +# CONFIG_BOOL_CHOICE0 is not set | |
| 3 | +CONFIG_BOOL_CHOICE1=y | |
| 4 | +# CONFIG_OPT_BOOL_CHOICE0 is not set | |
| 5 | +CONFIG_OPT_BOOL_CHOICE1=y | |
| 6 | +# CONFIG_TRI_CHOICE0 is not set | |
| 7 | +CONFIG_TRI_CHOICE1=y | |
| 8 | +# CONFIG_OPT_TRI_CHOICE0 is not set | |
| 9 | +CONFIG_OPT_TRI_CHOICE1=y | |
| @@ -0,0 +1,10 @@ | ||
| 1 | +Enable loadable module support (MODULES) [Y/n/?] (NEW) | |
| 2 | +boolean choice | |
| 3 | + 1. choice 0 (BOOL_CHOICE0) (NEW) | |
| 4 | +> 2. choice 1 (BOOL_CHOICE1) (NEW) | |
| 5 | +choice[1-2?]: | |
| 6 | +optional boolean choice [N/y/?] (NEW) | |
| 7 | +tristate choice [M/y/?] (NEW) | |
| 8 | + choice 0 (TRI_CHOICE0) [N/m/?] (NEW) | |
| 9 | + choice 1 (TRI_CHOICE1) [N/m/?] (NEW) | |
| 10 | +optional tristate choice [N/m/y/?] (NEW) | |
| @@ -0,0 +1,2 @@ | ||
| 1 | +# CONFIG_MODULES is not set | |
| 2 | +CONFIG_OPT_BOOL_CHOICE0=y | |
| @@ -0,0 +1,15 @@ | ||
| 1 | +Enable loadable module support (MODULES) [N/y/?] | |
| 2 | +boolean choice | |
| 3 | + 1. choice 0 (BOOL_CHOICE0) (NEW) | |
| 4 | +> 2. choice 1 (BOOL_CHOICE1) (NEW) | |
| 5 | +choice[1-2?]: | |
| 6 | +optional boolean choice [Y/n/?] (NEW) | |
| 7 | +optional boolean choice | |
| 8 | +> 1. choice 0 (OPT_BOOL_CHOICE0) | |
| 9 | + 2. choice 1 (OPT_BOOL_CHOICE1) (NEW) | |
| 10 | +choice[1-2?]: | |
| 11 | +tristate choice | |
| 12 | + 1. choice 0 (TRI_CHOICE0) (NEW) | |
| 13 | +> 2. choice 1 (TRI_CHOICE1) (NEW) | |
| 14 | +choice[1-2?]: | |
| 15 | +optional tristate choice [N/y/?] | |
| @@ -0,0 +1,19 @@ | ||
| 1 | +config MODULES | |
| 2 | + def_bool y | |
| 3 | + option modules | |
| 4 | + | |
| 5 | +config DEP | |
| 6 | + tristate | |
| 7 | + default m | |
| 8 | + | |
| 9 | +choice | |
| 10 | + prompt "Tristate Choice" | |
| 11 | + | |
| 12 | +config CHOICE0 | |
| 13 | + tristate "Choice 0" | |
| 14 | + | |
| 15 | +config CHOICE1 | |
| 16 | + tristate "Choice 1" | |
| 17 | + depends on DEP | |
| 18 | + | |
| 19 | +endchoice | |
| @@ -0,0 +1,15 @@ | ||
| 1 | +""" | |
| 2 | +Hide tristate choice values with mod dependency in y choice. | |
| 3 | + | |
| 4 | +If tristate choice values depend on symbols set to 'm', they should be | |
| 5 | +hidden when the choice containing them is changed from 'm' to 'y' | |
| 6 | +(i.e. exclusive choice). | |
| 7 | + | |
| 8 | +Related Linux commit: fa64e5f6a35efd5e77d639125d973077ca506074 | |
| 9 | +""" | |
| 10 | + | |
| 11 | + | |
| 12 | +def test(conf): | |
| 13 | + assert conf.oldaskconfig('config', 'y') == 0 | |
| 14 | + assert conf.config_contains('expected_config') | |
| 15 | + assert conf.stdout_contains('expected_stdout') | |
| @@ -0,0 +1,2 @@ | ||
| 1 | +CONFIG_CHOICE0=m | |
| 2 | +CONFIG_CHOICE1=m | |
| @@ -0,0 +1,3 @@ | ||
| 1 | +CONFIG_MODULES=y | |
| 2 | +CONFIG_DEP=m | |
| 3 | +CONFIG_CHOICE0=y | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +Tristate Choice [M/y/?] y | |
| 2 | +Tristate Choice | |
| 3 | +> 1. Choice 0 (CHOICE0) | |
| 4 | +choice[1]: 1 | |
| @@ -0,0 +1,291 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0 | |
| 2 | +# | |
| 3 | +# Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com> | |
| 4 | +# | |
| 5 | + | |
| 6 | +""" | |
| 7 | +Kconfig unit testing framework. | |
| 8 | + | |
| 9 | +This provides fixture functions commonly used from test files. | |
| 10 | +""" | |
| 11 | + | |
| 12 | +import os | |
| 13 | +import pytest | |
| 14 | +import shutil | |
| 15 | +import subprocess | |
| 16 | +import tempfile | |
| 17 | + | |
| 18 | +CONF_PATH = os.path.abspath(os.path.join('scripts', 'kconfig', 'conf')) | |
| 19 | + | |
| 20 | + | |
| 21 | +class Conf: | |
| 22 | + """Kconfig runner and result checker. | |
| 23 | + | |
| 24 | + This class provides methods to run text-based interface of Kconfig | |
| 25 | + (scripts/kconfig/conf) and retrieve the resulted configuration, | |
| 26 | + stdout, and stderr. It also provides methods to compare those | |
| 27 | + results with expectations. | |
| 28 | + """ | |
| 29 | + | |
| 30 | + def __init__(self, request): | |
| 31 | + """Create a new Conf instance. | |
| 32 | + | |
| 33 | + request: object to introspect the requesting test module | |
| 34 | + """ | |
| 35 | + # the directory of the test being run | |
| 36 | + self._test_dir = os.path.dirname(str(request.fspath)) | |
| 37 | + | |
| 38 | + # runners | |
| 39 | + def _run_conf(self, mode, dot_config=None, out_file='.config', | |
| 40 | + interactive=False, in_keys=None, extra_env={}): | |
| 41 | + """Run text-based Kconfig executable and save the result. | |
| 42 | + | |
| 43 | + mode: input mode option (--oldaskconfig, --defconfig=<file> etc.) | |
| 44 | + dot_config: .config file to use for configuration base | |
| 45 | + out_file: file name to contain the output config data | |
| 46 | + interactive: flag to specify the interactive mode | |
| 47 | + in_keys: key inputs for interactive modes | |
| 48 | + extra_env: additional environments | |
| 49 | + returncode: exit status of the Kconfig executable | |
| 50 | + """ | |
| 51 | + command = [CONF_PATH, mode, 'Kconfig'] | |
| 52 | + | |
| 53 | + # Override 'srctree' environment to make the test as the top directory | |
| 54 | + extra_env['srctree'] = self._test_dir | |
| 55 | + | |
| 56 | + # Run Kconfig in a temporary directory. | |
| 57 | + # This directory is automatically removed when done. | |
| 58 | + with tempfile.TemporaryDirectory() as temp_dir: | |
| 59 | + | |
| 60 | + # if .config is given, copy it to the working directory | |
| 61 | + if dot_config: | |
| 62 | + shutil.copyfile(os.path.join(self._test_dir, dot_config), | |
| 63 | + os.path.join(temp_dir, '.config')) | |
| 64 | + | |
| 65 | + ps = subprocess.Popen(command, | |
| 66 | + stdin=subprocess.PIPE, | |
| 67 | + stdout=subprocess.PIPE, | |
| 68 | + stderr=subprocess.PIPE, | |
| 69 | + cwd=temp_dir, | |
| 70 | + env=dict(os.environ, **extra_env)) | |
| 71 | + | |
| 72 | + # If input key sequence is given, feed it to stdin. | |
| 73 | + if in_keys: | |
| 74 | + ps.stdin.write(in_keys.encode('utf-8')) | |
| 75 | + | |
| 76 | + while ps.poll() is None: | |
| 77 | + # For interactive modes such as oldaskconfig, oldconfig, | |
| 78 | + # send 'Enter' key until the program finishes. | |
| 79 | + if interactive: | |
| 80 | + ps.stdin.write(b'\n') | |
| 81 | + | |
| 82 | + self.retcode = ps.returncode | |
| 83 | + self.stdout = ps.stdout.read().decode() | |
| 84 | + self.stderr = ps.stderr.read().decode() | |
| 85 | + | |
| 86 | + # Retrieve the resulted config data only when .config is supposed | |
| 87 | + # to exist. If the command fails, the .config does not exist. | |
| 88 | + # 'listnewconfig' does not produce .config in the first place. | |
| 89 | + if self.retcode == 0 and out_file: | |
| 90 | + with open(os.path.join(temp_dir, out_file)) as f: | |
| 91 | + self.config = f.read() | |
| 92 | + else: | |
| 93 | + self.config = None | |
| 94 | + | |
| 95 | + # Logging: | |
| 96 | + # Pytest captures the following information by default. In failure | |
| 97 | + # of tests, the captured log will be displayed. This will be useful to | |
| 98 | + # figure out what has happened. | |
| 99 | + | |
| 100 | + print("[command]\n{}\n".format(' '.join(command))) | |
| 101 | + | |
| 102 | + print("[retcode]\n{}\n".format(self.retcode)) | |
| 103 | + | |
| 104 | + print("[stdout]") | |
| 105 | + print(self.stdout) | |
| 106 | + | |
| 107 | + print("[stderr]") | |
| 108 | + print(self.stderr) | |
| 109 | + | |
| 110 | + if self.config is not None: | |
| 111 | + print("[output for '{}']".format(out_file)) | |
| 112 | + print(self.config) | |
| 113 | + | |
| 114 | + return self.retcode | |
| 115 | + | |
| 116 | + def oldaskconfig(self, dot_config=None, in_keys=None): | |
| 117 | + """Run oldaskconfig. | |
| 118 | + | |
| 119 | + dot_config: .config file to use for configuration base (optional) | |
| 120 | + in_key: key inputs (optional) | |
| 121 | + returncode: exit status of the Kconfig executable | |
| 122 | + """ | |
| 123 | + return self._run_conf('--oldaskconfig', dot_config=dot_config, | |
| 124 | + interactive=True, in_keys=in_keys) | |
| 125 | + | |
| 126 | + def oldconfig(self, dot_config=None, in_keys=None): | |
| 127 | + """Run oldconfig. | |
| 128 | + | |
| 129 | + dot_config: .config file to use for configuration base (optional) | |
| 130 | + in_key: key inputs (optional) | |
| 131 | + returncode: exit status of the Kconfig executable | |
| 132 | + """ | |
| 133 | + return self._run_conf('--oldconfig', dot_config=dot_config, | |
| 134 | + interactive=True, in_keys=in_keys) | |
| 135 | + | |
| 136 | + def olddefconfig(self, dot_config=None): | |
| 137 | + """Run olddefconfig. | |
| 138 | + | |
| 139 | + dot_config: .config file to use for configuration base (optional) | |
| 140 | + returncode: exit status of the Kconfig executable | |
| 141 | + """ | |
| 142 | + return self._run_conf('--olddefconfig', dot_config=dot_config) | |
| 143 | + | |
| 144 | + def defconfig(self, defconfig): | |
| 145 | + """Run defconfig. | |
| 146 | + | |
| 147 | + defconfig: defconfig file for input | |
| 148 | + returncode: exit status of the Kconfig executable | |
| 149 | + """ | |
| 150 | + defconfig_path = os.path.join(self._test_dir, defconfig) | |
| 151 | + return self._run_conf('--defconfig={}'.format(defconfig_path)) | |
| 152 | + | |
| 153 | + def _allconfig(self, mode, all_config): | |
| 154 | + if all_config: | |
| 155 | + all_config_path = os.path.join(self._test_dir, all_config) | |
| 156 | + extra_env = {'KCONFIG_ALLCONFIG': all_config_path} | |
| 157 | + else: | |
| 158 | + extra_env = {} | |
| 159 | + | |
| 160 | + return self._run_conf('--{}config'.format(mode), extra_env=extra_env) | |
| 161 | + | |
| 162 | + def allyesconfig(self, all_config=None): | |
| 163 | + """Run allyesconfig. | |
| 164 | + | |
| 165 | + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) | |
| 166 | + returncode: exit status of the Kconfig executable | |
| 167 | + """ | |
| 168 | + return self._allconfig('allyes', all_config) | |
| 169 | + | |
| 170 | + def allmodconfig(self, all_config=None): | |
| 171 | + """Run allmodconfig. | |
| 172 | + | |
| 173 | + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) | |
| 174 | + returncode: exit status of the Kconfig executable | |
| 175 | + """ | |
| 176 | + return self._allconfig('allmod', all_config) | |
| 177 | + | |
| 178 | + def allnoconfig(self, all_config=None): | |
| 179 | + """Run allnoconfig. | |
| 180 | + | |
| 181 | + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) | |
| 182 | + returncode: exit status of the Kconfig executable | |
| 183 | + """ | |
| 184 | + return self._allconfig('allno', all_config) | |
| 185 | + | |
| 186 | + def alldefconfig(self, all_config=None): | |
| 187 | + """Run alldefconfig. | |
| 188 | + | |
| 189 | + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) | |
| 190 | + returncode: exit status of the Kconfig executable | |
| 191 | + """ | |
| 192 | + return self._allconfig('alldef', all_config) | |
| 193 | + | |
| 194 | + def randconfig(self, all_config=None): | |
| 195 | + """Run randconfig. | |
| 196 | + | |
| 197 | + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) | |
| 198 | + returncode: exit status of the Kconfig executable | |
| 199 | + """ | |
| 200 | + return self._allconfig('rand', all_config) | |
| 201 | + | |
| 202 | + def savedefconfig(self, dot_config): | |
| 203 | + """Run savedefconfig. | |
| 204 | + | |
| 205 | + dot_config: .config file for input | |
| 206 | + returncode: exit status of the Kconfig executable | |
| 207 | + """ | |
| 208 | + return self._run_conf('--savedefconfig', out_file='defconfig') | |
| 209 | + | |
| 210 | + def listnewconfig(self, dot_config=None): | |
| 211 | + """Run listnewconfig. | |
| 212 | + | |
| 213 | + dot_config: .config file to use for configuration base (optional) | |
| 214 | + returncode: exit status of the Kconfig executable | |
| 215 | + """ | |
| 216 | + return self._run_conf('--listnewconfig', dot_config=dot_config, | |
| 217 | + out_file=None) | |
| 218 | + | |
| 219 | + # checkers | |
| 220 | + def _read_and_compare(self, compare, expected): | |
| 221 | + """Compare the result with expectation. | |
| 222 | + | |
| 223 | + compare: function to compare the result with expectation | |
| 224 | + expected: file that contains the expected data | |
| 225 | + """ | |
| 226 | + with open(os.path.join(self._test_dir, expected)) as f: | |
| 227 | + expected_data = f.read() | |
| 228 | + return compare(self, expected_data) | |
| 229 | + | |
| 230 | + def _contains(self, attr, expected): | |
| 231 | + return self._read_and_compare( | |
| 232 | + lambda s, e: getattr(s, attr).find(e) >= 0, | |
| 233 | + expected) | |
| 234 | + | |
| 235 | + def _matches(self, attr, expected): | |
| 236 | + return self._read_and_compare(lambda s, e: getattr(s, attr) == e, | |
| 237 | + expected) | |
| 238 | + | |
| 239 | + def config_contains(self, expected): | |
| 240 | + """Check if resulted configuration contains expected data. | |
| 241 | + | |
| 242 | + expected: file that contains the expected data | |
| 243 | + returncode: True if result contains the expected data, False otherwise | |
| 244 | + """ | |
| 245 | + return self._contains('config', expected) | |
| 246 | + | |
| 247 | + def config_matches(self, expected): | |
| 248 | + """Check if resulted configuration exactly matches expected data. | |
| 249 | + | |
| 250 | + expected: file that contains the expected data | |
| 251 | + returncode: True if result matches the expected data, False otherwise | |
| 252 | + """ | |
| 253 | + return self._matches('config', expected) | |
| 254 | + | |
| 255 | + def stdout_contains(self, expected): | |
| 256 | + """Check if resulted stdout contains expected data. | |
| 257 | + | |
| 258 | + expected: file that contains the expected data | |
| 259 | + returncode: True if result contains the expected data, False otherwise | |
| 260 | + """ | |
| 261 | + return self._contains('stdout', expected) | |
| 262 | + | |
| 263 | + def stdout_matches(self, expected): | |
| 264 | + """Check if resulted stdout exactly matches expected data. | |
| 265 | + | |
| 266 | + expected: file that contains the expected data | |
| 267 | + returncode: True if result matches the expected data, False otherwise | |
| 268 | + """ | |
| 269 | + return self._matches('stdout', expected) | |
| 270 | + | |
| 271 | + def stderr_contains(self, expected): | |
| 272 | + """Check if resulted stderr contains expected data. | |
| 273 | + | |
| 274 | + expected: file that contains the expected data | |
| 275 | + returncode: True if result contains the expected data, False otherwise | |
| 276 | + """ | |
| 277 | + return self._contains('stderr', expected) | |
| 278 | + | |
| 279 | + def stderr_matches(self, expected): | |
| 280 | + """Check if resulted stderr exactly matches expected data. | |
| 281 | + | |
| 282 | + expected: file that contains the expected data | |
| 283 | + returncode: True if result matches the expected data, False otherwise | |
| 284 | + """ | |
| 285 | + return self._matches('stderr', expected) | |
| 286 | + | |
| 287 | + | |
| 288 | +@pytest.fixture(scope="module") | |
| 289 | +def conf(request): | |
| 290 | + """Create a Conf instance and provide it to test functions.""" | |
| 291 | + return Conf(request) | |
| @@ -0,0 +1 @@ | ||
| 1 | +source "Kconfig.inc1" | |
| @@ -0,0 +1,4 @@ | ||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | +source "Kconfig.inc2" | |
| @@ -0,0 +1,3 @@ | ||
| 1 | + | |
| 2 | + | |
| 3 | +source "Kconfig.inc3" | |
| @@ -0,0 +1 @@ | ||
| 1 | +source "Kconfig.inc1" | |
| @@ -0,0 +1,10 @@ | ||
| 1 | +""" | |
| 2 | +Detect recursive inclusion error. | |
| 3 | + | |
| 4 | +If recursive inclusion is detected, it should fail with error messages. | |
| 5 | +""" | |
| 6 | + | |
| 7 | + | |
| 8 | +def test(conf): | |
| 9 | + assert conf.oldaskconfig() != 0 | |
| 10 | + assert conf.stderr_contains('expected_stderr') | |
| @@ -0,0 +1,6 @@ | ||
| 1 | +Recursive inclusion detected. | |
| 2 | +Inclusion path: | |
| 3 | + current file : Kconfig.inc1 | |
| 4 | + included from: Kconfig.inc3:1 | |
| 5 | + included from: Kconfig.inc2:3 | |
| 6 | + included from: Kconfig.inc1:4 | |
| @@ -0,0 +1,23 @@ | ||
| 1 | +config MODULES | |
| 2 | + def_bool y | |
| 3 | + option modules | |
| 4 | + | |
| 5 | +choice | |
| 6 | + prompt "Choice" | |
| 7 | + | |
| 8 | +config CHOICE_VAL0 | |
| 9 | + tristate "Choice 0" | |
| 10 | + | |
| 11 | +config CHOIVE_VAL1 | |
| 12 | + tristate "Choice 1" | |
| 13 | + | |
| 14 | +endchoice | |
| 15 | + | |
| 16 | +choice | |
| 17 | + prompt "Another choice" | |
| 18 | + depends on CHOICE_VAL0 | |
| 19 | + | |
| 20 | +config DUMMY | |
| 21 | + bool "dummy" | |
| 22 | + | |
| 23 | +endchoice | |
| @@ -0,0 +1,14 @@ | ||
| 1 | +""" | |
| 2 | +Do not affect user-assigned choice value by another choice. | |
| 3 | + | |
| 4 | +Handling of state flags for choices is complecated. In old days, | |
| 5 | +the defconfig result of a choice could be affected by another choice | |
| 6 | +if those choices interact by 'depends on', 'select', etc. | |
| 7 | + | |
| 8 | +Related Linux commit: fbe98bb9ed3dae23e320c6b113e35f129538d14a | |
| 9 | +""" | |
| 10 | + | |
| 11 | + | |
| 12 | +def test(conf): | |
| 13 | + assert conf.defconfig('defconfig') == 0 | |
| 14 | + assert conf.config_contains('expected_config') | |
| @@ -0,0 +1 @@ | ||
| 1 | +CONFIG_CHOICE_VAL0=y | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +CONFIG_MODULES=y | |
| 2 | +CONFIG_CHOICE_VAL0=y | |
| 3 | +# CONFIG_CHOIVE_VAL1 is not set | |
| 4 | +CONFIG_DUMMY=y | |
| @@ -0,0 +1,37 @@ | ||
| 1 | +config A | |
| 2 | + bool "A" | |
| 3 | + help | |
| 4 | + This is a new symbol. | |
| 5 | + | |
| 6 | +choice | |
| 7 | + prompt "Choice ?" | |
| 8 | + depends on A | |
| 9 | + help | |
| 10 | + "depends on A" has been newly added. | |
| 11 | + | |
| 12 | +config CHOICE_B | |
| 13 | + bool "Choice B" | |
| 14 | + | |
| 15 | +config CHOICE_C | |
| 16 | + bool "Choice C" | |
| 17 | + help | |
| 18 | + This is a new symbol, so should be asked. | |
| 19 | + | |
| 20 | +endchoice | |
| 21 | + | |
| 22 | +choice | |
| 23 | + prompt "Choice2 ?" | |
| 24 | + | |
| 25 | +config CHOICE_D | |
| 26 | + bool "Choice D" | |
| 27 | + | |
| 28 | +config CHOICE_E | |
| 29 | + bool "Choice E" | |
| 30 | + | |
| 31 | +config CHOICE_F | |
| 32 | + bool "Choice F" | |
| 33 | + depends on A | |
| 34 | + help | |
| 35 | + This is a new symbol, so should be asked. | |
| 36 | + | |
| 37 | +endchoice | |
| @@ -0,0 +1,14 @@ | ||
| 1 | +""" | |
| 2 | +Ask new choice values when they become visible. | |
| 3 | + | |
| 4 | +If new choice values are added with new dependency, and they become | |
| 5 | +visible during user configuration, oldconfig should recognize them | |
| 6 | +as (NEW), and ask the user for choice. | |
| 7 | + | |
| 8 | +Related Linux commit: 5d09598d488f081e3be23f885ed65cbbe2d073b5 | |
| 9 | +""" | |
| 10 | + | |
| 11 | + | |
| 12 | +def test(conf): | |
| 13 | + assert conf.oldconfig('config', 'y') == 0 | |
| 14 | + assert conf.stdout_contains('expected_stdout') | |
| @@ -0,0 +1,3 @@ | ||
| 1 | +CONFIG_CHOICE_B=y | |
| 2 | +# CONFIG_CHOICE_D is not set | |
| 3 | +CONFIG_CHOICE_E=y | |
| @@ -0,0 +1,10 @@ | ||
| 1 | +A (A) [N/y/?] (NEW) y | |
| 2 | + Choice ? | |
| 3 | + > 1. Choice B (CHOICE_B) | |
| 4 | + 2. Choice C (CHOICE_C) (NEW) | |
| 5 | + choice[1-2?]: | |
| 6 | +Choice2 ? | |
| 7 | + 1. Choice D (CHOICE_D) | |
| 8 | +> 2. Choice E (CHOICE_E) | |
| 9 | + 3. Choice F (CHOICE_F) (NEW) | |
| 10 | +choice[1-3?]: | |
| @@ -0,0 +1,14 @@ | ||
| 1 | +config A | |
| 2 | + bool "A" | |
| 3 | + | |
| 4 | +choice | |
| 5 | + prompt "Choice ?" | |
| 6 | + depends on A | |
| 7 | + | |
| 8 | +config CHOICE_B | |
| 9 | + bool "Choice B" | |
| 10 | + | |
| 11 | +config CHOICE_C | |
| 12 | + bool "Choice C" | |
| 13 | + | |
| 14 | +endchoice | |
| @@ -0,0 +1,19 @@ | ||
| 1 | +""" | |
| 2 | +Do not write choice values to .config if the dependency is unmet. | |
| 3 | + | |
| 4 | +"# CONFIG_... is not set" should not be written into the .config file | |
| 5 | +for symbols with unmet dependency. | |
| 6 | + | |
| 7 | +This was not working correctly for choice values because choice needs | |
| 8 | +a bit different symbol computation. | |
| 9 | + | |
| 10 | +This checks that no unneeded "# COFIG_... is not set" is contained in | |
| 11 | +the .config file. | |
| 12 | + | |
| 13 | +Related Linux commit: cb67ab2cd2b8abd9650292c986c79901e3073a59 | |
| 14 | +""" | |
| 15 | + | |
| 16 | + | |
| 17 | +def test(conf): | |
| 18 | + assert conf.oldaskconfig('config', 'n') == 0 | |
| 19 | + assert conf.config_matches('expected_config') | |
| @@ -0,0 +1 @@ | ||
| 1 | +CONFIG_A=y | |
| @@ -0,0 +1,5 @@ | ||
| 1 | +# | |
| 2 | +# Automatically generated file; DO NOT EDIT. | |
| 3 | +# Linux Kernel Configuration | |
| 4 | +# | |
| 5 | +# CONFIG_A is not set | |
| @@ -0,0 +1,7 @@ | ||
| 1 | +[pytest] | |
| 2 | +addopts = --verbose | |
| 3 | + | |
| 4 | +# Pytest requires that test files have unique names, because pytest imports | |
| 5 | +# them as top-level modules. It is silly to prefix or suffix a test file with | |
| 6 | +# the directory name that contains it. Use __init__.py for all test files. | |
| 7 | +python_files = __init__.py | |
| @@ -0,0 +1,33 @@ | ||
| 1 | +choice | |
| 2 | + prompt "choice" | |
| 3 | + | |
| 4 | +config A | |
| 5 | + bool "A" | |
| 6 | + | |
| 7 | +config B | |
| 8 | + bool "B" | |
| 9 | + | |
| 10 | +if B | |
| 11 | +choice | |
| 12 | + prompt "sub choice" | |
| 13 | + | |
| 14 | +config C | |
| 15 | + bool "C" | |
| 16 | + | |
| 17 | +config D | |
| 18 | + bool "D" | |
| 19 | + | |
| 20 | +if D | |
| 21 | +choice | |
| 22 | + prompt "subsub choice" | |
| 23 | + | |
| 24 | +config E | |
| 25 | + bool "E" | |
| 26 | + | |
| 27 | +endchoice | |
| 28 | +endif # D | |
| 29 | + | |
| 30 | +endchoice | |
| 31 | +endif # B | |
| 32 | + | |
| 33 | +endchoice | |
| @@ -0,0 +1,16 @@ | ||
| 1 | +""" | |
| 2 | +Set random values recursively in nested choices. | |
| 3 | + | |
| 4 | +Kconfig can create a choice-in-choice structure by using 'if' statement. | |
| 5 | +randconfig should correctly set random choice values. | |
| 6 | + | |
| 7 | +Related Linux commit: 3b9a19e08960e5cdad5253998637653e592a3c29 | |
| 8 | +""" | |
| 9 | + | |
| 10 | + | |
| 11 | +def test(conf): | |
| 12 | + for i in range(20): | |
| 13 | + assert conf.randconfig() == 0 | |
| 14 | + assert (conf.config_contains('expected_stdout0') or | |
| 15 | + conf.config_contains('expected_stdout1') or | |
| 16 | + conf.config_contains('expected_stdout2')) | |
| @@ -0,0 +1,2 @@ | ||
| 1 | +CONFIG_A=y | |
| 2 | +# CONFIG_B is not set | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +# CONFIG_A is not set | |
| 2 | +CONFIG_B=y | |
| 3 | +CONFIG_C=y | |
| 4 | +# CONFIG_D is not set | |
| @@ -0,0 +1,5 @@ | ||
| 1 | +# CONFIG_A is not set | |
| 2 | +CONFIG_B=y | |
| 3 | +# CONFIG_C is not set | |
| 4 | +CONFIG_D=y | |
| 5 | +CONFIG_E=y | |
| @@ -0,0 +1,62 @@ | ||
| 1 | +# depends on itself | |
| 2 | + | |
| 3 | +config A | |
| 4 | + bool "A" | |
| 5 | + depends on A | |
| 6 | + | |
| 7 | +# select itself | |
| 8 | + | |
| 9 | +config B | |
| 10 | + bool | |
| 11 | + select B | |
| 12 | + | |
| 13 | +# depends on each other | |
| 14 | + | |
| 15 | +config C1 | |
| 16 | + bool "C1" | |
| 17 | + depends on C2 | |
| 18 | + | |
| 19 | +config C2 | |
| 20 | + bool "C2" | |
| 21 | + depends on C1 | |
| 22 | + | |
| 23 | +# depends on and select | |
| 24 | + | |
| 25 | +config D1 | |
| 26 | + bool "D1" | |
| 27 | + depends on D2 | |
| 28 | + select D2 | |
| 29 | + | |
| 30 | +config D2 | |
| 31 | + bool | |
| 32 | + | |
| 33 | +# depends on and imply | |
| 34 | +# This is not recursive dependency | |
| 35 | + | |
| 36 | +config E1 | |
| 37 | + bool "E1" | |
| 38 | + depends on E2 | |
| 39 | + imply E2 | |
| 40 | + | |
| 41 | +config E2 | |
| 42 | + bool "E2" | |
| 43 | + | |
| 44 | +# property | |
| 45 | + | |
| 46 | +config F1 | |
| 47 | + bool "F1" | |
| 48 | + default F2 | |
| 49 | + | |
| 50 | +config F2 | |
| 51 | + bool "F2" | |
| 52 | + depends on F1 | |
| 53 | + | |
| 54 | +# menu | |
| 55 | + | |
| 56 | +menu "menu depending on its content" | |
| 57 | + depends on G | |
| 58 | + | |
| 59 | +config G | |
| 60 | + bool "G" | |
| 61 | + | |
| 62 | +endmenu | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +""" | |
| 2 | +Warn recursive inclusion. | |
| 3 | + | |
| 4 | +Recursive dependency should be warned. | |
| 5 | +""" | |
| 6 | + | |
| 7 | +def test(conf): | |
| 8 | + assert conf.oldaskconfig() == 0 | |
| 9 | + assert conf.stderr_contains('expected_stderr') | |
| @@ -0,0 +1,30 @@ | ||
| 1 | +Kconfig:9:error: recursive dependency detected! | |
| 2 | +Kconfig:9: symbol B is selected by B | |
| 3 | +For a resolution refer to Documentation/kbuild/kconfig-language.txt | |
| 4 | +subsection "Kconfig recursive dependency limitations" | |
| 5 | + | |
| 6 | +Kconfig:3:error: recursive dependency detected! | |
| 7 | +Kconfig:3: symbol A depends on A | |
| 8 | +For a resolution refer to Documentation/kbuild/kconfig-language.txt | |
| 9 | +subsection "Kconfig recursive dependency limitations" | |
| 10 | + | |
| 11 | +Kconfig:15:error: recursive dependency detected! | |
| 12 | +Kconfig:15: symbol C1 depends on C2 | |
| 13 | +Kconfig:19: symbol C2 depends on C1 | |
| 14 | +For a resolution refer to Documentation/kbuild/kconfig-language.txt | |
| 15 | +subsection "Kconfig recursive dependency limitations" | |
| 16 | + | |
| 17 | +Kconfig:30:error: recursive dependency detected! | |
| 18 | +Kconfig:30: symbol D2 is selected by D1 | |
| 19 | +Kconfig:25: symbol D1 depends on D2 | |
| 20 | +For a resolution refer to Documentation/kbuild/kconfig-language.txt | |
| 21 | +subsection "Kconfig recursive dependency limitations" | |
| 22 | + | |
| 23 | +Kconfig:59:error: recursive dependency detected! | |
| 24 | +Kconfig:59: symbol G depends on G | |
| 25 | +For a resolution refer to Documentation/kbuild/kconfig-language.txt | |
| 26 | +subsection "Kconfig recursive dependency limitations" | |
| 27 | + | |
| 28 | +Kconfig:50:error: recursive dependency detected! | |
| 29 | +Kconfig:50: symbol F2 depends on F1 | |
| 30 | +Kconfig:48: symbol F1 default value contains F2 | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | %option nostdinit noyywrap never-interactive full ecs |
| 2 | −%option 8bit nodefault perf-report perf-report | |
| 2 | +%option 8bit nodefault yylineno | |
| 3 | 3 | %option noinput |
| 4 | 4 | %x COMMAND HELP STRING PARAM |
| 5 | 5 | %{ |
| @@ -83,7 +83,6 @@ n [A-Za-z0-9_-] · 77 unmodified lines | ||
| 83 | 83 | |
| 84 | 84 | [ \t]*#.*\n | |
| 85 | 85 | [ \t]*\n { |
| 86 | − current_file->lineno++; | |
| 87 | 86 | return T_EOL; |
| 88 | 87 | } |
| 89 | 88 | [ \t]*#.* |
| @@ -104,7 +103,7 @@ n [A-Za-z0-9_-] · 14 unmodified lines | ||
| 104 | 103 | const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); |
| 105 | 104 | BEGIN(PARAM); |
| 106 | 105 | current_pos.file = current_file; |
| 107 | − current_pos.lineno = current_file->lineno; | |
| 106 | + current_pos.lineno = yylineno; | |
| 108 | 107 | if (id && id->flags & TF_COMMAND) { |
| 109 | 108 | yylval.id = id; |
| 110 | 109 | return id->token; |
| @@ -116,7 +115,6 @@ n [A-Za-z0-9_-] · 5 unmodified lines | ||
| 116 | 115 | . warn_ignored_character(*yytext); |
| 117 | 116 | \n { |
| 118 | 117 | BEGIN(INITIAL); |
| 119 | − current_file->lineno++; | |
| 120 | 118 | return T_EOL; |
| 121 | 119 | } |
| 122 | 120 | } |
| @@ -138,7 +136,7 @@ n [A-Za-z0-9_-] · 15 unmodified lines | ||
| 138 | 136 | new_string(); |
| 139 | 137 | BEGIN(STRING); |
| 140 | 138 | } |
| 141 | − \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; | |
| 139 | + \n BEGIN(INITIAL); return T_EOL; | |
| 142 | 140 | ({n}|[/.])+ { |
| 143 | 141 | const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); |
| 144 | 142 | if (id && id->flags & TF_PARAM) { |
| @@ -150,7 +148,7 @@ n [A-Za-z0-9_-] · 5 unmodified lines | ||
| 150 | 148 | return T_WORD; |
| 151 | 149 | } |
| 152 | 150 | #.* /* comment */ |
| 153 | − \\\n current_file->lineno++; | |
| 151 | + \\\n ; | |
| 154 | 152 | [[:blank:]]+ |
| 155 | 153 | . warn_ignored_character(*yytext); |
| 156 | 154 | <<EOF>> { |
| @@ -187,7 +185,6 @@ n [A-Za-z0-9_-] · 30 unmodified lines | ||
| 187 | 185 | fprintf(stderr, |
| 188 | 186 | "%s:%d:warning: multi-line strings not supported\n", |
| 189 | 187 | zconf_curname(), zconf_lineno()); |
| 190 | − current_file->lineno++; | |
| 191 | 188 | BEGIN(INITIAL); |
| 192 | 189 | return T_EOL; |
| 193 | 190 | } |
| @@ -220,12 +217,10 @@ n [A-Za-z0-9_-] · 26 unmodified lines | ||
| 220 | 217 | } |
| 221 | 218 | } |
| 222 | 219 | [ \t]*\n/[^ \t\n] { |
| 223 | − current_file->lineno++; | |
| 224 | 220 | zconf_endhelp(); |
| 225 | 221 | return T_HELPTEXT; |
| 226 | 222 | } |
| 227 | 223 | [ \t]*\n { |
| 228 | − current_file->lineno++; | |
| 229 | 224 | append_string("\n", 1); |
| 230 | 225 | } |
| 231 | 226 | [^ \t\n].* { |
| @@ -304,7 +299,7 @@ void zconf_initscan(const char *name) · 72 unmodified lines | ||
| 304 | 299 | memset(current_buf, 0, sizeof(*current_buf)); |
| 305 | 300 | |
| 306 | 301 | current_file = file_lookup(name); |
| 307 | − current_file->lineno = 1; | |
| 302 | + yylineno = 1; | |
| 308 | 303 | } |
| 309 | 304 | |
| 310 | 305 | void zconf_nextfile(const char *name) |
| @@ -325,24 +320,26 @@ void zconf_nextfile(const char *name) · 14 unmodified lines | ||
| 325 | 320 | buf->parent = current_buf; |
| 326 | 321 | current_buf = buf; |
| 327 | 322 | |
| 328 | − for (iter = current_file->parent; iter; iter = iter->parent ) { | |
| 329 | − if (!strcmp(current_file->name,iter->name) ) { | |
| 323 | + current_file->lineno = yylineno; | |
| 324 | + file->parent = current_file; | |
| 325 | + | |
| 326 | + for (iter = current_file; iter; iter = iter->parent) { | |
| 327 | + if (!strcmp(iter->name, file->name)) { | |
| 330 | 328 | fprintf(stderr, |
| 331 | − "%s:%d: recursive inclusion detected. " | |
| 332 | − "Inclusion path:\n current file : '%s'\n", | |
| 333 | − zconf_curname(), zconf_lineno(), | |
| 334 | − zconf_curname()); | |
| 335 | − iter = current_file; | |
| 329 | + "Recursive inclusion detected.\n" | |
| 330 | + "Inclusion path:\n" | |
| 331 | + " current file : %s\n", file->name); | |
| 332 | + iter = file; | |
| 336 | 333 | do { |
| 337 | 334 | iter = iter->parent; |
| 338 | − fprintf(stderr, " included from: '%s:%d'\n", | |
| 335 | + fprintf(stderr, " included from: %s:%d\n", | |
| 339 | 336 | iter->name, iter->lineno - 1); |
| 340 | − } while (strcmp(iter->name, current_file->name)); | |
| 337 | + } while (strcmp(iter->name, file->name)); | |
| 341 | 338 | exit(1); |
| 342 | 339 | } |
| 343 | 340 | } |
| 344 | − file->lineno = 1; | |
| 345 | − file->parent = current_file; | |
| 341 | + | |
| 342 | + yylineno = 1; | |
| 346 | 343 | current_file = file; |
| 347 | 344 | } |
| 348 | 345 | |
| @@ -351,6 +348,8 @@ static void zconf_endfile(void) · 2 unmodified lines | ||
| 351 | 348 | struct buffer *parent; |
| 352 | 349 | |
| 353 | 350 | current_file = current_file->parent; |
| 351 | + if (current_file) | |
| 352 | + yylineno = current_file->lineno; | |
| 354 | 353 | |
| 355 | 354 | parent = current_buf->parent; |
| 356 | 355 | if (parent) { |