Recherche de dépendances Gradle inutilisées

1. Vue d'ensemble

Parfois, pendant le développement, nous pourrions finir par ajouter plus de dépendances que nous n'en utilisons.

Dans ce rapide tutoriel, nous allons voir comment utiliser le plugin Gradle Nebula Lint pour identifier et résoudre des problèmes comme ceux-ci.

2. Installation et configuration

Nous utilisons une configuration Gradle 5 multi-module dans nos exemples.

Ce plugin ne fonctionne que pour les fichiers de construction basés sur Groovy .

Configurons-le dans le fichier de construction du projet racine:

plugins { id "nebula.lint" version "16.9.0" } description = "Gradle 5 root project" allprojects { apply plugin :"java" apply plugin :"nebula.lint" gradleLint { rules=['unused-dependency'] } group = "com.baeldung" version = "0.0.1" sourceCompatibility = "1.8" targetCompatibility = "1.8" repositories { jcenter() } }

Nous ne pouvons le configurer de cette façon que pour les builds multi-projets pour le moment . Cela signifie que nous ne pouvons pas l'appliquer séparément dans chaque module.

Ensuite, configurons nos dépendances de module:

description = "Gradle Unused Dependencies example" dependencies { implementation('com.google.guava:guava:29.0-jre') testImplementation('junit:junit:4.12') }

Ajoutons maintenant une classe principale simple dans nos sources de module:

public class UnusedDependencies { public static void main(String[] args) { System.out.println("Hello world"); } }

Nous allons construire là-dessus un peu plus tard et voir comment le plugin fonctionne.

3. Scénarios et rapports de détection

Le plugin recherche les fichiers jars de sortie pour détecter si une dépendance est utilisée ou non.

Cependant, en fonction de plusieurs conditions, cela peut nous donner des résultats différents .

Nous explorerons les cas les plus intéressants dans les sections suivantes.

3.1. Dépendances inutilisées

Maintenant que nous avons notre configuration, voyons le cas d'utilisation de base. Nous nous intéressons aux dépendances inutilisées.

Nous allons exécuter la lintGradle tâche:

$ ./gradlew lintGradle > Task :lintGradle FAILED # failure output omitted warning unused-dependency this dependency is unused and can be removed unused-dependencies/build.gradle:6 implementation('com.google.guava:guava:29.0-jre') ✖ 1 problem (0 errors, 1 warning) To apply fixes automatically, run fixGradleLint, review, and commit the changes. # some more failure output 

Voyons ce qui se passe. Nous avons une dépendance inutilisée ( goyave ) dans notre configuration compileClasspath .

Si nous courons fixGradleLint tâche comme le plug - in indique, la dépendance est automatiquement supprimée de notre build.gradle .

Cependant, utilisons plutôt une logique factice avec notre dépendance:

public static void main(String[] args) { System.out.println("Hello world"); useGuava(); } private static void useGuava() { List list = ImmutableList.of("Baledung", "is", "cool"); System.out.println(list.stream().collect(Collectors.joining(" "))); }

Si nous le réexécutons, nous n'obtenons plus d'erreurs:

$ ./gradlew lintGradle BUILD SUCCESSFUL in 559ms 3 actionable tasks: 1 executed, 2 up-to-date

3.2. Utilisation des dépendances transitives

Incluons maintenant une autre dépendance:

dependencies { implementation('com.google.guava:guava:29.0-jre') implementation('org.apache.httpcomponents:httpclient:4.5.12') testImplementation('junit:junit:4.12') }

Cette fois, utilisons quelque chose d'une dépendance transitive:

public static void main(String[] args) { System.out.println("Hello world"); useGuava(); useHttpCore(); } // other methods private static void useHttpCore() { SSLContextBuilder.create(); }

Voyons ce qui se passe:

$ ./gradlew lintGradle > Task :lintGradle FAILED # failure output omitted warning unused-dependency one or more classes in org.apache.httpcomponents:httpcore:4.4.13 are required by your code directly (no auto-fix available) warning unused-dependency this dependency is unused and can be removed unused-dependencies/build.gradle:8 implementation('org.apache.httpcomponents:httpclient:4.5.12') ✖ 2 problems (0 errors, 2 warnings)

Nous obtenons deux erreurs. La première erreur indique à peu près que nous devrions référencer directement httpcore .

Le SSLContextBuilder de notre exemple en fait partie.

La deuxième erreur indique que nous n'utilisons rien de httpclient.

Si nous utilisons une dépendance transitive, le plugin nous dit d'en faire une dépendance directe .

Jetons un coup d'œil à notre arbre de dépendances:

$ ./gradlew unused-dependencies:dependencies --configuration compileClasspath > Task :unused-dependencies:dependencies ------------------------------------------------------------ Project :unused-dependencies - Gradle Unused Dependencies example ------------------------------------------------------------ compileClasspath - Compile classpath for source set 'main'. +--- com.google.guava:guava:29.0-jre | +--- com.google.guava:failureaccess:1.0.1 | +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava | +--- com.google.code.findbugs:jsr305:3.0.2 | +--- org.checkerframework:checker-qual:2.11.1 | +--- com.google.errorprone:error_prone_annotations:2.3.4 | \--- com.google.j2objc:j2objc-annotations:1.3 \--- org.apache.httpcomponents:httpclient:4.5.12 +--- org.apache.httpcomponents:httpcore:4.4.13 +--- commons-logging:commons-logging:1.2 \--- commons-codec:commons-codec:1.11

Dans ce cas, nous pouvons voir que httpcore est introduit par httpclient .

3.3. Utilisation des dépendances avec réflexion

Qu'en est-il lorsque nous utilisons la réflexion?

Améliorons un peu notre exemple:

public static void main(String[] args) { System.out.println("Hello world"); useGuava(); useHttpCore(); useHttpClientWithReflection(); } // other methods private static void useHttpClientWithReflection() { try { Class httpBuilder = Class.forName("org.apache.http.impl.client.HttpClientBuilder"); Method create = httpBuilder.getMethod("create", null); create.invoke(httpBuilder, null); } catch (Exception e) { e.printStackTrace(); } }

Maintenant, réexécutons la tâche Gradle:

$ ./gradlew lintGradle > Task :lintGradle FAILED # failure output omitted warning unused-dependency one or more classes in org.apache.httpcomponents:httpcore:4.4.13 are required by your code directly (no auto-fix available) warning unused-dependency this dependency is unused and can be removed unused-dependencies/build.gradle:9 implementation('org.apache.httpcomponents:httpclient:4.5.12') ✖ 2 problems (0 errors, 2 warnings) 

Qu'est-il arrivé? Nous avons utilisé HttpClientBuilder à partir de notre dépendance (httpclient) mais nous avons toujours des erreurs.

Si nous utilisons une bibliothèque avec réflexion, le plugin ne détecte pas son utilisation .

En conséquence, nous pouvons voir les deux mêmes erreurs.

En général, nous devons configurer ces dépendances comme runtimeOnly .

3.4. Générer des rapports

Pour les grands projets, le nombre d'erreurs renvoyées dans un terminal devient difficile à gérer.

Configurons le plugin pour nous donner un rapport à la place:

allprojects { apply plugin :"java" apply plugin :"nebula.lint" gradleLint { rules=['unused-dependency'] reportFormat = 'text' } // other details omitted }

Exécutons la tâche generateGradleLintReport et vérifions notre sortie de construction:

$ ./gradlew generateGradleLintReport # task output omitted $ cat unused-dependencies/build/reports/gradleLint/unused-dependencies.txt CodeNarc Report - Jun 20, 2020, 3:25:28 PM Summary: TotalFiles=1 FilesWithViolations=1 P1=0 P2=3 P3=0 File: /home/user/tutorials/gradle-5/unused-dependencies/build.gradle Violation: Rule=unused-dependency P=2 Line=null Msg=[one or more classes in org.apache.httpcomponents:httpcore:4.4.13 are required by your code directly] Violation: Rule=unused-dependency P=2 Line=9 Msg=[this dependency is unused and can be removed] Src=[implementation('org.apache.httpcomponents:httpclient:4.5.12')] Violation: Rule=unused-dependency P=2 Line=17 Msg=[this dependency is unused and can be removed] Src=[testImplementation('junit:junit:4.12')] [CodeNarc (//www.codenarc.org) v0.25.2] 

Maintenant, il détecte les dépendances inutilisées sur la configuration testCompileClasspath .

Il s'agit, malheureusement, d'un comportement incohérent du plugin. En conséquence, nous obtenons maintenant trois erreurs.

4. Conclusion

Dans ce didacticiel, nous avons vu comment trouver les dépendances inutilisées sur les versions Gradle.

Tout d'abord, nous avons expliqué la configuration générale. Après cela, nous avons exploré les erreurs signalées avec différentes dépendances et leur utilisation.

Enfin, nous avons vu comment générer des rapports textuels.

Comme d'habitude, nous pouvons trouver les exemples de code complets sur GitHub.