Technical docs‎ > ‎New Build System‎ > ‎

Build System Concepts

The new build system goals are:
  • Make it easy to reuse code and resources
  • Make it easy to create several variants of an application, either for multi-apk distribution or for different flavors of an application
  • Ease of extending and configuring the build
To implement those goals we had to introduce some new concepts.

Product Flavors

A product flavor defines a customized version of the application build by the project. A single project can have different flavors which change the generated application.

Although different flavors could be very different applications, using Library Projects is a better use case for this, and the build system still supports this.
This new concept is designed to help when the differences are very, very minimum, for instance when using Google Play’s multi-apk support (e.g. the difference could be only gl textures compressed using a different format.)
If the answer to “Is this the same application?” is yes, then this is probably the way to go over Library Projects.

Product flavors can customize the following properties:
  • minSdkVersion
  • targetSdkVersion
  • versionCode
  • versionName
  • package name (overrides value from manifest)
  • release signing info (keystore, key alias, passwords,...).
  • BuildConfig: Ability to provide custom Java code.
  • NDK ABI filter (Not implemented yet)
  • test info
    • package name for test app (optional, default is <base>.test)
    • InstrumentationTestRunner class (optional)
  • Additionally, Product Flavor can provide their own source code, resources and manifest.

A project starts with a default Configuration. All the above properties can be set and it has its own source code, resources and manifest.
If the project does not create product flavors, then the application is built from this default configuration.

When a project declares Product Flavors, these extends the main configuration.

The source code of the product flavor is used in addition to the source code of the main configuration (1).
The resources of the product flavor overrides the resources of the main configuration.
The Manifest of the product flavor is merged on top of the Manifest of the main configuration.

(1) This allows some flexibility but also adds some restrictions:
  • Classes in the main configuration and in the product flavors can reference each other. All the source sets are used to generate a single output (instead of each generating its own output with a one-way dependency).
  • Classes exposed by product flavors and referenced by classes in the main configuration must be present in all product flavors, and must have the same API for all builds to succeed.
When a Product Flavor is defined, the main configuration is not available as a buildable product. To create more than one product, two or more flavors must be created and customized.

Note about the AndroidManifest attributes that can be set through the default Configuration or the flavor configurations:
Values present in the manifest file will be overridden with the values coming from the build configuration. All values, except for the package name can be omitted from the manifest file.
The main manifest must always have a package name. This is used as the package into which the R class is generated. Overriding the package name of a flavor has no impact on where the R class is generated. This is applied when the final APK is created only.

Build Types

A build type allows configuration of how an application is packaged for debugging or release purpose.

This concept is not meant to be used to create different versions of the same application. This is orthogonal to Product Flavor.

A Build Type provides configuration for the following build properties:
  • manifest debuggable flag
  • native compilation debug flag
  • proguard enabled + specific rules (Not implemented yet)
  • debug signing flag (ie whether to use debug key or release key)
  • package name suffix (2)
  • version name suffix (new in milestone 0.3)
  • Buildconfig
    • DEBUG flag. Set automatically based on the manifest debuggable flag.
    • Ability to provide custom Java code.
By default the build system provides two build types, debug and release, which can be reconfigured. New types can also be created to provide any combinations of the above properties.
For instance, one could make a Build Type that uses Proguard but creates a debuggable apk.

(2) This is appended to the manifest package name. The goal here is to allow having different build types create different apks that can all be installed at the same time. This is optional.

Like Product Flavors, Build Types can provide their own source code, resources and manifest.
The typical use cases are:
  • different MapView key in release and debug mode as this is linked to the signing key (3)
  • require extra permissions for a debug build, for instance ACCESS_MOCK_LOCATION
(3) If Product Flavors are used to create different versions built with different keys, the MapView API Key can be provided by the resources of the Product Flavor instead. See Build Variants below.

The source code and resources provided by the Build Types affects the project the same way Product Flavors do.

Build Variants

We’ve seen Product Flavors and Build Types and how both configure the output of a project.

In fact, the output of a project can only be the cross product of the Build Type and, if applicable, the Product Flavor. This is call a build variant.

If product flavors are defined, it is not possible to omit them.

For a project not customizing any Build Type or Product Flavor, the available build variants are debug and release versions, as the default configuration is implied.

However, if a project declares two flavors, for instance free and forpay, the available build variants are

  Debug Release
 Free Free-Debug Free-Release
 ForPay ForPay-Debug ForPay-Release

We have seen above how source code and resources provided by Build Types and Product Flavors extend or override the main configuration. When actually building an app, the Build Type and Product Flavor are both applied with the following rules:

All the source folders are used together (default config, build type, product flavor, and generated source code(4)) to create a single output.

This means the same restrictions mentioned above applies, with the addition that Build Types and Product Flavors cannot provide different versions of the same class.

(4) generated source is output from resource compilation (R.java), aidl and renderscript compilation, and things like BuildConfig which can be impacted by the BuildType. These are different per build variants.

Resources combination uses the following priority (lower number is higher priority and override higher number):
  1. build type
  2. flavor
  3. main configuration
Packaging of Assets and Java Resources follow a similar priority when encountering duplicates:
  1. Build Type
  2. Product Flavor
  3. default configuration
Note: Not implemented yet.

Flavor Groups

We’ve seen how variants are created from a Build Type and a Product Flavor. In some case it is useful to be able to have several dimensions of flavors. This is particularly useful when only packaging is impacted to use the multi-apk support in the Play Store.

For instance, let’s imagine a game that has GL texture for different formats, and has native component for arm, x86 and mips. Such a project could use a flavor dimension for the GL textures and one for the ABI to package.

Flavor groups are first defined. The order is important as it impacts Android resource priority.
Then each flavor is assigned to a group.

Variant are then created from each combinations of Build Types and Product Flavor Group.
Here’s an example giving us all the variant for 2 Texture formats and 2 ABIs.

   Debug Release
 GL Group ABI Group  
 DXT1ARM  dxt1-arm-debug dxt1-arm-release
 DXT1x86  dxt1-x86-debug dxt1-x86-release
 PVRTCARM  pvrtc-arm-debug pvrtc-arm-release
 PVRTCx86  pvrtc-x86-debug pvrtc-x86-release

Sourcesets

Due to Build Types and Product Flavors all providing their own sources, resources and manifest, the project structure need to change from the root level src/res/manifest used in the current build system.

Instead the build system moves to a top level src folder, containing folders for each build types, flavors as well as default config, and tests for each flavors. Each of those sub folders are self-contained for the element they represent and can contain a manifest, java source code, resources, etc...

A typical project will then have the following folder structure:
  • src/
    • main/
      • java/
      • jni/
      • resources/ -- this is java resources
      • aidl/
      • rs/
      • res/ -- this is Android resources
      • assets/
      • AndroidManifest.xml
    • debug/
      • Java, jni, res, AndroidManifest.xml, etc... (same as main)
    • release/
      • Java, jni, res, AndroidManifest.xml, etc... (same as main)
    • flavor1/
      • Java, jni, res, AndroidManifest.xml, etc... (same as main)
    • flavor2/
      • Java, jni, res, AndroidManifest.xml, etc... (same as main)
The current gen folder is to move under the project output, as it there are actually several of them, depending on the build variant. This is accompanied by the generated resources, and the generated (merged) Manifest.

Testing

In the current build system, test applications are created from a different project that is flagged as a “test project”. This project references the main project in order to get the main project’s classes on its classpath.

With possibly multiple Product Flavors it is important to test all of them.

The test projects are now part of the main projects. Sourcesets containing tests for the default configuration and each flavors are automatically used, and a test application is generated for each variant.

For a project with several flavors, the structure of the source folders will look like:
  • src/
    • main/
    • test/ -- this is the code to test “main”
    • debug/
    • release/
    • flavor1/
    • testFlavor1/ -- this is the code to test “flavor1”
    • flavor2/
    • testFlavor2/ -- this is the code to test “flavor2”
The manifest of a test application is always the same. However due to flavors being able to customize the package name of an application, it is important that the test manifest matches this package. To do this, test manifests are always generated.

The test applications are meant to run against a given Build Type variant. It is the “debug” variant by default, but it can be configured.

Building Library Projects

Library Projects are built in a very similar way to regular projects with a few exceptions.
  • There are no Product Flavor.
  • There are debug and release build types but they are used slightly differently.
Testing the library is important and this is done through the test component of the project. However, unlike a normal project, this component doesn’t generate a separate application from only its source set. Instead it implicitly references the library itself and packages it in its own apk.
This build is always a debug build.

When the Library Project is built and packaged into a distribution blob, then the build is always a release build.

Libraries can be packaging as binary bundle. These can be uploaded to online repository (Maven or Ivy) and reused by other projects.

They can also be used directly as part of a multi-project setup.

Lint integration

This is planned but it not available at the moment.