You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle 8.6中通过Gradle Task调用jarsigner签名JAR失败求助

问题描述

环境:Gradle 8.6

尝试通过Gradle Task调用jarsigner对JAR包签名,操作步骤如下:

  • 生成证书:执行命令keytool -genkey -alias test -validity 3650 -keyalg RSA -keystore test.jks,设置密码并填写必要信息。
  • 将证书放置在Gradle根目录:C:\Users\User\.gradle\test.jks
  • 配置全局gradle.properties文件:
org.gradle.warning.mode=all
testKeyStore=C:/Users/User/.gradle/test.jks
testKeyStoreAlias=test
testKeyStorePassword=test
testKeyStoreKeyPassword=test
tsa=http://time.certum.pl
  • 创建Gradle Task:
tasks.register("signJars") {

    onlyIf { // Skip the task if our secret data isn't available
        project.hasProperty("testKeyStore") &&
                project.hasProperty("testKeyStoreAlias") &&
                project.hasProperty("testKeyStorePassword") &&
                project.hasProperty("testKeyStoreKeyPassword") &&
                project.hasProperty("tsa")
    }

    final def testKeyStore = findProperty("testKeyStore")
    final def testKeyStoreAlias = findProperty("testKeyStoreAlias")
    final def testKeyStorePassword = findProperty("testKeyStorePassword")
    final def testKeyStoreKeyPassword = findProperty("testKeyStoreKeyPassword")
    final def tsa = findProperty("tsa")

    for (jar in fileTree(dir: "$buildDir/libs", include: '*.jar')) {

        final List<String> args = Arrays.asList(
                "jarsigner",
                "-keystore",
                "\"${testKeyStore}\"",
                "-storepass",
                "\"${testKeyStorePassword}\"",
                "-keypass",
                "\"${testKeyStoreKeyPassword}\"",
                "-tsa",
                "\"${tsa}\"",
                "-verbose",
                "\"${jar.toPath()}\"",
                "\"${testKeyStoreAlias}\""
        ) as List<String>

        final command = args.join(" ");
        println command

        def builder = new ProcessBuilder(command)
        builder.inheritIO()
        def process = builder.start()
        def finished = process.waitFor(15, TimeUnit.SECONDS)

        println "Finished: ${finished}"
        println "Exit value: ${process.exitValue()}"
        println "Output: ${process.text}"
    }
}

运行该Task后收到错误提示:

Cannot run program "jarsigner -keystore "C:\Users\User\.gradle\test.jks" -storepass "test" -keypass "test" -tsa "http://time.certum.pl" -verbose "E:\full\path\to\project\Project\build\libs\Test.jar" "test"": CreateProcess error=2, The system cannot find the file specified

关键现象:将打印出的命令复制到Windows PowerShell或IDEA终端中执行,能正常完成签名,但Gradle Task内调用就失败。

解决方案

错误原因

你把整个命令拼接成单个字符串传给ProcessBuilder,它会将整个字符串当作单个可执行文件的路径去查找,而非识别为「命令+参数」的组合。Windows系统找不到名为jarsigner -keystore ...的文件,因此报错。而终端(PowerShell/IDEA)会自动拆分命令与参数,所以能正常执行。

修复后的Task代码

tasks.register("signJars") {

    onlyIf { 
        project.hasProperty("testKeyStore") &&
                project.hasProperty("testKeyStoreAlias") &&
                project.hasProperty("testKeyStorePassword") &&
                project.hasProperty("testKeyStoreKeyPassword") &&
                project.hasProperty("tsa")
    }

    final def testKeyStore = findProperty("testKeyStore")
    final def testKeyStoreAlias = findProperty("testKeyStoreAlias")
    final def testKeyStorePassword = findProperty("testKeyStorePassword")
    final def testKeyStoreKeyPassword = findProperty("testKeyStoreKeyPassword")
    final def tsa = findProperty("tsa")
    // 通过JAVA_HOME获取jarsigner完整路径,避免PATH配置问题
    final def jarsignerPath = "${System.getenv('JAVA_HOME')}/bin/jarsigner"

    doLast { // 将签名逻辑放到doLast,遵循Gradle生命周期规范,避免配置阶段执行命令
        for (jar in fileTree(dir: "$buildDir/libs", include: '*.jar')) {
            final List<String> args = Arrays.asList(
                    jarsignerPath,
                    "-keystore",
                    testKeyStore,
                    "-storepass",
                    testKeyStorePassword,
                    "-keypass",
                    testKeyStoreKeyPassword,
                    "-tsa",
                    tsa,
                    "-verbose",
                    jar.absolutePath,
                    testKeyStoreAlias
            )

            println "执行命令:" + args.join(" ")

            def builder = new ProcessBuilder(args)
            builder.inheritIO()
            def process = builder.start()
            def finished = process.waitFor(15, java.util.concurrent.TimeUnit.SECONDS)

            if (!finished) {
                process.destroyForcibly()
                throw new GradleException("签名超时")
            }
            if (process.exitValue() != 0) {
                throw new GradleException("签名失败,退出码:${process.exitValue()}")
            }

            println "签名完成:${jar.name}"
        }
    }
}

额外优化说明

  1. 移除了参数中的多余双引号:ProcessBuilder会自动处理带空格的路径等特殊情况,无需手动添加引号。
  2. 将逻辑放到doLast闭包:避免在Gradle配置阶段就执行签名命令,符合Gradle的构建生命周期规范。
  3. 添加超时与错误检查:签名超时或失败时主动抛出异常,终止Task并明确提示错误。
  4. 使用jar.absolutePath:替代jar.toPath(),更简洁且兼容性更好。

内容的提问来源于stack exchange,提问作者The Prototype

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:50:16