Cadenas de herramientas independientes (obsoletas)

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.

Selecciona 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.

Selecciona 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.

Crea 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.

Trabaja 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 cuando se apunta a las otras ABI.

Para obtener más información sobre la compatibilidad con ABI, consulta ABI.

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.