Kconfig updates for v4.17
- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJaw6i1AAoJED2LAQed4NsGU7oP/Rc5DJmtQXbqONvEfVskfNzL NaQekDa4QuWRCbrAdRZM+8dlAgX76kXHd0I1LnL6XeCZ2KZ7f93zxpqBKHZAteGU Y6b06tDXiW9qIdI0wzfKB3KdZo0Jc1LELv7SMRrZ7+wFXZKmhh5M0mVX17sKrQai L3wPMqiu15ve2Ya9s8F8+PGFBZfqzhOBEzYij8YtTZWFWVEfoLDDD5YDUxQNcJrS FXO+fZH5EUpoWj+JseiIPuOKASChsyeqtwqCND444IrjqDQ0TLAyJSZJhSm+6bX3 qP/yMH0K+kMMkvKp8CCnaTfwkOJ2BZo+91Ydw1mnqLdnZ8gZndnyexrBFubIv+fJ 0KxX9IyGA+RBnwArsnF1yIoryktG3xtaR5diO2p5ztd8xgptKG+PqQfJ40DHjpu4 iZNpoVPIPh669w/Dfx033t1RZVhov8Mau2dZ5RCtpvXAAS6oRe+UmaazqUGt7O2z V8ekSNL3g7FN3YYx0awXJWzX8e3BDMOcUjRvw/SI16XBk0BdHiBMrYfnRN+e3mpy FjhzZdXajJclNwMVbUOAPaQypvbBQJjBMy0ryz05ZyTPEsmJqM+WjkPSLDppnMYT 8L3C5KSqC7WKdf1bj55YdGWyfyU0P9bCO026IClnvZNZDI/bg+3gB9ocyRfZG0sL 0Q7GJF+BixbUUQeUcejm =sKto -----END PGP SIGNATURE----- Merge tag 'kconfig-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild 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 ...
This commit is contained in:
commit
147a89bc71
|
@ -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
|
||||
--------------------------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
6
Makefile
6
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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=<path> 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'
|
||||
|
|
|
@ -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 <file> New config with default defined in <file>\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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#include <curses.h>
|
||||
#include <ncurses.h>
|
||||
#include <menu.h>
|
||||
#include <panel.h>
|
||||
#include <form.h>
|
||||
|
@ -24,8 +24,6 @@
|
|||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "ncurses.h"
|
||||
|
||||
#define max(a, b) ({\
|
||||
typeof(a) _a = a;\
|
||||
typeof(b) _b = b;\
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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.
|
|
@ -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')
|
|
@ -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)
|
|
@ -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
|
|
@ -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')
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -0,0 +1,2 @@
|
|||
# CONFIG_MODULES is not set
|
||||
CONFIG_OPT_BOOL_CHOICE0=y
|
|
@ -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/?]
|
|
@ -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
|
|
@ -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')
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_CHOICE0=m
|
||||
CONFIG_CHOICE1=m
|
|
@ -0,0 +1,3 @@
|
|||
CONFIG_MODULES=y
|
||||
CONFIG_DEP=m
|
||||
CONFIG_CHOICE0=y
|
|
@ -0,0 +1,4 @@
|
|||
Tristate Choice [M/y/?] y
|
||||
Tristate Choice
|
||||
> 1. Choice 0 (CHOICE0)
|
||||
choice[1]: 1
|
|
@ -0,0 +1,291 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
#
|
||||
|
||||
"""
|
||||
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=<file> 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)
|
|
@ -0,0 +1 @@
|
|||
source "Kconfig.inc1"
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
|
||||
source "Kconfig.inc2"
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
|
||||
source "Kconfig.inc3"
|
|
@ -0,0 +1 @@
|
|||
source "Kconfig.inc1"
|
|
@ -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')
|
|
@ -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
|
|
@ -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
|
|
@ -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')
|
|
@ -0,0 +1 @@
|
|||
CONFIG_CHOICE_VAL0=y
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_MODULES=y
|
||||
CONFIG_CHOICE_VAL0=y
|
||||
# CONFIG_CHOIVE_VAL1 is not set
|
||||
CONFIG_DUMMY=y
|
|
@ -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
|
|
@ -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')
|
|
@ -0,0 +1,3 @@
|
|||
CONFIG_CHOICE_B=y
|
||||
# CONFIG_CHOICE_D is not set
|
||||
CONFIG_CHOICE_E=y
|
|
@ -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?]:
|
|
@ -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
|
|
@ -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')
|
|
@ -0,0 +1 @@
|
|||
CONFIG_A=y
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Linux Kernel Configuration
|
||||
#
|
||||
# CONFIG_A is not set
|
|
@ -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
|
|
@ -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
|
|
@ -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'))
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_A=y
|
||||
# CONFIG_B is not set
|
|
@ -0,0 +1,4 @@
|
|||
# CONFIG_A is not set
|
||||
CONFIG_B=y
|
||||
CONFIG_C=y
|
||||
# CONFIG_D is not set
|
|
@ -0,0 +1,5 @@
|
|||
# CONFIG_A is not set
|
||||
CONFIG_B=y
|
||||
# CONFIG_C is not set
|
||||
CONFIG_D=y
|
||||
CONFIG_E=y
|
|
@ -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
|
|
@ -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')
|
|
@ -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
|
|
@ -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);
|
||||
<<EOF>> {
|
||||
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue