Scala et sbt, bientôt dans Guix ?

par Julien Lepiller — lun. 21 janvier 2019

Où l'on parle de yacks

L'objectif qui m'a fait m'intéresser aux paquets Java dans Guix, c'est d'être capable de développer et de compiler des applications android. Drôle d'idée… Le souci, c'est qu'un des outils les plus utilisés pour la compilation, c'est gradle, et ça commence mal, gradle est construit à partir de gradle…

Bon, gradle dépend aussi, entre autres, de Maven, un autre outil de construction populaire pour les paquets Java. Là aussi ça commence mal, Maven est construit avec Maven. Heureusement, depuis l'année dernière, j'ai réussi à reprendre le travail d'un accolyte et le terminer : nous avons désormais Maven dans Guix ! Tout cela, après pas mal de problèmes de type dépendances circulaires, explosion du nombre de dépendances à rajouter dans la distribution, etc…

Là où ça devient drôle, c'est que gradle a aussi besoin de Scala et Kotlin, deux langages pour la JVM, avec un compilateur écrit dans leur propre langage. Guix s'efforce de bootstrapper ses compilateurs, donc ceux-là ne font pas exception. Je me suis intéressé d'abord à Scala avant Kotlin, et c'est ce dont on va parler ici.

Un yack
crédits : Dennis Jarvis

Un compilateur Scala

Scala est donc écrit en Scala. Il nous faut donc une version précédente du compilateur pour compiler le compilateur récent. Et en remontant ainsi dans l'histoire de Scala, on devrait arriver à un compilateur qui n'était pas en Scala et qu'on pourra utiliser pour construire toute la chaîne jusqu'à la version actuelle. Non ? Non.

Le souci, c'est que d'une part, cette chaîne risque de s'avérer très, très, très longue, mais en plus la première version de Scala était écrite pour Pizza, un vieux compilateur pour un langage plus ou moins dérivé de Java, et il est fort probable que cette ancienne version 1.x soit incapable de compiler une version 2.x. Je me suis donc lancé dans la création d'un nouveau compilateur pour scala, écrit en Java qui devra être capable de compiler une version récente de Scala.

J'en suis encore loin. Pour le moment, je suis capable de parser la plupart des fichiers du compilateur et de créer un AST à peu près potable. Le souci, c'est que d'une part, la documentation étant très inexacte au sujet de la grammaire (elle ne parle pas des valeurs xml, fait des raccourcis dans la définition des opérateurs, et omet une bonne partie de ce qui fait que cette grammaire est compliquée) ma grammaire se retrouve bourrée d'ambigüités, et d'autres part, je me retrouve incapable de parser tous les fichiers du compilateur en même temps, faute de mémoire. Mon générateur de parser, ANTLR, garde en mémoire tous les choix qu'il fait, et ça peut vite grossir.

Alors, entre deux débogages de grammaire, je me suis lancé dans la compilation de sbt (Scala Build Tool) à partir de la version binaire de Scala.

En définitive, qu'est-ce qu'on voulait déjà ? Ah oui, android. À ce niveau-là, comme me l'a dit quelqu'un récemment, ce n'est plus un yack que j'essaye de tondre, mais un troupeau entier :D

La compilation de SBT

Donc, en partant du binaire de Scala, comment compiler sbt ? D'après les instructions, il suffit de lancer sbt. Mais justement, je n'ai pas encore sbt. J'ai depuis longtemps abandonné l'idée de lancer les instructions officielles, que ce soit pour maven, gradle, sbt ou quelque autre programme java que ce soit. Tous mes paquets sont construits à partir d'une phase qui fait tout « à la main ». Elle lance directement les commandes scalac nécessaires. Après avoir passé pas mal de temps à souffrir pour trouver la dizaine de fragments de sbt éparpillés dans l'espace de nom associé sur github, et les quelques dizaines de dépendances, en scala et en java, de ces composants, j'ai fini par réussir à compiler sbt.

Ce graphe ne tient pas compte des nombreux paquets java que j'ai du ajouter, en plus des paquets scala spécifiques : il est tronqué pour ne garder que les chemins entre scala et sbt. Sans ça, il serait très vite illisible ;)

Au fait, pour générer ce graphe, il suffit de lancer quelques commandes depuis guix :

# on s'assure d'avoir les outils
guix environment --ad-hoc imagemagick graphviz
guix graph sbt > sbt.dot
# le script est disponible ici.
# 78145984 est le numéro associé au nœud scala-official
gvpr -f markpath.g -a ex78145984 < sbt.dot > sbt-scala.dot
convert -Tpng sbt-scala.dot > sbt-graph.png

J'ai encore passé un peu de temps avant d'être capable de lancer sbt, car la partie chargée de faire cela, sbt-launcher.jar, a en fait son code source éparpillé entre deux dépôts distincts. Une fois le problème corrigé, je l'ai lancé et là magie :

$ sbt new lagom/lagom-java.g8
Getting org.scala-sbt sbt 1.2.8  (this may take some time)...
downloading https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.2.8/sbt-1.2.8.jar ...
        [SUCCESSFUL ] org.scala-sbt#sbt;1.2.8!sbt.jar (21ms)
downloading https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.7/scala-library-2.12.7.jar ...
        [SUCCESSFUL ] org.scala-lang#scala-library;2.12.7!scala-library.jar (418ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/main_2.12/1.2.8/main_2.12-1.2.8.jar ...
        [SUCCESSFUL ] org.scala-sbt#main_2.12;1.2.8!main_2.12.jar (158ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/logic_2.12/1.2.8/logic_2.12-1.2.8.jar ...
        [SUCCESSFUL ] org.scala-sbt#logic_2.12;1.2.8!logic_2.12.jar (17ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/actions_2.12/1.2.8/actions_2.12-1.2.8.jar ...
        [SUCCESSFUL ] org.scala-sbt#actions_2.12;1.2.8!actions_2.12.jar (28ms)

[... je coupe volontairement plusieurs écrans ...]

[info] Set current project to project (in build file:/home/tyreunom/packages/project/)
[info] Set current project to project (in build file:/home/tyreunom/packages/project/)
name [Hello]:
organization [com.example]: foo.bar
version [1.0-SNAPSHOT]:
package [foo.bar.hello]:

Template applied in /home/tyreunom/packages/project/./hello

Comme on peut le voir, sbt s'est lancé, mais la première chose qu'il fait, c'est de télécharger une version binaire de lui-même. Tant pis, au moins le launcher fonctionne ^^. Ensuite, évidemment tout se passe bien, puisqu'il ignore complètement ma propre version de sbt. Il va même jusqu'à télécharger la version précédente de scala !

En attendant que j'envoie tout ça en amont dans Guix, vous pouvez profiter de ce paquet (et bien d'autres encore !) en installant mon dépôt de recettes supplémentaires.

Travail restant

Cette petite expérience m'a permis de comprendre un peu mieux le fonctionnement de sbt. Je crois qu'il faudrait ajouter les dépendances dans le bon sous-dossier de lib/local-preloaded du paquet sbt. Je devrais aussi pouvoir trouver un moyen d'émuler le résultat de ivy, pour que sbt puisse trouver les dépendances des paquets qu'il compile, quand Guix aura un sbt-build-system.

Il me reste aussi à régénérer du code scala créé par un outil nommé contraband. Il n'est pas encore présent dans le graphe, justement parce que je ne l'ai pas encore construit. Enfin, je vais pouvoir me reconcentrer sur mon compilateur scala. Un jour. Ensuite, ce sera au tour de Kotlin.