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}" } } }
额外优化说明
- 移除了参数中的多余双引号:
ProcessBuilder会自动处理带空格的路径等特殊情况,无需手动添加引号。 - 将逻辑放到
doLast闭包:避免在Gradle配置阶段就执行签名命令,符合Gradle的构建生命周期规范。 - 添加超时与错误检查:签名超时或失败时主动抛出异常,终止Task并明确提示错误。
- 使用
jar.absolutePath:替代jar.toPath(),更简洁且兼容性更好。
内容的提问来源于stack exchange,提问作者The Prototype
相关产品推荐
相关产品推荐

