Java 从单个源文件夹中 Gradle 多个 jars

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20008324/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 22:17:48  来源:igfitidea点击:

Gradle multiple jars from single source folder

javajargradle

提问by vitalidze

As for now we have a project structure with single source folder named src, which contains source code for three modules. What I want to do is:

至于现在我们有一个名为 的单个源文件夹的项目结构src,其中包含三个模块的源代码。我想做的是:

1) Compile source code. This is easily done with sourceSets definition:

1) 编译源代码。这可以通过 sourceSets 定义轻松完成:

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

2) Put compilation results into three jars. I am doing this via three 'jar' type tasks:

2) 将编译结果放入三个jar中。我通过三个“jar”类型的任务来做到这一点:

I am doing this now via three separate tasks:

我现在通过三个独立的任务来做到这一点:

  • 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)
    }
    
  • 工具文件

    task utilJar(type: Jar) {
        from(sourceSets.main.output) {
            include "my/util/package/**"
        }
    }
    
  • 客户端文件

    task clientJar(type: Jar) {
        from(sourceSets.main.output) {
            include "my/client/package/**"
        }
    }
    
  • 服务器文件

    task serverJar(type: Jar) {
        from(sourceSets.main.output) {
            include "**"
        }
        excludes.addAll(utilJar.includes)
        excludes.addAll(clientJar.includes)
    }
    

The thing is that server.jarshould contain all classes that are not contained within client.jarand util.jar. In ant build script we solve this problem by using differenceant task. How this can be done in gradle (my current approach doesn't work)?

问题是server.jar应该包含所有未包含在client.jarand 中的类util.jar。在 ant 构建脚本中,我们通过使用differenceant 任务来解决这个问题。这如何在 gradle 中完成(我目前的方法不起作用)?

Maybe my approach is completely wrong. Please advice.

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

P.S. as for now we CAN NOT change the project source code folder structure.

PS 至于现在我们不能改变项目源代码文件夹结构。

采纳答案by vitalidze

I will post my working solution here as an answer (I've got a hint on gradle's forum).

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

The scopes in gradle are very strange thing :) I thought that every task definition creates an object of some 'Task' class, which is something like 'JarTask' in this particular case. Then I can access any property of the class from anywhere in my build.gradle script. However, I found the only place where I can see the patterns, which are included in jar file - inside a fromblock of a task. So my working solution for now is to:

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

1) Define a project-level collection to contain patterns to be excluded from server.jar

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

2) Exclude all patterns in fromblock of serverJartask.

2) 排除任务from块中的所有模式serverJar

Please see final version below

请参阅下面的最终版本

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

回答by Grzegorz ?ur

I think the approach is wrong. I recommend making a project with 3 sub projects.

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

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

If for some reason you cannot change the class structure use this kind of build files:

如果由于某种原因您无法更改类结构,请使用此类构建文件:

settings.gradle

设置.gradle

include 'util', 'client', 'server'

build.gradle

构建.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')
    }
}

You still need directories for subprojects but the sources are in one place as you wanted.

您仍然需要子项目的目录,但源在您想要的地方。

When you run gradle assembleyou will have 3 jars with separate set of classes. The advantage of this solution is that we make a proper Gradle multi module project with correct dependencies, not just tasks for building jars.

当您运行时,gradle assemble您将拥有 3 个具有独立类集的 jar。这个解决方案的优点是我们制作了一个具有正确依赖关系的适当 Gradle 多模块项目,而不仅仅是构建 jar 的任务。

Please read Multi-Project Builds.

请阅读多项目构建

回答by JonasOhrnell

We have the same problem at my company, ie. legacy code that is difficult to migrate into a "good" project structure, and the need to build several jars from the same codebase. We decided to define different sourceSets and build each of the sourceSets using standard Gradle.

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

We then use iterators to add jar- and javadoc-tasks for each 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
}

回答by peter_pilgrim

I agree in principal with the accepted answer too. I found a project where the client requires two JAR essentially of the same file except the Manifest is different only by the Class-Path key.

我也原则上同意接受的答案。我发现了一个项目,其中客户端需要两个基本上相同文件的 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/**' ])
}

The same manifest and the source code then is:

相同的清单和源代码是:

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/**'     }

So Grzegorz notation is correct, because the Gradle should know there are two different JAR with GAVs. Multi-module is the preferred option.

所以 Grzegorz 表示法是正确的,因为 Gradle 应该知道有两个不同的带有 GAV 的 JAR。多模块是首选。

compile "uk.gov.acme.secret:acme:1.0"  // CORE
compile "uk.gov.acme.secret:acme-client:1.0"

The only way to configure for this is to use the Multi-Module Gradle project and then add a compile and/or deploy dependency to the core / main project.

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

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

Inside the 'acme-micro-service-webapp' project, this ensures that the dependent 'common:acme-core' is compiled first.

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

PS: I am still trying to figure out a better solution.

PS:我仍在努力找出更好的解决方案。

PS PS: If you are using Maven as well as, it may be possible to hook on the `install' task.

PS PS:如果您同时使用 Maven,则可以挂钩“安装”任务。