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.
La "nueva" archivo de la cadena de herramientas
Los NDK anteriores experimentaron con una nueva implementación del archivo de la cadena de herramientas que reduciría las diferencias de comportamiento entre el uso del archivo de la cadena de herramientas del NDK y gracias a la compatibilidad integrada con CMake. Esto terminó requiriendo una cantidad significativa de trabajo (que no se completó), pero que en realidad no mejoró el comportamiento, por lo que no seguiremos trabajando en esto.
La "nueva" de la cadena de herramientas tiene regresiones de comportamiento en comparación con la versión
de la cadena de herramientas. El comportamiento predeterminado es el flujo de trabajo recomendado. Si estás
usando -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF
, te recomendamos que quites esa marca
de tu compilación. El nuevo archivo de la cadena de herramientas nunca alcanzó la paridad con el heredado.
de la cadena de herramientas, por lo que
probablemente existan regresiones de comportamiento.
Si bien recomendamos no usar el nuevo archivo de la cadena de herramientas, actualmente no hay planea quitarlo del NDK. Si lo hicieras, se dañarían las compilaciones que se basan en el diferencias de comportamiento entre los archivos nuevos y heredados de la cadena de herramientas lamentablemente, cambiaremos el nombre de la opción para que quede claro en realidad recomendado también interrumpiría a los usuarios de esa opción. Si estás feliz de usar el nuevo archivo de la cadena de herramientas que no necesitas migrar, pero ten en cuenta que cualquier error que se haya informado con el nuevo comportamiento del archivo de la cadena de herramientas. En su lugar, que deberás migrar.
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 forzar armeabi-v7a a que no compile con Neon.
compatibilidad, 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_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 administrar marcas del compilador
Si necesitas pasar indicadores específicos al compilador o vinculador para tu compilación, consulta la documentación de CMake para set_target_compile_options y la de la familia de opciones relacionada. La palabra "ver también" al final de la página. algunas pistas útiles.
En general, la práctica recomendada es aplicar marcas de compilador como las más específicas.
el alcance disponible. Marcas que desea aplicar a todas sus orientaciones (como
-Werror
) no es conveniente repetir por módulo, pero aun así deberían ser
se aplican de forma global (CMAKE_CXX_FLAGS
), ya que pueden tener efectos no deseados en
las dependencias de terceros en tu proyecto. En estos casos, las marcas se pueden
se aplica en el alcance del directorio (add_compile_options
).
En el caso de un subconjunto reducido de marcas de compilador, también se pueden configurar en tu archivo build.gradle.
con cppFlags
o propiedades similares. No debes hacer esto. Marcas
pasados a CMake desde Gradle tendrá comportamientos de precedencia sorprendentes, en algunos
que anulan las marcas que la implementación pasó implícitamente
necesaria para compilar código de Android. Siempre priorizar el control del comportamiento de CMake
directamente en CMake. Si necesitas controlar las marcas del compilador según buildType
de AGP,
consulta Cómo trabajar con tipos de compilación de AGP en CMake.
Cómo trabajar con tipos de compilación de AGP en CMake
Si necesitas adaptar el comportamiento de CMake a un buildType
de Gradle personalizado, usa ese
de compilación para pasar una marca adicional de CMake (no una marca de compilador) que tu
Las secuencias de comandos de compilación de CMake pueden leer. Por ejemplo, si tienes "gratis" y "premium"
variantes de compilación controladas por tu build.gradle.kts y debes pasar
esos datos a CMake:
android {
buildTypes {
free {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
}
}
}
premium {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
}
}
}
}
}
Luego, en tu CMakeLists.txt:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
El nombre de la variable depende de ti, pero asegúrate de evitar cualquier cosa que tenga una
los prefijos ANDROID_
, APP_
o CMAKE_
para evitar colisión o confusión
marcas existentes.
Consulta el ejemplo del NDK de Sanitizers para ver un ejemplo.
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:
- Llama a
enable_language
con el valor establecido enASM_NASM
. - Si estás compilando una biblioteca compartida, llama a
add_library
, pero si estás compilando un objeto binario ejecutable, llama aadd_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.