[GRADLE-1749] Provide support for optional dependencies and reflect in generated Maven metadata Created: 15/Aug/11  Updated: 17/Nov/16  Resolved: 17/Nov/16

Status: Resolved
Project: Gradle
Affects Version/s: None
Fix Version/s: None

Type: Improvement
Reporter: Chris Beams Assignee: Unassigned
Resolution: Duplicate Votes: 57


 Description   

Maven metadata allows for 'optional' dependencies, which is quite important for frameworks like Spring, where we must compile against certain artifacts, but use by end users is optional (i.e. if they don't use particular classes, they don't need the dependency).

Ivy allows for expressing this, Maven allows for expressing this.

What we need is something like:

    dependencies {
        compile "net.sourceforge.jopt-simple:joptsimple:3.0.0" {
            optional = true
        }
    }

And for that to be reflected in the generated pom.xml as:

                <dependency>
                        <groupId>net.sourceforge.jopt-simple</groupId>
                        <artifactId>joptsimple</artifactId>
                        <scope>compile</scope>
                        <optional>true</optional>
                </dependency>

See also: http://gradle.1045684.n5.nabble.com/Marking-Maven-Dependencies-as-optional-td1432582.html



 Comments   
Comment by Peter Niederwieser [ 15/Aug/11 ]

I agree that we should support this natively. Just wanted to throw in that

  • optional dependencies in a published POM are only meaningful for documentation purposes and have no effect on transitive dependency resolution (then again, removing a dependency from the generated POM isn't any simpler than making it optional)
  • it's fairly easy to cook up something on your own

See here for how I've solved the problem for the Spock project:

https://github.com/spockframework/spock/blob/groovy-1.7/gradle/publishMaven.gradle (provides an "optional" element)
https://github.com/spockframework/spock/blob/groovy-1.7/spock-core/build.gradle (consumes "optional")

Comment by Chris Beams [ 16/Aug/11 ]

Hi Peter,

Thanks for the response. To your first point, it's been my experience that optional dependencies do have a real effect on dependency resolution. For example, when managing a project through m2eclipse, or generating Eclipse metadata with `mvn eclipse:eclipse`, optional dependencies are not added to the project build path by default. So for example, if my project depends on spring-context jar and spring-context has an optional dependency on cglib, cglib will not be added to my project's classpath unless I explicitly express a dependency on it. This is exactly how we want things to work, as we try to minimize the number of dependencies a user is forced to deal with when using any of the Spring jars. From that point forward, you're quite right – the <dependency> declaration in the pom serves as documentation, as in "if you're going to use cglib, use this version"

As for rolling our own support, we already do this as well, however I like your implementation better and will probably steal it in the meantime

In any case, first-class support would be appreciated, thanks!

Chris

Comment by Mathias Kalb [ 17/Aug/11 ]

Hi Peter,

the optional dependencies in a published POM/Ivy can also be meaningful for dependency resolution.

See http://issues.gradle.org/browse/GRADLE-1293

Mathias

Comment by Kohsuke Kawaguchi [ 21/Dec/11 ]

I propose we solve this by making Dependency an ExtensionContainer. Here is why.

  • If I understand correctly, the notion of optional dependency is unique to Maven, and not present in Ivy. That is presumably why it's not available in the base Gradle. Making Dependency extensible allows plugins to define additional information on dependencies, like optional.
  • In a more complex project, marking dependencies with additional information is rather useful for packaging and so on. For example, in Jenkins, there's a dependency to a jar and then there's a dependency to a plugin, which creates a different result in packaging. Or imagine we have "recommended" dependency (that says people installing this plugin should also install others), "incompatible" dependency, or "supercedes" dependency.
  • In GlassFish, which I used to work on, we could have used the extensibility mechanism to distinguish public/private dependencies, which could have affected how OSGi manifest would look like.
Comment by Chris Pratt [ 22/Jun/14 ]

Has this ticket been addressed? As tools like IntelliJ Idea add support for Gradle, this is becoming more and more of a problem. Because tools can't specify optional dependencies, all dependencies of the project are added to the classpath creating a huge hairball of incompatible jar files leading to application failure. Is there any way to work around this lack of optional support? (not just the ability to keep it out of a generated Maven POM, but the ability for other tools that consume the gradle scripts to tell transitive dependencies from non-transitive dependencies in an included library)
(Chris)

Comment by Mauro Molinari [ 24/Nov/14 ]

@Peter Niederwieser: why doe you say that "optional" have no effect on transitive dependency? Gradle does not transitively add an optional dependency and this is exactly what optional is useful for. We are at version 2.2.1 but Gradle is still hard to use to publish artifacts Basic features like this are still missing and also the new Maven Publish plugin is extremely verbose to do what IMHO should be a simple operation.

Comment by Justin Griffin [ 24/Nov/14 ]

@mauromol, an "optional" dependency in Maven is no different transitively than any other dependency. That is, it is retained in the dependency graph in the same manner as any other dependency.

Strictly speaking, in Maven the difference is a dependency labeled as "optional" will not cause the resolution to fail if it cannot be resolved. In other words, it's a "soft" dependency. But it's still recognized as such transitively.

Comment by MariuszS [ 24/Nov/14 ]

Because gradle is using pom.xml for artifact dependency resolution, then should fully support pom.xml features like optionals and publishing. Now gradle takes us back to the era of ant... every build.gradle is full of gradle hacks and ugly code for obvious things.

Comment by Mauro Molinari [ 25/Nov/14 ]

@Justin: I was not clear. If you have module A which depends on B which has an optional dependency on C, C won't transitively fall into A dependencies unless A also defines an explicit dependency on C. This is what we need: to be able to specify an optional dependency for B, because B may be published on a Maven repository and it's often not ideal to split B into many micro-submodules (perhaps made up of just a single class...) just to avoid optional dependencies to fall into A dependencies.
As of now, I have to put this in my Gradle script to workaround this limitation:

publishing {
  publications {
    mavenJava(MavenPublication) { 
      // [...]
      pom.withXml {
        asNode().dependencies.dependency.findAll {	
          it.artifactId.text() == 'my-optional-artifact-id1' || it.artifactId.text() == 'my-optional-artifact-id2' 
        }.each {
          if(it.optional)
            it.optional.value = 'true'
          else 
            it.appendNode('optional', 'true')
        }
      }
    }
  }
}

Otherwise there's the Nebula Extra Configurations plugin that does this more easily: https://github.com/nebula-plugins/gradle-extra-configurations-plugin

@Kohsuke: I don't know Ivy, but based on the aforementioned plugin documentation, it seems like it's possible to specify the "optionality" of a dependency in Ivy too.

Comment by Endre Stølsvik [ 16/Jul/15 ]

Thanks Mauro Molinari for the suggestion! - Here's an alternative that is somewhat more generic:

publishing {
  publications {
    mavenJava(MavenPublication) {
        pom.withXml {
            def depMap = configurations.compile.dependencies.collectEntries { [it.name, it] }
            asNode().dependencies.dependency.findAll {
                def dep = depMap.get(it.artifactId.text())
                return dep?.hasProperty('optional') && dep.optional
            }.each {
                if (it.optional) {
                    it.optional.value = 'true'
                } else {
                    it.appendNode('optional', 'true')
                }
            }
        }
    }
}

Now you can just specify optional on a per dependency basis, nearly in the same fashion that one would expect Gradle to support it from the box. Notice the "ext."

dependencies {
    compile 'org.springframework:spring-jdbc:4.1.+'
    compile('org.apache.activemq:activemq-broker:5.11.+') { ext.optional = true }
    compile('com.h2database:h2:1.4.+') { ext.optional = true }
}

I find it strange that this simple thing isn't supported by Gradle by default, and would love to get it.

Comment by Clint Checketts [ 22/Jan/16 ]

@stolsvik I wish I understood how the

{ ext.optional = true }

works.

I can't get it to work for optional project dependencies:

compile (project(":other-module")) { ext.optional = true }

won't work.

Update The second option works I had merely had a typo, not the extra parenthesis needed for the project dep.

Comment by Steve Davids [ 15/Mar/16 ]

This ticket seems to be starving to death, I would think this would be resolved before adding the `compileOnly` dependency type. I would greatly appreciate this in multiple projects instead of just rolling with the basic `compile` dependency then ignoring the specific transitive dependencies that aren't actually necessary.

Comment by Sébastien Deleuze [ 07/Apr/16 ]

Now that `compileOnly` is supported, could we have an update from the the Gradle team about this issue which has been created almost 5 years ago?

Comment by Benjamin Muschko [ 15/Nov/16 ]

As announced on the Gradle blog we are planning to completely migrate issues from JIRA to GitHub.

We intend to prioritize issues that are actionable and impactful while working more closely with the community. Many of our JIRA issues are inactionable or irrelevant. We would like to request your help to ensure we can appropriately prioritize JIRA issues you’ve contributed to.

Please confirm that you still advocate for your JIRA issue before December 10th, 2016 by:

  • Checking that your issues contain requisite context, impact, behaviors, and examples as described in our published guidelines.
  • Leave a comment on the JIRA issue or open a new GitHub issue confirming that the above is complete.

We look forward to collaborating with you more closely on GitHub. Thank you for your contribution to Gradle!

Comment by David Smiley [ 15/Nov/16 ]

Benjamin Muschko please also consider the top voted issues. This issue here is very popular.

Comment by Eric Wendelin [ 17/Nov/16 ]

Moved to https://github.com/gradle/gradle/issues/867

Generated at Wed Jun 30 12:03:22 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.