How can I exclude a dependency from a POM built by the Gradle Maven Publishing plugin?
Asked Answered
E

3

5

I have the following dependencies in my build.gradle:

dependencies {
    compile 'org.antlr:antlr4-runtime:4.5.1'
    compile 'org.slf4j:slf4j-api:1.7.12'
    antlr "org.antlr:antlr4:4.5.1"
    testCompile group: 'junit', name: 'junit', version: '4.11'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'org.codehaus.groovy:groovy-all:2.4.4'
    testCompile 'cglib:cglib-nodep:3.1'
    testCompile 'org.objenesis:objenesis:2.1'
}

When I use the Maven Publishing plugin to publish my library, it includes both the ANTLR runtime and compile time JARs as dependencies in the generated POM:

<dependencies>
  <dependency>                    <!-- runtime artifact -->
    <groupId>org.antlr</groupId>
    <artifactId>antlr4-runtime</artifactId>
    <version>4.5.1</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>                    <!-- compile time artifact, should not be included -->
    <groupId>org.antlr</groupId>
    <artifactId>antlr4</artifactId>
    <version>4.5.1</version>
    <scope>runtime</scope>
  </dependency>
</dependencies>

I only want the runtime library to be included in this POM.

The culprit is the antlr dependency: If I remove this line, the generated POM does not have the compile-time dependency. However, then the build fails.

Ethylethylate answered 26/10, 2016 at 16:53 Comment(7)
clearly you're adding dependencies from antlr config to your compile config somewhere else in your build.gradle. Need to see more of build.gradle. Also why do you have an antlr config?Conah
Sure, here's the build.grade: github.com/graphql-java/graphql-java/blob/v2.1.0/build.gradle. I have an antlr configuration because I am using the Gradle ANTLR pluginEthylethylate
@RaGe: ./gradlew generatePomFileForGraphqlJavaPublication generates the pom in build/publications/graphqlJava/pom-default.xmlEthylethylate
The antlr plugin is doing internally, what I sort of accused you of doing. Something that would definitely work is to write a task to slurp in the generated xml and delete the offending sections. May even be possible to do with pom.withxml in the publication section.Conah
Not knowing enough about antlr, I'm still a bit confused about why you'd have two antlr4 dependencies and why it is important to exclude one from published pom.Conah
Thank you for helping me investigate this. I will look further at solutions. New to Gradle... Re: ANTLR: there are two ANTLR artifacts because you need one to generate your parser and related tools at compile time. But then the generated parser has some runtime requirements, and those are provided by the runtime artifact. Why one wouldn't want both dependencies published in the POM? Several reasons: (1) the non-runtime artifact is an unneeded dependency, but more practically (2) it has affected some users: github.com/graphql-java/graphql-java/issues/225Ethylethylate
The antlr plugin should then add antlr config to compileOnly instead of compile which would fix all of this. copileOnly dependencies don't make it to pom. Aside: Wonder if you can override what the plugin is doing, from your build script.Conah
J
12

From @RaGe suggestion to use pom.withXml I was able to use this hackery to remove that extra dependency.

pom.withXml {
  Node pomNode = asNode()
  pomNode.dependencies.'*'.findAll() {
    it.artifactId.text() == 'antlr4'
  }.each() {
    it.parent().remove(it)
  }
}

Before:

<dependencies>
    <dependency>
      <groupId>org.antlr</groupId>
      <artifactId>antlr4-runtime</artifactId>
      <version>4.5.1</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.antlr</groupId>
      <artifactId>antlr4</artifactId>
      <version>4.5.1</version>
      <scope>runtime</scope>
    </dependency>
</dependencies>

After:

<dependencies>
    <dependency>
      <groupId>org.antlr</groupId>
      <artifactId>antlr4-runtime</artifactId>
      <version>4.5.1</version>
      <scope>runtime</scope>
    </dependency>
</dependencies>

Some more links to explain the issue:

Jejune answered 27/10, 2016 at 6:35 Comment(0)
E
1

Give gradle-fury a shot. It definitely handles exclusions and I'm pretty sure that only known configurations are included in generated poms. It also had some code to ensure that there's no duplicate entries with conflicting scopes (which was a royal pain to figure out the solution)

https://github.com/gradle-fury/gradle-fury

disclaimer, i work on it

Eadwina answered 27/10, 2016 at 1:4 Comment(0)
U
0

In some cases, you may need to force a particular dependency to have an exclusion in the pom built by the gradle maven publish plugin. As far as I know at the time of writing this, there is not a straightforward way to do this without using pom.withXml

I'll leave this code here in case it helps anyone else. In particular, users of the spring dependency management plugin may find this useful

// in buildSrc/main/src/main/groovy/Exclusion.groovy 
// gradle needs groovy classes to be located here
// If they are, they automatically become accessibl in the build.gradle
class Exclusion {
  Node dependencies

  Exclusion(Node dependencies) {
    this.dependencies = dependencies
  }

  static void create(Node dependencies, Closure closure) {
    closure.delegate = new Exclusion(dependencies)
    closure()
  }

  void fromDependency(Map args, Closure closure) {
    closure.delegate = new ExcludedDependency(args)
    closure.resolveStrategy = Closure.DELEGATE_FIRST
    closure()
  }

  class ExcludedDependency {
    String groupId
    String artifactId
    String version

    ExcludedDependency(Map args) {
      this.groupId = args.groupId
      this.artifactId = args.artifactId
      this.version = args.version
    }

    void exclude(Map exclusionArgs) {
      Node dependency = dependencies.depthFirst().find({ Node it ->
        it.name() == 'dependency'
          && it.groupId.text() == groupId
          && it.artifactId.text() == artifactId
          && it.version.text() == version
      })

      if (null != dependency) {
        Node exclusionNode = (dependency.exclusions[0] as Node
          ?: dependency.appendNode('exclusions').appendNode('exclusion'))
        exclusionNode.appendNode('groupId', exclusionArgs.groupId)
        exclusionNode.appendNode('artifactId', exclusionArgs.artifactId)
      }
    }

  }
}
// Usage in build.gradle to force the pom to have our exclusions
publishing {
  publications {
    mavenJava(MavenPublication) {
      from components.java
      pom.withXml { ->
        Node pomDependencies = asNode() as Node
        Exclusion.create(pomDependencies) {
          fromDependency([groupId: 'com.enterprise', artifactId: 'xyz', version: "${xyzVersion}"]) {
            exclude([groupId: 'com.foo', artifactId: 'bar'])
          }
        }
      }
    }
  }
}

This would result in

<dependencies>
    <dependency>
        <groupId>org.enterprise</groupId>
        <artifactId>xyz</artifactId>
        <version>1.2.3</version>
        <scope>compile</scope>
        <exclusions>
            <exclusion>
                <groupId>org.foo</groupId>
                <artifactId>bar</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

whereas, previously, without the modification, we would have

<dependencies>
    <dependency>
        <groupId>org.enterprise</groupId>
        <artifactId>xyz</artifactId>
        <version>1.2.3</version>
        <scope>compile</scope>
    </dependency>
</dependencies>
Uremia answered 13/9, 2023 at 15:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.