Si eres nuevo en Bazel, comienza con el instructivo Compila Android con Bazel.
Descripción general
Bazel se puede ejecutar en muchas configuraciones de compilación diferentes, incluidas varias que usan
la cadena de herramientas del Kit de desarrollo nativo (NDK) de Android. Esto significa que las reglas normales
cc_library y cc_binary se pueden compilar para Android directamente en
Bazel. Bazel lo logra con la regla de repositorio android_ndk_repository.
Requisitos previos
Asegúrate de haber instalado el SDK y el NDK de Android.
Para configurar el SDK y el NDK, agrega el siguiente fragmento a tu WORKSPACE:
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.
)
Para obtener más información sobre la regla android_ndk_repository, consulta la entrada de Build
Encyclopedia.
Si usas una versión reciente del NDK de Android (r22 y versiones posteriores), usa la
implementación de Starlark de android_ndk_repository.
Sigue las instrucciones de
su README.
Inicio rápido
Para compilar C++ para Android, simplemente agrega cc_library dependencias a tus
android_binary o android_library reglas.
Por ejemplo, dado el siguiente archivo BUILD para una app para Android:
# 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",
)
Este archivo BUILD genera el siguiente gráfico de destino:

Figura 1: Gráfico de compilación del proyecto de Android con dependencias de cc_library
Para compilar la app, simplemente ejecuta lo siguiente:
bazel build //app/src/main:appEl comando bazel build compila los archivos Java, los archivos de recursos de Android y
cc_library reglas, y empaqueta todo en un 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.MFBazel compila todas las cc_libraries en un solo archivo de objeto compartido (.so),
orientado a la ABI armeabi-v7a de forma predeterminada. Para cambiar esto o compilar para
varias ABI al mismo tiempo, consulta la sección sobre cómo configurar la
ABI de destino.
Configuración de ejemplo
Este ejemplo está disponible en el repositorio de ejemplos de Bazel.
En el archivo BUILD.bazel, se definen tres destinos con las reglas android_binary,
android_library y cc_library.
El destino de nivel superior android_binary compila el APK.
El destino cc_library contiene un solo archivo fuente de C++ con una implementación de función JNI:
#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());
}
El destino android_library especifica las fuentes Java, los archivos de recursos y la
dependencia de un destino cc_library. En este ejemplo, MainActivity.java carga
el archivo de objeto compartido libapp.so y define la firma del método para la función JNI:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
Configura la STL
Para configurar la STL de C++, usa la marca --android_crosstool_top.
bazel build //:app --android_crosstool_top=target labelLas STL disponibles en @androidndk son las siguientes:
| STL | Etiqueta de destino |
|---|---|
| STLport | @androidndk//:toolchain-stlport |
| libc++ | @androidndk//:toolchain-libcpp |
| gnustl | @androidndk//:toolchain-gnu-libstdcpp |
Para r16 y versiones anteriores, la STL predeterminada es gnustl. Para r17 y versiones posteriores, es
libc++. Para mayor comodidad, el destino @androidndk//:default_crosstool tiene
un alias para las STL predeterminadas respectivas.
Ten en cuenta que, a partir de r18, se quitarán STLport y gnustl
,
lo que hará que libc++ sea la única STL en el NDK.
Consulta la documentación del NDK para obtener más información sobre estas STL.
Configura la ABI de destino
Para configurar la ABI de destino, usa la marca --fat_apk_cpu de la siguiente manera:
bazel build //:app --fat_apk_cpu=comma-separated list of ABIsDe forma predeterminada, Bazel compila código nativo de Android para armeabi-v7a. Para compilar para x86
(como para emuladores), pasa --fat_apk_cpu=x86. Para crear un APK grueso para varias
arquitecturas, puedes especificar varias CPUs: --fat_apk_cpu=armeabi-v7a,x86.
Si se especifica más de una ABI, Bazel compilará un APK que contenga un objeto compartido para cada ABI.
Según la revisión del NDK y el nivel de API de Android, están disponibles las siguientes ABI:
| Revisión del NDK | ABI |
|---|---|
| 16 y versiones anteriores | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
| 17 y versiones posteriores | armeabi-v7a, arm64-v8a, x86, x86_64 |
Consulta los documentos del NDK para obtener más información sobre estas ABI.
No se recomiendan los APK gruesos de varias ABI para las compilaciones de lanzamiento, ya que aumentan el tamaño del APK, pero pueden ser útiles para las compilaciones de desarrollo y QA.
Selecciona un estándar de C++
Usa las siguientes marcas para compilar según un estándar de C++:
| Estándar de C++ | Marcar |
|---|---|
| C++98 | Configuración predeterminada, no se necesita ninguna marca |
| C++11 | --cxxopt=-std=c++11 |
| C++14 | --cxxopt=-std=c++14 |
Por ejemplo:
bazel build //:app --cxxopt=-std=c++11Obtén más información para pasar marcas del compilador y del vinculador con --cxxopt, --copt y
--linkopt en el Manual del usuario.
Las marcas del compilador y del vinculador también se pueden especificar como atributos en cc_library
con copts y linkopts. Por ejemplo:
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
Integración con plataformas y cadenas de herramientas
El modelo de configuración de Bazel se está moviendo hacia
plataformas y
cadenas de herramientas. Si tu
compilación usa la marca --platforms para seleccionar la arquitectura o el sistema operativo
para compilar, deberás pasar la marca --extra_toolchains a Bazel para
usar el NDK.
Por ejemplo, para integrar con la cadena de herramientas android_arm64_cgo que proporcionan
las reglas de Go, pasa --extra_toolchains=@androidndk//:all además de la
--platforms marca.
bazel build //my/cc:lib \
--platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
--extra_toolchains=@androidndk//:allTambién puedes registrarlos directamente en el archivo WORKSPACE:
android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")
El registro de estas cadenas de herramientas le indica a Bazel que las busque en el archivo BUILD
del NDK (para NDK 20) cuando resuelva las restricciones de arquitectura y sistema operativo:
toolchain(
name = "x86-clang8.0.7-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:x86_32",
],
toolchain = "@androidndk//:x86-clang8.0.7-libcpp",
)
toolchain(
name = "x86_64-clang8.0.7-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:x86_64",
],
toolchain = "@androidndk//:x86_64-clang8.0.7-libcpp",
)
toolchain(
name = "arm-linux-androideabi-clang8.0.7-v7a-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:arm",
],
toolchain = "@androidndk//:arm-linux-androideabi-clang8.0.7-v7a-libcpp",
)
toolchain(
name = "aarch64-linux-android-clang8.0.7-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:aarch64",
],
toolchain = "@androidndk//:aarch64-linux-android-clang8.0.7-libcpp",
)
Cómo funciona: Presentamos las transiciones de configuración de Android
La regla android_binary puede pedirle explícitamente a Bazel que compile sus dependencias en
una configuración compatible con Android para que la compilación de Bazel simplemente funcione sin
marcas especiales, excepto --fat_apk_cpu y --android_crosstool_top para
la configuración de ABI y STL.
En segundo plano, esta configuración automática usa transiciones de configuración de Android configuration transitions.
Una regla compatible, como android_binary, cambia automáticamente la
configuración de sus dependencias a una configuración de Android, por lo que solo
se ven afectados los subárboles específicos de Android de la compilación. Otras partes del gráfico de compilación
se procesan con la configuración de destino de nivel superior. Incluso puede
procesar un solo destino en ambas configuraciones, si hay rutas de acceso a través del
gráfico de compilación para admitirlo.
Una vez que Bazel está en una configuración compatible con Android, ya sea especificada en el nivel superior o debido a un punto de transición de nivel superior, los puntos de transición adicionales que se encuentran no modifican aún más la configuración.
La única ubicación integrada que activa la transición a la configuración de Android
es el atributo android_binary's deps.
android_local_test y android_library
_no_ activan la transición de forma intencional.
Por ejemplo, si intentas compilar un destino android_library con una cc_library
dependencia sin ninguna marca, es posible que encuentres un error sobre un encabezado JNI
faltante:
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.
Lo ideal es que estas transiciones automáticas hagan que Bazel haga lo correcto en la
mayoría de los casos. Sin embargo, si el destino en la línea de comandos de Bazel ya está
por debajo de cualquiera de estas reglas de transición, como los desarrolladores de C++ que prueban un
cc_library, se debe usar un --crosstool_top personalizado.
Compila una cc_library para Android sin usar android_binary
Para compilar una cc_binary o cc_library independiente para Android sin usar una
android_binary, usa las marcas --crosstool_top, --cpu y --host_crosstool_top.
Por ejemplo:
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchainEn este ejemplo, los destinos cc_library y cc_binary de nivel superior se compilan
con la cadena de herramientas del NDK. Sin embargo, esto hace que las propias herramientas de host de Bazel se compilen
con la cadena de herramientas del NDK (y, por lo tanto, para Android), ya que la cadena de herramientas del host se
copia de la cadena de herramientas de destino. Para solucionar este problema, especifica el valor de
--host_crosstool_top como @bazel_tools//tools/cpp:toolchain para
establecer explícitamente la cadena de herramientas de C++ del host.
Con este enfoque, se ve afectado todo el árbol de compilación.
Estas marcas se pueden colocar en una bazelrc configuración (una para cada ABI), en
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
Luego, para compilar una cc_library para x86, por ejemplo, ejecuta lo siguiente:
bazel build //my/cc/jni:target --config=android_x86En general, usa este método para destinos de bajo nivel (como cc_library) o cuando
sepas exactamente lo que estás compilando; confía en las transiciones de configuración automáticas
de android_binary para destinos de alto nivel en los que esperas
compilar muchos destinos que no controlas.