简体   繁体   English

使用 Gradle 在清单中添加类路径

[英]Add classpath in manifest using Gradle

I would like my Gradle build script to add the complete Classpath to the manifest file contained in JAR file created after the build.我希望我的 Gradle 构建脚本将完整的类路径添加到构建后创建的 JAR 文件中包含的清单文件中。

Example:例子:

Manifest-Version: 1.0
Class-Path: MyProject.jar SomeLibrary.jar AnotherLib.jar

My build script already add some information to the manifest this way:我的构建脚本已经通过这种方式向清单添加了一些信息:

jar {
    manifest {
        attributes("Implementation-Title": project.name,
            "Implementation-Version": version,
            "Main-Class": mainClassName,
    }
}

How do I get the list of dependencies to add to the manifest?如何获取要添加到清单的依赖项列表?


This page of Java tutorials describes more in detail how and why adding classpath to the manifest: Adding Classes to the JAR File's Classpath Java 教程的这一页更详细地描述了如何以及为什么将类路径添加到清单: Adding Classes to the JAR File's Classpath

Found a solution on Gradle's forum:在 Gradle 的论坛上找到了一个解决方案:

jar {
  manifest {
    attributes(
      "Class-Path": configurations.compile.collect { it.getName() }.join(' '))
  }
}

Source: Manifest with Classpath in Jar Task for Subprojects来源: 子项目的 Jar 任务中带有类路径的清单

In the latest versions of gradle, compile and runtime becomes deprecated.在最新版本的 gradle 中, compileruntime被弃用。 Instead, use runtimeClasspath as follows:相反,使用runtimeClasspath如下:

'Class-Path': configurations.runtimeClasspath.files.collect { it.getName() }.join(' ')

EDIT:编辑:

Note that if you are using Kotlin DSL, you can configure the manifest as follows:请注意,如果您使用的是 Kotlin DSL,则可以按如下方式配置清单:

 
 
 
 
  
  
  configure<JavaPluginConvention> { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 manifest { attributes( "Manifest-Version" to "1.0", "Main-Class" to "io.fouad.AppLauncher") } }
 
 
 

tasks.withType(Jar::class) {
    manifest {
        attributes["Manifest-Version"] = "1.0"
        attributes["Main-Class"] = "io.fouad.AppLauncher"
    }
}

Place this at the end of the buid.gradle file.将其放在buid.gradle文件的末尾。 Change the com.example.Main to your own Main class.com.example.Main更改为您自己的 Main 类。

jar {
    doFirst {
        manifest {
            if (!configurations.compile.isEmpty()) {
                attributes(
                        'Class-Path': configurations.compile.collect{it.toURI().toString()}.join(' '),
                        'Main-Class': 'com.example.Main')
            }
        }
    }
}

The top answers helped me a lot.最重要的答案对我帮助很大。 Here is what worked for me:这是对我有用的:

jar {
    manifest {
        attributes "Main-Class": "your.package.classWithMain"
        attributes "Class-Path": configurations.compile.collect { it.absolutePath }.join(" ")
    }
}

So, instead of name, I had to use absolutePath.因此,我必须使用绝对路径而不是名称。 This may or may not work for you.这可能适合您,也可能不适合。 Some suggest using runtime instead of compile.有些人建议使用运行时而不是编译。 I used compile because, I have a compile section in dependencies in my build.gradle.我使用 compile 是因为,我的 build.gradle 中的依赖项中有一个 compile 部分。 So, the jar step picks up dependencies from there.因此,jar 步骤从那里获取依赖项。 The best thing to do is pick up something that you think will work, do a gradle build, then find the JAR file and expand it to find the META-INF/MANIFEST.MF file.最好的做法是选择您认为可行的内容,进行 gradle 构建,然后找到 JAR 文件并展开它以找到 META-INF/MANIFEST.MF 文件。 You should be able to see all the directories separated by spaces.您应该能够看到所有以空格分隔的目录。 If not, you should try something different.如果没有,你应该尝试不同的东西。 Autocomplete feature of your IDE should be helpful in seeing what all methods or fields are available under configurations/compile etc. All this can be done easily in IntelliJ. IDE 的自动完成功能应该有助于查看在配置/编译等下所有方法或字段可用的内容。所有这些都可以在 IntelliJ 中轻松完成。

Oh.. and if you want to see where the library JARs are physically located on your disk, right click on your project->open module settings->Libraries and then click on any library.哦.. 如果您想查看库 JAR 在磁盘上的物理位置,请右键单击您的项目-> 打开模块设置-> 库,然后单击任何库。

This is another solution for Kotlin DSL (build.gradle.kts).这是Kotlin DSL (build.gradle.kts) 的另一个解决方案。
When running your app, the library files are supposed to be in libs/ subdirectory of the app.运行您的应用程序时,库文件应该位于应用程序的libs/子目录中。

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

I know this is likely trivial for the groovy people here, but in my case, I wanted to change the location of the Class-Path in the manifest file depending on whether I was going to run in the production environment or local environment.我知道这对于这里的 groovy 人来说可能是微不足道的,但就我而言,我想根据我是要在生产环境还是本地环境中运行来更改清单文件中Class-Path的位置。 I did this by making my build.gradle 's jar section look like this:我通过使build.gradle的 jar 部分看起来像这样来做到这一点:

jar {
  from configurations.runtime
  manifest {
    attributes ('Main-Class': 'com.me.Main',
                'Class-Path': configurations.runtime.files.collect { jarDir+"/$it.name" }.join(' ')
               )
  }
}

In this case, the argument to gradle build is passed like so:在这种情况下, gradle build的参数是这样传递的:

$ gradle build -PjarDir="/opt/silly/path/"

I managed to create a custom Jar file with its own manifest file like so:我设法使用自己的清单文件创建了一个自定义 Jar 文件,如下所示:

task createJar(type : Jar) {
    manifest {
        attributes(
            'Manifest-Version': "1.0",
            'Main-Class': "org.springframework.boot.loader.JarLauncher",
            'Start-Class': "com.my.app.AppApplication",
            'Spring-Boot-Version': "2.2.4.RELEASE",
            'Spring-Boot-Classes': "BOOT-INF/classes/",
            'Spring-Boot-Lib': "BOOT-INF/lib/"
        )
    }
    def originDir = file("${buildDir}/unpacked")
    def destinationFile = "${buildDir}/repackaged/${project.name}-${version}"
    entryCompression ZipEntryCompression.STORED // no compression in case there are files in BOOT-INF/lib
    archiveName destinationFile
    from originDir
    archiveFile
}

I had a similar yet not identical problem.我有一个类似但不完全相同的问题。 I was publishing my lib jar L in the artifactory, and later fetching it as a dependency of module M, but the transitive dependencies, the ones which L need for compile and runtime, did not arrive with it.我在 artifactory 中发布了我的 lib jar L,然后将它作为模块 M 的依赖项获取,但是传递依赖项,即 L 编译和运行时所需的那些,并没有随它一起到达。 It took me sometime to realize that my jar was published into the artifactory with an empty pom file, hence gradle was not able to know which are L's transitive dependencies to be fetched.我花了一些时间才意识到我的 jar 是用一个空的 pom 文件发布到 artifactory 中的,因此 gradle 无法知道要获取 L 的哪些传递依赖项。 The missing piece was an instruction, in the L's build.gradle, to publish the pom.缺少的部分是 L 的 build.gradle 中的一条指令,用于发布 pom。 As often with gradle, the connection between the name of the insturction, and its meaning, is completely:与 gradle 一样,指令名称与其含义之间的联系完全是:

apply plugin: 'maven'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "file://localhost/tmp/myRepo/")
        }
    }
} 

Source: uploading_to_maven_repositories来源: uploading_to_maven_repositories

Looks like gradle has evolved.看起来 gradle 已经进化了。 This is another answer that looks similar to others, but there is a key difference: if you use a new keyword implementation in the dependencies , none of the other answers will work and you'll get an empty class path这是另一个看起来与其他答案相似的答案,但有一个关键区别:如果您在dependencies使用新关键字implementation ,则其他答案都不起作用,您将获得一个空的类路径

dependencies {
    // ensure the keyword here matches what 
    // you have in the jar->manifest->attributes
    // that is "implementation"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
    // ...
}

// by default, implementation cannot be referenced, 
// this allows us to use it below
project.configurations.implementation.setCanBeResolved(true)

jar{
    manifest {
        attributes(
                "Main-Class": "app.Program",                
                "Class-Path":  configurations.implementation.collect { it.name }.join(' ')              
        )
    }
    dependsOn ('dependencies')
}

If your project has external library dependencies, you could copy the jars to a folder and add the classpath entries in the manifest.如果您的项目具有外部库依赖项,您可以将 jar 复制到一个文件夹并在清单中添加类路径条目。

 def dependsDir = "${buildDir}/libs/dependencies/"
    task copyDependencies(type: Copy) {
    from configurations.compile
    into "${dependsDir}"
   }
task createJar(dependsOn: copyDependencies, type: Jar) {
  
    manifest {
        attributes('Main-Class': 'com.example.gradle.App',
                'Class-Path': configurations.compile.collect { 'dependencies/' + it.getName() }.join(' ')
        )
    }
    with jar
}

More details can be read here更多细节可以在这里阅读

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM