Expérience dans .Net

Samedi 21 août 2004 21:54 - Code

J'ai déjà écrit sur mono mais ma seule expérience, c'était d'avoir utilisé Muine, pas vraiment de quoi porter un avis sur le développement (ce dont je ne me suis pas privé mais c'était en fait plus râler contre les API en C qu'autre chose).

21 août. 17 degrés. 17 degrés. En août. Comme c'est nul. Et c'est ainsi que je n'ai mis le nez dehors que pour oublier d'acheter de la confiture et lire deux chapitres du bouquin en cours entre deux nuages. Le reste de la journée fut mono-maniaque. Ah ah. Jeu de mot.

L'objectif est simple, ajouter un binding .NET à Lasso. Le gros du boulot est prémâché par SWIG et rapidement une série de fichier .cs sont produits. J'y ajoute un fichier à moi:

using System;

public class runme
{
    static void Main()
    {
        Console.WriteLine("lasso_init");
        liblassosharpglue.lasso_init();
    }
}

Je compile tous les fichiers en un coup (il n'y a pas d'équivalent aux fichiers objets .o, il faut passer directement des sources à l'exécutable) et je peux lancer un test.exe. À ce sujet, distrayant:

 $ ldd test.exe 
not a dynamic executable
 $ file runme.exe 
test.exe: MS Windows PE 32-bit Intel 80386 console executable

Exécution donc, et pan dans la panade, free(): invalid pointer 0x41951ec0! et rien du tout. Longs tatonnements et ça semble être un bug dans les fichiers produits par SWIG concernant les chaînes constantes. Aucune n'est utilisée ici mais apparemment le lasso. fait qu'une classe est instanciée (?) et qu'en tout cas toutes les variables marquées readonly sont évaluées. Ce n'est pas trop clair mais un gros sed pour ajouter des malloc() aux endroits stratégiques et cette partie-ci est terminée.

Un exécutable, donc. Il faudrait une bibliothèque. apt-get source gtk-sharp pour avoir un exemple. Complexe, l'exemple. Mais en fait, il suffit d'ajouter un -target:library pour qu'un fichier .dll soit produit en lieu et place du précédent .exe.

Tout ça tourne. Pourquoi ne pas passer deux heures maintenant à essayer de remplacer le liblassosharpglue.whatever() par un plus court lasso.whatever() ? Ça a l'air con mais la bibliothèque qui fait liaison avec le système /usr/lib/liblassosharpglue.so se met à porter un nom différent de la dll, lasso.dll, ça s'embrouille, ça cherche des lasso.so, c'est la confusion totale, d'un chapeau nommé strace sort une info concernant des fichiers .config et essais/erreurs pour produire lasso.dll.config:

<configuration>
  <dllmap dll="lasso" target="liblassosharpglue.so"/>
</configuration>

C'est de l'XML, c'est lisible, j'ai aucune idée de ce que cela signifie, en tout cas aucune assise théorique pour affirmer quoi que ce soit, pas grave, ça marche. Je me suis déjà fait reprendre pour cette attitude mais les études sont loin, passons.

Installation de fichiers à gauche et à droite, /usr/share/dotnet/lasso/ pour le lasso.dll et son .config, un fichier pour pkg-config (le compilateur mono supporte ça de base, il suffit de faire -pkg:lasso-sharp et il trouve tout seul ce qu'il faut au bon endroit), etc.

Compilation d'un programme de test (un peu plus long que le précédent, pas la peine de le copier/coller ici), nickel, exécution, rien du tout, veut pas trouver la .dll ou le .so. À ce moment est évitée une disgression intéressante sur la féminité des dll et la masculanité des so, hors sujet.

Lecture d'un peu de doc et apparemment, .NET, c'est la même merde que Java (certains l'affirmaient sans preuve...). Java, c'est des galères avec des variables CLASSPATH, mono, ce sera MONO_PATH. D'où cela vient-il ? C'est Microsoft Windows, tellement le bordel, DLL et tout ça, chaque application vient avec ses dll et les garde dans son répertoire. Et c'est ainsi que les applics .NET vont chercher leurs DLL dans le répertoire de l'application puis dans le répertoire système (/usr/lib de ce côté de la force, j'imagine que c'est c:\window\system\ du côté obscur).

La solution passe par le GAC, le Global Assembly Cache. Au passage, ça réinvente les sonames et le versioning de bibliothèque. Un peu de magie noire et la bibliothèque se retrouve installée:

/usr/lib/mono/gac/lasso/0.0.0.0__eecda4bb0da29115/lasso.dll

Le répertoire au nom bizarre, c'est pour assurer l'unicité des bibliothèques; les esprits chagrins diront qu'un:

/usr/lib/liblasso.so.0.0.0.0

est équivalent et existe depuis "toujours". Ils auront bien sûr raison. (Sauf qu'il n'existe toujours pas de liens symboliques sous Windows, si ?).

Ah, un mot sur la magie noire évoquée quelques lignes plus haut. L'installation dans le GAC se fait via le gacutil. Mais ça ne marche pas pour toutes les DLL, il faut qu'elles aient un Strong Name. Ça se résume apparemment à ce que la bibliothèque soit signée:

using System.Reflection;
using System.Runtime.CompilerServices;

[assembly:AssemblyDelaySign(false)]
[assembly:AssemblyKeyFile("lasso-sharp.snk")]

Le Delay, c'est pour pouvoir remettre la signature à plus tard, la raison c'est bien sûr que tout le monde n'est pas autorisé à signer une bibliothèque, etc. Ainsi le KeyFile ne serait qu'une clé publique pour les pauvres petits développeurs et après qu'ils aient sué le nécessaire le chef peut arriver avec la clé privée pour rendre tout ça officiel. C'est à des "besoins" comme ça qu'on ne pense pas, quand on est habitué aux logiciels libres... Ici donc, signature immédiate avec la clé publique et privée qui est tapée dans le CVS.

C'est compilé, c'est signé, c'est installé dans le GAC:

mcs -g -nologo -pkg:lasso-sharp -out:test.exe test.cs 

Et c'en est terminé. (non, je ne sais pas ce que signifie le -nologo)

Dernière modification: samedi 21 août 2004 21:55