kconfig: Error out on duplicated kconfig inclusion

Let kconfig exit with error on duplicated Kconfig file inclusion.

Repeated inclusion of Kbuild files are considered bad-practise with
regard to maintenance; and Kconfig language is rich enough that there
should be no need for that.

If repeated inclusion of Kconfig files is detected, error out with
messages like:

    Kconfig.inc1:4: error: repeated inclusion of Kconfig.inc3
    Kconfig.inc2:3: note: location of first inclusion of Kconfig.inc3

While commit f094f8a1b2 ("kconfig: allow multiple inclusion of the
same file") introduced detection of recursive inclusions of Kconfig
files, it explicitly allowed repeated inclusions, unfortunately w/o
reasoning.

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Closes: https://lore.kernel.org/all/CAHk-=wj03hLzK2D=+OYmjgcmGM+XYymp8GyaEs=C0=rXG2nb7w@mail.gmail.com/
Reviewed-by: Nathan Chancellor <nathan@kernel.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Link: https://patch.msgid.link/20260220-kconfig-error-out-on-duplicated-inclusion-v1-1-be78aa241a53@kernel.org
Signed-off-by: Nicolas Schier <nsc@kernel.org>
This commit is contained in:
Nicolas Schier
2026-02-20 19:55:19 +01:00
parent 6de23f81a5
commit 102d712ded
9 changed files with 56 additions and 7 deletions

View File

@@ -402,7 +402,7 @@ void zconf_initscan(const char *name)
exit(1);
}
cur_filename = file_lookup(name);
cur_filename = file_lookup(name, NULL, 0);
yylineno = 1;
}
@@ -443,7 +443,7 @@ void zconf_nextfile(const char *name)
}
yylineno = 1;
cur_filename = file_lookup(name);
cur_filename = file_lookup(name, cur_filename, cur_lineno);
}
static void zconf_endfile(void)

View File

@@ -51,7 +51,8 @@ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
}
/* util.c */
const char *file_lookup(const char *name);
const char *file_lookup(const char *name,
const char *parent_name, int parent_lineno);
/* lexer.l */
int yylex(void);

View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc1"

View File

@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc2"
source "Kconfig.inc3"

View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
source "Kconfig.inc3"

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: GPL-2.0-only

View File

@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
"""
Detect repeated inclusion error.
If repeated inclusion is detected, it should fail with error message.
"""
def test(conf):
assert conf.oldaskconfig() != 0
assert conf.stderr_contains('expected_stderr')

View File

@@ -0,0 +1,2 @@
Kconfig.inc1:4: error: Repeated inclusion of Kconfig.inc3
Kconfig.inc2:3: note: Location of first inclusion of Kconfig.inc3

View File

@@ -18,25 +18,50 @@ static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
struct file {
struct hlist_node node;
struct {
const char *name;
int lineno;
} parent;
char name[];
};
/* file already present in list? If not add it */
const char *file_lookup(const char *name)
static void die_duplicated_include(struct file *file,
const char *parent, int lineno)
{
fprintf(stderr,
"%s:%d: error: repeated inclusion of %s\n"
"%s:%d: note: location of first inclusion of %s\n",
parent, lineno, file->name,
file->parent.name, file->parent.lineno, file->name);
exit(1);
}
/* file already present in list? If not add it */
const char *file_lookup(const char *name,
const char *parent_name, int parent_lineno)
{
const char *parent = NULL;
struct file *file;
size_t len;
int hash = hash_str(name);
if (parent_name)
parent = file_lookup(parent_name, NULL, 0);
hash_for_each_possible(file_hashtable, file, node, hash)
if (!strcmp(name, file->name))
return file->name;
if (!strcmp(name, file->name)) {
if (!parent_name)
return file->name;
die_duplicated_include(file, parent, parent_lineno);
}
len = strlen(name);
file = xmalloc(sizeof(*file) + len + 1);
memset(file, 0, sizeof(*file));
memcpy(file->name, name, len);
file->name[len] = '\0';
file->parent.name = parent;
file->parent.lineno = parent_lineno;
hash_add(file_hashtable, &file->node, hash);