Maven-publish plugin does not include dependencies in generated pom.xml file

A similar bug was previously discussed and resolved here. Unfortunately, many users are still encountering this issue in later versions of Gradle. I’m encountering this problem myself while attempting to port an existing Maven project to Gradle. I’d like to be able to publish the project to my local .m2 repository and have the transient dependencies listed in the generated pom.xml file so that other pre-existing Maven projects can resolve them. Here’s my build.gradle file so far:

apply plugin: 'java'
apply plugin: 'maven-publish'

group = 'com.my.company'
version = 'parentProject-SNAPSHOT'

repositories {
    mavenLocal()
    maven { url 'http://nexus.my.company.net/nexus/content/repositories/build-test-release' }
    maven { url 'http://nexus.my.company.net/nexus/content/repositories/build-test-snapshot' }
    maven { url 'http://nexus.my.company.net/nexus/content/repositories/central' }
    maven { url 'http://nexus.my.company.net/nexus/content/repositories/thirdparty' }
}

dependencies {
    compile group: 'org.testng', name: 'testng', version:'6.8.8'
    compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.7'
    compile group: 'org.slf4j', name: 'slf4j-log4j12', version:'1.7.7'
    compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version:'2.46.0'
    compile group: 'org.seleniumhq.selenium', name: 'selenium-api', version:'2.46.0'
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version:'2.5.3'
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version:'2.5.3'
    compile group: 'com.sun.mail', name: 'javax.mail', version:'1.5.3'
    compile group: 'net.lightbody.bmp', name: 'browsermob-core-littleproxy', version:'2.1.0-beta-1'
    compile group: 'io.netty', name: 'netty-all', version:'4.0.28.Final'

    // Included to fix a FirefoxDriver classpath issue
    compile group: 'xml-apis', name: 'xml-apis', version:'1.4.01'

    // Manually including Bouncycastle libraries to prevent dependency conflicts with older versions
    compile(group: 'org.bouncycastle', name: 'bcmail-jdk15on', version:'1.50') {
        exclude(module: 'bcprov-jdk15on') // Excluded to prevent cyclic dependencies
        exclude(module: 'bcpkix-jdk15on') // Excluded to prevent cyclic dependencies
    }
    compile(group: 'org.bouncycastle', name: 'bcpg-jdk15on', version:'1.50') {
        exclude(module: 'bcprov-jdk15on') // Excluded to prevent cyclic dependencies
    }
    compile(group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version:'1.50') {
        exclude(module: 'bcprov-jdk15on') // Excluded to prevent cyclic dependencies
    }
    compile(group: 'org.bouncycastle', name: 'bctsp-jdk15on', version:'1.46') {
        exclude(module: 'bcmail-jdk15on') // Excluded to prevent cyclic dependencies
        exclude(module: 'bcprov-jdk15on') // Excluded to prevent cyclic dependencies
    }

    // Explicit dependencies for JAXB and JAXWS are required in order to prevent version conflicts
    compile group: 'javax.xml.ws', name: 'jaxws-api', version:'2.2.11'
    compile(group: 'com.sun.xml.ws', name: 'jaxws-ri', version:'2.2.10') {
        // These are not used and depend on artifacts which are not available in our Nexus repositories
        exclude(module: 'jaxws-eclipselink-plugin')
        exclude(module: 'sdo-eclipselink-plugin')
    }
    compile group: 'com.sun.xml.ws', name: 'jaxws-rt', version:'2.2.10'
    compile group: 'javax.xml.bind', name: 'jaxb-api', version:'2.2.12'
    compile group: 'com.sun.xml.bind', name: 'jaxb-core', version:'2.2.11'
    compile group: 'com.sun.xml.bind', name: 'jaxb-impl', version:'2.2.11'
}

sourceSets {
    main {
        // Flattening these resource folders so users can load files without having to specify the entire path.  We may
        // have to remove this and require full paths to be specified if we ever encounter conflicts between files with
        // identical names.
        resources.srcDirs = ['src/main/resources/com/my/company/test/config',
                             'src/main/resources/com/my/company/test/selenium/chrome/drivers/chromedriver',
                             'src/main/resources/com/my/company/test/selenium/firefox/plugins/firebug',
                             'src/main/resources/com/my/company/test/photos']
        output.resourcesDir = 'build/resources'
    }
}

task sourcesJar (type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allJava
}

task javadocJar (type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

publishing {
    publications {
        mavenClassesJar(MavenPublication) {
                from components.java
            }
        }
        mavenSourcesJar(MavenPublication) {
            artifact(sourcesJar) {
                classifier = 'sources'
            }
        }
        mavenJavadocJar(MavenPublication) {
            artifact(javadocJar) {
                classifier = 'javadoc'
            }
        }
    }
    repositories {
        maven {
            url 'http://nexus.my.company.net/nexus/content/repositories/build-test-snapshot'
        }
    }
}

Here is the pom.xml file which gets generated and copied to my local .m2 cache:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.my.company</groupId>
  <artifactId>MyProject</artifactId>
  <version>parentProject-SNAPSHOT</version>
  <packaging>pom</packaging>
</project>

As you can see here, there are no dependencies being included. This causes ClassNotFoundExceptions whenever another project attempts to use classes from this project which depend on the missing dependencies.

I’ve tried resolving this by manually modifying the pom.xml file like this:

mavenClassesJar(MavenPublication) {
from components.java

        // Add project dependencies to generated pom.xml file (Note: scope and exclusions not yet supported)
        pom.withXml {
            def dependenciesNode = asNode().appendNode('dependencies')
            configurations.compile.allDependencies.each {
                def dependencyNode = dependenciesNode.appendNode('dependency')
                dependencyNode.appendNode('groupId', it.group)
                dependencyNode.appendNode('artifactId', it.name)
                dependencyNode.appendNode('version', it.version)
            }
        }
    }

Interestingly, this doesn’t appear to change the generated pom.xml file at all. In order to debug this, I tried spitting out the generated contents to a different file (it doesn’t appear that we can place breakpoints inside the build.gradle SDL closures in IntelliJ, so this was the next best thing). I modified and ran the generatePomFileForMavenClassesJarPublication task like this:

model {
    tasks.generatePomFileForMavenClassesJarPublication {
        destination = file("$buildDir/generated-pom.xml")
    }
}

When I went to $buildDir/generated-pom.xml I saw that the dependencies were all there! It seems that this task is functioning correctly, but there’s something wrong with the way the contents are ultimately being published.

Any ideas why this might be happening? Is this a bug or am I (and other users) overlooking something?

Well, as often happens, simply clarifying the problem has led to a solution. I just tried collapsing all of the publications into a single entry and it worked!

    publications {
    mavenClassesJar(MavenPublication) {
        from components.java
        artifact(sourcesJar) {
            classifier = 'sources'
        }
        artifact(javadocJar) {
            classifier = 'javadoc'
        }
    }
}

It seems that the publication for the classesJar was working fine, but the publication for either/both the sourcesJar and javadocJar were overriding that with an empty pom file. I’m still not sure why this happens or what I’d do if I needed separate publications.

If you need to create separate publications then they should have different artifactIds, that way they can have separate POMs. If you have a single publication (one POM) that publishes multiple artifacts, then the method you used in your second post is correct.

1 Like

That makes sense, thanks! There’s no bug here then.