mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-30 22:08:52 +09:00
![Eugeniu Rosca](/assets/img/avatar_default.png)
Align Kconfig to Linux 4.17-rc4 with minimal impact on non-kconfig files. Previous Kconfig sync was done by commitbf7ab1e70f
("kconfig: re-sync with Linux 4.10") and it achieved almost perfect alignment with a few (intended) exceptions, caused by below U-boot commits: [A] v2015.045f9eb22075
("kbuild: remove scripts/multiconfig.sh") [B] v2015.0720c20826ef
("Kconfig: Enable usage of escape char '\' in string values") [C] v2016.01da58dec866
("Various Makefiles: Add SPDX-License-Identifier tags") [D] v2016.035b8031ccb4
("Add more SPDX-License-Identifier tags") [E] v2016.03192bc6948b
("Fix GCC format-security errors and convert sprintfs.") Here is the list of Kconfig commits which followed the v4.10 alignment: [F] v2018.010931ed3c0d
("kconfig/symbol.c: use correct pointer type argument for sizeof") [G] v2018.031414e09b4f
("kconfig: revert change that was not needed for -Wformat-security") [H] v2018.0583d290c56f
("SPDX: Convert all of our single license tags to Linux Kernel style") Commit [F] was subsequently applied to Linux kernel as commit [I] with the same patch id, so it won't contribute to further misalignment. [I] v4.15-rc1 88127dae6ed9 ("kconfig/symbol.c: use correct pointer type argument for sizeof") Commit [G] is a Kconfig-specific revert of commit [E]. Commit [H] relocated and reformatted the license doing no functional change. In summary, the only functional change that makes U-boot Kconfig diverge from Linux Kconfig is commit [B]. After a brief analysis, the purpose of [B] seems to be placing "\n" literals in string symbols like CONFIG_AUTOBOOT_PROMPT="autoboot in %d seconds\n" in order to pass them directly to printf and expect correct output at runtime. Currently, Linux doesn't seem to have this requirement, so for the moment [B] looks like a U-boot specific feature/fix. From point of view of further Kconfig alignment and backporting efforts, it is highly desired that commits like [B] are propagated to Linux and any Kconfig fixes/features are contributed to Linux kernel first. This specific Kconfig re-sync just keeps [B] in place. Contrary to 4.10 Kconfig re-sync (which achieves zero non-kconfig changes), 4.17-rc4 re-sync does some amount of updates in Kbuild (striving to keep them at minimum), due to a number of reasons: * Kbuild is affected by the removal of Kconfig "*shipped" files and now requires flex and bison pre-installed on the host. * PYTHON{2,3} variables are defined in top-level Makefile as prerequisite for running the newly developed Kconfig unit tests. * silentoldconfig becomes an "internal implementation detail" deprecated for external use, being renamed to syncconfig. The exact non-kconfig files touched by this commit are: $ git show --format="" --stat -- ':!scripts/kconfig' .gitignore | 2 ++ Makefile | 9 +++++++-- scripts/Makefile.build | 11 +++++++++++ scripts/Makefile.lib | 41 ++++++++++++----------------------------- The imported Linux commits touching the above files are: c054be10ffdbd5 ("remove gperf left-overs from build system") 73a4f6dbe70a1b ("kbuild: add LEX and YACC variables") 033dba2ec06c47 ("kbuild: prepare to remove C files pre-generated by flex and bison") eea199b445f64c ("kbuild: remove unnecessary LEX_PREFIX and YACC_PREFIX") e71de5ee08dcb0 ("kbuild: remove remaining use of undefined YACC_PREFIX") d59fbbd09d5d6b ("kbuild: replace hardcoded bison in cmd_bison_h with $(YACC)") 911a91c39cabcb H ("kconfig: rename silentoldconfig to syncconfig") 59889300274569 (".gitignore: move *.lex.c *.tab.[ch] patterns to the top-level .gitignore") 9a8dfb394c0467 ("kbuild: clean up *.lex.c and *.tab.[ch] patterns from top-level Makefile") 833e622459432e H ("genksyms: generate lexer and parser during build instead of shipping") b23d1a241f4eb4 H ("kbuild: add %.lex.c and %.tab.[ch] to 'targets' automatically") e9781b52d4e0e3 H ("kbuild: add PYTHON2 and PYTHON3 variables") The commits marked with 'H' are assessed as "hard" (build will fail) prerequisites and the rest of them are assessed as "soft" prerequisites for the re-sync. In spite of relatively high number of non-H commits, they belong to this Kconfig update topic-wise and decrease the number of cherry pick conflicts for many commits in this series. Additional effort can be put in eliminating the soft prerequisites, if really needed. The commits which contributed to this Kconfig re-sync are listed below. Whenever a conflict resolution has been performed (mostly by hand, but sometimes automatically by git), it is revealed by the '!' sign in the second column, which means a patch id mismatch between Linux and U-boot commits: 9be3213b14d44f ("gconfig: remove misleading parentheses around a condition") ff85a1a80e0034 ("kconfig: Check for libncurses before menuconfig") ad8181060788c8 ("kconfig: fix sparse warnings in nconfig") cb77f0d623ff33 ! ("scripts: Switch to more portable Perl shebang") bb3290d91695bb ! ("Remove gperf usage from toolchain") c054be10ffdbd5 ("remove gperf left-overs from build system") b24413180f5600 ! ("License cleanup: add SPDX GPL-2.0 license identifier to files with no license") 9059a3493efea6 ! ("kconfig: fix relational operators for bool and tristate symbols") 2c37e08464a850 ("kconfig: Warn if choice default is not in choice") 33ca1a24866373 ("kconfig: Document the 'menu' struct") 52aede4ba5efd1 ("kconfig: Document the 'symbol' struct") c873443430ebd1 ("kconfig: Sync zconf.y with zconf.tab.c_shipped") 9a826842ff2fbd ("kconfig: Rename menu_check_dep() to rewrite_m()") fa8cedaef814ce ("kconfig: Clarify expression rewriting") f77850d3fe0c96 ("kconfig: Clean up modules handling and fix crash") e3b03bf29d6b99 ("kconfig: display recursive dependency resolution hint just once") 73a4f6dbe70a1b ! ("kbuild: add LEX and YACC variables") 033dba2ec06c47 ! ("kbuild: prepare to remove C files pre-generated by flex and bison") 29c833061c1d8c ("kconfig: generate lexer and parser during build instead of shipping") 26e47a3c11a25c ("kconfig: Don't leak symbol names during parsing") 24161a6711c945 ("kconfig: Don't leak 'source' filenames during parsing") bc28fe1d5ede88 ("kconfig: Don't leak 'option' arguments during parsing") 0724a7c32a54e3 ("kconfig: Don't leak main menus during parsing") ae7440ef0c8013 ("kconfig: Fix automatic menu creation mem leak") 5b1374b3b3c2fc ("kconfig: Fix expr_free() E_NOT leak") 7cf33f88e29410 ("kconfig: Fix choice symbol expression leak") 05cccce580456d ("kconfig: Document automatic submenu creation code") 0735f7e5def2ab ("kconfig: Document important expression functions") df60f4b92d3d0b ("kconfig: Remove menu_end_entry()") b92d804a51796b ("kconfig: drop 'boolean' keyword") 6479f327dea60d ("kconfig: Warn if there is more than one help text") 52e58a3caeba5d ("kconfig: make input_mode static") 5a3dc717b3c785 ("kconfig: make xfgets() really static") 84dd95d4f87a0d ("kconfig: make conf_unsaved a local variable of conf_read()") 765f4cdef6f80d ("kconfig: use default 'yy' prefix for lexer and parser") eea199b445f64c ("kbuild: remove unnecessary LEX_PREFIX and YACC_PREFIX") e71de5ee08dcb0 ("kbuild: remove remaining use of undefined YACC_PREFIX") d59fbbd09d5d6b ! ("kbuild: replace hardcoded bison in cmd_bison_h with $(YACC)") 3e41ba05b6d60c ("kconfig: Document SYMBOL_OPTIONAL logic") d3465af60f4471 ("kconfig: Clarify choice dependency propagation") 9d1a9e8bc18bea ("kconfig: Document 'if' flattening logic") b53688014e3325 ("kconfig: Clarify menu and 'if' dependency propagation") d0fd0428ecf04b ("kconfig: fix make xconfig when gettext is missing") 312ee68752faaa ("kconfig: announce removal of oldnoconfig if used") 1ccb27143360bd ("kconfig: make "Selected by:" and "Implied by:" readable") cedd55d49dee94 ! ("kconfig: Remove silentoldconfig from help and docs; fix kconfig/conf's help") 1b9eda2e4892cb ("kconfig: Warn if help text is blank") cb67ab2cd2b8ab ("kconfig: do not write choice values when their dependency becomes n") 4f208f392103e8 ("kconfig: show '?' prompt even if no help text is available") cd58a91def2acc ("kconfig: remove 'config*' pattern from .gitignnore") d2a04648a5dbc3 ("kconfig: remove check_stdin()") f3ff6fb5db68bc ("kconfig: echo stdin to stdout if either is redirected") 9e3e10c725360b ("kconfig: send error messages to stderr") d717f24d8c6808 ("kconfig: add xrealloc() helper") 523ca58b7db2e3 ("kconfig: remove const qualifier from sym_expand_string_value()") cd81fc82b93fa4 ("kconfig: add xstrdup() helper") f4bc1eefc1608e ("kconfig: set SYMBOL_AUTO to the symbol marked with defconfig_list") bf0bbdcf100322 ("kconfig: Don't leak choice names during parsing") 1a90ce36c6eff6 ("kconfig: Update ncurses package names for menuconfig") 5ae6fcc4bb82bd ("kconfig: fix line number in recursive inclusion error message") 07a422bb213adb ! ("kbuild: restore autoksyms.h touch to the top Makefile") 9a47ceec543bfb ("kconfig: clean-up reverse dependency help implementation") d9119b5925a03b ("kconfig: Print reverse dependencies in groups") f467c5640c29ad ("kconfig: only write '# CONFIG_FOO is not set' for visible symbols") 59a80b5e892dde ("kconfig: do not call check_conf() for olddefconfig") 4bb3a5b085cd6f ("kconfig: remove unneeded input_mode test in conf()") 99f0b6578bab44 ("kconfig: remove redundant input_mode test for check_conf() loop") 2aad9b89621386 ("kconfig: hide irrelevant sub-menus for oldconfig") 81d2bc2273052e ("kconfig: invoke oldconfig instead of silentoldconfig from local*config") 911a91c39cabcb ! ("kconfig: rename silentoldconfig to syncconfig") 2a61625835c7c8 ! ("kconfig: remove redundant streamline_config.pl prerequisite") 022a4bf6b59dfd ("kconfig: tests: add framework for Kconfig unit testing") 1903c511905984 ("kconfig: tests: add basic choice tests") 49ac3c0c3aa3b7 ("kconfig: tests: test automatic submenu creation") b76960c0f6b25d ("kconfig: tests: test if new symbols in choice are asked") 930c429a656fdb ("kconfig: tests: check unneeded "is not set" with unmet dependency") ee236610653ede ("kconfig: tests: check visibility of tristate choice values in y choice") beaaddb625400e ("kconfig: tests: test defconfig when two choices interact") 3e4888c2e3d77d ("kconfig: tests: test randconfig for choice in choice") 29c434f367ea7b ("kconfig: tests: test if recursive dependencies are detected") e2c75e7667c737 ("kconfig: tests: test if recursive inclusion is detected") f622f827958162 ("kconfig: warn unmet direct dependency of tristate symbols selected by y") f8f69dc0b4e070 ("kconfig: make unmet dependency warnings readable") 26561514cc9def ("kconfig: do not include both curses.h and ncurses.h for nconfig") 32a94b8b0c3e5a ("kconfig: remove duplicated file name and lineno of recursive inclusion") 379a8eb8eb1a55 ("kconfig: detect recursive inclusion earlier") 18492685e479fd ("kconfig: use yylineno option instead of manual lineno increments") 59889300274569 ! (".gitignore: move *.lex.c *.tab.[ch] patterns to the top-level .gitignore") 9a8dfb394c0467 ! ("kbuild: clean up *.lex.c and *.tab.[ch] patterns from top-level Makefile") 833e622459432e ! ("genksyms: generate lexer and parser during build instead of shipping") b23d1a241f4eb4 ! ("kbuild: add %.lex.c and %.tab.[ch] to 'targets' automatically") 17baab68d337a0 ("kconfig: extend output of 'listnewconfig'") e9781b52d4e0e3 ! ("kbuild: add PYTHON2 and PYTHON3 variables") The current Kconfig update generates below build-time warnings: YACC scripts/dtc/dtc-parser.tab.h scripts/dtc/dtc-parser.y: warning: 3 shift/reduce conflicts [-Wconflicts-sr] YACC scripts/dtc/dtc-parser.tab.c scripts/dtc/dtc-parser.y: warning: 3 shift/reduce conflicts [-Wconflicts-sr] This seems to happen because the Kbuild updates apparently didn't make room for both "*shipped"-based builds and flex/bison-based builds. A similar problem has been reported for genksyms parser in v4.17-rc1 commit 833e622459432e ("genksyms: generate lexer and parser during build instead of shipping"). I have figured out empirically that the warnings are healed after updating the in-tree U-boot DTC to upstream v1.4.6-9, same as done by Linux v4.17-rc1 commit 9130ba88464032 ("scripts/dtc: Update to upstream version v1.4.6-9-gaadd0b65c987"). Whether fixing the DTC-related yacc warnings should be done together with the Kconfig re-sync, I would like to hear from community. My testing was limited to: - make defconfig all - make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- r8a7795_ulcb_defconfig all - comparing .config before and after the re-sync - running the newly imported Kconfig unit tests as seen below: $ make testconfig Tested-by: Petr Vorel <petr.vorel@gmail.com> ============================= test session starts ============================= scripts/kconfig/tests/auto_submenu/__init__.py::test PASSED [ 7%] scripts/kconfig/tests/choice/__init__.py::test_oldask0 PASSED [ 14%] scripts/kconfig/tests/choice/__init__.py::test_oldask1 PASSED [ 21%] scripts/kconfig/tests/choice/__init__.py::test_allyes PASSED [ 28%] scripts/kconfig/tests/choice/__init__.py::test_allmod PASSED [ 35%] scripts/kconfig/tests/choice/__init__.py::test_allno PASSED [ 42%] scripts/kconfig/tests/choice/__init__.py::test_alldef PASSED [ 50%] scripts/kconfig/tests/choice_value_with_m_dep/__init__.py::test PASSED [ 57%] scripts/kconfig/tests/err_recursive_inc/__init__.py::test PASSED [ 64%] scripts/kconfig/tests/inter_choice/__init__.py::test PASSED [ 71%] scripts/kconfig/tests/new_choice_with_dep/__init__.py::test PASSED [ 78%] scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py::test PASSED [ 85%] scripts/kconfig/tests/rand_nested_choice/__init__.py::test PASSED [ 92%] scripts/kconfig/tests/warn_recursive_dep/__init__.py::test PASSED [100%] ========================== 14 passed in 0.34 seconds ========================== Signed-off-by: Eugeniu Rosca <erosca@de.adit-jv.com> Reviewed-by: Masahiro Yamada <yamada.masahiro@socionext.com> Tested-by: Petr Vorel <petr.vorel@gmail.com>
1433 lines
31 KiB
C
1433 lines
31 KiB
C
/*
|
|
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
|
|
* Released under the terms of the GNU GPL v2.0.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <regex.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include "lkc.h"
|
|
|
|
struct symbol symbol_yes = {
|
|
.name = "y",
|
|
.curr = { "y", yes },
|
|
.flags = SYMBOL_CONST|SYMBOL_VALID,
|
|
}, symbol_mod = {
|
|
.name = "m",
|
|
.curr = { "m", mod },
|
|
.flags = SYMBOL_CONST|SYMBOL_VALID,
|
|
}, symbol_no = {
|
|
.name = "n",
|
|
.curr = { "n", no },
|
|
.flags = SYMBOL_CONST|SYMBOL_VALID,
|
|
}, symbol_empty = {
|
|
.name = "",
|
|
.curr = { "", no },
|
|
.flags = SYMBOL_VALID,
|
|
};
|
|
|
|
struct symbol *sym_defconfig_list;
|
|
struct symbol *modules_sym;
|
|
tristate modules_val;
|
|
|
|
struct expr *sym_env_list;
|
|
|
|
static void sym_add_default(struct symbol *sym, const char *def)
|
|
{
|
|
struct property *prop = prop_alloc(P_DEFAULT, sym);
|
|
|
|
prop->expr = expr_alloc_symbol(sym_lookup(def, SYMBOL_CONST));
|
|
}
|
|
|
|
void sym_init(void)
|
|
{
|
|
struct symbol *sym;
|
|
struct utsname uts;
|
|
static bool inited = false;
|
|
|
|
if (inited)
|
|
return;
|
|
inited = true;
|
|
|
|
uname(&uts);
|
|
|
|
sym = sym_lookup("UNAME_RELEASE", 0);
|
|
sym->type = S_STRING;
|
|
sym->flags |= SYMBOL_AUTO;
|
|
sym_add_default(sym, uts.release);
|
|
}
|
|
|
|
enum symbol_type sym_get_type(struct symbol *sym)
|
|
{
|
|
enum symbol_type type = sym->type;
|
|
|
|
if (type == S_TRISTATE) {
|
|
if (sym_is_choice_value(sym) && sym->visible == yes)
|
|
type = S_BOOLEAN;
|
|
else if (modules_val == no)
|
|
type = S_BOOLEAN;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
const char *sym_type_name(enum symbol_type type)
|
|
{
|
|
switch (type) {
|
|
case S_BOOLEAN:
|
|
return "bool";
|
|
case S_TRISTATE:
|
|
return "tristate";
|
|
case S_INT:
|
|
return "integer";
|
|
case S_HEX:
|
|
return "hex";
|
|
case S_STRING:
|
|
return "string";
|
|
case S_UNKNOWN:
|
|
return "unknown";
|
|
case S_OTHER:
|
|
break;
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
struct property *sym_get_choice_prop(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
|
|
for_all_choices(sym, prop)
|
|
return prop;
|
|
return NULL;
|
|
}
|
|
|
|
struct property *sym_get_env_prop(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
|
|
for_all_properties(sym, prop, P_ENV)
|
|
return prop;
|
|
return NULL;
|
|
}
|
|
|
|
static struct property *sym_get_default_prop(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
|
|
for_all_defaults(sym, prop) {
|
|
prop->visible.tri = expr_calc_value(prop->visible.expr);
|
|
if (prop->visible.tri != no)
|
|
return prop;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct property *sym_get_range_prop(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
|
|
for_all_properties(sym, prop, P_RANGE) {
|
|
prop->visible.tri = expr_calc_value(prop->visible.expr);
|
|
if (prop->visible.tri != no)
|
|
return prop;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static long long sym_get_range_val(struct symbol *sym, int base)
|
|
{
|
|
sym_calc_value(sym);
|
|
switch (sym->type) {
|
|
case S_INT:
|
|
base = 10;
|
|
break;
|
|
case S_HEX:
|
|
base = 16;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return strtoll(sym->curr.val, NULL, base);
|
|
}
|
|
|
|
static void sym_validate_range(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
int base;
|
|
long long val, val2;
|
|
char str[64];
|
|
|
|
switch (sym->type) {
|
|
case S_INT:
|
|
base = 10;
|
|
break;
|
|
case S_HEX:
|
|
base = 16;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
prop = sym_get_range_prop(sym);
|
|
if (!prop)
|
|
return;
|
|
val = strtoll(sym->curr.val, NULL, base);
|
|
val2 = sym_get_range_val(prop->expr->left.sym, base);
|
|
if (val >= val2) {
|
|
val2 = sym_get_range_val(prop->expr->right.sym, base);
|
|
if (val <= val2)
|
|
return;
|
|
}
|
|
if (sym->type == S_INT)
|
|
sprintf(str, "%lld", val2);
|
|
else
|
|
sprintf(str, "0x%llx", val2);
|
|
sym->curr.val = xstrdup(str);
|
|
}
|
|
|
|
static void sym_set_changed(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
|
|
sym->flags |= SYMBOL_CHANGED;
|
|
for (prop = sym->prop; prop; prop = prop->next) {
|
|
if (prop->menu)
|
|
prop->menu->flags |= MENU_CHANGED;
|
|
}
|
|
}
|
|
|
|
static void sym_set_all_changed(void)
|
|
{
|
|
struct symbol *sym;
|
|
int i;
|
|
|
|
for_all_symbols(i, sym)
|
|
sym_set_changed(sym);
|
|
}
|
|
|
|
static void sym_calc_visibility(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
struct symbol *choice_sym = NULL;
|
|
tristate tri;
|
|
|
|
/* any prompt visible? */
|
|
tri = no;
|
|
|
|
if (sym_is_choice_value(sym))
|
|
choice_sym = prop_get_symbol(sym_get_choice_prop(sym));
|
|
|
|
for_all_prompts(sym, prop) {
|
|
prop->visible.tri = expr_calc_value(prop->visible.expr);
|
|
/*
|
|
* Tristate choice_values with visibility 'mod' are
|
|
* not visible if the corresponding choice's value is
|
|
* 'yes'.
|
|
*/
|
|
if (choice_sym && sym->type == S_TRISTATE &&
|
|
prop->visible.tri == mod && choice_sym->curr.tri == yes)
|
|
prop->visible.tri = no;
|
|
|
|
tri = EXPR_OR(tri, prop->visible.tri);
|
|
}
|
|
if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
|
|
tri = yes;
|
|
if (sym->visible != tri) {
|
|
sym->visible = tri;
|
|
sym_set_changed(sym);
|
|
}
|
|
if (sym_is_choice_value(sym))
|
|
return;
|
|
/* defaulting to "yes" if no explicit "depends on" are given */
|
|
tri = yes;
|
|
if (sym->dir_dep.expr)
|
|
tri = expr_calc_value(sym->dir_dep.expr);
|
|
if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
|
|
tri = yes;
|
|
if (sym->dir_dep.tri != tri) {
|
|
sym->dir_dep.tri = tri;
|
|
sym_set_changed(sym);
|
|
}
|
|
tri = no;
|
|
if (sym->rev_dep.expr)
|
|
tri = expr_calc_value(sym->rev_dep.expr);
|
|
if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
|
|
tri = yes;
|
|
if (sym->rev_dep.tri != tri) {
|
|
sym->rev_dep.tri = tri;
|
|
sym_set_changed(sym);
|
|
}
|
|
tri = no;
|
|
if (sym->implied.expr && sym->dir_dep.tri != no)
|
|
tri = expr_calc_value(sym->implied.expr);
|
|
if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
|
|
tri = yes;
|
|
if (sym->implied.tri != tri) {
|
|
sym->implied.tri = tri;
|
|
sym_set_changed(sym);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the default symbol for a choice.
|
|
* First try the default values for the choice symbol
|
|
* Next locate the first visible choice value
|
|
* Return NULL if none was found
|
|
*/
|
|
struct symbol *sym_choice_default(struct symbol *sym)
|
|
{
|
|
struct symbol *def_sym;
|
|
struct property *prop;
|
|
struct expr *e;
|
|
|
|
/* any of the defaults visible? */
|
|
for_all_defaults(sym, prop) {
|
|
prop->visible.tri = expr_calc_value(prop->visible.expr);
|
|
if (prop->visible.tri == no)
|
|
continue;
|
|
def_sym = prop_get_symbol(prop);
|
|
if (def_sym->visible != no)
|
|
return def_sym;
|
|
}
|
|
|
|
/* just get the first visible value */
|
|
prop = sym_get_choice_prop(sym);
|
|
expr_list_for_each_sym(prop->expr, e, def_sym)
|
|
if (def_sym->visible != no)
|
|
return def_sym;
|
|
|
|
/* failed to locate any defaults */
|
|
return NULL;
|
|
}
|
|
|
|
static struct symbol *sym_calc_choice(struct symbol *sym)
|
|
{
|
|
struct symbol *def_sym;
|
|
struct property *prop;
|
|
struct expr *e;
|
|
int flags;
|
|
|
|
/* first calculate all choice values' visibilities */
|
|
flags = sym->flags;
|
|
prop = sym_get_choice_prop(sym);
|
|
expr_list_for_each_sym(prop->expr, e, def_sym) {
|
|
sym_calc_visibility(def_sym);
|
|
if (def_sym->visible != no)
|
|
flags &= def_sym->flags;
|
|
}
|
|
|
|
sym->flags &= flags | ~SYMBOL_DEF_USER;
|
|
|
|
/* is the user choice visible? */
|
|
def_sym = sym->def[S_DEF_USER].val;
|
|
if (def_sym && def_sym->visible != no)
|
|
return def_sym;
|
|
|
|
def_sym = sym_choice_default(sym);
|
|
|
|
if (def_sym == NULL)
|
|
/* no choice? reset tristate value */
|
|
sym->curr.tri = no;
|
|
|
|
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;
|
|
struct property *prop;
|
|
struct expr *e;
|
|
|
|
if (!sym)
|
|
return;
|
|
|
|
if (sym->flags & SYMBOL_VALID)
|
|
return;
|
|
|
|
if (sym_is_choice_value(sym) &&
|
|
sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) {
|
|
sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES;
|
|
prop = sym_get_choice_prop(sym);
|
|
sym_calc_value(prop_get_symbol(prop));
|
|
}
|
|
|
|
sym->flags |= SYMBOL_VALID;
|
|
|
|
oldval = sym->curr;
|
|
|
|
switch (sym->type) {
|
|
case S_INT:
|
|
case S_HEX:
|
|
case S_STRING:
|
|
newval = symbol_empty.curr;
|
|
break;
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
newval = symbol_no.curr;
|
|
break;
|
|
default:
|
|
sym->curr.val = sym->name;
|
|
sym->curr.tri = no;
|
|
return;
|
|
}
|
|
sym->flags &= ~SYMBOL_WRITE;
|
|
|
|
sym_calc_visibility(sym);
|
|
|
|
if (sym->visible != no)
|
|
sym->flags |= SYMBOL_WRITE;
|
|
|
|
/* set default if recursively called */
|
|
sym->curr = newval;
|
|
|
|
switch (sym_get_type(sym)) {
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
if (sym_is_choice_value(sym) && sym->visible == yes) {
|
|
prop = sym_get_choice_prop(sym);
|
|
newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
|
|
} else {
|
|
if (sym->visible != no) {
|
|
/* if the symbol is visible use the user value
|
|
* if available, otherwise try the default value
|
|
*/
|
|
if (sym_has_value(sym)) {
|
|
newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri,
|
|
sym->visible);
|
|
goto calc_newval;
|
|
}
|
|
}
|
|
if (sym->rev_dep.tri != no)
|
|
sym->flags |= SYMBOL_WRITE;
|
|
if (!sym_is_choice(sym)) {
|
|
prop = sym_get_default_prop(sym);
|
|
if (prop) {
|
|
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;
|
|
newval.tri = EXPR_OR(newval.tri, sym->implied.tri);
|
|
}
|
|
}
|
|
calc_newval:
|
|
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 &&
|
|
(sym_get_type(sym) == S_BOOLEAN || sym->implied.tri == yes))
|
|
newval.tri = yes;
|
|
break;
|
|
case S_STRING:
|
|
case S_HEX:
|
|
case S_INT:
|
|
if (sym->visible != no && sym_has_value(sym)) {
|
|
newval.val = sym->def[S_DEF_USER].val;
|
|
break;
|
|
}
|
|
prop = sym_get_default_prop(sym);
|
|
if (prop) {
|
|
struct symbol *ds = prop_get_symbol(prop);
|
|
if (ds) {
|
|
sym->flags |= SYMBOL_WRITE;
|
|
sym_calc_value(ds);
|
|
newval.val = ds->curr.val;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
sym->curr = newval;
|
|
if (sym_is_choice(sym) && newval.tri == yes)
|
|
sym->curr.val = sym_calc_choice(sym);
|
|
sym_validate_range(sym);
|
|
|
|
if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {
|
|
sym_set_changed(sym);
|
|
if (modules_sym == sym) {
|
|
sym_set_all_changed();
|
|
modules_val = modules_sym->curr.tri;
|
|
}
|
|
}
|
|
|
|
if (sym_is_choice(sym)) {
|
|
struct symbol *choice_sym;
|
|
|
|
prop = sym_get_choice_prop(sym);
|
|
expr_list_for_each_sym(prop->expr, e, choice_sym) {
|
|
if ((sym->flags & SYMBOL_WRITE) &&
|
|
choice_sym->visible != no)
|
|
choice_sym->flags |= SYMBOL_WRITE;
|
|
if (sym->flags & SYMBOL_CHANGED)
|
|
sym_set_changed(choice_sym);
|
|
}
|
|
}
|
|
|
|
if (sym->flags & SYMBOL_AUTO)
|
|
sym->flags &= ~SYMBOL_WRITE;
|
|
|
|
if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
|
|
set_all_choice_values(sym);
|
|
}
|
|
|
|
void sym_clear_all_valid(void)
|
|
{
|
|
struct symbol *sym;
|
|
int i;
|
|
|
|
for_all_symbols(i, sym)
|
|
sym->flags &= ~SYMBOL_VALID;
|
|
sym_add_change_count(1);
|
|
sym_calc_value(modules_sym);
|
|
}
|
|
|
|
bool sym_tristate_within_range(struct symbol *sym, tristate val)
|
|
{
|
|
int type = sym_get_type(sym);
|
|
|
|
if (sym->visible == no)
|
|
return false;
|
|
|
|
if (type != S_BOOLEAN && type != S_TRISTATE)
|
|
return false;
|
|
|
|
if (type == S_BOOLEAN && val == mod)
|
|
return false;
|
|
if (sym->visible <= sym->rev_dep.tri)
|
|
return false;
|
|
if (sym->implied.tri == yes && val == mod)
|
|
return false;
|
|
if (sym_is_choice_value(sym) && sym->visible == yes)
|
|
return val == yes;
|
|
return val >= sym->rev_dep.tri && val <= sym->visible;
|
|
}
|
|
|
|
bool sym_set_tristate_value(struct symbol *sym, tristate val)
|
|
{
|
|
tristate oldval = sym_get_tristate_value(sym);
|
|
|
|
if (oldval != val && !sym_tristate_within_range(sym, val))
|
|
return false;
|
|
|
|
if (!(sym->flags & SYMBOL_DEF_USER)) {
|
|
sym->flags |= SYMBOL_DEF_USER;
|
|
sym_set_changed(sym);
|
|
}
|
|
/*
|
|
* setting a choice value also resets the new flag of the choice
|
|
* symbol and all other choice values.
|
|
*/
|
|
if (sym_is_choice_value(sym) && val == yes) {
|
|
struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
|
|
struct property *prop;
|
|
struct expr *e;
|
|
|
|
cs->def[S_DEF_USER].val = sym;
|
|
cs->flags |= SYMBOL_DEF_USER;
|
|
prop = sym_get_choice_prop(cs);
|
|
for (e = prop->expr; e; e = e->left.expr) {
|
|
if (e->right.sym->visible != no)
|
|
e->right.sym->flags |= SYMBOL_DEF_USER;
|
|
}
|
|
}
|
|
|
|
sym->def[S_DEF_USER].tri = val;
|
|
if (oldval != val)
|
|
sym_clear_all_valid();
|
|
|
|
return true;
|
|
}
|
|
|
|
tristate sym_toggle_tristate_value(struct symbol *sym)
|
|
{
|
|
tristate oldval, newval;
|
|
|
|
oldval = newval = sym_get_tristate_value(sym);
|
|
do {
|
|
switch (newval) {
|
|
case no:
|
|
newval = mod;
|
|
break;
|
|
case mod:
|
|
newval = yes;
|
|
break;
|
|
case yes:
|
|
newval = no;
|
|
break;
|
|
}
|
|
if (sym_set_tristate_value(sym, newval))
|
|
break;
|
|
} while (oldval != newval);
|
|
return newval;
|
|
}
|
|
|
|
bool sym_string_valid(struct symbol *sym, const char *str)
|
|
{
|
|
signed char ch;
|
|
|
|
switch (sym->type) {
|
|
case S_STRING:
|
|
return true;
|
|
case S_INT:
|
|
ch = *str++;
|
|
if (ch == '-')
|
|
ch = *str++;
|
|
if (!isdigit(ch))
|
|
return false;
|
|
if (ch == '0' && *str != 0)
|
|
return false;
|
|
while ((ch = *str++)) {
|
|
if (!isdigit(ch))
|
|
return false;
|
|
}
|
|
return true;
|
|
case S_HEX:
|
|
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|
|
str += 2;
|
|
ch = *str++;
|
|
do {
|
|
if (!isxdigit(ch))
|
|
return false;
|
|
} while ((ch = *str++));
|
|
return true;
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
switch (str[0]) {
|
|
case 'y': case 'Y':
|
|
case 'm': case 'M':
|
|
case 'n': case 'N':
|
|
return true;
|
|
}
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool sym_string_within_range(struct symbol *sym, const char *str)
|
|
{
|
|
struct property *prop;
|
|
long long val;
|
|
|
|
switch (sym->type) {
|
|
case S_STRING:
|
|
return sym_string_valid(sym, str);
|
|
case S_INT:
|
|
if (!sym_string_valid(sym, str))
|
|
return false;
|
|
prop = sym_get_range_prop(sym);
|
|
if (!prop)
|
|
return true;
|
|
val = strtoll(str, NULL, 10);
|
|
return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
|
|
val <= sym_get_range_val(prop->expr->right.sym, 10);
|
|
case S_HEX:
|
|
if (!sym_string_valid(sym, str))
|
|
return false;
|
|
prop = sym_get_range_prop(sym);
|
|
if (!prop)
|
|
return true;
|
|
val = strtoll(str, NULL, 16);
|
|
return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
|
|
val <= sym_get_range_val(prop->expr->right.sym, 16);
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
switch (str[0]) {
|
|
case 'y': case 'Y':
|
|
return sym_tristate_within_range(sym, yes);
|
|
case 'm': case 'M':
|
|
return sym_tristate_within_range(sym, mod);
|
|
case 'n': case 'N':
|
|
return sym_tristate_within_range(sym, no);
|
|
}
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool sym_set_string_value(struct symbol *sym, const char *newval)
|
|
{
|
|
const char *oldval;
|
|
char *val;
|
|
int size;
|
|
|
|
switch (sym->type) {
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
switch (newval[0]) {
|
|
case 'y': case 'Y':
|
|
return sym_set_tristate_value(sym, yes);
|
|
case 'm': case 'M':
|
|
return sym_set_tristate_value(sym, mod);
|
|
case 'n': case 'N':
|
|
return sym_set_tristate_value(sym, no);
|
|
}
|
|
return false;
|
|
default:
|
|
;
|
|
}
|
|
|
|
if (!sym_string_within_range(sym, newval))
|
|
return false;
|
|
|
|
if (!(sym->flags & SYMBOL_DEF_USER)) {
|
|
sym->flags |= SYMBOL_DEF_USER;
|
|
sym_set_changed(sym);
|
|
}
|
|
|
|
oldval = sym->def[S_DEF_USER].val;
|
|
size = strlen(newval) + 1;
|
|
if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
|
|
size += 2;
|
|
sym->def[S_DEF_USER].val = val = xmalloc(size);
|
|
*val++ = '0';
|
|
*val++ = 'x';
|
|
} else if (!oldval || strcmp(oldval, newval))
|
|
sym->def[S_DEF_USER].val = val = xmalloc(size);
|
|
else
|
|
return true;
|
|
|
|
strcpy(val, newval);
|
|
free((void *)oldval);
|
|
sym_clear_all_valid();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Find the default value associated to a symbol.
|
|
* For tristate symbol handle the modules=n case
|
|
* in which case "m" becomes "y".
|
|
* If the symbol does not have any default then fallback
|
|
* to the fixed default values.
|
|
*/
|
|
const char *sym_get_string_default(struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
struct symbol *ds;
|
|
const char *str;
|
|
tristate val;
|
|
|
|
sym_calc_visibility(sym);
|
|
sym_calc_value(modules_sym);
|
|
val = symbol_no.curr.tri;
|
|
str = symbol_empty.curr.val;
|
|
|
|
/* If symbol has a default value look it up */
|
|
prop = sym_get_default_prop(sym);
|
|
if (prop != NULL) {
|
|
switch (sym->type) {
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
/* The visibility may limit the value from yes => mod */
|
|
val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri);
|
|
break;
|
|
default:
|
|
/*
|
|
* The following fails to handle the situation
|
|
* where a default value is further limited by
|
|
* the valid range.
|
|
*/
|
|
ds = prop_get_symbol(prop);
|
|
if (ds != NULL) {
|
|
sym_calc_value(ds);
|
|
str = (const char *)ds->curr.val;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Handle select statements */
|
|
val = EXPR_OR(val, sym->rev_dep.tri);
|
|
|
|
/* transpose mod to yes if modules are not enabled */
|
|
if (val == mod)
|
|
if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no)
|
|
val = yes;
|
|
|
|
/* transpose mod to yes if type is bool */
|
|
if (sym->type == S_BOOLEAN && val == mod)
|
|
val = yes;
|
|
|
|
/* adjust the default value if this symbol is implied by another */
|
|
if (val < sym->implied.tri)
|
|
val = sym->implied.tri;
|
|
|
|
switch (sym->type) {
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
switch (val) {
|
|
case no: return "n";
|
|
case mod: return "m";
|
|
case yes: return "y";
|
|
}
|
|
case S_INT:
|
|
case S_HEX:
|
|
return str;
|
|
case S_STRING:
|
|
return str;
|
|
case S_OTHER:
|
|
case S_UNKNOWN:
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
const char *sym_get_string_value(struct symbol *sym)
|
|
{
|
|
tristate val;
|
|
|
|
switch (sym->type) {
|
|
case S_BOOLEAN:
|
|
case S_TRISTATE:
|
|
val = sym_get_tristate_value(sym);
|
|
switch (val) {
|
|
case no:
|
|
return "n";
|
|
case mod:
|
|
sym_calc_value(modules_sym);
|
|
return (modules_sym->curr.tri == no) ? "n" : "m";
|
|
case yes:
|
|
return "y";
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
return (const char *)sym->curr.val;
|
|
}
|
|
|
|
bool sym_is_changable(struct symbol *sym)
|
|
{
|
|
return sym->visible > sym->rev_dep.tri;
|
|
}
|
|
|
|
static unsigned strhash(const char *s)
|
|
{
|
|
/* fnv32 hash */
|
|
unsigned hash = 2166136261U;
|
|
for (; *s; s++)
|
|
hash = (hash ^ *s) * 0x01000193;
|
|
return hash;
|
|
}
|
|
|
|
struct symbol *sym_lookup(const char *name, int flags)
|
|
{
|
|
struct symbol *symbol;
|
|
char *new_name;
|
|
int hash;
|
|
|
|
if (name) {
|
|
if (name[0] && !name[1]) {
|
|
switch (name[0]) {
|
|
case 'y': return &symbol_yes;
|
|
case 'm': return &symbol_mod;
|
|
case 'n': return &symbol_no;
|
|
}
|
|
}
|
|
hash = strhash(name) % SYMBOL_HASHSIZE;
|
|
|
|
for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
|
|
if (symbol->name &&
|
|
!strcmp(symbol->name, name) &&
|
|
(flags ? symbol->flags & flags
|
|
: !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE))))
|
|
return symbol;
|
|
}
|
|
new_name = xstrdup(name);
|
|
} else {
|
|
new_name = NULL;
|
|
hash = 0;
|
|
}
|
|
|
|
symbol = xmalloc(sizeof(*symbol));
|
|
memset(symbol, 0, sizeof(*symbol));
|
|
symbol->name = new_name;
|
|
symbol->type = S_UNKNOWN;
|
|
symbol->flags |= flags;
|
|
|
|
symbol->next = symbol_hash[hash];
|
|
symbol_hash[hash] = symbol;
|
|
|
|
return symbol;
|
|
}
|
|
|
|
struct symbol *sym_find(const char *name)
|
|
{
|
|
struct symbol *symbol = NULL;
|
|
int hash = 0;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
if (name[0] && !name[1]) {
|
|
switch (name[0]) {
|
|
case 'y': return &symbol_yes;
|
|
case 'm': return &symbol_mod;
|
|
case 'n': return &symbol_no;
|
|
}
|
|
}
|
|
hash = strhash(name) % SYMBOL_HASHSIZE;
|
|
|
|
for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
|
|
if (symbol->name &&
|
|
!strcmp(symbol->name, name) &&
|
|
!(symbol->flags & SYMBOL_CONST))
|
|
break;
|
|
}
|
|
|
|
return symbol;
|
|
}
|
|
|
|
/*
|
|
* Expand symbol's names embedded in the string given in argument. Symbols'
|
|
* name to be expanded shall be prefixed by a '$'. Unknown symbol expands to
|
|
* the empty string.
|
|
*/
|
|
char *sym_expand_string_value(const char *in)
|
|
{
|
|
const char *src;
|
|
char *res;
|
|
size_t reslen;
|
|
|
|
/*
|
|
* Note: 'in' might come from a token that's about to be
|
|
* freed, so make sure to always allocate a new string
|
|
*/
|
|
reslen = strlen(in) + 1;
|
|
res = xmalloc(reslen);
|
|
res[0] = '\0';
|
|
|
|
while ((src = strchr(in, '$'))) {
|
|
char *p, name[SYMBOL_MAXLENGTH];
|
|
const char *symval = "";
|
|
struct symbol *sym;
|
|
size_t newlen;
|
|
|
|
strncat(res, in, src - in);
|
|
src++;
|
|
|
|
p = name;
|
|
while (isalnum(*src) || *src == '_')
|
|
*p++ = *src++;
|
|
*p = '\0';
|
|
|
|
sym = sym_find(name);
|
|
if (sym != NULL) {
|
|
sym_calc_value(sym);
|
|
symval = sym_get_string_value(sym);
|
|
}
|
|
|
|
newlen = strlen(res) + strlen(symval) + strlen(src) + 1;
|
|
if (newlen > reslen) {
|
|
reslen = newlen;
|
|
res = xrealloc(res, reslen);
|
|
}
|
|
|
|
strcat(res, symval);
|
|
in = src;
|
|
}
|
|
strcat(res, in);
|
|
|
|
return res;
|
|
}
|
|
|
|
const char *sym_escape_string_value(const char *in)
|
|
{
|
|
const char *p;
|
|
size_t reslen;
|
|
char *res;
|
|
size_t l;
|
|
|
|
reslen = strlen(in) + strlen("\"\"") + 1;
|
|
|
|
p = in;
|
|
for (;;) {
|
|
l = strcspn(p, "\"\\");
|
|
p += l;
|
|
|
|
if (p[0] == '\0')
|
|
break;
|
|
|
|
reslen++;
|
|
p++;
|
|
}
|
|
|
|
res = xmalloc(reslen);
|
|
res[0] = '\0';
|
|
|
|
strcat(res, "\"");
|
|
|
|
p = in;
|
|
for (;;) {
|
|
l = strcspn(p, "\"\\");
|
|
strncat(res, p, l);
|
|
p += l;
|
|
|
|
if (p[0] == '\0')
|
|
break;
|
|
|
|
strcat(res, "\\");
|
|
strncat(res, p++, 1);
|
|
}
|
|
|
|
strcat(res, "\"");
|
|
return res;
|
|
}
|
|
|
|
struct sym_match {
|
|
struct symbol *sym;
|
|
off_t so, eo;
|
|
};
|
|
|
|
/* Compare matched symbols as thus:
|
|
* - first, symbols that match exactly
|
|
* - then, alphabetical sort
|
|
*/
|
|
static int sym_rel_comp(const void *sym1, const void *sym2)
|
|
{
|
|
const struct sym_match *s1 = sym1;
|
|
const struct sym_match *s2 = sym2;
|
|
int exact1, exact2;
|
|
|
|
/* Exact match:
|
|
* - if matched length on symbol s1 is the length of that symbol,
|
|
* then this symbol should come first;
|
|
* - if matched length on symbol s2 is the length of that symbol,
|
|
* then this symbol should come first.
|
|
* Note: since the search can be a regexp, both symbols may match
|
|
* exactly; if this is the case, we can't decide which comes first,
|
|
* and we fallback to sorting alphabetically.
|
|
*/
|
|
exact1 = (s1->eo - s1->so) == strlen(s1->sym->name);
|
|
exact2 = (s2->eo - s2->so) == strlen(s2->sym->name);
|
|
if (exact1 && !exact2)
|
|
return -1;
|
|
if (!exact1 && exact2)
|
|
return 1;
|
|
|
|
/* As a fallback, sort symbols alphabetically */
|
|
return strcmp(s1->sym->name, s2->sym->name);
|
|
}
|
|
|
|
struct symbol **sym_re_search(const char *pattern)
|
|
{
|
|
struct symbol *sym, **sym_arr = NULL;
|
|
struct sym_match *sym_match_arr = NULL;
|
|
int i, cnt, size;
|
|
regex_t re;
|
|
regmatch_t match[1];
|
|
|
|
cnt = size = 0;
|
|
/* Skip if empty */
|
|
if (strlen(pattern) == 0)
|
|
return NULL;
|
|
if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE))
|
|
return NULL;
|
|
|
|
for_all_symbols(i, sym) {
|
|
if (sym->flags & SYMBOL_CONST || !sym->name)
|
|
continue;
|
|
if (regexec(&re, sym->name, 1, match, 0))
|
|
continue;
|
|
if (cnt >= size) {
|
|
void *tmp;
|
|
size += 16;
|
|
tmp = realloc(sym_match_arr, size * sizeof(struct sym_match));
|
|
if (!tmp)
|
|
goto sym_re_search_free;
|
|
sym_match_arr = tmp;
|
|
}
|
|
sym_calc_value(sym);
|
|
/* As regexec returned 0, we know we have a match, so
|
|
* we can use match[0].rm_[se]o without further checks
|
|
*/
|
|
sym_match_arr[cnt].so = match[0].rm_so;
|
|
sym_match_arr[cnt].eo = match[0].rm_eo;
|
|
sym_match_arr[cnt++].sym = sym;
|
|
}
|
|
if (sym_match_arr) {
|
|
qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp);
|
|
sym_arr = malloc((cnt+1) * sizeof(struct symbol *));
|
|
if (!sym_arr)
|
|
goto sym_re_search_free;
|
|
for (i = 0; i < cnt; i++)
|
|
sym_arr[i] = sym_match_arr[i].sym;
|
|
sym_arr[cnt] = NULL;
|
|
}
|
|
sym_re_search_free:
|
|
/* sym_match_arr can be NULL if no match, but free(NULL) is OK */
|
|
free(sym_match_arr);
|
|
regfree(&re);
|
|
|
|
return sym_arr;
|
|
}
|
|
|
|
/*
|
|
* When we check for recursive dependencies we use a stack to save
|
|
* current state so we can print out relevant info to user.
|
|
* The entries are located on the call stack so no need to free memory.
|
|
* Note insert() remove() must always match to properly clear the stack.
|
|
*/
|
|
static struct dep_stack {
|
|
struct dep_stack *prev, *next;
|
|
struct symbol *sym;
|
|
struct property *prop;
|
|
struct expr *expr;
|
|
} *check_top;
|
|
|
|
static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym)
|
|
{
|
|
memset(stack, 0, sizeof(*stack));
|
|
if (check_top)
|
|
check_top->next = stack;
|
|
stack->prev = check_top;
|
|
stack->sym = sym;
|
|
check_top = stack;
|
|
}
|
|
|
|
static void dep_stack_remove(void)
|
|
{
|
|
check_top = check_top->prev;
|
|
if (check_top)
|
|
check_top->next = NULL;
|
|
}
|
|
|
|
/*
|
|
* Called when we have detected a recursive dependency.
|
|
* check_top point to the top of the stact so we use
|
|
* the ->prev pointer to locate the bottom of the stack.
|
|
*/
|
|
static void sym_check_print_recursive(struct symbol *last_sym)
|
|
{
|
|
struct dep_stack *stack;
|
|
struct symbol *sym, *next_sym;
|
|
struct menu *menu = NULL;
|
|
struct property *prop;
|
|
struct dep_stack cv_stack;
|
|
|
|
if (sym_is_choice_value(last_sym)) {
|
|
dep_stack_insert(&cv_stack, last_sym);
|
|
last_sym = prop_get_symbol(sym_get_choice_prop(last_sym));
|
|
}
|
|
|
|
for (stack = check_top; stack != NULL; stack = stack->prev)
|
|
if (stack->sym == last_sym)
|
|
break;
|
|
if (!stack) {
|
|
fprintf(stderr, "unexpected recursive dependency error\n");
|
|
return;
|
|
}
|
|
|
|
for (; stack; stack = stack->next) {
|
|
sym = stack->sym;
|
|
next_sym = stack->next ? stack->next->sym : last_sym;
|
|
prop = stack->prop;
|
|
if (prop == NULL)
|
|
prop = stack->sym->prop;
|
|
|
|
/* for choice values find the menu entry (used below) */
|
|
if (sym_is_choice(sym) || sym_is_choice_value(sym)) {
|
|
for (prop = sym->prop; prop; prop = prop->next) {
|
|
menu = prop->menu;
|
|
if (prop->menu)
|
|
break;
|
|
}
|
|
}
|
|
if (stack->sym == last_sym)
|
|
fprintf(stderr, "%s:%d:error: recursive dependency detected!\n",
|
|
prop->file->name, prop->lineno);
|
|
|
|
if (stack->expr) {
|
|
fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n",
|
|
prop->file->name, prop->lineno,
|
|
sym->name ? sym->name : "<choice>",
|
|
prop_get_type_name(prop->type),
|
|
next_sym->name ? next_sym->name : "<choice>");
|
|
} else if (stack->prop) {
|
|
fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n",
|
|
prop->file->name, prop->lineno,
|
|
sym->name ? sym->name : "<choice>",
|
|
next_sym->name ? next_sym->name : "<choice>");
|
|
} else if (sym_is_choice(sym)) {
|
|
fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n",
|
|
menu->file->name, menu->lineno,
|
|
sym->name ? sym->name : "<choice>",
|
|
next_sym->name ? next_sym->name : "<choice>");
|
|
} else if (sym_is_choice_value(sym)) {
|
|
fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n",
|
|
menu->file->name, menu->lineno,
|
|
sym->name ? sym->name : "<choice>",
|
|
next_sym->name ? next_sym->name : "<choice>");
|
|
} else {
|
|
fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n",
|
|
prop->file->name, prop->lineno,
|
|
sym->name ? sym->name : "<choice>",
|
|
next_sym->name ? next_sym->name : "<choice>");
|
|
}
|
|
}
|
|
|
|
fprintf(stderr,
|
|
"For a resolution refer to Documentation/kbuild/kconfig-language.txt\n"
|
|
"subsection \"Kconfig recursive dependency limitations\"\n"
|
|
"\n");
|
|
|
|
if (check_top == &cv_stack)
|
|
dep_stack_remove();
|
|
}
|
|
|
|
static struct symbol *sym_check_expr_deps(struct expr *e)
|
|
{
|
|
struct symbol *sym;
|
|
|
|
if (!e)
|
|
return NULL;
|
|
switch (e->type) {
|
|
case E_OR:
|
|
case E_AND:
|
|
sym = sym_check_expr_deps(e->left.expr);
|
|
if (sym)
|
|
return sym;
|
|
return sym_check_expr_deps(e->right.expr);
|
|
case E_NOT:
|
|
return sym_check_expr_deps(e->left.expr);
|
|
case E_EQUAL:
|
|
case E_GEQ:
|
|
case E_GTH:
|
|
case E_LEQ:
|
|
case E_LTH:
|
|
case E_UNEQUAL:
|
|
sym = sym_check_deps(e->left.sym);
|
|
if (sym)
|
|
return sym;
|
|
return sym_check_deps(e->right.sym);
|
|
case E_SYMBOL:
|
|
return sym_check_deps(e->left.sym);
|
|
default:
|
|
break;
|
|
}
|
|
fprintf(stderr, "Oops! How to check %d?\n", e->type);
|
|
return NULL;
|
|
}
|
|
|
|
/* return NULL when dependencies are OK */
|
|
static struct symbol *sym_check_sym_deps(struct symbol *sym)
|
|
{
|
|
struct symbol *sym2;
|
|
struct property *prop;
|
|
struct dep_stack stack;
|
|
|
|
dep_stack_insert(&stack, sym);
|
|
|
|
sym2 = sym_check_expr_deps(sym->rev_dep.expr);
|
|
if (sym2)
|
|
goto out;
|
|
|
|
for (prop = sym->prop; prop; prop = prop->next) {
|
|
if (prop->type == P_CHOICE || prop->type == P_SELECT)
|
|
continue;
|
|
stack.prop = prop;
|
|
sym2 = sym_check_expr_deps(prop->visible.expr);
|
|
if (sym2)
|
|
break;
|
|
if (prop->type != P_DEFAULT || sym_is_choice(sym))
|
|
continue;
|
|
stack.expr = prop->expr;
|
|
sym2 = sym_check_expr_deps(prop->expr);
|
|
if (sym2)
|
|
break;
|
|
stack.expr = NULL;
|
|
}
|
|
|
|
out:
|
|
dep_stack_remove();
|
|
|
|
return sym2;
|
|
}
|
|
|
|
static struct symbol *sym_check_choice_deps(struct symbol *choice)
|
|
{
|
|
struct symbol *sym, *sym2;
|
|
struct property *prop;
|
|
struct expr *e;
|
|
struct dep_stack stack;
|
|
|
|
dep_stack_insert(&stack, choice);
|
|
|
|
prop = sym_get_choice_prop(choice);
|
|
expr_list_for_each_sym(prop->expr, e, sym)
|
|
sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
|
|
|
|
choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
|
|
sym2 = sym_check_sym_deps(choice);
|
|
choice->flags &= ~SYMBOL_CHECK;
|
|
if (sym2)
|
|
goto out;
|
|
|
|
expr_list_for_each_sym(prop->expr, e, sym) {
|
|
sym2 = sym_check_sym_deps(sym);
|
|
if (sym2)
|
|
break;
|
|
}
|
|
out:
|
|
expr_list_for_each_sym(prop->expr, e, sym)
|
|
sym->flags &= ~SYMBOL_CHECK;
|
|
|
|
if (sym2 && sym_is_choice_value(sym2) &&
|
|
prop_get_symbol(sym_get_choice_prop(sym2)) == choice)
|
|
sym2 = choice;
|
|
|
|
dep_stack_remove();
|
|
|
|
return sym2;
|
|
}
|
|
|
|
struct symbol *sym_check_deps(struct symbol *sym)
|
|
{
|
|
struct symbol *sym2;
|
|
struct property *prop;
|
|
|
|
if (sym->flags & SYMBOL_CHECK) {
|
|
sym_check_print_recursive(sym);
|
|
return sym;
|
|
}
|
|
if (sym->flags & SYMBOL_CHECKED)
|
|
return NULL;
|
|
|
|
if (sym_is_choice_value(sym)) {
|
|
struct dep_stack stack;
|
|
|
|
/* for choice groups start the check with main choice symbol */
|
|
dep_stack_insert(&stack, sym);
|
|
prop = sym_get_choice_prop(sym);
|
|
sym2 = sym_check_deps(prop_get_symbol(prop));
|
|
dep_stack_remove();
|
|
} else if (sym_is_choice(sym)) {
|
|
sym2 = sym_check_choice_deps(sym);
|
|
} else {
|
|
sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
|
|
sym2 = sym_check_sym_deps(sym);
|
|
sym->flags &= ~SYMBOL_CHECK;
|
|
}
|
|
|
|
if (sym2 && sym2 == sym)
|
|
sym2 = NULL;
|
|
|
|
return sym2;
|
|
}
|
|
|
|
struct property *prop_alloc(enum prop_type type, struct symbol *sym)
|
|
{
|
|
struct property *prop;
|
|
struct property **propp;
|
|
|
|
prop = xmalloc(sizeof(*prop));
|
|
memset(prop, 0, sizeof(*prop));
|
|
prop->type = type;
|
|
prop->sym = sym;
|
|
prop->file = current_file;
|
|
prop->lineno = zconf_lineno();
|
|
|
|
/* append property to the prop list of symbol */
|
|
if (sym) {
|
|
for (propp = &sym->prop; *propp; propp = &(*propp)->next)
|
|
;
|
|
*propp = prop;
|
|
}
|
|
|
|
return prop;
|
|
}
|
|
|
|
struct symbol *prop_get_symbol(struct property *prop)
|
|
{
|
|
if (prop->expr && (prop->expr->type == E_SYMBOL ||
|
|
prop->expr->type == E_LIST))
|
|
return prop->expr->left.sym;
|
|
return NULL;
|
|
}
|
|
|
|
const char *prop_get_type_name(enum prop_type type)
|
|
{
|
|
switch (type) {
|
|
case P_PROMPT:
|
|
return "prompt";
|
|
case P_ENV:
|
|
return "env";
|
|
case P_COMMENT:
|
|
return "comment";
|
|
case P_MENU:
|
|
return "menu";
|
|
case P_DEFAULT:
|
|
return "default";
|
|
case P_CHOICE:
|
|
return "choice";
|
|
case P_SELECT:
|
|
return "select";
|
|
case P_IMPLY:
|
|
return "imply";
|
|
case P_RANGE:
|
|
return "range";
|
|
case P_SYMBOL:
|
|
return "symbol";
|
|
case P_UNKNOWN:
|
|
break;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
static void prop_add_env(const char *env)
|
|
{
|
|
struct symbol *sym, *sym2;
|
|
struct property *prop;
|
|
char *p;
|
|
|
|
sym = current_entry->sym;
|
|
sym->flags |= SYMBOL_AUTO;
|
|
for_all_properties(sym, prop, P_ENV) {
|
|
sym2 = prop_get_symbol(prop);
|
|
if (strcmp(sym2->name, env))
|
|
menu_warn(current_entry, "redefining environment symbol from %s",
|
|
sym2->name);
|
|
return;
|
|
}
|
|
|
|
prop = prop_alloc(P_ENV, sym);
|
|
prop->expr = expr_alloc_symbol(sym_lookup(env, SYMBOL_CONST));
|
|
|
|
sym_env_list = expr_alloc_one(E_LIST, sym_env_list);
|
|
sym_env_list->right.sym = sym;
|
|
|
|
p = getenv(env);
|
|
if (p)
|
|
sym_add_default(sym, p);
|
|
else
|
|
menu_warn(current_entry, "environment variable %s undefined", env);
|
|
}
|