From 23bfc9750be0e48dbe0ed379d3cecee23bca9467 Mon Sep 17 00:00:00 2001 From: Tanish Ranjan <62828604+Tanish-Ranjan@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:48:55 +0530 Subject: [PATCH 1/4] feat - Extract classes.jar from *.aar (#1594) --- .../GradleBuildServerBuildSupport.java | 56 ++++++++++--------- .../microsoft/gradle/bs/importer/Utils.java | 50 +++++++++++++++++ 2 files changed, 81 insertions(+), 25 deletions(-) diff --git a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerBuildSupport.java b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerBuildSupport.java index 978d36b9e..e004eae80 100644 --- a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerBuildSupport.java +++ b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerBuildSupport.java @@ -513,30 +513,33 @@ private void setProjectJdk(Map classpathMap, List classpathAttributes = new LinkedList<>(); - if (isModular) { - classpathAttributes.add(modularAttribute); + List classpathAttributes = new LinkedList<>(); + if (isModular) { + classpathAttributes.add(modularAttribute); + } + classpathAttributes.add(buildServerAttribute); + IClasspathEntry jdkEntry = JavaCore.newContainerEntry( + JavaRuntime.newJREContainerPath(vm), + ClasspathEntry.NO_ACCESS_RULES, + classpathAttributes.toArray(new IClasspathAttribute[0]), + false /*isExported*/ + ); + classpathMap.putIfAbsent(jdkEntry.getPath(), jdkEntry); + } catch (URISyntaxException e) { + throw new CoreException(new Status(IStatus.ERROR, ImporterPlugin.PLUGIN_ID, + "Invalid Java home: " + jvmBuildTarget.getJavaHome(), e)); } - classpathAttributes.add(buildServerAttribute); - IClasspathEntry jdkEntry = JavaCore.newContainerEntry( - JavaRuntime.newJREContainerPath(vm), - ClasspathEntry.NO_ACCESS_RULES, - classpathAttributes.toArray(new IClasspathAttribute[0]), - false /*isExported*/ - ); - classpathMap.putIfAbsent(jdkEntry.getPath(), jdkEntry); - } catch (URISyntaxException e) { - throw new CoreException(new Status(IStatus.ERROR, ImporterPlugin.PLUGIN_ID, - "Invalid Java home: " + jvmBuildTarget.getJavaHome(), e)); } } @@ -585,8 +588,10 @@ private JvmBuildTargetEx getJvmTarget(List buildTargets) throws Cor } if (StringUtils.isBlank(jvmTarget.getJavaHome()) || StringUtils.isBlank(jvmTarget.getGradleVersion())) { - throw new CoreException(new Status(IStatus.ERROR, ImporterPlugin.PLUGIN_ID, - "Invalid JVM build target: " + jvmTarget.toString())); + JavaLanguageServerPlugin.logException( + new CoreException(new Status(IStatus.WARNING, ImporterPlugin.PLUGIN_ID, + "Empty Java Home or Gradle Version in JVM target.")) + ); } return jvmTarget; @@ -641,7 +646,8 @@ private List getDependencyJars(DependencyModulesResult dependen } String classifier = artifactData.getClassifier(); try { - File jarFile = new File(new URI(uri)); + File artifactFile = new File(new URI(uri)); + File jarFile = Utils.getJarFile(artifactFile); if (classifier == null) { artifact = jarFile; } else if ("sources".equals(classifier)) { diff --git a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java index fd312aee6..c365b5dd6 100644 --- a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java +++ b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java @@ -2,14 +2,21 @@ import static org.eclipse.jdt.ls.core.internal.handlers.MapFlattener.getString; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Path; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IProject; @@ -22,6 +29,7 @@ import org.eclipse.core.runtime.URIUtil; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.ls.core.internal.JavaClientConnection.JavaLanguageClient; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.preferences.Preferences; import org.eclipse.lsp4j.ExecuteCommandParams; @@ -214,4 +222,46 @@ public static void sendTelemetry(JavaLanguageClient client, Object message) { client.sendNotification(new ExecuteCommandParams("_java.gradle.buildServer.sendTelemetry", Arrays.asList(message))); } + + /** + * Extracts the jar file from the aar file, since JDT.LS is not able to understand + * the structure of aar files. + */ + public static File getJarFile(File file) { + + String filepath = file.getAbsolutePath(); + + if (filepath.endsWith(".aar")) { + + // Extracting classes.jar from AAR files + try(ZipInputStream is = new ZipInputStream(new FileInputStream(file))) { + + ZipEntry entry; + while ((entry = is.getNextEntry()) != null) { + if (entry.getName().equals("classes.jar")) { + String fileName = file.getName(); + fileName = fileName.substring(0, fileName.length() - 4); + fileName = fileName + ".jar"; + File outputFile = Path.of(file.getParentFile().getAbsolutePath(), fileName).toFile(); + try (FileOutputStream outputStream = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[1024]; + int len; + while((len = is.read(buffer)) > 0) { + outputStream.write(buffer, 0, len); + } + return outputFile; + } + } + } + + } catch(IOException e) { + JavaLanguageServerPlugin.logException(e); + } + + } + + return file; + + } + } From 9ac3ffecdda84a52e6ae1c2bffed7be2f7ef47df Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Mon, 7 Oct 2024 19:50:15 -0700 Subject: [PATCH 2/4] feat - Gradle project view keeps project hierarchy (#1612) * Gradle project view keeps project hierarchy Implements #945 by parsing the project path which is found in the 'definition.script' property. The project path, which is unique for every subproject, is then used as key in the 'projectTreeItemMap'. * Update 'actions/download-artifact' and 'actions/upload-artifact' to v4 * Update target platform definition --------- Co-authored-by: Sheng Chen --- .github/workflows/main.yml | 4 ++-- ...com.microsoft.gradle.bs.importer.tp.target | 4 ++-- .../DefaultProjectsTreeDataProvider.ts | 4 ++-- .../GradleTasksTreeDataProvider.ts | 24 +++++++++++++------ .../gradleTasks/TreeItemWithTasksOrGroups.ts | 11 ++++++++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67db38a52..ca2ab11e1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,7 +51,7 @@ jobs: JAVA_HOME: "" NODE_OPTIONS: "--max-old-space-size=4096" - name: Upload lib - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib path: | @@ -93,7 +93,7 @@ jobs: restore-keys: | ${{ runner.os }}-vscode- - name: Download lib - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: lib path: extension/ diff --git a/extension/jdtls.ext/com.microsoft.gradle.bs.importer.target/com.microsoft.gradle.bs.importer.tp.target b/extension/jdtls.ext/com.microsoft.gradle.bs.importer.target/com.microsoft.gradle.bs.importer.tp.target index 4d5f4d344..e6151ef3f 100644 --- a/extension/jdtls.ext/com.microsoft.gradle.bs.importer.target/com.microsoft.gradle.bs.importer.tp.target +++ b/extension/jdtls.ext/com.microsoft.gradle.bs.importer.target/com.microsoft.gradle.bs.importer.tp.target @@ -22,11 +22,11 @@ - + - + diff --git a/extension/src/views/defaultProject/DefaultProjectsTreeDataProvider.ts b/extension/src/views/defaultProject/DefaultProjectsTreeDataProvider.ts index 89b87af65..2fc5ec0b6 100644 --- a/extension/src/views/defaultProject/DefaultProjectsTreeDataProvider.ts +++ b/extension/src/views/defaultProject/DefaultProjectsTreeDataProvider.ts @@ -55,7 +55,7 @@ export class DefaultProjectsTreeDataProvider implements vscode.TreeDataProvider< private async getChildrenForProjectTreeItem(element: ProjectTreeItem): Promise { const projectTaskItem = new ProjectTaskTreeItem("Tasks", vscode.TreeItemCollapsibleState.Collapsed, element); - projectTaskItem.setChildren([...element.groups, ...element.tasks]); + projectTaskItem.setChildren([...element.tasks, ...element.groups]); const results: vscode.TreeItem[] = [projectTaskItem]; const resourceUri = element.resourceUri; if (!resourceUri) { @@ -68,6 +68,6 @@ export class DefaultProjectsTreeDataProvider implements vscode.TreeDataProvider< path.dirname(resourceUri.fsPath), typeof element.label === "string" ? element.label : resourceUri.fsPath ); - return [...results, projectDependencyTreeItem]; + return [projectDependencyTreeItem, ...results]; } } diff --git a/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts b/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts index b37f0a931..b6c7f4713 100644 --- a/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts +++ b/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts @@ -213,11 +213,11 @@ export class GradleTasksTreeDataProvider implements vscode.TreeDataProvider { const projectTaskItem = new ProjectTaskTreeItem("Tasks", vscode.TreeItemCollapsibleState.Collapsed, element); - projectTaskItem.setChildren([...element.groups, ...element.tasks]); + projectTaskItem.setChildren([...element.tasks, ...element.groups]); const results: vscode.TreeItem[] = [projectTaskItem]; const resourceUri = element.resourceUri; if (!resourceUri) { - return results; + return [...results, ...element.subprojects]; } const projectDependencyTreeItem: ProjectDependencyTreeItem = new ProjectDependencyTreeItem( "Dependencies", @@ -226,7 +226,7 @@ export class GradleTasksTreeDataProvider implements vscode.TreeDataProvider Date: Wed, 16 Oct 2024 08:20:28 +0200 Subject: [PATCH 3/4] fix - Add root project folder to each key in projectTreeItemMap (#1617) In #1612, the keys of this map were changed to the Gradle project path. This path is unique for each project within one Gradle build, but not if there are multiple Gradle builds loaded in one workspace by using: 'gradle.nestedProjects': true This change combined the old and new behavior by creating the Map key from two parts: (1) The root project folder which uniquely identifies the build (accessible via definition.projectFolder) (2) The Gradle project path that uniquely identifies a project inside the build Signed-off-by: Jendrik Johannes --- .../src/views/gradleTasks/GradleTasksTreeDataProvider.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts b/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts index b6c7f4713..d7c4007b7 100644 --- a/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts +++ b/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts @@ -254,13 +254,14 @@ export class GradleTasksTreeDataProvider implements vscode.TreeDataProvider Date: Thu, 24 Oct 2024 11:10:53 +0200 Subject: [PATCH 4/4] Discover Gradle builds by looking for setting.gradle(.kts) (#1618) * Discover Gradle builds by looking for setting.gradle(.kts) When the option 'gradle.nestedProjects: true' is set, the build roots are currently discovered by looking for the wrapper scripts. These however are completely optional for a build. But there is one thing each Gradle build definitely needs to have: A 'settings.gradle' or 'setting.gradle.kts' file. Gradle itself searches for these files to accept a folder as a Gradle build/project. This change proposes to check for these files instead of the gradlw scripts. A good project to test this with is https://github.com/microsoft/build-server-for-gradle which contains a lot of Gradle build in the 'testProjects' folder. --------- Signed-off-by: Jendrik Johannes Co-authored-by: Sheng Chen --- extension/src/stores/RootProjectsStore.ts | 2 +- extension/src/test/testUtil.ts | 4 ++-- extension/src/util/index.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extension/src/stores/RootProjectsStore.ts b/extension/src/stores/RootProjectsStore.ts index f694e8b6e..2f07a6f89 100644 --- a/extension/src/stores/RootProjectsStore.ts +++ b/extension/src/stores/RootProjectsStore.ts @@ -7,7 +7,7 @@ import { RootProject } from "../rootProject/RootProject"; import { GRADLE_BUILD_FILE_NAMES } from "../constant"; async function getNestedRootProjectFolders(): Promise { - const matchingNestedWrapperFiles = await vscode.workspace.findFiles("**/{gradlew,gradlew.bat}"); + const matchingNestedWrapperFiles = await vscode.workspace.findFiles("**/{settings.gradle,settings.gradle.kts}"); return [...new Set(matchingNestedWrapperFiles.map((uri) => path.dirname(uri.fsPath)))]; } diff --git a/extension/src/test/testUtil.ts b/extension/src/test/testUtil.ts index 45ca12f0e..be0f6c33b 100644 --- a/extension/src/test/testUtil.ts +++ b/extension/src/test/testUtil.ts @@ -86,13 +86,13 @@ export function stubWorkspaceFolders(workspaceFolders: vscode.WorkspaceFolder[]) const getWorkspaceFolderStub = sinon.stub(vscode.workspace, "getWorkspaceFolder"); const dirnameStub = sinon.stub(path, "dirname"); workspaceFolders.forEach((workspaceFolder) => { - existsSyncStub.withArgs(path.join(workspaceFolder.uri.fsPath, "gradlew")).returns(true); + existsSyncStub.withArgs(path.join(workspaceFolder.uri.fsPath, "settings.gradle")).returns(true); getWorkspaceFolderStub.withArgs(sinon.match.has("fsPath", workspaceFolder.uri.fsPath)).returns(workspaceFolder); dirnameStub.withArgs(workspaceFolder.uri.fsPath).returns(workspaceFolder.uri.fsPath); }); sinon .stub(vscode.workspace, "findFiles") - .withArgs("**/{gradlew,gradlew.bat}") + .withArgs("**/{settings.gradle,settings.gradle.kts}") .returns(Promise.resolve(workspaceFolders.map((folder) => folder.uri))); } diff --git a/extension/src/util/index.ts b/extension/src/util/index.ts index 27d9ba0ef..cf05ca677 100644 --- a/extension/src/util/index.ts +++ b/extension/src/util/index.ts @@ -57,8 +57,8 @@ export function waitOnTcp(host: string, port: number): Promise { export function isGradleRootProject(rootProject: RootProject): boolean { return ( - fs.existsSync(path.join(rootProject.getProjectUri().fsPath, "gradlew")) || - fs.existsSync(path.join(rootProject.getProjectUri().fsPath, "gradlew.bat")) + fs.existsSync(path.join(rootProject.getProjectUri().fsPath, "settings.gradle")) || + fs.existsSync(path.join(rootProject.getProjectUri().fsPath, "settings.gradle.kts")) ); }