How do I exclude second-level dependencies?

We depend on orientdb-graphdb, which unfortunately has a hard dependency on gremlin-groovy. We don’t want to include Groovy with our application, mostly because it is a crap language and we don’t want to deal with support issues from users trying to use it and not being able to figure it out.

So our dependency on orientdb-graphdb excludes it:

  orientdb_graphdb: ['com.orientechnologies:orientdb-graphdb:2.1.19', {
    exclude group: 'com.tinkerpop.gremlin', module: 'gremlin-groovy'
  }],

Now the problem is, I found it was still getting pulled in, because some other module of ours was depending on orientdb-distributed, which depends on orientdb-graphdb. For some reason - the exclude rules for orientdb-graphdb are not used in this situation. (IMO, this is crap and the exclude should apply since it’s the same dependency! But okay, I’m trying to work around it.)

I’m having trouble figuring out how I can override this sort of second-level dependency.

Something like this would reflect the kind of thing I’ve seen in the docs:

  orientdb_distributed: ['com.orientechnologies:orientdb-distributed:2.1.19', {
    module('com.orientechnologies:orientdb-graphdb:2.1.19') {
      exclude group: 'com.tinkerpop.gremlin', module: 'gremlin-groovy'
    }
  }],

But it gives an error about module being undefined. Certainly when I look at the Javadoc, the method is missing from those docs, but then again, so are half the other methods Gradle lets me call with no issue.

Is there a way to do this then? I mean, other than cracking open their POM, removing the dependency and reuploading it as a new version…

If your objective is to exclude ‘gremlin-groovy’ regardless of where it comes from, then you probably should be excluding by configuration, not by dependency. See Dependency Management: Excluding transitive dependencies.

Creating a client module is just overriding the meta data for one instance of the dependency. They are not global exclude rules for that dependency or intended to be treated as such. Excluding a dependency by configuration for one or even all configurations is designed specifically for this case where you really don’t want the dependency, period.

2 Likes

I have two issues with that approach:

(1) If someone does add a dependency which actually depends on gremlin-groovy (as opposed to the current ones, which are false dependencies that probably should have been marked as optional!), it will be excluded, and won’t work because the dependency is missing.

(2) It puts info about which transitive dependencies are included over in a completely different file to the rest of the dependency information, but also not grouped with the culprit library which was originally pulling the dependency in, making it difficult to clean up redundant exclusions.

Your original post seemed pretty adamant that you absolutely would not want this dependency in your application, period. If that’s not truly the case and you do want the dependency if and when something actually needs it, I agree that this is a critical difference that would change your approach.

If you were wanting to exclude the dependency no matter what in one or all configurations, there’s no reason this would need to be in a completely different file. Like in the Gradle project itself, our projects have a gradle/dependencies.gradle which configures all groupings, excludes, and resolution rules, etc. for the dependencies. Excluding by configuration does not need to be in the same location as the declaration of your own configurations.

As far as just making your excludes work by dependency, your second example is just overcomplicated. You should be able to use the syntax in your original attempt with both dependencies for it to be excluded no matter how deep it is in the dependency tree.

orientdb_distributed: ['com.orientechnologies:orientdb-distributed:2.1.19', {
    exclude group: 'com.tinkerpop.gremlin', module: 'gremlin-groovy'
}],
orientdb_graphdb: ['com.orientechnologies:orientdb-graphdb:2.1.19', {
    exclude group: 'com.tinkerpop.gremlin', module: 'gremlin-groovy'
}],

Yep, that works. I never would have guessed that excludes applied beyond the first level… I don’t remember reading anything like that in the docs.