在Gradle中重新包装包裹

在我的文章中,我想谈谈另一个可以使用Gradle轻松实现的技巧-重新包装库包。 甚至对这个构建系统都做了一点工作的每个人都知道,它会自动知道如何解决不同版本的库的冲突,并且如果您愿意,您可以对此进行影响,例如,对特定版本的库进行过大的修改:


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

不幸的是,这并不总是有助于解决版本冲突的问题。 例如,存在一个已知问题 ,即固件中的某些htc设备已经具有gson库,并且如果您的gson版本与内置库不同,则可能会出现问题,因为ClassLoader仅将一个类加载到内存中,因此在这种情况下它将是系统级的。


开发库时也会发生此问题。 如果将2个使用不同版本(例如1和2)的相同第三方库连接到项目的库,则Gradle将解析并获取最新版本,即第二个。 但是,如果此第三方库中没有向后兼容性,并且不能仅使用第二个版本而不是第一个版本,那么肯定会出现很难跟踪的问题。 等待第一个版本的库将接收第二个类,并且只会崩溃。


在编写grad插件时遇到版本冲突,它使用了冲突的asm库。 编写插件后,我在一个测试项目上检查了它的性能:一切都很好,在pet项目上进行了检查,一切也都很好,但是当我将其连接到具有大量第三方依赖项的真实工作项目时,我遇到了一个问题。



下切问题的解决方案。


然而它奏效了,出了什么问题?


我们得到完整的错误跟踪:



我们看到asm ClassVisitor库的构造函数中的错误在第79行。 让我们看看那里,但是当尝试打开ClassVisitor ,工作室提供了2个选项



我的插件使用asm版本7.2 ,所以我们去了那里,在第79行,我们看到了以下内容:



显然这不是我们所需要的。 现在转到ClassVisitor版本6:



只是我们的IllegalArgumentException没有消息。 我的插件使用Opcodes.ASM7ASM api 7版本 ,并且在该库的6版本中此api不存在,因此,构造函数中的IllegalArgumentException Opcodes.ASM7 。 我们可以得出结论,插件收到了不正确的库版本。


我想是垃圾问题,并这样做:


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

令我遗憾的是,这绝对没有效果。 尽管./gradlew app:dependencies命令显示该版本已被7.2取代,但我仍然无法找到无法对asm版本进行过大修正的确切原因。 如果有人有想法或假设,我将很高兴听到您的意见。


该问题必须以某种方式解决


开始了一系列的谷歌搜索并加深对冰雹的工作。 结果,我去了asm网站,也许他们对此有所了解。 原来,他们真的知道, 我的问题答案是在FAQ部分。 他们说要用另一个替换asm软件包,他们甚至为此提供了一个实用程序 。 好的,让我们尝试。 您只需要连接插件并进行一些设置:


 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会生成将带有重新打包的软件包的asm库jar文件生成到的目录,因此您需要通过fileTree打开对此目录的依赖关系访问。 现在,该库可通过import stater.org.objectweb.asm.*代替org.objectweb.asm.* 。 该插件具有各种设置,但是在我的示例中,只需更改软件包即可。


接下来,遍历整个项目并将导入从org.objectweb.asm更改为
stater.org.objectweb.asm 。 在我看来,这是一个非常方便的实用程序,比手工完成要容易很多倍,尤其是在更新库时,我们只需将from 'org.ow2.asm:asm:7.2'更改为新版本,并使用新版本生成重新包装的jar昵称自动机。


如果您只有一个项目(而不是一个库),那么这就足以解决不可解决的冲突,就像本文开头提到的gson一样。 但是,如果您像我一样写一个库,那还不是全部。


我们解决了重新打包的问题,​​但是现在asm并不是通过依赖于远程maven存储库而是通过本地jar文件连接到项目的,而该jar文件将在部署库时丢失,并且会出现NoClassDefFoundError错误。 但是这个问题很容易解决:


  1. 在我们的gradle文件中,创建一个新配置:


     configurations { extraLibs implementation.extendsFrom(extraLibs) } 

  2. 接下来我们改变


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


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

  3. 重新定义负责收集最终jar文件的任务,并在最终jar昵称中使用我们的新配置编写所有库:


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


就是这样,像以前一样部署我们的插件,连接到存在无法解决的冲突且一切正常的项目。
当与其他库连接到各种项目时,这种重新打包使我们的库更具容错能力。


如果您只是将冲突库的jar文件连接到插件而无需重新打包?


坏主意,不会带来任何好处。 在构建项目的过程中,有一个非常有趣的任务check...DuplicateClasses ,它仅使用相同的包来削减文件。 即,从连接的库的jar文件获得的文件以及从通过远程存储库连接的同一库的文件。 结果将是此错误:



仅此而已。 感谢所有阅读的人!


塔尔萨重新包装
示例插件

Source: https://habr.com/ru/post/zh-CN472002/


All Articles