Julien Jorge's Personal Website

Découvrir Docker, Python, LLVM et Emscripten

Tue Feb 23, 2021

Ce post a été publié sur LinuxFr.org. Vous pouvez le lire là bas, ainsi que les commentaires associés

Alors que l’année 2020 se terminait, je me suis mis à faire un petit tour des outils qui sont rentrés dans mon quotidien pendant cette période. Ça t’intéresse ? Allez, regardons ensemble.

Docker

Je ne connaissais Docker que de nom et j’avais entendu ici et là que c’était chouette pour isoler des trucs. C’est vrai que c’est chouette.

Si tu développes une application sous Linux, c’est un vrai gain d’avoir une image Docker avec toutes les dépendances de ton application. D’une part cela te permet de retrouver très facilement la liste de ses dépendances ainsi que leurs versions, d’autre part cela t’autorise à lancer des builds dans un environnement exempt de parasitage. Fini le problème du build qui tire libfoo 2.4 quand t’as besoin de la 4.2.

Sur la CI c’est bien pratique. Tu lances ton build dans une image Docker qui ne contient que ce qui est nécessaire pour compiler. Si tu oublies des dépendances, tu le vois tout de suite ; si tu mets trop de dépendances, euh…, tu ne le vois toujours pas… Si tu as un projet qui nécessite awesome-tool 2.3 et un autre qui requiert awesome-tool 3.2, tu fais une image avec chaque version et ça roule.

Après pour le cas d’un projet ayant des dépendances spécifiques, en général ça se gère aussi facilement sans Docker en configurant correctement son build et en installant les dépendances dans le build et non pas au niveau système. Mais il y a quand même quelques outils qui n’aiment pas trop être dupliqués. Par exemple Python ou Ruby. Avec Python on s’en sort un peu avec virtualenv, même si ça m’a l’air un poil fragile du fait que cela crée des liens vers le Python du système. Avec Ruby, si je me souviens bien, c’est hyper galère, voire impossible, d’avoir plusieurs installations de Ruby Version Manager. Cela dit ça fait deux ans que j’ai regardé ça, cela a peut-être changé depuis.

Néanmoins, je n’irais pas utiliser une image Docker pour le développement au quotidien. Déjà parce que ça prend une éternité de télécharger les images, ensuite parce que c’est inconfortable au possible de compiler dans l’image. À la rigueur tu peux partager ton dossier de dev pour coder sur l’hôte et compiler dans l’image, mais alors tous les fichiers deviennent la propriété de root et ça devient vite crado.

Du coup il faut quand même quelque chose d’autre pour gérer les dépendances proprement.

Enfin le plus gros problème de Docker est que ça n’existe pas sous OSX et à peine sous Windows. Quand tu cibles toutes ces plates-formes ça devient vite limitant.

Au final j’utilise volontiers Docker sur la CI pour partir d’un truc frais à chaque build, et pour pouvoir relancer d’anciens builds, mais je crois que pour ce qui est des dépendances je préfère encore utiliser quelque chose au niveau du projet.

Python

J’ai fait très peu de Python durant ma carrière ; il faut dire que j’en avait une assez mauvaise image. Déjà c’est de l’interprété, donc tu ne peux rien faire de sérieux avec, et en plus c’est lent.

Finalement c’est pas si mal.

Venant du C++ j’apprécie en particulier les facilités de formatage de chaînes de caractères, de manipulations de listes, etc. Il y a un côté concis et direct de certaines opérations qui rendent l’écriture de code assez confortable.

Venant du C++ je n’apprécie guère le duck typing et le côté interprété du langage. Lors du premier jet c’est assez agréable, le script est propre et on a tout en tête, mais lorsqu’il faut y revenir plus tard par exemple pour ajouter un paramètre à une fonction, alors ça devient galère. On se met à la recherche de tous les appelants pour les modifier, puis les appelants des appelants. Et si tu en oublies tu ne le sauras pas tant que le chemin d’exécution ne passera pas par l’appel erroné. Quelle galère.

Alors tu vas me dire, ouiiiiii, tests unitaiiiiiiires, gna gna gna, ça oblige les devs à bien couvrir leur code. Oui, mais non. C’est quand même pénible.

De mon point de vue, un code doit être correct syntaxiquement, sémantiquement, et algorithmiquement. Les tests écrits par le développeur couvrent le dernier point. Pour les deux autres un outil peut parfaitement le faire. Valider la sémantique au niveau des tests c’est mélanger les problèmes, c’est pas très single responsibility principle.

Un truc qui me perturbe un peu en Python, c’est qu’il y a une certaine résistance à l’algorithmique — le côté négatif de la concision. Genre si t’écris une boucle for à l’ancienne, avec initialisation, condition d’arrêt et incrément, tu te sens sale. Par exemple, pour extraire les éléments d’une liste selon deux propriétés indépendantes, en Python on préférera « filtrer » deux fois la liste avec un prédicat pour chaque propriété, plutôt que de boucler une seule fois et tester les deux propriétés à chaque itération. Ce que je trouve discutable puisque le deuxième filtre va retester des éléments sélectionnés par le premier.

Enfin bon, c’est juste l’avis d’un dev C++. En dehors de ces soucis c’est franchement pas mal. J’en viens même à apprécier d’écrire mes scripts en Python plutôt qu’en Bash.

LLVM

Aaaah LLVM. Quel bel outil. C’est la base de Clang, un des meilleurs compilateurs C++ du moment. En plus de l’outil je me suis mis à travailler sur les composants de LLVM lui-même, c’est à dire l’API du langage intermédiaire.

Ces dernières années LLVM est monté à toute vitesse en popularité. Une base de code jeune et accessible, en C++ et orienté objet, ça rafraîchit. Et puis ils ont bien amélioré l’état des messages d’erreurs affichés par le compilateur par rapport à ce qu’on pouvait avoir avec GCC. Entre temps, du côté du vieux GCC, les devs se plaignaient de la complexité du code depuis des années. LLVM a offert un vent de renouveau, et tout le monde est parti vers lui, délaissant l’ancêtre qui, bien que fort de 30 années d’expérience, se voit mis au placard au profit du jeunot fraîchement sorti de l’œuf.

La grande force de LLVM c’est aussi l’ensemble des outils à disposition pour transformer et manipuler du code, notamment clang-tidy, pour vérifier certaines propriétés du code, et clang-format, pour formater le code selon certaines propriétés.

Savais-tu que LLVM était lui-même codé en C++ ? À coup sûr ça doit être hyper clean.

Argh. Et bien non, ce n’est pas hyper clean du tout. LLVM est l’incarnation du cordonnier mal chaussé. On y trouve des fichiers de dizaines de milliers de lignes de code, et de l’objet à foison, même quand ça ne colle pas du tout. Dans quel modèle objet est-il acceptable pour une instance de se downcaster ? La documentation est d’un côté excellente (cf. la référence du langage par exemple) et de l’autre côté complètement inutile (cf. une bonne vieille doc Doxygen pleine de « diagrammes de collaboration » inutiles, et qui ne fait que lister les méthodes sans expliquer l’intention).

À côté de ça on a des templates à foison et des compilations interminables. J’ai vaguement espéré contourner ça en utilisant des builds unitaires mais évidemment, entre les using namespace au niveau global et les #define qui ne sont jamais #undef, ça ne fonctionne pas.

Ajoute à tout ça des fichiers de plusieurs dizaines de milliers de lignes et une structure de dépôt bien chaotique, et c’est complet.

Bref, les outils fournis par LLVM sont vraiment top mais en interne c’est très décevant.

Emscripten

Emscripten est un compilateur C ou C++ vers JavaScript basé sur LLVM qui se veut être un remplaçant direct de GCC ou Clang pour compiler à peu près n’importe quel projet vers WebAssembly.

On en est à la troisième version : la première que je ne connais pas, la deuxième nommée fastcomp qui était un fork de LLVM, et la version actuelle qui s’appuie sur la version officielle de LLVM, sans modifications supplémentaires. Ce qui est assez chouette.

J’avais déjà tenté d’utiliser Emscripten il y a 8 ans pour faire une version web d’un jeu en C++ qui utilisait la SDL, OpenGL, Boost. Je ne me souviens pas du résultat mais je pense que ça n’avait pas donné grand chose. Il y a encore la trace des galères de compilation de Boost sur StackOverflow. Il faut dire qu’associer le pénible BJam avec le tout jeune Emscripten c’était un peu chercher les problèmes.

À titre personnel, je trouve que combiner du C et du JavaScript c’est un peu unir deux mondes complètement opposés ; et en dehors du fun de la technique j’ai quelques doutes sur l’utilité de l’outil. Ça permet de mon point de vue de ne pas avoir la performance du natif tout en n’ayant pas la simplicité de JavaScript, et en ne bénéficiant ni les outils développeurs du natif, ni de ceux du navigateur.

J’ai pu retoucher un peu à Emscripten récemment et j’ai découvert au passage l’existence de WASI, qui offre notamment un moyen de lancer les programmes WebAssembly en dehors du navigateur. Et là, franchement, l’idée de pouvoir écrire mon programme en C++, le compiler en WebAssembly, l’intégrer à une app Electron, installée via Flatpak, pour ensuite la lancer dans un Docker qui tourne dans une VM… Toutes ces indirections, ça me fait rêver.

Allez allez, j’arrête de blaguer là dessus.

Donc, j’ai refait un peu de WebAssembly pour le travail, et franchement c’est assez accessible. Effectivement ça remplace assez simplement un autre compilateur, et il y a de plus de nombreux points de customisation. On peut facilement, par exemple, remplacer la version de LLVM utilisée en back-end par une autre version. Facilement avec quelques limites quand même car Emscripten utilise des options de compilation en dur, qui doivent être comprises par le compilateur.

Pour ce qui est de la conversion C++ vers WebAssembly, on peut quasiment tout convertir tant qu’il n’y a pas de threads, pas d’exceptions, et autres subtilités.

Au final, il y a un truc que j’ai adoré en utilisant Emscripten, c’est l’accueil des développeurs. J’ai envoyé quelques petits patchs et ouvert quelques rapports de bug, et à chaque fois les retours étaient clairs, encourageants et constructifs. Un projet qui prend soin des nouveaux arrivants comme ça, fraaaaaaaanchement. C’est top.

C’est tout

Ces quatre projets sont les plus gros que j’ai découvert cette année. Peut-être que mon jugement est un peu à côté de la plaque ? Il faut dire que, de fait, je n’ai pas beaucoup d’expérience sur chacun.

Si tu en sais plus ou si tu vois des points remarquables à côté desquels je serai passé, n’hésite pas à me corriger dans les commentaires !