Thématiques principales

dimanche 28 janvier 2018

Gradle

Lors de la production de systèmes logiciels, il ne fait aucun doute que la phase la plus importante est la compilation qui nécessite l’utilisation d'outils tels que gcc ou javac. Aujourd’hui pourtant limiter la production d’un logiciel a la compilation n’est plus suffisant. De nombreuses phases périphériques à la compilation sont devenu indispensables, comme l'exécution des tests unitaires ou des tests fonctionnels, la production automatique de documentations ou de code, le packaging multi plateforme, ou même la mise en production. Ceci s'appelle l'intégration continu au sein duquel on va trouver divers outils à différents niveau d’abstraction (Jenkins[1] , artifactory [2]) ou differents roles (Sonar [3], RobotFramework [4]). Nous reviendrons sur ces outils, mais nous en avions déjà vu un nommé Maven dans un précédent article [5] dont le but est justement de couvrir l’ensemble des phases du build.

Aujourd’hui nous allons nous intéresser à ce que l’on pourrait appeler un peu abusivement son concurrent: Gradle [7].

A mon sens Gradle en est plutot le successeur et nous verrons pourquoi. En effet, même si personnellement j’utilise encore beaucoup Maven, les raisons sont surtout historique et que faire la migration de l’un vers l’autre, ne peut être qu’un bien [9] mais cela repose sur une bonne disponibilité en temps.

Voyons ce que propose Gradle par dela Maven. Pour faire simple et rapide sur l'intérêt de Gradle est qu’il permet de dépasser le côté monolithe de Maven. En effet, Maven repose sur un socle stable et très sain d’un process de build. Du moins lorsque ce processus est classique et standard. Gradle, tout en conservant ce socle permet d'être plus souple et dynamique car contrairement a Maven qui repose sur xml pour décrire le contenu du build (donc a une vision configuration du process), Gradle lui repose sur groovy qui est lui un langage de programmation.

En fait si l’on reprend le titre de la publication de Leon Osterweil:  “software processes are software too” [8], nous sommes face à un véritable changement de paradigme dans la façon de gérer la production de logiciel ou celle-ci est également vu comme une partie intégrante du logiciel lui même.

Par exemple, Google a adopté Gradle pour la production de ses applicatifs Android et l’on peut assister que cette adoption a été largement suivi par une très bonne intégration de ce nouvel outils dans les IDE standard de la communauté. Ainsi developper une application android sous Gradle n’est vraiment pas un probleme [11] (nous y reviendrons dans un futur article)

Initialisation

Avant de nous pencher sur android, regardons déjà comment faire le build d’une application java standard avec Gradle.

Il faut commencer par télécharger Gradle [12] et d’ajouter son GRADLE_HOME/bin au PATH du système. Ceci permettra de lancer gradle en ligne de commande :

gradle -v

Votre installation est bonne? Ok nous allons alors pouvoir initialiser notre premier projet Gradle en lancant la commande init:

gradle init

A la console normalement vous devriez obtenir les log suivant

Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details
Support for running Gradle using Java 7 has been deprecated and is scheduled to be removed in Gradle 5.0. Please see https://docs.gradle.org/4.3/userguide/java_plugin.html#sec:java_cross_compilation for more details.
BUILD SUCCESSFUL in 7s
2 actionable tasks: 2 executed

Cette commande va tout d’abord lancer un demon pour accélérer les build suivant du projet et va preinitialiser pour votre système un certain nombre de fichiers, un répertoire wrapper et un répertoire .gradle (invisible sous unix)

ls -a
.  ..  build.gradle  gradle  .gradle  gradlew  gradlew.bat  settings.gradle

Alors commençons par la partie wrapper. Celle ci n’est pas indispensable mais Gradle préconise son utilisation afin de garantir l'homogénéité des builds du projet [13]. Les repertoires et fichiers concernés sont gradle, gradlew et gradlew.bat. En fait le wrapper permet contrairement à Maven,  le rechargement lors du build de la version proposée de gradle qui doit être utilisée pour construire le projet. Ainsi, pour tous les développeurs utilisant le wrapper, le build sera forcément identique (ou du moins reposera sur un environnement identique). Les fichiers gradlew permettent de lancer le wrapper et dans le repertoire nous trouverons un jar (réalisant le maintien de la version) et un fichier de propriétés pour le wrapper (contenant des données d’environnement) [14].

Les fichiers et répertoires participant a proprement parler au build sont build.gradle, settings.gradle et .gradle. Le repertoire .gradle est un repertoire interne a gradle propre au projet permettant à gradle de tagger les fichiers nouvellements modifiés et devant être incorporé au build incremental (c’est lui qui va rendre le processus de build plus efficace et plus rapide également). Normalement il ne devrait pas y avoir de besoin d’aller mettre son nez dedans; par contre, nous arrivons enfin sur les deux fichiers qui nous interesse le plus settings.gradle et build.gradle.

Le fichier settings.gradle est optionnel mais, il est recommandé de systématiquement l’utiliser. En effet celui-ci n’est pas utile pour un projet isolé mais sera indispensable dans le cas des multi-projets (equivalent aux modules maven). De plus il permet de définir un nom aux projets qui par défaut s’il n’existe pas est pris sur la base du nom du repertoire contenant le projet (ce qui n’est pas tiptop…)

settings.gradle

De façon simple voici ce qu’il peut contenir:

/*
// To declare projects as part of a multi-project build use the 'include' method
include 'shared'
include 'api'
include 'services:webservice'
*/
rootProject.name = 'monProjetGradle'

On aura compris que rootProject.name est le nom de notre projet…. A noter la primitive include afin de déclarer des sous projets à construire.

build.gradle

Intéressons nous au fichier de build lui-même: build.gradle. Ce fichier a pour vocation de décrire le contenu du build lui-même en s’appuyant sur la syntaxe Groovy. On imagine alors ce que l’on va y decrire sera considéré avant tout comme du comportement plutôt que de la configuration (a l’inverse de Maven) et c’est effectivement le cas à quelques exception près.

Prenons l’exemple d’un projet Java, il va falloir commencer par indiquer à Gradle que le build s’effectuera en Java. Pour cela, on va lui specifier l’utilisation d’un plugin [15]:

apply plugin: 'java'

Ensuite, à l'instar de maven on pourra donner une description et une version à l’artifact que nous allons créer. A la suite de quoi on va lui donner la version java pour la compilation:

description ="Projet de test pour gradle"
version = '0.1.0-SNAPSHOT'
sourceCompatibility = 1.8

Pour identifier les sources, il est possible de spécifier leur emplacement, ainsi que l’emplacement des tests.

sourceSets {
    main {
         java {
              srcDir 'src/main/java'
              }
         }
    test {
         java {
              srcDir 'src/test/java'
              }
         }
}

Encore une fois comme pour maven, il faut définir quelles vont être les dépendances nécessaires à la compilation et aux tests: Plus simplement que pour maven il suffit de déclarer le bloc dependencies comme suit:

dependencies {
    compile 'org.tc.osgi.bundle.utils:tc-osgi-bundle-utils-interfaces:0.1.0-SNAPSHOT'
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-all:1.9.5'
}

A noter que le nommage des artifacts peut suivre les règles d’ecriture prescrit par Maven et permettre d’utiliser des repository Maven.
C’est d’ailleur ce que l’on va ajouter à notre build, la possibilité d’aller rechercher des artifacts dans un artifactory maven. Ainsi, si vous utilisez un repository maven perso, il faudra le déclarer avec la primitive repositories comme suit:

repositories {
    //jcenter()
    maven {
        url "http://localhost:8081/artifactory/cots"
        credentials {
            username = "toto"
            password = "tata"
        }
   }
}

Voila, et enfin pour les plus aguerri, il est possible de modifier manuellement le Manifest du jar produit. Pour exemple, pour construire des Bundles OSGI (nous regarderons cela dans un autre article sur ce qu’est OSGI) il faudra non seulement rajouter le plugin osgi mais aussi triturer un peu le manifest:

apply plugin: 'osgi'
jar {
    manifest {
    attributes 'Bundle-SymbolicName' : project.name
attributes 'Bundle-Name' : project.name + '-' + version
attributes 'Bundle-Version' : version
attributes 'Bundle-Vendor' : 'TC'
        attributes 'Bundle-Activator': 'org.tc.osgi.bundle.utils.module.activator.UtilsActivator'
        attributes 'Bundle-Description': 'Un bundle pour contenir des services utilitaires'
    }
}

ou plus classiquement pour un jar java standard:

jar {
    manifest {
        attributes 'Main-Class': 'monProjet.Main'
    }
}

Voila, ceci termine la partie statique du build. Maintenant découvrons sa partie dynamique, les taches.

Les taches

Les tâches sont du code groovy permettant d’orchestrer le déroulement du build. Alors bien sur, il ne faudra pas écrire tout le code nécessaire à son exécution. En effet si il existe deux types de tâches, les premières et les plus employées sont celles par défauts déclarer dans les plugins [15] [16].

Ensuite bien sur, il est possible d’en créer soit même avec à terme la définition d’un plugin.. A noter que outre la simplicité évidente du langage, dans le monde maven, il ne serait pas possible d’ajouter du comportement au build sans faire obligatoirement un plugin-maven puisque l’on ne peut ajouter directement du comportement dans le pom (a moins d’utiliser un plugin de type script).

Par exemple voici une petite tache propre a notre build:

// définition d'une tache perso nommé hello
task matache {
    doLast {
        println 'Building ' + project.name
    }
}

Voila il suffira d’appeler la commande

gradle matache

Pour afficher dans la console de build, “Building monProjetGradle”

Voila on a fait le tour de Gradle sans forcement etre rentrer dans le détail mais probablement suffisamment pour pouvoir rapidement mettre en oeuvre un début de build. A mon sens gradle, au vue de sa flexibilité, ne peut que finir par remplacer Maven car il en détient tous les avantages et en complète les défauts (attention par contre a ce que flexibilité ne ressemble pas a chaos). Finalement, son seul tort est peut etre encore son manque de popularité et de visibilité auprès des grands groupes du fait de son “jeune” âge même si la communauté est très active et fournit du contenu riche et très varié.  Pour plus d’informations, n'hésitez pas à consulter [17] [18] qui m’ont été d’une grande aide pour découvrir gradle et écrire cet article.

References:

[1] https://jenkins.io/
[2] https://jfrog.com/artifactory/
[3] https://www.sonarqube.org/
[4] http://robotframework.org/
[5] https://maven.apache.org/
[6] http://un-est-tout-et-tout-est-un.blogspot.com/2017/12/maven-introduction.html
[7] https://gradle.org/releases/
[8] Leon Osterweil:  “software processes are software too”, Proceeding ICSE '87 Proceedings of the 9th international conference on Software Engineering, Pages 2-13
[9] https://dzone.com/articles/gradle-vs-maven?edition=306207&utm_source=Daily%20Digest&utm_medium=email&utm_campaign=dd%202017-07-02
[10] https://devops.com/puzzle-gradle-maven/
[11] http://www.tutos-android.com/maitriser-gradle-partie-1
[12] http://www.gradle.org/downloads
[13] https://docs.gradle.org/current/userguide/gradle_wrapper.html
[14] https://docs.gradle.org/2.4/userguide/build_environment.html#sec:gradle_configuration_properties
[15] https://docs.gradle.org/2.4/userguide/standard_plugins.html
[16] https://plugins.gradle.org/
[17] https://www.petrikainulainen.net/getting-started-with-gradle/
[18] http://www.vogella.com/tutorials/Gradle/article.html

Aucun commentaire:

Enregistrer un commentaire