Paquetage à Gradle

Dans mon article, je veux parler d'une autre astuce qui peut être mise en œuvre assez facilement à l'aide de packages de bibliothèque Gradle - reconditionnement. Tous ceux qui ont même un peu travaillé avec ce système de build savent qu'il sait automatiquement comment résoudre les conflits de différentes versions de bibliothèques, et si vous le souhaitez, vous pouvez influencer cela, par exemple, sur-fixer une version spécifique d'une bibliothèque:


configurations.all { resolutionStrategy { force "org.ow2.asm:asm:7.2" } } 

Malheureusement, cela ne permet pas toujours de résoudre le problème de conflit de version. Par exemple, il existe un problème connu selon lequel certains périphériques htc dans le micrologiciel ont déjà une bibliothèque gson, et si votre version de gson diffère de la version intégrée, des problèmes peuvent survenir, car ClassLoader ne chargera qu'une seule classe en mémoire, et dans ce cas, ce sera une système.


Ce problème peut également se produire lors du développement de bibliothèques. Si vous connectez 2 bibliothèques à votre projet qui utilisent la même bibliothèque tierce de versions différentes, par exemple 1 et 2, Gradle résoudra et prendra la dernière version, la seconde. Mais s'il n'y a pas de compatibilité descendante dans cette bibliothèque tierce et que la deuxième version ne peut pas être simplement utilisée à la place de la première, alors il y aura des problèmes qui seront sûrement très difficiles à suivre par trace. Une bibliothèque en attente de la première version recevra les deuxièmes classes et plantera juste.


J'ai rencontré un conflit de version lors de l'écriture d'un plugin grad , il utilise la bibliothèque asm , qui était en conflit. Après avoir écrit le plugin, j'ai vérifié ses performances sur un projet de test: tout va bien, vérifié sur un projet pour animaux de compagnie, tout va bien aussi, mais quand je l'ai connecté à un vrai projet de travail avec un tas de dépendances tierces, j'ai rencontré un problème.



La solution au problème sous la coupe.


Pourtant, cela a fonctionné, qu'est-ce qui a mal tourné?


Nous obtenons la trace d'erreur complète:



Nous voyons que l'erreur dans le constructeur de la bibliothèque asm ClassVisitor est sur la ligne 79. Regardons là-bas, mais en essayant d'ouvrir ClassVisitor , le studio offrait 2 options



Mon plugin utilise asm version 7.2 , donc nous y allons et sur la ligne 79, nous voyons ce qui suit:



Ce n'est clairement pas ce dont nous avons besoin. Passez maintenant à ClassVisitor version 6:



Juste notre IllegalArgumentException sans message. Mon plugin utilise la version ASM api 7 d' Opcodes.ASM7 , et dans la version 6 de la bibliothèque, cette api n'existe pas encore, par conséquent, une IllegalArgumentException dans le constructeur vole. Nous pouvons conclure que le plugin reçoit une version incorrecte de la bibliothèque.


Question poubelle, je pensais, et a fait ceci:


 configurations.all { resolutionStrategy { force "org.ow2.asm:asm:7.2" } } 

À mon grand regret, cela n'a eu absolument aucun effet. Je ne pouvais toujours pas comprendre la raison exacte pour laquelle il n'était pas possible de surfixer la version de asm, bien que la commande ./gradlew app:dependencies indique que la version a été remplacée par 7.2. Si quelqu'un a des pensées ou des hypothèses, je serai heureux d'entendre une opinion.


Le problème doit être résolu d'une manière ou d'une autre


Une série de recherches sur Google et d'approfondissement du travail de la grêle a commencé. En conséquence, je suis allé sur le site asm, peut-être qu'ils savent quelque chose à ce sujet. Il s'est avéré qu'ils le savaient vraiment, la réponse à ma question était dans la section FAQ. Ils disent que pour remplacer le paquet asm par un autre, ils offrent même un utilitaire pour cela. Ok, essayons. Il vous suffit de connecter le plug-in et de faire une petite configuration:


 apply plugin: 'org.anarres.jarjar' ... dependencies { implementation fileTree(dir: 'build/jarjar', include: ['*.jar']) implementation jarjar.repackage('asm') { from 'org.ow2.asm:asm:7.2' classRename "org.objectweb.asm.**", "stater.org.objectweb.asm.@1" } } 

build/jarjar dans ce cas, le répertoire dans lequel le fichier jar de la bibliothèque asm avec les packages reconditionnés sera généré, vous devez donc ouvrir l'accès de dépendance à ce répertoire via fileTree . La bibliothèque sera désormais disponible avec import stater.org.objectweb.asm.* Au lieu de org.objectweb.asm.* . Ce plugin a différents paramètres, mais dans mon exemple, il suffit de changer les packages.


Ensuite, parcourez l'ensemble du projet et modifiez l'importation partout de org.objectweb.asm à
stater.org.objectweb.asm . À mon avis, un utilitaire très pratique, beaucoup plus facile que de le faire à la main, en particulier lors de la mise à jour de la bibliothèque, nous passons simplement from 'org.ow2.asm:asm:7.2' à la nouvelle version et le surnom du pot reconditionné avec la nouvelle version sera généré sur machine automatique.


Si vous n'avez qu'un projet (pas une bibliothèque), cela vous suffira pour résoudre les conflits insolubles, comme le gson mentionné au début de l'article. Mais si vous, comme moi, écrivez une bibliothèque, ce n'est pas tout.


Nous avons résolu le problème de reconditionnement, mais maintenant asm connecté au projet non pas via une dépendance du référentiel maven distant, mais via le fichier jar local, qui sera simplement perdu lors du déploiement de votre bibliothèque et il y aura une NoClassDefFoundError erreur NoClassDefFoundError . Mais ce problème est assez simple à résoudre:


  1. Dans notre fichier gradle, créez une nouvelle configuration:


     configurations { extraLibs implementation.extendsFrom(extraLibs) } 

  2. Ensuite, nous changeons


     implementation fileTree(dir: 'build/jarjar', include: ['*.jar']) 

    sur


     extraLibs fileTree(dir: 'build/jarjar', include: ['*.jar']) 

  3. Redéfinissez la tâche qui est responsable de la collecte de votre fichier jar final et écrivez toutes les bibliothèques avec notre nouvelle configuration dans le surnom de jar final:


     jar { from { configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } } } 


C'est tout, déployez notre plugin comme avant, connectez-vous au projet où il y avait des conflits insolubles et tout fonctionne bien.
Un tel reconditionnement rend notre bibliothèque plus tolérante aux pannes lorsqu'elle est connectée à divers types de projets avec d'autres bibliothèques.


Et si vous connectez simplement le fichier jar de la bibliothèque en conflit au plugin sans reconditionner?


Mauvaise idée, cela ne mènera à rien de bon. Dans le processus de construction du projet, il y a une telle check...DuplicateClasses tâche intéressante check...DuplicateClasses , qui coupe simplement les fichiers avec les mêmes packages. Autrement dit, les fichiers obtenus à partir du fichier jar de la bibliothèque connectée et les fichiers de la même bibliothèque connectés via le référentiel distant. Le résultat sera cette erreur:



C’est tout. Merci à tous ceux qui ont lu!


Tulsa pour le reconditionnement
Exemple de plugin

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


All Articles