69

我在 Gradle 4.6 中有一个简单的项目,并且想制作一个可执行的 JAR。我已经尝试过shadow, gradle-fatjar-plugin, gradle-one-jar,spring-boot-gradle-plugin插件,但它们都没有添加我声明为的依赖项implementation(我没有任何依赖项compile)。它适用于compile例如gradle-one-jar插件,但我想有implementation依赖关系。

4

7 回答 7

115

您可以使用以下代码。

jar {
    manifest {
        attributes(
                'Main-Class': 'com.package.YourClass'
        )
    }
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
 }

请务必替换com.package.YourClass为包含static void main( String args[] ).

这将打包运行时依赖项。如果您需要更多信息,请查看文档。

于 2018-03-14T17:41:25.427 回答
28

根据接受的答案,我需要再添加一行代码:

task fatJar(type: Jar) {
  manifest {
    attributes 'Main-Class': 'com.yourpackage.Main'
  }
  archiveClassifier = "all"
  from {
    configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
  with jar
}

没有这个附加行,它省略了我的源文件,只添加了依赖项:

configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }

对于较新的 gradle (7+),您可能会看到以下错误:

Execution failed for task ':fatJar'.
> Entry [some entry here] is a duplicate but no duplicate handling strategy has been set. Please 
refer to https://docs.gradle.org/7.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy
 for details.

如果发生这种情况,请在任务中添加一个duplicatesStrategy诸如。duplicatesStrategy "exclude"fatJar

同样,对于 Gradle 7+,您只需删除该configuration.compile.collect行,因为它在此版本的 gradle 中不再是有效配置。

于 2020-04-13T23:21:53.377 回答
12

使用Gradle Kotlin DSL可以以类似的方式完成相同的任务:

val jar by tasks.getting(Jar::class) {
    manifest {
        attributes["Main-Class"] = "com.package.YourClass"
    }

    from(configurations
        .runtime
        // .get() // uncomment this on Gradle 6+
        // .files
        .map { if (it.isDirectory) it else zipTree(it) })
}
于 2019-06-22T12:34:42.520 回答
4

from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }

这条线对我来说是必不可少的。

于 2020-05-11T04:47:27.423 回答
1

Kotlin 1.3.72 & JVM 插件,Gradle 6.5.1

所有这些平台的语法都在迅速变化

tasks {
    compileKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }
    compileTestKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }
    val main = sourceSets.main.get()
    //TODO
    register<Jar>("buildFatJar") {
        group = "app-backend"
        dependsOn(build)
        // shouldRunAfter(parent!!.tasks["prepCopyJsBundleToKtor"]) -> This is for incorporating KotlinJS gradle subproject resulting js file.
        manifest {
            attributes["Main-Class"] = "com.app.app.BackendAppKt"
        }

        from(configurations.compileClasspath.get().files.map { if (it.isDirectory) it else zipTree(it) })
        with(jar.get() as CopySpec)
        archiveBaseName.set("${project.name}-fat")
    }
}
于 2020-08-09T23:47:27.710 回答
1

以前的答案现在有点过时了,请参阅此处了解使用 gradle-7.4 的内容: 如何使用 Gradle Kotlin 脚本创建胖 JAR?

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    val dependencies = configurations
        .runtimeClasspath
        .get()
        .map(::zipTree) // OR .map { zipTree(it) }
    from(dependencies)
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
于 2022-02-12T18:30:25.340 回答
1

这里我提供 Kotlin DSL (build.gradle.kts) 的解决方案。
请注意,前 3 个方法修改了JarGradle 的现有任务。

方法 1:将库文件放在结果 JAR 旁边

此方法不需要application或任何其他插件。

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    manifest.attributes["Class-Path"] = configurations
        .runtimeClasspath
        .get()
        .joinToString(separator = " ") { file ->
            "libs/${file.name}"
        }
}

请注意,Java 要求我们为Class-Path属性使用相对 URL。因此,我们不能使用 Gradle 依赖项的绝对路径(这也容易被更改并且在其他系统上不可用)。如果您想使用绝对路径,也许这种解决方法会起作用。

使用以下命令创建 JAR:

./gradlew jar

结果 JAR 将默认创建在build/libs/目录中。

创建 JAR 后,将库 JAR 复制到放置结果 JAR 的libs/子目录中。确保您的库 JAR 文件的文件名中不包含空格(它们的文件名应与${file.name}上述任务中的变量指定的文件名匹配)。

方法 2:在结果 JAR 文件中嵌入库(fat 或 uber JAR)

这种方法也不需要任何 Gradle 插件。

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    val dependencies = configurations
        .runtimeClasspath
        .get()
        .map(::zipTree) // OR .map { zipTree(it) }
    from(dependencies)
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

创建 JAR 与前面的方法完全相同。

方法 3:使用Shadow 插件(创建 fat 或 uber JAR)

plugins {
    id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these configs are reflected for Shadow as well
tasks.jar {
    manifest.attributes["Main-Class"] = "org.example.MainKt"
}

使用以下命令创建 JAR:

./gradlew shadowJar

有关配置插件的更多信息,请参阅Shadow 文档

方法四:新建任务(而不是修改Jar任务)

tasks.create("MyFatJar", Jar::class) {
    group = "my tasks" // OR, for example, "build"
    description = "Creates a self-contained fat JAR of the application that can be run."
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    val dependencies = configurations
        .runtimeClasspath
        .get()
        .map(::zipTree)
    from(dependencies)
    with(tasks.jar.get())
}

运行创建的 JAR

java -jar my-artifact.jar

上述解决方案经过以下测试:

  • 爪哇 17
  • Gradle 7.1(将 Kotlin 1.4.31 用于.kts构建脚本)

有关创建 uber (fat) JARs的信息,请参阅官方Gradle 文档。

有关清单的更多信息,请参阅Oracle Java 文档:使用清单文件

有关和之间的区别tasks.create()tasks.register()请参阅这篇文章

请注意,您的资源文件将自动包含在 JAR 文件中(假设它们放置在/src/main/resources/目录或在构建文件中设置为资源根目录的任何自定义目录中)。要访问应用程序中的资源文件,请使用以下代码(注意/名称开头的 ):

  • 科特林
    val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
    // Alternative ways:
    // val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
    // val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
    // val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
    
  • 爪哇
    var stream = MyClass.class.getResource("/vegetables.txt").openStream();
    // OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
    
    var reader = new BufferedReader(new InputStreamReader(stream));
    var vegetables = reader.lines().collect(Collectors.joining("\n"));
    
于 2022-02-18T17:07:58.630 回答