Compilation croisée de Scala dans un projet Gradle

Il est assez courant que les projets Scala fournissent des artefacts binaires compilés pour plusieurs versions du compilateur Scala. En règle générale, dans le but de créer plusieurs versions d'un artefact dans une communauté, il est habituel d'utiliser SBT, où cette fonctionnalité est prête à l'emploi et est configurée en quelques lignes. Mais que se passe-t-il si nous voulons être confus et créer un build pour la compilation croisée sans utiliser SBT?


Pour l'un de mes projets Java, j'ai décidé de créer une façade Scala. Historiquement, l'ensemble du projet est assemblé à l'aide de Gradle, et il a été décidé d'ajouter la façade au même projet qu'un sous-module. Gradle dans son ensemble peut compiler des modules Scala avec la mise en garde qu'aucun support de compilation croisée n'a été déclaré. Il y a un ticket ouvert en 2017 et quelques plugins ( 1 , 2 ) qui promettent d'ajouter cette fonctionnalité à votre projet, mais ils posent des problèmes, généralement associés à la publication d'artefacts. Et il n'y a rien de plus en général. J'ai décidé de vérifier à quel point il est difficile de configurer la build pour la compilation croisée sans plugins spéciaux ni SMS.


Tout d'abord, nous décrivons le résultat souhaité. Je voudrais que le même ensemble de sources soit compilé par trois versions du compilateur Scala: 2.11, 2.12 et 2.13 (en ce moment le plus récent 2.13.0-RC2). Et puisque Scala 2.13 a un tas de changements incompatibles en amont dans les collections, je voudrais pouvoir ajouter des ensembles de sources supplémentaires pour le code spécifique à chacun des compilateurs. Encore une fois, dans SBT, tout cela est ajouté à quelques lignes de configuration. Voyons ce qui peut être fait à Gradle.


Structure du projet


La première difficulté à laquelle vous devez faire face est que la version du compilateur est calculée à partir de la version de la dépendance déclarée sur la scala-library. De plus, toutes les dépendances qui ont un préfixe pour la version Scala du compilateur doivent également être modifiées. C'est-à-dire Pour chaque version du compilateur, la liste des dépendances doit être différente. De plus, l'ensemble d'indicateurs pour les différentes versions du compilateur est en fait différent. Certains indicateurs ont été renommés entre les versions, tandis que d'autres ont simplement été marqués comme obsolètes ou supprimés. J'ai décidé qu'essayer de saisir toutes les nuances des différents compilateurs dans un seul fichier de construction semble être une tâche trop difficile et son support encore plus difficile. Par conséquent, j'ai décidé d'explorer d'autres façons possibles de résoudre ce problème. Mais que se passe-t-il si nous créons plusieurs versions de configurations pour la même structure de répertoires de projet?


Dans la déclaration d'inclusion des sous-modules dans le projet Gradle, vous pouvez spécifier le répertoire où se trouvera la racine du sous-module et le nom du fichier responsable de sa configuration. Spécifions le même répertoire pour plusieurs importations et créons plusieurs copies du script de construction pour chaque version du compilateur.


settings.gradle
rootProject.name = 'test' include 'java-library' include 'scala-facade_2.11' project(':scala-facade_2.11').with { projectDir = file('scala-facade') buildFileName = 'build-2.11.gradle' } include 'scala-facade_2.12' project(':scala-facade_2.12').with { projectDir = file('scala-facade') buildFileName = 'build-2.12.gradle' } include 'scala-facade_2.13' project(':scala-facade_2.13').with { projectDir = file('scala-facade') buildFileName = 'build-2.13.gradle' } 

Pas mal, mais de temps en temps, nous pouvons obtenir d'étranges erreurs de compilation liées au fait que les trois scripts de construction utilisent le même répertoire de construction. Nous pouvons résoudre ce problème en les définissant pour chaque build:


build-2.12.gradle
 plugins { id 'scala' } buildDir = 'build-2.12' clean { delete 'build-2.12' } // ... 

Maintenant c'est très beau. Avec un seul problème, qu'une telle construction rendra fou votre IDE préféré, et très probablement la poursuite de l'édition de votre projet devra être effectuée à l'aide d'instruments. Je pensais que ce n'était pas un gros problème, car vous pouvez toujours simplement commenter les importations excessives de sous-modules et transformer la construction croisée en une construction régulière, avec laquelle votre IDE sait très probablement comment travailler.


Qu'en est-il des ensembles de sources supplémentaires? Encore une fois, avec des fichiers séparés, cela s'est avéré assez simple, créez un nouveau répertoire et configurez-le comme un ensemble source.


build-2.12.gradle
 // ... sourceSets { compat { scala { srcDir 'src/main/scala-2.12-' } } main { scala { compileClasspath += compat.output } } test { scala { compileClasspath += compat.output runtimeClasspath += compat.output } } } // ... 

build-2.13.gradle
 // ... sourceSets { compat { scala { srcDir 'src/main/scala-2.13+' } } main { scala { compileClasspath += compat.output } } test { scala { compileClasspath += compat.output runtimeClasspath += compat.output } } } // ... 

La structure finale du projet ressemble à ceci:


Projet final


Ici, vous pouvez également séparer les éléments communs individuels dans des fichiers de configuration externes et les importer dans la génération afin de réduire le nombre de répétitions. Mais pour moi, cela s'est avéré assez bien, déclarativement, isolé et compatible avec tous les plugins Gradle possibles.


Au total, le problème a été résolu, la flexibilité de Gradle était suffisante pour exprimer avec élégance une configuration assez non triviale, et la construction croisée Scala est possible non seulement en utilisant SBT, et si pour une raison ou une autre vous utilisez Gradle pour construire un projet Scala, la compilation croisée est une opportunité pour vous également disponible. J'espère que quelqu'un ce poste sera utile. Merci de votre attention.

Source: https://habr.com/ru/post/fr452552/


All Articles