mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[gradle] IDEA-370426 fix deep DefaultGradleLightBuild hierarchy
Before, a root build was not specified as a parent for any of its included builds. Also moves `GradleDaemonModelHolder#convertNestedGradleBuilds` into `DefaultGradleLightBuild#convertGradleBuilds`. Tests: - org.jetbrains.plugins.gradle.model.DefaultGradleLightBuildTest.test DefaultGradleLightBuild.convertGradleBuilds GitOrigin-RevId: 360ad9ccc4332a91bde203e1669f0f82a309ad3e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
304d52eb3e
commit
ce7ec3afd6
@@ -146,6 +146,7 @@ jvm_library(
|
||||
"//platform/external-system-impl:externalSystem-tests_test_lib",
|
||||
"//platform/external-system-api/testFramework",
|
||||
"//java/testFramework",
|
||||
"//plugins/gradle/tooling-extension-impl:toolingExtension-impl",
|
||||
"//plugins/gradle/tooling-extension-impl:toolingExtension-tests_test_lib",
|
||||
"//java/execution/impl",
|
||||
"@lib//:assert_j",
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.externalSystem.tests" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.externalSystem.testFramework" exported="" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.java.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.gradle.toolingExtension.impl" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.gradle.toolingExtension.tests" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.java.execution.impl" scope="TEST" />
|
||||
<orderEntry type="library" scope="TEST" name="assertJ" level="project" />
|
||||
|
||||
+2
-2
@@ -620,8 +620,8 @@ public final class GradleProjectResolver implements ExternalSystemProjectResolve
|
||||
String rootProjectName = build.getName();
|
||||
BuildParticipant buildParticipant = new BuildParticipant();
|
||||
String projectPath = toCanonicalPath(build.getBuildIdentifier().getRootDir().getPath());
|
||||
String parentPath = build.getParentBuildIdentifier() != null ?
|
||||
toCanonicalPath(build.getParentBuildIdentifier().getRootDir().getPath()) : null;
|
||||
String parentPath = build.getParentBuild() != null ?
|
||||
toCanonicalPath(build.getParentBuild().getBuildIdentifier().getRootDir().getPath()) : null;
|
||||
buildParticipant.setRootProjectName(rootProjectName);
|
||||
buildParticipant.setRootPath(projectPath);
|
||||
buildParticipant.setParentRootPath(parentPath);
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gradle.model
|
||||
|
||||
import org.gradle.tooling.internal.gradle.DefaultBuildIdentifier
|
||||
import org.gradle.tooling.model.gradle.BasicGradleProject
|
||||
import org.gradle.tooling.model.gradle.GradleBuild
|
||||
import org.gradle.tooling.model.internal.ImmutableDomainObjectSet
|
||||
import org.jetbrains.plugins.gradle.tooling.serialization.internal.adapter.InternalBuildIdentifier
|
||||
import org.jetbrains.plugins.gradle.tooling.serialization.internal.adapter.InternalProjectIdentifier
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertNotNull
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.io.File
|
||||
|
||||
class DefaultGradleLightBuildTest {
|
||||
|
||||
@Test
|
||||
fun `test DefaultGradleLightBuild#convertGradleBuilds`() {
|
||||
// GIVEN
|
||||
val rootBuildPath = "rootBuild"
|
||||
val includedBuildPath = "rootBuild/includedBuild"
|
||||
val subIncludedBuildPath = "rootBuild/includedBuild/subIncludedBuild"
|
||||
|
||||
val gradleRootBuild = mockBuildWithProjectAndSubproject(rootBuildPath)
|
||||
val gradleIncludedBuild = mockBuildWithProjectAndSubproject(includedBuildPath, parent = gradleRootBuild)
|
||||
val gradleSubIncludedBuild = mockBuildWithProjectAndSubproject(subIncludedBuildPath, parent = gradleIncludedBuild)
|
||||
val gradleBuilds = listOf(gradleRootBuild, gradleIncludedBuild, gradleSubIncludedBuild)
|
||||
// WHEN
|
||||
val convertedBuilds = DefaultGradleLightBuild.convertGradleBuilds(gradleBuilds)
|
||||
// THEN
|
||||
Assertions.assertIterableEquals(gradleBuilds.map { it.buildIdentifier.rootDir.name },
|
||||
convertedBuilds.map { it.buildIdentifier.rootDir.name },
|
||||
"The order of builds in `DefaultGradleLightBuild.convertGradleBuilds` should be the same as in the input")
|
||||
val rootBuild = convertedBuilds[0]
|
||||
verifyBuildRootProjectAndSubproject(rootBuild, rootBuildPath)
|
||||
|
||||
val includedBuild = convertedBuilds[1]
|
||||
verifyBuildRootProjectAndSubproject(includedBuild, includedBuildPath, parentBuild = rootBuild)
|
||||
|
||||
val subIncluded = convertedBuilds[2]
|
||||
verifyBuildRootProjectAndSubproject(subIncluded, subIncludedBuildPath, parentBuild = includedBuild)
|
||||
}
|
||||
|
||||
private fun mockBuildWithProjectAndSubproject(path: String, parent: GradleBuild? = null): GradleBuild {
|
||||
val buildDir = File(path)
|
||||
val rootProject = mockGradleProject(name = buildDir.name,
|
||||
path = ":",
|
||||
buildDir = buildDir,
|
||||
projectDir = buildDir)
|
||||
val subproject = mockGradleProject(name = "subproject",
|
||||
path = ":subproject",
|
||||
buildDir = buildDir,
|
||||
projectDir = File("$path/subproject"),
|
||||
parent = rootProject)
|
||||
val gradleBuild = mockGradleBuild(buildDir, listOf(rootProject, subproject))
|
||||
if (parent != null) {
|
||||
whenever(parent.includedBuilds).thenReturn(ImmutableDomainObjectSet(listOf(gradleBuild)))
|
||||
}
|
||||
return gradleBuild
|
||||
}
|
||||
|
||||
private fun mockGradleProject(
|
||||
name: String,
|
||||
path: String,
|
||||
buildDir: File,
|
||||
projectDir: File? = null,
|
||||
parent: BasicGradleProject? = null,
|
||||
): BasicGradleProject =
|
||||
mock<BasicGradleProject>().also {
|
||||
whenever(it.projectIdentifier).thenReturn(
|
||||
InternalProjectIdentifier(InternalBuildIdentifier(buildDir), path))
|
||||
whenever(it.name).thenReturn(name)
|
||||
whenever(it.path).thenReturn(path)
|
||||
whenever(it.projectDirectory).thenReturn(projectDir ?: buildDir)
|
||||
whenever(it.parent).thenReturn(parent)
|
||||
whenever(it.children).thenReturn(ImmutableDomainObjectSet(emptyList()))
|
||||
whenever(it.buildTreePath).thenThrow(NotMockedMemberError::class.java)
|
||||
|
||||
if (parent != null) {
|
||||
whenever(parent.children).thenReturn(ImmutableDomainObjectSet(listOf(it)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun mockGradleBuild(buildDir: File, projects: List<BasicGradleProject>): GradleBuild =
|
||||
mock<GradleBuild>().also {
|
||||
whenever(it.buildIdentifier).thenReturn(DefaultBuildIdentifier(buildDir))
|
||||
whenever(it.rootProject).thenReturn(projects.first())
|
||||
whenever(it.projects).thenReturn(ImmutableDomainObjectSet(projects))
|
||||
whenever(it.includedBuilds).thenReturn(ImmutableDomainObjectSet(emptyList()))
|
||||
whenever(it.editableBuilds).thenThrow(NotMockedMemberError::class.java)
|
||||
}
|
||||
|
||||
private fun verifyBuildRootProjectAndSubproject(
|
||||
build: DefaultGradleLightBuild,
|
||||
buildPath: String,
|
||||
parentBuild: DefaultGradleLightBuild? = null,
|
||||
) {
|
||||
val buildDir = File(buildPath)
|
||||
val buildName = buildDir.name
|
||||
|
||||
Assertions.assertEquals(buildName, build.name)
|
||||
Assertions.assertEquals(buildDir.absolutePath, build.buildIdentifier.rootDir.absolutePath)
|
||||
Assertions.assertEquals(parentBuild, build.parentBuild)
|
||||
|
||||
Assertions.assertEquals(2, build.projects.count())
|
||||
// The order of elements in `DefaultGradleLightBuild.getProjects` is random
|
||||
val rootProject = build.projects.find { it.name == buildName }
|
||||
.also { Assertions.assertEquals(build.rootProject, it) }!!
|
||||
val subproject = build.projects.find { it.name == "subproject" }
|
||||
.also { assertNotNull(it) }!!
|
||||
|
||||
verifyProject(project = rootProject,
|
||||
name = buildName,
|
||||
path = ":",
|
||||
build = build,
|
||||
projectDir = buildDir,
|
||||
subprojects = listOf(subproject))
|
||||
verifyProject(project = subproject,
|
||||
name = "subproject",
|
||||
path = ":subproject",
|
||||
build = build,
|
||||
projectDir = File("$buildPath/subproject"))
|
||||
}
|
||||
|
||||
private fun verifyProject(
|
||||
project: DefaultGradleLightProject,
|
||||
name: String,
|
||||
path: String,
|
||||
build: DefaultGradleLightBuild,
|
||||
projectDir: File,
|
||||
subprojects: List<DefaultGradleLightProject>? = emptyList(),
|
||||
) {
|
||||
Assertions.assertEquals(build, project.build)
|
||||
Assertions.assertEquals(name, project.name)
|
||||
Assertions.assertEquals(path, project.path)
|
||||
Assertions.assertEquals(projectDir.absolutePath, project.projectDirectory.absolutePath)
|
||||
Assertions.assertEquals(build.buildIdentifier.rootDir.absolutePath, project.projectIdentifier.buildIdentifier.rootDir.absolutePath)
|
||||
Assertions.assertEquals(path, project.projectIdentifier.projectPath)
|
||||
Assertions.assertIterableEquals(subprojects, project.childProjects)
|
||||
}
|
||||
|
||||
private class NotMockedMemberError : IllegalStateException("The accessed member is not mocked. Please mock it in the test.")
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ public interface GradleLightBuild extends BuildModel {
|
||||
BuildIdentifier getBuildIdentifier();
|
||||
|
||||
@Nullable
|
||||
BuildIdentifier getParentBuildIdentifier();
|
||||
GradleLightBuild getParentBuild();
|
||||
|
||||
@NotNull
|
||||
GradleLightProject getRootProject();
|
||||
|
||||
+16
-59
@@ -4,23 +4,21 @@ package com.intellij.gradle.toolingExtension.impl.modelAction;
|
||||
import com.intellij.gradle.toolingExtension.impl.modelSerialization.ToolingSerializerConverter;
|
||||
import com.intellij.gradle.toolingExtension.impl.util.GradleExecutorServiceUtil;
|
||||
import org.gradle.tooling.BuildController;
|
||||
import org.gradle.tooling.internal.gradle.DefaultBuildIdentifier;
|
||||
import org.gradle.tooling.model.BuildIdentifier;
|
||||
import org.gradle.tooling.model.BuildModel;
|
||||
import org.gradle.tooling.model.gradle.BasicGradleProject;
|
||||
import org.gradle.tooling.model.gradle.GradleBuild;
|
||||
import org.gradle.util.GradleVersion;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.gradle.model.DefaultGradleLightBuild;
|
||||
import org.jetbrains.plugins.gradle.model.DefaultBuildController;
|
||||
import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider.GradleModelConsumer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static org.jetbrains.plugins.gradle.model.DefaultGradleLightBuild.convertGradleBuilds;
|
||||
|
||||
/**
|
||||
* Holder for the Gradle models that consumes and holds data on the Gradle Daemon side.
|
||||
* <p>
|
||||
@@ -53,8 +51,7 @@ public class GradleDaemonModelHolder {
|
||||
|
||||
private final @NotNull GradleVersion myGradleVersion;
|
||||
|
||||
private final @NotNull BlockingQueue<Future<DefaultGradleLightBuild>> myConvertedRootBuild = new LinkedBlockingQueue<>();
|
||||
private final @NotNull BlockingQueue<Future<Collection<DefaultGradleLightBuild>>> myConvertedNestedBuilds = new LinkedBlockingQueue<>();
|
||||
private final @NotNull BlockingQueue<Future<List<DefaultGradleLightBuild>>> myConvertedBuilds = new LinkedBlockingQueue<>();
|
||||
private final @NotNull BlockingQueue<Future<ConvertedModel>> myConvertedModelQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
public GradleDaemonModelHolder(
|
||||
@@ -68,11 +65,12 @@ public class GradleDaemonModelHolder {
|
||||
myRootGradleBuild = rootGradleBuild;
|
||||
myNestedGradleBuilds = nestedGradleBuilds;
|
||||
myGradleVersion = gradleVersion;
|
||||
GradleExecutorServiceUtil.submitTask(converterExecutor, myConvertedRootBuild, () -> {
|
||||
return DefaultGradleLightBuild.convertGradleBuild(rootGradleBuild);
|
||||
});
|
||||
GradleExecutorServiceUtil.submitTask(converterExecutor, myConvertedNestedBuilds, () -> {
|
||||
return convertNestedGradleBuilds(nestedGradleBuilds);
|
||||
|
||||
GradleExecutorServiceUtil.submitTask(converterExecutor, myConvertedBuilds, () -> {
|
||||
List<GradleBuild> gradleBuilds = new ArrayList<>();
|
||||
gradleBuilds.add(myRootGradleBuild);
|
||||
gradleBuilds.addAll(myNestedGradleBuilds);
|
||||
return convertGradleBuilds(gradleBuilds);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,18 +107,16 @@ public class GradleDaemonModelHolder {
|
||||
}
|
||||
|
||||
public @NotNull GradleModelHolderState pollPendingState() {
|
||||
DefaultGradleLightBuild rootBuild = pollPendingConvertedRootBuild();
|
||||
Collection<DefaultGradleLightBuild> nestedBuilds = pollPendingConvertedNestedBuilds();
|
||||
List<DefaultGradleLightBuild> builds = pollPendingConvertedNestedBuilds();
|
||||
DefaultGradleLightBuild rootBuild = builds.isEmpty() ? null : builds.get(0);
|
||||
List<DefaultGradleLightBuild> nestedBuilds = builds.size() > 1 ? builds.subList(1, builds.size()) : Collections.emptyList();
|
||||
|
||||
Map<GradleModelId, Object> models = pollAllPendingConvertedModels();
|
||||
return new GradleModelHolderState(rootBuild, nestedBuilds, models);
|
||||
return new GradleModelHolderState(rootBuild, new ArrayList<>(nestedBuilds), models);
|
||||
}
|
||||
|
||||
private @Nullable DefaultGradleLightBuild pollPendingConvertedRootBuild() {
|
||||
return GradleExecutorServiceUtil.poolPendingResult(myConvertedRootBuild);
|
||||
}
|
||||
|
||||
private @NotNull Collection<DefaultGradleLightBuild> pollPendingConvertedNestedBuilds() {
|
||||
Collection<DefaultGradleLightBuild> builds = GradleExecutorServiceUtil.poolPendingResult(myConvertedNestedBuilds);
|
||||
private @NotNull List<DefaultGradleLightBuild> pollPendingConvertedNestedBuilds() {
|
||||
List<DefaultGradleLightBuild> builds = GradleExecutorServiceUtil.poolPendingResult(myConvertedBuilds);
|
||||
return builds == null ? Collections.emptyList() : builds;
|
||||
}
|
||||
|
||||
@@ -133,45 +129,6 @@ public class GradleDaemonModelHolder {
|
||||
return modelMap;
|
||||
}
|
||||
|
||||
private static @NotNull Collection<DefaultGradleLightBuild> convertNestedGradleBuilds(
|
||||
@NotNull Collection<? extends GradleBuild> nestedGradleBuilds
|
||||
) {
|
||||
List<DefaultGradleLightBuild> nestedBuilds = new ArrayList<>();
|
||||
for (GradleBuild gradleBuild : nestedGradleBuilds) {
|
||||
DefaultGradleLightBuild build = DefaultGradleLightBuild.convertGradleBuild(gradleBuild);
|
||||
nestedBuilds.add(build);
|
||||
}
|
||||
setupNestedBuildHierarchy(nestedBuilds, nestedGradleBuilds);
|
||||
return nestedBuilds;
|
||||
}
|
||||
|
||||
private static void setupNestedBuildHierarchy(
|
||||
@NotNull Collection<DefaultGradleLightBuild> builds,
|
||||
@NotNull Collection<? extends GradleBuild> gradleBuilds
|
||||
) {
|
||||
Set<DefaultGradleLightBuild> updatedBuilds = new HashSet<>();
|
||||
Map<File, DefaultGradleLightBuild> rootDirsToBuilds = new HashMap<>();
|
||||
for (DefaultGradleLightBuild build : builds) {
|
||||
BuildIdentifier buildIdentifier = build.getBuildIdentifier();
|
||||
rootDirsToBuilds.put(buildIdentifier.getRootDir(), build);
|
||||
}
|
||||
|
||||
for (GradleBuild gradleBuild : gradleBuilds) {
|
||||
BuildIdentifier buildIdentifier = gradleBuild.getBuildIdentifier();
|
||||
DefaultGradleLightBuild build = rootDirsToBuilds.get(buildIdentifier.getRootDir());
|
||||
if (build == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (GradleBuild includedGradleBuild : gradleBuild.getIncludedBuilds()) {
|
||||
DefaultGradleLightBuild buildToUpdate = rootDirsToBuilds.get(includedGradleBuild.getBuildIdentifier().getRootDir());
|
||||
if (buildToUpdate != null && updatedBuilds.add(buildToUpdate)) {
|
||||
buildToUpdate.setParentBuildIdentifier(new DefaultBuildIdentifier(buildIdentifier.getRootDir()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConvertedModel {
|
||||
|
||||
private final @NotNull GradleModelId myId;
|
||||
|
||||
+41
-9
@@ -23,7 +23,7 @@ public final class DefaultGradleLightBuild implements GradleLightBuild, Serializ
|
||||
private final @NotNull DefaultGradleLightProject myRootProject;
|
||||
private final @NotNull List<DefaultGradleLightProject> myProjects;
|
||||
|
||||
private @Nullable DefaultBuildIdentifier myParentBuildIdentifier = null;
|
||||
private @Nullable DefaultGradleLightBuild myParentBuild = null;
|
||||
|
||||
public DefaultGradleLightBuild(@NotNull GradleBuild gradleBuild) {
|
||||
BasicGradleProject rootGradleProject = gradleBuild.getRootProject();
|
||||
@@ -66,12 +66,12 @@ public final class DefaultGradleLightBuild implements GradleLightBuild, Serializ
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DefaultBuildIdentifier getParentBuildIdentifier() {
|
||||
return myParentBuildIdentifier;
|
||||
public @Nullable DefaultGradleLightBuild getParentBuild() {
|
||||
return myParentBuild;
|
||||
}
|
||||
|
||||
public void setParentBuildIdentifier(@Nullable DefaultBuildIdentifier parentBuildIdentifier) {
|
||||
myParentBuildIdentifier = parentBuildIdentifier;
|
||||
public void setParentBuild(@Nullable DefaultGradleLightBuild parentBuild) {
|
||||
myParentBuild = parentBuild;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,10 +82,6 @@ public final class DefaultGradleLightBuild implements GradleLightBuild, Serializ
|
||||
'}';
|
||||
}
|
||||
|
||||
public static @NotNull DefaultGradleLightBuild convertGradleBuild(@NotNull GradleBuild gradleBuild) {
|
||||
return new DefaultGradleLightBuild(gradleBuild);
|
||||
}
|
||||
|
||||
public static <ModelA, ModelB> void replicateModelHierarchy(
|
||||
@NotNull ModelA rootModelA,
|
||||
@NotNull Function<@NotNull ModelA, @Nullable ModelB> getModel,
|
||||
@@ -115,4 +111,40 @@ public final class DefaultGradleLightBuild implements GradleLightBuild, Serializ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code gradleBuilds} converted to {@link DefaultGradleLightBuild} instances.
|
||||
* Original order is preserved: if a root build is a first element of {@code gradleBuilds},
|
||||
* then the first element of returned list is also a root build.
|
||||
*/
|
||||
public static @NotNull List<DefaultGradleLightBuild> convertGradleBuilds(
|
||||
@NotNull Collection<? extends GradleBuild> gradleBuilds
|
||||
) {
|
||||
Map<GradleBuild, DefaultGradleLightBuild> gradleBuildsToConverted = new HashMap<>();
|
||||
List<DefaultGradleLightBuild> convertedBuilds = new ArrayList<>();
|
||||
// TODO traverse builds via graph to avoid separated parent build field initialization
|
||||
for (GradleBuild gradleBuild : gradleBuilds) {
|
||||
DefaultGradleLightBuild build = new DefaultGradleLightBuild(gradleBuild);
|
||||
gradleBuildsToConverted.put(gradleBuild, build);
|
||||
convertedBuilds.add(build);
|
||||
}
|
||||
setHierarchy(gradleBuilds, gradleBuildsToConverted);
|
||||
return convertedBuilds;
|
||||
}
|
||||
|
||||
private static void setHierarchy(
|
||||
@NotNull Collection<? extends GradleBuild> gradleBuilds,
|
||||
Map<GradleBuild, DefaultGradleLightBuild> gradleBuildsToConverted
|
||||
) {
|
||||
for (GradleBuild gradleBuild : gradleBuilds) {
|
||||
DefaultGradleLightBuild build = gradleBuildsToConverted.get(gradleBuild);
|
||||
assert build != null;
|
||||
|
||||
for (GradleBuild includedGradleBuild : gradleBuild.getIncludedBuilds()) {
|
||||
DefaultGradleLightBuild buildToUpdate = gradleBuildsToConverted.get(includedGradleBuild);
|
||||
assert buildToUpdate != null;
|
||||
buildToUpdate.setParentBuild(build);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user