You may have read the article “A confusing dependency”. If not I really recommend it. But if you are lazy (as me) I will quickly summarize the issue the guy had. He declared a dependency and Gradle fetched it from a different repository than what he expected. This is hard to explain and therefore I tried to visualize it:
The project declared the dependency abc1
. The author expected that this dependency will be taken from JitPack. But his build tool — Gradle — used the dependency from jcenter instead.
I will not explain the issue with the “fake artifacts” here. Because this is another topic. If you are really interested in the whole story. Read the article.
This happed because Gradle respects the order of your declared repositories. That means if you declare jcenter as your first repository it will try to fetch all of your declared dependencies from there! When a dependency is not found (or generally speaking not all dependencies resolved via the given repository) it will use the next repository to fetch the leftover (not yet resolved) dependencies. Gradle does this as long as not all dependencies are fetched or no more repositories are declared anymore. The latter leads to a failing build obviously.
One solution to fix that issue is to change the order of the repositories. When JitPack would be declared as the first repository the issue wouldn’t happen. On the other side, jcenter hosts the most artifacts. Scanning (and maybe fetching) JitPack for all of your dependencies which will lead to a 99 percent failure rate leads to slower builds. So what to do?
Gradle 5.1 introduced an attempt to fix that:
Matching repository to dependencies
With that new behaviour (or not so new — we are currently at Gradle 5.3-rc3) we can define which dependencies should be fetched and rejected from a given repository! There is a simple API for that which can be used to say either:
- Include this, reject everything else
- Exclude this, include everything else
- Both — include this but only if its not excluded
By “this” I mean a:
group
(includeGroup("group")
&excludeGroup("group")
)module
— which is agroup
+artifactId
(includeModule("group", "module")
&excludeModule("group", "module")
)version
— which is agroup
+artifactId
+version
(includeVersion("group", "module", "version")
&excludeVersion("group", "module", "version")
)
These are the “basics” and probably the ones which are most used. There is also a
onlyForAttribute
, aonlyForConfigurations
,snapshotOnly
andreleaseOnly
. I will not explain that here, only that you have heard about it.
The next example shows a behaviour like “fetch com.squareup.retrofit2
(group
) only from JitPack; never from jcenter”:
jcenter {
content {
excludeGroup("com.squareup.retrofit2")
}
}
maven(url = "https://jitpack.io") {
content {
includeGroup("com.squareup.retrofit2")
}
}
But this configuration has a side effect which is easy to oversee:
It will include only retrofit2 from JitPack, all other dependencies from there will be rejected! If we want to fetch other dependencies from JitPack we have to add the dependencies explicitly to it (via an include
method).
Excluding behaves the same but the other way around. Gradle tries to fetch all other dependencies except the given one. That means retrofit2 will be rejected from jcenter. All other dependencies will be included.
Also keep in mind that the ordering is still important! Let’s say we don’t explicitly exclude
retrofit2 from jcenter but still have it declared as the first repository. Then retrofit2 will be still fetched from jcenter:
jcenter()
maven(url = "https://jitpack.io") {
content {
includeGroup("com.squareup.retrofit2")
}
}
This is because Gradle tries to resolves a dependency from repo to repo. jcenter is declared first and doesn’t exclude it.
I hope I could give you an understanding about the matching repositories to dependencies feature which is pretty neat in my opinion 🙂.