Gunwant Jain

Building Android: BTS

Posted on ·
Table of Contents

So you've successfully compiled AOSP. But you are still not entertained and rather inquisitive about how things worked under the hood after you hit make bacon or brunch on your Lineage tree.

I am too and so I decided to take a look under the myriad that AOSP is. In this blog, I would share what I have learned so far, also being relevent to my GSoC obligations and therefore prepping hand by hand for a native aarch64 build.

So just setup your build environment and follow along.


Toolchains

Android has decided to phase out of using a GCC toolchain to the LLVM/Clang based solution since Android 8.0. People usually see a performance boost after the switch.

The actual Toolchain AOSP uses is already prebuilt and bundled with the entire AOSP source. But since AOSP only supports a Linux/Darwin x86 host enviroment for building, we will have to build our own toolchain supporting aarch64 hosts.

In order to do that we shall dive deep first and study llvm_android, the source of the prebuilt toolchain.

LLVM_android

I should mention that we could have used vanilla LLVM, but Google pushes some patches along with its LLVM fork which is required by AOSP. So it is better if we stick to the way Google handles it. These patches are applied to the toolchain in between its compilation.

After setting that toolchain up and doing a build test on x86, we realise that LLVM_android ships with prebuilt GCC and Clang toolchains. It uses Clang as its compiler and GCC's libs and binaries to build the stage 1 of the LLVM_android compiler.

LLVM_android also ships with the Bionic (Android's libc) sources. Though we notice while building the toolchain that only Bionic's headers are copied to the building toolchain. Bionic is not compiled at this stage.

After building swig and libedit we finally reach the start of stage 2. As expected the script now calls CMake again but with debug flags referring to the newly built Clang/++ and its libraries. It also calls the prebuilt libraries like it did earlier.

Briefly put these are all the building parts, in order:

  • For Linux:
    • Stage 1: builds the crude Clang
    • swig: required for Stage 2
    • libedit: required for Stage 2
    • Stage 2: builds the final Clang
  • For Android { arm, aarch64, x86, i386 }:
    • sysroots
    • compiler-rt
    • compiler-rt-i386-host (only for linux)
    • libomp
    • lldb-server
    • asan-mapfile

We will have to patch paths.py and replace the paths where the script calls its tools with the host's to compensate for the aarch64 host. Additionally, we should only sync the repositories which are necessary by editing the .repo/manifest.xml.

But enough of that, let's move on to building Android now.

Building Android using the toolchain we just made

If not done already, sync Lineage's Android Manifest. Put the newly made toolchain at prebuilts/clang/host/linux-x86/ and change the Clang version to that of the one we just built at build/soong/cmd/cc/config/global.go.

Now setup the environment and brunch your device.

source build/envsetup.sh
brunch _TARGET_NAME_

You'll notice that Building would give errors due to warnings. We come to yet another realisation that AOSP uses an older version of Clang (9.0.3 currently). The toolchain we just built is at bleeding edge, and is throwing errors at the old code as it finds vulnerabilities in it. We should recall that the lineage tree we synced is stable and so all the warnings generated by the newer compiler can actually be avoided. We could approach this in two ways:

  • Disable certain flags which throws those errors.
  • compile the current supported version of clang and use it instead.

Approach 1 would be time intensive and not scalable in any way, since we don't control the main tree and upstream could make our forks fatal. So we should follow the second approach.

And so once we get a hold of the correct toolchain version, the builds should not be throwing errors.

Let me explain the build process now. Once you make bacon or brunch, the soong build system is called and is compiled. The very first program built is soong_ui which is the build system used to iniate the build process. The target you lunch will be passed to the newly built soong_ui.

Go is used to build all of soong, and AOSP ships its prebuilt version of Go. We will have to change the location of GOROOT to use host's tools. GOROOT is called at build/blueprint/microfactory/microfactory.bash so we will edit it there.

Eventually it parses all the blueprint files (.bp extensions), and kati parses all the makefiles. Once the build.ninja file is created, the Android build is ready to start.

build.ninja is called and it starts building certain intermediates first which would be used later in assistance to build Android. One of these intermediates which is of concern to us is Bionic.

Compiling Bionic

Like we discussed earlier, Bionic's header files are already included in the llvm_android toolchain. But the libc sources are not compiled then.

libc and friends are actually compiled within the build as intermediates and then copied to some different folder under /out, from where it gets called later in the build process.

So, as I believe, once we have the toolchain ready, we should be good to go for building Android using aarch64 host. I will be setting up an ARM based VPS for the same, since I only have 32 gig of storage on my phone.


Conclusion

We need to build llvm_android on aarch64 host, by patching the build scripts provided by Google. Also we need to build the latest supported version of Clang/LLVM. Once that is done, we will have to edit the paths of various tools called in the AOSP tree with our host's. And then finally, test a build.

Achieving this should mark my task of building Android on aarch64 Host, which is incomplete in the sense that I don't have a separatable toolchain. But once we are through with this process, Separating Bionic and implementing crossdev for the toolchain would be easier.

Read other posts