sábado, mayo 21, 2011

Compilar programas y librerías para distintas arquitecturas en MacOS X

Suelo compilar bastantes programas opensource en MacOS X, normalmente, sin demasiados problemas. En las versiones de XCode 3.x que se hizo público con la versión del sistema Snow Leopard, genera por defecto binarios de 64 bits. Casi nunca he tenido la necesidad de tener que compilar algún binario para 32 bits, hasta que tropecé con wine. Al menos en la versión 1.3.19 que es la que he probado, no puede compilarse en 64 bits, con lo cual tenía que tirar de las librerías en 32 bits ... cosa que no tenía preparadas. Teniendo en cuenta que no quería perder las de 64 bits que ya tenía, empecé a buscar información de como generar binarios universales.

Una particularidad que tiene MacOS X es que tanto sus ficheros ejecutables como sus ficheros de objeto, permiten tener código para varias arquitecturas. Esta particularidad, los binarios Universales, fue una de las ventajas que aprovechó Apple para hacer la transición de la arquitectura PowerPC a la arquitectura Intel x86 de manera poco traumática. Pero a su vez, nos permite generar código en 32 bits o 64 bits según queramos de manera bastante elegante. Se puede encontrar bastante información sobre este formato en Mach-O Runtime.

Lo primero que hice fue buscar por la web de desarrollo de Apple si había alguna información sencilla para generar los binarios. Y encontré la nota técnica Building Universal Binaries from "configure"-based Open Source Projects, que es la manera más sencilla de empezar a hacer este tipo de cosas, puesto que hay gran cantidad de software opensource que utiliza las autotools para configurarse. Si este es el caso, se puede empezar probando con la siguiente secuencia de órdenes (en este caso estoy compilando las swftools)

env CFLAGS="-arch i386 -arch x86_64" LDFLAGS="-arch i386 -arch x86_64" ./configure --prefix=/usr/local
make

Aquí es clave entender los valores que les estamos pasando en las variables CFLAGS y LDFLAGS. Si se ha generado todo el sistema de compilación de las autotools:

  • El contenido de la variable de entorno CFLAGS se añadirá a las opciones que se le pasará al compilador. En el caso de estar compilando código C++ esta variable de entorno será CXXFLAGS.
  • El contenido de la variable de entorno LDFLAGS será las opciones que se le añadirán al linker.
En este caso le estamos pasando una opción que es específica de Apple que es -arch que le indica al compilador y al linker que genere código para todas las arquitecturas que se le pasen. En este caso i386 y x86_64. Es más, podría generar código para PowerPC de 32 bits (-arch ppc) y para PowerPC de 64 bits (-arch ppc64).

Sin embargo las cosas no son tan fáciles. Puede ocurrir, por ejemplo, que haya opciones incompatibles con -arch, cosa que ocurre al compilar la librería libjpeg: Se usan algunas opciones en el gcc que no son compatibles con usar las variables de entorno CFLAGS y LDFLAGS con los valores que nos pueden interesar. En este caso hay que jugar un poco, compilar para las dos arquitecturas y posteriormente usar lipo que nos va a permitir combinar dos ficheros binarios, dos librerías binarias o dos ficheros de archivo, cada uno de diferentes arquitecturas en un binario universal que las arquitecturas de procesador que queramos:

lipo -create -output file -arch i386 file.i386 -arch x86_65 file.x86_64

En este caso vamos a crear un fichero universal file a partir de los otros dos de cada una de las arquitecturas. Sin embargo hay que ser bastante cuidadoso en estos casos. Tenemos que asegurarnos que no dejamos ningún fichero binario por detrás. Desde mi punto de vista, lo más sencillo es instalar en dos directorios distintos y luego con diff ver realmente que ficheros difieren, explorar que son ejecutables o librerías con la utilidad file y posteriormente, generar los ficheros universales que se necesiten.


Technorati Tags:

No hay comentarios: