diff --git a/Documentation/kbuild/kconfig.txt b/Documentation/kbuild/kconfig.txt index bbc99c0c1094..7233118f3a05 100644 --- a/Documentation/kbuild/kconfig.txt +++ b/Documentation/kbuild/kconfig.txt @@ -119,7 +119,7 @@ Examples: 15% of tristates will be set to 'y', 15% to 'm', 70% to 'n' ______________________________________________________________________ -Environment variables for 'silentoldconfig' +Environment variables for 'syncconfig' KCONFIG_NOSILENTUPDATE -------------------------------------------------- diff --git a/Documentation/networking/i40e.txt b/Documentation/networking/i40e.txt index 57e616ed10b0..c2d6e1824b29 100644 --- a/Documentation/networking/i40e.txt +++ b/Documentation/networking/i40e.txt @@ -32,7 +32,7 @@ Enabling the driver The driver is enabled via the standard kernel configuration system, using the make command: - Make oldconfig/silentoldconfig/menuconfig/etc. + make config/oldconfig/menuconfig/etc. The driver is located in the menu structure at: diff --git a/Makefile b/Makefile index 65b61599b513..c1a608a1bc4c 100644 --- a/Makefile +++ b/Makefile @@ -386,6 +386,8 @@ INSTALLKERNEL := installkernel DEPMOD = /sbin/depmod PERL = perl PYTHON = python +PYTHON2 = python2 +PYTHON3 = python3 CHECK = sparse CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ @@ -432,7 +434,7 @@ GCC_PLUGINS_CFLAGS := export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC export CPP AR NM STRIP OBJCOPY OBJDUMP HOSTLDFLAGS HOST_LOADLIBES -export MAKE LEX YACC AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE +export MAKE LEX YACC AWK GENKSYMS INSTALLKERNEL PERL PYTHON PYTHON2 PYTHON3 UTS_MACHINE export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS @@ -591,7 +593,7 @@ $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; # include/generated/ and include/config/. Update them if .config is newer than # include/config/auto.conf (which mirrors .config). include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd - $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig + $(Q)$(MAKE) -f $(srctree)/Makefile syncconfig else # external modules needs include/generated/autoconf.h and include/config/auto.conf # but do not care if they are up-to-date. Use auto.conf to trigger the test diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 949842e8c97e..764ffd1bb1c5 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2797,7 +2797,10 @@ sub process { # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*config\s+/) { + # 'choice' is usually the last thing on the line (though + # Kconfig supports named choices), so use a word boundary + # (\b) rather than a whitespace character (\s) + $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { my $length = 0; my $cnt = $realcnt; my $ln = $linenr + 1; @@ -2812,9 +2815,13 @@ sub process { next if ($f =~ /^-/); last if (!$file && $f =~ /^\@\@/); - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { $is_start = 1; - } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) { + if ($lines[$ln - 1] =~ "---help---") { + WARN("CONFIG_DESCRIPTION", + "prefer 'help' over '---help---' for new help texts\n" . $herecurr); + } $length = -1; } @@ -2822,7 +2829,13 @@ sub process { $f =~ s/#.*//; $f =~ s/^\s+//; next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { + + # This only checks context lines in the patch + # and so hopefully shouldn't trigger false + # positives, even though some of these are + # common words in help texts + if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| + if|endif|menu|endmenu|source)\b/x) { $is_end = 1; last; } diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index eb139a17383c..f9bdd02c06a2 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -3,7 +3,7 @@ # Kernel configuration targets # These targets are used from top-level makefile -PHONY += xconfig gconfig menuconfig config silentoldconfig update-po-config \ +PHONY += xconfig gconfig menuconfig config syncconfig update-po-config \ localmodconfig localyesconfig ifdef KBUILD_KCONFIG @@ -36,22 +36,22 @@ nconfig: $(obj)/nconf # This has become an internal implementation detail and is now deprecated # for external use. -silentoldconfig: $(obj)/conf +syncconfig: $(obj)/conf $(Q)mkdir -p include/config include/generated $< $(silent) --$@ $(Kconfig) -localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf +localyesconfig localmodconfig: $(obj)/conf $(Q)mkdir -p include/config include/generated - $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config + $(Q)perl $(srctree)/$(src)/streamline_config.pl --$@ $(srctree) $(Kconfig) > .tmp.config $(Q)if [ -f .config ]; then \ cmp -s .tmp.config .config || \ (mv -f .config .config.old.1; \ mv -f .tmp.config .config; \ - $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ + $< $(silent) --oldconfig $(Kconfig); \ mv -f .config.old.1 .config.old) \ else \ mv -f .tmp.config .config; \ - $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ + $< $(silent) --oldconfig $(Kconfig); \ fi $(Q)rm -f .tmp.config @@ -86,7 +86,7 @@ PHONY += $(simple-targets) $(simple-targets): $(obj)/conf $< $(silent) --$@ $(Kconfig) -PHONY += oldnoconfig savedefconfig defconfig +PHONY += oldnoconfig silentoldconfig savedefconfig defconfig # oldnoconfig is an alias of olddefconfig, because people already are dependent # on its behavior (sets new symbols to their default value but not 'n') with the @@ -95,6 +95,13 @@ oldnoconfig: olddefconfig @echo " WARNING: \"oldnoconfig\" target will be removed after Linux 4.19" @echo " Please use \"olddefconfig\" instead, which is an alias." +# We do not expect manual invokcation of "silentoldcofig" (or "syncconfig"). +silentoldconfig: syncconfig + @echo " WARNING: \"silentoldconfig\" has been renamed to \"syncconfig\"" + @echo " and is now an internal implementation detail." + @echo " What you want is probably \"oldconfig\"." + @echo " \"silentoldconfig\" will be removed after Linux 4.19" + savedefconfig: $(obj)/conf $< $(silent) --$@=defconfig $(Kconfig) @@ -133,6 +140,14 @@ PHONY += tinyconfig tinyconfig: $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config +# CHECK: -o cache_dir= working? +PHONY += testconfig +testconfig: $(obj)/conf + $(PYTHON3) -B -m pytest $(srctree)/$(src)/tests \ + -o cache_dir=$(abspath $(obj)/tests/.cache) \ + $(if $(findstring 1,$(KBUILD_VERBOSE)),--capture=no) +clean-dirs += tests/.cache + # Help text used by make help help: @echo ' config - Update current config utilising a line-oriented program' diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 822dc51923d6..4e08121a35fb 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -23,7 +23,7 @@ static void check_conf(struct menu *menu); enum input_mode { oldaskconfig, - silentoldconfig, + syncconfig, oldconfig, allnoconfig, allyesconfig, @@ -100,7 +100,7 @@ static int conf_askvalue(struct symbol *sym, const char *def) switch (input_mode) { case oldconfig: - case silentoldconfig: + case syncconfig: if (sym_has_value(sym)) { printf("%s\n", def); return 0; @@ -293,7 +293,7 @@ static int conf_choice(struct menu *menu) printf("[1-%d?]: ", cnt); switch (input_mode) { case oldconfig: - case silentoldconfig: + case syncconfig: if (!is_new) { cnt = def; printf("%d\n", cnt); @@ -358,10 +358,11 @@ static void conf(struct menu *menu) switch (prop->type) { case P_MENU: - if ((input_mode == silentoldconfig || - input_mode == listnewconfig || - input_mode == olddefconfig) && - rootEntry != menu) { + /* + * Except in oldaskconfig mode, we show only menus that + * contain new symbols. + */ + if (input_mode != oldaskconfig && rootEntry != menu) { check_conf(menu); return; } @@ -424,7 +425,7 @@ static void check_conf(struct menu *menu) if (sym->name && !sym_is_choice_value(sym)) { printf("%s%s\n", CONFIG_, sym->name); } - } else if (input_mode != olddefconfig) { + } else { if (!conf_cnt++) printf(_("*\n* Restart config...\n*\n")); rootEntry = menu_get_parent_menu(menu); @@ -440,7 +441,7 @@ static void check_conf(struct menu *menu) static struct option long_opts[] = { {"oldaskconfig", no_argument, NULL, oldaskconfig}, {"oldconfig", no_argument, NULL, oldconfig}, - {"silentoldconfig", no_argument, NULL, silentoldconfig}, + {"syncconfig", no_argument, NULL, syncconfig}, {"defconfig", optional_argument, NULL, defconfig}, {"savedefconfig", required_argument, NULL, savedefconfig}, {"allnoconfig", no_argument, NULL, allnoconfig}, @@ -467,8 +468,8 @@ static void conf_usage(const char *progname) printf(" --listnewconfig List new options\n"); printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); printf(" --oldconfig Update a configuration using a provided .config as base\n"); - printf(" --silentoldconfig Similar to oldconfig but generates configuration in\n" - " include/{generated/,config/} (oldconfig used to be more verbose)\n"); + printf(" --syncconfig Similar to oldconfig but generates configuration in\n" + " include/{generated/,config/}\n"); printf(" --olddefconfig Same as oldconfig but sets new symbols to their default value\n"); printf(" --oldnoconfig An alias of olddefconfig\n"); printf(" --defconfig New config with default defined in \n"); @@ -500,7 +501,7 @@ int main(int ac, char **av) } input_mode = (enum input_mode)opt; switch (opt) { - case silentoldconfig: + case syncconfig: sync_kconfig = 1; break; case defconfig: @@ -582,7 +583,7 @@ int main(int ac, char **av) } break; case savedefconfig: - case silentoldconfig: + case syncconfig: case oldaskconfig: case oldconfig: case listnewconfig: @@ -662,24 +663,24 @@ int main(int ac, char **av) case oldaskconfig: rootEntry = &rootmenu; conf(&rootmenu); - input_mode = silentoldconfig; + input_mode = oldconfig; /* fall through */ case oldconfig: case listnewconfig: - case olddefconfig: - case silentoldconfig: + case syncconfig: /* Update until a loop caused no more changes */ do { conf_cnt = 0; check_conf(&rootmenu); - } while (conf_cnt && - (input_mode != listnewconfig && - input_mode != olddefconfig)); + } while (conf_cnt); + break; + case olddefconfig: + default: break; } if (sync_kconfig) { - /* silentoldconfig is used during the build so we shall update autoconf. + /* syncconfig is used during the build so we shall update autoconf. * All other commands are only used to generate a config. */ if (conf_get_changed() && conf_write(NULL)) { diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index d45381986ac7..e1a39e90841d 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -1137,49 +1137,9 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) return 0; } -static inline struct expr * -expr_get_leftmost_symbol(const struct expr *e) -{ - - if (e == NULL) - return NULL; - - while (e->type != E_SYMBOL) - e = e->left.expr; - - return expr_copy(e); -} - -/* - * Given expression `e1' and `e2', returns the leaf of the longest - * sub-expression of `e1' not containing 'e2. - */ -struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2) -{ - struct expr *ret; - - switch (e1->type) { - case E_OR: - return expr_alloc_and( - expr_simplify_unmet_dep(e1->left.expr, e2), - expr_simplify_unmet_dep(e1->right.expr, e2)); - case E_AND: { - struct expr *e; - e = expr_alloc_and(expr_copy(e1), expr_copy(e2)); - e = expr_eliminate_dups(e); - ret = (!expr_eq(e, e1)) ? e1 : NULL; - expr_free(e); - break; - } - default: - ret = e1; - break; - } - - return expr_get_leftmost_symbol(ret); -} - -static void __expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken, bool revdep) +void expr_print(struct expr *e, + void (*fn)(void *, struct symbol *, const char *), + void *data, int prevtoken) { if (!e) { fn(data, NULL, "y"); @@ -1234,14 +1194,9 @@ static void __expr_print(struct expr *e, void (*fn)(void *, struct symbol *, con fn(data, e->right.sym, e->right.sym->name); break; case E_OR: - if (revdep && e->left.expr->type != E_OR) - fn(data, NULL, "\n - "); - __expr_print(e->left.expr, fn, data, E_OR, revdep); - if (revdep) - fn(data, NULL, "\n - "); - else - fn(data, NULL, " || "); - __expr_print(e->right.expr, fn, data, E_OR, revdep); + expr_print(e->left.expr, fn, data, E_OR); + fn(data, NULL, " || "); + expr_print(e->right.expr, fn, data, E_OR); break; case E_AND: 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 fn(data, NULL, ")"); } -void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) -{ - __expr_print(e, fn, data, prevtoken, false); -} - static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) { xfwrite(str, strlen(str), 1, data); @@ -1329,7 +1279,27 @@ void expr_gstr_print(struct expr *e, struct gstr *gs) * line with a minus. This makes expressions much easier to read. * Suitable for reverse dependency expressions. */ -void expr_gstr_print_revdep(struct expr *e, struct gstr *gs) +static void expr_print_revdep(struct expr *e, + void (*fn)(void *, struct symbol *, const char *), + void *data, tristate pr_type, const char **title) { - __expr_print(e, expr_print_gstr_helper, gs, E_NONE, true); + if (e->type == E_OR) { + expr_print_revdep(e->left.expr, fn, data, pr_type, title); + expr_print_revdep(e->right.expr, fn, data, pr_type, title); + } else if (expr_calc_value(e) == pr_type) { + if (*title) { + fn(data, NULL, *title); + *title = NULL; + } + + fn(data, NULL, " - "); + expr_print(e, fn, data, E_NONE); + fn(data, NULL, "\n"); + } +} + +void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, + tristate pr_type, const char *title) +{ + expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title); } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index c16e82e302a2..94a383b21df6 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -305,12 +305,12 @@ struct expr *expr_transform(struct expr *e); int expr_contains_symbol(struct expr *dep, struct symbol *sym); bool expr_depends_symbol(struct expr *dep, struct symbol *sym); struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); -struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2); void expr_fprint(struct expr *e, FILE *out); struct gstr; /* forward */ void expr_gstr_print(struct expr *e, struct gstr *gs); -void expr_gstr_print_revdep(struct expr *e, struct gstr *gs); +void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, + tristate pr_type, const char *title); static inline int expr_is_yes(struct expr *e) { diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 2d5ec2d0e952..f4394af6e4b8 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -68,6 +68,7 @@ struct kconf_id { enum symbol_type stype; }; +extern int yylineno; void zconfdump(FILE *out); void zconf_starthelp(void); FILE *zconf_fopen(const char *name); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 36cd3e1f1c28..5c5c1374b151 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -828,16 +828,16 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym, get_symbol_props_str(r, sym, P_SELECT, _(" Selects: ")); if (sym->rev_dep.expr) { - str_append(r, _(" Selected by: ")); - expr_gstr_print_revdep(sym->rev_dep.expr, r); - str_append(r, "\n"); + expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, " Selected by [y]:\n"); + expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, " Selected by [m]:\n"); + expr_gstr_print_revdep(sym->rev_dep.expr, r, no, " Selected by [n]:\n"); } get_symbol_props_str(r, sym, P_IMPLY, _(" Implies: ")); if (sym->implied.expr) { - str_append(r, _(" Implied by: ")); - expr_gstr_print_revdep(sym->implied.expr, r); - str_append(r, "\n"); + expr_gstr_print_revdep(sym->implied.expr, r, yes, " Implied by [y]:\n"); + expr_gstr_print_revdep(sym->implied.expr, r, mod, " Implied by [m]:\n"); + expr_gstr_print_revdep(sym->implied.expr, r, no, " Implied by [n]:\n"); } str_append(r, "\n\n"); diff --git a/scripts/kconfig/nconf.h b/scripts/kconfig/nconf.h index 0d5261705ef5..9f6f21d3b0d4 100644 --- a/scripts/kconfig/nconf.h +++ b/scripts/kconfig/nconf.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -24,8 +24,6 @@ #include #include -#include "ncurses.h" - #define max(a, b) ({\ typeof(a) _a = a;\ typeof(b) _b = b;\ diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 2220bc4b051b..f0b2e3b3102d 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -243,7 +243,7 @@ static void sym_calc_visibility(struct symbol *sym) tri = yes; if (sym->dir_dep.expr) tri = expr_calc_value(sym->dir_dep.expr); - if (tri == mod) + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->dir_dep.tri != tri) { sym->dir_dep.tri = tri; @@ -333,6 +333,27 @@ static struct symbol *sym_calc_choice(struct symbol *sym) return def_sym; } +static void sym_warn_unmet_dep(struct symbol *sym) +{ + struct gstr gs = str_new(); + + str_printf(&gs, + "\nWARNING: unmet direct dependencies detected for %s\n", + sym->name); + str_printf(&gs, + " Depends on [%c]: ", + sym->dir_dep.tri == mod ? 'm' : 'n'); + expr_gstr_print(sym->dir_dep.expr, &gs); + str_printf(&gs, "\n"); + + expr_gstr_print_revdep(sym->rev_dep.expr, &gs, yes, + " Selected by [y]:\n"); + expr_gstr_print_revdep(sym->rev_dep.expr, &gs, mod, + " Selected by [m]:\n"); + + fputs(str_get(&gs), stderr); +} + void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; @@ -403,9 +424,10 @@ void sym_calc_value(struct symbol *sym) if (!sym_is_choice(sym)) { prop = sym_get_default_prop(sym); if (prop) { - sym->flags |= SYMBOL_WRITE; newval.tri = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); + if (newval.tri != no) + sym->flags |= SYMBOL_WRITE; } if (sym->implied.tri != no) { sym->flags |= SYMBOL_WRITE; @@ -413,18 +435,8 @@ void sym_calc_value(struct symbol *sym) } } calc_newval: - if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) { - struct expr *e; - e = expr_simplify_unmet_dep(sym->rev_dep.expr, - sym->dir_dep.expr); - fprintf(stderr, "warning: ("); - expr_fprint(e, stderr); - fprintf(stderr, ") selects %s which has unmet direct dependencies (", - sym->name); - expr_fprint(sym->dir_dep.expr, stderr); - fprintf(stderr, ")\n"); - expr_free(e); - } + if (sym->dir_dep.tri < sym->rev_dep.tri) + sym_warn_unmet_dep(sym); newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); } if (newval.tri == mod && diff --git a/scripts/kconfig/tests/auto_submenu/Kconfig b/scripts/kconfig/tests/auto_submenu/Kconfig new file mode 100644 index 000000000000..c17bf2caa7e6 --- /dev/null +++ b/scripts/kconfig/tests/auto_submenu/Kconfig @@ -0,0 +1,50 @@ +config A + bool "A" + default y + +config A0 + bool "A0" + depends on A + default y + help + This depends on A, so should be a submenu of A. + +config A0_0 + bool "A1_0" + depends on A0 + help + Submenus are created recursively. + This should be a submenu of A0. + +config A1 + bool "A1" + depends on A + default y + help + This should line up with A0. + +choice + prompt "choice" + depends on A1 + help + Choice should become a submenu as well. + +config A1_0 + bool "A1_0" + +config A1_1 + bool "A1_1" + +endchoice + +config B + bool "B" + help + This is independent of A. + +config C + bool "C" + depends on A + help + This depends on A, but not a consecutive item, so can/should not + be a submenu. diff --git a/scripts/kconfig/tests/auto_submenu/__init__.py b/scripts/kconfig/tests/auto_submenu/__init__.py new file mode 100644 index 000000000000..32e79b85faeb --- /dev/null +++ b/scripts/kconfig/tests/auto_submenu/__init__.py @@ -0,0 +1,12 @@ +""" +Create submenu for symbols that depend on the preceding one. + +If a symbols has dependency on the preceding symbol, the menu entry +should become the submenu of the preceding one, and displayed with +deeper indentation. +""" + + +def test(conf): + assert conf.oldaskconfig() == 0 + assert conf.stdout_contains('expected_stdout') diff --git a/scripts/kconfig/tests/auto_submenu/expected_stdout b/scripts/kconfig/tests/auto_submenu/expected_stdout new file mode 100644 index 000000000000..bf5236f39a56 --- /dev/null +++ b/scripts/kconfig/tests/auto_submenu/expected_stdout @@ -0,0 +1,10 @@ +A (A) [Y/n/?] (NEW) + A0 (A0) [Y/n/?] (NEW) + A1_0 (A0_0) [N/y/?] (NEW) + A1 (A1) [Y/n/?] (NEW) + choice + > 1. A1_0 (A1_0) (NEW) + 2. A1_1 (A1_1) (NEW) + choice[1-2?]: +B (B) [N/y/?] (NEW) +C (C) [N/y/?] (NEW) diff --git a/scripts/kconfig/tests/choice/Kconfig b/scripts/kconfig/tests/choice/Kconfig new file mode 100644 index 000000000000..cc60e9ce2c03 --- /dev/null +++ b/scripts/kconfig/tests/choice/Kconfig @@ -0,0 +1,54 @@ +config MODULES + bool "Enable loadable module support" + option modules + default y + +choice + prompt "boolean choice" + default BOOL_CHOICE1 + +config BOOL_CHOICE0 + bool "choice 0" + +config BOOL_CHOICE1 + bool "choice 1" + +endchoice + +choice + prompt "optional boolean choice" + optional + default OPT_BOOL_CHOICE1 + +config OPT_BOOL_CHOICE0 + bool "choice 0" + +config OPT_BOOL_CHOICE1 + bool "choice 1" + +endchoice + +choice + prompt "tristate choice" + default TRI_CHOICE1 + +config TRI_CHOICE0 + tristate "choice 0" + +config TRI_CHOICE1 + tristate "choice 1" + +endchoice + +choice + prompt "optional tristate choice" + optional + default OPT_TRI_CHOICE1 + +config OPT_TRI_CHOICE0 + tristate "choice 0" + +config OPT_TRI_CHOICE1 + tristate "choice 1" + +endchoice diff --git a/scripts/kconfig/tests/choice/__init__.py b/scripts/kconfig/tests/choice/__init__.py new file mode 100644 index 000000000000..9edcc5262134 --- /dev/null +++ b/scripts/kconfig/tests/choice/__init__.py @@ -0,0 +1,40 @@ +""" +Basic choice tests. + +The handling of 'choice' is a bit complicated part in Kconfig. + +The behavior of 'y' choice is intuitive. If choice values are tristate, +the choice can be 'm' where each value can be enabled independently. +Also, if a choice is marked as 'optional', the whole choice can be +invisible. +""" + + +def test_oldask0(conf): + assert conf.oldaskconfig() == 0 + assert conf.stdout_contains('oldask0_expected_stdout') + + +def test_oldask1(conf): + assert conf.oldaskconfig('oldask1_config') == 0 + assert conf.stdout_contains('oldask1_expected_stdout') + + +def test_allyes(conf): + assert conf.allyesconfig() == 0 + assert conf.config_contains('allyes_expected_config') + + +def test_allmod(conf): + assert conf.allmodconfig() == 0 + assert conf.config_contains('allmod_expected_config') + + +def test_allno(conf): + assert conf.allnoconfig() == 0 + assert conf.config_contains('allno_expected_config') + + +def test_alldef(conf): + assert conf.alldefconfig() == 0 + assert conf.config_contains('alldef_expected_config') diff --git a/scripts/kconfig/tests/choice/alldef_expected_config b/scripts/kconfig/tests/choice/alldef_expected_config new file mode 100644 index 000000000000..7a754bf4be94 --- /dev/null +++ b/scripts/kconfig/tests/choice/alldef_expected_config @@ -0,0 +1,5 @@ +CONFIG_MODULES=y +# CONFIG_BOOL_CHOICE0 is not set +CONFIG_BOOL_CHOICE1=y +# CONFIG_TRI_CHOICE0 is not set +# CONFIG_TRI_CHOICE1 is not set diff --git a/scripts/kconfig/tests/choice/allmod_expected_config b/scripts/kconfig/tests/choice/allmod_expected_config new file mode 100644 index 000000000000..f1f5dcdb7923 --- /dev/null +++ b/scripts/kconfig/tests/choice/allmod_expected_config @@ -0,0 +1,9 @@ +CONFIG_MODULES=y +# CONFIG_BOOL_CHOICE0 is not set +CONFIG_BOOL_CHOICE1=y +# CONFIG_OPT_BOOL_CHOICE0 is not set +CONFIG_OPT_BOOL_CHOICE1=y +CONFIG_TRI_CHOICE0=m +CONFIG_TRI_CHOICE1=m +CONFIG_OPT_TRI_CHOICE0=m +CONFIG_OPT_TRI_CHOICE1=m diff --git a/scripts/kconfig/tests/choice/allno_expected_config b/scripts/kconfig/tests/choice/allno_expected_config new file mode 100644 index 000000000000..b88ee7a43136 --- /dev/null +++ b/scripts/kconfig/tests/choice/allno_expected_config @@ -0,0 +1,5 @@ +# CONFIG_MODULES is not set +# CONFIG_BOOL_CHOICE0 is not set +CONFIG_BOOL_CHOICE1=y +# CONFIG_TRI_CHOICE0 is not set +CONFIG_TRI_CHOICE1=y diff --git a/scripts/kconfig/tests/choice/allyes_expected_config b/scripts/kconfig/tests/choice/allyes_expected_config new file mode 100644 index 000000000000..e5a062a1157c --- /dev/null +++ b/scripts/kconfig/tests/choice/allyes_expected_config @@ -0,0 +1,9 @@ +CONFIG_MODULES=y +# CONFIG_BOOL_CHOICE0 is not set +CONFIG_BOOL_CHOICE1=y +# CONFIG_OPT_BOOL_CHOICE0 is not set +CONFIG_OPT_BOOL_CHOICE1=y +# CONFIG_TRI_CHOICE0 is not set +CONFIG_TRI_CHOICE1=y +# CONFIG_OPT_TRI_CHOICE0 is not set +CONFIG_OPT_TRI_CHOICE1=y diff --git a/scripts/kconfig/tests/choice/oldask0_expected_stdout b/scripts/kconfig/tests/choice/oldask0_expected_stdout new file mode 100644 index 000000000000..b251bba9698b --- /dev/null +++ b/scripts/kconfig/tests/choice/oldask0_expected_stdout @@ -0,0 +1,10 @@ +Enable loadable module support (MODULES) [Y/n/?] (NEW) +boolean choice + 1. choice 0 (BOOL_CHOICE0) (NEW) +> 2. choice 1 (BOOL_CHOICE1) (NEW) +choice[1-2?]: +optional boolean choice [N/y/?] (NEW) +tristate choice [M/y/?] (NEW) + choice 0 (TRI_CHOICE0) [N/m/?] (NEW) + choice 1 (TRI_CHOICE1) [N/m/?] (NEW) +optional tristate choice [N/m/y/?] (NEW) diff --git a/scripts/kconfig/tests/choice/oldask1_config b/scripts/kconfig/tests/choice/oldask1_config new file mode 100644 index 000000000000..b67bfe3c641f --- /dev/null +++ b/scripts/kconfig/tests/choice/oldask1_config @@ -0,0 +1,2 @@ +# CONFIG_MODULES is not set +CONFIG_OPT_BOOL_CHOICE0=y diff --git a/scripts/kconfig/tests/choice/oldask1_expected_stdout b/scripts/kconfig/tests/choice/oldask1_expected_stdout new file mode 100644 index 000000000000..c2125e9bf96a --- /dev/null +++ b/scripts/kconfig/tests/choice/oldask1_expected_stdout @@ -0,0 +1,15 @@ +Enable loadable module support (MODULES) [N/y/?] +boolean choice + 1. choice 0 (BOOL_CHOICE0) (NEW) +> 2. choice 1 (BOOL_CHOICE1) (NEW) +choice[1-2?]: +optional boolean choice [Y/n/?] (NEW) +optional boolean choice +> 1. choice 0 (OPT_BOOL_CHOICE0) + 2. choice 1 (OPT_BOOL_CHOICE1) (NEW) +choice[1-2?]: +tristate choice + 1. choice 0 (TRI_CHOICE0) (NEW) +> 2. choice 1 (TRI_CHOICE1) (NEW) +choice[1-2?]: +optional tristate choice [N/y/?] diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig b/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig new file mode 100644 index 000000000000..11ac25c26040 --- /dev/null +++ b/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig @@ -0,0 +1,19 @@ +config MODULES + def_bool y + option modules + +config DEP + tristate + default m + +choice + prompt "Tristate Choice" + +config CHOICE0 + tristate "Choice 0" + +config CHOICE1 + tristate "Choice 1" + depends on DEP + +endchoice diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py b/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py new file mode 100644 index 000000000000..f8d728c7b101 --- /dev/null +++ b/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py @@ -0,0 +1,15 @@ +""" +Hide tristate choice values with mod dependency in y choice. + +If tristate choice values depend on symbols set to 'm', they should be +hidden when the choice containing them is changed from 'm' to 'y' +(i.e. exclusive choice). + +Related Linux commit: fa64e5f6a35efd5e77d639125d973077ca506074 +""" + + +def test(conf): + assert conf.oldaskconfig('config', 'y') == 0 + assert conf.config_contains('expected_config') + assert conf.stdout_contains('expected_stdout') diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/config b/scripts/kconfig/tests/choice_value_with_m_dep/config new file mode 100644 index 000000000000..3a126b7a2546 --- /dev/null +++ b/scripts/kconfig/tests/choice_value_with_m_dep/config @@ -0,0 +1,2 @@ +CONFIG_CHOICE0=m +CONFIG_CHOICE1=m diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/expected_config b/scripts/kconfig/tests/choice_value_with_m_dep/expected_config new file mode 100644 index 000000000000..4d07b449540e --- /dev/null +++ b/scripts/kconfig/tests/choice_value_with_m_dep/expected_config @@ -0,0 +1,3 @@ +CONFIG_MODULES=y +CONFIG_DEP=m +CONFIG_CHOICE0=y diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout b/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout new file mode 100644 index 000000000000..2b50ab65c86a --- /dev/null +++ b/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout @@ -0,0 +1,4 @@ +Tristate Choice [M/y/?] y +Tristate Choice +> 1. Choice 0 (CHOICE0) +choice[1]: 1 diff --git a/scripts/kconfig/tests/conftest.py b/scripts/kconfig/tests/conftest.py new file mode 100644 index 000000000000..0345ef6e3273 --- /dev/null +++ b/scripts/kconfig/tests/conftest.py @@ -0,0 +1,291 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2018 Masahiro Yamada +# + +""" +Kconfig unit testing framework. + +This provides fixture functions commonly used from test files. +""" + +import os +import pytest +import shutil +import subprocess +import tempfile + +CONF_PATH = os.path.abspath(os.path.join('scripts', 'kconfig', 'conf')) + + +class Conf: + """Kconfig runner and result checker. + + This class provides methods to run text-based interface of Kconfig + (scripts/kconfig/conf) and retrieve the resulted configuration, + stdout, and stderr. It also provides methods to compare those + results with expectations. + """ + + def __init__(self, request): + """Create a new Conf instance. + + request: object to introspect the requesting test module + """ + # the directory of the test being run + self._test_dir = os.path.dirname(str(request.fspath)) + + # runners + def _run_conf(self, mode, dot_config=None, out_file='.config', + interactive=False, in_keys=None, extra_env={}): + """Run text-based Kconfig executable and save the result. + + mode: input mode option (--oldaskconfig, --defconfig= etc.) + dot_config: .config file to use for configuration base + out_file: file name to contain the output config data + interactive: flag to specify the interactive mode + in_keys: key inputs for interactive modes + extra_env: additional environments + returncode: exit status of the Kconfig executable + """ + command = [CONF_PATH, mode, 'Kconfig'] + + # Override 'srctree' environment to make the test as the top directory + extra_env['srctree'] = self._test_dir + + # Run Kconfig in a temporary directory. + # This directory is automatically removed when done. + with tempfile.TemporaryDirectory() as temp_dir: + + # if .config is given, copy it to the working directory + if dot_config: + shutil.copyfile(os.path.join(self._test_dir, dot_config), + os.path.join(temp_dir, '.config')) + + ps = subprocess.Popen(command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=temp_dir, + env=dict(os.environ, **extra_env)) + + # If input key sequence is given, feed it to stdin. + if in_keys: + ps.stdin.write(in_keys.encode('utf-8')) + + while ps.poll() is None: + # For interactive modes such as oldaskconfig, oldconfig, + # send 'Enter' key until the program finishes. + if interactive: + ps.stdin.write(b'\n') + + self.retcode = ps.returncode + self.stdout = ps.stdout.read().decode() + self.stderr = ps.stderr.read().decode() + + # Retrieve the resulted config data only when .config is supposed + # to exist. If the command fails, the .config does not exist. + # 'listnewconfig' does not produce .config in the first place. + if self.retcode == 0 and out_file: + with open(os.path.join(temp_dir, out_file)) as f: + self.config = f.read() + else: + self.config = None + + # Logging: + # Pytest captures the following information by default. In failure + # of tests, the captured log will be displayed. This will be useful to + # figure out what has happened. + + print("[command]\n{}\n".format(' '.join(command))) + + print("[retcode]\n{}\n".format(self.retcode)) + + print("[stdout]") + print(self.stdout) + + print("[stderr]") + print(self.stderr) + + if self.config is not None: + print("[output for '{}']".format(out_file)) + print(self.config) + + return self.retcode + + def oldaskconfig(self, dot_config=None, in_keys=None): + """Run oldaskconfig. + + dot_config: .config file to use for configuration base (optional) + in_key: key inputs (optional) + returncode: exit status of the Kconfig executable + """ + return self._run_conf('--oldaskconfig', dot_config=dot_config, + interactive=True, in_keys=in_keys) + + def oldconfig(self, dot_config=None, in_keys=None): + """Run oldconfig. + + dot_config: .config file to use for configuration base (optional) + in_key: key inputs (optional) + returncode: exit status of the Kconfig executable + """ + return self._run_conf('--oldconfig', dot_config=dot_config, + interactive=True, in_keys=in_keys) + + def olddefconfig(self, dot_config=None): + """Run olddefconfig. + + dot_config: .config file to use for configuration base (optional) + returncode: exit status of the Kconfig executable + """ + return self._run_conf('--olddefconfig', dot_config=dot_config) + + def defconfig(self, defconfig): + """Run defconfig. + + defconfig: defconfig file for input + returncode: exit status of the Kconfig executable + """ + defconfig_path = os.path.join(self._test_dir, defconfig) + return self._run_conf('--defconfig={}'.format(defconfig_path)) + + def _allconfig(self, mode, all_config): + if all_config: + all_config_path = os.path.join(self._test_dir, all_config) + extra_env = {'KCONFIG_ALLCONFIG': all_config_path} + else: + extra_env = {} + + return self._run_conf('--{}config'.format(mode), extra_env=extra_env) + + def allyesconfig(self, all_config=None): + """Run allyesconfig. + + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) + returncode: exit status of the Kconfig executable + """ + return self._allconfig('allyes', all_config) + + def allmodconfig(self, all_config=None): + """Run allmodconfig. + + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) + returncode: exit status of the Kconfig executable + """ + return self._allconfig('allmod', all_config) + + def allnoconfig(self, all_config=None): + """Run allnoconfig. + + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) + returncode: exit status of the Kconfig executable + """ + return self._allconfig('allno', all_config) + + def alldefconfig(self, all_config=None): + """Run alldefconfig. + + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) + returncode: exit status of the Kconfig executable + """ + return self._allconfig('alldef', all_config) + + def randconfig(self, all_config=None): + """Run randconfig. + + all_config: fragment config file for KCONFIG_ALLCONFIG (optional) + returncode: exit status of the Kconfig executable + """ + return self._allconfig('rand', all_config) + + def savedefconfig(self, dot_config): + """Run savedefconfig. + + dot_config: .config file for input + returncode: exit status of the Kconfig executable + """ + return self._run_conf('--savedefconfig', out_file='defconfig') + + def listnewconfig(self, dot_config=None): + """Run listnewconfig. + + dot_config: .config file to use for configuration base (optional) + returncode: exit status of the Kconfig executable + """ + return self._run_conf('--listnewconfig', dot_config=dot_config, + out_file=None) + + # checkers + def _read_and_compare(self, compare, expected): + """Compare the result with expectation. + + compare: function to compare the result with expectation + expected: file that contains the expected data + """ + with open(os.path.join(self._test_dir, expected)) as f: + expected_data = f.read() + return compare(self, expected_data) + + def _contains(self, attr, expected): + return self._read_and_compare( + lambda s, e: getattr(s, attr).find(e) >= 0, + expected) + + def _matches(self, attr, expected): + return self._read_and_compare(lambda s, e: getattr(s, attr) == e, + expected) + + def config_contains(self, expected): + """Check if resulted configuration contains expected data. + + expected: file that contains the expected data + returncode: True if result contains the expected data, False otherwise + """ + return self._contains('config', expected) + + def config_matches(self, expected): + """Check if resulted configuration exactly matches expected data. + + expected: file that contains the expected data + returncode: True if result matches the expected data, False otherwise + """ + return self._matches('config', expected) + + def stdout_contains(self, expected): + """Check if resulted stdout contains expected data. + + expected: file that contains the expected data + returncode: True if result contains the expected data, False otherwise + """ + return self._contains('stdout', expected) + + def stdout_matches(self, expected): + """Check if resulted stdout exactly matches expected data. + + expected: file that contains the expected data + returncode: True if result matches the expected data, False otherwise + """ + return self._matches('stdout', expected) + + def stderr_contains(self, expected): + """Check if resulted stderr contains expected data. + + expected: file that contains the expected data + returncode: True if result contains the expected data, False otherwise + """ + return self._contains('stderr', expected) + + def stderr_matches(self, expected): + """Check if resulted stderr exactly matches expected data. + + expected: file that contains the expected data + returncode: True if result matches the expected data, False otherwise + """ + return self._matches('stderr', expected) + + +@pytest.fixture(scope="module") +def conf(request): + """Create a Conf instance and provide it to test functions.""" + return Conf(request) diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig b/scripts/kconfig/tests/err_recursive_inc/Kconfig new file mode 100644 index 000000000000..0e4c8750ab65 --- /dev/null +++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig @@ -0,0 +1 @@ +source "Kconfig.inc1" diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc1 b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc1 new file mode 100644 index 000000000000..00e408d653fc --- /dev/null +++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc1 @@ -0,0 +1,4 @@ + + + +source "Kconfig.inc2" diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc2 b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc2 new file mode 100644 index 000000000000..349ea2db15dc --- /dev/null +++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc2 @@ -0,0 +1,3 @@ + + +source "Kconfig.inc3" diff --git a/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc3 b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc3 new file mode 100644 index 000000000000..0e4c8750ab65 --- /dev/null +++ b/scripts/kconfig/tests/err_recursive_inc/Kconfig.inc3 @@ -0,0 +1 @@ +source "Kconfig.inc1" diff --git a/scripts/kconfig/tests/err_recursive_inc/__init__.py b/scripts/kconfig/tests/err_recursive_inc/__init__.py new file mode 100644 index 000000000000..0e4c839c54aa --- /dev/null +++ b/scripts/kconfig/tests/err_recursive_inc/__init__.py @@ -0,0 +1,10 @@ +""" +Detect recursive inclusion error. + +If recursive inclusion is detected, it should fail with error messages. +""" + + +def test(conf): + assert conf.oldaskconfig() != 0 + assert conf.stderr_contains('expected_stderr') diff --git a/scripts/kconfig/tests/err_recursive_inc/expected_stderr b/scripts/kconfig/tests/err_recursive_inc/expected_stderr new file mode 100644 index 000000000000..6b582eee2176 --- /dev/null +++ b/scripts/kconfig/tests/err_recursive_inc/expected_stderr @@ -0,0 +1,6 @@ +Recursive inclusion detected. +Inclusion path: + current file : Kconfig.inc1 + included from: Kconfig.inc3:1 + included from: Kconfig.inc2:3 + included from: Kconfig.inc1:4 diff --git a/scripts/kconfig/tests/inter_choice/Kconfig b/scripts/kconfig/tests/inter_choice/Kconfig new file mode 100644 index 000000000000..e44449f075df --- /dev/null +++ b/scripts/kconfig/tests/inter_choice/Kconfig @@ -0,0 +1,23 @@ +config MODULES + def_bool y + option modules + +choice + prompt "Choice" + +config CHOICE_VAL0 + tristate "Choice 0" + +config CHOIVE_VAL1 + tristate "Choice 1" + +endchoice + +choice + prompt "Another choice" + depends on CHOICE_VAL0 + +config DUMMY + bool "dummy" + +endchoice diff --git a/scripts/kconfig/tests/inter_choice/__init__.py b/scripts/kconfig/tests/inter_choice/__init__.py new file mode 100644 index 000000000000..5c7fc365ed40 --- /dev/null +++ b/scripts/kconfig/tests/inter_choice/__init__.py @@ -0,0 +1,14 @@ +""" +Do not affect user-assigned choice value by another choice. + +Handling of state flags for choices is complecated. In old days, +the defconfig result of a choice could be affected by another choice +if those choices interact by 'depends on', 'select', etc. + +Related Linux commit: fbe98bb9ed3dae23e320c6b113e35f129538d14a +""" + + +def test(conf): + assert conf.defconfig('defconfig') == 0 + assert conf.config_contains('expected_config') diff --git a/scripts/kconfig/tests/inter_choice/defconfig b/scripts/kconfig/tests/inter_choice/defconfig new file mode 100644 index 000000000000..162c4148e2a5 --- /dev/null +++ b/scripts/kconfig/tests/inter_choice/defconfig @@ -0,0 +1 @@ +CONFIG_CHOICE_VAL0=y diff --git a/scripts/kconfig/tests/inter_choice/expected_config b/scripts/kconfig/tests/inter_choice/expected_config new file mode 100644 index 000000000000..5dceefb054e3 --- /dev/null +++ b/scripts/kconfig/tests/inter_choice/expected_config @@ -0,0 +1,4 @@ +CONFIG_MODULES=y +CONFIG_CHOICE_VAL0=y +# CONFIG_CHOIVE_VAL1 is not set +CONFIG_DUMMY=y diff --git a/scripts/kconfig/tests/new_choice_with_dep/Kconfig b/scripts/kconfig/tests/new_choice_with_dep/Kconfig new file mode 100644 index 000000000000..53ef1b86e7bf --- /dev/null +++ b/scripts/kconfig/tests/new_choice_with_dep/Kconfig @@ -0,0 +1,37 @@ +config A + bool "A" + help + This is a new symbol. + +choice + prompt "Choice ?" + depends on A + help + "depends on A" has been newly added. + +config CHOICE_B + bool "Choice B" + +config CHOICE_C + bool "Choice C" + help + This is a new symbol, so should be asked. + +endchoice + +choice + prompt "Choice2 ?" + +config CHOICE_D + bool "Choice D" + +config CHOICE_E + bool "Choice E" + +config CHOICE_F + bool "Choice F" + depends on A + help + This is a new symbol, so should be asked. + +endchoice diff --git a/scripts/kconfig/tests/new_choice_with_dep/__init__.py b/scripts/kconfig/tests/new_choice_with_dep/__init__.py new file mode 100644 index 000000000000..f0e0ead0f32f --- /dev/null +++ b/scripts/kconfig/tests/new_choice_with_dep/__init__.py @@ -0,0 +1,14 @@ +""" +Ask new choice values when they become visible. + +If new choice values are added with new dependency, and they become +visible during user configuration, oldconfig should recognize them +as (NEW), and ask the user for choice. + +Related Linux commit: 5d09598d488f081e3be23f885ed65cbbe2d073b5 +""" + + +def test(conf): + assert conf.oldconfig('config', 'y') == 0 + assert conf.stdout_contains('expected_stdout') diff --git a/scripts/kconfig/tests/new_choice_with_dep/config b/scripts/kconfig/tests/new_choice_with_dep/config new file mode 100644 index 000000000000..47ef95d567fd --- /dev/null +++ b/scripts/kconfig/tests/new_choice_with_dep/config @@ -0,0 +1,3 @@ +CONFIG_CHOICE_B=y +# CONFIG_CHOICE_D is not set +CONFIG_CHOICE_E=y diff --git a/scripts/kconfig/tests/new_choice_with_dep/expected_stdout b/scripts/kconfig/tests/new_choice_with_dep/expected_stdout new file mode 100644 index 000000000000..74dc0bcb22bc --- /dev/null +++ b/scripts/kconfig/tests/new_choice_with_dep/expected_stdout @@ -0,0 +1,10 @@ +A (A) [N/y/?] (NEW) y + Choice ? + > 1. Choice B (CHOICE_B) + 2. Choice C (CHOICE_C) (NEW) + choice[1-2?]: +Choice2 ? + 1. Choice D (CHOICE_D) +> 2. Choice E (CHOICE_E) + 3. Choice F (CHOICE_F) (NEW) +choice[1-3?]: diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig b/scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig new file mode 100644 index 000000000000..c00b8fe54f45 --- /dev/null +++ b/scripts/kconfig/tests/no_write_if_dep_unmet/Kconfig @@ -0,0 +1,14 @@ +config A + bool "A" + +choice + prompt "Choice ?" + depends on A + +config CHOICE_B + bool "Choice B" + +config CHOICE_C + bool "Choice C" + +endchoice diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py b/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py new file mode 100644 index 000000000000..207261b0fe00 --- /dev/null +++ b/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py @@ -0,0 +1,19 @@ +""" +Do not write choice values to .config if the dependency is unmet. + +"# CONFIG_... is not set" should not be written into the .config file +for symbols with unmet dependency. + +This was not working correctly for choice values because choice needs +a bit different symbol computation. + +This checks that no unneeded "# COFIG_... is not set" is contained in +the .config file. + +Related Linux commit: cb67ab2cd2b8abd9650292c986c79901e3073a59 +""" + + +def test(conf): + assert conf.oldaskconfig('config', 'n') == 0 + assert conf.config_matches('expected_config') diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/config b/scripts/kconfig/tests/no_write_if_dep_unmet/config new file mode 100644 index 000000000000..abd280e2f616 --- /dev/null +++ b/scripts/kconfig/tests/no_write_if_dep_unmet/config @@ -0,0 +1 @@ +CONFIG_A=y diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/expected_config b/scripts/kconfig/tests/no_write_if_dep_unmet/expected_config new file mode 100644 index 000000000000..0d15e41da475 --- /dev/null +++ b/scripts/kconfig/tests/no_write_if_dep_unmet/expected_config @@ -0,0 +1,5 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux Kernel Configuration +# +# CONFIG_A is not set diff --git a/scripts/kconfig/tests/pytest.ini b/scripts/kconfig/tests/pytest.ini new file mode 100644 index 000000000000..85d7ce8e448b --- /dev/null +++ b/scripts/kconfig/tests/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +addopts = --verbose + +# Pytest requires that test files have unique names, because pytest imports +# them as top-level modules. It is silly to prefix or suffix a test file with +# the directory name that contains it. Use __init__.py for all test files. +python_files = __init__.py diff --git a/scripts/kconfig/tests/rand_nested_choice/Kconfig b/scripts/kconfig/tests/rand_nested_choice/Kconfig new file mode 100644 index 000000000000..c591d512929f --- /dev/null +++ b/scripts/kconfig/tests/rand_nested_choice/Kconfig @@ -0,0 +1,33 @@ +choice + prompt "choice" + +config A + bool "A" + +config B + bool "B" + +if B +choice + prompt "sub choice" + +config C + bool "C" + +config D + bool "D" + +if D +choice + prompt "subsub choice" + +config E + bool "E" + +endchoice +endif # D + +endchoice +endif # B + +endchoice diff --git a/scripts/kconfig/tests/rand_nested_choice/__init__.py b/scripts/kconfig/tests/rand_nested_choice/__init__.py new file mode 100644 index 000000000000..e729a4e85218 --- /dev/null +++ b/scripts/kconfig/tests/rand_nested_choice/__init__.py @@ -0,0 +1,16 @@ +""" +Set random values recursively in nested choices. + +Kconfig can create a choice-in-choice structure by using 'if' statement. +randconfig should correctly set random choice values. + +Related Linux commit: 3b9a19e08960e5cdad5253998637653e592a3c29 +""" + + +def test(conf): + for i in range(20): + assert conf.randconfig() == 0 + assert (conf.config_contains('expected_stdout0') or + conf.config_contains('expected_stdout1') or + conf.config_contains('expected_stdout2')) diff --git a/scripts/kconfig/tests/rand_nested_choice/expected_stdout0 b/scripts/kconfig/tests/rand_nested_choice/expected_stdout0 new file mode 100644 index 000000000000..05450f3d4eb5 --- /dev/null +++ b/scripts/kconfig/tests/rand_nested_choice/expected_stdout0 @@ -0,0 +1,2 @@ +CONFIG_A=y +# CONFIG_B is not set diff --git a/scripts/kconfig/tests/rand_nested_choice/expected_stdout1 b/scripts/kconfig/tests/rand_nested_choice/expected_stdout1 new file mode 100644 index 000000000000..37ab29584157 --- /dev/null +++ b/scripts/kconfig/tests/rand_nested_choice/expected_stdout1 @@ -0,0 +1,4 @@ +# CONFIG_A is not set +CONFIG_B=y +CONFIG_C=y +# CONFIG_D is not set diff --git a/scripts/kconfig/tests/rand_nested_choice/expected_stdout2 b/scripts/kconfig/tests/rand_nested_choice/expected_stdout2 new file mode 100644 index 000000000000..849ff47e9848 --- /dev/null +++ b/scripts/kconfig/tests/rand_nested_choice/expected_stdout2 @@ -0,0 +1,5 @@ +# CONFIG_A is not set +CONFIG_B=y +# CONFIG_C is not set +CONFIG_D=y +CONFIG_E=y diff --git a/scripts/kconfig/tests/warn_recursive_dep/Kconfig b/scripts/kconfig/tests/warn_recursive_dep/Kconfig new file mode 100644 index 000000000000..a65bfcb7137e --- /dev/null +++ b/scripts/kconfig/tests/warn_recursive_dep/Kconfig @@ -0,0 +1,62 @@ +# depends on itself + +config A + bool "A" + depends on A + +# select itself + +config B + bool + select B + +# depends on each other + +config C1 + bool "C1" + depends on C2 + +config C2 + bool "C2" + depends on C1 + +# depends on and select + +config D1 + bool "D1" + depends on D2 + select D2 + +config D2 + bool + +# depends on and imply +# This is not recursive dependency + +config E1 + bool "E1" + depends on E2 + imply E2 + +config E2 + bool "E2" + +# property + +config F1 + bool "F1" + default F2 + +config F2 + bool "F2" + depends on F1 + +# menu + +menu "menu depending on its content" + depends on G + +config G + bool "G" + +endmenu diff --git a/scripts/kconfig/tests/warn_recursive_dep/__init__.py b/scripts/kconfig/tests/warn_recursive_dep/__init__.py new file mode 100644 index 000000000000..adb21951ba41 --- /dev/null +++ b/scripts/kconfig/tests/warn_recursive_dep/__init__.py @@ -0,0 +1,9 @@ +""" +Warn recursive inclusion. + +Recursive dependency should be warned. +""" + +def test(conf): + assert conf.oldaskconfig() == 0 + assert conf.stderr_contains('expected_stderr') diff --git a/scripts/kconfig/tests/warn_recursive_dep/expected_stderr b/scripts/kconfig/tests/warn_recursive_dep/expected_stderr new file mode 100644 index 000000000000..3de807dd9cb2 --- /dev/null +++ b/scripts/kconfig/tests/warn_recursive_dep/expected_stderr @@ -0,0 +1,30 @@ +Kconfig:9:error: recursive dependency detected! +Kconfig:9: symbol B is selected by B +For a resolution refer to Documentation/kbuild/kconfig-language.txt +subsection "Kconfig recursive dependency limitations" + +Kconfig:3:error: recursive dependency detected! +Kconfig:3: symbol A depends on A +For a resolution refer to Documentation/kbuild/kconfig-language.txt +subsection "Kconfig recursive dependency limitations" + +Kconfig:15:error: recursive dependency detected! +Kconfig:15: symbol C1 depends on C2 +Kconfig:19: symbol C2 depends on C1 +For a resolution refer to Documentation/kbuild/kconfig-language.txt +subsection "Kconfig recursive dependency limitations" + +Kconfig:30:error: recursive dependency detected! +Kconfig:30: symbol D2 is selected by D1 +Kconfig:25: symbol D1 depends on D2 +For a resolution refer to Documentation/kbuild/kconfig-language.txt +subsection "Kconfig recursive dependency limitations" + +Kconfig:59:error: recursive dependency detected! +Kconfig:59: symbol G depends on G +For a resolution refer to Documentation/kbuild/kconfig-language.txt +subsection "Kconfig recursive dependency limitations" + +Kconfig:50:error: recursive dependency detected! +Kconfig:50: symbol F2 depends on F1 +Kconfig:48: symbol F1 default value contains F2 diff --git a/scripts/kconfig/zconf.l b/scripts/kconfig/zconf.l index 88b650eb9cc9..045093d827e1 100644 --- a/scripts/kconfig/zconf.l +++ b/scripts/kconfig/zconf.l @@ -1,5 +1,5 @@ %option nostdinit noyywrap never-interactive full ecs -%option 8bit nodefault perf-report perf-report +%option 8bit nodefault yylineno %option noinput %x COMMAND HELP STRING PARAM %{ @@ -83,7 +83,6 @@ n [A-Za-z0-9_-] [ \t]*#.*\n | [ \t]*\n { - current_file->lineno++; return T_EOL; } [ \t]*#.* @@ -104,7 +103,7 @@ n [A-Za-z0-9_-] const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); BEGIN(PARAM); current_pos.file = current_file; - current_pos.lineno = current_file->lineno; + current_pos.lineno = yylineno; if (id && id->flags & TF_COMMAND) { yylval.id = id; return id->token; @@ -116,7 +115,6 @@ n [A-Za-z0-9_-] . warn_ignored_character(*yytext); \n { BEGIN(INITIAL); - current_file->lineno++; return T_EOL; } } @@ -138,7 +136,7 @@ n [A-Za-z0-9_-] new_string(); BEGIN(STRING); } - \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + \n BEGIN(INITIAL); return T_EOL; ({n}|[/.])+ { const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); if (id && id->flags & TF_PARAM) { @@ -150,7 +148,7 @@ n [A-Za-z0-9_-] return T_WORD; } #.* /* comment */ - \\\n current_file->lineno++; + \\\n ; [[:blank:]]+ . warn_ignored_character(*yytext); <> { @@ -187,7 +185,6 @@ n [A-Za-z0-9_-] fprintf(stderr, "%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); - current_file->lineno++; BEGIN(INITIAL); return T_EOL; } @@ -220,12 +217,10 @@ n [A-Za-z0-9_-] } } [ \t]*\n/[^ \t\n] { - current_file->lineno++; zconf_endhelp(); return T_HELPTEXT; } [ \t]*\n { - current_file->lineno++; append_string("\n", 1); } [^ \t\n].* { @@ -304,7 +299,7 @@ void zconf_initscan(const char *name) memset(current_buf, 0, sizeof(*current_buf)); current_file = file_lookup(name); - current_file->lineno = 1; + yylineno = 1; } void zconf_nextfile(const char *name) @@ -325,24 +320,26 @@ void zconf_nextfile(const char *name) buf->parent = current_buf; current_buf = buf; - for (iter = current_file->parent; iter; iter = iter->parent ) { - if (!strcmp(current_file->name,iter->name) ) { + current_file->lineno = yylineno; + file->parent = current_file; + + for (iter = current_file; iter; iter = iter->parent) { + if (!strcmp(iter->name, file->name)) { fprintf(stderr, - "%s:%d: recursive inclusion detected. " - "Inclusion path:\n current file : '%s'\n", - zconf_curname(), zconf_lineno(), - zconf_curname()); - iter = current_file; + "Recursive inclusion detected.\n" + "Inclusion path:\n" + " current file : %s\n", file->name); + iter = file; do { iter = iter->parent; - fprintf(stderr, " included from: '%s:%d'\n", + fprintf(stderr, " included from: %s:%d\n", iter->name, iter->lineno - 1); - } while (strcmp(iter->name, current_file->name)); + } while (strcmp(iter->name, file->name)); exit(1); } } - file->lineno = 1; - file->parent = current_file; + + yylineno = 1; current_file = file; } @@ -351,6 +348,8 @@ static void zconf_endfile(void) struct buffer *parent; current_file = current_file->parent; + if (current_file) + yylineno = current_file->lineno; parent = current_buf->parent; if (parent) {