lunes, 4 de noviembre de 2019

Los desafíos de portar PSeInt a macOS Catalina (1/3): El Toolchain

Finalmente PSeInt anda (eso parece) en macOS de 64bits! Las versiones anteriores eran de 32bits, y la novedad en macOS Catalina es que ya no acepta aplicaciones de 32bits. Las versiones previas de macOS habían empezada a avisar que en cualquier momento esto iba a suceder. Pensé que esperarían más tiempo, pero no, pasó, y PSeInt no estaba listo.

¿Por qué? ¿No es simplemente recompilar todo con otras banderas? Resulta que no, que tenía vaaarios problemas. El primero de ellos era obtener acceso fácil (automatizable con scripts) y permanente (no una mac prestada, algo que tenga siempre en mi propio sistema) a un toolchain adecuado.

Los comienzos: IMCROSS

Tengo scripts que automatizan las compilaciones de todas las versiones de ZinjaI y PSeint desde mi notebook, para generar todo lo que hay que subir en cada release con unos pocos comandos. Para eso me valgo de: [a] dos máquinas virtuales con Ubuntus (muy viejos, para garantizar una compatibilidad básica con muchos GNU/Linux); [b] un emulador de Windows (wine) con mingw32 instalado; y [c] un compilador "cruzado" (cross-compiler, que se ejecuta en una arquitectura, pero genera binarios para otra) para macOS.

Es fácil instalar un compilador "derecho" en GNU/Linux. Pero lo de "cruzado" no es tan simple. No se trata solo de que el compilador genere código binario diferente; sino también de que utilice un conjunto totalmente diferente de bibliotecas, del lenguaje y del sistema.

Hace años, cuando empecé con esto, había encontrado IMCROSS. Básicamente, un conjunto de scripts para descargar gcc y un montón de otros paquetes desde sus fuentes,  y compilarlos con las banderas adecuadas para que le apunte a macOS; y además, para tomar una imagen de XCode y extraer de allí las bibliotecas y demás archivos adicionales necesarios. Son muchos los factores involucradas, y no es nada fácil conseguir la combinación de herramientas, versiones y banderas que genere un resultado exitoso. Eso hacía IMCROSS.

En aquel momento funcionó y obtuve unos binarios de gcc para GNU/Linux de 32bits que compilaban apuntando a darwin8 (el núcleo de Mac OS X Tiger, la versión 10.4) para dos tipos de hardware: i686 y PowerPC (ambos 32bits). Pero esos scripts quedaron desactualizados y me fue imposible volver a reproducir el resultado con nuevas versiones de todo. Así que cuidadosamente fui migrando los binarios de ese compilador de máquina en máquina, aunque se imaginarán que no es bueno no poder reproducir la construcción de la herramienta.


La nueva solución: osxcross

Hacía un tiempo, cuando empezaron los avisos apocalípticos sobre desterrar para siempre a las aplicaciones de 32bits, me puse a buscar soluciones. IMCROSS parece no estar siendo mantenido, las instrucciones todavía hablan de darwin8, cuando ya andamos por darwin19. Pero googleando encontré algo muy parecido: osxcross.

El problema fue hacerlo andar. Cuando hace tiempo probé esos scripts obtuve montones de errores diferentes, así que lo dejé. Ahora, con el problema en estado ineludible, lo volví a intentar, y luego de salvar varias dificultades, funcionó.


Estos scripts necesitan archivos de las bibliotecas básicas del sistema, así que piden descargar XCode para extraerlos de allí, y empaquetarlos de forma más conveniente. Las imágenes de XCode pesan entre 5GB y 9GB, y lo que se descarga en realidad es una imagen comprimida. O sea que el script lo descomprime, y obtiene un nuevo archivo único un poquito más grande. De allí debe desempaquetar todo, y de ese todo separar lo que necesita. Con tantas veces lo mismo, no es mentira cuando la documentación dice que el proceso puede necesitar como 25GB libres en el disco.

La documentación actual dice que está probado con versiones de XCode hasta la 11. Con la 11 no me funcionó (aunque el problema podría haberestado en la descarga, el script se quejaba de hashes). Reintenté con la 10.3 y ahí sí pude extraer esos archivos. Los 25GB fueron necesarios (tuve que borrar unas cuantas cosas), aunque el tgz final con los archivos extraídos es de tan solo 30MB.


Luego hay que armar el compilador cruzado. Se supone que llvm es cruzado por naturaleza. Es decir, no hay que compilar todo un llvm nuevo, sino que si ya tenemos uno es solo agregarle la salida para macOS. Esto no funcionó con el par llvm-clang de mi Slackware-current, pudo compilar la parte nueva, pero luego al querer usarlo tiraba muchos errores relacionados a las bibliotecas del sistema. Los scripts tenían la opción de usar gcc. Gcc funcionó, pero solo para lo básico. Pude compilar mi primer "hola mundo" para macOS; pero no pude con wxWidgets. En algún punto empezó a tirar errores, que aparentemente tienen que ver con partes de las bibliotecas del sistema que tienen rastros de objetive-C. Así que fui por la tercera opción, que es compilar un par llmv+clang nuevo, de cero, y no usar el del sistema. Esto dio resultado, ahora clang funcionaba.

Solo quedaba compilar wxWidgets 3.1.2. Lo intenté a través de cmake con un archivo de toolchain que osxcross genera y no pude, más errores. Volví al clásico configure de las autotools (wx ofrece ambos mecanismos), y ahora sí, luego de probar distintos sets de banderas y variables de entorno dí con una combinación ganadora.



Como resultado, ahora tengo un toolchain en mi GNU/Linux con el cual compilar binarios para macOS de 64bits, apuntando a darwin18. Supongo que por un tiempo seguiré usando los 2 y subiendo al sitio ambas (3!) versiones hasta que todos los usuarios estén actualizados. Sin embargo esta historia recién empieza. Hablamos solamente del compilador, ¿pero qué hay del código de PSeInt? ¿compiló derecho y sin cambios? ¡Claro que no! Pero dejo eso para otro post.

1 comentario:

  1. Hola! quería saber si iban a actualizar ZinjaI para poder correrlo en macOS pero en 64bits.

    ResponderEliminar