28 de Septiembre, 2013

Cargando librerías locales en Pyglet

El principal punto débil de pyglet a la hora de distribuir los proyectos es AVbin, que es el único componente no python que propociona un API estable para acceder a la parte audio/video de pyglet.

Bueno, en realidad no es tan difícil ya que solo hay que instalar la librería, pero la versión que suele venir empaquetada en las distribuciones es algo vieja, y con AVbin es mejor ir siempre a la última.

¿Podemos instalar con el instalador de AVbin? Sí, pero no es tan cómodo y conveniente como por ejemplo los paquetes binarios para Windows creados con py2exe, y los jugones quieren jugar, no pelearse con dependencias :P.

En Windows se pueden incluir las DLL y al final tenemos un ZIP que es solo descomprimir y ya se puede jugar, así que he estado pensando: ¿se podría hacer algo así en Linux? Ojeando el código de pyglet resulta que es muy fácil de hacer ;).

pyglet utiliza ctypes.util.find_library para encontrar las librerías a cargar, que a su vez utiliza diferentes mecanismos dependiendo del sistema operativo. La idea es cambiar el comportamiento de esa función de forma que cuando se busque AVbin devolvamos la ruta a una librería local que podemos distribuir con nuestro juego.

Por ejemplo:

def find_local_library(path, fn):
    """Load libraries for a local path"""
    LOCAL = { "avbin": r"%s/lib/%s/libavbin.so.11" }
    bits = "64bit" if sys.maxsize > 2**32 else "32bit"
    if path in LOCAL:
        return LOCAL[path] % (pyglet.resource.get_script_home(), bits)
    return fn(path)

_find_library = ctypes.util.find_library
ctypes.util.find_library = lambda path: find_local_library(path, _find_library)

Lo que hago es monkeypatch ctypes.util.find_library para que si se pide la ibrería avbin, se devuelva una ruta relativa a la ubicación del script que ejecuta el juego tal que .../lib/BITS/libavbin.so.11.

He incluído la parte de los bits porque al ser una librería binaria tenemos que incluir una versión para 32-bit y otra para 64-bit. Además notar que solo lo uso en Linux y por eso no me he preocupado de poner la ruta de una forma portable.

De esta forma es sencillo incluir las librerías que queramos de forma que sea descomprimir y jugar; o incluso empaquetar en formato deb o rpm incluyendo una librería local independiente de lo que el usuario tenga instalado en el sistema.

El caso es que funciona muy bien y lo he usado para construir los paquetes de Lunar y For Science! para Debian y Ubuntu (aunque he hecho un paquete para 32-bit y otro para 64-bit, la idea es la misma).

Potencialmente se podría hacer lo mismo en Mac, pero no tengo un sistema para hacer pruebas, así que por ahora no he podido empaquetar para el sistema de Apple :(.

Actualización: he compartido la idea en la lista de correo y hemos acabado haciendo esto: search for local libs based on the script path (Linux, Mac), así que si usas el código del repo no es necesario recurrir al monkeypatching ;).

Anotación por Juan J. Martínez, clasificada en: pyglet, python.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: