From 1a1db1c8e1df948f984a8f3901c999e11838d0b5 Mon Sep 17 00:00:00 2001 From: ZanDev32 <52073144+ZanDev32@users.noreply.github.com> Date: Thu, 9 Jul 2026 22:13:14 +0700 Subject: [PATCH] fix: preserve exec permissions on jspawnhelper in bundled JDK ([#1537](https://github.com/processing/processing4/issues/1537)) Fix: - Move the permission loop in includeJdk into a doLast block so it runs after the copy, and re-apply the exec bit from the source JDK for files that should be executable. - Extend setExecutablePermissions to also cover the IDE runtime/ path (not just resources/), since the runtime's jspawnhelper had the same issue. - Add JAVA_HOME / JDK_HOME env var support in Platform.getJavaHome(), inside the existing "If the JDK is set in the environment" fallback block, so users can point Processing at a different JDK when the bundle is absent. --- app/build.gradle.kts | 21 +++++++++++++++++---- app/src/processing/app/Platform.java | 12 +++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 791cb0f739..08d1175bad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -144,7 +144,7 @@ dependencies { testImplementation(libs.junitJupiterParams) testRuntimeOnly("org.junit.platform:junit-platform-launcher") - + } tasks.test { @@ -451,9 +451,18 @@ tasks.register("includeJdk") { from(jdkHome) destinationDir = composeResources("jdk").get().asFile - fileTree(destinationDir).files.forEach { file -> - file.setWritable(true, false) - file.setReadable(true, false) + doLast { + val sourceJdkHome = jdkHome.get() + fileTree(destinationDir).files.forEach { file -> + // Copy preserves the source exec bit, but some environments strip it. + // Re-apply exec on files that should be executable. + val sourceFile = File(sourceJdkHome, file.relativeTo(destinationDir).path) + if (sourceFile.canExecute()) { + file.setExecutable(true, false) + } + file.setWritable(true, false) + file.setReadable(true, false) + } } } tasks.register("includeSharedAssets"){ @@ -648,6 +657,10 @@ tasks.register("setExecutablePermissions") { include("**/resources/**/*.dylib") include("**/resources/**/*.so") include("**/resources/**/*.exe") + // Also cover the IDE runtime (jspawnhelper, jexec, bin/*) — same issue + // https://github.com/processing/processing4/issues/1537 + include("**/runtime/**/bin/**") + include("**/runtime/**/lib/**") }.forEach { file -> if (file.isFile) { file.setExecutable(true, false) diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index 3372e3f9bc..31ff169718 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -371,7 +371,7 @@ static public File getContentFile(String name) { } static public File getJavaHome() { - // Get the build in JDK location from the Jetpack Compose resources + // Get the built-in JDK location from the Jetpack Compose resources var resourcesDir = System.getProperty("compose.application.resources.dir"); if(resourcesDir != null) { var jdkFolder = new File(resourcesDir,"jdk"); @@ -381,6 +381,16 @@ static public File getJavaHome() { } // If the JDK is set in the environment, use that. + // if not, fall back to the java.home system property. + for (String envKey : new String[] { "JAVA_HOME", "JDK_HOME" }) { + String envPath = System.getenv(envKey); + if (envPath != null && !envPath.isEmpty()) { + File envHome = new File(envPath); + if (new File(envHome, "bin/java" + (Platform.isWindows() ? ".exe" : "")).canExecute()) { + return envHome; + } + } + } var home = System.getProperty("java.home"); if(home != null){ return new File(home);