CMake

A partir de Android Studio 2.2 y versiones posteriores, puedes usar el NDK y CMake para compilar código C y C++ en una biblioteca nativa. Android Studio empaqueta la biblioteca en el APK mediante Gradle, el sistema de compilación integrado en IDE.

Si es la primera vez que usas CMake con Android Studio, debes ir a Cómo agregar código C y C++ a tu proyecto para aprender los fundamentos básicos sobre cómo agregar fuentes nativas a tu proyecto, cómo crear una secuencia de comandos de compilación de CMake y cómo agregar el proyecto de CMake en forma de dependencia de Gradle. En esta página, se proporciona información adicional que puedes usar para personalizar la compilación de CMake.

Cómo usar variables de CMake en Gradle

Una vez que vinculas Gradle a tu proyecto de CMake, puedes configurar ciertas variables específicas del NDK que modifican la forma en que CMake compila las bibliotecas nativas. Para pasar un argumento a CMake desde el archivo build.gradle en el nivel del módulo, use el siguiente DSL:

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 {...}
  }
}

En la siguiente tabla, se describen algunas de las variables que puedes configurar al usar CMake con el NDK.

Nombre de la variable Argumentos Descripción
ANDROID_PLATFORM

Para obtener una lista completa de los nombres de las plataformas y las imágenes del sistema Android correspondientes, consulta Android NDK Native API.

Especifica el nombre de la plataforma de Android de destino. Por ejemplo, android-18 especifica Android 4.3 (nivel de API 18).

En lugar de cambiar este indicador directamente, debes establecer la propiedad minSdkVersion en los bloques defaultConfig o productFlavors del archivo build.gradle en el nivel del módulo . De este modo, garantizas que la biblioteca solo se utilice en las aplicaciones instaladas en los dispositivos que ejecutan una versión adecuada de Android. El conjunto de herramientas de CMake elige la versión de plataforma más adecuada para la ABI que estás compilando mediante la siguiente lógica:

  1. Si existe una versión de la plataforma para la ABI que es igual a minSdkVersion, CMake usa esta versión.
  2. De lo contrario, si existen versiones de plataforma inferiores a minSdkVersion para la ABI, CMake utiliza la más alta de dichas versiones. Esta elección es lógica, ya que si falta una versión de plataforma, en general es porque no se hicieron cambios en las API de plataforma nativa desde la versión anterior disponible.
  3. De lo contrario, CMake utiliza la siguiente versión de plataforma disponible más alta que minSdkVersion.
ANDROID_STL

Para obtener una lista completa de opciones, consulta Tiempos de ejecución auxiliares

De forma predeterminada, CMake utiliza c++_static.

Especifica el STL que CMake debe usar.

ANDROID_PIE
  • ON (valor predeterminado cuando ANDROID_PLATFORM = android-16 y versiones posteriores)
  • OFF (valor predeterminado cuando ANDROID_PLATFORM = android-15 y versiones anteriores)

Especifica si se usarán ejecutables de posición independiente (PIE). El vinculador dinámico de Android admite PIE en Android 4.1 (nivel de API 16) y versiones posteriores.

ANDROID_CPP_FEATURES

Esta variable está vacía de forma predeterminada. Sin embargo, los siguientes son algunos ejemplos de argumentos que puedes pasar:

  • rtti (indica que el código usa RTTI)
  • exceptions (indica que el código usa excepciones de C++)

Especifica ciertas características de C++ que CMake necesita usar durante la compilación de la biblioteca nativa, como RTTI (información de tipo de tiempo de ejecución) y excepciones de C++.

ANDROID_ALLOW_UNDEFINED_SYMBOLS
  • TRUE
  • FALSE (predeterminado)

Especifica si aparece un error de símbolo indefinido si CMake encuentra una referencia indefinida durante la compilación de la biblioteca nativa. Para deshabilitar este tipo de errores, establezca la variable en TRUE.

ANDROID_ARM_MODE
  • arm
  • thumb (predeterminado)

Especifica si se van a generar ejecutables de destino ARM en el modo arm o thumb. En el modo thumb, cada instrucción tiene 16 bits de ancho y está vinculada con las bibliotecas de STL en el directorio thumb/. Al pasar arm, se le indica a CMake que genere los archivos de objeto de la biblioteca en modo arm de 32 bits.

ANDROID_ARM_NEON
  • TRUE
  • FALSE

Especifica si CMake debe compilar la biblioteca nativa de modo que admita NEON. El valor predeterminado es true para el nivel de API 23 o más recientes; de lo contrario, es false.

ANDROID_DISABLE_FORMAT_STRING_CHECKS
  • TRUE
  • FALSE (predeterminado)

Especifica si se va a compilar el código fuente con protección de la cadena de formato. Cuando se habilita, el compilador arroja un error si se usa una cadena de formato no constante en una función de estilo printf.

Explicación del comando de compilación de CMake

Al depurar los problemas de compilación de CMake, resulta útil conocer los argumentos específicos de la compilación que Android Studio utiliza en la compilación cruzada para Android.

Android Studio guarda los argumentos de compilación que usa para ejecutar una compilación de CMake en un archivo cmake_build_command.txt. Para cada interfaz binaria de aplicación (ABI) que perfila la aplicación, y cada tipo de compilación para dichas ABI (a saber, de lanzamiento o depuración), Android Studio genera una copia del archivo cmake_build_command.txt para esa configuración específica. A continuación, Android Studio coloca el archivo que genera en los siguientes directorios:

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

Sugerencia: En Android Studio, puedes ver los archivos rápidamente si usas las combinaciones de teclas de búsqueda (shift+shift) e introduces cmake_build_command.txt en el campo de entrada.

El siguiente snippet muestra un ejemplo de los argumentos de CMake para compilar una versión depurable de la muestra hello-jni que perfila la arquitectura de 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 compilación

En la siguiente tabla, se destacan los argumentos de compilación de CMake clave para Android. Estos argumentos de compilación no están pensados para que los establezcan los desarrolladores. Esto está a cargo del complemento de Android para Gradle, que establece los argumentos a partir de la configuración de build.gradle del proyecto.

Argumentos de compilación Descripción
-G <build-system>

Tipo de archivos de compilación que genera CMake.

Para los proyectos de Android Studio con código nativo, <build-system> se configura en Android Gradle - Ninja. Esta configuración indica que CMake usa el sistema de compilación Ninja a fin de compilar y vincular las fuentes C/C++ para la aplicación. CMake también genera un archivo android_gradle_build.json que contiene metadatos para el complemento Gradle sobre la compilación de CMake, como los indicadores del compilador y los nombres de los objetos de destino.

Esta configuración indica que CMake usa Gradle junto con el sistema de compilación Ninja a fin de compilar y vincular fuentes C/C++ para la aplicación. El sistema de compilación Ninja es el único generador que admite Studio.

-DANDROID_ABI <abi>

La ABI de destino.

El NDK admite un conjunto de ABI, como se describe en Administración de ABI. Esta opción es similar a la variable APP_ABI que usa la herramienta ndk-build.

De forma predeterminada, Gradle compila la biblioteca nativa en archivos .so separados para las ABI que admite NDK y, luego, los empaqueta en el APK. Si quieres que Gradle compile solo determinadas configuraciones de ABI, sigue las instrucciones en Cómo agregar código C y C++ a tu proyecto.

Si la ABI de destino no se especifica, CMake usa armeabi-v7a de forma predeterminada.

Los nombres de destino válidos son:

  • armeabi: CPU basada en ARMv5TE con operaciones de punto flotante por software.
  • armeabi-v7a: Dispositivos basados en ARMv7 con instrucciones para unidades de punto flotante asistidas por hardware (VFPv3_D16).
  • armeabi-v7a with NEON: Igual a armeabi-v7a, pero se habilitan las instrucciones de punto flotante de NEON. Equivale a las configuraciones -DANDROID_ABI=armeabi-v7a y -DANDROID_ARM_NEON=ON.
  • arm64-v8a: Conjunto de instrucciones ARMv8 AArch64.
  • x86: Conjunto de instrucciones IA-32.
  • x86_64: conjunto de instrucciones para la arquitectura x86-64.
-DANDROID_NDK <path> Ruta de acceso absoluta al directorio root de la instalación del NDK en el host.
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY <path> Ubicación en el host donde CMake coloca los archivos de destino LIBRARY durante la compilación.
-DCMAKE_BUILD_TYPE <type> Es similar a los tipos de compilación de la herramienta ndk-build. Los valores válidos son Release y Debug. Para simplificar la depuración, CMake no elimina la versión de lanzamiento o depuración como parte de la compilación. Sin embargo, Gradle elimina los ejecutables cuando los empaqueta en el APK.
-DCMAKE_MAKE_PROGRAM <program-name> Herramienta para lanzar el sistema de compilación nativo. El complemento Gradle configura este valor en el compilador ninja de CMake que viene contenido en el SDK de Android.
-DCMAKE_TOOLCHAIN_FILE <path> Ruta de acceso al archivo android.toolchain.cmake que CMake usa en la compilación cruzada para Android. En general, este campo se ubica en el directorio $NDK/build/cmake/, donde $NDK es el directorio de instalación del NDK del host. Para obtener más información sobre el archivo del conjunto de herramientas, consulta Cross Compiling for Android.
-DANDROID_NATIVE_API_LEVEL <level> Nivel de API de Android para el cual compila CMake.
-DANDROID_TOOLCHAIN <type> El conjunto de herramientas del compilador que usa CMake. El valor predeterminado es clang.

Compatibilidad de YASM en CMake

El NDK proporciona compatibilidad con CMake para compilar el código de ensamblado escrito en YASM de modo que se ejecute en arquitecturas x86 y x86-64. YASM es un ensamblador de código abierto para arquitecturas x86 y x86-64, que se basa en el ensamblador NASM.

Puede resultarte útil vincular rutinas o programas de lenguaje de ensamblado con código C para acceder a las funciones o bibliotecas C desde el código de ensamblado. También puedes incluir rutinas de ensamblado breves en el código C compilado para aprovechar el rendimiento mejorado de máquina que proporciona el código de ensamblado.

Para compilar el código de ensamblado con CMake, debes hacer los siguientes cambios en el archivo CMakeLists.txt del proyecto:

  1. Llama a enable_language con el valor configurado en ASM_NASM.
  2. Según estés compilando una biblioteca compartida o un binario ejecutable, llama a add_library o a add_executable. En los argumentos, pasa una lista de archivos de origen conformada por los archivos .asm para el programa de ensamblado en YASM y los archivos .c para las funciones o bibliotecas C asociadas.

El siguiente snippet muestra cómo puedes configurar 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 sobre cómo compilar un programa YASM como un ejecutable, consulta el código yasm en el repositorio Git del NDK.

Informe de problemas

Si ocurren problemas que no se deben a la versión de código abierto de CMake, deberás informarlos por medio del android-ndk/ndk seguimiento de errores de GitHub.