Markus Pscheidt
Markus Pscheidt

Reputation: 7351

Gradle maven-publish: How to add testCompile dependencies to generated pom?

When publishing with the use of maven-publish (incubating, I know), compile dependencies are added to the generate POM (in the runtime scope), but testCompile dependencies are ignored.

How to get the testCompile dependencies into the generated POM as test scope?

Upvotes: 5

Views: 2797

Answers (3)

bric3
bric3

Reputation: 42293

I wasn't able to use any of the feature of the groovy language when I needed to modify the POM xml. And I had to rely on the API directly just like krzychu's answer.

Otherwise the xml closure wasn't applied as I expected it would, the build was failing with some warning, or the closure wasn't applied correctly resulting in invalid pom.

But recently, after reading carefully the groovy's closure documentation, I noticed one can apply a resolutionStrategy to a closure, to help the runtime find the right context (the implicit this).

The default resolution strategy is Closure.OWNER_FIRST, which explains why I got errors about the closure being applied to publications in some of my trials. From their documentation I tried to set the strategy to Closure.DELEGATE_FIRST and this proved working as expected.

Note however the closure has to apply on a Node, hence the .children() returns a list, .last() returns a Node on which you can add another node either via the .plus(...) method or its alias +.

publishing {
    publications {
        core(MavenPublication) {
            pom.withXml {
                def dependenciesNode = 
                    asNode().dependencyManagement
                            .first()
                            .dependencies
                            .first()

                dependenciesNode.children().last().plus( {
                    resolveStrategy = Closure.DELEGATE_FIRST
                    dependency {
                        'groupId'('org.springframework.boot')
                        'artifactId'('spring-boot-dependencies')
                        'version'(rootProject.'spring-boot.version')
                        'type'('pom')
                        'scope'('import')
                    }
                })
            }
        }
    }
}

Finding the right syntax was like finding a pin in haystack, here's some links (1), (2), (3) that helped me one I found the resolutionStrategy.

Upvotes: 0

krzychu
krzychu

Reputation: 3687

I used several hours to find a nice way to choose what build configuration an artifact or publication should use, but with no luck. My findings were, that the only way to achieve it is by modifying resulting POM XML as below:

// build.gradle

publishing {
    repositories { /* skipped for brevity */ }

    publications {
        core(MavenPublication) {
            from components.java
            artifactId project.name

            artifact sourcesJar {
                classifier 'sources'
            }
        }

        generators(MavenPublication) {
            from components.java
            artifactId "${project.name}-generators"

            artifacts = [ generatorsJar ]
            artifact generatorsSourcesJar {
                classifier 'sources'
            }

            pom.withXml { pomXml -> replaceDependenciesWith('generatorsBase', pomXml) }
        }
    }
}

void replaceDependenciesWith(String configurationName, XmlProvider pomXml) {
    Node configurationDependencies = new Node(null, 'dependencies')
    project.configurations.getByName(configurationName).allDependencies.each { dep ->
        Node dependency = new Node(null, 'dependency')
        dependency.appendNode('groupId', dep.group)
        dependency.appendNode('artifactId', dep.name)
        dependency.appendNode('version', dep.version)
        dependency.appendNode('scope', 'compile')
        configurationDependencies.append(dependency)
    }
    pomXml.asNode().dependencies*.replaceNode(configurationDependencies)
}

Above worked on Gradle 3.3


Comment on Groovy's XML builder-like syntax

I also tried to use Groovy's XML builder-like syntax, but unfortunately wrong context was attached to closure passed to replaceNode method and hence it did not work. When inlined it was getting the same context as publications {} closure, while when extracted to a method, version dep.version did not work as expected).

// Does not work!
void replaceDependenciesWith(String configurationName, Node pomXmlNode) {
    pomXmlNode.dependencies*.replaceNode {
        dependencies {
            project.configurations.getByName(configurationName).allDependencies.each { dep ->
                dependency {
                    groupId dep.group
                    artifactId dep.name
                    version dep.version
                    scope 'compile'
                }
            }
        }
    }
}

Upvotes: 3

Jolta
Jolta

Reputation: 2725

The POM is only used when publishing an artifact; it gets uploaded to the Maven repo along with the artifact. Therefore, the POM only needs runtime dependencies.

Gradle executes tests independent of your deployment plugin, so it does not use the POM file.

Assuming you're using the Java plugin, it adds the test source set. This in turn creates the testCompile task.

Now, Gradle assumes that your runtime dependencies will be the same as your compile-time dependencies, if you don't configure otherwise. However, it only considers the main source set. That's why you POM doesn't include test dependencies.

So, in summary, configure your test dependencies similar to the below. Then, just live happy, knowing that the published artifact will not include your test code or its dependencies.

dependencies {
    testCompile 'org.springframework:spring-test:4.+'
}

If you have an exceptional situation, where tests are executed on a machine that doesn't have access to the test source code, please describe in more detail what your requirements are. It should be possible to set up a separate output artifact for the test code, so it can be published, but I still don't think you should release it in the same package (or POM) as the main source set.

Upvotes: 2

Related Questions