O Android NDK oferece suporte ao uso do CMake (link em inglês) para
criar o código C e C++ do app. Esta página discute como usar o
CMake com o NDK pelo ExternalNativeBuild
do Plug-in do Android para Gradle ou ao
invocar o CMake diretamente.
O arquivo do conjunto de ferramentas do CMake
O NDK é compatível com o CMake graças a um arquivo do conjunto de ferramentas (link em inglês). Esses arquivos são do CMake e personalizam o comportamento do conjunto de ferramentas para compilação cruzada. O arquivo do conjunto de ferramentas usado para o NDK está localizado no NDK em <NDK>/build/cmake/android.toolchain.cmake
.
Parâmetros de compilação como ABI, minSdkVersion
etc. são fornecidos na linha de
comando ao invocar cmake
. Para ver uma lista de argumentos compatíveis, consulte a
seção Argumentos do conjunto de ferramentas.
O "novo" arquivo do conjunto de ferramentas
Os NDKs anteriores experimentaram uma nova implementação do arquivo do conjunto de ferramentas que reduziria as diferenças de comportamento entre o uso do arquivo do conjunto de ferramentas do NDK e o uso do suporte integrado do CMake. Isso acabou exigindo uma quantidade significativa de trabalho (que não foi concluído), mas não melhorou o comportamento, então não vamos mais fazer isso.
O arquivo do conjunto de ferramentas "novo" tem regressões de comportamento em comparação com o arquivo "legado". O comportamento padrão é o fluxo de trabalho recomendado. Se você estiver
usando -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF
, recomendamos remover essa flag
do seu build. O novo arquivo de conjunto de ferramentas nunca alcançou a paridade com o arquivo de
legado, portanto, é provável que haja regressões de comportamento.
Embora não seja recomendado usar o novo arquivo de conjunto de ferramentas, não há planos para removê-lo do NDK no momento. Isso interromperia os builds que dependem das diferenças de comportamento entre os arquivos de toolchain novos e legados, e infelizmente renomear a opção para deixar claro que "legado" é realmente recomendado também interromperia os usuários dessa opção. Se você estiver usando o novo arquivo do conjunto de ferramentas, não será necessário migrar, mas saiba que os bugs registrados no novo comportamento do arquivo do conjunto de ferramentas provavelmente não serão corrigidos. Nesse caso, você precisará migrar.
Uso
Gradle
O uso do arquivo do conjunto de ferramentas do CMake é automático ao usar
externalNativeBuild
. Consulte o guia Adicionar código C e C++ ao seu
projeto do Android Studio para saber mais.
Linha de comando
Ao criar com o CMake fora do Gradle, o próprio arquivo do conjunto de ferramentas e os respectivos argumentos precisam ser transmitidos para o CMake. Exemplo:
$ cmake \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$MINSDKVERSION \
$OTHER_ARGS
Argumentos do conjunto de ferramentas
Os argumentos a seguir podem ser transmitidos para o arquivo do conjunto de ferramentas do CMake. Se estiver criando com o Gradle, adicione argumentos ao android.defaultConfig.externalNativeBuild.cmake.arguments
, conforme descrito nos documentos do ExternalNativeBuild (link em inglês). Se estiver criando a partir da linha de comando, transmita argumentos para o CMake com -D
. Por exemplo, para forçar o armeabi-v7a a não criar com a compatibilidade
com Neon, transmita -DANDROID_ARM_NEON=FALSE
.
ANDROID_ABI
ABI desejada. Para ver informações sobre as ABIs compatíveis, consulte ABIs do Android.
Gradle
O Gradle fornece esse argumento automaticamente. Não defina esse argumento explicitamente no seu arquivo build.gradle
. Para controlar quais ABIs o Gradle visa, use abiFilters
conforme descrito em ABIs do Android.
Linha de comando
O CMake cria para um único destino por build. Para segmentar mais de uma ABI do Android, é necessário criar uma vez por ABI. É recomendável usar diretórios de compilação diferentes para cada ABI de modo a evitar colisões entre builds.
Valor | Observações |
---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
Igual a armeabi-v7a . |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
Especifica se serão geradas instruções de arm ou thumb para armeabi-v7a. Não tem efeito para outras ABIs. Para saber mais, consulte a documentação ABIs do Android.
Valor | Observações |
---|---|
arm | |
thumb | Comportamento padrão. |
ANDROID_NATIVE_API_LEVEL
Alias para ANDROID_PLATFORM.
ANDROID_PLATFORM
Especifica o nível mínimo da API compatível com o app ou a biblioteca. Esse valor corresponde à minSdkVersion
do app.
Gradle
Ao usar o Plug-in do Android para Gradle, esse valor é definido automaticamente para corresponder à minSdkVersion
do app e não pode ser definido manualmente.
Linha de comando
Ao invocar o CMake diretamente, esse valor assume como padrão a API de nível mais baixo compatível com o NDK em uso. Por exemplo, para o NDK r20, esse valor assume como padrão a API de nível 16.
Vários formatos são aceitos para esse parâmetro:
android-$API_LEVEL
$API_LEVEL
android-$API_LETTER
O formato $API_LETTER
permite que você especifique android-N
sem a necessidade de determinar o número associado a essa versão. Algumas versões receberam um aumento de API sem aumento de letras. Essas APIs podem ser especificadas anexando o sufixo -MR1
. Por exemplo, a API de nível 25 é android-N-MR1
.
ANDROID_STL
Especifica o STL que será usado para esse app. Para saber mais, consulte Compatibilidade
com a biblioteca C++. Por padrão, c++_static
será usado.
Valor | Observações |
---|---|
c++_shared | A variante da biblioteca compartilhada de libc++. |
c++_static | A variante da biblioteca estática de libc++. |
none | Não há suporte para a biblioteca C++ padrão. |
system | A STL do sistema |
Gerenciar flags do compilador
Se você precisar transmitir flags específicas para o compilador ou vinculador do build, consulte a documentação do CMake para set_target_compile_options e a família de opções relacionada. A seção “Veja também” na parte inferior dessa página tem algumas dicas úteis.
Em geral, a prática recomendada é aplicar flags do compilador como o escopo
mais estreito disponível. As flags que você quer aplicar a todos os destinos (como
-Werror
) são inconvenientes para repetir por módulo, mas raramente devem ser
aplicadas globalmente (CMAKE_CXX_FLAGS
), porque podem ter efeitos indesejados nas
dependências de terceiros no seu projeto. Nesses casos, as flags podem ser
aplicadas no escopo do diretório (add_compile_options
).
Para um subconjunto estreito de flags do compilador, elas também podem ser definidas no arquivo build.gradle
usando cppFlags
ou propriedades semelhantes. Não faça isso. As flags
transmitidas ao CMake pelo Gradle vão ter comportamentos de precedência surpreendentes. Em alguns
casos, elas substituem flags transmitidas implicitamente pela implementação, que são
necessárias para criar código do Android. Sempre prefira processar o comportamento do CMake
diretamente no CMake. Se você precisar controlar flags do compilador por buildType
do AGP,
consulte Trabalhar com tipos de build do AGP no CMake.
Trabalhar com tipos de build do AGP no CMake
Se você precisar adaptar o comportamento do CMake a um buildType
do Gradle personalizado, use esse
tipo de build para transmitir uma flag do CMake adicional (não uma flag do compilador) que os
scripts de build do CMake possam ler. Por exemplo, se você tiver variantes de build "free" e "premium"
que são controladas pelo build.gradle.kts e precisa transmitir
esses dados para o CMake:
android {
buildTypes {
free {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
}
}
}
premium {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
}
}
}
}
}
Em seguida, no CMakeLists.txt:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
O nome da variável fica a seu critério, mas evite usar qualquer coisa com um
prefixo ANDROID_
, APP_
ou CMAKE_
para evitar colisões ou confusão com
sinais existentes.
Consulte o exemplo de limpadores NDK (link em inglês).
Entender o comando de build do CMake
Ao depurar problemas de build do CMake, é útil conhecer os argumentos de build específicos que o Gradle usa ao fazer compilação cruzada para o Android.
O Plug-in do Android para Gradle salva os argumentos de compilação usados para executar um
build do CMake para cada par de ABI e tipo de build para o build_command.txt
. Esses arquivos são encontrados no seguinte diretório:
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
O snippet a seguir mostra um exemplo dos argumentos do CMake para criar uma
versão depurável do exemplo hello-jni
(link em inglês) destinada à arquitetura
armeabi-v7a
.
Executable : ${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/src/main/cpp
-DCMAKE_FIND_ROOT_PATH=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
-B${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/armeabi-v7a
-GNinja
jvmArgs :
Build command args: []
Version: 1
Usar bibliotecas pré-compiladas
Se a biblioteca pré-compilada que você precisa importar for distribuída como um AAR, siga os documentos de dependência do Studio para importar e usar essas bibliotecas. Se você não estiver usando o AGP, você pode seguir as etapas em https://google.github.io/prefab/example-lifecycle.html (link em inglês), mas é muito mais fácil migrar para o AGP.
No caso de bibliotecas que não são distribuídas como um AAR, para ver instruções sobre como usar bibliotecas
pré-compiladas com o CMake, consulte a documentação add_library
sobre destinos
IMPORTED
no manual do CMake (link em inglês).
Criar com código de terceiros
Há várias maneiras de usar código de terceiros como parte do seu projeto CMake, e a opção que funciona melhor depende da sua situação. Em geral, a melhor opção é não fazer isso. Em vez disso, crie um AAR para a biblioteca e consuma-o no aplicativo. Não é necessário publicar esse AAR. Ele pode ser interno ao seu projeto do Gradle.
Se não for possível, faça o seguinte:
- Forneça (ou seja, copie) a fonte de terceiros no seu repositório e use add_subdirectory para criá-lo. Isso só funcionará se a outra biblioteca também for criada com o CMake.
- Defina um ExternalProject.
- Crie a biblioteca separada do projeto e siga as instruções da seção Usar bibliotecas pré-compiladas para que ela seja importada como pré-compilada.
Compatibilidade com YASM no CMake
O NDK oferece compatibilidade com o CMake para criação do código Assembly escrito em YASM (link em inglês) para execução em arquiteturas x86 e x86-64. O YASM é um assembler de código aberto para arquiteturas x86 e x86-64, baseado no assembler NASM.
Para criar o código Assembly com o CMake, faça as seguintes mudanças no CMakeLists.txt
do projeto:
- Chame
enable_language
(link em inglês) com o valor definido comoASM_NASM
. - Chame
add_library
ouadd_executable
, dependendo se você está criando uma biblioteca compartilhada ou um binário executável. Nos argumentos, transmita uma lista de arquivos de origem composta pelos arquivos.asm
para o programa Assembly no YASM e arquivos.c
para as funções ou bibliotecas C associadas.
O snippet a seguir mostra como configurar CMakeLists.txt
para
criar um programa YASM como uma biblioteca compartilhada.
cmake_minimum_required(VERSION 3.6.0)
enable_language(ASM_NASM)
add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)
Para ver um exemplo de como criar um programa YASM como executável, consulte o teste yasm (link em inglês) no repositório git do NDK.
Denunciar problemas
Caso tenha problemas com o NDK ou o arquivo do conjunto de ferramentas do CMake, nos informe pelo Issue Tracker android-ndk/ndk no GitHub (link em inglês). Para problemas com o Gradle ou com o Plug-in do Android para Gradle, informe um bug do Studio.