从单个源文件夹中 Gradle 多个 jar

问题描述 投票:0回答:5

目前我们有一个项目结构,其中包含名为

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/**"
        }
    }
    
  • 客户端.jar

    task clientJar(type: Jar) {
        from(sourceSets.main.output) {
            include "my/client/package/**"
        }
    }
    
  • 服务器.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 中完成此操作(我当前的方法不起作用)?

也许我的做法是完全错误的。请指教。

附注目前我们无法更改项目源代码文件夹结构。

java jar gradle
5个回答
36
投票

我将在这里发布我的工作解决方案作为答案(我在 gradle 论坛上有提示)。

gradle 中的范围是非常奇怪的事情:)我认为每个任务定义都会创建某个“Task”类的对象,在这种特殊情况下类似于“JarTask”。然后我可以从 build.gradle 脚本中的任何位置访问该类的任何属性。然而,我发现唯一可以看到模式的地方,这些模式包含在 jar 文件中 - 在任务的

from
块内。所以我现在的工作解决方案是:

1) 定义一个项目级集合以包含要排除的模式

server.jar

2) 排除

from
任务的
serverJar
块中的所有模式。

请参阅下面的最终版本

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

33
投票

我认为这种做法是错误的。我建议制作一个包含 3 个子项目的项目。

project
- util
- server (depends on util)
- client (depends on util)

如果由于某种原因你无法使用这种构建文件更改类结构:

设置.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 的任务。

请阅读多项目构建


4
投票

我们公司也有同样的问题,即。遗留代码很难迁移到“好的”项目结构中,并且需要从同一代码库构建多个 jar。我们决定定义不同的源集并使用标准 Gradle 构建每个源集。

然后我们使用迭代器为每个源集添加 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
}

3
投票

我也原则上同意已接受的答案。 我发现一个项目,其中客户端需要两个本质上相同文件的 JAR,除了清单仅通过类路径键不同之外。

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"

对此进行配置的唯一方法是使用多模块 Gradle 项目,然后将编译和/或部署依赖项添加到核心/主项目。

project(':common:acme-micro-service-webapp') {
    dependencies {
        compile project(':common:acme-core')
    }
}

在“acme-micro-service-webapp”项目内,这确保首先编译依赖的“common:acme-core”。

PS:我仍在尝试找出更好的解决方案。

PS PS:如果您也使用 Maven,则可能可以挂接“安装”任务。


0
投票

@vitalidze 的答案给了我正确的方向,但我无法让它发挥作用。所有罐子的名称都相同,并且互相覆盖。添加 archiveBaseName 解决了它。

sourceSets {
    main {
        java {
            srcDir 'src'
        }
    }
}


task myTestJar(type: Jar) {
    from(sourceSets.main.output) {
        include "my/path/demo/**"
    }

    archiveBaseName = 'myTestLib'
}
© www.soinside.com 2019 - 2024. All rights reserved.