Permalink
Browse files

Documentation for Android NDK support with Bazel

Example workspace: https://github.com/bazelbuild/examples/pull/66/files

RELNOTES: None.
PiperOrigin-RevId: 204162234
  • Loading branch information...
jin authored and Copybara-Service committed Jul 11, 2018
1 parent 93adb10 commit e169b94ce9b0e964b8edd231ef7eb3bed1cd2ccf
Binary file not shown.
@@ -0,0 +1,365 @@
---
layout: documentation
title: Using the Android Native Development Kit with Bazel
---

# Using the Android Native Development Kit with Bazel

_If you're new to Bazel, please start with the [Building Android with
Bazel](https://docs.bazel.build/versions/master/tutorial/android-app.html)
tutorial._

## Table of contents

- [Overview](#overview)
- [Prerequisites](#prerequisites)
- [Quick start](#quick-start)
- [Example setup](#example-setup)
- [Configuring the STL](#configuring-the-stl)
- [Configuring the target ABI](#configuring-the-target-abi)
- [Selecting a C++ standard](#selecting-a-c-standard)
- [How it works: introducing Android configuration transitions](#how-it-works-introducing-android-configuration-transitions)
- [Building a `cc_library` for Android without using `android_binary`](#building-a-cclibrary-for-android-without-using-androidbinary)

## Overview

Bazel can run in many different build configurations, including several that use
the Android Native Development Kit (NDK) toolchain. This means that normal
`cc_library` and `cc_binary` rules can be compiled for Android directly within
Bazel. Bazel accomplishes this by using the `android_ndk_repository` repository
rule.

## Prerequisites

Please ensure that you have installed the Android SDK and NDK.

To set up the SDK and NDK, add the following snippet to your `WORKSPACE`:

```python
android_sdk_repository(
name = "androidsdk", # Required. Name *must* be "androidsdk".
path = "/path/to/sdk", # Optional. Can be omitted if `ANDROID_HOME` environment variable is set.
)
android_ndk_repository(
name = "androidndk", # Required. Name *must* be "androidndk".
path = "/path/to/ndk", # Optional. Can be omitted if `ANDROID_NDK_HOME` environment variable is set.
)
```

For more information on the `android_ndk_repository` rule, see its the [Build
Encyclopedia
entry](https://docs.bazel.build/versions/master/be/android.html#android_ndk_repository).

## Quick start

To build C++ for Android, simply add `cc_library` dependencies to your
`android_binary` or `android_library` rules.

For example, given the following BUILD file for an Android app:

```python
# In <project>/app/src/main/BUILD.bazel
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
)
android_library(
name = "lib",
srcs = ["java/com/example/android/bazel/MainActivity.java"],
resource_files = glob(["res/**/*"]),
custom_package = "com.example.android.bazel",
manifest = "LibraryManifest.xml",
deps = [":jni_lib"],
)
android_binary(
name = "app",
deps = [":lib"],
manifest = "AndroidManifest.xml",
)
```

This BUILD file results in the following target graph:

<img src="/assets/android_ndk.png" alt="Build graph of Android project with cc_library dependencies" width="600px"/>

To build the app, simply run:

```
$ bazel build //app/src/main:app
```

The `bazel build` command compiles the Java files, Android resource files, and
`cc_library` rules, and packages everything into an APK:

```
$ zipinfo -1 bazel-bin/app/src/main/app.apk
nativedeps
lib/armeabi-v7a/libapp.so
classes.dex
AndroidManifest.xml
...
res/...
...
META-INF/CERT.SF
META-INF/CERT.RSA
META-INF/MANIFEST.MF
```

Bazel compiles all of the cc_libraries into a single shared object (`.so`) file,
targeted for the `armeabi-v7a` ABI by default. To change this or build for
multiple ABIs at the same time, see the section on [configuring the target
ABI](#configuring-the-target-abi).

## Example setup

This example is available in the [Bazel examples
repository](https://github.com/bazelbuild/examples/tree/master/android-ndk).

In the `BUILD.bazel` file, we define three targets with the `android_binary`,
`android_library` and `cc_library` rules.

The `android_binary` top-level target builds the APK.

The `cc_library` target contains a single C++ source file with a JNI function
implementation:

```c++
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_android_bazel_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
```
The `android_library` target specifies the Java sources, resource files, and the
dependency on a `cc_library` target. For this example, `MainActivity.java` loads
the shared object file `libapp.so`, and defines the method signature for the JNI
function:
```java
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
```
**Note**: The name of the native library is derived from the name of the top
level `android_binary` target. In this example, it is `app`.
## Configuring the STL
To configure the C++ STL, use the flag `--android_crosstool_top`.
```
bazel build //:app --android_crosstool_top=<target label>
```
The available STLs in `@androidndk` are:
| STL | Target label |
|---------|-----------------------------------------|
| STLport | `@androidndk//:toolchain-stlport` |
| libc++ | `@androidndk//:toolchain-libcpp` |
| gnustl | `@androidndk//:toolchain-gnu-libstdcpp` |
For r16 and below, the default STL is `gnustl`. For r17 and above, it is
`libc++`. For convenience, the target `@androidndk//:default_crosstool` is
aliased to the respective default STLs.
Please note that from r18 onwards, [STLport and gnustl will be
removed](https://android.googlesource.com/platform/ndk/+/master/docs/Roadmap.md#ndk-r18),
making `libc++` the only STL in the NDK.
See the [NDK
documentation](https://developer.android.com/ndk/guides/cpp-support) for more
information on these STLs.
## Configuring the target ABI
To configure the target ABI, use the `--fat_apk_cpu` flag as follows:
```
bazel build //:app --fat_apk_cpu=<comma-separated list of ABIs>
```
By default, Bazel builds native Android code for `armeabi-v7a`. To build for x86
(e.g. for emulators), pass `--fat_apk_cpu=x86`. To create a fat APK for multiple
architectures, you can specify multiple CPUs: `--fat_apk_cpu=armeabi-v7a,x86`.
If more than one ABI is specified, Bazel will build an APK containing a shared
object for each ABI.
Depending on the NDK revision and Android API level, the following ABIs are
available:
| NDK revision | ABIs |
|--------------|-------------------------------------------------------------|
| 16 and lower | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86\_64 |
| 17 and above | armeabi-v7a, arm64-v8a, x86, x86\_64 |
See [the NDK docs](https://developer.android.com/ndk/guides/abis.html) for more
information on these ABIs.
Multi-ABI Fat APKs are not recommended for release builds since they increase
the size of the APK, but can be useful for development and QA builds.
## Selecting a C++ standard
Use the following flags to build according to a C++ standard:
| C++ Standard | Flag |
|--------------|-------------------------|
| C++98 | Default, no flag needed |
| C++11 | `--cxxopt=-std=c++11` |
| C++14 | `--cxxopt=-std=c++14` |
For example:
```
bazel build //:app --cxxopt=-std=c++11
```
Read more about passing compiler and linker flags with `--cxxopt`, `--copt`, and
`--linkopt` in the [User
Manual](https://docs.bazel.build/versions/master/user-manual.html#flag--cxxopt).
Compiler and linker flags can also be specified as attributes in `cc_library`
using `copts` and `linkopts`. For example:
```python
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
```
## How it works: introducing Android configuration transitions
The `android_binary` rule can explicitly ask Bazel to build its dependencies in
an Android-compatible configuration so that the Bazel build *just works* without
any special flags, except for `--fat_apk_cpu` and `--android_crosstool_top` for
ABI and STL configuration.
Behind the scenes, this automatic configuration uses Android [configuration
transitions](https://docs.bazel.build/versions/master/skylark/rules.html#configurations).
A compatible rule, like `android_binary`, automatically changes the
configuration of its dependencies to an Android configuration, so only
Android-specific subtrees of the build are affected. Other parts of the build
graph are processed using the top-level target configuration. It may even
process a single target in both configurations, if there are paths through the
build graph to support that.
Once Bazel is in an Android-compatible configuration, either specified at the
top level or due to a higher-level transition point, additional transition
points encountered do not further modify the configuration.
The only built-in location that triggers the transition to the Android
configuration is `android_binary`'s `deps` attribute.
**Note:** The `data` attribute of `android_binary` intentionally does *not*
trigger the transition. Additionally, `android_local_test` and `android_library`
intentionally do *not* trigger the transition at all.
For example, if you try to build an `android_library` target with a `cc_library`
dependency without any flags, you may encounter an error about a missing JNI
header:
```
ERROR: <project>/app/src/main/BUILD.bazel:16:1: C++ compilation of rule '//app/src/main:jni_lib' failed (Exit 1)
app/src/main/cpp/native-lib.cpp:1:10: fatal error: 'jni.h' file not found
#include <jni.h>
^~~~~~~
1 error generated.
Target //app/src/main:lib failed to build
Use --verbose_failures to see the command lines of failed build steps.
```
Ideally, these automatic transitions should make Bazel do the right thing in the
majority of cases. However, if the target on the Bazel command-line is already
below any of these transition rules, such as C++ developers testing a specific
`cc_library`, then a custom `--crosstool_top` must be used.
## Building a `cc_library` for Android without using `android_binary`
To build a standalone `cc_binary` or `cc_library` for Android without using an
`android_binary`, use the `--crosstool_top`, `--cpu` and `--host_crosstool_top`
flags.
For example:
```shell
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain
```
Here, we specify that top-level `cc_library` and `cc_binary` targets are built
using the NDK toolchain. However, this causes Bazel's own host tools to be built
with the NDK toolchain (and thus for Android), because the host toolchain is
copied from the target toolchain. To work around this, we specify the value of
`--host_crosstool_top` to be `@bazel_tools//tools/cpp:toolchain` to explicitly
set the host's C++ toolchain.
With this approach, the entire build tree is affected.
Note that all of the targets on the command line must be compatible with
building for Android when specifying these flags, which may make it difficult to
use [Bazel
wild-cards](https://docs.bazel.build/versions/master/command-line-reference.html#target-pattern-syntax)
like `/...` and `:all`.
These flags can be put into a `bazelrc` config (one for each ABI), in
`<project>/.bazelrc`:
```
common:android_x86 --crosstool_top=@androidndk//:default_crosstool
common:android_x86 --cpu=x86
common:android_x86 --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
common:android_armeabi-v7a --crosstool_top=@androidndk//:default_crosstool
common:android_armeabi-v7a --cpu=armeabi-v7a
common:android_armeabi-v7a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
# In general
common:android_<abi> --crosstool_top=@androidndk//:default_crosstool
common:android_<abi> --cpu=<abi>
common:android_<abi> --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
```
Then, to build a `cc_library` for `x86_64` for example, run:
```shell
bazel build //my/cc/jni:target --config=android_x86_64
```
In general, use this method for low-level targets (like `cc_library`) or when
you know exactly what you're building; rely on the automatic configuration
transitions from `android_binary` for high-level targets where you're expecting
to build a lot of targets you don't control.
@@ -26,6 +26,7 @@ The following resources will help you work with Bazel on Android projects:
Android Studio using the [Android Studio with Bazel](https://ij.bazel.build/)
plugin.
* Bazel supports [Android instrumentation testing](https://docs.bazel.build/versions/master/android-instrumentation-test.html).
* Learn how to use the [Android NDK with Bazel](https://docs.bazel.build/versions/master/android-ndk.html).
* Learn [How Android Builds Work in Bazel](https://blog.bazel.build/2018/02/14/how-android-builds-work-in-bazel.html).

## Android and new rules
@@ -84,6 +84,11 @@ public Metadata getMetadata() {
<p>Note that building for Android also requires an <code>android_sdk_repository</code> rule in your
<code>WORKSPACE</code> file.
<p>
For more information, read the <a href="https://docs.bazel.build/versions/master/android-ndk.html">
full documentation on using Android NDK with Bazel</a>.
</p>
<h4 id="android_ndk_repository_examples">Examples</h4>
<pre class="code">

0 comments on commit e169b94

Please sign in to comment.