mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
kconfig: add optional warnings for changed input values
When reading .config input, Kconfig stores user-provided values first and then resolves the final value after applying dependencies, ranges, and other constraints. If the final value differs from the user input, Kconfig already tracks that state internally, but it does not provide a focused diagnostic to show which explicit inputs were adjusted. This is particularly confusing for requested values that get forced down by unmet dependencies or clamped by ranges. Add an opt-in diagnostic controlled by KCONFIG_WARN_CHANGED_INPUT. Emit the warnings from conf_write() and conf_write_defconfig() after value resolution. Print the diagnostic to stderr directly, not through the normal message callback, so it remains visible when conf is run with -s, such as from make -s. Keep the diagnostic out of the conf_message() formatting buffer so long warning lists are not truncated, and mark processed symbols as written before the SYMBOL_WRITE check so duplicate menu nodes cannot emit duplicate warnings. Document the new environment variable and add tests for olddefconfig, savedefconfig, and the silent-conf path. Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> Tested-by: Julian Braha <julianbraha@gmail.com> Link: https://patch.msgid.link/20260611060000.23858-1-pengpeng@iscas.ac.cn Signed-off-by: Nathan Chancellor <nathan@kernel.org>
This commit is contained in:
committed by
Nathan Chancellor
parent
9a289cc425
commit
645323a7f4
@@ -59,6 +59,11 @@ Environment variables for ``*config``:
|
||||
This environment variable makes Kconfig warn about all unrecognized
|
||||
symbols in the config input.
|
||||
|
||||
``KCONFIG_WARN_CHANGED_INPUT``
|
||||
If set to a non-blank value, Kconfig prints optional warnings for
|
||||
user-provided values that change after Kconfig resolves dependencies
|
||||
or applies other constraints such as ranges.
|
||||
|
||||
``KCONFIG_WERROR``
|
||||
If set, Kconfig treats warnings as errors.
|
||||
|
||||
|
||||
@@ -206,6 +206,78 @@ static void conf_message(const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void conf_changed_input_warning(const char *s)
|
||||
{
|
||||
fputs(s, stderr);
|
||||
}
|
||||
|
||||
static bool conf_warn_changed_input_enabled(void)
|
||||
{
|
||||
const char *env = getenv("KCONFIG_WARN_CHANGED_INPUT");
|
||||
|
||||
return env && *env;
|
||||
}
|
||||
|
||||
static const char *sym_get_user_value_string(struct symbol *sym)
|
||||
{
|
||||
switch (sym->type) {
|
||||
case S_BOOLEAN:
|
||||
case S_TRISTATE:
|
||||
switch (sym->def[S_DEF_USER].tri) {
|
||||
case yes:
|
||||
return "y";
|
||||
case mod:
|
||||
return "m";
|
||||
default:
|
||||
return "n";
|
||||
}
|
||||
default:
|
||||
return sym->def[S_DEF_USER].val ?: "";
|
||||
}
|
||||
}
|
||||
|
||||
static bool sym_user_value_changed(struct symbol *sym)
|
||||
{
|
||||
if (!sym_has_value(sym) || sym->type == S_UNKNOWN)
|
||||
return false;
|
||||
|
||||
switch (sym->type) {
|
||||
case S_BOOLEAN:
|
||||
case S_TRISTATE:
|
||||
return sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym);
|
||||
default:
|
||||
return strcmp(sym_get_user_value_string(sym),
|
||||
sym_get_string_value(sym));
|
||||
}
|
||||
}
|
||||
|
||||
static void conf_clear_written_flags(void)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
for_all_symbols(sym)
|
||||
sym->flags &= ~SYMBOL_WRITTEN;
|
||||
}
|
||||
|
||||
static void conf_append_changed_input_warning(struct gstr *gs,
|
||||
struct symbol *sym,
|
||||
bool *changed_input_found)
|
||||
{
|
||||
if (!sym_user_value_changed(sym))
|
||||
return;
|
||||
|
||||
if (!*changed_input_found) {
|
||||
str_printf(gs,
|
||||
"warning: user-provided values changed by Kconfig:\n");
|
||||
*changed_input_found = true;
|
||||
}
|
||||
|
||||
str_printf(gs, " %s%s: %s -> %s\n",
|
||||
CONFIG_, sym->name,
|
||||
sym_get_user_value_string(sym),
|
||||
sym_get_string_value(sym));
|
||||
}
|
||||
|
||||
const char *conf_get_configname(void)
|
||||
{
|
||||
char *name = getenv("KCONFIG_CONFIG");
|
||||
@@ -759,11 +831,15 @@ int conf_write_defconfig(const char *filename)
|
||||
{
|
||||
struct symbol *sym;
|
||||
struct menu *menu;
|
||||
struct gstr gs;
|
||||
FILE *out;
|
||||
bool warn_changed_input = conf_warn_changed_input_enabled();
|
||||
bool changed_input_found = false;
|
||||
|
||||
out = fopen(filename, "w");
|
||||
if (!out)
|
||||
return 1;
|
||||
gs = str_new();
|
||||
|
||||
sym_clear_all_valid();
|
||||
|
||||
@@ -772,10 +848,14 @@ int conf_write_defconfig(const char *filename)
|
||||
|
||||
sym = menu->sym;
|
||||
|
||||
if (!sym || sym_is_choice(sym))
|
||||
if (!sym || sym_is_choice(sym) || sym->flags & SYMBOL_WRITTEN)
|
||||
continue;
|
||||
|
||||
sym_calc_value(sym);
|
||||
if (warn_changed_input)
|
||||
conf_append_changed_input_warning(&gs, sym,
|
||||
&changed_input_found);
|
||||
sym->flags |= SYMBOL_WRITTEN;
|
||||
if (!(sym->flags & SYMBOL_WRITE))
|
||||
continue;
|
||||
sym->flags &= ~SYMBOL_WRITE;
|
||||
@@ -798,6 +878,13 @@ int conf_write_defconfig(const char *filename)
|
||||
print_symbol_for_dotconfig(out, sym);
|
||||
}
|
||||
fclose(out);
|
||||
|
||||
conf_clear_written_flags();
|
||||
|
||||
if (changed_input_found)
|
||||
conf_changed_input_warning(str_get(&gs));
|
||||
|
||||
str_free(&gs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -809,7 +896,10 @@ int conf_write(const char *name)
|
||||
const char *str;
|
||||
char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1];
|
||||
char *env;
|
||||
struct gstr gs;
|
||||
bool need_newline = false;
|
||||
bool warn_changed_input = conf_warn_changed_input_enabled();
|
||||
bool changed_input_found = false;
|
||||
|
||||
if (!name)
|
||||
name = conf_get_configname();
|
||||
@@ -838,6 +928,7 @@ int conf_write(const char *name)
|
||||
}
|
||||
if (!out)
|
||||
return 1;
|
||||
gs = str_new();
|
||||
|
||||
conf_write_heading(out, &comment_style_pound);
|
||||
|
||||
@@ -859,13 +950,16 @@ int conf_write(const char *name)
|
||||
} else if (!sym_is_choice(sym) &&
|
||||
!(sym->flags & SYMBOL_WRITTEN)) {
|
||||
sym_calc_value(sym);
|
||||
if (warn_changed_input)
|
||||
conf_append_changed_input_warning(&gs, sym,
|
||||
&changed_input_found);
|
||||
sym->flags |= SYMBOL_WRITTEN;
|
||||
if (!(sym->flags & SYMBOL_WRITE))
|
||||
goto next;
|
||||
if (need_newline) {
|
||||
fprintf(out, "\n");
|
||||
need_newline = false;
|
||||
}
|
||||
sym->flags |= SYMBOL_WRITTEN;
|
||||
print_symbol_for_dotconfig(out, sym);
|
||||
}
|
||||
|
||||
@@ -892,8 +986,12 @@ int conf_write(const char *name)
|
||||
}
|
||||
fclose(out);
|
||||
|
||||
for_all_symbols(sym)
|
||||
sym->flags &= ~SYMBOL_WRITTEN;
|
||||
conf_clear_written_flags();
|
||||
|
||||
if (changed_input_found)
|
||||
conf_changed_input_warning(str_get(&gs));
|
||||
|
||||
str_free(&gs);
|
||||
|
||||
if (*tmpname) {
|
||||
if (is_same(name, tmpname)) {
|
||||
|
||||
@@ -37,7 +37,8 @@ class Conf:
|
||||
|
||||
# runners
|
||||
def _run_conf(self, mode, dot_config=None, out_file='.config',
|
||||
interactive=False, in_keys=None, extra_env={}):
|
||||
interactive=False, in_keys=None, extra_env={},
|
||||
silent=False):
|
||||
"""Run text-based Kconfig executable and save the result.
|
||||
|
||||
mode: input mode option (--oldaskconfig, --defconfig=<file> etc.)
|
||||
@@ -48,7 +49,10 @@ class Conf:
|
||||
extra_env: additional environments
|
||||
returncode: exit status of the Kconfig executable
|
||||
"""
|
||||
command = [CONF_PATH, mode, 'Kconfig']
|
||||
command = [CONF_PATH]
|
||||
if silent:
|
||||
command.append('-s')
|
||||
command += [mode, 'Kconfig']
|
||||
|
||||
# Override 'srctree' environment to make the test as the top directory
|
||||
extra_env['srctree'] = self._test_dir
|
||||
|
||||
40
scripts/kconfig/tests/warn_changed_input/Kconfig
Normal file
40
scripts/kconfig/tests/warn_changed_input/Kconfig
Normal file
@@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config DEP
|
||||
bool "DEP"
|
||||
help
|
||||
Test dependency symbol for Kconfig warning coverage.
|
||||
This is used by the warn_changed_input selftest.
|
||||
It intentionally stays unset in the input fragment.
|
||||
The test checks how dependent user input is adjusted.
|
||||
|
||||
config A
|
||||
bool "A"
|
||||
depends on DEP
|
||||
help
|
||||
Test bool symbol for changed-input diagnostics.
|
||||
The input fragment requests this symbol as built-in.
|
||||
The unmet dependency on DEP forces the final value to n.
|
||||
The warning should report that downgrade.
|
||||
|
||||
config NUM
|
||||
int "NUM"
|
||||
range 10 20
|
||||
help
|
||||
Test integer symbol for changed-input diagnostics.
|
||||
The input fragment requests a value outside the allowed range.
|
||||
Kconfig resolves it to the constrained in-range value.
|
||||
The warning should report that adjustment.
|
||||
|
||||
config DUP
|
||||
bool "DUP"
|
||||
depends on DEP
|
||||
help
|
||||
Test duplicate-definition handling for changed-input diagnostics.
|
||||
The input fragment requests this symbol as built-in.
|
||||
The duplicate definition below must not produce a duplicate warning.
|
||||
This keeps the warning output stable for repeated menu entries.
|
||||
|
||||
config DUP
|
||||
bool
|
||||
depends on DEP
|
||||
33
scripts/kconfig/tests/warn_changed_input/__init__.py
Normal file
33
scripts/kconfig/tests/warn_changed_input/__init__.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
"""
|
||||
Test optional warnings for user-provided values changed by Kconfig.
|
||||
|
||||
Warnings should stay disabled by default, and should only appear when
|
||||
KCONFIG_WARN_CHANGED_INPUT is enabled.
|
||||
"""
|
||||
|
||||
|
||||
def test(conf):
|
||||
assert conf.olddefconfig('config') == 0
|
||||
assert 'user-provided values changed by Kconfig' not in conf.stderr
|
||||
|
||||
assert conf._run_conf('--olddefconfig', dot_config='config',
|
||||
extra_env={
|
||||
'KCONFIG_WARN_CHANGED_INPUT': '1',
|
||||
}) == 0
|
||||
assert conf.stderr_contains('expected_stderr')
|
||||
assert conf.config_matches('expected_config')
|
||||
|
||||
assert conf._run_conf('--olddefconfig', dot_config='config',
|
||||
extra_env={
|
||||
'KCONFIG_WARN_CHANGED_INPUT': '1',
|
||||
}, silent=True) == 0
|
||||
assert conf.stderr_contains('expected_stderr')
|
||||
|
||||
assert conf._run_conf('--savedefconfig=defconfig', dot_config='config',
|
||||
out_file='defconfig',
|
||||
extra_env={
|
||||
'KCONFIG_WARN_CHANGED_INPUT': '1',
|
||||
}) == 0
|
||||
assert conf.stderr_contains('expected_stderr')
|
||||
assert conf.config_matches('expected_defconfig')
|
||||
3
scripts/kconfig/tests/warn_changed_input/config
Normal file
3
scripts/kconfig/tests/warn_changed_input/config
Normal file
@@ -0,0 +1,3 @@
|
||||
CONFIG_A=y
|
||||
CONFIG_NUM=30
|
||||
CONFIG_DUP=y
|
||||
6
scripts/kconfig/tests/warn_changed_input/expected_config
Normal file
6
scripts/kconfig/tests/warn_changed_input/expected_config
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Main menu
|
||||
#
|
||||
# CONFIG_DEP is not set
|
||||
CONFIG_NUM=20
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_NUM=20
|
||||
4
scripts/kconfig/tests/warn_changed_input/expected_stderr
Normal file
4
scripts/kconfig/tests/warn_changed_input/expected_stderr
Normal file
@@ -0,0 +1,4 @@
|
||||
warning: user-provided values changed by Kconfig:
|
||||
CONFIG_A: y -> n
|
||||
CONFIG_NUM: 30 -> 20
|
||||
CONFIG_DUP: y -> n
|
||||
Reference in New Issue
Block a user