blob: d773f6d2d3135bb2d3e9bd4238394c87bd238cf5 [file] [log] [blame] [view]
# Kleaf Development
This documentation summarizes principles used in Kleaf development.
[TOC]
## Style Guides
* Follow [.bzl style guide](https://bazel.build/rules/bzl-style) for
[Starlark](https://bazel.build/rules/language) files.
* Follow [BUILD Style Guide](https://bazel.build/build/style-guide) for BUILD
files.
For Python:
* Use PEP 8 for formatting. Our team uses autopep8.
* Use [.pylintrc](../../.pylintrc) for linting. The file is copied from
[https://google.github.io/styleguide/pylintrc](https://google.github.io/styleguide/pylintrc)
with indent size changed to 4 to follow PEP 8.
## Conventions
* Follow these [conventions](https://bazel.build/extending/macros#conventions)
for Macros, in particular:
* In most cases, optional parameters should have a default value of `None`.
* Avoid using
[`unittest.suite`](https://github.com/bazelbuild/bazel-skylib/blob/main/docs/unittest_doc.md#unittestsuite)
. 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 {#damp}
`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:
```py
# GOOD
# BUILD.bazel
foo_library(name = "my_target_lib", ...)
foo_binary(name = "my_target", ...)
```
```py
# 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:
```py
# 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](https://bazel.build/rules/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 `depset`s.
- `ctx.file.X` is okay to use because there's only a single file.
## Updating external repositories
Inside the kernel tree, run:
```sh
prebuilts/kernel-build-tools/linux-x86/bin/external_updater update <project_path> --no-build
```
Example:
```sh
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.