从单个源文件夹 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.jarutil.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 个),在目录结构更改后合并它们应该是一个真正的痛苦(你知道,树冲突)。另一个原因是utilclient 相互依赖,所以我猜它们应该在一个单独的模块中,这会带来同样的问题。我知道这在我们的源代码结构设计中是一团糟,但我相信 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项目中创建单个可执行的jar?

如何将 gradle 生成的源文件夹添加到 Eclipse 项目?

使用 Build Flavors - 正确构建源文件夹和 build.gradle

如何访问位于源文件夹中的 Jar 文件?

android studio中so库文件夹放在哪