aboutsummaryrefslogtreecommitdiffhomepage
path: root/site/docs/android-ndk.md
blob: cf47487d656b5122414ba1fc24631de166248887 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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-cc_library-for-android-without-using-android_binary)

## 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:

```
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` for example, run:

```
bazel build //my/cc/jni:target --config=android_x86
```

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.