kconfig: Support conditional deps using "depends on X if Y"

Extend the "depends on" syntax to support conditional dependencies
using "depends on X if Y". While functionally equivalent to "depends
on X || (Y == n)", "depends on X if Y" is much more readable and
makes the kconfig language uniform in supporting the "if <expr>"
suffix.
This also improves readability for "optional" dependencies, which
are the subset of conditional dependencies where X is Y.
Previously such optional dependencies had to be expressed as
the counterintuitive "depends on X || !X", now this can be
represented as "depends on X if X".

The change is implemented by converting the "X if Y" syntax into the
"X || (Y == n)" syntax during "depends on" token processing.

Signed-off-by: Nicolas Pitre <nico@fluxnic.net>
[Graham Roff: Rewrote commit message, updated patch, added tests]
Signed-off-by: Graham Roff <grahamr@qti.qualcomm.com>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Link: https://patch.msgid.link/20251215-kconfig_conditional_deps-v3-1-59519af0a5df@qti.qualcomm.com
[nathan: Minor adjustments to spacing]
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
This commit is contained in:
Nicolas Pitre 2025-12-15 15:06:54 -08:00 committed by Nathan Chancellor
parent 5ce3218d4f
commit 76df6815da
No known key found for this signature in database
GPG key ID: 1D6B269171C01A96
12 changed files with 130 additions and 8 deletions

View file

@ -118,7 +118,7 @@ applicable everywhere (see syntax).
This is a shorthand notation for a type definition plus a value.
Optionally dependencies for this default value can be added with "if".
- dependencies: "depends on" <expr>
- dependencies: "depends on" <expr> ["if" <expr>]
This defines a dependency for this menu entry. If multiple
dependencies are defined, they are connected with '&&'. Dependencies
@ -134,6 +134,16 @@ applicable everywhere (see syntax).
bool "foo"
default y
The dependency definition itself may be conditional by appending "if"
followed by an expression. For example::
config FOO
tristate
depends on BAR if BAZ
meaning that FOO is constrained by the value of BAR only if BAZ is
also set.
- reverse dependencies: "select" <symbol> ["if" <expr>]
While normal dependencies reduce the upper limit of a symbol (see
@ -602,8 +612,14 @@ Some drivers are able to optionally use a feature from another module
or build cleanly with that module disabled, but cause a link failure
when trying to use that loadable module from a built-in driver.
The most common way to express this optional dependency in Kconfig logic
uses the slightly counterintuitive::
The recommended way to express this optional dependency in Kconfig logic
uses the conditional form::
config FOO
tristate "Support for foo hardware"
depends on BAR if BAR
This slightly counterintuitive style is also widely used::
config FOO
tristate "Support for foo hardware"

View file

@ -82,7 +82,7 @@ void menu_warn(const struct menu *menu, const char *fmt, ...);
struct menu *menu_add_menu(void);
void menu_end_menu(void);
void menu_add_entry(struct symbol *sym, enum menu_type type);
void menu_add_dep(struct expr *dep);
void menu_add_dep(struct expr *dep, struct expr *cond);
void menu_add_visibility(struct expr *dep);
struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct expr *dep);

View file

@ -127,8 +127,18 @@ static struct expr *rewrite_m(struct expr *e)
return e;
}
void menu_add_dep(struct expr *dep)
void menu_add_dep(struct expr *dep, struct expr *cond)
{
if (cond) {
/*
* We have "depends on X if Y" and we want:
* Y != n --> X
* Y == n --> y
* That simplifies to: (X || (Y == n))
*/
dep = expr_alloc_or(dep,
expr_trans_compare(cond, E_EQUAL, &symbol_no));
}
current_entry->dep = expr_alloc_and(current_entry->dep, dep);
}

View file

@ -323,7 +323,7 @@ if_entry: T_IF expr T_EOL
{
printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno);
menu_add_entry(NULL, M_IF);
menu_add_dep($2);
menu_add_dep($2, NULL);
$$ = menu_add_menu();
};
@ -422,9 +422,9 @@ help: help_start T_HELPTEXT
/* depends option */
depends: T_DEPENDS T_ON expr T_EOL
depends: T_DEPENDS T_ON expr if_expr T_EOL
{
menu_add_dep($3);
menu_add_dep($3, $4);
printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno);
};

View file

@ -0,0 +1,32 @@
# SPDX-License-Identifier: GPL-2.0
# Test Kconfig file for conditional dependencies.
# Enable module support for tristate testing
config MODULES
bool "Enable loadable module support"
modules
default y
config FOO
bool "FOO symbol"
config BAR
bool "BAR symbol"
config TEST_BASIC
bool "Test basic conditional dependency"
depends on FOO if BAR
default y
config TEST_COMPLEX
bool "Test complex conditional dependency"
depends on (FOO && BAR) if (FOO || BAR)
default y
config BAZ
tristate "BAZ symbol"
config TEST_OPTIONAL
tristate "Test simple optional dependency"
depends on BAZ if BAZ
default y

View file

@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
"""
Correctly handle conditional dependencies.
"""
def test(conf):
assert conf.oldconfig('test_config1') == 0
assert conf.config_matches('expected_config1')
assert conf.oldconfig('test_config2') == 0
assert conf.config_matches('expected_config2')
assert conf.oldconfig('test_config3') == 0
assert conf.config_matches('expected_config3')

View file

@ -0,0 +1,11 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_MODULES=y
CONFIG_FOO=y
CONFIG_BAR=y
CONFIG_TEST_BASIC=y
CONFIG_TEST_COMPLEX=y
CONFIG_BAZ=m
CONFIG_TEST_OPTIONAL=m

View file

@ -0,0 +1,9 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_MODULES=y
# CONFIG_FOO is not set
CONFIG_BAR=y
CONFIG_BAZ=y
CONFIG_TEST_OPTIONAL=y

View file

@ -0,0 +1,11 @@
#
# Automatically generated file; DO NOT EDIT.
# Main menu
#
CONFIG_MODULES=y
# CONFIG_FOO is not set
# CONFIG_BAR is not set
CONFIG_TEST_BASIC=y
CONFIG_TEST_COMPLEX=y
# CONFIG_BAZ is not set
CONFIG_TEST_OPTIONAL=y

View file

@ -0,0 +1,6 @@
# Basic check that everything can be configured if selected.
CONFIG_FOO=y
CONFIG_BAR=y
CONFIG_BAZ=m
# Ensure that TEST_OPTIONAL=y with BAZ=m is converted to TEST_OPTIONAL=m
CONFIG_TEST_OPTIONAL=y

View file

@ -0,0 +1,7 @@
# If FOO is not selected, then TEST_BASIC should fail the conditional
# dependency since BAR is set.
# TEST_COMPLEX will fail dependency as it depends on both FOO and BAR
# if either of those is selected.
CONFIG_FOO=n
CONFIG_BAR=y
CONFIG_BAZ=y

View file

@ -0,0 +1,6 @@
# If FOO is not selected, but BAR is also not selected, then TEST_BASIC
# should pass since the dependency on FOO is conditional on BAR.
# TEST_COMPLEX should be also set since neither FOO nor BAR are selected
# so it has no dependencies.
CONFIG_FOO=n
CONFIG_BAR=n