Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select suitable JDK to launch Gradle. #2812

Merged
merged 2 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException {
subMonitor.setTaskName(IMPORTING_GRADLE_PROJECTS);
JavaLanguageServerPlugin.logInfo(IMPORTING_GRADLE_PROJECTS);
subMonitor.worked(1);
// run just once at the first project, assuming that all projects are using the same gradle version.
inferGradleJavaHome(directories.iterator().next(), monitor);
MultiStatus compatibilityStatus = new MultiStatus(IConstants.PLUGIN_ID, -1, "Compatibility issue occurs when importing Gradle projects", null);
MultiStatus gradleUpgradeWrapperStatus = new MultiStatus(IConstants.PLUGIN_ID, -1, "Gradle upgrade wrapper", null);
for (Path directory : directories) {
Expand Down Expand Up @@ -291,6 +293,33 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException {
subMonitor.done();
}

private void inferGradleJavaHome(Path projectFolder, IProgressMonitor monitor) {
if (StringUtils.isNotBlank(getPreferences().getGradleJavaHome())) {
return;
}

File javaHome = getJavaHome(getPreferences());
String javaVersion;
if (javaHome == null) {
javaVersion = System.getProperty("java.version");
} else {
StandardVMType type = new StandardVMType();
javaVersion = type.readReleaseVersion(javaHome);
}
if (StringUtils.isBlank(javaVersion)) {
// return if failed to get java version.
return;
}
GradleVersion gradleVersion = GradleUtils.getGradleVersion(projectFolder, monitor);
if (GradleUtils.isIncompatible(gradleVersion, javaVersion)) {
String highestJavaVersion = GradleUtils.getHighestSupportedJava(gradleVersion);
File javaHomeFile = GradleUtils.getJdkToLaunchDaemon(highestJavaVersion);
if (javaHomeFile != null) {
getPreferences().setGradleJavaHome(javaHomeFile.getAbsolutePath());
}
}
}

private IStatus importDir(Path projectFolder, IProgressMonitor monitor) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
Expand Down Expand Up @@ -438,11 +467,18 @@ protected IStatus startSynchronization(Path projectFolder, IProgressMonitor moni
}

public static BuildConfiguration getBuildConfiguration(Path rootFolder) {
return getBuildConfiguration(rootFolder, false);
}

public static BuildConfiguration getBuildConfiguration(Path rootFolder, boolean noDaemon) {
GradleDistribution distribution = getGradleDistribution(rootFolder);
Preferences preferences = getPreferences();
File javaHome = getJavaHome(preferences);
File gradleUserHome = getGradleUserHomeFile();
List<String> gradleArguments = new ArrayList<>();
if (noDaemon) {
gradleArguments.add("--no-daemon");
}
gradleArguments.addAll(getGradleInitScriptArgs());
gradleArguments.addAll(preferences.getGradleArguments());
List<String> gradleJvmArguments = preferences.getGradleJvmArguments();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamCorruptedException;
import java.lang.Runtime.Version;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -28,8 +29,11 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.buildship.core.BuildConfiguration;
import org.eclipse.buildship.core.GradleBuild;
import org.eclipse.buildship.core.GradleCore;
Expand All @@ -39,14 +43,21 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.launching.StandardVMType;
import org.eclipse.jdt.launching.AbstractVMInstall;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.RuntimeEnvironment;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.gradle.tooling.model.build.BuildEnvironment;
import org.gradle.tooling.model.build.GradleEnvironment;

public class GradleUtils {

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

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

/**
* A pattern to parse annotation processing arguments.
*/
private static final Pattern OPTION_PATTERN = Pattern.compile("-A([^ \\t\"']+)");

public static boolean isIncompatible(GradleVersion gradleVersion, String javaVersion) {
if (gradleVersion == null || javaVersion == null || javaVersion.isEmpty()) {
return false;
Expand All @@ -72,7 +78,13 @@ public static String getHighestSupportedJava(GradleVersion gradleVersion) {
GradleVersion baseVersion = gradleVersion.getBaseVersion();
try {
// https://docs.gradle.org/current/userguide/compatibility.html
if (baseVersion.compareTo(GradleVersion.version("7.3")) >= 0) {
if (baseVersion.compareTo(GradleVersion.version("8.3")) >= 0) {
return JavaCore.VERSION_20;
} else if (baseVersion.compareTo(GradleVersion.version("7.6")) >= 0) {
return JavaCore.VERSION_19;
} else if (baseVersion.compareTo(GradleVersion.version("7.5")) >= 0) {
return JavaCore.VERSION_18;
} else if (baseVersion.compareTo(GradleVersion.version("7.3")) >= 0) {
return JavaCore.VERSION_17;
} else if (baseVersion.compareTo(GradleVersion.version("7.0")) >= 0) {
return JavaCore.VERSION_16;
Expand All @@ -91,10 +103,10 @@ public static String getHighestSupportedJava(GradleVersion gradleVersion) {
} else if (baseVersion.compareTo(GradleVersion.version("4.3")) >= 0) {
return JavaCore.VERSION_9;
}
return JavaCore.VERSION_1_8;
} catch (IllegalArgumentException e) {
return MAX_SUPPORTED_JAVA;
// ignore
}
return JavaCore.VERSION_1_8;
}

public static boolean hasGradleInvalidTypeCodeException(IStatus status, Path projectFolder, IProgressMonitor monitor) {
Expand Down Expand Up @@ -123,7 +135,7 @@ public static boolean isGradleInvalidTypeCodeException(Throwable throwable) {

public static GradleVersion getGradleVersion(Path projectFolder, IProgressMonitor monitor) {
try {
BuildConfiguration build = GradleProjectImporter.getBuildConfiguration(projectFolder);
BuildConfiguration build = GradleProjectImporter.getBuildConfiguration(projectFolder, true);
GradleBuild gradleBuild = GradleCore.getWorkspace().createBuild(build);
BuildEnvironment environment = gradleBuild.withConnection(connection -> connection.getModel(BuildEnvironment.class), monitor);
GradleEnvironment gradleEnvironment = environment.getGradle();
Expand Down Expand Up @@ -293,4 +305,75 @@ public static void synchronizeAnnotationProcessingConfiguration(IProgressMonitor
}
}
}

/**
* Find the latest JDK but equal or lower than the {@code highestJavaVersion}.
*/
public static File getJdkToLaunchDaemon(String highestJavaVersion) {
if (StringUtils.isBlank(highestJavaVersion)) {
return null;
}

Map<String, File> jdks = getAllVmInstalls();;
Entry<String, File> selected = null;
for (Entry<String, File> jdk : jdks.entrySet()) {
String javaVersion = jdk.getKey();
if (Version.parse(javaVersion).compareTo(Version.parse(highestJavaVersion)) <= 0
&& (selected == null || Version.parse(selected.getKey()).compareTo(Version.parse(javaVersion)) < 0)) {
selected = jdk;
}
}

return selected == null ? null : selected.getValue();
}

/**
* Get all the available JDK installations in the Eclipse VM registry. If multiple installations
* are found for the same major version, the first found one is return.
*
* The results are returned as map, where key is the major version and value is the file instance of
* the installation.
*/
private static Map<String, File> getAllVmInstalls() {
List<IVMInstall> vmList = Stream.of(JavaRuntime.getVMInstallTypes())
.map(IVMInstallType::getVMInstalls)
.flatMap(Arrays::stream)
.toList();
Map<String, File> vmInstalls = new HashMap<>();
for (IVMInstall vmInstall : vmList) {
if (vmInstall instanceof AbstractVMInstall vm) {
String javaVersion = getMajorJavaVersion(vm.getJavaVersion());
if (StringUtils.isBlank(javaVersion) || vm.getInstallLocation() == null) {
continue;
}

vmInstalls.putIfAbsent(javaVersion, vm.getInstallLocation());
}
}

Preferences preferences = JavaLanguageServerPlugin.getPreferencesManager().getPreferences();
Set<RuntimeEnvironment> runtimes = preferences.getRuntimes();
for (RuntimeEnvironment runtime : runtimes) {
if (StringUtils.isBlank(runtime.getPath())) {
continue;
}
File javaHome = new File(runtime.getPath());
if (vmInstalls.containsValue(javaHome)) {
continue;
}

String javaVersion = new StandardVMType().readReleaseVersion(javaHome);
if (StringUtils.isNotBlank(javaVersion)) {
// the user preference should have higher priority and replace
// the existing one if the major version is the same.
vmInstalls.put(getMajorJavaVersion(javaVersion), javaHome);
}
}

return vmInstalls;
}

public static String getMajorJavaVersion(String version) {
return CompilerOptions.versionFromJdkLevel(CompilerOptions.versionToJdkLevel(version));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences.FeatureStatus;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
Expand All @@ -79,6 +80,12 @@
public class GradleProjectImporterTest extends AbstractGradleBasedTest{

private static final String GRADLE1_PATTERN = "**/gradle1";
private String gradleJavaHome;

@Before
public void setUp() {
gradleJavaHome = JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getGradleJavaHome();
}

@Test
public void importSimpleGradleProject() throws Exception {
Expand All @@ -94,6 +101,7 @@ public void importSimpleGradleProject() throws Exception {
public void cleanUp() throws Exception {
super.cleanUp();
Job.getJobManager().join(CorePlugin.GRADLE_JOB_FAMILY, new NullProgressMonitor());
JavaLanguageServerPlugin.getPreferencesManager().getPreferences().setGradleJavaHome(gradleJavaHome);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2023 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.managers;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;

import org.junit.Test;

public class GradleUtilsTest {
@Test
public void testGetJdkToLaunchDaemon() {
assertEquals("17", GradleUtils.getMajorJavaVersion("17.0.8"));
assertEquals("1.8", GradleUtils.getMajorJavaVersion("1.8.0_202"));
}

@Test
public void testGetMajorJavaVersion() {
File javaHome = GradleUtils.getJdkToLaunchDaemon("10");
assertTrue(javaHome.getAbsolutePath().contains("fakejdk" + File.separator + "10"));
}
}