Como actualmente no hay dispositivos para ingeniería públicos de Intel, trabajaremos con dos imágenes para destinos emulados (para conocer los detalles, consulte las páginas http://source.android.com/source/initializing.html y http://source.android.com/source/downloading.html, en las cuales se ayuda a resolver problemas comunes con las herramientas y el entorno; es posible que necesite una cuenta de Google): el emulador de destino basado en QEMU y el destino para VirtualBox.
1 Creación de imágenes de Android* con el compilador GNU* para Android
Las instrucciones para compilar una imagen de emulador para ARM* se pueden aplicar directamente al destino Intel x86. Aquí incluimos una breve descripción de los pasos a seguir.
1.1 Preparación del espacio de trabajo
mkdir <WORKSPACE_DIRECTORY>
cd <WORKSPACE_DIRECTORY>
repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r3
Nota: se puede probar con otra rama, por ejemplo, master.
repo sync
1.2 Configuración del entorno de compilación
La página de Google no menciona de forma explícita el destino full_x86 (consulte http://source.android.com/source/building.html para conocer los detalles), pero este se muestra si ejecuta el comando lunch sin argumentos:
source build/envsetup.sh
lunch full_x86-eng
1.3 Compilación de la imagen
Por último, elija la cantidad de trabajos en paralelo y ejecute make:
make –j
Para comprobar que la compilación haya sido exitosa, inicie el comando emulator. Localice la información del sistema: Applications->Settings->About Phone, como se muestra en la Figura 8.1.
Figura 8.1 Localización de la información del sistema con About Phone
A fin de compilar la imagen para Virtual Box, especifique el parámetro vbox_x86-eng para el comando lunch y
source build/envsetup.sh
lunch vbox_x86-eng
make -j android_disk_vdi
2 Compilación de kernels
Las instrucciones para compilar el kernel para el emulador de ARM* no se aplican directamente a los destinos x86. Es necesario especificar el compilador. Se debe tener en cuenta que los compiladores predeterminados GCC versiones 4.4.3 y 4.6 con destino Android no se pueden usar tal como vienen para compilar el kernel, porque la interfaz binaria de aplicación (ABI) de Android difiere de la ABI de Linux. La diferencia clave es que, de manera predeterminada, los compiladores para sistemas Android generan código independiente de la posición. Si se compila el kernel con GNU* o el Compilador Intel® C++ para Android, es necesario agregar las opciones –mno-android o –fno-PIC durante la compilación.
Describiremos brevemente los pasos de compilación del kernel para el emulador. Suponemos que ya hemos hecho la compilación del emulador en el directorio <AOSP WORKSPACE>. Los compiladores del destino se toman del espacio de trabajo.
Preparación del espacio de trabajo:
git clone http://android.googlesource.com/kernel/goldfish.git goldfish-kernel
git branch goldfish remotes/origin/android-goldfish-2.6.29
git checkout goldfish
Configuración del kernel para el emulador:
make ARCH=x86 goldfish_defconfig
O para Virtual Box:
make ARCH=x86 vbox_defconfig.
ARCH=x86 se agregó para especificar de manera explícita que vamos a compilar un kernel para un sistema de 32 bits.
Ahora estamos en condiciones de compilar el kernel. En las secciones siguientes, compilaremos el kernel con dos compiladores.
2.1 Compilación del kernel con el compilador GNU* para Android
Para compilar el kernel, es necesario especificar la ruta del compilador y otras herramientas, el vinculador, el ensamblador, etc.
La manera más fácil de configurar todas las rutas es definir la variable de entorno CROSS_COMPILE. Como se indicó antes, también es necesario agregar la opción –mno-android para deshabilitar la generación de código independiente de la posición. Redefinimos la variable CC de modo que se use –mno-andriod cada vez que se invoque el compilador.
export CROSS_COMPILE=<AOSP WORKSPACE>/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.4.3/bin/i686-linux-android-
make ARCH=x86 CC="${CROSS_COMPILE}gcc -mno-android" bzImage
Ejecute el emulador con el nuevo kernel
emulator –kernel arch/x86/boot/bzImage
y observe que el emulador de Android ejecuta el nuevo kernel (ver Figura 8.2).
Figura 8.2 El emulador de Android ejecuta el nuevo kernel.
Si se elige trabajar con la imagen de Virtual Box, se necesita realizar una recompilación de la imagen de todo el sistema y especificar el nuevo kernel como parámetro de make:
cd <AOSP WORKSPACE >
rm out/target/product/vbox_x86/kernel
make -j <NUM_JOBS> LOCAL_KERNEL=<path to goldfish-kernel directory>/arch/x86/boot/bzImage android_disk_vdi
2.2 Compilación del kernel con el Compilador Intel® C++ para Android
Las instrucciones para el compilador de Intel son ligeramente más complicadas. Vamos a suponer que el kernel para el emulador ya está configurado.
export CROSS_COMPILE=<AOSP WORKSPACE>/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.4.3/bin/i686-linux-android-
export ANDROID_GNU_X86_TOOLCHAIN=$(dirname $CROSS_COMPILE)/../
make ARCH=x86 CC="<path to compiler>/icc -fno-PIC -falign-stack=assume-4-byte -diag-disable 47,1419,311,147,175 -mno-sse -mno-sse2" bzImage
El compilador de Intel necesita archivos de encabezado de GCC, y la variable de entorno ANDROID_GNU_X86_TOOLCHAIN especifica la instalación de GCC en root para el compilador de Intel. También agregamos varias opciones:
- -fno-PIC es necesaria para deshabilitar la generación de código independiente de la posición.
- -diag-disable 47,1419,311,147,175 es necesaria para evitar errores de compilación debidos a la opción –Werror en los archivos make del kernel; por lo general, el compilador de Intel genera más advertencias que el GCC;
- -mno-sse -mno-sse2 es necesaria porque algunos archivos de los subdirectorios arch/x86/boot se compilan sin estos indicadores. Estas opciones se especifican para la mayoría de los archivos fuente; informamos esto a los desarrolladores de kernel.
Si se ejecuta el emulador con el nuevo kernel en modo detallado
emulator –kernel arch/x86/boot/bzImage –show-kernel
la salida permitirá ver que el kernel se compiló con icc en modo de compatibilidad con gcc 4.4.3.
Para habilitar las optimizaciones específicas de Intel para Intel® Atom™, se debe modificar el archivo arch/x86/Makefile_32.cpu:
- eliminar la línea que configura cflags-$(CONFIG_X86_GENERIC);
- agregar la línea cflags-$(CONFIG_MCORE2) += $(call cc-option,-xSSSE3_ATOM).
No podemos agregar simplemente -xSSSE3_ATOM a las opciones restantes, porque las invalidarían opciones del compilador GNU* tales como –march=i686.
3 Compilación de imágenes con el Compilador Intel® C++ para Android
El sistema de compilación de Android está diseñado de manera que permita la configuración fina de componentes. Por ejemplo, es posible especificar opciones de compilador especiales para mejorar el rendimiento componente por componente. Se puede definir una variable LOCAL_CC para invalidar el compilador C en el archivo Android.mk del componente. La única restricción es que el compilador debe admitir opciones de compilador globales. Si no admite todas las opciones, entonces se deben hacer retoques especiales en el sistema de compilación de Android.
Este sistema de compilación también deja la posibilidad de conmutar compiladores globalmente; el principal lugar para hacer ajustes dirigidos a un compilador específico es el archivo build/core/combo/TARGET_linux-x86.mk. Aquí se ubican las rutas al compilador, el ensamblador, otras herramientas y las herramientas binarias. En este archivo también se especifican las opciones predeterminadas del compilador.
3.1 Integración del compilador de Intel
Haremos que el compilador de Intel sea el predeterminado. Comenzamos con modificaciones en el archivo build/core/combo/TARGET_linux-x86.mk.
Primero especificamos el root da la instalación del compilador en la variable TARGET_TOOLCHAIN_ROOT.
TARGET_TOOLCHAIN_ROOT := prebuilts/icc
Vamos a suponer que el compilador de Intel se copió al directorio prebuilts/icc. Luego inicializamos la variable de entorno ANDROID_GNU_X86_TOOLCHAIN con el directorio de nivel superior de las herramientas de GNU:
export ANDROID_GNU_X86_TOOLCHAIN:=$(abspath prebuilts/gcc/$(HOST_PREBUILT_TAG)/x86/i686-android-linux-4.4.3)
También hay que cambiar las rutas de las herramientas y los compiladores:
TARGET_TOOLS_PREFIX :=$(TARGET_TOOLCHAIN_ROOT)/bin/
TARGET_CC:=$(TARGET_TOOLS_PREFIX)icc$(HOST_EXECUTABLE_SUFFIX)
TARGET_CXX:=$(TARGET_TOOLS_PREFIX)icpc$(HOST_EXECUTABLE_SUFFIX)
TARGET_AR:=$(TARGET_TOOLS_PREFIX)xiar$(HOST_EXECUTABLE_SUFFIX)
TARGET_LD:=$(TARGET_TOOLS_PREFIX)xild$(HOST_EXECUTABLE_SUFFIX)
TARGET_OBJCOPY:=$(ANDROID_GNU_X86_TOOLCHAIN)/bin/i686-android-linux-objcopy$(HOST_EXECUTABLE_SUFFIX)
TARGET_STRIP:=$(ANDROID_GNU_X86_TOOLCHAIN)/bin/i686-android-linux-strip$(HOST_EXECUTABLE_SUFFIX)
Revisamos la variable TARGET_GLOBAL_CFLAGS, reemplazamos las opciones del compilador GNU con las correspondientes del compilador de Intel y agregamos opciones específicas de la arquitectura.
Al modificar la variable TARGET_GLOBAL_CFLAGS, no hay que olvidarse de corregir las opciones para el compilador clang. Con el fin de eliminar la opción específica de Intel –xSSSE3_ATOM para clang, se inserta la línea $(call clang-flags-subst,-xSSSE3_ATOM) en el archivo build/core/llvm_config.mk.
El siguiente paso es configurar componentes precompilados correspondientes a las bibliotecas del compilador de Intel. Para abreviar, consideraremos sólo la versión compartida de la biblioteca libimf. Creamos el archivo prebuilts/icc/Android.mk y le agregamos las siguientes líneas:
LOCAL_PATH := /
include $(CLEAR_VARS)
LOCAL_SYSTEM_SHARED_LIBRARIES:=
LOCAL_MODULE := libimf
LOCAL_MODULE_SUFFIX:=.so
LOCAL_STRIP_MODULE:=true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SRC_FILES := $(shell env ANDROID_GNU_X86_TOOLCHAIN=$(ANDROID_GNU_X86_TOOLCHAIN) $(TARGET_CC) -print-file-name=libimf.so)
include $(BUILD_PREBUILT)
En el caso de las bibliotecas estáticas, la actualización de LOCAL_MODULE_SUFFIX, LOCAL_SRC_FILES y LOCAL_MODULE_CLASS debería ser trivial. Sugerimos tener diferentes valores de LOCAL_MODULE para las versiones compartidas y estáticas de la misma biblioteca, porque los nombres repetidos de los módulos precompilados confundían a los sistemas de compilación de Android anteriores.
El valor de LOCAL_PATH parece inusual. En la mayoría de los archivos Android.mk, LOCAL_PATH se inicializa al valor de la llamada a la macro my-dir: $(call my-dir). LOCAL_PATH se inicializa a ‘/’ porque las rutas de las bibliotecas, especificadas en la variable LOCAL_SRC_FILES, son absolutas. Con el fin de evitar el copiado manual de las bibliotecas, invocamos el compilador con la opción especial -print-file-name para consultar la ubicación de las bibliotecas.
Como muchas bibliotecas compartidas y muchos archivos ejecutables vinculados de manera no estática dependerán de bibliotecas compartidas de Intel, las bibliotecas dependientes deberían encontrarlos. Por ejemplo, la imagen del instalador de la imagen de Virtual Box debe contener bibliotecas de Intel y es necesario agregarlas al archivo bootable/diskinstaller/config.mk:
installer_base_files += libimf libintlc libsvml
El entorno TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES de build/core/combo/TARGET_linux-x86.mk define la lista de bibliotecas compartidas que se vinculan de manera predeterminada. Se deben agregar a esta variable todas las bibliotecas compartidas de Intel.
Antes de empezar a compilar la imagen, hay que agregar la opción –ffreestanding a la variable LOCAL_CFLAGS para todos los módulos del directorio bionic. Esta opción hace que las bibliotecas bionic y el vinculador dinámico sean independientes de las bibliotecas del compilador.
Ahora se puede comenzar a compilar la imagen:
make –j 4 SHOW_COMMANDS=1
Es probable que la compilación no sea exitosa. Y por varias razones. La primera es que algunos componentes necesitarán bibliotecas del compilador de Intel durante la vinculación. Agregar bibliotecas del compilador de Intel a TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES resuelve la mayoría de los casos, pero no todos. Se necesitará agregar bibliotecas del compilador de Intel a las variables LOCAL_SYSTEM_SHARED_LIBRARIES o LOCAL_STATIC_LIBRARIES si el componente es un archivo ejecutable vinculado estáticamente o si el componente invalida la configuración predeterminada.
La segunda razón es que el compilador de Intel genera más advertencias que el GCC y se produce un error de compilación en algún componente debido a to-Werror. Hay que deshabilitar las advertencias en la variable TARGET_GLOBAL_CFLAGS desde el archivo build/core/combo/TARGET_linux-x86.mk mediante el uso de la opción -diag-disable.
La tercera razón es la incompatibilidad de código fuente entre los compiladores GNU e Intel*. Hay algunas construcciones no estándares de código fuente de Android que el compilador de Intel rechaza. Estas se corrigieron y fueron informadas a Google. También encontramos incompatibilidad binaria entre el compilador GNU y el de Intel; se implementó una solución alternativa en el código.
3.2 Configuración flexible del sistema de compilación
La integración descrita en la sección anterior es directa y no adecuada para experimentar, porque cambiar el compilador para el componente requiere de varios pasos. Después de varios experimentos iniciales, implementamos una reconfiguración precomponente más simple del compilador.
Ahora se implementa un sistema similar en fuentes Android para el compilador clang de LLVM. Para compilar el componente con clang, sólo es necesario asignar el valor “true” a la variable LOCAL_CLANG. La habilitación de clang afecta a tres archivos del directorio build,
- core/llvm_config.mk– diversos parámetros y funciones variables para clang;
- core/clear_vars.mk– valor predeterminado de la variable LOCAL_CLANG;
- core/binary.mk– aquí se implementa toda la lógica relacionada con los indicadores del compilador, los indicadores del vinculador, etc.
Los cambios al compilador clang se explican por sí mismos. Recomendamos echarles un vistazo.