Se você não conhece o Bazel, comece com o tutorial Como criar apps Android com o Bazel.
Visão geral
O Bazel pode ser executado em várias configurações de build diferentes, incluindo várias que usam
a cadeia de ferramentas do Kit de desenvolvimento nativo do Android (NDK). Isso significa que as regras normais de
cc_library
e cc_binary
podem ser compiladas para Android diretamente no
Bazel. O Bazel faz isso usando a regra de repositório android_ndk_repository
e a extensão bzlmod relacionada.
Para compilação geral do Android, use rules_android
.
Este tutorial demonstra como integrar dependências de biblioteca C++ a
apps Android e usa
rules_android_ndk
para descoberta e registro de
cadeia de ferramentas do NDK.
Pré-requisitos
Verifique se você instalou o SDK e o NDK do Android.
Configuração do NDK e do SDK
A configuração do repositório externo varia dependendo se você está usando WORKSPACE ou bzlmod (MODULE.bazel). O Bzlmod é a solução preferida para o Bazel 7 ou versões mais recentes. As estrofes de configuração MODULE.bazel e WORKSPACE são independentes umas das outras. Se você estiver usando uma solução de gerenciamento de dependências, não será necessário adicionar o boilerplate para a outra.
Configuração do MODULE.bazel do Bzlmod
Adicione o seguinte snippet ao seu MODULE.bazel:
# NDK
bazel_dep(name = "rules_android_ndk", version = "0.1.3")
android_ndk_repository_extension = use_extension("@rules_android_ndk//:extension.bzl", "android_ndk_repository_extension")
use_repo(android_ndk_repository_extension, "androidndk")
register_toolchains("@androidndk//:all")
# SDK
bazel_dep(name = "rules_android", version = "0.6.6")
register_toolchains(
"@rules_android//toolchains/android:android_default_toolchain",
"@rules_android//toolchains/android_sdk:android_sdk_tools",
)
android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
use_repo(android_sdk_repository_extension, "androidsdk")
register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
Configuração legada do WORKSPACE
Adicione o seguinte snippet ao seu WORKSPACE
:
load("@rules_android//rules:rules.bzl", "android_sdk_repository")
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.
)
load("@rules_android_ndk//:rules.bzl", "android_ndk_repository")
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.
)
Observações sobre compatibilidade para o WORKSPACE:
- As regras
rules_android
erules_android_ndk
exigem um modelo extra que não está representado no snippet do WORKSPACE acima. Para uma estrofe de instanciação atualizada e totalmente formada, consulte o arquivo WORKSPACE do app de exemplo básico derules_android_ndk
.
Para mais informações sobre a regra android_ndk_repository
, consulte a
string de documentação.
Início rápido
Para criar C++ para Android, basta adicionar dependências cc_library
às regras
android_binary
ou android_library
.
Por exemplo, considerando o seguinte arquivo BUILD
para um app Android:
# In <project>/app/src/main/BUILD.bazel
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_android//rules:rules.bzl", "android_binary", "android_library")
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",
)
Esse arquivo BUILD
resulta no seguinte gráfico de destino:
Figura 1. Crie um gráfico de build do projeto Android com dependências cc_library.
Para criar o app, basta executar:
bazel build //app/src/main:app --android_platforms=<your platform>
Se você não especificar --android_platforms
, o build vai falhar com erros sobre cabeçalhos JNI ausentes.
O comando bazel build
compila os arquivos Java, os arquivos de recursos do Android e as
regras cc_library
e empacota tudo em um 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
O Bazel compila todas as cc_libraries em um único arquivo de objeto compartilhado (.so
),
direcionado às arquiteturas especificadas por --android_platforms
.
Consulte a seção sobre como configurar a ABI de destino para
mais detalhes.
Exemplo de configuração
Este exemplo está disponível no repositório de exemplos do Bazel.
No arquivo BUILD.bazel
, três destinos são definidos com as regras android_binary
, android_library
e cc_library
.
O destino de nível superior android_binary
cria o APK.
O destino cc_library
contém um único arquivo de origem C++ com uma implementação de função 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());
}
O destino android_library
especifica as fontes Java, os arquivos de recursos e a
dependência de um destino cc_library
. Neste exemplo, MainActivity.java
carrega
o arquivo de objeto compartilhado libapp.so
e define a assinatura do método para a função
JNI:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
Como configurar a ABI de destino
Para configurar a ABI de destino, use a flag --android_platforms
da seguinte maneira:
bazel build //:app --android_platforms=comma-separated list of platforms
Assim como a flag --platforms
, os valores transmitidos para --android_platforms
são os rótulos de destinos platform
, usando valores de restrição padrão para descrever seu dispositivo.
Por exemplo, para um dispositivo Android com um processador ARM de 64 bits, você definiria sua plataforma assim:
platform(
name = "android_arm64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:arm64",
],
)
Todo platform
do Android precisa usar a restrição de SO @platforms//os:android
. Para migrar a restrição de CPU, confira este gráfico:
Valor da CPU | Plataforma |
---|---|
armeabi-v7a |
@platforms//cpu:armv7 |
arm64-v8a |
@platforms//cpu:arm64 |
x86 |
@platforms//cpu:x86_32 |
x86_64 |
@platforms//cpu:x86_64 |
E, claro, para um APK de várias arquiteturas, você transmite vários rótulos, por exemplo: --android_platforms=//:arm64,//:x86_64
(supondo que você os tenha definido no arquivo BUILD.bazel
de nível superior).
O Bazel não consegue selecionar uma plataforma Android padrão. Portanto, é necessário definir e especificar uma com --android_platforms
.
Dependendo da revisão do NDK e do nível da API do Android, as seguintes ABIs estão disponíveis:
Revisão do NDK | Interfaces binárias de aplicativo (ABIs, na sigla em inglês) |
---|---|
16 e versões anteriores | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
17 e mais recentes | armeabi-v7a, arm64-v8a, x86, x86_64 |
Consulte a documentação do NDK para mais informações sobre essas ABIs.
APKs multiarquitetura não são recomendados para builds de lançamento porque aumentam o tamanho do APK, mas podem ser úteis para builds de desenvolvimento e controle de qualidade.
Selecionar um padrão C++
Use as seguintes flags para criar de acordo com um padrão C++:
Padrão C++ | Sinalização |
---|---|
C++98 | Padrão, sem necessidade de flag |
C++11 | --cxxopt=-std=c++11 |
C++14 | --cxxopt=-std=c++14 |
C++17 | --cxxopt=-std=c++17 |
Exemplo:
bazel build //:app --cxxopt=-std=c++11
Leia mais sobre como transmitir flags do compilador e do vinculador com --cxxopt
, --copt
e --linkopt
no manual do usuário.
As flags do compilador e do vinculador também podem ser especificadas como atributos em cc_library
usando copts
e linkopts
. Exemplo:
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
Como criar um cc_library
para Android sem usar o android_binary
Para criar um cc_binary
ou cc_library
independente para Android sem usar um
android_binary
, use a flag --platforms
.
Por exemplo, supondo que você tenha definido plataformas Android em
my/platforms/BUILD
:
bazel build //my/cc/jni:target \
--platforms=//my/platforms:x86_64
Com essa abordagem, toda a árvore de build é afetada.
Essas flags podem ser colocadas em uma configuração bazelrc
(uma para cada ABI), em
project/.bazelrc
:
common:android_x86 --platforms=//my/platforms:x86
common:android_armeabi-v7a --platforms=//my/platforms:armeabi-v7a
# In general
common:android_<abi> --platforms=//my/platforms:<abi>
Em seguida, para criar um cc_library
para x86
, por exemplo, execute:
bazel build //my/cc/jni:target --config=android_x86
Em geral, use esse método para destinos de baixo nível (como cc_library
) ou quando você sabe exatamente o que está criando. Confie nas transições de configuração automática de android_binary
para destinos de alto nível em que você espera criar muitos destinos que não controla.