funwithlinux blog

How to Recursively Disable CONFIG Dependencies Automatically in Linux Kernel Builds

The Linux kernel is renowned for its flexibility, allowing users to enable or disable features via configuration options (e.g., CONFIG_NET, CONFIG_DEBUG_FS). These options are defined in Kconfig files, and many have dependencies—rules that dictate whether an option can be enabled or disabled based on other options. For example, CONFIG_BT (Bluetooth support) depends on CONFIG_NET (networking); if CONFIG_NET is disabled, CONFIG_BT cannot be enabled.

But what if you want to disable a specific option and all options that depend on it? Manually tracking and disabling dependencies is tedious, error-prone, and time-consuming—especially in large kernel configurations. This blog will guide you through understanding kernel dependencies, the challenges of manual disabling, and how to automate the process using tools like Kconfiglib to recursively disable dependencies.

2025-11

Table of Contents#

  1. Understanding Linux Kernel Configuration and Dependencies
  2. Challenges of Manual Dependency Disabling
  3. Automating Recursive Disabling with Kconfiglib
  4. Testing and Validating the Modified Configuration
  5. Troubleshooting Common Issues
  6. Conclusion
  7. References

1. Understanding Linux Kernel Configuration and Dependencies#

What is Kconfig?#

Kconfig is the configuration system used by the Linux kernel to define build-time options. Every kernel subsystem (e.g., networking, storage) has a Kconfig file that declares options, their descriptions, and dependencies. The user selects options via tools like make menuconfig or make xconfig, and the selections are stored in a .config file at the kernel source root.

Types of Dependencies#

Kernel options depend on other options to ensure the build is valid. The most common dependency types are:

  • depends on: Weak dependency. If CONFIG_A depends on CONFIG_B, CONFIG_A can only be enabled if CONFIG_B is enabled. Disabling CONFIG_B forces CONFIG_A to be disabled.
  • select: Strong dependency. If CONFIG_A select CONFIG_B, enabling CONFIG_A automatically enables CONFIG_B (even if CONFIG_B is disabled by the user). select overrides depends on for CONFIG_B.
  • imply: Suggestive dependency. If CONFIG_A imply CONFIG_B, enabling CONFIG_A suggests enabling CONFIG_B (but the user can still disable CONFIG_B manually).

Why Recursive Disabling Matters#

Suppose you want to disable CONFIG_DEBUG_FS (a debugging filesystem) to reduce kernel size. If CONFIG_DEBUG_FS is a dependency for CONFIG_TRACEPOINTS and CONFIG_KPROBES, those options will fail to build if CONFIG_DEBUG_FS is disabled but they remain enabled. To avoid build errors, you must first disable CONFIG_TRACEPOINTS and CONFIG_KPROBES—and any options that depend on them, recursively.

Manual tracking of these dependencies is impractical for large configurations. Automation is critical to avoid missing dependencies and ensure a valid build.

2. Challenges of Manual Dependency Disabling#

Even experienced developers struggle with manual dependency management:

  • Hidden Dependencies: Dependencies may be nested (e.g., A → B → C), requiring multiple levels of checks.
  • select Overrides: If CONFIG_X select Y, disabling Y manually will fail if X is still enabled.
  • Configuration Drift: After disabling options, running make olddefconfig (which updates the config with defaults) may re-enable dependencies, breaking your intended setup.
  • Time-Consuming: For complex subsystems (e.g., CONFIG_NET), disabling all dependencies could take hours of manual work.

3. Automating Recursive Disabling with Kconfiglib#

What is Kconfiglib?#

Kconfiglib is a Python library for parsing and manipulating Kconfig configurations. It provides programmatic access to kernel options, their dependencies, and relationships, making it ideal for automating config modifications. Unlike interactive tools like menuconfig, Kconfiglib lets you script dependency traversal and option toggling.

Installing Kconfiglib#

Kconfiglib requires Python 3.6+. Install it via pip:

pip3 install kconfiglib

Parsing Kernel Configurations#

To use Kconfiglib, you need:

  • The kernel source tree (to access Kconfig files).
  • A base .config file (e.g., from make defconfig or a distribution-specific config like arch/x86/configs/defconfig).

Kconfiglib parses the entire Kconfig tree and the .config file to build a model of all options and their dependencies.

Recursive Dependency Disabler Script#

Below is a Python script that disables a target CONFIG option and all options that depend on it (recursively). It uses Kconfiglib to traverse reverse dependencies (rev_deps) and disable them in the correct order.

Step 1: Script Overview#

The script will:

  1. Load the kernel’s Kconfig tree and existing .config.
  2. Identify the target option (e.g., CONFIG_DEBUG_FS).
  3. Recursively collect all options that depend on the target (reverse dependencies).
  4. Disable the collected dependencies and the target option.
  5. Save the modified configuration to a new file (e.g., .config.new).

Step 2: The Script#

import sys
from kconfiglib import Kconfig, Symbol
 
def collect_rev_deps(symbol, visited=None):
    """Recursively collect all symbols that depend on 'symbol' (reverse dependencies)."""
    if visited is None:
        visited = set()
    if symbol in visited:
        return visited
    visited.add(symbol)
    # Traverse all symbols that directly depend on 'symbol' (rev_deps)
    for rev_dep in symbol.rev_deps:
        # Skip non-user-configurable symbols (e.g., 'not set' or hardcoded)
        if rev_dep.configurable:
            collect_rev_deps(rev_dep, visited)
    return visited
 
def disable_symbols(kconfig, symbols):
    """Disable a list of symbols in the Kconfig configuration."""
    for sym in symbols:
        if not sym.configurable:
            print(f"Skipping non-configurable symbol: {sym.name}")
            continue
        # Disable the symbol (set to 'n')
        sym.set_value('n')
        print(f"Disabled: {sym.name}")
 
def main():
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} <kernel_source_dir> <target_config>")
        print(f"Example: {sys.argv[0]} ~/linux-src CONFIG_DEBUG_FS")
        sys.exit(1)
 
    kernel_src = sys.argv[1]
    target_config = sys.argv[2]
 
    # Load the Kconfig tree and .config (uses .config in kernel_src by default)
    kconfig = Kconfig(f"{kernel_src}/Kconfig", warn=False)
 
    # Find the target symbol
    target_sym = kconfig.get_symbol(target_config)
    if not target_sym:
        print(f"Error: Symbol {target_config} not found in Kconfig.")
        sys.exit(1)
    if not target_sym.configurable:
        print(f"Error: {target_config} is not user-configurable.")
        sys.exit(1)
 
    # Collect all reverse dependencies (symbols that depend on the target)
    print(f"Collecting reverse dependencies for {target_config}...")
    rev_deps = collect_rev_deps(target_sym)
    # Exclude the target itself from the initial list (disable it last)
    rev_deps.discard(target_sym)
 
    # Disable dependencies first, then the target
    print(f"Disabling {len(rev_deps)} dependencies...")
    disable_symbols(kconfig, rev_deps)
    print(f"Disabling target: {target_config}")
    disable_symbols(kconfig, [target_sym])
 
    # Save the modified config
    with open(f"{kernel_src}/.config.new", "w") as f:
        f.write(kconfig.write_config())
    print(f"Modified config saved to {kernel_src}/.config.new")
 
if __name__ == "__main__":
    main()

Step 3: Using the Script#

  1. Prepare the Kernel Source: Ensure you have a kernel source tree with a valid .config (e.g., run make defconfig to generate a default config).

  2. Run the Script: Specify the kernel source directory and target CONFIG option:

    python3 disable_deps.py ~/linux-src CONFIG_DEBUG_FS
  3. Output: The script will:

    • Collect all symbols depending on CONFIG_DEBUG_FS (e.g., CONFIG_TRACEPOINTS).
    • Disable those symbols and CONFIG_DEBUG_FS.
    • Save the result to ~/linux-src/.config.new.

4. Testing and Validating the Modified Configuration#

Sanity Checks with make olddefconfig#

After generating .config.new, validate it to ensure no dependencies are broken:

cd ~/linux-src
mv .config.new .config
make olddefconfig  # Updates config with defaults for missing options

make olddefconfig will resolve any remaining dependencies and print warnings if critical options are disabled. If you see errors like error: "CONFIG_A" depends on "CONFIG_B" which is not set, the script may have missed a dependency (see Troubleshooting below).

Building and Testing the Kernel#

Build the kernel with the modified config and test it in a VM or on hardware:

make -j$(nproc)  # Build the kernel
# Install modules and kernel (adjust for your distro)
sudo make modules_install install

Boot the new kernel and verify the target option is disabled (e.g., check /proc/config.gz):

zcat /proc/config.gz | grep CONFIG_DEBUG_FS
# Should output: # CONFIG_DEBUG_FS is not set

5. Troubleshooting Common Issues#

Circular Dependencies#

Problem: Some options depend on each other (e.g., A depends on B and B depends on A). The script may get stuck in a loop.
Fix: Kconfiglib’s rev_deps includes a visited set to avoid cycles, but manual intervention may be needed for complex cases. Use make menuconfig to search for circular dependencies.

Unavoidable Dependencies#

Problem: Core kernel options (e.g., CONFIG_64BIT, CONFIG_SMP) cannot be disabled, as they are required for basic functionality.
Fix: The script skips non-configurable symbols, but if you see Error: CONFIG_X is not user-configurable, choose a different target.

Handling select and imply#

Problem: The script ignores select dependencies. If CONFIG_A select B, disabling B will fail if A is still enabled.
Fix: Extend the script to check symbol.selected_by (symbols that select the target) and disable them first. For example:

def collect_selectors(symbol, visited=None):
    """Collect symbols that 'select' the target symbol."""
    if visited is None:
        visited = set()
    if symbol in visited:
        return visited
    visited.add(symbol)
    for selector in symbol.selected_by:
        if selector.configurable:
            collect_selectors(selector, visited)
    return visited

Add this to the script to disable selectors before dependencies.

6. Conclusion#

Recursively disabling kernel CONFIG dependencies manually is error-prone and inefficient. By using Kconfiglib to script dependency traversal, you can automate the process, ensuring all dependencies are disabled and your kernel config is valid. This approach saves time, reduces errors, and gives you fine-grained control over kernel features—critical for embedded systems, security-hardened kernels, or minimal builds.

7. References#