Conjuntos de herramientas independientes

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 cruzado para agregarle compatibilidad con Android.

Selección de tu conjunto de herramientas

Ante todo, debes decidir la arquitectura de procesamiento a la cual apuntará tu cadena de herramientas independiente. Esto puede hacerse con el indicador --arch.

Selección de tu Sysroot

Lo siguiente que debes hacer es definir tu sysroot. Un sysroot es un directorio que contiene 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 cual deseas apuntar para obtener compatibilidad nativa; las API nativas disponibles varían según el nivel de API de Android.

Las bibliotecas para las API nativas de los correspondientes niveles de API de Android se encuentran en $NDK/platforms/; cada directorio de nivel de API contiene 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 respectivas API nativas que admiten, consulta API nativas.

Cómo crear la cadena de herramientas

El NDK proporciona la secuencia de comandos make_standalone_toolchain.py para permitirte realizar la instalación de una cadena de herramientas personalizada desde la línea de comandos.

Esta es una nueva herramienta que reemplaza a la anterior, make-standalone-toolchain.sh. Se implementó en Python, de modo que los usuarios de Windows no necesitarán instalar Cygwin ni MSYS para ejecutarla.

La secuencia de comandos se ubica en el directorio $NDK/build/tools/, donde $NDK es la raíz de instalación para el 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
    

Este comando crea un directorio llamado /tmp/my-android-toolchain/, que contiene una copia 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 lugar e incluso moverlos si fuera necesario.

El argumento --arch es obligatorio, pero la API se establece de manera predeterminada en el nivel de compatibilidad mínimo para la arquitectura dada (actualmente el nivel es 16 para arquitecturas de 32 bits o 21 para arquitecturas de 64 bits).

A partir de r18, todos los conjuntos 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. Para forzar el uso de la biblioteca estática, pasa -static-libstdc++ durante la vinculación. Este 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. Este paquete tarball puede ubicarse en otro directorio mediante --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 los conjuntos de herramientas independientes.

También hay dos secuencias de comandos de wrappers, denominadas clang y clang++, en <install-dir>/bin. Estas secuencias de comandos invocan el objeto binario clang con los indicadores de arquitectura de destino correctos. En otras palabras, deben funcionar sin ninguna modificación, y debes poder usarlas en tus propias compilaciones simplemente configurando las variables de entorno CC y CXX para que apunten a ellas.

También existen las secuencias de comandos de wrappers denominadas gcc y g++, que también llaman a Clang. Esto permite proporcionar cierto nivel de compatibilidad para los archivos de compilación que hacen referencia explícitamente 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

Al realizar compilaciones para ARM, Clang cambia el destino en función de la presencia de los indicadores del compilador -march=armv7-a o -mthumb:

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 sencillos y directos para gcc y g++ en un archivo make. Si tienes dudas, usa las siguientes opciones al invocar 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 preprocesadores predefinidos
  • -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 armeabi-v7a ABI. Se puede anular si pasas la opción correspondiente, -march o -target.

Te recomendamos usar el indicador 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 instrucciones NEON, debes cambiar el indicador 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.

Asegúrate también de proporcionar los siguientes dos indicadores al vinculador: -march=armv7-a -Wl,--fix-cortex-a8.

El primero instruye al vinculador para que seleccione bibliotecas de cadenas de herramientas que se adaptan a armv7-a. El segundo se requiere como solución a un error de la CPU en algunas implementaciones de Cortex-A8.

No es necesario que uses indicadores de compiladores específicos al apuntar a las otras ABI.

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

Advertencias y limitaciones

Compatibilidad con Windows

Los binarios de Windows no dependen de Cygwin. Esta falta 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, en contraposición a C:/foo/bar.

Excepciones, RTTI y STL

Los binarios del conjunto de herramientas admiten excepciones C++ y RTTI de forma predeterminada. Si deseas inhabilitar las excepciones C++ y RTTI al compilar fuentes (para generar código máquina más liviano, por ejemplo), usa -fno-exceptions y -fno-rtti.

Compatibilidad de la STL C++

La cadena de herramientas independiente incluye una implementación de la biblioteca de plantillas estándar C++ (STL).

  • Usa -static-libstdc++ para obtener la versión de biblioteca estática de libc++. Esto garantiza que se incluya todo el código de la STL C++ requerido en tu objeto binario final. Este método es ideal si solo generas un ejecutable o una biblioteca compartida, que es lo que recomendamos.

  • De forma predeterminada, se usará la versión de biblioteca compartida de libc++. No se necesitan indicadores adicionales para vincular con la biblioteca compartida. Debes empaquetar libc++_shared.so en tu aplicación; 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

    Conjunto 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 este conjunto 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 que te permita 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 para compilación cruzada. También ten presente 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, tendrás que seguir la documentación de ese proyecto para realizar un arranque.