From 79c7d9d72802acc7e4cd3ee4c8bbc863b2dfb4ed Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Sat, 26 Sep 2026 03:30:25 +0900 Subject: [PATCH] [2.x] fix: Honor JAVA_HOME in the Unix launcher (#9825) Problem: The Unix launcher ignores JAVA_HOME and selects Java from PATH when JAVACMD is absent. Solution: Check JAVA_HOME/bin/java before falling back to PATH while preserving explicit --java-home and JAVACMD precedence. Fixes #9794 --- .../src/test/scala/JavaHomeScriptTest.scala | 53 +++++++++++++++++++ .../src/test/scala/ShellScriptUtil.scala | 1 + sbt | 2 + 3 files changed, 56 insertions(+) diff --git a/launcher-package/integration-test/src/test/scala/JavaHomeScriptTest.scala b/launcher-package/integration-test/src/test/scala/JavaHomeScriptTest.scala index 147978116..532a77813 100644 --- a/launcher-package/integration-test/src/test/scala/JavaHomeScriptTest.scala +++ b/launcher-package/integration-test/src/test/scala/JavaHomeScriptTest.scala @@ -9,6 +9,8 @@ package example.test import java.io.File +import sbt.io.IO +import scala.sys.process.{ Process, ProcessLogger } /** * First-hop propagation guard: an explicit `-java-home` must be the JDK the Windows launcher runs @@ -33,4 +35,55 @@ object JavaHomeScriptTest extends verify.BasicTestSuite with ShellScriptUtil: out.exists(_.contains(javaExe)), s"launcher should run $javaExe; command echo was: ${out.mkString(" | ")}" ) + List( + ("JAVA_HOME overrides PATH", "", "home", "", "home"), + ("JAVACMD overrides JAVA_HOME", "command", "home", "", "command"), + ("invalid JAVACMD falls back to JAVA_HOME", "missing", "home", "", "home"), + ("empty JAVA_HOME falls back to PATH", "", "", "", "path"), + ("invalid JAVA_HOME falls back to PATH", "", "missing", "", "path"), + ("invalid JAVACMD and JAVA_HOME fall back to PATH", "missing", "missing", "", "path"), + ("--java-home overrides environment", "command", "home", "option", "option"), + ).foreach: (name, command, home, option, expected) => + test(name): + checkJavaSelection(command, home, option, expected) + + private def checkJavaSelection( + command: String, + home: String, + option: String, + expected: String + ): Unit = + if isWindows then cancel("Bash launcher Java selection") + else + IO.withTemporaryDirectory: dir => + def javaHome(name: String): File = new File(dir, s"$name jdk") + def java(name: String): File = new File(javaHome(name), "bin/java") + List("path", "home", "command", "option").foreach: name => + IO.write( + java(name), + IO.read(new File(javaBinDir, "java")) + .replace("else:\n", s"else:\n print('SELECTED: $name')\n") + ) + Predef.assert(java(name).setExecutable(true)) + IO.write(new File(dir, "build.sbt"), "") + val env = Seq( + "PATH" -> s"${java("path").getParent}${File.pathSeparator}${sys.env("PATH")}", + "JAVACMD" -> (if command.isEmpty then "" else java(command).getAbsolutePath), + "JAVA_HOME" -> (if home.isEmpty then "" else javaHome(home).getAbsolutePath), + "JAVA_OPTS" -> "", + "SBT_OPTS" -> "", + "JAVA_TOOL_OPTIONS" -> "", + "XDG_CONFIG_HOME" -> dir.getAbsolutePath, + "SBT_ETC_FILE" -> new File(dir, "missing-sbtopts").getAbsolutePath, + ) + val args = if option.isEmpty then Seq.empty + else Seq("--java-home", javaHome(option).getAbsolutePath) + val output = scala.collection.mutable.ListBuffer.empty[String] + val exit = Process( + Seq(sbtScript.getAbsolutePath, "--server") ++ args ++ Seq("compile"), + dir, + env* + ).!(ProcessLogger(line => output += line, line => output += line)) + Predef.assert(exit == 0, output.mkString("\n")) + Predef.assert(output.contains(s"SELECTED: $expected"), output.mkString("\n")) end JavaHomeScriptTest diff --git a/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala b/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala index 1eb5986e5..2148ef2c9 100644 --- a/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala +++ b/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala @@ -173,6 +173,7 @@ trait ShellScriptUtil extends BasicTestSuite: envVars("JAVACMD") = new File(javaBinDir, "java").getAbsolutePath() envVars("JAVA_HOME") = javaHomeEnv else + envVars("JAVACMD") = new File(javaBinDir, "java").getAbsolutePath() envVars("PATH") = javaBinDir + File.pathSeparator + path envVars("JAVA_HOME") = javaHomeEnv val cmd = diff --git a/sbt b/sbt index 03ac3baae..800f4ac90 100755 --- a/sbt +++ b/sbt @@ -995,6 +995,8 @@ sbt_file_opts=() if [[ -f "$JAVACMD" ]]; then java_cmd="$JAVACMD" +elif [[ -n "$JAVA_HOME" && -f "$JAVA_HOME/bin/java" ]]; then + java_cmd="$JAVA_HOME/bin/java" fi # Pull in the machine-wide settings configuration.