Configuring kernel_build

Modify defconfig

To run make *config or config.sh in Kleaf, run

$ tools/bazel run <name_of_kernel_build>_config [-- [menuconfig|nconfig|savedefconfig...]]

... where <name_of_kernel_build> is the name of the kernel_build target with the requested build config.

The menu command (menuconfig, xconfig, etc.) must be provided to the underlying executable, so they need to be provided after --. See Running executables. If nothing is provided, the default is savedefconfig.

Example:

$ tools/bazel run //common:kernel_aarch64_config

$ tools/bazel run //common:kernel_x86_64_config -- nconfig

Conditions

The above command works if the following conditions are satisified:

  • kernel_build.defconfig is set
  • kernel_build.pre_defconfig_fragments has at most one element.

For nconfig etc. to work, ncurses may also be required on the host machine.

If these conditions are not satisified, the command may or may not work.

If kernel_build.pre_defconfig_fragments contains more than one element, the above command prints the path to the generated configs. You may need to apply the generated configs on pre_defconfig_fragments manually.

Effects

After the developer goes through the menuconfig / nconfig / xconfig etc. to configure the the kernel, Kleaf does the following:

If kernel_build.pre_defconfig_fragments is empty, Kleaf calls make savedefconfig and copies the minimized defconfig to the source file pointed by kernel_build.defconfig. The path to the updated file is printed.

If kernel_build.pre_defconfig_fragments has a single element, the difference of the .config after and before the developer invokes menuconfig is calculated, and then applied to the pre defconfig fragment. The path to the updated file is printed.

If kernel_build.pre_defconfig_fragments has more than one element, the difference of the .config after and before the developer invokes menuconfig is calculated. Then, the command cowardly fails, with the path to the temporary difference file printed. The developer is expected to move the differences to the correct pre_defconfig_fragments.

NOTE: post_defconfig_fragments does not participate in any of these calculations.

Internals of configuring the kernel

When a kernel_build is built, Kleaf configures the kernel by applying the following steps:

  1. The kernel_build.defconfig file is used as a base.
  2. The kernel_build.pre_defconfig_fragments are applied.
  3. Calls make ..._defconfig to build .config
  4. If kernel_build.check_defconfig is set, calls make savedefconfig and compares it with kernel_build.defconfig. (Note: check_defconfig requires pre_defconfig_fragments to be empty)
  5. If step 4 did not take place, enforces that .config contains all configurations in defconfig and pre_defconfig_fragments; see Checks. Otherwise skip.
  6. The kernel_build.post_defconfig_fragments, --defconfig_fragment and other command line flags (e.g. --kasan) are applied on .config. If anything is applied, calls make olddefconfig.
  7. Enforces that .config contains all configurations in post_defconfig_fragments; see Checks.

See kernel_build in documentation for all rules for details.

Defconfig

This is the base defconfig.

For GKI and mixed device builds that sets base_kernel to GKI, this is usually the gki_defconfig for the architecture, e.g. //common:arch/arm64/configs/gki_defconfig.

Pre Defconfig fragments

This usually contains a single item so that tools/bazel run XXX_config works. see Modify defconfig: Conditions.

This usually contains configs to build in-tree modules that are not built in the base kernel, e.g. CONFIG_SOME_MODULE=m.

At step 2, When pre defconfig fragments are applied, items in defconfig are overridden. In addition, order matters; items appearing later in the pre_defconfig_fragments list overrides items appearing earlier.

At step 7, Checks are applied with the above in consideration, so you don't have to manually add # nocheck for conflicting items.

Example:

# foo_defconfig
CONFIG_A=y
# set_a_defconfig
CONFIG_A=y
# unset_a_defconfig
# CONFIG_A is not set
# CONFIG_A=y
kernel_build(
    defconfig = "foo_defconfig",
    pre_defconfig_fragments = [],
    # ...
)

# CONFIG_A is not set
kernel_build(
    defconfig = "foo_defconfig",
    pre_defconfig_fragments = ["unset_a_defconfig"],
    # ...
)

# CONFIG_A=y
kernel_build(
    defconfig = "foo_defconfig",
    pre_defconfig_fragments = ["unset_a_defconfig", "set_a_defconfig"],
    # ...
)

Post Defconfig fragments

This usually contains debug configs to build a variant of the kernel and modules.

Post defconfig fragments consist of the following, in this order:

  1. Apply post defconfig fragments:
    1. kernel_build.post_defconfig_fragments
    2. --defconfig_fragment
    3. Other pre-defined flags, e.g., --kasan, in an unspecified order.

At step 6, When pre defconfig fragments are applied, items in defconfig and pre_defconfig_fragments are overridden by these post defconfig fragments. Then at step 7, Checks are applied with the above in consideration, so you don't have to manually add # nocheck on defconfig and pre_defconfig_fragments even if their values are overridden by post defconfig fragments later.

At step 6, order matters when post defconfig fragments are applied. Items appearing later in the post defconfig fragments list overrides items appearing earlier. However, at step 7, the order in post defconfig fragments does not matter in Checks; all items must exist in the final .config file. As a result, unless you have # nocheck that suppresses conflicts, order usually does not matter.

Example (using foo_defconfig and other files from the previous example):

# CONFIG_A is not set
kernel_build(
    defconfig = "foo_defconfig",
    post_defconfig_fragments = ["unset_a_defconfig"],
    # ...
)

# Build error
# Because unset_a_defconfig conflicts with set_defconfig
kernel_build(
    post_defconfig_fragments = ["unset_a_defconfig", "set_defconfig"],
    # ...
)

kernel_build.post_defconfig_fragments

The convention is that the files should be named X_defconfig, where X describes what the defconfig fragment does.

Example:

# path/to/tuna/BUILD.bazel
kernel_build(
    name = "tuna",
    post_defconfig_fragments = ["tuna_defconfig"],
    ...
)
# path/to/tuna/tuna_defconfig

# Precondition:
#   CONFIG_TUNA_GRAPHICS must already be declared in kernel_build.kconfig_ext
CONFIG_TUNA_GRAPHICS=y

--defconfig_fragment flag

You may specify a single target in the --defconfig_fragment flag to add defconfig fragment(s) via the command line. To refer to a file in the source tree, the file must already be exported via exports_files or included in a filegroup.

NOTE: If multiple --defconfig_fragment are supplied, only the last one takes effect.

The convention is that the files should be named X_defconfig, where X describes what the defconfig fragment does.

Example:

# path/to/tuna/BUILD.bazel
exports_files([
    "kasan_hw_tags_defconfig",
])
kernel_build(name = "tuna", ...)
# kasan_hw_tags_defconfig
CONFIG_KASAN=y
CONFIG_KASAN_HW_TAGS=y
# CONFIG_KASAN_SW_TAGS is not set
# etc. Add your configs!
$ tools/bazel build \
    --defconfig_fragment=//path/to/tuna:kasan_hw_tags_defconfig \
    //path/to/tuna:tuna

To specify multiple fragments in the flag, use a filegroup.

Example:

# path/to/tuna/BUILD.bazel
filegroup(
    name = "all_kasan_defconfigs",
    srcs = ["kasan_defconfig", "lto_none_defconfig"]
)
kernel_build(name = "tuna", ...)
$ tools/bazel build \
    --defconfig_fragment=//path/to/tuna:all_kasan_defconfigs \
    //path/to/tuna:tuna

Other pre-defined flags

There are a few pre-defined command-line flags and attributes on kernel_build that are commonly used. When these flags and/or attributes are set, additional defconfig fragments are applied on .config, and checked after .config is built. It is recommended to use these common flags instead of defining your own defconfig fragments to avoid fragmentation in the ecosystem (pun intended).

  • --btf_debug_info
  • --debug
  • --gcov
  • --kasan
  • --kasan_sw_tags
  • --kasan_generic
  • --kcsan
  • --page_size
  • --rust / --norust
  • --rust_ashmem / --norust_ashmem

NOTE: w.r.t. to KMI, the following flags will disable both TRIM_UNUSED_KSYMS (by not setting it) and MODULE_SIG_PROTECT(by explicitly turning it off): (--notrim, --debug, --gcov, --k*san, --kgdb).

User-defined flags

To control kernel_build.post_defconfig_fragments with command line flags, you may use configurable build attributes (sometimes referred to as select()).

Example:

bool_flag(
    name = "khwasan",
    build_setting_default = False,
)

config_setting(
    name = "khwasan_is_set",
    flag_values = {":khwasan": "true"},
)

kernel_build(
    name = "tuna",
    post_defconfig_fragments = select({
        ":khwasan_is_set": ["khwasan_defconfig"],
        "//conditions:default": []
    }) + [...],
    ...
)
$ tools/bazel build --//path/to/tuna:khwasan //path/to/tuna:tuna

Use device.bazelrc to shorten flags:

# device.bazelrc
build --flag_alias=khwasan=--//path/to/tuna:khwasan
$ tools/bazel build --khwasan //path/to/tuna:tuna

To shorten --defconfig_fragment flags, you may use --config in device.bazelrc:

# device.bazelrc
build:kasan_hw_tags --defconfig_fragment=//path/to/tuna:kasan_hw_tags_defconfig
$ tools/bazel build --config=kasan_hw_tags //path/to/tuna:tuna

Checks

All requirements in defconfig and pre_defconfig_fragments must be present in the intermediate .config before post defconfig fragments are applied, unless:

  • A CONFIG_ in defconfig is overridden by pre_defconfig_fragments.
  • A CONFIG_ in pre_defconfig_fragments is overridden by a later value in pre_defconfig_fragments.
  • The line in defconfig, pre_defconfig_fragments has a # nocheck comment appended to it.

All post_defconfig_fragments must be present in the final .config, unless the line in post_defconfig_fragments has a # nocheck comment appended to it.

The checks are in place to prevent typos and mistakes. For example, if an item is not declared in Kconfig, then make ..._defconfig silently drops it, but these checks properly flags potential issues.

Example:

# bar_defconfig
CONFIG_BASE=y
CONFIG_BASE_MODULE=m
# CONFIG_MODULE_1 is not set
# CONFIG_MODULE_2 is not set
# CONFIG_DEBUG_1 is not set
# CONFIG_DEBUG_2 is not set
# pre_defconfig
CONFIG_MODULE_2=m
# post_1_defconfig
CONFIG_DEBUG_1=y
CONFIG_DEBUG_2=y # nocheck: (b/12345678) a device does not support this
# post_2_defconfig
# CONFIG_DEBUG_2 is not set
kernel_build(
    name = "bar",
    defconfig = "bar_defconfig",
    pre_defconfig_fragments = ["pre_defconfig"],
    post_defconfig_fragments = ["post_1_defconfig", "post_2_defconfig"],
)

The resulting .config contains the following, and the check passes:

CONFIG_BASE=y
CONFIG_BASE_MODULE=m
# CONFIG_MODULE_1 is not set
CONFIG_MODULE_2=m
CONFIG_DEBUG_1=y
# CONFIG_DEBUG_2 is not set