Using AOSP Kernel Build
In my previous blog, we talked about how to cross-compile Android Kernel, but there is another official way that ODMs use to compile their kernel sources, via AOSP Kernel Build.
This blog covers 2 aspects:
- Compile using Legacy AOSP Kernel Build
- Add support for bazel Kernel Build
Bazel is an open-source build and test tool from Google. It supports a wide range of programming languages and platforms, and it's known for its speed and ability to handle large codebases efficiently. Bazel uses a build system that allows for incremental builds, parallel execution, and sophisticated dependency analysis, making it a popular choice for complex projects like AOSP.
AOSP Kernel Build System is the system used to compile the Linux kernel for Android devices. It includes the necessary build scripts, configurations, and toolchains needed to build the kernel from source. The build system is designed to work with AOSP's infrastructure and often uses tools like make
and ninja
for the actual compilation process.
In recent years, Bazel has been adopted by some parts of the AOSP for building specific components, but the traditional kernel build process has largely remained based on make
. However, there's ongoing work to integrate Bazel more deeply into AOSP's build system to leverage its advantages, like better dependency tracking and faster builds.
Prerequisites
Before diving into the AOSP Kernel Build process, certain requirements need to be met:
- Basic File and Text Editing: Familiarity with editing and writing files and text in a Linux environment.
- Linux Command Line: Comfort with using the Linux command line interface.
- Basic Git Knowledge: Understanding basic Git commands and workflows.
- Repo Sync and Manifests: Familiarity with syncing repositories using
repo sync
and working with manifest files.
Establishing a Build Environment
If you've already set up a Linux build environment, you can skip this step. Otherwise, follow these instructions to get started:
- Setup Script: Run the following command on your Linux machine with elevated permissions to set up the necessary environment:
curl https://raw.githubusercontent.com/akhilnarang/scripts/master/setup/android_build_env.sh | sudo sh
This script will install all the required dependencies for building the AOSP Kernel.
Setup Kernel Manifest
To build the AOSP kernel, you need to set up a kernel manifest, which will allow you to sync the required scripts and compiler.
To do that, head to here and sync the required manifest. I would recommend using common-
and your kernel version branch.
repo init -u https://android.googlesource.com/kernel/manifest/ -b common-android-4.19-stable --depth 1
- Depth Sync: The
--depth 1
option is used to perform a shallow sync, which is faster because it doesn't download the entire history. This is ideal if you don't need to develop on the build system itself. If you do plan to develop further, you can perform a full sync by omitting the--depth 1
option.
After a sync, you can view the structure of the source tree:
~/kernel/sp# tree -L 1
.
├── build
├── common
├── common-modules
├── hikey-modules
├── kernel
├── prebuilts
├── prebuilts-master
└── tools
8 directories, 0 files
Inside the common/
directory, you'll find the Android Common Kernel source, which we won't need for our purposes. You can replace this with your own kernel source.
Replace common kernel directory with your kernel source with version V4.19.
Setup Build Config
The AOSP Kernel Build system relies on a BUILD_CONFIG
variable, which configures the build environment, including kernel configuration and compiler flags.
- Create a file named
build.config.<device-name>
- Write basic configuration, like your kernel build directory, which is set to common by default, then required defconfig
KERNEL_DIR=common
. ${ROOT_DIR}/${KERNEL_DIR}/build.config.billie.common.clang
- Create a file named
build.config.<device-name>.common.clang
- Specify compiler settings, I am differing the names with my perspective, these can be anything.
. ${ROOT_DIR}/${KERNEL_DIR}/build.config.billie.common
CC=clang
LD=ld.lld
CLANG_TRIPLE=aarch64-linux-gnu-
- Create a file named
build.config.billie.common
- To call the common configuration and define additional settings.
. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
BUILD_INITRAMFS=1
DEFCONFIG=vendor/billie-perf_defconfig
Here, the build.config.common
file, which is already present in the kernel directory, contains the remaining configuration.
You can find the full reference commit here. Fill in the remaining configuration details as per your needs. Depending on your kernel version, you can fully utilize the Build System to start the kernel build process and package the distribution into a boot image.
Starting the build
Once you have set up your build environment and configured the necessary build files, you can begin compiling the kernel. The AOSP kernel build process uses the build/build.sh
script, and the build is controlled by the BUILD_CONFIG
environment variable.
Step-by-Step Guide to Starting the Build
- Setting the
BUILD_CONFIG
Variable:- The
BUILD_CONFIG
variable specifies the path to your configuration file, which contains all the necessary settings for your build, including kernel directories, compiler settings, and other environment variables. - You can set the
BUILD_CONFIG
variable by running the following command in your terminal:
- The
export BUILD_CONFIG=common/build.config.<device-name>
Replace <device-name>
with the actual name of your device, or the name you used in your configuration files.
- Running the Build Script:
- With the
BUILD_CONFIG
variable set, you can now trigger the build process by executing thebuild/build.sh
script. - The
-j
option is used to specify the number of jobs to run simultaneously, which can significantly speed up the build process on multi-core systems. For example, to run the build with 14 parallel jobs, you can use:
build/build.sh -j14
This command will start the kernel compilation process using the configuration you specified in the BUILD_CONFIG
file.
- Monitoring the Build Process:
- As the build progresses, you'll see various messages in the terminal, including the compilation of individual source files and linking of the final kernel image.
- The time it takes to complete the build will depend on your system's hardware and the complexity of the kernel being built. On a modern multi-core system, the build might take anywhere from several minutes to an hour or more.
- Successful Compilation:
- If everything is configured correctly, the build should complete without errors, and the kernel image will be successfully compiled.
Customizing build.config.common
It's important to note that the build.config.common
file can vary depending on the kernel sources you're working with. This file contains common settings that are shared across different configurations, such as the default compiler, build options, and any additional environment variables.
- Modifying
build.config.common
:- You may need to customize this file to match the specific requirements of your kernel version or device.
- For instance, the file might include different compiler flags or specify a different defconfig file depending on the target device or kernel features you're working with.
Part 2: Converting to BUILD.bazel
Inital Impressions
When I first started experimenting with Bazel for kernel builds, my initial reaction was that it seemed overly complex. However, after spending more time with it and testing it across different kernel versions, I came to a conclusion that it is still in development.
It's important to note that Bazel support in kernel builds is still evolving. Currently, only the latest Pixel kernels (starting from Pixel 8 and kernel version 5.15+) fully support Bazel builds. If you're working with older kernel versions or downstream kernels, you'll face challenges as Bazel support is either incomplete or entirely missing.
If you're interested in trying Bazel for your custom kernel builds, I strongly recommend starting with kernel version 5.15 or newer. These versions are better supported, and you'll encounter fewer issues.
For those working with older or downstream versions (like the 4.19 kernel mentioned earlier), be prepared for a more challenging experience. You'll likely need to make significant modifications to get Bazel working correctly. In this section, I'll share my experience porting a downstream version 4.19 kernel to Bazel, highlighting the challenges I encountered.
Understanding the Basic Bazel File Structure
At the heart of the Bazel build system is the BUILD.bazel
file. This file is used to define build rules, targets, and dependencies for your project. In the context of a kernel build, the BUILD.bazel
file acts as a blueprint, specifying how the kernel and its components should be compiled and linked.
Prebuilt Bazel Definitons
The top-level BUILD.bazel
file typically inherits prebuilt Bazel definitions. These definitions are found in the build/bazel/kleaf
directory within the AOSP kernel manifest source tree. The kleaf
directory contains essential Bazel macros and rules specifically designed for kernel builds.
You can load these definitions into your BUILD.bazel
file and override them to customize the build process. This allows you to:
- Declare New Build Targets: Define specific targets for different kernel components or modules.
- Use External Kernel Modules: Integrate external modules that may not be part of the main kernel source.
- Customize GKI Configs: Manage Generic Kernel Image (GKI) configurations, which are crucial for Android devices.
- Create Boot Images: Define how the boot image is generated, including the integration of the compiled kernel and ramdisk.
Example of a basic BUILD.bazel
file
Here's a simplified example of what a basic BUILD.bazel
file might look like:
package(
default_visibility = [
"//visibility:public",
],
)
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir")
load(
"//build/kernel/kleaf:kernel.bzl",
"kernel_build",
)
load("@kernel_toolchain_info//:dict.bzl", "BRANCH", "CLANG_VERSION")
kernel_build(
name = "kernel_billie",
srcs = glob(
["**"],
exclude = [
"**/BUILD.bazel",
"**/*.bzl",
".git/**",
],
),
outs = [
"Image",
"System.map",
"modules.builtin",
"modules.builtin.modinfo",
"vmlinux",
"vmlinux.symvers",
],
build_config = "build.config.billie",
)
Below is a basic BUILD.bazel
file that I created to compile the kernel source discussed earlier, utilizing the kleaf
framework provided by Bazel. This file is tailored to work with the specific kernel version and configuration we've been discussing.
Rather than me explaining each and every bit of line, I'll prefer going through bazel documentation to checkout, the changes from build.config
towards BUILD.bazel
here.
Sync Kernel Manifest
To ensure you're working with the latest tools and scripts required for kernel compilation, I recommend syncing the common-android-mainline
branch from the kernel/refs+
. This branch is kept up-to-date with the latest changes and includes upstreamed scripts that are essential for a smooth build process.
repo init -u https://android.googlesource.com/kernel/manifest -b common-android-mainline --depth 1
This branch is particularly advantageous because it includes:
- Latest Compilation Tools: Ensures you have the most recent versions of tools required for kernel builds.
- Upstreamed Scripts: Contains updated scripts that streamline various stages of the build process, from kernel configuration to image generation.
- Boot Image Generation: Provides tools necessary for creating boot images directly from the compiled kernel.
- Kernel ABI and Symbol Generation: Includes utilities for generating ABI symbols, which are critical for ensuring compatibility with the Android ecosystem.
- Distribution and Artifacts: Facilitates the creation of distribution artifacts, making it easier to package and deploy your kernel.
After Sync, the new source tree should look like:
~/kernel/mainline# tree -L 1
.
├── build
├── common
├── common-modules
├── external
├── kernel
├── MODULE.bazel -> build/kernel/kleaf/bzlmod/bazel.MODULE.bazel
├── prebuilts
├── test
└── tools
8 directories, 1 file
Once the source tree is set up, the next step is to replace the common
directory with your custom kernel source. After doing this, you can proceed to write or modify the BUILD.bazel
file to tailor the build process to your specific kernel.To compile the kernel target
Start the build
To initiate the build process for your custom kernel using Bazel:
tools/bazel build //common:kernel_billie
tools/bazel
: This specifies the path to the Bazel executable or script that we will use to run the build.build
: This Bazel command triggers the build process for the specified target.//common:kernel_billie
: This is the Bazel target you are building. In this case,kernel_billie
is the target defined in theBUILD.bazel
file located in thecommon
directory.
This will start the compilation process.
Porting a Downstream Version 4.19 Kernel to Bazel
When I attempted to port a 4.19 downstream kernel to Bazel, I encountered several challenges:
- Missing Bazel Support: The kernel version didn't natively support Bazel, so I had to manually create
BUILD.bazel
files for various components. - Toolchain Compatibility: Ensuring that the toolchain definitions in the Bazel files matched the kernel's requirements was tricky, especially when dealing with older versions of Clang or GCC.
- Dependency Management: Some external kernel modules and dependencies required additional customization to work with Bazel.
Successful Compilation
- If everything is configured correctly, the build should complete without errors, and the kernel image will be successfully compiled.
For those interested in diving deeper, I encourage you to explore the official Bazel documentation, which provides extensive resources on setting up and customizing Bazel builds. Ill put everything in references, or you can mail me to ask specific things.
Tips & Tricks:
- When setting up your own kernel build definitions and Bazel configurations, it's beneficial to refer to the Pixel kernel manifest repositories and their associated
build.config.pixel
files. These resources are maintained by Google and provide a robust base for writing your custom build definitions and Bazel declarations.
References:
- Kernel Manifest
- Kernel Trees:
- AOSP Kernel Build
- Build with Bazel
Let me know your new experiences at my email.