]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
kconfig: Support conditional deps using "depends on X if Y"
authorNicolas Pitre <nico@fluxnic.net>
Mon, 15 Dec 2025 23:06:54 +0000 (15:06 -0800)
committerNathan Chancellor <nathan@kernel.org>
Tue, 6 Jan 2026 21:57:15 +0000 (14:57 -0700)
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>
12 files changed:
Documentation/kbuild/kconfig-language.rst
scripts/kconfig/lkc.h
scripts/kconfig/menu.c
scripts/kconfig/parser.y
scripts/kconfig/tests/conditional_dep/Kconfig [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/__init__.py [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/expected_config1 [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/expected_config2 [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/expected_config3 [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/test_config1 [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/test_config2 [new file with mode: 0644]
scripts/kconfig/tests/conditional_dep/test_config3 [new file with mode: 0644]

index abce88f15d7cb303104bc8e9aaee45ce2fbcb1e7..9ff3e530b2b43538848da6703d682232f2a533b0 100644 (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"
index 56548efc14d70103c2287c0c81e3916c08021c35..79898596121563350d2fa63fcf879743b756d003 100644 (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);
index 0f1a6513987c8f0a446fbef3ce1eb7f305a94a42..b2d8d4e11e07c02d7bb5c4ed7713fb4334d9dbf5 100644 (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);
 }
 
index 49b79dde1725ab500b57c823499ac1838d14b64d..6d1bbee38f5d16c71ce9dcb985a7df6ef43a94ba 100644 (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);
 };
 
diff --git a/scripts/kconfig/tests/conditional_dep/Kconfig b/scripts/kconfig/tests/conditional_dep/Kconfig
new file mode 100644 (file)
index 0000000..2015dfb
--- /dev/null
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/__init__.py b/scripts/kconfig/tests/conditional_dep/__init__.py
new file mode 100644 (file)
index 0000000..ab16df6
--- /dev/null
@@ -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')
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config1 b/scripts/kconfig/tests/conditional_dep/expected_config1
new file mode 100644 (file)
index 0000000..826ed7f
--- /dev/null
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config2 b/scripts/kconfig/tests/conditional_dep/expected_config2
new file mode 100644 (file)
index 0000000..10d2354
--- /dev/null
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config3 b/scripts/kconfig/tests/conditional_dep/expected_config3
new file mode 100644 (file)
index 0000000..b04fa6f
--- /dev/null
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/test_config1 b/scripts/kconfig/tests/conditional_dep/test_config1
new file mode 100644 (file)
index 0000000..9b05f3c
--- /dev/null
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/test_config2 b/scripts/kconfig/tests/conditional_dep/test_config2
new file mode 100644 (file)
index 0000000..5e66d23
--- /dev/null
@@ -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
diff --git a/scripts/kconfig/tests/conditional_dep/test_config3 b/scripts/kconfig/tests/conditional_dep/test_config3
new file mode 100644 (file)
index 0000000..86304f3
--- /dev/null
@@ -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