CMake

A partir do Android Studio 2.2, é possível usar o NDK e o CMake para compilar código em C e C++ a fim de criar uma biblioteca nativa. O Android Studio incluirá essa biblioteca no seu APK usando o Gradle, o sistema de compilação do ambiente de desenvolvimento integrado.

Caso você não tenha experiência em usar o CMake com o Android Studio, acesse Adicione código C e C++ ao seu projeto para ver como adicionar origens nativas ao seu projeto, criar um script de compilação do CMake e adicionar seu projeto do CMake como dependência do Gradle. Esta página fornece informações adicionais para personalizar a compilação do CMake.

Como usar variáveis do CMake no Gradle

Após vincular o Gradle ao seu projeto do CMake, é possível configurar variáveis específicas do NDK que mudam a maneira como o CMake compila bibliotecas nativas. Para passar um argumento ao CMake do arquivo build.gradle no nível do módulo, use o DSL a seguir:

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake build script.
    externalNativeBuild {
      cmake {
        ...
        // Use the following syntax when passing arguments to variables:
        // arguments "-DVAR_NAME=ARGUMENT".
        arguments "-DANDROID_ARM_NEON=TRUE",
        // If you're passing multiple arguments to a variable, pass them together:
        // arguments "-DVAR_NAME=ARG_1 ARG_2"
        // The following line passes 'rtti' and 'exceptions' to 'ANDROID_CPP_FEATURES'.
                  "-DANDROID_CPP_FEATURES=rtti exceptions"
      }
    }
  }
  buildTypes {...}

  // Use this block to link Gradle to your CMake build script.
  externalNativeBuild {
    cmake {...}
  }
}

A tabela a seguir descreve algumas variáveis que você pode configurar ao usar o CMake com o NDK.

Nome da variável Argumentos Descrição
ANDROID_PLATFORM

Para ver a lista completa de nomes de plataforma e imagens de sistema Android correspondentes, consulte APIs nativas do Android NDK.

Especifica o nome da plataforma Android de destino. Por exemplo, android-18 especifica o Android 4.3 (nível de API 18).

Em vez de alterar essa sinalização diretamente, defina a propriedade minSdkVersion nos blocos defaultConfig ou productFlavors do arquivo build.gradle no nível do módulo. Desse modo, sua biblioteca será usada somente por aplicativos instalados em dispositivos com as versões adequadas do Android. A cadeia de ferramentas do CMake escolhe a melhor versão da plataforma para a ABI que você está compilando de acordo com a seguinte lógica:

  1. Se houver uma versão da plataforma para a ABI igual a minSdkVersion, o CMake usará essa versão.
  2. Se houver versões da plataforma menores que minSdkVersion para a ABI, o CMake usará a de número maior. Essa é uma opção razoável porque, quando uma versão da plataforma não está presente, isso normalmente indica que não houve alterações nas APIs da plataforma nativa desde a versão anterior disponível.
  3. Em outros casos, o CMake usará a próxima versão da plataforma que for maior do que minSdkVersion.
ANDROID_STL

Para ver uma lista completa de opções, consulte Tempos de execução auxiliares

Por padrão, o CMake usa c++_static.

Especifica o STL que o CMake usará.

ANDROID_PIE
  • ON (padrão quando ANDROID_PLATFORM = android-16 ou maior)
  • OFF (padrão quando ANDROID_PLATFORM = android-15 ou menor)

Especifica se executáveis independentes de posição (PIE, na sigla em inglês) serão usados. O vinculador dinâmico do Android é compatível com PIE no Android 4.1 (nível de API 16) ou mais recente.

ANDROID_CPP_FEATURES

Esta variável é vazia por padrão. No entanto, veja a seguir alguns exemplos de argumentos que podem ser passados:

  • rtti (indica que o código usa RTTI)
  • exceptions (indica que o código usa exceções de C++)

Especifica determinados recursos do C ++ que o CMake precisa usar ao compilar sua biblioteca nativa, como RTTI (RunTime Type Information) e exceções de C ++.

ANDROID_ALLOW_UNDEFINED_SYMBOLS
  • TRUE
  • FALSE (padrão)

Especifica se é necessário gerar um erro de símbolo indefinido se o CMake encontrar uma referência indefinida ao compilar sua biblioteca nativa. Para desativar esses tipos de erros, defina esta variável como TRUE.

ANDROID_ARM_MODE
  • arm
  • thumb (padrão)

Especifica se é necessário gerar binários de destino ARM nos modos arm ou thumb. No modo thumb, cada instrução tem 16 bits de tamanho e é vinculada às bibliotecas STL no diretório thumb/. O argumento arm solicitará que o CMake gere os arquivos de objeto da biblioteca no modo ARM de 32 bits.

ANDROID_ARM_NEON
  • TRUE
  • FALSE

Especifica se a biblioteca nativa compilada pelo CMake será compatível com NEON. O valor padrão é “true” para nível de API 23 ou mais recentes e “false” em outros casos.

ANDROID_DISABLE_FORMAT_STRING_CHECKS
  • TRUE
  • FALSE (padrão)

Especifica se o código-fonte será compilado com proteção da string de formato. Quando esta opção estiver ativada, o compilador gerará um erro caso uma string de formato não constante seja usada em uma função do tipo printf.

O comando “build” do CMake

Ao depurar problemas de compilação do CMake, é recomendável conhecer os argumentos específicos que o Android Studio usa ao fazer a compilação cruzada para o Android.

O Android Studio salva os argumentos usados para executar uma compilação do CMake em um arquivo cmake_build_command.txt. Para cada interface binária do aplicativo (ABI, na sigla em inglês) segmentada pelo aplicativo e cada tipo de compilação para essas ABIs (especificamente, release ou debug), o Android Studio gera uma cópia do arquivo cmake_build_command.txt para essa configuração específica. O Android Studio colocará os arquivos gerados nos seguintes diretórios:

<project-root>/<module-root>/.externalNativeBuild/cmake/<build-type>/<ABI>/

Dica: Use o atalho de teclado de pesquisa (shift+shift) e digite cmake_build_command.txt no campo de entrada para ver rapidamente esses arquivos no Android Studio.

O snippet a seguir mostra um exemplo dos argumentos do CMake para compilar uma versão de depuração da amostra hello-jni segmentando a arquitetura armeabi-v7a.

Executable : /usr/local/google/home/{$USER}/Android/Sdk/cmake/3.6.3155560/bin/cmake
arguments :
-H/usr/local/google/home/{$USER}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/src/main/cpp
-B/usr/local/google/home/{$USER}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/.externalNativeBuild/cmake/arm7Debug/armeabi-v7a
-GAndroid Gradle - Ninja
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=/usr/local/google/home/{$USER}/Android/Sdk/ndk-bundle
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/usr/local/google/home/{$USER}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/build/intermediates/cmake/arm7/debug/obj/armeabi-v7a
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_MAKE_PROGRAM=/usr/local/google/home/{$USER}/Android/Sdk/cmake/3.6.3155560/bin/ninja
-DCMAKE_TOOLCHAIN_FILE=/usr/local/google/home/{$USER}/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DANDROID_NATIVE_API_LEVEL=23
-DANDROID_TOOLCHAIN=clang
jvmArgs : 

Argumentos de compilação

A tabela a seguir destaca os principais argumentos de compilação do CMake para Android. Estes argumentos de compilação não devem ser definidos pelos desenvolvedores. Em vez disso, o Plug-in do Android para Gradle define estes argumentos com base na configuração build.gradle no seu projeto.

Argumentos de compilação Descrição
-G <build-system>

Tipo de arquivos de compilação gerados pelo CMake.

Para projetos do Android Studio com código nativo, <build-system> é definido como Android Gradle - Ninja. Essa configuração indica que o CMake usa o sistema de compilação ninja para compilar e vincular as origens C/C ++ ao seu aplicativo. Além disso, o CMake gera um arquivo android_gradle_build.json que contém metadados para o plug-in do Gradle sobre a compilação do CMake, como sinalizadores do compilador e nomes dos alvos.

Essa configuração indica que o CMake usa o Gradle com o sistema de compilação ninja para compilar e vincular as origens C/C++ para seu aplicativo. O sistema de compilação ninja é o único gerador compatível com o Studio.

-DANDROID_ABI <abi>

O ABI de destino.

O NDK é compatível com um conjunto de ABIs, como descrito em Gerenciamento de ABI. Essa opção é semelhante à variável APP_ABI usada pela ferramenta ndk-build.

Por padrão, o Gradle compila a biblioteca nativa em arquivos .so separados para as ABIs compatíveis com o NDK e inclui todas elas no APK. Se você quiser que o Gradle compile somente para determinadas configurações de ABI, siga as instruções em Adicione código C e C++ ao seu projeto.

Se o ABI de destino não for especificado, o CMake usará armeabi-v7a por padrão.

Os nomes de destinos válidos são:

  • armeabi: ARMv5TE com base em CPU com operações de ponto flutuante de software.
  • armeabi-v7a: ARMv7 com base em dispositivos com instruções FPU de hardware (VFPv3_D16).
  • armeabi-v7a with NEON: Igual a armeabi-v7a, mas com as instruções de ponto flutuante do NEON ativadas. Essa configuração é equivalente a -DANDROID_ABI=armeabi-v7a e -DANDROID_ARM_NEON=ON.
  • arm64-v8a: Conjunto de instruções ARMv8 AArch64.
  • x86: Conjunto de instruções IA-32.
  • x86_64 - Conjunto de instruções para a arquitetura x86-64.
-DANDROID_NDK <path> Caminho absoluto para o diretório raiz da instalação do NDK no seu host.
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY <path> A localização no host em que o CMake coloca os arquivos de destino compilados de LIBRARY.
-DCMAKE_BUILD_TYPE <type> Essa função é semelhante aos tipos de compilação para a ferramenta ndk-build. Os valores válidos são Release e Debug. Para simplificar a depuração, o CMake não remove a versão de lançamento ou depuração como parte da compilação. No entanto, o Gradle remove os binários quando eles são incluídos no APK.
-DCMAKE_MAKE_PROGRAM <program-name> Ferramenta para lançar o sistema de compilação nativo. O plug-in do Gradle define esse valor para o gerador ninja do CMake incluído com o Android SDK.
-DCMAKE_TOOLCHAIN_FILE <path> O caminho do arquivo android.toolchain.cmake que o CMake usa ao fazer a compilação cruzada para Android. Normalmente, esse arquivo é localizado no diretório $NDK/build/cmake/, em que $NDK é o diretório de instalação do NDK no seu host. Para mais informações sobre o arquivo de cadeia de ferramentas, consulte Compilação cruzada para Android (página em inglês).
-DANDROID_NATIVE_API_LEVEL <level> Nível de API do Android para que o CMake faz a compilação.
-DANDROID_TOOLCHAIN <type> A cadeia de ferramentas do compilador que o CMake usa. O padrão é clang.

Compatibilidade do CMake com YASM

O NDK oferece suporte ao CMake para compilar código Assembly em YASM 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.

É recomendável vincular programas ou rotinas em linguagem Assembly ao código C para acessar as bibliotecas C ou funções do seu código Assembly. Além disso, é possível incluir rotinas Assembly curtas no seu código C compilado para aproveitar o melhor desempenho da máquina que o código Assembly oferece.

Para compilar código Assembly com o CMake, faça as seguintes alterações no arquivo CMakeLists.txt do projeto:

  1. Faça uma chamada para enable_language com o valor ASM_NASM.
  2. Se você estiver compilando uma biblioteca compartilhada ou um binário executável, faça uma chamada para add_library ou add_executable, respectivamente. Nos argumentos, passe uma lista de arquivos de origem com os arquivos .asm para o programa Assembly no YASM e os arquivos .c para as funções ou bibliotecas C associadas.

O snippet a seguir mostra como configurar o arquivo CMakeLists.txt para compilar um programa YASM como 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 compilar um programa YASM como executável, veja código yasm no repositório Git do NDK.

Comunicar problemas

Se você encontrar problemas não relacionados à versão de código aberto do CMake, comunique-os pelo android-ndk/ndk rastreador de problemas no GitHub.