février
2013
Il y a parfois des périodes dans l’année durant lesquelles il me faut me sortir de mon train-train Java / JavaFX quotidien pour écrire dans d’autres langages (C, C++, C#, bash, cmd ou même R et une fois ou deux du SQL voir du VB ou du FORTRAN) histoire de découvrir (ou de redécouvrir car le manque de pratique fait oublier) ces langages, API ou VMs.
Le problème du jour consistait à porter du vieux code Java datant de 2001-2002 rapidement sous Windows pour faire un exécutable en ligne de commande (un convertisseur que le boss peut appeler de son code R). Comme je ne suis pas trop d’humeur à faire du C++ en ce moment, j’ai opté pour C# à la place : les différences de syntaxe entre Java et C# sont minimales (encore plus avec du vieux code), de même que les APIs d’entre la JVM et .NET, ce qui promet un portage rapide et sans heurs du code.
Donc, dans VC# 2010 Express, j’ai créé le port de mon code dans un projet de type library (dll) et le code du convertisseur dans un projet de type exécutable. Je passe rapidement sous silence le cours moment embarrassant où il me faut arriver à me souvenir comment inclure des références dans le projet (hum). Non, mon élan s’est brisé lorsque j’ai glissé sur une simple peau de banane : compiler le projet produit un exécutable MAIS il faut également fournir le DLL avec. Cela peut sembler banal, mais évidement c’est un gros « no-no » de la part de mon boss qui veut juste un exec simple a copier é droite et é gauche sans devoir penser é rechercher un second fichier ou é se taper une erreur assez cryptique quand il oublie de le faire.
De mon coté, ayant été formé à C puis C++ qui permettent la compilation statique, puis ayant utilisé pendant des années Borland JBuilder qui permettait de consolider l’ensemble du code Java dans un unique JAR (non que bidouiller à la main le contenu d’un JAR, qui n’est qu’un fichier ZIP de toute manière, soit d’une difficulté extrême), ma réaction a été un gros « Gné ?! » venu fort à propos devant le fait que VC# ne propose pas du tout ce genre d’option.
Donc, en fouillant (Google-powa) bien on trouve des références à l’outil en ligne de commande ILMerge de Microsoft qui permet de consolider plusieurs Assembly en un seul (je ne me ferai jamais a cette syntaxe…). L’idée est tentante et après dépatouillage avec les flags pour arriver a trouver comment fusionner des Assembly compiles pour .NET 4 en bravant divers messages d’erreur absconds, j’arrive bien a produire un exécutable…
Note, utiliser :
ou dans mon cas sur une machine 64bit :
Mais l’exécutable produit ne fait rien quand on l’invoque… Trop cool !
J’ai essayé également l’outil graphique IL Merger Tools mais au final les messages d’erreur étaient encore plus cryptiques qu’en utilisant ILMerge… ><
Je suis alors tombé sur ce post un peu plus prometteur qui indique comment inclure directement le DLL dans le projet et l’empaqueter dans l’exécutable (en modifiant son Build Action dans ses propriétés). Un problème surgit cependant : lors de l’exécution, la VM ne peut pas trouver le DLL et donc il faut lui dire comment le charger ce qui se fait en rajoutant un solveur. Quelques part c’est un peut comme si on injectait un ClassLoader customise en Java.
Un code similaire à celui-ci soit être place au tout début du main() :
String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Astuce pour que cela fonctionne : remplacer « AssemblyLoadingAndReflection » par le nom de votre Assembly (ici dans mon cas le nom de mon exécutable, sans le .exe à la fin).
Dans le cadre d’un programme en ligne de commande, cela demande un petit aménagement également : il faut déplacer le code appelant les fonctions ou classes du DLL dans une classe externe et aussi déplacer l’appel à cette classe dans une méthode autre que dans le main(). Si on ne le fait pas le solveur n’est jamais appelé, et le programme échoue à trouver le DLL. Si on le fait, lors que les fonctions ou classes du DLL sont appelées, le solveur est correctement invoque et va charger le DLL.
Yipi ! Je suis content et encore plus le boss est content !
Certes tout n’est pas parfait : bien que les projets soient dépendants l’un de l’autre, il faut toujours recopier a mano les nouvelles versions du DLL depuis le projet library vers le projet exécutable. Ça doit pouvoir se régler je pense en mettant un action post build ou pre build quelques part dans un des projets mais franchement ça sera pour plus tard, on a perdu déjà un peu trop de temps sur ce qui devait être un port rapide pour faire un convertisseur tout simple.
Bon, maintenant il va me falloir expliquer à mon boss en long et en large que ce n’est pas une bonne idée que de désinstaller .NET juste pour éviter de se taper 350.000 mises-à-jour tous les mois… ><
1 Commentaire + Ajouter un commentaire
Commentaires récents
- Back from the future… dans
- Back from the future… dans
- Static linking = does not Compute dans
- Paquetage x 2 dans
- Why you little… dans
[…] Static linking = does not Compute par bouye (05/02/2013 02:08) […]