Puedes usar las cadenas de herramientas proporcionadas con el NDK de Android de forma independiente o como complementos con un IDE existente. Esta flexibilidad puede ser útil si ya tienes tu propio sistema de compilación y solo necesitas la capacidad de invocar el compilador de forma cruzada para agregarle compatibilidad con Android.
Cómo seleccionar tu cadena de herramientas
Ante todo, debes decidir la arquitectura de procesamiento a la que se orientará tu cadena de herramientas independiente. Esto se hace con la marca --arch
.
Cómo seleccionar tu sysroot
Luego, debes definir el sysroot. Un sysroot es un directorio que tiene los encabezados y las bibliotecas del sistema para el objeto de destino. Para definir el sysroot, debes conocer el nivel de API de Android al que deseas orientar la cadena de herramientas a fin de tener compatibilidad nativa; las API nativas disponibles varían según el nivel de API de Android.
Las bibliotecas para las API nativas de los niveles de API de Android correspondientes están en $NDK/platforms/
; cada directorio de nivel de API tiene subdirectorios para las diferentes CPU y arquitecturas. Los encabezados se encuentran en $NDK/sysroot
.
Para obtener más información sobre los niveles de API de Android y las API nativas respectivas que admiten, consulta API nativas.
Cómo crear la cadena de herramientas
El NDK entrega la secuencia de comandos make_standalone_toolchain.py
que permite la instalación de una cadena de herramientas personalizada desde la línea de comandos.
Es una herramienta nueva que reemplaza la anterior, make-standalone-toolchain.sh
. Se reimplementó en Python, por lo que los usuarios de Windows no necesitarán instalar Cygwin ni MSYS para ejecutarla.
La secuencia de comandos está en el directorio $NDK/build/tools/
, donde $NDK
es la raíz de instalación del NDK.
A continuación, te mostramos un ejemplo de uso de esta secuencia de comandos:
$NDK/build/tools/make_standalone_toolchain.py \
--arch arm --api 21 --install-dir /tmp/my-android-toolchain
El comando crea un directorio llamado /tmp/my-android-toolchain/
, que tiene una copia de la raíz del sysroot android-21/arch-arm
y de los objetos binarios de la cadena de herramientas para una arquitectura ARM de 32 bits.
Ten en cuenta que los objetos binarios de la cadena de herramientas no dependen de las rutas de acceso específicas del host ni las contienen. En otras palabras, puedes instalarlos en cualquier ubicación e incluso moverlos si fuera necesario.
El argumento --arch
es obligatorio, pero el nivel de API se establece de forma predeterminada en el nivel de compatibilidad mínimo para la arquitectura en cuestión (ahora, el nivel es 16 en arquitecturas de 32 bits o 21 en arquitecturas de 64 bits).
A partir de r18, todas las cadenas de herramientas independientes usan Clang y libc++. De forma predeterminada, se usa la biblioteca compartida libc++, a menos que se compile un ejecutable estático. Pasa -static-libstdc++
durante la vinculación para forzar el uso de la biblioteca estática. El comportamiento coincide con el de una cadena de herramientas de host normal.
Como se mencionó en Compatibilidad de la biblioteca C++, a menudo necesitarás pasar -latomic
durante la vinculación con libc++.
Ten en cuenta que, si omites la opción --install-dir
, la herramienta creará un paquete tarball en el directorio actual denominado $TOOLCHAIN_NAME.tar.bz2
. El paquete tarball puede ubicarse en otro directorio con --package-dir
.
Para acceder a más opciones y detalles, usa --help
.
Cómo trabajar con Clang
Los binarios de Clang se incluyen automáticamente en las cadenas de herramientas independientes.
También hay dos secuencias de comandos de wrapper en <install-dir>/bin
, denominadas clang
y clang++
. Estas secuencias de comandos invocan el objeto binario clang
con las marcas de arquitectura de destino correctas. En otras palabras, deberían funcionar en tus propias compilaciones sin ninguna modificación si configuras las variables de entorno CC
y CXX
para que se orienten a ellas.
Hay otras secuencias de comandos de wrapper denominadas gcc
y g++
, que también llaman a Clang.
Esto permite proporcionar cierto nivel de compatibilidad con los archivos de compilación que hacen referencia explícita a GCC, incluso cuando el NDK ya no contiene GCC. Por supuesto, si un archivo de compilación usa opciones de la línea de comandos que no son compatibles con Clang, deberás quitarlas o reemplazarlas.
Destinos Clang con ARM
Cuando realizas compilaciones para ARM, Clang cambia el destino en función de la presencia de las marcas -march=armv7-a
o -mthumb
del compilador:
Tabla 1. Valores -march
que pueden especificarse y sus destinos resultantes.
Valor -march |
Destino resultante |
---|---|
-march=armv7-a |
armv7-none-linux-androideabi |
-mthumb |
thumb-none-linux-androideabi |
-march=armv7-a y -mthumb |
thumbv7-none-linux-androideabi |
Si lo deseas, también puedes realizar anulaciones con tu propio -target
.
clang
y clang++
deben ser reemplazos directos de gcc
y g++
en un archivo make. Si tienes dudas, usa las siguientes opciones cuando invoques el compilador para verificar que funcionen correctamente:
-v
para descartar comandos asociados con problemas del controlador del compilador-###
para descartar opciones de líneas de comandos, incluidas las predefinidas implícitamente-x c < /dev/null -dM -E
para descartar definiciones de preprocesador predefinidas-save-temps
para comparar archivos preprocesados*.i
o*.ii
Compatibilidad con ABI
De forma predeterminada, una cadena de herramientas independientes ARM Clang apunta a la ABI armeabi-v7a.
Se puede anular si pasas la opción -march
o -target
correspondiente.
Recomendamos el uso de la marca del compilador -mthumb
para forzar la generación de instrucciones de Thumb-2 de 16 bits. De lo contrario, la cadena de herramientas emitirá instrucciones de ARM de 32 bits.
Para usar las instrucciones NEON, debes usar la marca del compilador -mfpu
: -mfpu=neon
.
Ten en cuenta que esta configuración fuerza el uso de VFPv3-D32
, según la especificación de ARM.
Además, asegúrate de proporcionar las dos marcas siguientes al vinculador: -march=armv7-a -Wl,--fix-cortex-a8
.
La primera instruye al vinculador que seleccione bibliotecas de la cadena de herramientas que se adapten a armv7-a. La segunda se requiere como solución alternativa a un error de la CPU en algunas implementaciones de Cortex-A8.
No es necesario que uses marcas de compiladores específicas al apuntar a las otras ABI.
Para obtener más información sobre la compatibilidad con ABI, consulta ABI de Android.
Advertencias y limitaciones
Compatibilidad con Windows
Los objetos binarios de Windows no dependen de Cygwin. Esta ausencia de dependencia los hace más rápidos. No obstante, el inconveniente reside en que no interpretan las especificaciones de la ruta de acceso de Cygwin como cygdrive/c/foo/bar
, a diferencia de C:/foo/bar
.
Excepciones, RTTI y STL
Los objetos binarios de la cadena de herramientas admiten excepciones C++ y RTTI de forma predeterminada. Si deseas inhabilitar las excepciones C++ y RTTI cuando compiles las fuentes (por ejemplo, para generar un código máquina más liviano), usa -fno-exceptions
y -fno-rtti
.
Compatibilidad con la STL de C++
La cadena de herramientas independiente incluye una implementación de la biblioteca de plantillas estándar (STL) de C++.
Usa
-static-libstdc++
para obtener la versión estática de la biblioteca de libc++. Esto garantiza que se incluya todo el código de la STL de C++ requerido en tu objeto binario final. Este método es ideal si solo generas un ejecutable o una biblioteca compartida, que es lo recomendado.De forma predeterminada, se usará la versión de biblioteca compartida de libc++. No se necesitan marcas adicionales para vincular con la biblioteca compartida. Debes empaquetar
libc++_shared.so
en tu app; de lo contrario, el código no se cargará.En la tabla 2, se muestra la ubicación de este archivo para cada arquitectura.
Tabla 2: Valores
-march
que pueden especificarse y sus destinos resultantes.Cadena de herramientas Ubicación arm $TOOLCHAIN/arm-linux-androideabi/lib/
arm64 $TOOLCHAIN/aarch64-linux-android/lib/
x86 $TOOLCHAIN/i686-linux-android/lib/
x86_64 $TOOLCHAIN/x86_64-linux-android/lib/
Cómo compilar proyectos de código abierto mediante cadenas de herramientas independientes
Para esta cadena de herramientas de ejemplo:
# Create an arm64 API 26 libc++ toolchain.
$NDK/build/tools/make_standalone_toolchain.py \
--arch arm64 \
--api 26 \
--install-dir=my-toolchain
Aquí te mostramos cómo configurar el entorno para compilar un proyecto de código abierto tradicional:
# Add the standalone toolchain to the search path.
export PATH=$PATH:`pwd`/my-toolchain/bin
# Tell configure what tools to use.
target_host=aarch64-linux-android
export AR=$target_host-ar
export AS=$target_host-clang
export CC=$target_host-clang
export CXX=$target_host-clang++
export LD=$target_host-ld
export STRIP=$target_host-strip
# Tell configure what flags Android requires.
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"
Proyectos con sistemas de compilación personalizados
Aquí te mostramos un ejemplo sobre cómo compilar Toybox después de realizar los pasos anteriores:
git clone https://github.com/landley/toybox.git
cd toybox
make defconfig && make
Proyectos con autoconf
Por otro lado, un proyecto basado en autoconf se verá más bien así:
tar zxvf make-4.2.tar.gz
cd make-4.2
./configure --host=$target_host && make
Ten en cuenta que los proyectos basados en autoconf varían ampliamente en la compatibilidad que ofrecen con la compilación cruzada. Ten presente también que, si usas el comando git clone
en un proyecto basado en autoconf, es improbable que haya una secuencia de comandos configure
comprobada; por lo tanto, deberás seguir la documentación de ese proyecto sobre cómo realizar un arranque.