Skip to content

Commit 717d36d

Browse files
committed
Select suitable JDK to launch Gradle.
- When the Gradle Java home preference is not set, and the default JDK is not compatible with the project Gradle, try to find compatible JDK if there is one available. Signed-off-by: Sheng Chen <sheche@microsoft.com>
1 parent a01129c commit 717d36d

File tree

3 files changed

+178
-10
lines changed

3 files changed

+178
-10
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java

+51
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException {
199199
if (!applies(monitor)) {
200200
return;
201201
}
202+
inferGradleJavaHome(rootFolder.toPath(), monitor);
202203
int projectSize = directories.size();
203204
SubMonitor subMonitor = SubMonitor.convert(monitor, projectSize + 1);
204205
subMonitor.setTaskName(IMPORTING_GRADLE_PROJECTS);
@@ -291,6 +292,49 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException {
291292
subMonitor.done();
292293
}
293294

295+
private void inferGradleJavaHome(Path projectFolder, IProgressMonitor monitor) {
296+
if (StringUtils.isNotBlank(getPreferences().getGradleJavaHome())) {
297+
return;
298+
}
299+
300+
File javaHome = getJavaHome(getPreferences());
301+
String javaVersion;
302+
if (javaHome == null) {
303+
javaVersion = System.getProperty("java.version");
304+
} else {
305+
StandardVMType type = new StandardVMType();
306+
javaVersion = type.readReleaseVersion(javaHome);
307+
}
308+
if (StringUtils.isBlank(javaVersion)) {
309+
// return if failed to get java version.
310+
return;
311+
}
312+
313+
BuildConfiguration build = getBuildConfiguration(projectFolder, true);
314+
GradleBuild gradleBuild = GradleCore.getWorkspace().createBuild(build);
315+
String gradleVersionString = null;
316+
try {
317+
BuildEnvironment environment = gradleBuild.withConnection(connection -> connection.getModel(BuildEnvironment.class), monitor);
318+
GradleEnvironment gradleEnvironment = environment.getGradle();
319+
gradleVersionString = gradleEnvironment.getGradleVersion();
320+
} catch (Exception e) {
321+
// Do nothing
322+
}
323+
if (gradleVersionString == null) {
324+
// return if failed to get gradle version.
325+
return;
326+
}
327+
328+
GradleVersion gradleVersion = GradleVersion.version(gradleVersionString);
329+
if (GradleUtils.isIncompatible(gradleVersion, javaVersion)) {
330+
String highestJavaVersion = GradleUtils.getHighestSupportedJava(gradleVersion);
331+
File javaHomeFile = GradleUtils.getJdkToLaunchDaemon(highestJavaVersion);
332+
if (javaHomeFile != null) {
333+
getPreferences().setGradleJavaHome(javaHomeFile.getAbsolutePath());
334+
}
335+
}
336+
}
337+
294338
private IStatus importDir(Path projectFolder, IProgressMonitor monitor) {
295339
if (monitor.isCanceled()) {
296340
return Status.CANCEL_STATUS;
@@ -438,11 +482,18 @@ protected IStatus startSynchronization(Path projectFolder, IProgressMonitor moni
438482
}
439483

440484
public static BuildConfiguration getBuildConfiguration(Path rootFolder) {
485+
return getBuildConfiguration(rootFolder, false);
486+
}
487+
488+
public static BuildConfiguration getBuildConfiguration(Path rootFolder, boolean noDaemon) {
441489
GradleDistribution distribution = getGradleDistribution(rootFolder);
442490
Preferences preferences = getPreferences();
443491
File javaHome = getJavaHome(preferences);
444492
File gradleUserHome = getGradleUserHomeFile();
445493
List<String> gradleArguments = new ArrayList<>();
494+
if (noDaemon) {
495+
gradleArguments.add("--no-daemon");
496+
}
446497
gradleArguments.addAll(getGradleInitScriptArgs());
447498
gradleArguments.addAll(preferences.getGradleArguments());
448499
List<String> gradleJvmArguments = preferences.getGradleJvmArguments();

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleUtils.java

+93-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.io.IOException;
1717
import java.io.InputStream;
1818
import java.io.StreamCorruptedException;
19+
import java.lang.Runtime.Version;
1920
import java.net.URL;
2021
import java.nio.file.Files;
2122
import java.nio.file.Path;
@@ -28,8 +29,11 @@
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Optional;
31-
import java.util.regex.Pattern;
32+
import java.util.Set;
33+
import java.util.Map.Entry;
34+
import java.util.stream.Stream;
3235

36+
import org.apache.commons.lang3.StringUtils;
3337
import org.eclipse.buildship.core.BuildConfiguration;
3438
import org.eclipse.buildship.core.GradleBuild;
3539
import org.eclipse.buildship.core.GradleCore;
@@ -39,14 +43,21 @@
3943
import org.eclipse.core.runtime.IProgressMonitor;
4044
import org.eclipse.core.runtime.IStatus;
4145
import org.eclipse.jdt.core.JavaCore;
46+
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
47+
import org.eclipse.jdt.internal.launching.StandardVMType;
48+
import org.eclipse.jdt.launching.AbstractVMInstall;
49+
import org.eclipse.jdt.launching.IVMInstall;
50+
import org.eclipse.jdt.launching.IVMInstallType;
51+
import org.eclipse.jdt.launching.JavaRuntime;
4252
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
4353
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
54+
import org.eclipse.jdt.ls.core.internal.RuntimeEnvironment;
55+
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
4456
import org.gradle.tooling.model.build.BuildEnvironment;
4557
import org.gradle.tooling.model.build.GradleEnvironment;
4658

4759
public class GradleUtils {
4860

49-
public static String MAX_SUPPORTED_JAVA = JavaCore.VERSION_17;
5061
// see https://github.com/gradle/gradle/pull/17397
5162
public static String INVALID_TYPE_FIXED_VERSION = "7.2";
5263
// see https://github.com/gradle/gradle/issues/890
@@ -55,11 +66,6 @@ public class GradleUtils {
5566

5667
private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256";
5768

58-
/**
59-
* A pattern to parse annotation processing arguments.
60-
*/
61-
private static final Pattern OPTION_PATTERN = Pattern.compile("-A([^ \\t\"']+)");
62-
6369
public static boolean isIncompatible(GradleVersion gradleVersion, String javaVersion) {
6470
if (gradleVersion == null || javaVersion == null || javaVersion.isEmpty()) {
6571
return false;
@@ -72,7 +78,13 @@ public static String getHighestSupportedJava(GradleVersion gradleVersion) {
7278
GradleVersion baseVersion = gradleVersion.getBaseVersion();
7379
try {
7480
// https://docs.gradle.org/current/userguide/compatibility.html
75-
if (baseVersion.compareTo(GradleVersion.version("7.3")) >= 0) {
81+
if (baseVersion.compareTo(GradleVersion.version("8.3")) >= 0) {
82+
return JavaCore.VERSION_20;
83+
} else if (baseVersion.compareTo(GradleVersion.version("7.6")) >= 0) {
84+
return JavaCore.VERSION_19;
85+
} else if (baseVersion.compareTo(GradleVersion.version("7.5")) >= 0) {
86+
return JavaCore.VERSION_18;
87+
} else if (baseVersion.compareTo(GradleVersion.version("7.3")) >= 0) {
7688
return JavaCore.VERSION_17;
7789
} else if (baseVersion.compareTo(GradleVersion.version("7.0")) >= 0) {
7890
return JavaCore.VERSION_16;
@@ -91,10 +103,10 @@ public static String getHighestSupportedJava(GradleVersion gradleVersion) {
91103
} else if (baseVersion.compareTo(GradleVersion.version("4.3")) >= 0) {
92104
return JavaCore.VERSION_9;
93105
}
94-
return JavaCore.VERSION_1_8;
95106
} catch (IllegalArgumentException e) {
96-
return MAX_SUPPORTED_JAVA;
107+
// ignore
97108
}
109+
return JavaCore.VERSION_1_8;
98110
}
99111

100112
public static boolean hasGradleInvalidTypeCodeException(IStatus status, Path projectFolder, IProgressMonitor monitor) {
@@ -293,4 +305,75 @@ public static void synchronizeAnnotationProcessingConfiguration(IProgressMonitor
293305
}
294306
}
295307
}
308+
309+
/**
310+
* Find the latest JDK but equal or lower than the {@code highestJavaVersion}.
311+
*/
312+
public static File getJdkToLaunchDaemon(String highestJavaVersion) {
313+
if (StringUtils.isBlank(highestJavaVersion)) {
314+
return null;
315+
}
316+
317+
Map<String, File> jdks = getAllVmInstalls();;
318+
Entry<String, File> selected = null;
319+
for (Entry<String, File> jdk : jdks.entrySet()) {
320+
String javaVersion = jdk.getKey();
321+
if (Version.parse(javaVersion).compareTo(Version.parse(highestJavaVersion)) <= 0
322+
&& (selected == null || Version.parse(selected.getKey()).compareTo(Version.parse(javaVersion)) < 0)) {
323+
selected = jdk;
324+
}
325+
}
326+
327+
return selected == null ? null : selected.getValue();
328+
}
329+
330+
/**
331+
* Get all the available JDK installations in the Eclipse VM registry. If multiple installations
332+
* are found for the same major version, the first found one is return.
333+
*
334+
* The results are returned as map, where key is the major version and value is the file instance of
335+
* the installation.
336+
*/
337+
private static Map<String, File> getAllVmInstalls() {
338+
List<IVMInstall> vmList = Stream.of(JavaRuntime.getVMInstallTypes())
339+
.map(IVMInstallType::getVMInstalls)
340+
.flatMap(Arrays::stream)
341+
.toList();
342+
Map<String, File> vmInstalls = new HashMap<>();
343+
for (IVMInstall vmInstall : vmList) {
344+
if (vmInstall instanceof AbstractVMInstall vm) {
345+
String javaVersion = getMajorJavaVersion(vm.getJavaVersion());
346+
if (StringUtils.isBlank(javaVersion) || vm.getInstallLocation() == null) {
347+
continue;
348+
}
349+
350+
vmInstalls.putIfAbsent(javaVersion, vm.getInstallLocation());
351+
}
352+
}
353+
354+
Preferences preferences = JavaLanguageServerPlugin.getPreferencesManager().getPreferences();
355+
Set<RuntimeEnvironment> runtimes = preferences.getRuntimes();
356+
for (RuntimeEnvironment runtime : runtimes) {
357+
if (StringUtils.isBlank(runtime.getPath())) {
358+
continue;
359+
}
360+
File javaHome = new File(runtime.getPath());
361+
if (vmInstalls.containsValue(javaHome)) {
362+
continue;
363+
}
364+
365+
String javaVersion = new StandardVMType().readReleaseVersion(javaHome);
366+
if (StringUtils.isNotBlank(javaVersion)) {
367+
// the user preference should have higher priority and replace
368+
// the existing one if the major version is the same.
369+
vmInstalls.put(getMajorJavaVersion(javaVersion), javaHome);
370+
}
371+
}
372+
373+
return vmInstalls;
374+
}
375+
376+
public static String getMajorJavaVersion(String version) {
377+
return CompilerOptions.versionFromJdkLevel(CompilerOptions.versionToJdkLevel(version));
378+
}
296379
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Microsoft Corporation - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.jdt.ls.core.internal.managers;
14+
15+
import static org.junit.Assert.assertEquals;
16+
import static org.junit.Assert.assertTrue;
17+
18+
import java.io.File;
19+
20+
import org.junit.Test;
21+
22+
public class GradleUtilsTest {
23+
@Test
24+
public void testGetJdkToLaunchDaemon() {
25+
assertEquals("17", GradleUtils.getMajorJavaVersion("17.0.8"));
26+
assertEquals("1.8", GradleUtils.getMajorJavaVersion("1.8.0_202"));
27+
}
28+
29+
@Test
30+
public void testGetMajorJavaVersion() {
31+
File javaHome = GradleUtils.getJdkToLaunchDaemon("10");
32+
assertTrue(javaHome.getAbsolutePath().contains("fakejdk" + File.separator + "10"));
33+
}
34+
}

0 commit comments

Comments
 (0)