Google has a fair document for building kernel for Android. Yet it didn't cover how to integrate the kernel with AOSP source tree so that kernel gets built along with whole platform, which I'll explain in this post. Here I'll mainly focus on android-4.4.4_r1 (Kitkat) for Nexus 5 (hammerhead). The instructions should be easy to adapt to other models or AOSP releases.

Determine Kernel Version

The best and safest way to determine the right kernel version you need is to examine the pre-included kernel image. For hammerhead, it's in device/lge/hammerhead-kernel/.

$ bzgrep -a 'Linux version' device/lge/hammerhead-kernel/vmlinux.bz2
Linux version 3.4.0-gd59db4e (android-build@vpbs1.mtv.corp.google.com) (gcc version 4.7 (GCC) ) #1 SMP PREEMPT Mon Mar 17 15:16:36 PDT 2014

As per this stackoverflow thread, the commit hash you want is d59db4e part from the version name, without leading g.

Download the Sources

For hammerhead, the kernel sources lie in msm tree. After cloning it into kernel directory, checkout the commit hash you found in above step.

$ git clone https://android.googlesource.com/kernel/msm.git kernel
$ cd kernel
$ git checkout d59db4e

Adapt kernel/AndroidKernel.mk

Two changes need to be made for kernel to be successfully built in-tree.

  • Use zImage-dtb instead of zImage as target.

First, change TARGET_PREBUILT_INT_KERNEL (~line 8).

-TARGET_PREBUILT_INT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/zImage
+TARGET_PREBUILT_INT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/zImage-dtb

Then change corresponding make rule (~line 47).

$(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_CONFIG) $(KERNEL_HEADERS_INSTALL)
-       $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi-
+       $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- zImage-dtb
  • Do not build modules (~line 48-51).
-       $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- modules
-       $(MAKE) -C kernel O=../$(KERNEL_OUT) INSTALL_MOD_PATH=../../$(KERNEL_MODULES_INSTALL) INSTALL_MOD_STRIP=1 ARCH=arm CROSS_COMPILE=arm-eabi- modules_install
-       $(mv-modules)
-       $(clean-module-folder)

Adapt device/lge/hammerhead Project

Next we need to tell the device to build kernel, instead of copying the pre-built one. This patch should do the trick. Basically, a new AndroidBoard.mk file is added to include the rules to build and copy kernel. And some lines in device.mk related to kernel are removed, since it's already taken care of in AndroidBoard.mk.

Build It!

After all above changes, do a make clobber to make sure we have a clean slate, otherwise, some strange errors may strike you. Then just build AOSP in normal way and kernel should get built on the fly.

Here is a snapshot of the kernel version I built. The version name is no longer d59db4e because I made some changes.

Credits

Thanks to this blog from Jameson for describing most of it.

UPDATE

The above setup works fine as long as you didn't specify a separate output directory, since we assume the kernel output directory is ../$(KERNEL_OUT) in make options. Apparently, it will fail if the out directory is not the default one.

The kernel Makefile support two ways of specify output directory (see comments starting from line 79). One is to use O= command line option, another is to set the KBUILD_OUTPUT environment variable.

Since we use -C option to first switch working directory, O= options is a bit tricky to use, so we leverage the KBUILD_OUT variable.

We first figure out the absolute path of the KERNEL_OUT

FULL_KERNEL_OUT := $(shell readlink -e $(KERNEL_OUT))

Then we set KBUILD_OUT before calling make:

$(KERNEL_CONFIG): $(KERNEL_OUT)
    env KBUILD_OUTPUT=$(FULL_KERNEL_OUT) \
    $(MAKE) -C kernel ARCH=arm CROSS_COMPILE=arm-eabi- $(KERNEL_DEFCONFIG)

This way will work no matter where the actual AOSP output directory is.

UPDATE (09/03/2015)

As Ryan pointed out, for Mac users, you may need to install GNU readlink, instead of the built-in one.