GradleMaven publish:如何将testCompile依赖项添加到生成的pom中?

GradleMaven publish:如何将testCompile依赖项添加到生成的pom中?,gradle,pom.xml,maven-publish,Gradle,Pom.xml,Maven Publish,使用maven publish(孵化,我知道)发布时,compile依赖项被添加到generate POM(在运行时范围内),但testCompile依赖项被忽略 如何将testCompile依赖项作为test范围获取到生成的POM中?POM仅在发布工件时使用;它与工件一起上传到Maven repo。因此,POM只需要运行时依赖项 dependencies { testCompile 'org.springframework:spring-test:4.+' } Gradle独立于部署

使用
maven publish
(孵化,我知道)发布时,
compile
依赖项被添加到generate POM(在
运行时
范围内),但
testCompile
依赖项被忽略


如何将
testCompile
依赖项作为
test
范围获取到生成的POM中?

POM仅在发布工件时使用;它与工件一起上传到Maven repo。因此,POM只需要运行时依赖项

dependencies {
    testCompile 'org.springframework:spring-test:4.+'
}
Gradle独立于部署插件执行测试,因此它不使用POM文件

假设您正在使用Java插件,它会添加
test
源代码集。这又创建了testCompile任务

现在,Gradle假设,如果不进行其他配置,则运行时依赖项将与编译时依赖项相同。但是,它只考虑
main
源集。这就是POM不包含
test
依赖项的原因

dependencies {
    testCompile 'org.springframework:spring-test:4.+'
}
因此,总而言之,配置您的测试依赖项,如下所示。然后,开心地生活,知道发布的工件将不包括您的测试代码或其依赖项

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

如果您遇到异常情况,测试是在无法访问测试源代码的机器上执行的,请更详细地描述您的需求。应该可以为测试代码设置一个单独的输出工件,这样它就可以发布了,但是我仍然认为您不应该将它发布在与
main
源代码集相同的包(或POM)中。

我用了几个小时找到了一个很好的方法来选择工件或发布应该使用的构建配置,但是没有运气。我的发现是,实现它的唯一方法是修改生成的POM XML,如下所示:

// 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)
}
在Gradle 3.3上进行了上述工作


对Groovy的XML builder式语法的评论 我还尝试使用Groovy的XML builder式语法,但不幸的是,传递给
replaceNode
方法的闭包附加了错误的上下文,因此它不起作用。内联时,它获得与
publications{}
closure相同的上下文,而提取到方法时,
version dep.version
未按预期工作)


当我需要修改POM xml时,我无法使用groovy语言的任何特性。我必须直接依赖API,就像

否则,xml闭包没有像我预期的那样应用,生成失败并发出一些警告,或者闭包没有正确应用,导致pom无效

但是最近,在仔细阅读了文档之后,我注意到可以对闭包应用
resolutionStrategy
,以帮助运行时找到正确的上下文(隐式上下文)

默认的解决策略是
Closure.OWNER_FIRST
,这解释了为什么在我的一些试验中,在
出版物
上应用闭包时出现错误。从他们的文档中,我试图将策略设置为
close.DELEGATE_FIRST
,这证明了它的预期效果

// 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'
                }
            }
        }
    }
}
但是请注意,闭包必须应用于
节点
,因此
.children()
返回一个列表,
.last()
返回一个
节点
,您可以通过
.plus(…)
方法或其别名
+
在该节点上添加另一个节点

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')
                    }
                })
            }
        }
    }
}

找到正确的语法就像大海捞针,这里有一些链接,帮助我找到了一个我找到的
解析策略

请解释为什么要将测试代码发布为工件。这当然不是标准程序。有一些测试库,如
弹簧测试
弹簧批量测试
jsonpath
,它们由基本模块和相关模块在模块设置中使用。因此,最好在
测试
范围内的基本模块中声明它们,方法与
运行时
范围内声明
spring上下文
相同。好的,我已经发布了一个单独的
工件testJar
,模块之间共享测试代码。为声明外部库(如
springtest
)的测试jar创建一个额外的POM也可以。这可能吗?发布测试代码的用例是什么?抱歉这么坚持,但我认为如果不需要的话,不要在某些事情上花费精力是很重要的是的,您可以声明更多要发布的工件。有关如何声明您的
出版物
,请参阅。没问题,这里有一些关于动机的详细信息。让我们看一下初始化内存中数据库或test constants Java类的database init脚本。这些用于主/基本模块中的单元测试以及相关模块。另一种选择是复制粘贴这样的测试资源。多模块设置中的可传递测试依赖性问题在这里为Maven演示:结论似乎是,可传递测试依赖性不被视为正确的做法。因此,我将为每个项目声明测试依赖项,即使这是代码的重复并且违反了DRY原则。