从单个源文件夹 Gradle 多个 jar
Posted
技术标签:
【中文标题】从单个源文件夹 Gradle 多个 jar【英文标题】:Gradle multiple jars from single source folder 【发布时间】:2013-11-29 06:34:20 【问题描述】:目前我们有一个名为src
的单一源文件夹的项目结构,其中包含三个模块的源代码。我想做的是:
1) 编译源代码。这可以通过 sourceSets 定义轻松完成:
sourceSets
main
java
srcDir 'src'
2) 将编译结果放入三个jar中。我通过三个“jar”类型的任务来做到这一点:
我现在通过三个单独的任务来做这件事:
util.jar
task utilJar(type: Jar)
from(sourceSets.main.output)
include "my/util/package/**"
client.jar
task clientJar(type: Jar)
from(sourceSets.main.output)
include "my/client/package/**"
server.jar
task serverJar(type: Jar)
from(sourceSets.main.output)
include "**"
excludes.addAll(utilJar.includes)
excludes.addAll(clientJar.includes)
问题是server.jar
应该包含所有未包含在client.jar
和util.jar
中的类。在 ant 构建脚本中,我们使用 difference
ant 任务解决了这个问题。这如何在 gradle 中完成(我目前的方法不起作用)?
也许我的方法是完全错误的。请指教。
附:至于现在我们不能更改项目源代码文件夹结构。
【问题讨论】:
【参考方案1】:我将在此处发布我的工作解决方案作为答案(我在 gradle 的论坛上有提示)。
gradle 中的范围是非常奇怪的东西 :) 我认为每个任务定义都会创建某个“任务”类的对象,在这种特殊情况下类似于“JarTask”。然后我可以从 build.gradle 脚本中的任何位置访问该类的任何属性。但是,我找到了唯一可以看到模式的地方,这些模式包含在 jar 文件中 - 在任务的 from
块内。所以我现在的工作解决方案是:
1) 定义一个项目级集合以包含要从server.jar
中排除的模式
2) 排除serverJar
任务的from
块中的所有模式。
请看下面的最终版本
sourceSets
main
java
srcDir 'src'
// holds classes included into client.jar and util.jar, so they are to be excluded from server.jar
ext.serverExcludes = []
// util.jar
task utilJar(type: Jar)
from(sourceSets.main.output)
include "my/util/package/**"
project.ext.serverExcludes.addAll(includes)
// client.jar
task clientJar(type: Jar)
from(sourceSets.main.output)
include "my/client/package/**"
project.ext.serverExcludes.addAll(includes)
// server.jar
task serverJar(type: Jar)
from(sourceSets.main.output)
exclude project.ext.serverExcludes
【讨论】:
这很好,但是你如何引用来自其他项目的单独的.jar
文件 - 例如你怎么能把client.jar
拉进另一个子项目?
@z0r 我们只是将工件发布到存储库,然后将它们用作子项目中的依赖项。附言很抱歉延迟回复。【参考方案2】:
我也原则上同意接受的答案。 我发现了一个项目,其中客户端需要两个 JAR,基本上是同一个文件,除了 Manifest 仅在 Class-Path 键上有所不同。
jar
manifest
attributes(
"Main-Class": platformMainClass,
"Implementation-Title": platformDisplayName,
"Implementation-Description": platformDescription,
"Platform-Version": platformVersion,
"Implementation-Version": version,
"Build-Assembly-User": System.getProperty("user.name"),
"Build-Assembly-Date": new java.util.Date().toString(),
"Class-Path": configurations.compile.collect "lib/"+it.getName() .join(' ')
)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude( [ 'log4j*.properties', 'uk/gov/acme/secret/product/server/**' ])
同样的清单和源代码则为:
task applicationClientJar(type: Jar, description: "Creates the Application Client JAR file.")
dependsOn compileJava
manifest
attributes(
"Main-Class": platformMainClass,
"Implementation-Title": platformDisplayName,
"Implementation-Description": platformDescription,
"Platform-Version": platformVersion,
"Implementation-Version": version,
"Assembly-Date": new java.util.Date().toString()
)
archiveName = "acme-client-$platformVersion.jar"
destinationDir = file("$buildDir/libs")
from sourceSets.main.output
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude( [ 'log4j*.properties', 'uk/gov/acme/secret/product/server/**'
所以 Grzegorz 表示法是正确的,因为 Gradle 应该知道 GAV 有两个不同的 JAR。多模块是首选。
compile "uk.gov.acme.secret:acme:1.0" // CORE
compile "uk.gov.acme.secret:acme-client:1.0"
为此配置的唯一方法是使用 Multi-Module Gradle 项目,然后将编译和/或部署依赖项添加到核心/主项目。
project(':common:acme-micro-service-webapp')
dependencies
compile project(':common:acme-core')
在“acme-micro-service-webapp”项目中,这确保了依赖的“common:acme-core”首先被编译。
PS:我还在努力寻找更好的解决方案。
PS PS:如果您也使用 Maven,可能会挂上“安装”任务。
【讨论】:
如果项目目录结构必须保持不变(如某些非标准项目结构),这个答案非常巧妙。 Grzegorz Żur 的答案是一个干净的方法,但需要修改目录结构,这对我来说不是一个选择。我只有一个以上的具有主要功能的类,并且每个类都需要一个 jar。我会将这个tasks.build.dependsOn(['JarOne', 'JarTwo', 'applicationClientJar'])
添加到 build.gradle 文件中,以确保一个简单的 gradle build
将 jar 任务添加到其过程中。【参考方案3】:
我们公司也有同样的问题,即。难以迁移到“良好”项目结构中的遗留代码,并且需要从同一代码库构建多个 jar。我们决定定义不同的 sourceSet 并使用标准 Gradle 构建每个 sourceSet。
然后我们使用迭代器为每个 sourceSet 添加 jar 和 javadoc 任务:
sourceSets.all SourceSet sourceSet ->
Task jarTask = tasks.create("jar" + sourceSet.name, Jar.class)
jarTask.from(sourceSet.output)
// Configure other jar task properties: group, description, manifest etc
Task javadocTask = tasks.create("javadoc" + sourceSet.name, Javadoc.class)
javadocTask.setClasspath(sourceSet.output + sourceSet.compileClasspath)
javadocTask.setSource(sourceSet.allJava)
// Extra config for the javadoc task: group, description etc
Task javadocJarTask = tasks.create("javadocJar" + sourceSet.name, Jar.class)
javadocJarTask.setClassifier("javadoc") // adds "-javadoc" to the name of the jar
javadocJarTask.from(javadocTask.outputs)
// Add extra config: group, description, manifest etc
【讨论】:
【参考方案4】:我认为这种方法是错误的。我建议创建一个包含 3 个子项目的项目。
project
- util
- server (depends on util)
- client (depends on util)
如果由于某种原因您无法更改类结构,请使用这种构建文件:
settings.gradle
include 'util', 'client', 'server'
build.gradle
subprojects
apply plugin: 'java'
project(':util')
sourceSets
main
java
srcDir '../src'
include 'util/**'
project(':server')
sourceSets
main
java
srcDir '../src'
include 'server/**'
dependencies
compile project(':util')
project(':client')
sourceSets
main
java
srcDir '../src'
include 'client/**'
dependencies
compile project(':util')
您仍然需要子项目的目录,但源代码在您想要的地方。
当您运行gradle assemble
时,您将拥有 3 个带有不同类集的 jar。这个解决方案的优点是我们制作了一个具有正确依赖关系的正确 Gradle 多模块项目,而不仅仅是构建 jar 的任务。
请阅读Multi-Project Builds。
【讨论】:
感谢您的评论。我已经考虑过这种方法,但是完全不改变目录结构是非常可取的。主要原因是我们有很多 SVN 分支(超过 50 个),在目录结构更改后合并它们应该是一个真正的痛苦(你知道,树冲突)。另一个原因是util
和client
相互依赖,所以我猜它们应该在一个单独的模块中,这会带来同样的问题。我知道这在我们的源代码结构设计中是一团糟,但我相信 gradle 可以做 ant 可以做的一切:)
@vitalidze 我提出的解决方案是不改变目录结构。额外的空目录有问题吗?你有依赖循环吗?
问题是类没有被文件夹分隔(即客户端、服务器、实用程序)。例如,我在客户端jar中包含'a/b'和'z/x/y',util包括'q/w/e',服务器应该包括所有的rest类,可能在'a','z '、'z/x'、'q'、'q/w'。
我对此投了反对票,因为在某些情况下,您需要一个项目来生成多个工件,而这正是 OP 所寻找的。span>
如果你使用 Gradle >= 3,compile
配置已被弃用,而implementation
已被弃用。见Migrate to android Plugin for Gradle 3.0.0。以上是关于从单个源文件夹 Gradle 多个 jar的主要内容,如果未能解决你的问题,请参考以下文章
如何将 gradle 生成的源文件夹添加到 Eclipse 项目?