Umpacken von Paketen in Gradle

In meinem Artikel möchte ich über einen weiteren Trick sprechen, der mit Gradle ganz einfach implementiert werden kann - das Umpacken von Bibliothekspaketen. Jeder, der sogar ein wenig mit diesem Build-System gearbeitet hat, weiß, dass es automatisch weiß, wie Konflikte verschiedener Versionen von Bibliotheken gelöst werden können. Wenn Sie dies wünschen, können Sie dies beeinflussen, indem Sie beispielsweise eine bestimmte Version einer Bibliothek überfixieren:


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

Leider hilft dies nicht immer, das Problem des Versionskonflikts zu lösen. Beispielsweise ist ein Problem bekannt, dass einige HTC-Geräte in der Firmware bereits über eine Gson-Bibliothek verfügen. Wenn sich Ihre Version von Gson von der integrierten unterscheidet, können Probleme auftreten, da ClassLoader nur eine Klasse in den Speicher lädt. In diesem Fall handelt es sich um eine Systemklasse.


Dieses Problem kann auch bei der Entwicklung von Bibliotheken auftreten. Wenn Sie zwei Bibliotheken mit Ihrem Projekt verbinden, die dieselbe Drittanbieter-Bibliothek mit verschiedenen Versionen verwenden, z. B. 1 und 2, wird Gradle die neueste Version, die zweite, auflösen und verwenden. Wenn diese Bibliothek von Drittanbietern jedoch keine Abwärtskompatibilität aufweist und die zweite Version nicht einfach anstelle der ersten verwendet werden kann, gibt es Probleme, die mit der Ablaufverfolgung sicherlich nur sehr schwer zu verfolgen sind. Eine Bibliothek, die auf die erste Version wartet, erhält die zweiten Klassen und stürzt einfach ab.


Beim Schreiben eines Grad-Plugins ist ein Versionskonflikt aufgetreten. Es verwendet die asm- Bibliothek, die in Konflikt geraten ist. Nachdem ich das Plugin geschrieben hatte, überprüfte ich seine Leistung bei einem Testprojekt: Alles ist in Ordnung, ich überprüfte bei einem Haustierprojekt, alles ist auch in Ordnung, aber als ich es mit einer Reihe von Abhängigkeiten von Drittanbietern mit einem echten Arbeitsprojekt verband, stieß ich auf ein Problem.



Die Lösung des Problems unter dem Schnitt.


Aber es hat funktioniert, was ist schief gelaufen?


Wir erhalten die vollständige Fehlersuche:



Wir sehen, dass der Fehler im Konstruktor der asm ClassVisitor Bibliothek in Zeile 79 liegt. Schauen wir uns das an, aber beim Versuch, ClassVisitor zu öffnen, bot das Studio zwei Optionen



Mein Plugin verwendet asm Version 7.2 , also gehen wir dorthin und in Zeile 79 sehen wir Folgendes:



Dies ist eindeutig nicht das, was wir brauchen. Gehen ClassVisitor jetzt zu ClassVisitor Version 6:



Nur unsere IllegalArgumentException ohne Nachricht. Mein Plugin verwendet die ASM-API-7-Version von Opcodes.ASM7 , und in der 6-Version der Bibliothek ist diese API noch nicht vorhanden, daher wird eine IllegalArgumentException im Konstruktor ausgeführt. Wir können daraus schließen, dass das Plugin eine falsche Version der Bibliothek erhält.


Müllfrage, dachte ich und tat dies:


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

Zu meinem Bedauern hatte dies absolut keine Wirkung. Ich konnte immer noch nicht herausfinden, warum es nicht möglich ist, die asm-Version zu überfixieren, obwohl der ./gradlew app:dependencies anzeigt, dass die Version durch 7.2 ersetzt wurde. Wenn jemand Gedanken oder Annahmen hat, würde ich mich freuen, eine Meinung zu hören.


Das Problem muss irgendwie gelöst werden


Eine Reihe von googeln und vertiefen in die Arbeit des Hagels begann. Infolgedessen bin ich auf die asm-Website gegangen, vielleicht wissen sie etwas darüber. Es stellte sich heraus, dass sie wirklich wissen, dass die Antwort auf meine Frage im FAQ-Bereich war. Sie sagen, um das asm-Paket durch ein anderes zu ersetzen, bieten sie sogar ein Dienstprogramm dafür an. Ok, lass es uns versuchen. Sie müssen nur das Plug-In anschließen und ein kleines Setup vornehmen:


 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 In diesem Fall das Verzeichnis, in das die JAR-Datei der ASM-Bibliothek mit neu gepackten Paketen generiert wird. Sie müssen daher den Abhängigkeitszugriff auf dieses Verzeichnis über fileTree . Die Bibliothek ist jetzt mit dem Import stater.org.objectweb.asm.* verfügbar stater.org.objectweb.asm.* Anstelle von org.objectweb.asm.* . Dieses Plugin hat verschiedene Einstellungen, aber in meinem Beispiel hat es gereicht, nur die Pakete zu ändern.


Gehen Sie als Nächstes das gesamte Projekt durch und ändern Sie den Import überall von org.objectweb.asm in
stater.org.objectweb.asm . Meiner Meinung nach, ein sehr praktisches Dienstprogramm, das um ein Vielfaches einfacher ist als from 'org.ow2.asm:asm:7.2' , insbesondere beim Aktualisieren der Bibliothek, wechseln wir einfach from 'org.ow2.asm:asm:7.2' zur neuen Version, und der neu from 'org.ow2.asm:asm:7.2' jar-Spitzname mit der neuen Version wird am generiert automatische Maschine.


Wenn Sie nur ein Projekt haben (keine Bibliothek), reicht dies aus, um unlösbare Konflikte zu lösen, wie sie am Anfang des Artikels erwähnt wurden. Aber wenn Sie wie ich eine Bibliothek schreiben, ist das noch nicht alles.


Wir haben das Problem beim erneuten Packen gelöst, aber jetzt ist asm nicht über die Abhängigkeit vom Remote-Maven-Repository mit dem Projekt verbunden, sondern über die lokale JAR-Datei, die einfach verloren geht, wenn Ihre Bibliothek bereitgestellt wird, und es tritt ein solcher NoClassDefFoundError Fehler auf. Dieses Problem ist jedoch recht einfach zu lösen:


  1. Erstellen Sie in unserer Gradle-Datei eine neue Konfiguration:


     configurations { extraLibs implementation.extendsFrom(extraLibs) } 

  2. Als nächstes ändern wir uns


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

    auf


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

  3. Definieren Sie die Aufgabe, die für das Sammeln Ihrer endgültigen JAR-Datei verantwortlich ist, neu und schreiben Sie alle Bibliotheken mit unserer neuen Konfiguration in den endgültigen JAR-Spitznamen:


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


Das ist alles, stellen Sie unser Plugin wie zuvor bereit, stellen Sie eine Verbindung zu dem Projekt her, bei dem es unlösbare Konflikte gab und alles gut funktioniert.
Ein solches Umpacken macht unsere Bibliothek fehlertoleranter, wenn sie mit verschiedenen Arten von Projekten mit anderen Bibliotheken verbunden ist.


Und wenn Sie nur die JAR-Datei der in Konflikt stehenden Bibliothek mit dem Plugin verbinden, ohne sie neu zu verpacken?


Schlechte Idee, es wird zu nichts Gutem führen. Während des Projektaufbaus gibt es eine so interessante Aufgabenprüfung check...DuplicateClasses , bei der einfach Dateien mit denselben Paketen gekürzt werden. Das heißt, Dateien, die aus der JAR-Datei der verbundenen Bibliothek abgerufen wurden, und Dateien aus derselben Bibliothek, die über das Remote-Repository verbunden sind. Das Ergebnis ist dieser Fehler:



Das ist alles. Vielen Dank an alle, die gelesen haben!


Tulsa zum Umpacken
Beispiel Plugin

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


All Articles