Pages

dimanche 19 avril 2015

Construire ses propres plugins Gradle, prenez le contrôle du build

Je vais aujourd'hui revenir sur la session "Plugin Gradle, prenez le contrôle du build !" présentée à DevoxxFr par Eyal Lezmy développeur chez Genymotion et Cédric Champeau développeur Groovy et Gradle. Cette même session a également été jouée à Mix-IT par Eyal.

Les slides sont disponibles en ligne. 


Gradle devient au fil du temps une alternative et un concurrent très sérieux à Maven. Je pense que son utilisation a surtout été boostée depuis que Google préconise Gradle pour construire un projet Android. Ce choix n'a pas été fait à la légère car la communauté était principalement parti vers Maven. Mais la souplesse de Gradle l'a remporté. En tant qu'utilisateur des deux sytèmes de build je préfère de plus en plus Gradle.

Si vous êtes utilisateur de Maven et que vous voulez passez à Gradle, vous devez savoir que comme pour chaque framework, solution, langage, vous aurez un coût d'entrée car Gradle n'est pas Maven. J'ai beaucoup aimé la définition de Cédric Champeau (néo Gradle depuis l'arrêt du soutien de Groovy par Pivotal) : "Gradle est plus qu'un système de build, c'est une API riche qui vous donne la possibilité d'intervenir à chaque étape du cycle de vie de vos applications".

Bien sûr Gradle apporte toute la mécanique pour gérer ce cycle de vie, les dépendances (librairies de votre projet ou de votre build), l'extension via des plugins...

Gradle offre tout un ensemble de plugins. Si vous voulez démarrer consultez le guide de l'utilisateur qui est très riche.

Construire un plugin Gradle
Revenons au contenu de la présentation. Cédric et Eyal ont décidé de parler du système de plugin de Gradle et de montrer qu'il était assez simple de créer ses propres plugins. Quand on veut paramétrer Gradle nous pouvons le faire de 3 manières
  • mode déclaratif dans le build.gradle 
  • des scripts Groovy dans le fichier build.gradle 
  • des plugins extérieurs (binary plugins) que l'on déclare dans le build.gradle 
Ce fichier build.gradle est le point central et nous pouvons déclarer des tâches à l'intérieur. La tâche est l'élément de base. Les tâches peuvent s’enchaîner dépendre les unes des autres. L'API Gradle permet de surcharger le comportement par défaut de ces tâches ou du système complet.

Si on prend un exemple simple dans lequel nous voulons à la fin des tests copier les rapports dans un répertoire contenant la date d'exécution dans son nom. Avec Gradle on pourrait écrire

task archiveTests (type: Copy){
    from project/testReportDir
    into "${project.buildDir}/reports-${System.currentTimeMillis()}"
}
tasks.test.finalizedBy archiveTests

Si nous faisons beaucoup de personnalisation nous allons avoir tendance à surcharger le fichier build.gradle et aller contre la philosophie des systèmes de build (convention over configuration).

Les plugins Gradle permettent
  • de mutualiser vos parties de script et créer votre propre convention
  • garder un système de build propre et simple
  • rester dans un mode déclaratif
  • de partager votre plugin dans votre entreprise ou à la communauté

Pour écrire votre plugin vous pouvez soit le faire en Java soit le faire en Groovy. Pour commencer vous pouvez tester votre plugin en écrivant directement le code dans votre fichier build.gradle.

L'étape d'après consiste à l'isoler dans un projet à part. Ce projet est bien évidemment gérer via Gradle.Nous utilisons le groovy local car la version de Groovy est spécifique à une version de Gradle.

task wrapper(type: Wrapper) {
    gradleVersion = '2.0'
}

apply plugin: 'groovy'
apply plugin: 'idea'
apply plugin: 'maven-publish'

dependencies {
    compile gradleApi()
    compile localGroovy()
}

group = 'com.javamind'
version = '0.1'

publishing {
    publications {
        mavenJava(MavenPublication) {
            artifactId 'myplugintest'
            from components.java
        }
    }
}

Si notre plugin s'appelle MyPluginTestPlugin nous créons l'arborescence suivante


Le fichier myplugintest.properties permet de définir un id de plugin et de faire le lien avec la classe d'implémentation de notre plugin. Par exemple ici

implementation-class=com.javamind.MyPluginTestPlugin

Regardons à quoi ressemble la classe d'implémentation du plugin. Un plugin peut être implémenté en Java ou en Groovy. Les exemple sont ici en Groovy

class MyPluginTestPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        //Create the extension
        project.extensions.create("myplugintest", new MyPluginTestPluginExtension(project))

        //Create the tasks
        Task myplugintest = project.task(name:"myplugintest", type:MyPluginTask){}

        //Link the tasks to the build chain
        project.afterEvaluate {
            Task task = project.tasks.getByName(project.myplugintest.task)
            task.finalizedBy myplugintest
        }
    }
}

Nous aurions pu utiliser une tâche Gradle mais nous avons choisi de créer notre propre tâche MyPluginTask

class MyPluginTask extends Copy {

    MyPluginTask(){
        from project.myplugintest.from
        into project.myplugintest.into
    }

    @TaskAction
    def exec() {
        println "Reports copied into ${project.myplugintest.into}"
    }
}


Maintenant nous pourrons utiliser notre plugin dans notre build via la syntaxe suivante

myplugintest {
    from "origin/folder"
    into "destination/folder"
    task "taskToLaunchCopy"
}

Le troisième script Groovy suffixé par Extension n'est pas nécessaire mais il permet de définir un dsl pour utiliser votre plugin et permet d'avoir un bloc de configuration externalisé avec des valeurs par défaut

class MyPluginTestPluginExtension {

    def from
    def into
    def task

    MyPluginTestPluginExtension(Project project) {
        from = project.reportsDir
        into = "${project.testReportDir}-${System.currentTimeMillis()}"
        task = "test"
    }
}

Si nous respectons notre convention seule la déclaration du plugin suffira.

buildscript {
    repositories {
        mavenLocal()
    }
    dependencies {
        classpath 'com.javamind:myplugintest:0.1'
    }
}
apply plugin: 'myplugintest'

Nous pouvons maintenant publier notre plugin dans notre repo Maven
gradle publishToMavenLocal

Si vous voulez surcharger les paramètres vous pouvez. Par exemple

myplugintest {
    from "origin/folder"
    into "destination/folder"
    task "taskToLaunchCopy"
}

Je vais m'arrêter ici dans mon article mais je vous conseille vivement de parcourir les slides de la session qui sont très bien fait. Cédric et Eyal ont également abordé l'arbre d'exécution des tâches et son mécanisme. Je n'en ai pas parlé ici mais il est important de tester unitairement votre plugin. Toutes ces astuces sont à retrouver ici et le code présenté ici est sur Github.

Aucun commentaire:

Enregistrer un commentaire

Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.