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

Selecciona tu cadena de herramientas

Ante todo, debes decidir la arquitectura de procesamiento a la que se orientará tu cadena de herramientas independiente. Esto puede hacerse con la marca --arch.

Selecciona 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 que deseas orientar la cadena de herramientas a fin de 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.

Crea la cadena de herramientas

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

Esta es una nueva herramienta que reemplaza la anterior, make-standalone-toolchain.sh. Se volvió a implementar 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 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
    

Este comando crea un directorio llamado /tmp/my-android-toolchain/, que contiene 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 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 en cuestión (por el momento, el nivel es 16 para arquitecturas de 32 bits o 21 para 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. 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.

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 wrappers 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, 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 se orienten a ellas.

También hay 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

Cuando realizas compilaciones para ARM, Clang cambia el destino en función de la presencia de las marcas -march=armv7-a y/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 para 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 independiente ARM Clang apunta a la armeabi-v7a ABI. Se puede anular si pasas la opción correspondiente, -march o -target.

Te recomendamos usar 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 para que seleccione bibliotecas de cadenas de herramientas que se adaptan a armv7-a. La segunda se requiere como solución 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.

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 cuando compiles las fuentes (por ejemplo, para generar un código de máquina más liviano), usa -fno-exceptions y -fno-rtti.

Compatibilidad con 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 estática de la biblioteca 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 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 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. 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 para realizar un arranque.