domingo, junio 03, 2007

Portando aplicaciones .NET a Mono

Manual de buenas prácticas de programación multiplataforma con tecnologías .NET

Jonathan Pobst, el creador de la herramienta de testeo de ensamblados por excelencia de Mono (Moma) que viene de serie con Mono desde la versión 1.2, ahora ha dejado un regalito para todos aquéllos fans de las posibilidades multiplataforma de .NET que quieran portar sin mucho esfuerzo sus aplicaciones a Mono.

La realidad, queramos o no verla, es que a pesar de todos los pesares, Mono y .NET son plataformas distintas funcionando en medios distintos, pero que proveen un entorno común para la ejecución de código. Lo verdaderamente interesante de esta tecnología es que podemos hacer que un mismo programa, sin volver a compilar, funcione en muchas plataformas... si lo diseñamos y programamos bien.

Por supuesto, si nuestro programa va orientado sólo a usuarios Windows, utilizaremos .NET y nos dará igual portarlo a Mono... y con el tiempo desarrollaremos hábitos de programación de programadores Windows que costará desarraigar. A la inversa, si lo hacemos con Linux o MacOS como objetivo, usaremos Mono y sus librerías especiales, y nos olvidaremos de que hay algunas cosas más a tener en cuenta cuando programamos una aplicación multiplataforma. Pero si queréis que vuestras aplicaciones estén disponibles en el mismo momento para todas las plataformas, hay que aprender hábitos de programación multiplataforma.

El siguiente artículo muestra, con un ejemplo, cómo transformar una aplicación dependiente de la arquitectura .NET de Microsoft sobre Windows a una multiplataforma que funcione al tiempo sobre .NET o Mono. Las directrices son muy sencillas. Paso a resumirlas ahora en español.

1. Rompiendo la brecha entre Mono y .NET

A pesar de los pesares, Mono sigue siendo una tecnología diferente en algunos aspectos de .NET. La mayor de sus diferencias es que, mientras .NET va por su segundo Runtime y su tercera iteración de librerías de clases, Mono todavía está inmerso en el desarrollo de la capa de compatibilidad para la versión 2.0 de .NET. Esta situación deja patente que algunos métodos y clases de .NET que podríamos estar usando en nuestra aplicación para Windows, podrían no existir todavía para Mono.

Para detectar el problema de aplicaciones que funcionan en .NET y no en Mono, el propio autor del artículo, Jonathan Pobst, liberó Moma, que hace un análisis de los métodos a los que llama cada ensamblado y da un informe detallado de lo que está y no está implementado hasta el momento. Además, podemos hacer nosotros mismos la prueba con Mono y ver si nuestra aplicación compila y funciona en un entorno Linux.

Para solucionar el problema, dado el caso, hay dos alternativas. La primera es generar dos ensamblados, uno para cada una de las plataformas, donde adaptemos el código a las plataformas que tenemos como objetivos. Lo bueno de esta solución es que el programa funcionará bien siempre que usemos el ensamblado apropiado a nuestra plataforma y que, una vez Mono llegue al punto de no generar errores, podremos usar el ensamblado de .NET en Mono sin problemas. Lo malo es que mientras tanto tendremos que generar dos veces el mismo código (o tres... o cuatro... dependiendo de si hay más incompatibilidades que sólamente las del Runtime).

La segunda opción es utilizar mecanismos de detección en tiempo de ejecución para hacer funcionar una u otra parte del código según esté siendo utilizado en Mono o .NET. La ventaja es que con un único ensamblado contemplaremos todas las plataformas. La desventaja, que tendremos duplicidad de código, dificultad de búsqueda de errores y mucha más dificultad de readaptación una vez Mono cumpla los requisitos básicos de nuestra aplicación.

También hay otra opción, aunque puede que no se de para todos los casos. Adaptar nuestro código al estado actual de Mono. Esto envolverá utilizar otros métodos o clases que ya estén implementados en Mono y que hagan lo mismo que esperamos que haga nuestra aplicación, o crearnos los nuestros propios. Para esto habrá que estar atento al punto en el que se encuentra el desarrollo de Mono y saber qué métodos y clases de compatibilidad están disponibles en cada versión de Mono.

2. Rompiendo la brecha entre Windows y Linux (y Solaris (y MacOSX (y BSD (...))))

Incluso evitando problemas con las diferencias en el Runtime y las clases de compatibilidad, todavía puede ser que tengamos problemas con las llamadas al sistema o el uso de ficheros, que depende del sistema. Los tres grandes grupos de errores en este aspecto son:

  • Win32 P/Invokes
  • Mayúsculas y minúsculas
  • Definición de subdirectorios y rutas de archivos

Los Win32 P/Invokes son las llamadas que se hacen a librerías DLL del sistema. En ocasiones esas librerías son nativas del sistema, otras veces son de otras aplicaciones o distribuídas por Internet, pero el gran problema es que no son de código manejado. En otras palabras, dependen del sistema. Por eso, para solucionar problemas con los P/Invoke siempre se preferirá, a ser posible, evitar llamadas directas a librerías del sistema y utilizar código manejado. Por lo general, con la trayectoria que lleva .NET y Mono ya existe código manejado para casi todo. En caso de no existir, la prioridad es generarlo nosotros... y sólo en casos extremos, utilizar P/Invokes. El problema es que la llamada a las librerías del sistema nos obligará a detectar en tiempo de ejecución en qué sistema estamos y hacer llamadas a una u otra librería dependiendo de ello.

Los otros dos problemas son mucho más fáciles de solucionar. Sobre el primero de ellos, el problema de mayúsculas y minúsculas, lo único que hay que tener en cuenta es que, a la hora de programar, debemos hacerlo tomando las reglas más restrictivas, es decir, siendo consistentes en el nombrado de los ficheros y utilizando mayúsculas donde haya que usarlas. Si siempre usamos minúsculas en todo no habrá problemas, pero si usamos mayúsculas tenemos que acordarnos de usarlas también en el código de nuestra aplicación.

El problema de usar "\" en Windows y "/" en Linux para dividir los directorios nos obliga a no poder utilizar directamente strings para llamar a ubicaciones de archivo. En su lugar tendremos que hacerlo utilizando Path.DirectorySeparatorChar para dividir los archivos o bien Path.Combine, que nos combinará los nombres de los subdirectorios usando el carácter divisor apropiado.

Conclusión

Si vas a lanzarte a la programación .NET ten en cuenta que tus aplicaciones venderán más si van dirigidas a más público. La multiplataforma no es simplemente una idea friki idealista de cómo debería ser el mundo de la informática. Es un objetivo claro de mercado. Puedes convertirte en un programador .NET, tener hábitos de programación sobre Windows y tener como público objetivo los usuarios de Windows. Puedes convertirte en un programador Mono, tener hábitos de programación sobre plataformas Unix y tener como objetivo los usuarios de Linux. O puedes convertirte en un programador multiplataforma, tener hábitos de programación sobre multiplataforma, siendo capaz de programar en Visual Studio o Monodevelop (o cualquier otro) la misma aplicación y de desarrollar soluciones teniendo como público objetivo a todo el mundo que tenga un ordenador, sea cual sea la marca, modelo o sistema operativo. Si tu fueras la empresa, ¿a quién buscarías?

Mi Tienda Lulu