Gradle多项目可选子项目的传递依赖关系应该被解析为一个现有的子项目 [英] Gradle multiproject optional subproject's transitive dependency should be resolved to an existing subproject
问题描述
假设以下项目。主项目是一个多项目,但是大型项目的每个部分可以单独开发或混合在一起:
/ master /build.gradle
/m1/build.gradle
/m2/build.gradle
/m3/build.gradle
假设 m3
使用 m2
和 m2
使用 m1
( m1 < - m2 < - m3)
存在 m2
是可选的,具有以下布局的多项目也合理
/master/build.gradle
/m1/build.gradle
/m3/build.gradle
,但在这种情况下,将从工件存储库中提取好的$ code> m2 。 m1 是 m2
的传递依赖,这是很好的,但是我怎么能告诉毕业生使用本地版本的 m1
而不是烘焙的神器?
我被卡住了,每个地方,我可以访问OVERR这些东西毕业给我只是 ModuleVersionSelector
级别访问,我如何添加一个 DefaultProjectDependency
根据下载的工件传递依赖关系?
如果我可以访问归档工件的完整依赖关系图,并且放置一些覆盖/排除,我可能会有一个替代方案。
$ b
编辑:
我所想出的最好的是使用一个使用resolutionStrategy的过滤器,我通过进一步开发创造了一个例子'elastic-deps'项目
https:/ /github.com/kgyrtkirk/elastic-deps
从 elastic-deps ,并借助此答案(也来自彼得< a))我想出了下面的技巧。
在顶级build.gradle()中:
< pre class =lang-java prettyprint-override>
//确保我们已经解析了子项目依赖项
evaluationDependsOnChildren()
def subprojectsByName = subprojects.collectEntries {it - > [it.name,it]}
subprojects.each {p - >
def hacks = [] //我们要做的更改列表
p.configurations.each {c - >
c.dependencies.each {d - >
if(d.group.startsWith(my.group.prefix)){
def sub = subprojectsByName [d.name]
if(sub!= null){
hacks.add({
//不能立即做到这一点,否则我们会得到ConcurrentModificationExceptions
c.dependencies.remove(d)
p.dependencies.add(c.name, sub)
})
}
}
}
}
//现在我们可以安全地应用更改
(hack in hacks ){
hack()
}
}
关于这一点的是,与 elastic-deps 不同,您不必修改子项目。
这仍然有一个问题,一旦你打了一个二进制依赖,任何纯传递的依赖关系被解析为二进制。例如,我有一个项目 cyan
,它直接依赖于 green
和 blue
并且通过绿色
,在黄色
:
compile - 为源集main编译类路径。
+ --- my.shared:blue:+ - > 2.0-SNAPSHOT
+ --- my.shared:green:+ - > 2.0-SNAPSHOT
| + --- my.shared:yellow:+ - > 2.0-SNAPSHOT
| \ --- my.shared:blue:+ - > 2.0-SNAPSHOT
现在如果我添加 blue
和黄色
到我的多模块项目,但不是绿色
,我得到:
compile - 为源集main编译类路径。
+ --- com.iii.shared:green:+ - > 2.0-SNAPSHOT
| + --- com.iii.shared:yellow:+ - > 2.0-SNAPSHOT
| \ --- com.iii.shared:blue:+ - >项目:蓝色
\ ---项目:蓝色
请注意,蓝色
正在被解析为项目,即使它是可传递的,但黄色
不是。
我认为这是一个功能,而不是一个错误 - 它反映了在分发时间实际发生的情况。我可以将所有更改变为黄色
,但如果我没有在我的新的黄色
存储库以及更新的绿色
与更新的依赖关系,则实际发布的 cyan
不会得到更改。
suppose the following project. the master project is a multi-project, however every part of the larger project can be developed individually or mixed in:
/master/build.gradle
/m1/build.gradle
/m2/build.gradle
/m3/build.gradle
suppose m3
uses m2
and m2
uses m1
( m1 <- m2 <- m3 )
the presence of m2
is optional a multi-project with the following layout also reasonable
/master/build.gradle
/m1/build.gradle
/m3/build.gradle
but in this case m2
would be pulled in from the artifact repository which is fine...however m1
was a transitive dependency of m2
which is good, but how can i tell gradle to use the local version of m1
instead of the baked artifact?
I'm stuck with this, every place i have access to override these thing gradle gives me "just" ModuleVersionSelector
level access, how can i add a DefaultProjectDependency
according to the downloaded artifact transitive dependencies?
i may have an alternative if i can access the full dependency graph of the archived artifacts, and put in some overrides/excludes.
EDIT:
the best i've come up with is using a filter using resolutionStrategy, i've created an example by further developing the 'elastic-deps' project
https://github.com/kgyrtkirk/elastic-deps
Starting with elastic-deps and with the help of this answer (also from Peter) I came up with the trick below.
In the top-level build.gradle():
// make sure we've parsed the subproject dependencies
evaluationDependsOnChildren()
def subprojectsByName = subprojects.collectEntries { it -> [it.name, it] }
subprojects.each { p ->
def hacks = [] // list of changes we're going to make
p.configurations.each { c ->
c.dependencies.each { d ->
if (d.group.startsWith("my.group.prefix")) {
def sub = subprojectsByName[d.name]
if (sub != null) {
hacks.add({
// can't do this immediately or we'll get ConcurrentModificationExceptions
c.dependencies.remove(d)
p.dependencies.add(c.name, sub)
})
}
}
}
}
// Now we can safely apply the changes
for (hack in hacks) {
hack()
}
}
The nice thing about this is that unlike elastic-deps you don't have to modify the subprojects.
This still has the problem that once you hit a binary dependency, any purely transitive dependencies are resolved as binary. E.g., say I have a project cyan
which depends directly on green
and blue
and transitively, through green
, on yellow
:
compile - Compile classpath for source set 'main'.
+--- my.shared:blue:+ -> 2.0-SNAPSHOT
+--- my.shared:green:+ -> 2.0-SNAPSHOT
| +--- my.shared:yellow:+ -> 2.0-SNAPSHOT
| \--- my.shared:blue:+ -> 2.0-SNAPSHOT
Now if I add blue
and yellow
to my multi-module project, but not green
, I get:
compile - Compile classpath for source set 'main'.
+--- com.iii.shared:green:+ -> 2.0-SNAPSHOT
| +--- com.iii.shared:yellow:+ -> 2.0-SNAPSHOT
| \--- com.iii.shared:blue:+ -> project :blue
\--- project :blue
Note that blue
is resolved correctly to the project even when it's transitive, but yellow
isn't.
Personally I think this is a feature, not a bug -- it reflects what would actually happen at distribution time. I can make all the changes to yellow
I want, but if I don't put a new yellow
artifact in my repository and also an updated green
with the updated dependency, then the actual release of cyan
isn't going to get those changes.
这篇关于Gradle多项目可选子项目的传递依赖关系应该被解析为一个现有的子项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!