Skip to content

Commit 3aacd09

Browse files
ctruedenclaude
andcommitted
Ask the java executable for its java.home
The java executable located by jgo is not necessarily at JAVA_HOME/bin: it may be a symlink (Linux alternatives) or a launcher stub (macOS's /usr/bin/java). In such cases, resolve_java() set JAVA_HOME to /usr, where JPype finds no JVM library. Instead, query the java.home property, falling back to the parent of the executable's bin folder if that fails. See #95. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 296dd84 commit 3aacd09

2 files changed

Lines changed: 75 additions & 5 deletions

File tree

‎src/scyjava/_jdk_fetch.py‎

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66

77
import logging
88
import os
9-
from typing import TYPE_CHECKING
9+
import subprocess
10+
from pathlib import Path
1011

1112
from jgo.exec import JavaLocator, JavaSource
1213

1314
import scyjava.config
1415

15-
if TYPE_CHECKING:
16-
from pathlib import Path
17-
1816
_logger = logging.getLogger(__name__)
1917

2018

@@ -59,13 +57,43 @@ def resolve_java(vendor: str | None = None, version: str | None = None) -> None:
5957

6058
# Locate returns path to java executable (e.g., /path/to/java/bin/java)
6159
java_exe = locator.locate()
62-
java_home = java_exe.parent.parent # Navigate from bin/java to JAVA_HOME
60+
java_home = _java_home(java_exe)
6361

6462
_logger.debug(f"java_home -> {java_home}")
6563
_add_to_path(str(java_home / "bin"), front=True)
6664
os.environ["JAVA_HOME"] = str(java_home)
6765

6866

67+
def _java_home(java_exe: Path) -> Path:
68+
"""
69+
Determine the Java installation directory of the given java executable.
70+
71+
The executable is asked for its java.home property, because it is not
72+
necessarily located at JAVA_HOME/bin/java: it might be a symlink (e.g.
73+
/usr/bin/java with Linux alternatives) or a launcher stub (e.g. macOS's
74+
/usr/bin/java, which delegates to the registered JDK).
75+
"""
76+
try:
77+
output = subprocess.run(
78+
[str(java_exe), "-XshowSettings:properties", "-version"],
79+
check=False,
80+
capture_output=True,
81+
text=True,
82+
timeout=30,
83+
).stderr
84+
for line in output.splitlines():
85+
key, sep, value = line.partition("=")
86+
if sep and key.strip() == "java.home":
87+
java_home = Path(value.strip())
88+
if java_home.is_dir():
89+
return java_home
90+
except (OSError, subprocess.SubprocessError) as e:
91+
_logger.debug(f"Failed to query java.home from {java_exe}: {e}")
92+
93+
_logger.debug(f"Could not query java.home from {java_exe}; assuming bin/java")
94+
return java_exe.parent.parent
95+
96+
6997
def _add_to_path(path: Path | str, front: bool = False) -> None:
7098
"""Add a path to the PATH environment variable.
7199

‎tests/test_jdk_fetch.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Tests for functions in _jdk_fetch submodule.
3+
"""
4+
5+
import os
6+
7+
import pytest
8+
9+
from scyjava._jdk_fetch import _java_home
10+
11+
posix_only = pytest.mark.skipif(os.name == "nt", reason="uses a shell script")
12+
13+
14+
def _fake_java(path, stderr):
15+
path.parent.mkdir(parents=True, exist_ok=True)
16+
path.write_text(f"#!/bin/sh\ncat >&2 <<'EOF'\n{stderr}\nEOF\n")
17+
path.chmod(0o755)
18+
return path
19+
20+
21+
@posix_only
22+
def test_java_home_from_launcher_stub(tmp_path):
23+
# E.g. macOS's /usr/bin/java, which is not located inside its Java home.
24+
real_home = tmp_path / "jdk" / "Contents" / "Home"
25+
real_home.mkdir(parents=True)
26+
java = _fake_java(
27+
tmp_path / "usr" / "bin" / "java",
28+
f"Property settings:\n java.home = {real_home}\n"
29+
' java.version = 25.0.1\n\nopenjdk version "25.0.1"',
30+
)
31+
assert _java_home(java) == real_home
32+
33+
34+
@posix_only
35+
def test_java_home_fallback(tmp_path):
36+
java = _fake_java(tmp_path / "jdk" / "bin" / "java", "no properties here")
37+
assert _java_home(java) == tmp_path / "jdk"
38+
39+
40+
def test_java_home_missing_executable(tmp_path):
41+
java = tmp_path / "jdk" / "bin" / "java"
42+
assert _java_home(java) == tmp_path / "jdk"

0 commit comments

Comments
 (0)