Subproject A depends on module L which has an optional dependency on another module X.
Subproject B depends on module M which has a required dependency on X.
This optional dependency (a feature I still find highly dubious) on X results in pulling in additional jar files which I do not want to ship, partially because managing all the licence text for included libraries is a hassle, but also because it bloats the size of our distribution. We’re a desktop app and don’t have the luxury of hiding everything behind a nice server so that nobody knows how big it is.
If I put this into my common build then it excludes X, but does so even for module B, which breaks it.
I know I could put it just into the build file for module B, but then the buildfile for other modules which behave like B end up duplicating code with that one.
Additionally, I feel like having this exclusion information closer to the list of modules which make up one dependency would make it easier to find the exclusion list when you’re looking for it.
So is there a way to keep this information about exclusions in dependencies.gradle, such that any module which includes module L will have the transitive dependency X excluded, while modules which include module M will not, and modules which include both will not?
Is there a pattern for reusing dependencies that does allow for this sort of thing? Seeing as it’s such a common thing to want to do, I’d expect a single correct way to do it to exist, especially considering how long Gradle has been around now.
Well, what you essentially want is to rewrite the dependencies for certain module.
I can understand why you want to do it in a central place, and you can surely concoct something useing all { ... }, but from where I stand now, I would argue that it would be better if you repeat the exclusions everywhere to avoid surprises down the road. Centralizing stuff like this tends to cause more problems than it solves.
On the other hand, you can abbreviate it a bit by assigning the closure to a local variable - that is explicit and reasonably concise.
(I’m trying add() because I couldn’t get compile() itself to work, but I think being able to use compile() would be more elegant. Being able to put the closure and the notation into the same thing would be even more elegant since it removes more duplication.)
I get an error:
> Cannot convert the provided notation to an object of type Dependency: dependencies_57urgqmegl6y6w9aheb5kwegg$_run_closure1@56f3f9da.
The following types/formats are supported:
- Instances of Dependency.
- String or CharSequence values, for example 'org.gradle:gradle-core:1.0'.
- Maps, for example [group: 'org.gradle', name: 'gradle-core', version: '1.0'].
- FileCollections, for example files('some.jar', 'someOther.jar').
- Projects, for example project(':some:project:path').
- ClassPathNotation, for example gradleApi().
I find this odd, because the add method is documented as having three parameters - a string, a dependency notation object and a closure. I have passed the closure as the third parameter as-is, but somehow it seems like it’s being misinterpreted as a dependency notation object.
> Could not find method compile() for arguments [org.jruby:jruby-core:9.0.5.0, build_7sitauikbgnylchfbg4j698l1$_run_closure1$_closure3@2564410b] on project ':scripting-impl'.
If I remove the closure:
compile('org.jruby:jruby-core:9.0.5.0')
Now the build works fine… so it’s like it can’t find the overload which takes a Closure?
Hi. Thanks for this, super helpful. I want to know how do I go about adding multiple withouts. For example, here’s a sample where I’m trying to achieve this…
Unfortunately, this doesn’t work for me. it works with only the extra one parameter i.e. compile deps.datePicker, withoutSupportv4 but adding more commas fails and I get this error:
Error:(93, 0) Cannot convert the provided notation to an object of type Dependency: build_ey73hh1h8f4tc0h2ljim03u1k$_run_closure2$_closure11@122c5b55.
The following types/formats are supported:
- Instances of Dependency.
- String or CharSequence values, for example 'org.gradle:gradle-core:1.0'.
- Maps, for example [group: 'org.gradle', name: 'gradle-core', version: '1.0'].
- FileCollections, for example files('some.jar', 'someOther.jar').
- Projects, for example project(':some:project:path').
- ClassPathNotation, for example gradleApi().
Comprehensive documentation on dependency notations is available in DSL reference for DependencyHandler type.
Putting all three exclude lines into one closure will work. I found the same trap - if you have more than one artifact you want to apply the excludes to, that won’t work either, so you just have to duplicate a lot of stuff…
You have a few options here. The most trivial is to put all excludes in one closure - if certain dependency is not in the transitives, the exclude clause will have no power. Using your code above:
And finally if you fancy another design you may use a higher order function to create the dependency config closure (depending on the build complexity and audience that may be overkill):
dependencies {
// note that this is a closure returning closure
def withExcludes = { boolean excludeDesign -> return {
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'support-v13'
if (excludeDesign) {
exclude group: 'com.android.support', module: 'design-v13'
}
}}
// For Material Datepicker
compile deps.datePicker, withExcludes(true)
compile deps.foobar, withExcludes(false)
}
I hope this helps - these are just illustrations of what you can do with the Gradle DSL. It is worth spending some time to understand the Gradle domain model, and then you can come up with your own variations.
Indeed, another reason to use the ext { ... } block is to share the closures between subprojects.
On the other hand, you may want to keep the closures in the dependencies { ... } block if you want to make the exclusions more visible (for example because the average team member is not experienced with Gradle and doesn’t know what ext { ... } is, but is skilled enough to look around and figure the pattern).