This doc aims to describe the Chrome build process that takes a set of .java
files and turns them into a classes.dex
file.
[TOC]
The following have supports_android
and requires_android
set to false by
default:
java_library()
: Compiles.java
->.jar
java_prebuilt()
: Imports a prebuilt.jar
file.
The following have supports_android
and requires_android
set to true. They
also have a default jar_excluded_patterns
set (more on that later):
android_library()
android_java_prebuilt()
All target names must end with "_java" so that the build system can distinguish them from non-java targets (or other variations).
Most targets produce two separate .jar
files:
- Device
.jar
: Used to produce.dex.jar
, which is used on-device. - Host
.jar
: For use on the host machine (junit_binary
/java_binary
).- Host
.jar
files live inlib.java/
so that they are archived in builder/tester bots (which do not archiveobj/
).
- Host
What are interface jars?:
- They contain
.class
files with all private symbols and all method bodies removed. - Dependant targets use interface
.jar
files to skip having to be rebuilt when only private implementation details change.
For prebuilt .jar
files: we use //third_party/ijar to create interface
.jar
files from the prebuilt ones.
For non-prebuilt .jar
files: we use [//third_party/turbine] to create interface
.jarfiles directly from
.javasource files. Turbine is faster than javac because it does not compile method bodies. Although Turbine causes us to compile files twice, it speeds up builds by allowing
javac` compilation
of targets to happen concurrently with their dependencies. We also use Turbine
to run our annotation processors.
This step is the only step that does not apply to prebuilt targets.
- All
.java
files in a target are compiled byjavac
into.class
files.- This includes
.java
files that live within.srcjar
files, referenced throughsrcjar_deps
.
- This includes
- The
classpath
used when compiling a target is comprised of.jar
files of its deps.- When deps are library targets, the Step 1
.jar
file is used. - When deps are prebuilt targets, the original
.jar
file is used. - All
.jar
processing done in subsequent steps does not impact compilation classpath.
- When deps are library targets, the Step 1
.class
files are zipped into an output.jar
file.- There is no support for incremental compilation at this level.
- If one source file changes within a library, then the entire library is recompiled.
- Prefer smaller targets to avoid slow compiles.
This step can be disabled via GN arg: use_errorprone_java_compiler = false
- Concurrently with step 1a: ErrorProne compiles java files and checks for bug patterns, including some custom to Chromium.
- ErrorProne used to replace step 1a, but was changed to a concurrent step after being identified as being slower.
This step happens only when targets have supports_android = true
. It is not
applied to .jar
files used by junit_binary
.
//third_party/bazel/desugar
converts certain Java 8 constructs, such as lambdas and default interface methods, into constructs that are compatible with Java 7.
This step happens only when this GN arg is set: use_jacoco_coverage = true
- Jacoco adds instrumentation hooks to methods.
This step happens only when targets that have jar_excluded_patterns
or
jar_included_patterns
set (e.g. all android_
targets).
- Remove
.class
files that match the filters from the.jar
. These.class
files are generally those that are re-created with different implementations further on in the build process.- E.g.:
R.class
files - a part of Android Resources. - E.g.:
GEN_JNI.class
- a part of our JNI glue. - E.g.:
AppHooksImpl.class
- howchrome_java
wires up different implementations for non-public builds.
- E.g.:
This step happens only when targets have supports_android = true
.
- d8 converts
.jar
files containing.class
files into.dex.jar
files containingclasses.dex
files. - Dexing is incremental - it will reuse dex'ed classes from a previous build if
the corresponding
.class
file is unchanged. - These per-library
.dex.jar
files are used directly by incremental install, and are inputs to the Apk step whenenable_proguard = false
.- Even when
is_java_debug = false
, many apk targets do not enable ProGuard (e.g. unit tests).
- Even when
- Each
android_apk
andandroid_bundle_module
template has a nestedjava_library
target. The nested library includes final copies of files stripped out by prior filtering steps. These files include:- Final
R.java
files, created bycompile_resources.py
. - Final
GEN_JNI.java
for JNI glue. BuildConfig.java
andNativeLibraries.java
(//base dependencies).
- Final
This step is skipped when building using Incremental Install.
When is_java_debug = true
:
- d8 merges all library
.dex.jar
files into a final.mergeddex.jar
.
When is_java_debug = false
:
- R8 performs whole-program optimization on all library
lib.java
.jar
files and outputs a final.r8dex.jar
.- For App Bundles, R8 creates a
.r8dex.jar
for each module.
- For App Bundles, R8 creates a
Test APKs are normal APKs that contain an <instrumentation>
tag within their
AndroidManifest.xml
. If this tag specifies an android:targetPackage
different from itself, then Android will add that package's classes.dex
to the
test APK's Java classpath when run. In GN, you can enable this behavior using
the apk_under_test
parameter on instrumentation_test_apk
targets. Using it
is discouraged if APKs have proguard_enabled=true
.
When enable_proguard=false
:
- Any library depended on by the test APK that is also depended on by the apk-under-test is excluded from the test APK's final dex step.
When enable_proguard=true
:
- Test APKs cannot make use of the apk-under-test's dex because only symbols
explicitly kept by
-keep
directives are guaranteed to exist after ProGuarding. As a work-around, test APKs include all of the apk-under-test's libraries directly in its own final dex such that the under-test apk's Java code is never used (because it is entirely shadowed by the test apk's dex).- We've found this configuration to be fragile, and are trying to move away from it.
- Calling native methods using JNI glue requires that a
GEN_JNI.java
class be generated that contains all native methods for an APK. There cannot be conflictingGEN_JNI
classes in both the test apk and the apk-under-test, so only the apk-under-test has one generated for it. As a result this, instrumentation test APKs that use apk-under-test cannot use native methods that aren't already part of the apk-under-test.
There are two ways to go about generating source files: Annotation Processors and custom build steps.
- These are run by
javac
as part of the compile step. - They cannot modify the source files that they apply to. They can only generate new sources.
- Use these when:
- an existing Annotation Processor does what you want (E.g. Dagger, AutoService, etc.), or
- you need to understand Java types to do generation.
- These use discrete build actions to generate source files.
- Some generate
.java
directly, but most generate a zip file of sources (called a.srcjar
) to simplify the number of inputs / outputs.
- Some generate
- Examples of existing templates:
jinja_template
: Generates source files using Jinja.java_cpp_template
: Generates source files using the C preprocessor.java_cpp_enum
: Generates@IntDef
s based on enums within.h
files.java_cpp_strings
: Generates String constants based on strings defined in.cc
files.
- Custom build steps are preferred over Annotation Processors because they are generally easier to understand, and can run in parallel with other steps (rather than being tied to compiles).