Kleaf runs the main Make/Kbuild build inside one Bazel action. The detailed compiler commands remain nested inside that action. This creates a candidate process-level integration boundary that still requires kernel-scale validation.
Bazel remote execution distributes Bazel actions. Native Bazel C++ rules often expose one compile action per translation unit, giving the scheduler many independent units of work. Kleaf’s main kernel build is coarser: Bazel schedules the enclosing KernelBuild action, while Make and Kbuild perform a larger compilation fan-out inside the environment executing that action.
The practical consequence is specific: adding Bazel remote workers does not automatically create more independently schedulable kernel compilation actions. Other Kleaf actions can still run independently, and a Bazel cache hit can reuse the enclosing action; the limitation applies to commands nested inside the main KernelBuild action.
In the pinned Kleaf revision examined here, the main build is registered as a Bazel action named KernelBuild:
ctx.actions.run_shell(
mnemonic = “KernelBuild”,
…
command = command,
)
The command executed by that action contains the handoff:
# Actual kernel build
make -C ${KERNEL_DIR} ${TOOL_ARGS} O=${OUT_DIR} {make_goals}
Bazel analyzes the rule and registers the outer action. When the action runs, its shell command launches GNU Make. Make and Kbuild then determine and launch the compilation, assembly, archive, link, BTF, and module-related commands required by the selected configuration.
ers, “must Bazel re-evaluate this graph node?” The local action cache lives under the output base and answers, “must this command run again on this machine?” The disk or remote cache answers, “has anyone already produced an equivalent result?”
Figure 1. Bazel schedules one KernelBuild action. Make/Kbuild launches the child commands inside it. This boundary creates a candidate point for process-level evaluation.
Kleaf creates other targets and actions for configuration, modules, ABI work, signing, packaging, and distribution. The boundary discussed here is narrower: the main Make/Kbuild invocation is one coarse KernelBuild action.
A Bazel target may produce several actions, and one action may launch several child processes. Those child processes remain part of the enclosing Bazel action. In the Kleaf path examined here, Bazel can schedule the main KernelBuild action on one execution worker, and Make can use the resources available there, but the compiler commands remain part of the enclosing action.
Inside that action, Kbuild reads .config, applies architecture and feature rules, selects built-in objects and modules, and drives generated files, compilation, archives, links, BTF, and module tooling. It also records commands and dependencies in .cmd files for its own incremental logic.
There are therefore three distinct reuse layers: Bazel may reuse the whole outer action; a persistent local OUT_DIR may preserve Kbuild’s internal state; and a process-level system may separately evaluate qualified commands. These mechanisms are complementary, but they have different correctness and invalidation boundaries.
We reproduced the structural pattern using one generated C source built in two ways. The small experiment ran on an Islo-hosted Linux machine. Islo, developed by Incredibuild and available at islo.dev, provided the execution layer for the Bazel, Make, and tracing commands. The native version used Bazel C++ rules, and aquery reported a CppCompile action. The wrapped version used one genrule that launched Make; aquery reported the outer Genrule, while strace observed Make launching the compiler beneath it.
Native:
bazel aquery ‘mnemonic(“CppCompile”, //native:app)’
Wrapped:
bazel aquery //wrapped:build
strace -f -e execve -o processes.log bazel build //wrapped:build
The result established the action/process distinction: native Bazel compilation exposed a compile action, while the Make-wrapped build exposed one outer Bazel action with a child compiler process.
The experiment used a controlled sandbox with a small C build. Its scope covered action and process structure. Remote distribution, cache reuse, artifact correctness, and performance remain untested.
Incredibuild operates at the process layer. Its Linux configuration model can classify an orchestration process as intercepted, qualified child commands as allow_remote, and unsupported or stateful commands as local_only. Structurally, that model matches the process tree beneath KernelBuild: observe Make, classify its child commands, and apply remote execution only where the command is qualified.
Distribution and cache reuse must be treated separately. A qualified compiler command may be a remote-execution candidate. Cache reuse requires a narrower, operation-specific determination that the relevant inputs and execution conditions are represented and that the output is safe to reuse. Public Incredibuild documentation scopes Build Cache support for Clang/GCC-family tools to compilation. Linking remains outside that documented cache scope.
A conservative initial scope is therefore C/C++ compiler invocations, with automatic local fallback. Rust, assembly, archives, link/LTO, BTF, signing, packaging, and other commands should remain local until their exact command patterns and side effects are separately qualified.
The source and process model support a plausible integration point. A production Kleaf integration still requires validation. If Bazel executes KernelBuild on a remote worker, the process-level agent and its required filesystem and network behavior must exist and be permitted on that worker—or the action must use a compatible execution strategy. Execution topology must be validated explicitly.
Teams evaluating process-level acceleration for Kleaf should start with a narrow, reproducible test and expand only after each command class meets correctness and performance requirements:
This sequence gives teams a practical adoption path: qualify one command class at a time, preserve local fallback, and use artifact correctness and end-to-end build time as the acceptance criteria.
The Make handoff marks a clear architectural boundary: Bazel schedules the enclosing KernelBuild action, while Make/Kbuild drive the detailed work inside it. Teams exploring process-level acceleration should begin with compiler-only qualification on a pinned Android Common Kernel build, retain local fallback, and expand only after correctness and end-to-end performance are demonstrated.
[1] Pinned Kleaf kernel_build implementation — https://android.googlesource.com/kernel/build/+/0931b0be9862e98950469555dd8916c5336ccbb4/kleaf/impl/kernel_build.bzl
[2] Bazel actions.run_shell reference — https://bazel.build/rules/lib/builtins/actions
[3] Bazel remote execution overview — https://bazel.build/remote/rbe
[4] Linux kernel Kbuild Makefiles — https://docs.kernel.org/kbuild/makefiles.html
[5] Kleaf sandboxing and persistent OUT_DIR — https://android.googlesource.com/kernel/build/+/refs/heads/master/kleaf/docs/sandbox.md
[6] Incredibuild process distribution types — https://docs.incredibuild.com/lin/v3latest/linux/distribution_types_tools_processes.html
[7] Incredibuild supported platforms, tools, and Build Cache scope — https://docs.incredibuild.com/lin/latest/linux/supported_platforms_tools.html
[8] Islo overview — https://docs.islo.dev/overview
Yossi Eliaz
Incredibuild empowers your teams to be productive and focus on innovating.