IDEA-352675 [Gradle|Sync] new: independently transfer external projects per Gradle project model

GitOrigin-RevId: 10b2830a575c6d49289374839310dee1a509a1aa
This commit is contained in:
Sergei Vorobyov
2024-06-12 12:17:23 +02:00
committed by intellij-monorepo-bot
parent f9ae000c95
commit 98fbbcd7ea
12 changed files with 149 additions and 232 deletions

View File

@@ -43,10 +43,7 @@ import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.gradle.api.ProjectConfigurationException;
import org.gradle.tooling.BuildActionFailureException;
import org.gradle.tooling.CancellationTokenSource;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.*;
import org.gradle.tooling.model.build.BuildEnvironment;
import org.gradle.tooling.model.idea.IdeaModule;
import org.gradle.tooling.model.idea.IdeaProject;
@@ -708,36 +705,33 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
}
private static void extractExternalProjectModels(@NotNull GradleIdeaModelHolder models) {
associateProjectModelsWithExternalProjects(models);
replicateBuildModelHierarchyInExternalProjectHierarchy(models);
replicateProjectModelHierarchyInExternalProjectHierarchy(models);
associateSourceSetModelsWithExternalProjects(models);
associateSourceSetDependencyModelsWithSourceSetModels(models);
}
private static void associateProjectModelsWithExternalProjects(@NotNull GradleIdeaModelHolder models) {
for (var buildModel : models.getAllBuilds()) {
var rootExternalProject = (DefaultExternalProject)models.getBuildModel(buildModel, ExternalProject.class);
if (rootExternalProject == null) continue;
var externalProjectIndex = createExternalProjectIndex(rootExternalProject);
for (var projectModel : buildModel.getProjects()) {
var projectPath = projectModel.getProjectIdentifier().getProjectPath();
var externalProject = externalProjectIndex.get(projectPath);
if (externalProject != null) {
models.addProjectModel(projectModel, ExternalProject.class, externalProject);
}
}
private static void replicateBuildModelHierarchyInExternalProjectHierarchy(@NotNull GradleIdeaModelHolder models) {
var rootBuildModel = models.getRootBuild();
var rootProjectModel = rootBuildModel.getRootProject();
var rootExternalProject = (DefaultExternalProject)models.getProjectModel(rootProjectModel, ExternalProject.class);
if (rootExternalProject == null) return;
for (var nestedBuildModel : models.getNestedBuilds()) {
var projectModel = nestedBuildModel.getRootProject();
var externalProject = (DefaultExternalProject)models.getProjectModel(projectModel, ExternalProject.class);
if (externalProject == null) continue;
rootExternalProject.addChildProject(externalProject);
}
}
private static void replicateBuildModelHierarchyInExternalProjectHierarchy(@NotNull GradleIdeaModelHolder models) {
var rootBuildModel = models.getRootBuild();
var rootExternalProject = (DefaultExternalProject)models.getBuildModel(rootBuildModel, ExternalProject.class);
if (rootExternalProject == null) return;
for (var nestedBuildModel : models.getNestedBuilds()) {
var externalProject = (DefaultExternalProject)models.getBuildModel(nestedBuildModel, ExternalProject.class);
if (externalProject == null) continue;
rootExternalProject.addChildProject(externalProject);
private static void replicateProjectModelHierarchyInExternalProjectHierarchy(@NotNull GradleIdeaModelHolder models) {
for (var buildModel : models.getAllBuilds()) {
DefaultGradleLightBuild.replicateModelHierarchy(
buildModel.getRootProject(),
it -> (DefaultExternalProject)models.getProjectModel(it, ExternalProject.class),
GradleLightProject::getChildProjects,
DefaultExternalProject::addChildProject
);
}
}
@@ -773,20 +767,6 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
}
}
private static @NotNull Map<String, DefaultExternalProject> createExternalProjectIndex(
@NotNull DefaultExternalProject rootExternalProject
) {
var externalProjectMap = new HashMap<String, DefaultExternalProject>();
var queue = new ArrayDeque<DefaultExternalProject>();
queue.add(rootExternalProject);
while (!queue.isEmpty()) {
var externalProject = queue.remove();
queue.addAll(externalProject.getChildProjects().values());
externalProjectMap.put(externalProject.getQName(), externalProject);
}
return externalProjectMap;
}
private static class Counter {
int count;

View File

@@ -12,14 +12,12 @@ object GradleModelBuilderMessageCollector : CounterUsagesCollector() {
override fun getGroup() = GROUP
private val GROUP: EventLogGroup = EventLogGroup("build.gradle.errors", 15)
private val GROUP: EventLogGroup = EventLogGroup("build.gradle.errors", 16)
private val ACTIVITY_ID = EventFields.Long("ide_activity_id")
private val MESSAGE_KIND = EventFields.Enum<Message.Kind>("message_kind")
private val MESSAGE_GROUP = EventFields.String("message_group", listOf(
Messages.PROJECT_MODEL_GROUP,
Messages.PROJECT_MODEL_CACHE_GET_GROUP,
Messages.PROJECT_MODEL_CACHE_SET_GROUP,
Messages.SCALA_PROJECT_MODEL_GROUP,

View File

@@ -22,13 +22,16 @@ public interface GradleLightBuild extends BuildModel {
@NotNull
String getName();
@NotNull
Collection<? extends GradleLightProject> getProjects();
@Override
@NotNull
BuildIdentifier getBuildIdentifier();
@Nullable
BuildIdentifier getParentBuildIdentifier();
@NotNull
GradleLightProject getRootProject();
@NotNull
Collection<? extends GradleLightProject> getProjects();
}

View File

@@ -6,6 +6,7 @@ import org.gradle.tooling.model.ProjectModel;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Collection;
/**
* {@link GradleLightProject} is a "lightweight" model represents a {@link org.gradle.tooling.model.GradleProject}.
@@ -31,4 +32,7 @@ public interface GradleLightProject extends ProjectModel {
@Override
@NotNull
ProjectIdentifier getProjectIdentifier();
@NotNull
Collection<? extends GradleLightProject> getChildProjects();
}

View File

@@ -1,31 +0,0 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.gradle.tooling.util;
import org.jetbrains.plugins.gradle.model.ExternalProject;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
/**
* @author Vladislav.Soroka
*/
public final class ProjectHierarchyIterator implements Iterator<ExternalProject> {
private final Queue<ExternalProject> myProjects = new LinkedList<>();
public ProjectHierarchyIterator(ExternalProject project) {
myProjects.add(project);
}
@Override
public boolean hasNext() {
return !myProjects.isEmpty();
}
@Override
public ExternalProject next() {
ExternalProject project = myProjects.remove();
myProjects.addAll(project.getChildProjects().values());
return project;
}
}

View File

@@ -1,75 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.gradle.toolingExtension.impl.model.projectModel;
import com.intellij.gradle.toolingExtension.impl.modelBuilder.Messages;
import com.intellij.gradle.toolingExtension.impl.util.GradleProjectUtil;
import com.intellij.gradle.toolingExtension.modelAction.GradleModelFetchPhase;
import org.gradle.api.Project;
import org.gradle.tooling.model.ProjectIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.DefaultExternalProject;
import org.jetbrains.plugins.gradle.tooling.Message;
import org.jetbrains.plugins.gradle.tooling.ModelBuilderContext;
import org.jetbrains.plugins.gradle.tooling.ModelBuilderContext.DataProvider;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class GradleExternalProjectCache {
private final ModelBuilderContext context;
private final ConcurrentMap<ProjectIdentifier, DefaultExternalProject> models;
private GradleExternalProjectCache(@NotNull ModelBuilderContext context) {
this.context = context;
this.models = new ConcurrentHashMap<>();
}
public @NotNull DefaultExternalProject getProjectModel(@NotNull Project project) {
ProjectIdentifier projectIdentifier = GradleProjectUtil.getProjectIdentifier(project);
DefaultExternalProject model = models.get(projectIdentifier);
if (model == null) {
context.getMessageReporter().createMessage()
.withGroup(Messages.PROJECT_MODEL_CACHE_GET_GROUP)
.withTitle("Project model isn't found")
.withText(
"Projects for " + project.getDisplayName() + " wasn't collected. " +
"All projects should be collected during " + GradleModelFetchPhase.PROJECT_MODEL_PHASE + "."
)
.withInternal().withStackTrace()
.withKind(Message.Kind.ERROR)
.reportMessage(project);
return new DefaultExternalProject();
}
return model;
}
public void setProjectModel(@NotNull Project project, @NotNull DefaultExternalProject model) {
ProjectIdentifier projectIdentifier = GradleProjectUtil.getProjectIdentifier(project);
DefaultExternalProject previousModel = models.put(projectIdentifier, model);
if (previousModel != null) {
context.getMessageReporter().createMessage()
.withGroup(Messages.PROJECT_MODEL_CACHE_SET_GROUP)
.withTitle("Project model redefinition")
.withText("Projects for " + project.getDisplayName() + " was already collected.")
.withInternal().withStackTrace()
.withKind(Message.Kind.ERROR)
.reportMessage(project);
}
}
/**
* Marks that an external project model is loaded with errors.
* This mark means that error for {@code project} is already processed and reported.
*/
public void markProjectModelAsError(@NotNull Project project) {
ProjectIdentifier projectIdentifier = GradleProjectUtil.getProjectIdentifier(project);
models.put(projectIdentifier, new DefaultExternalProject());
}
private static final @NotNull DataProvider<GradleExternalProjectCache> INSTANCE_PROVIDER = GradleExternalProjectCache::new;
public static @NotNull GradleExternalProjectCache getInstance(@NotNull ModelBuilderContext context) {
return context.getData(INSTANCE_PROVIDER);
}
}

View File

@@ -61,10 +61,6 @@ public class GradleExternalProjectModelBuilder extends AbstractModelBuilderServi
externalProject.setGroup(wrap(project.getGroup()));
externalProject.setProjectDir(project.getProjectDir());
externalProject.setTasks(getTasks(project, context));
externalProject.setChildProjects(getChildProjects(project, context));
GradleExternalProjectCache.getInstance(context)
.setProjectModel(project, externalProject);
return externalProject;
}
@@ -103,19 +99,6 @@ public class GradleExternalProjectModelBuilder extends AbstractModelBuilderServi
return result;
}
private static @NotNull Map<String, DefaultExternalProject> getChildProjects(
@NotNull Project project,
@NotNull ModelBuilderContext context
) {
GradleExternalProjectCache projectCache = GradleExternalProjectCache.getInstance(context);
Map<String, DefaultExternalProject> result = new TreeMap<>();
for (Map.Entry<String, Project> entry : project.getChildProjects().entrySet()) {
DefaultExternalProject childProject = projectCache.getProjectModel(entry.getValue());
result.put(entry.getKey(), childProject);
}
return result;
}
private static @NotNull String wrap(@Nullable Object o) {
return o instanceof CharSequence ? o.toString() : "";
}
@@ -127,9 +110,6 @@ public class GradleExternalProjectModelBuilder extends AbstractModelBuilderServi
@NotNull ModelBuilderContext context,
@NotNull Exception exception
) {
GradleExternalProjectCache.getInstance(context)
.markProjectModelAsError(project);
context.getMessageReporter().createMessage()
.withGroup(Messages.PROJECT_MODEL_GROUP)
.withKind(Message.Kind.ERROR)

View File

@@ -4,7 +4,6 @@ package com.intellij.gradle.toolingExtension.impl.model.projectModel;
import com.intellij.gradle.toolingExtension.impl.util.GradleModelProviderUtil;
import com.intellij.gradle.toolingExtension.modelAction.GradleModelFetchPhase;
import org.gradle.tooling.BuildController;
import org.gradle.tooling.model.gradle.BasicGradleProject;
import org.gradle.tooling.model.gradle.GradleBuild;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -27,13 +26,6 @@ public class GradleExternalProjectModelProvider implements ProjectImportModelPro
@NotNull GradleBuild buildModel,
@NotNull GradleModelConsumer modelConsumer
) {
GradleModelProviderUtil.buildModelsBackwardRecursively(controller, buildModel, ExternalProject.class, new GradleModelConsumer() {
@Override
public void consumeProjectModel(@NotNull BasicGradleProject projectModel, @NotNull Object object, @NotNull Class<?> clazz) {
if (projectModel == buildModel.getRootProject()) {
modelConsumer.consumeBuildModel(buildModel, object, clazz);
}
}
});
GradleModelProviderUtil.buildModels(controller, buildModel, ExternalProject.class, modelConsumer);
}
}

View File

@@ -9,8 +9,6 @@ public final class Messages {
// @formatter:off
public final static @NotNull String PROJECT_MODEL_GROUP = "gradle.projectModel.group";
public final static @NotNull String PROJECT_MODEL_CACHE_GET_GROUP = "gradle.projectModel.cacheGet.group";
public final static @NotNull String PROJECT_MODEL_CACHE_SET_GROUP = "gradle.projectModel.cacheSet.group";
public final static @NotNull String SCALA_PROJECT_MODEL_GROUP = "gradle.scalaProjectModel.group";

View File

@@ -104,19 +104,4 @@ public final class GradleModelProviderUtil {
return gradleProject.getChildren();
});
}
public static <M> void buildModelsBackwardRecursively(
@NotNull BuildController controller,
@NotNull GradleBuild buildModel,
@NotNull Class<M> modelClass,
@NotNull GradleModelConsumer consumer
) {
@NotNull BasicGradleProject root = buildModel.getRootProject();
GradleTreeTraverserUtil.backwardTraverseTree(root, BasicGradleProject::getChildren, (gradleProject) -> {
M model = controller.findModel(gradleProject, modelClass);
if (model != null) {
consumer.consumeProjectModel(gradleProject, model, modelClass);
}
});
}
}

View File

@@ -1,31 +1,43 @@
// Copyright 2000-2024 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 com.intellij.openapi.util.Pair;
import org.gradle.tooling.internal.gradle.DefaultBuildIdentifier;
import org.gradle.tooling.internal.gradle.DefaultProjectIdentifier;
import org.gradle.tooling.model.BuildIdentifier;
import org.gradle.tooling.model.ProjectIdentifier;
import org.gradle.tooling.model.gradle.BasicGradleProject;
import org.gradle.tooling.model.gradle.GradleBuild;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@ApiStatus.Internal
public final class DefaultGradleLightBuild implements GradleLightBuild, Serializable {
private final @NotNull String myName;
private final @NotNull DefaultBuildIdentifier myBuildIdentifier;
private final @NotNull Collection<DefaultGradleLightProject> myProjects = new ArrayList<>(0);
private final @NotNull DefaultGradleLightProject myRootProject;
private final @NotNull List<DefaultGradleLightProject> myProjects;
private @Nullable DefaultBuildIdentifier myParentBuildIdentifier = null;
private DefaultGradleLightBuild(@NotNull String name, @NotNull File rootDir) {
public DefaultGradleLightBuild(
@NotNull String name,
@NotNull DefaultBuildIdentifier identifier,
@NotNull DefaultGradleLightProject project,
@NotNull List<DefaultGradleLightProject> projects
) {
myName = name;
myBuildIdentifier = new DefaultBuildIdentifier(rootDir);
myBuildIdentifier = identifier;
myRootProject = project;
myProjects = projects;
}
@Override
@@ -34,17 +46,22 @@ public final class DefaultGradleLightBuild implements GradleLightBuild, Serializ
}
@Override
public @NotNull BuildIdentifier getBuildIdentifier() {
public @NotNull DefaultBuildIdentifier getBuildIdentifier() {
return myBuildIdentifier;
}
@Override
public @NotNull Collection<? extends GradleLightProject> getProjects() {
public @NotNull DefaultGradleLightProject getRootProject() {
return myRootProject;
}
@Override
public @NotNull List<DefaultGradleLightProject> getProjects() {
return myProjects;
}
@Override
public @Nullable BuildIdentifier getParentBuildIdentifier() {
public @Nullable DefaultBuildIdentifier getParentBuildIdentifier() {
return myParentBuildIdentifier;
}
@@ -52,13 +69,84 @@ public final class DefaultGradleLightBuild implements GradleLightBuild, Serializ
myParentBuildIdentifier = parentBuildIdentifier;
}
@Override
public String toString() {
return "ProjectModel{" +
"name='" + myName + '\'' +
", id=" + myBuildIdentifier +
'}';
}
public static @NotNull DefaultGradleLightBuild convertGradleBuild(@NotNull GradleBuild gradleBuild) {
String name = gradleBuild.getRootProject().getName();
File rootDir = gradleBuild.getBuildIdentifier().getRootDir();
DefaultGradleLightBuild build = new DefaultGradleLightBuild(name, rootDir);
for (BasicGradleProject gradleProject : gradleBuild.getProjects()) {
build.myProjects.add(DefaultGradleLightProject.convertGradleProject(gradleProject));
Map<BasicGradleProject, DefaultGradleLightProject> projects = gradleBuild.getProjects().stream()
.map(it -> new Pair<>(it, convertGradleProject(it)))
.collect(Collectors.toMap(it -> it.getFirst(), it -> it.getSecond()));
BasicGradleProject rootGradleProject = gradleBuild.getRootProject();
replicateModelHierarchy(
rootGradleProject,
it -> projects.get(it),
BasicGradleProject::getChildren,
DefaultGradleLightProject::addChildProject
);
return new DefaultGradleLightBuild(
rootGradleProject.getName(),
convertGradleBuildIdentifier(gradleBuild.getBuildIdentifier()),
projects.get(rootGradleProject),
new ArrayList<>(projects.values())
);
}
public static @NotNull DefaultGradleLightProject convertGradleProject(@NotNull BasicGradleProject gradleProject) {
return new DefaultGradleLightProject(
gradleProject.getName(),
gradleProject.getPath(),
gradleProject.getProjectDirectory(),
convertGradleProjectIdentifier(gradleProject.getProjectIdentifier())
);
}
private static @NotNull DefaultBuildIdentifier convertGradleBuildIdentifier(@NotNull BuildIdentifier buildIdentifier) {
return new DefaultBuildIdentifier(
buildIdentifier.getRootDir()
);
}
private static @NotNull DefaultProjectIdentifier convertGradleProjectIdentifier(@NotNull ProjectIdentifier projectIdentifier) {
return new DefaultProjectIdentifier(
projectIdentifier.getBuildIdentifier().getRootDir(),
projectIdentifier.getProjectPath()
);
}
public static <ModelA, ModelB> void replicateModelHierarchy(
@NotNull ModelA rootModelA,
@NotNull Function<@NotNull ModelA, @Nullable ModelB> getModel,
@NotNull Function<@NotNull ModelA, @NotNull Collection<? extends @NotNull ModelA>> getChildModel,
@NotNull BiConsumer<@NotNull ModelB, @NotNull ModelB> addChildModel
) {
ModelB rootModelB = getModel.apply(rootModelA);
if (rootModelB == null) return;
Queue<Pair<ModelA, ModelB>> queue = new ArrayDeque<>();
queue.add(new Pair<>(rootModelA, rootModelB));
while (!queue.isEmpty()) {
Pair<ModelA, ModelB> parentModel = queue.remove();
ModelA parentModelA = parentModel.getFirst();
ModelB parentModelB = parentModel.getSecond();
for (ModelA childModelA : getChildModel.apply(parentModelA)) {
ModelB childModelB = getModel.apply(childModelA);
if (childModelB == null) continue;
queue.add(new Pair<>(childModelA, childModelB));
addChildModel.accept(parentModelB, childModelB);
}
}
return build;
}
}

View File

@@ -3,22 +3,24 @@ package org.jetbrains.plugins.gradle.model;
import org.gradle.tooling.internal.gradle.DefaultProjectIdentifier;
import org.gradle.tooling.model.ProjectIdentifier;
import org.gradle.tooling.model.gradle.BasicGradleProject;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ApiStatus.Internal
class DefaultGradleLightProject implements GradleLightProject, Serializable {
public final class DefaultGradleLightProject implements GradleLightProject, Serializable {
private final @NotNull String myName;
private final @NotNull String myPath;
private final @NotNull File myProjectDirectory;
private final @NotNull DefaultProjectIdentifier myProjectIdentifier;
private final @NotNull List<DefaultGradleLightProject> myChildren = new ArrayList<>();
private DefaultGradleLightProject(
public DefaultGradleLightProject(
@NotNull String name,
@NotNull String path,
@NotNull File projectDirectory,
@@ -50,6 +52,15 @@ class DefaultGradleLightProject implements GradleLightProject, Serializable {
return myProjectIdentifier;
}
@Override
public @NotNull List<DefaultGradleLightProject> getChildProjects() {
return myChildren;
}
public void addChildProject(@NotNull DefaultGradleLightProject childProject) {
myChildren.add(childProject);
}
@Override
public String toString() {
return "ProjectModel{" +
@@ -57,20 +68,4 @@ class DefaultGradleLightProject implements GradleLightProject, Serializable {
", id=" + myProjectIdentifier +
'}';
}
public static @NotNull DefaultGradleLightProject convertGradleProject(@NotNull BasicGradleProject gradleProject) {
return new DefaultGradleLightProject(
gradleProject.getName(),
gradleProject.getPath(),
gradleProject.getProjectDirectory(),
convertGradleProjectIdentifier(gradleProject.getProjectIdentifier())
);
}
private static @NotNull DefaultProjectIdentifier convertGradleProjectIdentifier(@NotNull ProjectIdentifier projectIdentifier) {
return new DefaultProjectIdentifier(
projectIdentifier.getBuildIdentifier().getRootDir(),
projectIdentifier.getProjectPath()
);
}
}