CMake

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.

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. Por 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 suporte ao 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_ARM_NEON

Ativa ou desativa NEON para armeabi-v7a. Não tem efeito para outras ABIs. O padrão é verdadeiro para a API de nível 23 ou mais recente (minSdkVersion ou ANDROID_PLATFORM). Caso contrário, o valor será falso. Para mais informações, consulte a documentação de suporte da Neon.

Valor Observações
TRUE Padrão para a API de nível 23 ou mais recente.
FALSE Padrão para a API de nível 22 ou anterior.

ANDROID_LD

Seleciona qual vinculador usar. No momento, lld (link em inglês) é experimental para o NDK e pode ser ativado com esse argumento.

Valor Observações
lld Ativa lld.
padrão Use o vinculador padrão para a ABI especificada.

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

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:

  1. Chame enable_language (link em inglês) com o valor definido como ASM_NASM.
  2. Chame add_library ou add_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.