Ninja and GN
Ninja is a build system focus on speed. It has very little options.
GN is a meta-build system that generates build files for Ninja.
GN script file usually has an extension of
*.gni are normally been imported by
Normally, we have some
BUILD.gn file in project directories. In the script, we define rules, targets… for GN.
GN has some built-in rules which can do specified work, rule can be called with predefined variables.
copy rule can do some file copy works, and
shared_library rule can build C++ code into a shared library.
And we can define new rules or redefine built-in rules using
Examples in Chromium Project:
Target represents a build operation, and it can have dependencies of other targets. We can use a rule to define targets.
For example, we have a GN file
chrome/android/BUILD.gn and it defined a target with name
chrome_public_apk using rule
chrome_public_apk_or_module_tmpl. And this target can build an apk of Chromium. The full name of this target is
When a target depends on other targets, we can use
deps to define that. And we can use relative name or full name of other targets.
GN grammar is very simple, and can be found here.
Frequently Used Commands
Here is some frequently used commands of GN and Ninja.
# configure args declared by the build (args.gn file)
GN in Chromium Android
Chromium project has defined many platform specified rules for different platforms including Windows, Linux, Mac, Android, iOS. For Android, these rules are defined in
build/config/android/rules.gni file. If you want to use these rules, import
rules.gni file first.
config can define variables for other targets, and we can use
-= for configs. For example:
Integrate Android Library from Sources
Android Resources / Manifest
# target name should ends with `_resources`
Override Manifest minSdkVersion
If you need to override
minSdkVersion of manifest in SDK, you can do it as follows.
<!-- chrome/android/java/AndroidManifest.xml -->
R.java will be generated by the
Normally, BuildConfig File is generated by gradle task. If build with GN, we can simply write a
BuildConfig.java file by hand and put it into some directory.
we can use
android_library to integrate Java source code of an Android Library.
# target name should ends with `_java`
CPP Source Code
If CPP code needs to be integrated as source code into main project, you can use
sources to add then into main project.
If CPP code needs to be compiled into standalone shared library (
.so file), then you need to see shared libary part for more details. Then use
loadable_modules variable to integrate the so file into main project.
You can config proguard here:
Integrate Local JAR Files
If you have a local JAR file, you can use
java_prebuilt rule to integrate it into project.
Integrate Local AAR Files
If you have a local AAR file, you can use
android_aar_prebuilt rule to integrate it into project.
# target name must ends with "_java"
Info File for AAR
AAR file needs an info file which describe the contents in the AAR and it is used for speeding up the build. Info file can be generated by
aar.py as follows.
python ./build/android/gyp/aar.py list input.aar --output out/output.info
The info file content is like follows:
aidl = 
Native Libraries (.so files)
If AAR contains so files, you must set one of
ignore_native_libraries to ignore the native libraries or
extract_native_libraries to use the native libraries and then use loadable_modules to integrate.
AIDL and Assets
android_aar_prebuilt currently not supports aidl and assets. If AAR contains these files, you must configure
ignore_aidl = true and
ignore_assets = true. If you need to add these files into project, you can extract the files and add them by other rules. See integrate android library from sources part for more detail.
Ignore/Replace Manifest in AAR
We can use ignore_manifest to exclude manifest from AAR. If you need to replace the manifest file, you need to create an
android_resources target to specify the new manifest file and add it to the deps of
Integrate Maven Project with Gradle
If you want to add maven dependencies for Android, a simple solution is to use
android_deps module provided by Chromium instead of downloading AAR/JAR and writing GN manually. This tool can resolve all the transitive dependencies and download all the AAR/JAR, generate info file, generate GN script and integrate them into project.
Here is the steps:
- Add your repositories and dependencies in
third_party/android_deps/build.gradle, just like the normal Gradle project does. If your package is used for building, use
buildCompile. If your package needs to be compiled into Android APK, use
third_party/android_deps/fetch_all.py, this script will run Gradle first to resolve dependencies and then do the remaining works.
- The script will create cpid package for each AAR/JAR file, and the packages will be managed by gclient. Run the commands printed by
fetch_all.pyto create new and updated packages via cpid.
- The generated GN targets is in
third_party/android_deps/BUILD.gn. You can use them in other GN scripts.
See more details here:
More Details of Java Related Rules
java_library rule is defined in
rules.gni can it called
java_library_impl defined in
java_prebuilt rule called
android_library rule called
java_library, and the
We can see more details about the variables of these rules in the source code.
Here is some common and useful variables:
supports_android: True if target can run on Android. This value is true by default in
android_library rule. If an target supports Android, then all its deps need to support Android.
require_android: True if target can only run on Android. It also means the target will depends on Android Java SDK. This value is true by default in
jar_excluded_patterns: We can use this variable to exclude/replace some Java class from an JAR/AAR file. For example:
jar_excluded_patterns = [
enable_bytecode_checks: By default, GN will run bytecode checks for prebuilt JAR/AAR to ensure Java class dependencies is OK. If class A in target T1 dependent on class B in target T2, then target T1 should dependent on T2. Sometimes we may want to disable bytecode checks. See trouble shotting part for more details.
Build C++ Code into Shared Library
A shared library is a binary file (dll for windows, so for linux) which can be loaded in the runtime. The following demo is on linux platform.
Defines for C++ code
We can use
defines to define some macro for C++ code.
// C++ code
Define exported symbols
Shared library binary file needs to define what symbols to be exported. Only exported symbols can be accessed from outside.
In linux, we can use an version script file to define exported C++ symbols for so file.
# version script file: login_so.lst
// C++ code
We can use
nm command to view exported symbols of so file.
# view exported symbols for so.
JNI Config for so File
See JNI part for more details.
If a so file has JNI calls, JNI related symbols must be exported, and Java code should call loadLibrary to load the so. See Shared Library part to known more about export symbols for so file.
Java Calls C++
You can use
native keywords to call C++ code in Java.
When running to this native method call, JVM will find the corresponding symbols from the loaded so files. The C++ code is like follows. If the native method is non-static, the parameter of obj will be this object from Java. If the native method is static, then the parameter of obj will be a reference to containg class instead.
C++ Calls Java
C++ calls Java code is bit like using reflection to call Java code.
When using JNI, most of the data type conversion job is done in the C++ code.
jni.h file has provide many definition of Java basic data types and conversion functions.
Here is a very simple demo of calling android log method from C++.
- Find the Java class with full class name.
- Get the static method with its name and class signature.
- Convert C++ string of
jstringtype as arguments.
- Call static Java method with arguments.
jintresult returned by Java code.
- Covert the result from Java type to C++ type and return the result.
Notes: Don’t forget to config proguard to avoid code obfuscation of the Java class.
You may noticed that a JNIEnv type of variable is always needed when C++ make any Java related operations. If C++ is called by Java, the method can receive this env arguments. But what if C++ code whats to call Java but no env arguments are passed here ?
Here is a simple solution:
- When JNI_OnLoad is called, save the JavaVM reference, and clear it when JNI_OnUnload is called.
- When a env arguments is needed, call AttachCurrentThread to get it.
- In fact, this is already implemented in Chromium project in
base/android/jni_android.ccfile. But if you have an standalone so file, you cannot access the code from other so directly. So you can implement it for you own so.
Djinni is a tool for generating cross-language type declarations and interface bindings. It’s designed to connect C++ with either Java or Objective-C.
You may noticed that if you use JNI directly without any auxiliary tools, C++ code of data type conversion and method call will be very complicated. One option is to use Djinni to help us generate these code. Of course, Djinni is not designed only to solve this problem.
Djinni’s support lib has many data type conversion utils, you can see it here:
JNI in Chromium
In Chromium project, JNI binding can be generated automatically with
jni_generator.py configured in GN.
Here is a simple demo of using JNI in Chromium.
You can see some more details here.
Type xxx is defined multiple times
some Java class is duplicated, normally because some package is defined repeatedly through more than one targets.
Type android.support.customtabs.ICustomTabsCallback is defined multiple times
Errors from jni_generator.py
Errors comes from
Inner class (%s) can not be imported and used by JNI (%s). Please import the outer class and use Outer.Inner instead.
By default, target of
chrome_public_apk will generate JNI binding for all Java source code if found keywords
native. But it has some issue currently. Some style of Java code is not supported.
// use full class name as native method params or return type without import it,
If the code needs JNI binding generation, you can rewrite the code to make it works. If the code does not need JNI binding generation, you can exclude them from JNI sources.
missing and no known rule to make it
You need to check if the missing files exists.
ninja: error: xxx.aar/java/cpp, needed by xxx, missing and no known rule to make it.
Not all deps support the Android platform
Exception: Not all deps support the Android platform: [u'com_xxx_java.build_config']
Dependency Checks Failed
Normally, GN will run byte code checks for prebuilt jar/aar to ensure Java class dependencies is OK. If class A in target T1 dependent on class B in target T2, then target T1 should dependent on T2.
If missing class is from Android SDK, you need to set
requires_android to true for the target.
If dependent class if from other target (maven package / local Java library / sources ), normally you need to add deps on the target.
Sometimes a maven package can have optional dependencies of other packages, and code in these dependencies will only be called when running specified code logic. In this scenario, you may want to disable byte code checks. This can lead to risks of Runtime Error, so use this option only if you are pretty sure the class in the optional packages will not be called.
C++ Build Issue
You may encounter some C++ build issue. For example, header file not found. Normally it is because of wrong
../../third_party/xxx.h:7:10: fatal error: 'xxx.hpp' file not found
You can use
gn desc to view all the variables for your target, including
ldflags, etc. Here is an incomplete output example.