Gradle - 如何构建一个包含其他jar文件的lib目录的jar文件?

73
在Gradle中,我该如何将jar包嵌入到输出的jar包中,并放置在lib目录下(具体来说是lib/enttoolkit.jar和lib/mail.jar)?

https://dev59.com/iW445IYBdhLWcg3wappH - jcsahnwaldt Reinstate Monica
这个回答解决了你的问题吗?使用Gradle创建可运行的JAR - Mahozad
我更喜欢这种方式,而不是将数百个类文件压缩到根jar中(这似乎更为常见)。 - Sridhar Sarnobat
10个回答

40

如果在您的项目中有一个目录(我们称之为libs),其中包含所有的jar文件,则只需要执行以下操作:

jar {
    into('lib') {
        from 'libs'
    }
}

我猜这些jar包更可能是某种依赖关系。那么您可以按照以下方式操作:

configurations {
    // configuration that holds jars to copy into lib
    extraLibs
}
dependencies {
    extraLibs 'org.something:something-dep1:version'
    extraLibs 'org.something:something-dep2:version'
}

jar {
    into('lib') {
        from configurations.extraLibs
    }
}

2
如果您想要获取所有依赖项,您还可以从configurations.runtime中复制,这将为您提供默认的“compile”依赖项,省去了创建自己的“extraLibs”配置的麻烦。 - Thunderforge
错误:在类型为org.gradle.api.Project的项目“:app”上找不到参数为[build_bndovev91pu8trwrdngc8qh7i $_run_closure5 @ 3be2321e]的jar()方法。 - naXa stands with Ukraine
2
@Thunderforge 为了获取所有依赖项,我不得不从configurations.runtimeClasspath复制,而不是从configurations.runtime复制,同时避免了需要定义extraLibs的需要。 - Steve Pitchers
我们只需要在 srcDirs 后添加资源即可:processResources{ from(zipTree("src/main/resources/lib/local-jar-name.jar")) } - Lokesh Singal

36

以下内容源自: http://docs.codehaus.org/display/GRADLE/Cookbook#Cookbook-Creatingafatjar

Gradle 0.9:

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

Gradle 0.8:

Gradle 0.8:
jar.doFirst {
    for(file in configurations.compile) {
        jar.merge(file)
    }
}
上述代码片段只包括该项目的编译依赖项,而不包括任何传递性运行时依赖项。如果您还想合并它们,请使用configurations.runtime替换configurations.compile。
编辑:仅选择您需要的jar文件。
创建一个新的配置,例如releaseJars。
configurations {
    releaseJars
}
将您想要添加的JAR包添加到该配置中。
dependencies {
    releaseJars group: 'javax.mail', name: 'mail', version: '1.4'
    //etc
}

然后在上述jar任务中使用该配置。


1
我正在使用0.9rc1版本。所以尝试了你的第一个建议。结果拉进来了很多我不需要的东西..... - phil swenson
1
我知道这并没有回答问题,但这是一个非常好的技巧。谢谢Lucas。 - Basil Musa
2
很好的提示,但完全没有回答那个非常具体的问题。 - Sebastian Ganslandt
2
该链接无法访问,因为"所有Codehaus服务已终止"。 - naXa stands with Ukraine
请更新答案,因为使用“from”需要花括号:参见https://dev59.com/iW445IYBdhLWcg3wappH#4894308。 - radistao
显示剩余2条评论

23

简单:

task copyToLib( type: Copy ) {
    into "$buildDir/libs/lib"
    from configurations.runtime
}

jar { dependsOn copyToLib }

运行它:

$ gradle jar
...
$ tree build/libs

build/libs
├── your-project-0.0.1.BUILD-SNAPSHOT.jar
└── lib
    ├── akka-actor-2.0.jar
    ├── akka-camel-2.0.jar
    ├── ... ... ...
    ├── spring-expression-3.1.0.RELEASE.jar
    └── zmq-2.1.9.jar

1 directory, 46 files

10
这将把库复制到输出目录,但不会将它们复制到JAR文件本身中——这是问题的核心。 - TheOperator

8

我也需要做类似的事情,但是没有完全能够理解Guus和stigkj所建议的方法。在他们的帮助下,我接近了解决此问题(对于我来说,Guus的示例在dependencies { compile { extendsFrom myLibs }}闭包处爆炸了)。

apply plugin: 'groovy'

repositories {
    mavenCentral()
}

configurations {
    // custom config of files we want to include in our fat jar that we send to hadoop
    includeInJar
}

dependencies {
    includeInJar 'org.codehaus.groovy:groovy:1.8.6'

    configurations.compile.extendsFrom(configurations.includeInJar)
}

jar {
    into('lib') {
        println "includeInJar: " + configurations.includeInJar.collect { File file -> file }
        from configurations.includeInJar
    }

}

然后运行gradle jar并检查创建的jar文件,我得到了这个输出,显示我得到的jar文件包含groovy以及所有依赖它的jar文件在“fat jar”中:

%  gradle jar                                                                                                                         
includeInJar: [/Users/tnaleid/.gradle/caches/artifacts-8/filestore/org.codehaus.groovy/groovy/1.8.6/jar/553ca93e0407c94c89b058c482a404427ac7fc72/groovy-1.8.6.jar, /Users/tnaleid/.gradle/caches/artifacts-8/filestore/antlr/antlr/2.7.7/jar/83cd2cd674a217ade95a4bb83a8a14f351f48bd0/antlr-2.7.7.jar, /Users/tnaleid/.gradle/caches/artifacts-8/filestore/asm/asm/3.2/jar/9bc1511dec6adf302991ced13303e4140fdf9ab7/asm-3.2.jar, /Users/tnaleid/.gradle/caches/artifacts-8/filestore/asm/asm-tree/3.2/jar/cd792e29c79d170c5d0bdd05adf5807cf6875c90/asm-tree-3.2.jar, /Users/tnaleid/.gradle/caches/artifacts-8/filestore/asm/asm-commons/3.2/jar/e7a19b8c60589499e35f5d2068d09013030b8891/asm-commons-3.2.jar, /Users/tnaleid/.gradle/caches/artifacts-8/filestore/asm/asm-util/3.2/jar/37ebfdad34d5f1f45109981465f311bbfbe82dcf/asm-util-3.2.jar, /Users/tnaleid/.gradle/caches/artifacts-8/filestore/asm/asm-analysis/3.2/jar/c624956db93975b7197699dcd7de6145ca7cf2c8/asm-analysis-3.2.jar]
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar

BUILD SUCCESSFUL

Total time: 3.387 secs

%  jar tvf build/libs/gradletest.jar                                                                                                  
     0 Mon Mar 12 11:40:00 CDT 2012 META-INF/
    25 Mon Mar 12 11:40:00 CDT 2012 META-INF/MANIFEST.MF
     0 Mon Mar 12 11:40:00 CDT 2012 lib/
5546084 Mon Mar 05 13:13:32 CST 2012 lib/groovy-1.8.6.jar
445288 Mon Mar 05 13:13:38 CST 2012 lib/antlr-2.7.7.jar
 43398 Mon Mar 05 13:13:40 CST 2012 lib/asm-3.2.jar
 21878 Mon Mar 05 13:13:40 CST 2012 lib/asm-tree-3.2.jar
 33094 Mon Mar 05 13:13:40 CST 2012 lib/asm-commons-3.2.jar
 36551 Mon Mar 05 13:13:40 CST 2012 lib/asm-util-3.2.jar
 17985 Mon Mar 05 13:13:40 CST 2012 lib/asm-analysis-3.2.jar

好的,我已经按照描述做了,但是仍然出现“Exception in thread "main" java.lang.NoClassDefFoundError: com/rabbitmq/client/ConnectionFactory”错误。我应该如何设置类路径? - omikron

7

以下代码可尝试使用。它依赖于jar任务,并且是Jar类型

task createJobJar(dependsOn:jar,type:Jar) {
    manifest {
        attributes(
                "Implementation-Title": 'Job '
                ,"Implementation-Version": version
        )
    }
    classifier 'job'
    destinationDir new File("$buildDir")
    into('libs'){
         from configurations.compile
    }
    into('classes'){
        from "$buildDir/classes"
    }
    into('resources'){
        from "$projectDir/src/main/resources"
    }
    into('scripts'){
        from "$projectDir/src/main/scripts"
    }
}

上面的代码将在不同的目录中打包不同的内容。 已测试过适用于gradle 2.2。

6

我需要做与您提出的相同的事情,并使用了这种方法。您可能不需要自定义配置声明,但我需要将本地使用的jar文件与超级构建文件中声明的文件分开。

configurations{
    //declare custom config if necessary, otherwise just use compile
    myLibs
}
dependencies {
    //add lib/*.jar files to myLibs
    myLibs fileTree(dir: 'lib', include: '*.jar')
    compile {
        //set compile configuration to extend from myLibs
        extendsFrom myLibs
    }
}
// task to copy libs to output/lib dir
task copyToLib(type: Copy) {
    into "$buildDir/output/lib"
    from configurations.myLibs
}

jar {
    //include contents of output dir
    from "$buildDir/output"
    manifest {
        //...
    }
}

//set build task to depend on copyToLib
build.dependsOn(copyToLib)

1
我也遇到了同样的问题。我通过以下方式解决:
使用copyToLib将文件复制到lib文件夹中,并通过Class-Path引用依赖项。
ext.mainClass = 'test.main'

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.google.code.gson:gson:2.8.2'
    //....
}

jar {
    from "$buildDir/libs/lib"
    manifest {
        attributes 'Main-Class': 'test.main',
        'Class-Path': configurations.compile.collect { 'lib/'+it.getName() }.join(' ')
    }
}

task copyToLib(type: Copy) {
    into "$buildDir/libs/lib"
    from configurations.compile
}

build.dependsOn(copyToLib)

0

以下是我使用Gradle实现此构建设置的方法。这将构建您的应用程序为Jar,将生成的库jar复制到build/libs下的libs目录中,并配置类路径以包括创建的build/libs/libs目录中的jar。

 plugins {
    id 'application'
 }

 group 'org.pkg'
 version '1.0-SNAPSHOT'

 ext.mainClass = 'org.pkg.MainClass'

 repositories {
    mavenCentral()
    mavenLocal()
 }

 dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile group: 'junit', name: 'junit', version: '4.12'

    implementation group: 'com.mchange', name: 'c3p0', version: '0.9.5.5'
 }
 
 task copyToLib(type: Copy) {
    into "$buildDir/libs/libs"
    // configurations.runtimeClasspath includes maven jars
    from configurations.runtimeClasspath 
 }

 def archiveVersion = "1.0.0"

 task uberJar(type: Jar) {
    archiveClassifier = 'uber'

    from sourceSets.main.output
    // include the copy task
    dependsOn(copyToLib)

    // use onfigurations.runtimeClasspath to collect all the jars
    manifest {
      attributes(
            'Class-Path': configurations.runtimeClasspath.collect { 'libs/' + it.getName() }.join(' '),
            'Main-Class': 'org.pkg.MainClass',
            "Implementation-Title": "Gradle",
            "Implementation-Version": archiveVersion
      )
    }
 }

0
在我的情况下,我需要将根项目 Jar 的内容包含到子项目 Jar 中。因此,为了使其正常工作,可以使用以下模板:
jar{
  manifest{
    attributes 'Main-Class':'<main class>'
  }
  def conf= configurations.find {it.name.equals('compile') }
  File jar= conf.files.find {it.name.contains('<name or part of the name of produced Jar>')}

  FileTree fileTree=zipTree(jar)
  from fileTree
}

我的例子:

jar{
   manifest{
       attributes 'Main-Class':'alexiy.jace.Jace'
   }
   description='Make a runnable API Jar'
   def conf= configurations.find {it.name.equals('compile') }
   File tools= conf.files.find {it.name.contains('Tools')}

   FileTree fileTree=zipTree(tools)
   from fileTree
}

-1
task <taskname>(type: Jar) {
    archiveName 'nameofjar.jar'
    doFirst {
    manifest {
            attributes 'Class-Path': configurations.compile.files.collect{ project.uri(it) }.join(' ')
        }
    }
}

1
这段代码的一些解释可能有助于解释其中的一些具体部分。 - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接