Kleaf Development

This documentation summarizes principles used in Kleaf development.

Style Guides

For Python:

Conventions

  • Follow these conventions for Macros, in particular:
    • In most cases, optional parameters should have a default value of None.
  • Avoid using unittest.suite . Reasons:
    • Individual test names are tagged with indices, not descriptive names. This makes failures hard to triage.
    • When items are reordered or inserted into the list, the indicies change, and thus individual test names change as well. Continuous testing processes may be confused and show incomplete test histories because of name changes.

“DAMP” BUILD.bazel files

BUILD.bazel files should apply the “DAMP” (descriptive and meaningful phrases) rule, as opposed to the “DRY” (do not repeat yourself) rule that is otherwise often applied to production code.

In practice, this rule encourages all targets visible to the user to be declared in BUILD.bazel files instead of being wrapped in macros.

Usually, this means the implementation of a macro should only declare one target public to the user, named with the same name of the macro invocation. Private targets should usually be named name + "_<suffix>", unknown to the user and not guaranteed through the contract of the interface.

Example:

# GOOD

# BUILD.bazel
foo_library(name = "my_target_lib", ...)
foo_binary(name = "my_target", ...)
# BAD

# my_target.bzl
def my_rule(name, ...)
    foo_library(name = name + "_lib", ...)
    foo_binary(name = name, ...)

# BUILD.bazel
my_rule(name = "my_target", ...)

If you expect users to know about both my_target and my_target_lib; that is, a user can do bazel build :my_target :my_target_lib, it is encouraged that both are declared explicitly in the BUILD.bazel file. There might be some repitition in the list of attributes shared between my_target and my_target_lib, but the overall maintenance cost is lower in the long run.

A notable exception for macros is when the additional targets are implementation details and not visible to users. For example:

# GOOD

# my_target.bzl
def my_rule(name, ...)
    foo_library(
        name = name + "_internal_lib",
        visibility = ["//visibility:private"],
        # ...
    )
    foo_binary(
        name = name,
        deps = [name + "_internal_lib"]
        # ...
    )

# BUILD.bazel
my_rule(name = "my_target", ...)

In this case, the user is expected to only do bazel build :my_target, not bazel build :my_target_internal_lib, because the library is an implementation detail of my_rule.

NOTE: There are multiple violations of the “DAMP” rule even within Kleaf's code base, notably the following. They require some cleanup, but due to backwards compatibility of the API, they might not be completely “DAMP” even in the future.

  • kernel_build
  • kernel_module (it calls kernel_module_test)
  • define_common_kernels
  • etc.

Performance

  • For performance optimizations follow the tips in Optimizing Performance.
    • E.g. Use depsets instead of lists; depsets are a tree of objects that can be concatenated effortlessly.lists are concatenated by copying contents.

Prefer depset over ctx.files.X in rules

In general, in rule implementations, prefer depsets and avoid using ctx.files.X.

  • If you need a depset, prefer depset(transitive = [target.files for target in ctx.attr.X]). This is more memory efficient because it does not retain the ctx.files.X list. Instead, it only creates the depset tree using existing depsets in memory.
    • If there are too many labels in attribute X, you may micro-optimize by assingning the depset to a variable instead of computing it every time, or using an intermediate filegroup. Use Bazel profiles to confirm time reduction before doing micro-optimization.
  • If you need a list[File], and the list is generally small, ctx.files.X is okay to use. Note that ctx.files.X is lazily computed when you request the list. If you keep a large list around, there may be a memory impact.
  • If you need a list[File] but it is large, consider optimizing your implementation so it works with depsets.
  • ctx.file.X is okay to use because there's only a single file.

Updating external repositories

Inside the kernel tree, run:

prebuilts/kernel-build-tools/linux-x86/bin/external_updater update <project_path> --no-build

Example:

prebuilts/kernel-build-tools/linux-x86/bin/external_updater update external/python/absl-py --no-build

Always prefer tags. For example, if you are prompted with:

Current version: v1.4.0
Latest version: v2.1.0
Alternative latest version: fae7e951d46011fdaf62685893ef4efd48544c0a
Out of date!
Would you like to upgrade to sha fae7e951d46011fdaf62685893ef4efd48544c0a instead of tag v2.1.0? (yes/no)
We DO NOT recommend upgrading to sha fae7e951d46011fdaf62685893ef4efd48544c0a.

Enter “no” and proceed.