是否可以检查哪些gradle依赖项包含给定的类?
最近,我们遇到了类org.apache.commons.beanutils.PropertyUtilsBean
的版本不匹配问题.我们认为不匹配只是在1.8和1.9.3版中引入commons-beanutils
的某些依赖项之间,但是在跟踪并排除每个传递性依赖项之后,我们仍然面临问题.
Recently we had a version mismatch problem with a class org.apache.commons.beanutils.PropertyUtilsBean
. We thought that mismatch is just between some dependency that brings commons-beanutils
in versions 1.8 and 1.9.3 but after tracking and excluding each transitive dependency we were still facing an issue.
事实证明,PropertyUtilsBean也包装在commons-digester3-3.2-with-deps
内,而不是声明为对commons-beanutils
的依赖.
It turns out that the the PropertyUtilsBean was also packed inside the commons-digester3-3.2-with-deps
instead declared as dependency to commons-beanutils
.
是否可以在gradle中搜索所有依赖项(包括可传递的依赖项)以查找特定的完全限定的类名?这样我们可以当场解决此类问题.
Is it possible in gradle to search all dependencies (including transitive ones) for specific fully qualified classname? That way we could resolve such problems on the spot.
我尝试过,可以使用一些自定义的gradle构建逻辑:
I tried it and it is possible using some custom gradle build logic:
科特林DSL
tasks {
val searchClass by creating {
doLast {
configurations.forEach { // check all configurations
if (it.isCanBeResolved) {
try {
val classLoader = configToClassloader(it)
// replace here class you are looking for
val cl = Class.forName("arrow.core.Either", false, classLoader)
println("found in Configuration $it")
println(cl.protectionDomain.codeSource.location)
} catch (e: Exception) {}
}
}
}
}
}
// Helper function: convert a gradle configuration to ClassLoader
fun configToClassloader(config: Configuration) =
URLClassLoader(
config.files.map {
it.toURI().toURL()
}.toTypedArray())
可以通过使用某些参数机制替换硬编码的类名来进一步增强此功能.
This could be further enhanced by replacing the hard coded classname with some parameter mechanism.
示例输出:
> Task :searchClass
Configuration configuration ':domain:apiDependenciesMetadata'
file:/Users/abendt/.gradle/caches/modules-2/files-2.1/io.arrow-kt/arrow-core-data/0.9.0/a5b0228eebd5ee2f233f9aa9b9b624a32f84f328/arrow-core-data-0.9.0.jar
Groovy DSL
def configToClassloader(config) {
return new URLClassLoader(
*config.files.collect {
it.toURI().toURL()
}.toArray())
}
task searchClass {
doLast {
configurations.forEach { // check all configurations
if (it.canBeResolved) {
try {
def classLoader = configToClassloader(it)
// replace here class you are looking for
def cl = Class.forName("arrow.core.Either", false, classLoader)
println("found in Configuration $it")
println(cl.protectionDomain.codeSource.location)
} catch (e) {}
}
}
}
}