The CHRE build system is based on Make, and uses a set of Makefiles that allow building the CHRE framework for a variety of hardware and software architectures and variants (e.g. different combinations of CPU and OS/underlying system). It is also flexible to different build toolchains (though LLVM/Clang or GCC are recommended), by abstracting out key operations to a few Make or environment variables. While the CHRE framework source code may be integrated into another build system, it can be beneficial to leverage the existing build system to reduce maintenance burden when the CHRE framework is updated. Additionally, it should be possible to build nanoapps from the CHRE build system (to have commonality across devices), and the CHRE framework build shares configuration with the nanoapp build.
By default, the output of the build is linked into both a static archive libchre.a
and a shared library libchre.so
, though generally only one of the two is used, depending on the target device details.
The CHRE build system was originally designed around the philosophy that a vanilla invocation of make
or make all
should build any given nanoapp for all targets. This allows for efficient use of the job scheduler in the Make build system for multi-threaded builds and also promotes a level of separation of concerns between targets (this is not enforced by any Make language construct, merely convention). In practice, the CHRE build system is rarely used to build multiple targets with one invocation of Make. However, the design goal is carried forward for the variant support and separation of concerns between targets.
All variant-specific compiler and linker flags are held under variables that only apply to their specific target. This encourages developers to avoid leaking build details between targets. This is important because not all compiler or linker flags are compatible with all toolchains. For example: if a target uses a compiler that does not support -Wdouble-promotion
, but this were to be enforced for all builds, then by definition this target would not compatible with the CHRE build. The goal is for the CHRE build to be as flexible as possible.
The CHRE build system is implemented using template meta-programming techniques. A build template is used to create Make rules for tasks that are common to all targets. This includes compiling C/C++/assembly sources, linking, nanoapp header generation, etc. The rationale behind this approach is to reduce boilerplate when adding support for a new build target.
The build template is located at build/build_template.mk
, and is documented with all the variables used to generate build targets, like TARGET_CFLAGS
.
Compiling the framework for different devices is done by specifying the build target when invoking make
. Conventionally, build targets consist of three parts: (software) vendor, architecture and variant and follow the <vendor>_<arch>_<variant>
pattern. A “vendor” is typically the company that created the CHRE implementation, which may bring with it some details related to nanoapp compatibility, for example the Nanoapp Support Library from the same vendor may be required. The “arch” field refers to the Instruction Set Architecture (ISA) and related compiler configuration to create a binary for the target processor. The “variant” is primarily related to the underlying platform software that the CHRE framework builds on top of, such as the combination of operating system and other software needed to select the appropriate combination of code in the platform/
folder, but can also define other attributes of the build, such as the target memory region for the binary. If a vendor, architecture, or variant consist of multiple words or components, then they should be separated by a hyphen and not an underscore.
For example, if we assume that a fictional company named Aperture developed its own CHRE framework implementation, targeting a CPU family called Potato, and a collection of platform software called GladOS/Cake, then a suitable build target name would be aperture_potato_glados-cake
.
The build target may optionally have _debug
appended, which is a common suffix which enables -g
and any additional target-specific debug flags.
The architecture-specific portion of the build deals with mainly the build toolchain, and its associated flags.
It is easiest to check if the architecture is currently listed in build/arch
, and if it is, Hooray! You're (almost) done. It is still worthwhile to quickly read through to know how the build is layered.
CHRE expects the build toolchain to be exported via Makefile variables, specifically the compiler (TARGET_CC
), archiver (TARGET_AR
), and the linker (TARGET_LD
). Architecture specific compiler and linker flags are passed in via the TARGET_CFLAGS
and TARGET_LDFLAGS
respectively. Additional architecture-specific configuration is possible - refer to existing files under build/arch
and build/build_template.mk
for details.
Makefiles for each build target can be found at build/variant/<target_name>.mk
. These files are included at the end of the top-level Makefile, and has the responsibility of collecting arguments for the build template and invoking it to instantiate build rules. This involves doing steps including (not an exhaustive listing):
Setting the target name and platform ID
Configuring (if needed) and including the apporpriate build/arch/*.mk
file
Collecting sources and flags specific to the platform into TARGET_VARIANT_SRCS
and TARGET_CFLAGS
Including build/build_template.mk
to instantiate the build targets - this must be the last step, as the make targets cannot be modified once generated
Refer to existing files under build/variant
for examples.
While the build target is primarily concerned with configuring the CHRE build for a particular chipset, the same chipset can appear in multiple device models/SKUs, potentially with different peripheral hardware, targeted levels of feature support, etc. Additionally, a device/chip vendor may wish to provide additional build customization outside of the Makefiles contained in the system/chre project. The build system supports configuration at this level via the device variant makefile, typically named variant.mk
, which is injected into the build by setting the CHRE_VARIANT_MK_INCLUDES
environment variable when invoking the top-level Makefile. Refer to the file variant/android/variant.mk
for an example.
The file at platform/platform.mk
lists sources and flags needed to compile the CHRE framework for each supported platform. These must be added to Make variables prefixed with the platform name (for example, SIM_SRCS
for platform sources used with the simulator build target), and not COMMON_SRCS
or other common variables, to avoid affecting other build targets.
At the end of a successful build, the following are generated in the out
directory:
<build_target>/libchre.so
and libchre.a
: the resulting CHRE framework binary, built as a dynamic/static library
<build_target>_objs/
: Directory with object files and other intermediates
Depending on the build target, additional intermediates (e.g. nanopb_gen
for files generated for use with NanoPB)