CMake

El NDK de Android admite el uso de CMake para compilar código C y C++ para tu app. En esta página, se analiza cómo usar CMake con el NDK a través de ExternalNativeBuild del complemento de Gradle para Android o cuando se invoca directamente a CMake.

El archivo de la cadena de herramientas de CMake

El NDK admite CMake a través de un archivo de cadena de herramientas. Los archivos de cadena de herramientas son archivos de CMake que personalizan el comportamiento de la cadena de herramientas para la compilación de forma cruzada. El archivo de cadena de herramientas que se usa para el NDK se encuentra dentro del NDK, en <NDK>/build/cmake/android.toolchain.cmake.

Los parámetros de compilación, como ABI, minSdkVersion, etc., se muestran en la línea de comandos cuando se invoca a cmake. Para obtener una lista de argumentos compatibles, consulta la sección Argumentos de la cadena de herramientas.

Uso

Gradle

El uso del archivo de la cadena de herramientas de CMake es automático cuando se usa externalNativeBuild. Consulta la guía Cómo agregar código C y C++ a tu proyecto de Android Studio para obtener más información.

Línea de comandos

Cuando compiles con CMake fuera de Gradle, se deben pasar a CMake el archivo de la cadena de herramientas y sus argumentos. Por ejemplo:

$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=$ABI \
    -DANDROID_PLATFORM=android-$MINSDKVERSION \
    $OTHER_ARGS

Argumentos de la cadena de herramientas

Se pueden pasar los siguientes argumentos al archivo de la cadena de herramientas de CMake. Si compilas con Gradle, agrega argumentos a android.defaultConfig.externalNativeBuild.cmake.arguments como se describe en los documentos de ExternalNativeBuild. Si compilas desde la línea de comandos, pasa argumentos a CMake con -D. Por ejemplo, para hacer que armeabi-v7a no compile con compatibilidad con Neon, pasa -DANDROID_ARM_NEON=FALSE.

ANDROID_ABI

La ABI de destino. Para obtener información sobre las ABI compatibles, consulta ABI de Android.

Gradle

Gradle proporciona automáticamente este argumento. No lo establezcas manera explícita en tu archivo build.gradle. Para controlar las ABI de destino de Gradle, usa abiFilters como se describe en ABI de Android.

Línea de comandos

CMake compila para un solo destino por compilación. Para establecer más de una ABI de Android como destino, debes compilar una vez por cada ABI. Se recomienda usar diferentes directorios de compilación para cada ABI a fin de evitar colisiones entre compilaciones.

Valor Notas
armeabi-v7a
armeabi-v7a with NEON Es igual que armeabi-v7a.
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

Especifica si se deben generar instrucciones arm o thumb para armeabi-v7a. No tiene ningún efecto en otras ABI. Para obtener más información, consulta la documentación de ABI de Android.

Valor Notas
arm
thumb Es el comportamiento predeterminado.

ANDROID_ARM_NEON

Habilita o inhabilita NEON para armeabi-v7a. No tiene ningún efecto en otras ABI. El valor predeterminado es verdadero para el nivel de API (minSdkVersion o ANDROID_PLATFORM) 23 o versiones posteriores; de lo contrario, es falso. Para obtener más información, consulta la documentación sobre la Compatibilidad con Neon.

Valor Notas
TRUE Predeterminado para API nivel 23 o superior.
FALSE Es predeterminado para el nivel de API 22 o inferiores.

ANDROID_LD

Selecciona qué vinculador se va a usar. Por el momento, lld es de uso experimental para el NDK y se puede habilitar con este argumento.

Valor Notas
lld Habilita lld.
default Usa el vinculador predeterminado para la ABI determinada.

ANDROID_NATIVE_API_LEVEL

Es el alias para ANDROID_PLATFORM.

ANDROID_PLATFORM

Especifica el nivel mínimo de API compatible con la app o biblioteca. Este valor corresponde al objeto minSdkVersion de la app.

Gradle

Cuando se usa el complemento de Gradle para Android, este valor se establece automáticamente para que coincida con el objeto minSdkVersion de la app y no se debe configurar de forma manual.

Línea de comandos

Cuando se invoca directamente a CMake, este valor se establece de forma predeterminada en el nivel de API más bajo compatible con el NDK en uso. Por ejemplo, con NDK r20, este valor se establece de forma predeterminada en la API nivel 16.

Se aceptan múltiples formatos para este parámetro:

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

El formato $API_LETTER te permite especificar android-N sin la necesidad de determinar el número asociado con esa actualización. Ten en cuenta que en algunas versiones se aumentó el nivel de API, pero no el de letra. Se pueden especificar estas API agregando el sufijo -MR1. Por ejemplo, el nivel de API 25 es android-N-MR1.

ANDROID_STL

Especifica qué STL usar para esta app. Para obtener más información, consulta Compatibilidad de la biblioteca C++. De forma predeterminada, se usará c++_static.

Valor Notas
c++_shared Es la variante de biblioteca compartida de libc++.
c++_static Es la variante de biblioteca estática de libc++.
ninguna No se admite la biblioteca C++ estándar.
sistema Es la STL del sistema.

Cómo comprender el comando de compilación CMake

Al depurar los problemas de compilación de CMake, es útil conocer los argumentos de compilación específicos que usa Gradle cuando realiza compilaciones de forma cruzada para Android.

El complemento de Gradle para Android guarda los argumentos de compilación que utiliza a fin de ejecutar una compilación de CMake para cada par de ABI y tipo de compilación en el build_command.txt. Estos archivos están en el siguiente directorio:

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

En el siguiente fragmento, se muestra un ejemplo de los argumentos de CMake para compilar una versión depurable de la muestra de hello-jni que se orienta a la arquitectura 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

Cómo usar bibliotecas compiladas previamente

Si la biblioteca compilada previamente que necesitas importar está distribuida como una AAR, sigue los documentos de dependencia de Studio para importarlos y usarlos. Si no usas AGP, puedes seguir https://google.github.io/prefab/example-workflow.html, pero seguro resulte mucho más fácil migrar a AGP.

En el caso de las bibliotecas que no se distribuyen como AAR, puedes obtener instrucciones para usar bibliotecas compiladas previamente con CMake en la documentación de add_library sobre destinos de IMPORTED en el manual de CMake.

Cómo compilar código de terceros

Hay varias maneras de compilar código de terceros como parte de tu proyecto CMake, y la opción que mejor funcione dependerá de tu situación. A menudo, la mejor opción será no hacer eso. En su lugar, debes compilar una AAR para la biblioteca y consumirlo en tu aplicación. No es necesario que publiques esa AAR. Puede ser algo interno de tu proyecto de Gradle.

Si no es posible, haz lo siguiente:

  • Utiliza (es decir, copia) la fuente de terceros en tu repositorio y usa add_subdirectory a fin de compilarla. Esto solo funciona si la otra biblioteca también se compila con CMake.
  • Define un ExternalProject.
  • Compila la biblioteca independientemente del proyecto y sigue los pasos de la sección Cómo usar bibliotecas compiladas previamente para importarlas como compilaciones previas.

Compatibilidad con YASM en CMake

Con el NDK, CMake es compatible con la compilación de código ensamblado escrito en YASM para ejecutarse en arquitecturas x86 y x86-64. YASM es un ensamblador de código abierto para las arquitecturas x86 y x86-64 que se basa en el ensamblador NASM.

Para compilar el código ensamblado con CMake, realiza los siguientes cambios en el objeto CMakeLists.txt de tu proyecto:

  1. Llama a enable_language con el valor establecido en ASM_NASM.
  2. Si estás compilando una biblioteca compartida, llama a add_library, pero si estás compilando un objeto binario ejecutable, llama a add_executable. En los argumentos, pasa una lista de archivos de origen que conste de archivos .asm para el programa de ensamblaje en YASM y archivos .c para las funciones o bibliotecas C asociadas.

En el siguiente fragmento, se muestra cómo puedes configurar el objeto CMakeLists.txt para compilar un programa YASM como una biblioteca compartida.

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 un ejemplo de cómo compilar un programa YASM como ejecutable, consulta la prueba de yasm en el repositorio git del NDK.

Cómo informar problemas

Si tienes problemas con el NDK o su archivo de cadena de herramientas de CMake, infórmalo por medio de la Herramienta de seguimiento de errores android-ndk/ndk en GitHub. Si tienes problemas con el complemento de Android para Gradle o Gradle, informa un error de Studio.