mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 20:15:20 +00:00
Fix Java Gradle import JAVA_HOME fallback (#1657)
This commit is contained in:
1 parent
6171c484d6
commit
9ccd5f02a2
4 files changed
+185
-20
No files matched your search
@@ -62,6 +62,8 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
- `JuliaLanguageServer`: Fix the stdio MCP server exiting right after `initialize` ("tools fetch failed")
|
||||
when `julia` is enabled. #1577
|
||||
- `Java`: invalidate JDTLS workspace cache when Java import settings change #1576
|
||||
- `Java`: use `JAVA_HOME` for Gradle import when `use_system_java_home` is enabled and `gradle_java_home`
|
||||
is unset. #1657
|
||||
- Improve quoting of arguments in shell executions
|
||||
- Add **LaTeX** support (experimental) via [texlab](https://github.com/latex-lsp/texlab).
|
||||
- PHP: add support for PHPantom as alternative to the already supported PHP LS #1554.
|
||||
|
||||
@@ -695,8 +695,8 @@ The following settings are supported for the Java language server:
|
||||
| `maven_user_settings` | `~/.m2/settings.xml` | Path to Maven `settings.xml` |
|
||||
| `gradle_user_home` | `~/.gradle` | Path to Gradle user home directory |
|
||||
| `gradle_wrapper_enabled` | `false` | Use the project's Gradle wrapper (`gradlew`) instead of the bundled Gradle distribution. Enable this for projects with custom plugins or repositories. |
|
||||
| `gradle_java_home` | `null` | Path to the JDK used by Gradle. When unset, Gradle uses the bundled JRE. |
|
||||
| `use_system_java_home` | `false` | Use the system's `JAVA_HOME` environment variable for JDTLS itself. Enable this if your project requires a specific JDK vendor or version for Gradle's JDK checks. |
|
||||
| `gradle_java_home` | `null` | Path to the JDK used by Gradle. When unset, Gradle uses `JAVA_HOME` if `use_system_java_home` is enabled and `JAVA_HOME` is set; otherwise it falls back to Serena's bundled JRE. |
|
||||
| `use_system_java_home` | `false` | Use the system's `JAVA_HOME` environment variable for JDTLS itself and, when `gradle_java_home` is unset, Gradle import. Enable this if your project requires a specific JDK vendor or version for Gradle's JDK checks. |
|
||||
| `gradle_version` | `8.14.2` | (vscode-java mode only) Override the Gradle distribution version Serena downloads by default. |
|
||||
| `vscode_java_version` | `1.54.0-923` | (vscode-java mode only) Override the bundled `vscode-java` runtime bundle version Serena downloads by default. |
|
||||
| `intellicode_version` | `1.2.30` | (vscode-java mode only) Override the IntelliCode VSIX version Serena downloads by default. |
|
||||
|
||||
@@ -180,8 +180,10 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
- maven_user_settings: Path to Maven settings.xml file (default: ~/.m2/settings.xml)
|
||||
- gradle_user_home: Path to Gradle user home directory (default: ~/.gradle)
|
||||
- gradle_wrapper_enabled: Whether to use the project's Gradle wrapper (default: false)
|
||||
- gradle_java_home: Path to JDK for Gradle (default: null, uses bundled JRE)
|
||||
- use_system_java_home: Whether to use the system's JAVA_HOME for JDTLS itself (default: false)
|
||||
- gradle_java_home: Path to JDK for Gradle (default: null, falls back to JAVA_HOME when
|
||||
use_system_java_home is true, then the bundled JRE)
|
||||
- use_system_java_home: Whether to use the system's JAVA_HOME for JDTLS itself and, when
|
||||
gradle_java_home is unset, Gradle import (default: false)
|
||||
- jdtls_xmx: Maximum heap size for the JDTLS server JVM (default: "3G")
|
||||
- jdtls_xms: Initial heap size for the JDTLS server JVM (default: "100m")
|
||||
- intellicode_xmx: Maximum heap size for the IntelliCode embedded JVM (default: "1G")
|
||||
@@ -217,7 +219,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
# gradle_user_home: 'C:\\Users\\YourName\\.gradle' # Windows (use single quotes!)
|
||||
gradle_wrapper_enabled: true # set to true for projects with custom plugins/repositories
|
||||
gradle_java_home: "/path/to/jdk" # set to override Gradle's JDK
|
||||
use_system_java_home: true # set to true to use system JAVA_HOME for JDTLS
|
||||
use_system_java_home: true # set to true to use system JAVA_HOME for JDTLS and Gradle fallback
|
||||
jdtls_xmx: "3G" # maximum heap size for the JDTLS server JVM
|
||||
jdtls_xms: "100m" # initial heap size for the JDTLS server JVM
|
||||
intellicode_xmx: "1G" # maximum heap size for the IntelliCode embedded JVM
|
||||
@@ -904,6 +906,40 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
log.info(f"Using bundled JRE for JDTLS: {java_home}")
|
||||
return {"syntaxserver": "false", "JAVA_HOME": java_home}
|
||||
|
||||
def _resolve_gradle_java_home(self) -> str:
|
||||
"""
|
||||
Resolve the Java home configured for JDTLS Gradle import settings.
|
||||
|
||||
:return: Java home selected from explicit Gradle override, system ``JAVA_HOME`` when enabled, or bundled runtime.
|
||||
:raises FileNotFoundError: If an explicitly configured ``gradle_java_home`` path does not exist.
|
||||
"""
|
||||
# explicit Gradle override...
|
||||
gradle_java_home = self._custom_settings.get("gradle_java_home")
|
||||
if gradle_java_home is not None:
|
||||
if not os.path.exists(gradle_java_home):
|
||||
error_msg = (
|
||||
f"Gradle Java home not found: {gradle_java_home}. "
|
||||
f"Fix: update path in ~/.serena/serena_config.yml (ls_specific_settings -> java -> gradle_java_home), "
|
||||
f"or remove the setting to use JAVA_HOME or the bundled JRE fallback"
|
||||
)
|
||||
log.error(error_msg)
|
||||
raise FileNotFoundError(error_msg)
|
||||
log.info(f"Using Gradle Java home from custom location: {gradle_java_home}")
|
||||
return gradle_java_home
|
||||
|
||||
# system JAVA_HOME fallback...
|
||||
use_system_java_home = self._custom_settings.get("use_system_java_home", False)
|
||||
if use_system_java_home:
|
||||
system_java_home = os.environ.get("JAVA_HOME")
|
||||
if system_java_home:
|
||||
log.info(f"Using system JAVA_HOME for Gradle: {system_java_home}")
|
||||
return system_java_home
|
||||
log.warning("use_system_java_home is set but JAVA_HOME is not set in environment, falling back to bundled JRE for Gradle")
|
||||
|
||||
# bundled runtime fallback...
|
||||
log.info(f"Using bundled JRE for Gradle: {self.runtime_dependency_paths.jre_path}")
|
||||
return self.runtime_dependency_paths.jre_path
|
||||
|
||||
def _create_base_initialize_params(self) -> dict:
|
||||
"""
|
||||
Returns the initialize parameters for the EclipseJDTLS server.
|
||||
@@ -977,20 +1013,8 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
f"Gradle wrapper {'enabled' if gradle_wrapper_enabled else 'disabled'} (configurable via ls_specific_settings -> java -> gradle_wrapper_enabled)"
|
||||
)
|
||||
|
||||
# Gradle Java home: default to None, which means the bundled JRE is used
|
||||
gradle_java_home = self._custom_settings.get("gradle_java_home")
|
||||
if gradle_java_home is not None:
|
||||
if not os.path.exists(gradle_java_home):
|
||||
error_msg = (
|
||||
f"Gradle Java home not found: {gradle_java_home}. "
|
||||
f"Fix: update path in ~/.serena/serena_config.yml (ls_specific_settings -> java -> gradle_java_home), "
|
||||
f"or remove the setting to use the bundled JRE"
|
||||
)
|
||||
log.error(error_msg)
|
||||
raise FileNotFoundError(error_msg)
|
||||
log.info(f"Using Gradle Java home from custom location: {gradle_java_home}")
|
||||
else:
|
||||
log.info(f"Using bundled JRE for Gradle: {self.runtime_dependency_paths.jre_path}")
|
||||
# Gradle Java home: explicit setting, system JAVA_HOME when requested, then bundled runtime.
|
||||
gradle_java_home = self._resolve_gradle_java_home()
|
||||
|
||||
initialize_params = {
|
||||
"locale": "en",
|
||||
@@ -1312,7 +1336,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
# ./gradlew wrapper or a system-installed Gradle via its standard discovery rules.
|
||||
if self.runtime_dependency_paths.gradle_path is not None:
|
||||
gradle_settings["home"] = self.runtime_dependency_paths.gradle_path
|
||||
gradle_settings["java"] = {"home": gradle_java_home if gradle_java_home is not None else self.runtime_dependency_paths.jre_path}
|
||||
gradle_settings["java"] = {"home": gradle_java_home}
|
||||
return initialize_params
|
||||
|
||||
def _start_server(self) -> None:
|
||||
|
||||
@@ -21,6 +21,7 @@ from solidlsp.language_servers.eclipse_jdtls import (
|
||||
JDTLS_CONFIG_DIR_BY_PLATFORM,
|
||||
JDTLS_MIN_JDK_VERSION,
|
||||
EclipseJDTLS,
|
||||
RuntimeDependencyPaths,
|
||||
)
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
@@ -54,6 +55,144 @@ def _make_fake_jdtls_install(
|
||||
return root
|
||||
|
||||
|
||||
def _make_runtime_dependency_paths(root: Path) -> RuntimeDependencyPaths:
|
||||
"""
|
||||
Build minimal runtime dependency paths for initialize-parameter tests.
|
||||
|
||||
:param root: Root directory under which fake runtime files are created.
|
||||
:return: Runtime paths sufficient for ``EclipseJDTLS._create_base_initialize_params``.
|
||||
"""
|
||||
jre_home = root / "jre-home"
|
||||
jre_home.mkdir(parents=True)
|
||||
jre_bin = jre_home / "bin"
|
||||
jre_bin.mkdir()
|
||||
jre_path = jre_bin / _JAVA_EXE_NAME
|
||||
jre_path.touch()
|
||||
|
||||
gradle_path = root / "gradle"
|
||||
gradle_path.mkdir()
|
||||
|
||||
launcher = root / "jdtls-launcher.jar"
|
||||
launcher.touch()
|
||||
config = root / "config"
|
||||
config.mkdir()
|
||||
lombok = root / "lombok.jar"
|
||||
lombok.touch()
|
||||
|
||||
return RuntimeDependencyPaths(
|
||||
jre_path=str(jre_path),
|
||||
jre_home_path=str(jre_home),
|
||||
jdtls_launcher_jar_path=str(launcher),
|
||||
jdtls_readonly_config_path=str(config),
|
||||
lombok_jar_path=str(lombok),
|
||||
gradle_path=str(gradle_path),
|
||||
)
|
||||
|
||||
|
||||
def _make_uninitialized_jdtls(
|
||||
repository_root: Path, custom_settings: dict[str, object], runtime_dependency_paths: RuntimeDependencyPaths
|
||||
) -> EclipseJDTLS:
|
||||
"""
|
||||
Build an EclipseJDTLS instance without starting dependency setup or JDTLS.
|
||||
|
||||
:param repository_root: Repository root reported to initialization-parameter generation.
|
||||
:param custom_settings: Java language-server settings to attach to the instance.
|
||||
:param runtime_dependency_paths: Runtime paths to attach to the instance.
|
||||
:return: Partially initialized server suitable for pure parameter-generation tests.
|
||||
"""
|
||||
server = object.__new__(EclipseJDTLS)
|
||||
server.repository_root_path = str(repository_root)
|
||||
server._custom_settings = SolidLSPSettings.CustomLSSettings(custom_settings)
|
||||
server.runtime_dependency_paths = runtime_dependency_paths
|
||||
return server
|
||||
|
||||
|
||||
def _gradle_java_home(initialize_params: dict) -> str:
|
||||
"""
|
||||
Return the Gradle Java home from initialize parameters.
|
||||
|
||||
:param initialize_params: JDTLS initialize-parameter payload.
|
||||
:return: Configured Gradle Java home.
|
||||
"""
|
||||
return initialize_params["initializationOptions"]["settings"]["java"]["import"]["gradle"]["java"]["home"]
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Gradle Java-home initialize settings
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestGradleJavaHomeInitializeSettings:
|
||||
def test_explicit_gradle_java_home_takes_precedence_over_java_home(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Verify explicit Gradle Java home wins over system ``JAVA_HOME``."""
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
runtime_paths = _make_runtime_dependency_paths(tmp_path / "runtime")
|
||||
explicit_gradle_jdk = tmp_path / "explicit-gradle-jdk"
|
||||
explicit_gradle_jdk.mkdir()
|
||||
env_jdk = tmp_path / "env-jdk"
|
||||
env_jdk.mkdir()
|
||||
monkeypatch.setenv("JAVA_HOME", str(env_jdk))
|
||||
|
||||
server = _make_uninitialized_jdtls(
|
||||
repo,
|
||||
{"gradle_java_home": str(explicit_gradle_jdk), "use_system_java_home": True},
|
||||
runtime_paths,
|
||||
)
|
||||
|
||||
assert _gradle_java_home(server._create_base_initialize_params()) == str(explicit_gradle_jdk)
|
||||
|
||||
def test_uses_java_home_for_gradle_when_system_java_home_enabled(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Verify Gradle import uses system ``JAVA_HOME`` when the system setting is enabled."""
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
runtime_paths = _make_runtime_dependency_paths(tmp_path / "runtime")
|
||||
env_jdk = tmp_path / "env-jdk"
|
||||
env_jdk.mkdir()
|
||||
monkeypatch.setenv("JAVA_HOME", str(env_jdk))
|
||||
|
||||
server = _make_uninitialized_jdtls(repo, {"use_system_java_home": True}, runtime_paths)
|
||||
|
||||
assert _gradle_java_home(server._create_base_initialize_params()) == str(env_jdk)
|
||||
|
||||
def test_uses_bundled_runtime_for_gradle_when_system_java_home_disabled(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Verify Gradle import uses the bundled runtime when system Java home is disabled."""
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
runtime_paths = _make_runtime_dependency_paths(tmp_path / "runtime")
|
||||
env_jdk = tmp_path / "env-jdk"
|
||||
env_jdk.mkdir()
|
||||
monkeypatch.setenv("JAVA_HOME", str(env_jdk))
|
||||
|
||||
server = _make_uninitialized_jdtls(repo, {"use_system_java_home": False}, runtime_paths)
|
||||
|
||||
assert _gradle_java_home(server._create_base_initialize_params()) == runtime_paths.jre_path
|
||||
|
||||
def test_uses_bundled_runtime_for_gradle_when_requested_java_home_is_unset(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Verify Gradle import falls back to the bundled runtime when ``JAVA_HOME`` is unset."""
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
runtime_paths = _make_runtime_dependency_paths(tmp_path / "runtime")
|
||||
monkeypatch.delenv("JAVA_HOME", raising=False)
|
||||
|
||||
server = _make_uninitialized_jdtls(repo, {"use_system_java_home": True}, runtime_paths)
|
||||
|
||||
assert _gradle_java_home(server._create_base_initialize_params()) == runtime_paths.jre_path
|
||||
|
||||
def test_missing_explicit_gradle_java_home_raises(self, tmp_path: Path) -> None:
|
||||
"""Verify missing explicit Gradle Java home remains a configuration error."""
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
runtime_paths = _make_runtime_dependency_paths(tmp_path / "runtime")
|
||||
|
||||
server = _make_uninitialized_jdtls(repo, {"gradle_java_home": str(tmp_path / "missing-jdk")}, runtime_paths)
|
||||
|
||||
with pytest.raises(FileNotFoundError, match="Gradle Java home not found"):
|
||||
server._create_base_initialize_params()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# _resolve_launcher_jar
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user