martes, 19 de noviembre de 2019

Los desafíos de portar PSeInt a macOS Catalina (3/3): Los últimos detalles

Ya conté los problemas y soluciones para poner en marcha un toolchain para las nuevas versiones de macOS, y la migración del código de wx 2 ansi a wx 3 unicode, en GNU/Linux. Solo me faltaba combinar todo esto para ver si andaba en macOS, si tenía un paquete para publicar en el sitio. Por supuesto que no anduvo a la primera. He aquí los últimos detalles de esta historia.


El problema de los dylibs


Una vez que pude compilar tanto wx, como los distintos módulos de PSeInt con el nuevo toolchain, armé un paquete con la estructura que le gusta a macOS y me dispuse a probarlo en una máquina virtual: no podía ni iniciar PSeInt porque los binarios de PSeInt no encontraban a los binarios de wxWidgets.

Los dylibs en mac son más o menos como los dlls en Windows, o como los .so en GNU/Linux. Antes, con IMCROSS, había compilado wx para enlazado estático. Esto es, para ponerlo dentro de cada ejecutable. El problema es que PSeInt en realidad está formado por varios ejecutables, y entonces esto hace al paquete final innecesariamente grande. Lo mejor en este caso es enlazado dinámico: que estén una sola vez, fuera de los ejecutable, y que todos ellos busquen esos mismos archivos.

En el paquete para mac, hay un directorio Contents, que adentro tenía dos directorios: MacOS y Resources. En MacOS está el ejecutable principal; en Resources todo los demás: los otros ejecutables, la ayuda, los íconos, etc. Ahora hay un tercero: Frameworks, donde está wxWidgets. El problema es que luego de compilar, me quedó en el ejecutable la ruta al binario de la biblioteca tal cual se la di al compilador. Esto es, la ruta donde yo los tengo en mi GNU/Linux. En la Mac esta ruta no existe ni tiene sentido. Las rutas deberían llevar a Frameworks, y ser relativas.

Después de googlear un rato, llego a que una herramienta otool me puede mostrar las rutas que tiene adentro cada binario, y otra herramienta install_names_tool me deja modificarlas. Entonces, desde un script de bash, uso la primera para encontrar las dependencias, y la segunda para corregirlas (recursivamente, también entre los propios archivos de la biblioteca). El detalle de color fue que en uno de los binario los paths nuevos no entraban y entonces tuve que dar con un argumento de compilación más para agregar un relleno a los paths, de modo que luego install_names_tool tenga espacio de sobra.


GNU/Linux vs. macOS

Una vez compilado, empaquetado, y ejecutándose, era hora de ver si la ejecución hacía lo que debía. Se supone que el objetivo de wx es independizarnos de la plataforma, que programemos contra wx y eso se compile y comporte igual en cualquier sistema operativo. No siempre funciona así al 100%, a veces varían sutilmente cosas como el orden de los eventos que reciben los controles, o el tipo de evento que generan ciertas acciones.

Antes no podía quejarme de esas diferencias por no usar el wx que corresponde, ahora puedo! Pero igual, hasta que se resuelvan, tengo que identificarlas yo y aplicar algún parche a mi código. Me pasó algo de esto con el editor de diagramas de flujo. La forma de detectar las teclas me funcionaba perfectamente en GNU/Linux, pero no en macOS.

Los detalles de este tipo que eran funcionales fueron corregidos. Algunos llevaron mucha prueba y error. Los que son estéticos todavía no, esos tienen por el momento baja prioridad. Por ejemplo, si usan un tema de fondo oscuro en macOS, algunos elementos de la interfaz se ven "feos". Feos pero andan que es lo más importante. La estética se irá arreglando luego de a poco.


Otras, varios y miscelánea

Finalmente, ahora que PSeInt funciona con wx 3 unicode, no solo tengo una versión para los usuarios de macOS Catalina. Sino que también de a poco me voy despegando de un compilador y una biblioteca muy viejos. Un compilador más nuevo me permite migrar a C++11 o C++14 y limpiar mucho el código. Y una biblioteca más nueva agrega funcionalidad. Por ejemplo, ahora scintilla ofrece annotations, que puedo usar para mostrar los errores de una forma algo mejor que la anterior.

Comparación entre mostrar los errores como annotations (arriba, wx3), o mediante tooltips (abajo, wx2).

Por otro lado, usar otro compilador distinto al habitual y más nuevo hizo que aparecieran muchos warnings. Algunos me sirvieron para corregir pequeños errores, otros tuve que silenciarlos o dejarlos para más adelante. Pero en cualquier caso, más información de diagnóstico siempre es buena noticia.


Toda esta experiencia me servirá cuando intente migrar ZinjaI a wx3. ZinjaI puede sacarle mucho más jugo a las nuevas funcionalidades de wxWidgets, pero también es mucho más difícil de portar. Es mucho más grande, y hay mucha más interacción entre la interfaz y todo tipo de cosas ajenas (el compilador, el depurador, el parser, el diseñador, los profilers, el sistema de archivos, mis propias clase, etc). Por eso llevará mucho más tiempo, pero que PSeInt ya funcione es una muy buena señal.

7 comentarios:

  1. Hola:

    Gracias por su trabajo en PSeInt, tengo las siguientes preguntas y quiero saber si me puede ayudar con ellas:
    ¿Dónde se puede encontrar el código fuente de la versión que se compila con wxWidgets 3.0.x? así mismo, ¿Existe una guía reciente para compilar esa versión en GNU/Linux?, conozco una guía que usé para compilar PSeInt en GNU/Linux, pero utilizaba la versión 2.8.x de wxWidgets; y por último, ¿La nueva versión para wxWidgets 3.0.x, es una versión en estado, si se puede decir, alfa, beta o estable?. Gracias.

    ResponderEliminar
    Respuestas
    1. Ya están subidos los fuentes con todos estos cambios (al repo git, y a la sección descargas). Ahora no hay requisitos especiales sobre wx... instalándola desde los repos de tu distro (versión dev), después con "make linux3" (notar el 3) compilás PSeInt. Y también se eliminaron otras dependencias (como glut y glew), así que no hace falta instalar mucho más que wx y opengl básico (creo que era gl1-mesa-dev en debian o ubuntu).

      Eliminar
    2. Hola:

      Al tratar de compilar con la versión 3.0.x o la versión 3.1.x de wxWidgets se presenta el siguiente error:

      pruebas@ubuntu:~/sourcecode/pseint$ make linux3
      make -f Makefile.lnx3
      make[1]: se entra en el directorio '/home/pc/Descargas/pseint'
      make -C pseint -f Makefile.lnx
      make[2]: se entra en el directorio '/home/pc/Descargas/pseint/pseint'
      mkdir -p ../temp/int_lnx_rls
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c main.cpp -o ../temp/int_lnx_rls/main.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c intercambio.cpp -o ../temp/int_lnx_rls/intercambio.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c SynCheck.cpp -o ../temp/int_lnx_rls/SynCheck.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c Ejecutar.cpp -o ../temp/int_lnx_rls/Ejecutar.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c global.cpp -o ../temp/int_lnx_rls/global.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c zockets.cpp -o ../temp/int_lnx_rls/zockets.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c utils.cpp -o ../temp/int_lnx_rls/utils.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c zcurlib.cpp -o ../temp/int_lnx_rls/zcurlib.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c new_evaluar.cpp -o ../temp/int_lnx_rls/new_evaluar.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c new_memoria.cpp -o ../temp/int_lnx_rls/new_memoria.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c new_funciones.cpp -o ../temp/int_lnx_rls/new_funciones.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c new_programa.cpp -o ../temp/int_lnx_rls/new_programa.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c case_map.cpp -o ../temp/int_lnx_rls/case_map.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c zcurlib_lnx.cpp -o ../temp/int_lnx_rls/zcurlib_lnx.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c zcurlib_win.cpp -o ../temp/int_lnx_rls/zcurlib_win.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c LangSettings.cpp -o ../temp/int_lnx_rls/LangSettings.o
      g++ -O3 -DUSE_ZOCKETS -ffloat-store -DNDEBUG -c DataValue.cpp -o ../temp/int_lnx_rls/DataValue.o
      g++ ../temp/int_lnx_rls/main.o ../temp/int_lnx_rls/intercambio.o ../temp/int_lnx_rls/SynCheck.o ../temp/int_lnx_rls/Ejecutar.o ../temp/int_lnx_rls/global.o ../temp/int_lnx_rls/zockets.o ../temp/int_lnx_rls/utils.o ../temp/int_lnx_rls/zcurlib.o ../temp/int_lnx_rls/new_evaluar.o ../temp/int_lnx_rls/new_memoria.o ../temp/int_lnx_rls/new_funciones.o ../temp/int_lnx_rls/new_programa.o ../temp/int_lnx_rls/case_map.o ../temp/int_lnx_rls/zcurlib_lnx.o ../temp/int_lnx_rls/zcurlib_win.o ../temp/int_lnx_rls/LangSettings.o ../temp/int_lnx_rls/DataValue.o -s -o ../bin/bin/pseint
      make[2]: se sale del directorio '/home/pc/Descargas/pseint/pseint'
      make -C psexport -f Makefile.lnx
      make[2]: se entra en el directorio '/home/pc/Descargas/pseint/psexport'

      Eliminar
    3. mkdir ../temp/exp_lnx_rls
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c psexport.cpp -o ../temp/exp_lnx_rls/psexport.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/utils.cpp -o ../temp/exp_lnx_rls/utils.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/global.cpp -o ../temp/exp_lnx_rls/global.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/intercambio.cpp -o ../temp/exp_lnx_rls/intercambio.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/zcurlib.cpp -o ../temp/exp_lnx_rls/zcurlib.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/new_evaluar.cpp -o ../temp/exp_lnx_rls/new_evaluar.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/new_funciones.cpp -o ../temp/exp_lnx_rls/new_funciones.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/new_memoria.cpp -o ../temp/exp_lnx_rls/new_memoria.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/new_programa.cpp -o ../temp/exp_lnx_rls/new_programa.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_cpp.cpp -o ../temp/exp_lnx_rls/export_cpp.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c exportexp.cpp -o ../temp/exp_lnx_rls/exportexp.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/zcurlib_lnx.cpp -o ../temp/exp_lnx_rls/zcurlib_lnx.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/zcurlib_win.cpp -o ../temp/exp_lnx_rls/zcurlib_win.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_vb.cpp -o ../temp/exp_lnx_rls/export_vb.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_php.cpp -o ../temp/exp_lnx_rls/export_php.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_common.cpp -o ../temp/exp_lnx_rls/export_common.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_c.cpp -o ../temp/exp_lnx_rls/export_c.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_tipos.cpp -o ../temp/exp_lnx_rls/export_tipos.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_java.cpp -o ../temp/exp_lnx_rls/export_java.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_python3.cpp -o ../temp/exp_lnx_rls/export_python3.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_pascal.cpp -o ../temp/exp_lnx_rls/export_pascal.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_javascript.cpp -o ../temp/exp_lnx_rls/export_javascript.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_html.cpp -o ../temp/exp_lnx_rls/export_html.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_matlab.cpp -o ../temp/exp_lnx_rls/export_matlab.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_cs.cpp -o ../temp/exp_lnx_rls/export_cs.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/LangSettings.cpp -o ../temp/exp_lnx_rls/LangSettings.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c export_qbasic.cpp -o ../temp/exp_lnx_rls/export_qbasic.o
      g++ -O2 -I../pseint -D_FOR_PSEXPORT -c ../pseint/DataValue.cpp -o ../temp/exp_lnx_rls/DataValue.o
      g++ ../temp/exp_lnx_rls/psexport.o ../temp/exp_lnx_rls/utils.o ../temp/exp_lnx_rls/global.o ../temp/exp_lnx_rls/intercambio.o ../temp/exp_lnx_rls/zcurlib.o ../temp/exp_lnx_rls/new_evaluar.o ../temp/exp_lnx_rls/new_funciones.o ../temp/exp_lnx_rls/new_memoria.o ../temp/exp_lnx_rls/new_programa.o ../temp/exp_lnx_rls/export_cpp.o ../temp/exp_lnx_rls/exportexp.o ../temp/exp_lnx_rls/zcurlib_lnx.o ../temp/exp_lnx_rls/zcurlib_win.o ../temp/exp_lnx_rls/export_vb.o ../temp/exp_lnx_rls/export_php.o ../temp/exp_lnx_rls/export_common.o ../temp/exp_lnx_rls/export_c.o ../temp/exp_lnx_rls/export_tipos.o ../temp/exp_lnx_rls/export_java.o

      Eliminar
  2. ../temp/exp_lnx_rls/export_python3.o ../temp/exp_lnx_rls/export_pascal.o ../temp/exp_lnx_rls/export_javascript.o ../temp/exp_lnx_rls/export_html.o ../temp/exp_lnx_rls/export_matlab.o ../temp/exp_lnx_rls/export_cs.o ../temp/exp_lnx_rls/LangSettings.o ../temp/exp_lnx_rls/export_qbasic.o ../temp/exp_lnx_rls/DataValue.o -s -o ../bin/bin/psexport
    make[2]: se sale del directorio '/home/pc/Descargas/pseint/psexport'
    make -C hoewrap -f Makefile.lnx
    make[2]: se entra en el directorio '/home/pc/Descargas/pseint/hoewrap'
    mkdir -p ../temp/hoew_lnx_rls
    g++ -O3 -fPIC -c hoewrap.cpp -o ../temp/hoew_lnx_rls/hoewrap.o
    gcc -O3 -fPIC -c hoedown/html.c -o ../temp/hoew_lnx_rls/html.o
    gcc -O3 -fPIC -c hoedown/document.c -o ../temp/hoew_lnx_rls/document.o
    gcc -O3 -fPIC -c hoedown/buffer.c -o ../temp/hoew_lnx_rls/buffer.o
    gcc -O3 -fPIC -c hoedown/escape.c -o ../temp/hoew_lnx_rls/escape.o
    gcc -O3 -fPIC -c hoedown/stack.c -o ../temp/hoew_lnx_rls/stack.o
    gcc -O3 -fPIC -c hoedown/autolink.c -o ../temp/hoew_lnx_rls/autolink.o
    gcc -O3 -fPIC -c hoedown/html_blocks.c -o ../temp/hoew_lnx_rls/html_blocks.o
    ar cr ../temp/hoew_lnx_rls/libhoewrap.a ../temp/hoew_lnx_rls/hoewrap.o ../temp/hoew_lnx_rls/html.o ../temp/hoew_lnx_rls/document.o ../temp/hoew_lnx_rls/buffer.o ../temp/hoew_lnx_rls/escape.o ../temp/hoew_lnx_rls/stack.o ../temp/hoew_lnx_rls/autolink.o ../temp/hoew_lnx_rls/html_blocks.o
    make[2]: se sale del directorio '/home/pc/Descargas/pseint/hoewrap'
    make -C wxPSeInt -f Makefile.lnx3
    make[2]: se entra en el directorio '/home/pc/Descargas/pseint/wxPSeInt'
    mkdir -p ../temp/wx_lnx3_rls
    g++ -O2 `wx-config --cppflags --unicode=yes --version=3.1` -DFOR_WXPSEINT -DWX3 -c DebugManager.cpp -o ../temp/wx_lnx3_rls/DebugManager.o
    In file included from DebugManager.cpp:1:0:
    DebugManager.h:3:10: fatal error: wx/string.h: No existe el archivo o el directorio
    #include
    ^~~~~~~~~~~~~
    compilation terminated.
    Makefile.common:4: recipe for target '../temp/wx_lnx3_rls/DebugManager.o' failed
    make[2]: *** [../temp/wx_lnx3_rls/DebugManager.o] Error 1
    make[2]: se sale del directorio '/home/pc/Descargas/pseint/wxPSeInt'
    Makefile.lnx3:2: recipe for target 'all' failed
    make[1]: *** [all] Error 2
    make[1]: se sale del directorio '/home/pc/Descargas/pseint'
    Makefile:17: recipe for target 'linux3' failed
    make: *** [linux3] Error 2

    Cuando pueda hacer la prueba en Debian, comentaré el resultado.

    Gracias.

    Nota: la respuesta quedó en tres partes.

    ResponderEliminar
  3. make[2]: se sale del directorio '/home/pruebas/sourcecode/pseint/hoewrap'
    make -C wxPSeInt -f Makefile.lnx3
    make[2]: se entra en el directorio '/home/pruebas/sourcecode/pseint/wxPSeInt'
    mkdir -p ../temp/wx_lnx3_rls
    g++ -O2 `wx-config --cppflags --unicode=yes --version=3.1` -DFOR_WXPSEINT -DWX3 -c DebugManager.cpp -o ../temp/wx_lnx3_rls/DebugManager.o
    In file included from DebugManager.cpp:1:0:
    DebugManager.h:3:10: fatal error: wx/string.h: No existe el archivo o el directorio
    #include < wx/string.h >
    ^~~~~~~~~~~~~
    compilation terminated.
    Makefile.common:4: recipe for target '../temp/wx_lnx3_rls/DebugManager.o' failed
    make[2]: *** [../temp/wx_lnx3_rls/DebugManager.o] Error 1
    make[2]: se sale del directorio '/home/pruebas/sourcecode/pseint/wxPSeInt'
    Makefile.lnx3:2: recipe for target 'all' failed
    make[1]: *** [all] Error 2
    make[1]: se sale del directorio '/home/pruebas/sourcecode/pseint'
    Makefile:17: recipe for target 'linux3' failed
    make: *** [linux3] Error 2
    pruebas@ubuntu:~/sourcecode/pseint$

    ResponderEliminar
    Respuestas
    1. Los makefiles dicen "version=3.1" cuando invocan a wx-config, pero se debería funcionar igual si se cambia por 3.0.

      Eliminar