Logo Spiria

WebAssembly avec Nim

12 juin 2018.

Vous avez probablement déjà entendu parler de Nim. C’est un langage moderne avec une syntaxe qui vous rappellera un peu Python, mais il est à la fois statiquement typé et compilé. Il prend en charge plusieurs styles de programmation (fonctionnel, orienté objet, métaprogrammation et passage de messages pour n’en nommer que quelques-uns) et offre en option un “garbage collector” ajustable.

Il produit un code extrêmement rapide et efficace, ce qui en fait un outil idéal pour les systèmes embarqués et autres environnements où les ressources sont restreintes. Ajoutez à cela sa capacité à être compilé en code C, et c’est un choix naturel pour les projets Internet des Objets (IoT), l’une de nos spécialités.

Dans cet article, je vais me pencher sur une utilisation différente. Les mêmes qualités qui en font un bon choix pour les projets IoT en font également un bon choix pour de nombreux projets Web. L’introduction de WebAssembly (WASM) rend cela possible.

Tout comme vous avez probablement entendu parler de Nim, vous avez probablement entendu parler de WebAssembly. C’est une nouvelle technique pour obtenir des niveaux de performance presque natifs dans le moteur de rendu du navigateur. Il s’agit essentiellement d’une sorte de langage assembleur pour le Web qui est indépendant de la plateforme matérielle. Ce langage de bas niveau au format binaire compact est optimisé pour un chargement rapide, un démarrage rapide et un fonctionnement rapide, au détriment d’une écriture facile du code. Pour faciliter l’utilisation de WASM pour les humains, des compilateurs C, C, C++ et Rust qui compilent en WASM ont été créés plutôt que de passer par l’assembleur classique qui dépend de la plateforme matérielle.

L’option Nim vers C ouvre la porte à l’utilisation de Nim pour créer du code WASM grâce au compilateur Emscripten LLVM qui prend en charge la compilation de C vers WASM. Je bricole avec la compilation de Nim vers WASM depuis presque un an maintenant et je vais vous fournir quelques exemples basiques pour vous montrer une méthode qui fait que ça fonctionne et que vous pouvez utilisez comme base de travail. Mon approche présentée ici n’est probablement pas la seule façon de faire en sorte que ça marche (voir le projet NLVM pour un compilateur LLVM pour Nim ou le projet NWAsm pour un back-end WASM expérimental pour Nim — et je ne suis pas vraiment le seul à compiler du Nim en WASM (j’ai repéré ce message de Lando sur un forum après avoir indépendamment fait le travail, et j’ai depuis adopté certains de ses changements de configuration, car ils sont plus propres que mes originaux). Aussi, de nouvelles ressources Nim/WASM apparaissent de toutes parts maintenant.

Je vais supposer un environnement de build Linux. Ce n’est pas une grande restriction ; de fait, j’ai inclus un fichier Vagrant qui vous permettra d’utiliser facilement Vagrant pour créer une machine virtuelle VirtualBox (VM) autonome, ce qui vous permettra de faire tourner un environnement de développement correctement équipé sans perturber votre configuration système globale, que vous utilisiez Linux, MacOS, MS-Windows, ou autre chose de plus exotique. Si vous ne l’avez jamais fait auparavant, cela revient à installer VirtualBox et Vagrant, à cloner une copie de mon repo git nim-wasm-helpers et à exécuter Vagrant dans le dossier résultant via :

> git clone https://github.com/Feneric/nim-wasm-helpers.git
> cd nim-wasm-helpers
> vagrant up

…et à attendre un moment. Combien de temps ? Sérieusement, allez vous chercher une bonne tasse de café après avoir donné le coup d’envoi. Ça crachera aussi beaucoup de sorties, surtout du vert, pas mal de gris et un peu de blanc, de jaune et de bleu clair, mais pas trop de rouge (quelques lignes, l’une au sujet de dpkg-preconfigure et l’autre sur la création d’un lien symbolique qui sont censées être rouges). Le résultat de cette opération est que vous devriez avoir une nouvelle VM fonctionnant localement dans laquelle vous pouvez travailler et dans laquelle vous pouvez vous identifier via la commande vagrant ssh. Cette VM fonctionnera sous Bodhi Linux et aura des versions appropriées de Nim et Emscripten installées. Elles seront correctement configurées pour compiler Nim vers WASM. Cette installation et configuration représente plus de la moitié de votre mission, profitez donc de cet environnement préconstruit pour gagner pas mal de temps.

Le saviez-vous ?
Les équipes de Spiria ont une longue expérience en développement d’applications web complexes et peuvent vous aider sur tout projet d’envergure.

C’est le moment d’aller dans votre nouvelle VM et de créer un dossier de travail pour votre premier projet Nim/WASM ; vous pouvez l’appeler comme vous voulez. Ma configuration comprend un dossier appelé Samples. Tout ce qui est placé sous le dossier /vagrant sur la VM (comme mon dossier Samples) est automatiquement partagé avec votre machine hôte, ce qui facilite l’édition de fichiers avec votre éditeur préféré. Pour plus de commodité, vous pouvez également utiliser un Makefile qui peut compiler génériquement le code Nim dans WASM. J’en ai inclus un ici pour vous rendre les choses encore plus faciles.

Vous devriez maintenant être capable de faire un programme Nim de base et de le compiler dans WASM. Voici un banal programme « hello world » que vous pouvez essayer :

echo "Hello world from Nim!"

Il est inclus dans mes exemples comme hello.nim (voir dossier Samples). En utilisant le Makefile que j’ai fourni, compiler ce fichier pour l’exécuter comme un script devrait être aussi simple que :

> make hello

Et exécuter le programme qui en résulte aussi facile que :

> ./hello

Mais bien sûr, nous n’essayons pas vraiment de faire un programme de console ici, nous voulons WebAssembly ! Avec l’installation en place, obtenir une sortie WASM n’est pas beaucoup plus difficile :

> make hello.html

Ce qui donne le fichier à ouvrir dans votre navigateur.

Emscripten-Generated Code / Hello World!

Si votre système supporte mDNS/Avahi/Bonjour, vous pouvez faire référence à la VM via l'adresse bodhi64.local.

Vous voyez comme c’est facile ? Nim est un langage expressif, une seule ligne suffit pour réaliser ce vieux classique de la programmation. Avec le Makefile en place, le build ne prend qu’une seule ligne aussi. Il n’est pas nécessaire de faire d’abord le build de la console, bien que cela aide parfois avec le débogage.

Voici une variation supplémentaire sur le thème « hello world ». Celle-ci utilise WASM pour appeler JavaScript afin de modifier le DOM. Notez qu’à l’heure actuelle, la seule façon d’accéder au DOM dans WASM est via des appels JavaScript ; vous pouvez donc extrapoler cette technique pour toute manipulation de DOM que vous souhaiteriez effectuer.

proc emscripten_run_script(scriptstr: cstring) {.header: "<emscripten.h>",
importc: "emscripten_run_script".}

emscripten_run_script(
    "document.getElementById('output').value='Hello world from Nim via JavaScript!';"
)

Cet exemple est un peu plus compliqué que le précédent. Il y a deux points clés à noter : le premier est que emscripten_run_script() est un moyen courant fourni par Emscripten pour exécuter du code JavaScript dans WASM, et le second est que la ligne proc initiale est ce qui indique au compilateur Nim où obtenir sa définition et comment l’appeler. Sa construction est similaire à celle d’avant ; en utilisant mon exemple de fichier Nim hellojs.nim, la ligne de commande est :

> make hellojs.html

Et la compilation est faite.

Emscripten-Generated Code / Hello World!

Bien que cet exemple n’utilise que du JavaScript tout simple, rien ne vous empêche d’utiliser React, MooTools, jQuery ou autre. L’exemple suivant utilise le Dojo Toolkit. Il sera nécessaire de télécharger le fichier HTML brut Emscripten, de le modifier et de l’utiliser comme modèle. Pour ce faire, les lignes suivantes sont exécutées dans la VM :

> curl https://raw.githubusercontent.com/kripken/emscripten/master/src/shell_minimal.html > /tmp/template.html
> sed 's/<script/<script src="https:\/\/ajax.googleapis.com\/ajax\/libs\/dojo\/1.13.0\/dojo\/dojo.js"><\/script>\n    <script/' /tmp/template.html > template.html

Chaque ligne doit être saisie sous la forme d’une seule commande, quelle que soit la façon dont c’est affiché ci-dessus. Aussi, alors que la commande sed est utilisée ici pour faciliter le copier-coller depuis ce billet de blogue, en pratique, vous ferez probablement ce genre d’édition avec un éditeur interactif. Vous pouvez éditer ce fichier modèle comme vous le souhaitez.

Vous devez également ajouter l’entrée spéciale suivante à votre Makefile pour forcer l’inclusion de votre nouveau modèle afin de construire le nouveau fichier ; ajoutez simplement ces deux lignes en bas, et assurez-vous d’utiliser une tabulation au début de la deuxième ligne :

hellodojo.html: hellodojo.nim
        $(NIM) c -d:emscripten -l:"--shell-file template.html" -o:$@ $(NIMFLAGS) $<

Enfin, vous aurez besoin du build du fichier hellodojo.nim. Ce qui suit fera l’affaire :

proc emscripten_run_script(scriptstr: cstring) {.header: "<emscripten.h>",
importc: "emscripten_run_script".}

emscripten_run_script(
    "require(['dojo/dom'],function(dom){dom.byId('output').value='Hello world from Nim via JavaScript with Dojo!';});"
)

Évidemment, cela peut devenir rapidement lourd à manipuler. La meilleure pratique est de garder le Nim et le JavaScript dans des fichiers sources séparés autant que possible. Pour vous épargner le copier-coller, j’ai fait tout le travail pour vous et j’ai créé un Makefile spécial appelé specific.mk avec les paramétrages appropriés ainsi qu’un fichier source Nim appelé hellodojo.nim. Tout ce que vous avez à faire est d’exécuter la commande :

> make -f specific.mk hellodojo.html

Et cela fera tout le rapatriement et l’édition des modèles ainsi que la compilation.

Maintenant que vous avez les bases, vous devriez être capable de jouer avec Nim et WASM. Il y a beaucoup plus de choses sur le sujet que ce que j’ai présenté ici. S’il y a assez d’intérêt, il se peut que j’y revienne dans de futurs billets. En attendant, si vous souhaitez en savoir plus sur Nim, je vous renvoie à l’excellente documentation en ligne de Nim. De même, si vous souhaitez en savoir plus sur WASM, je vous dirige vers les ressources WebAssembly du Mozilla Developer Network.

[Texte traduit de l’anglais.]