[gradle] for Gradle 8.0, process buildSrc builds in main resolver call IDEA-313748

GitOrigin-RevId: 5a4e66b803ac5d95e3a75af72c10ef36299e4ce1
This commit is contained in:
Nikita.Skvortsov
2023-02-24 10:52:17 +00:00
committed by intellij-monorepo-bot
parent bc682fe448
commit 1fa206f78c
6 changed files with 220 additions and 23 deletions
@@ -261,6 +261,54 @@ class GradleBuildSrcImportingTest : GradleImportingTestCase() {
"build2.buildSrc", "build2.buildSrc.main", "build2.buildSrc.test")
}
/*
Builds inclusion and buildSrc presence graph
A--> B--> D--> buildSrc
| └--> buildSrc
|
└--> C--> D--> buildSrc
└--> buildSrc
*/
@Test
@TargetVersions("8.0+")
fun `test buildSrc in a composite with build names duplication`() {
createSettingsFile("""
rootProject.name = "A"
includeBuild("B")
includeBuild("C")
""".trimIndent())
createProjectSubFile("B/settings.gradle", """
rootProject.name = "B"
includeBuild("D")
""".trimIndent())
createProjectSubFile("B/buildSrc/settings.gradle", "")
createProjectSubFile("B/D/settings.gradle", "rootProject.name = 'D'")
createProjectSubFile("B/D/buildSrc/settings.gradle", "")
createProjectSubFile("C/settings.gradle", """
rootProject.name = "C"
includeBuild("D")
""".trimIndent())
createProjectSubFile("C/buildSrc/settings.gradle", "")
createProjectSubFile("C/D/settings.gradle", "rootProject.name = 'D'")
createProjectSubFile("C/D/buildSrc/settings.gradle", "")
importProject("")
assertModules("A", "B", "C", "D", "C.D",
"B.buildSrc", "B.buildSrc.main", "B.buildSrc.test",
"C.buildSrc", "C.buildSrc.main", "C.buildSrc.test",
"D.buildSrc", "D.buildSrc.main", "D.buildSrc.test",
"C.D.buildSrc", "C.D.buildSrc.main", "C.D.buildSrc.test")
}
private fun assertBuildScriptClassPathContains(moduleName: String, expectedEntries: Collection<VirtualFile>) {
val module = ModuleManager.getInstance(myProject).findModuleByName(moduleName)
val modulePath = ExternalSystemApiUtil.getExternalProjectPath(module)
@@ -18,6 +18,7 @@ import org.gradle.tooling.model.idea.IdeaModule;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.Build;
import org.jetbrains.plugins.gradle.model.ProjectImportAction;
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings;
@@ -201,6 +202,20 @@ public class DefaultProjectResolverContext extends UserDataHolderBase implements
return myBuildSrcGroup;
}
@Nullable
@Override
public String getBuildSrcGroup(@NotNull IdeaModule module) {
if (!"buildSrc".equals(module.getProject().getName())) {
return myBuildSrcGroup;
}
String parentRootDir = module.getGradleProject().getProjectIdentifier().getBuildIdentifier().getRootDir().getParent();
return getModels().getAllBuilds().stream()
.filter(b -> b.getBuildIdentifier().getRootDir().toString().equals(parentRootDir))
.findFirst()
.map(Build::getName)
.orElse(myBuildSrcGroup);
}
@Override
public void report(@NotNull MessageEvent.Kind kind, @NotNull BuildIssue buildIssue) {
BuildIssueEventImpl buildIssueEvent = new BuildIssueEventImpl(myExternalSystemTaskId, buildIssue, kind);
@@ -47,6 +47,7 @@ import org.jetbrains.plugins.gradle.execution.target.TargetBuildLauncher;
import org.jetbrains.plugins.gradle.issue.DeprecatedGradleVersionIssue;
import org.jetbrains.plugins.gradle.model.*;
import org.jetbrains.plugins.gradle.model.data.BuildParticipant;
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData;
import org.jetbrains.plugins.gradle.model.data.CompositeBuildData;
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
import org.jetbrains.plugins.gradle.remote.impl.GradleLibraryNamesMixer;
@@ -57,8 +58,10 @@ import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.io.File;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -156,9 +159,12 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
getProjectDataFunction(resolverContext, projectResolverChain, false));
// auto-discover buildSrc projects of the main and included builds
File gradleUserHome = resolverContext.getUserData(GRADLE_HOME_DIR);
new GradleBuildSrcProjectsResolver(this, resolverContext, gradleUserHome, settings, listener, syncTaskId, projectResolverChain)
.discoverAndAppendTo(projectDataNode);
if (GradleVersion.version(resolverContext.getProjectGradleVersion()).compareTo(GradleVersion.version("8.0")) < 0) {
File gradleUserHome = resolverContext.getUserData(GRADLE_HOME_DIR);
new GradleBuildSrcProjectsResolver(this, resolverContext, gradleUserHome, settings, listener, syncTaskId, projectResolverChain)
.discoverAndAppendTo(projectDataNode);
}
return projectDataNode;
}
finally {
@@ -488,6 +494,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
mergeLibraryAndModuleDependencyData(resolverCtx, projectDataNode, resolverCtx.getGradleUserHome(), gradleHomeDir, gradleVersion);
}
processBuildSrcModules(resolverCtx, projectDataNode);
for (GradleProjectResolverExtension resolver = tracedResolverChain; resolver != null; resolver = resolver.getNext()) {
resolver.resolveFinished(projectDataNode);
}
@@ -503,6 +511,49 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
return projectDataNode;
}
private static void processBuildSrcModules(DefaultProjectResolverContext ctx, DataNode<ProjectData> projectDataNode) {
// since Gradle 8.0 buildSrc are available as composite build members
DataNode<CompositeBuildData> compositeNode = find(projectDataNode, CompositeBuildData.KEY);
if (compositeNode == null) return;
GradleBuildSrcProjectsResolver.Index index = GradleBuildSrcProjectsResolver.prepareIndexes(projectDataNode);
CompositeBuildData compositeBuildData = compositeNode.getData();
for (BuildParticipant participant : compositeBuildData.getCompositeParticipants()) {
if (participant.getRootProjectName().endsWith("buildSrc")) {
Set<String> buildSrcProjectPaths = participant.getProjects();
@NotNull Collection<DataNode<BuildScriptClasspathData>> buildClasspathNodes =
index.buildClasspathNodesMap().get(Path.of(participant.getRootPath()).getParent());
@NotNull Map<String, DataNode<? extends ModuleData>> buildSrcModules = new HashMap<>();
@Nullable AtomicReference<DataNode<? extends ModuleData>> buildSrcModuleNode = new AtomicReference<>();
findAll(projectDataNode, ProjectKeys.MODULE).stream()
.filter(node -> buildSrcProjectPaths.contains(node.getData().getLinkedExternalProjectPath()))
.forEach(node -> {
buildSrcModules.put(node.getData().getId(), node);
findAll(node, GradleSourceSetData.KEY).forEach(
sourceSetNode -> buildSrcModules.put(sourceSetNode.getData().getId(), sourceSetNode));
if (participant.getRootPath().equals(node.getData().getLinkedExternalProjectPath())) {
if (ctx.isResolveModulePerSourceSet()) {
buildSrcModuleNode.set(findChild(node, GradleSourceSetData.KEY,
sourceSetNode -> sourceSetNode.getData().getExternalName().endsWith(":main")));
}
else {
buildSrcModuleNode.set(node);
}
}
});
GradleBuildSrcProjectsResolver.addBuildSrcToBuildScriptClasspathData(buildClasspathNodes,
buildSrcModules,
buildSrcModuleNode.get());
}
}
}
private static boolean isCustomSerializationSupported(@NotNull DefaultProjectResolverContext resolverCtx,
GradleVersion gradleVersion,
boolean isCompositeBuildsSupported) {
@@ -177,7 +177,7 @@ public final class GradleProjectResolverUtil {
@NotNull ProjectResolverContext resolverCtx) {
String delimiter;
StringBuilder moduleName = new StringBuilder();
String buildSrcGroup = resolverCtx.getBuildSrcGroup();
String buildSrcGroup = resolverCtx.getBuildSrcGroup(gradleModule);
if (resolverCtx.isUseQualifiedModuleNames()) {
delimiter = ".";
if (StringUtil.isNotEmpty(buildSrcGroup)) {
@@ -228,8 +228,16 @@ public final class GradleProjectResolverUtil {
String gradlePath = projectIdentifier.getProjectPath();
String compositePrefix = "";
boolean isRootPath = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath);
if (GradleVersion.version(resolverCtx.getProjectGradleVersion()).compareTo(GradleVersion.version("8.0")) >= 0) {
if (build != resolverCtx.getModels().getMainBuild() || isRootPath) {
compositePrefix = buildCompositePrefixFromBuildNames(resolverCtx, build.getBuildIdentifier());
}
return compositePrefix + (StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? "" : gradlePath);
}
if (!isRootPath && build != resolverCtx.getModels().getMainBuild()) {
compositePrefix = buildCompositePrefixFromBuildNames(resolverCtx, build.getBuildIdentifier());
compositePrefix = build.getName();
} else if (!StringUtil.isEmpty(resolverCtx.getBuildSrcGroup())) {
compositePrefix = resolverCtx.getBuildSrcGroup() + (isRootPath ? ":" : ":buildSrc");
}
@@ -244,9 +252,15 @@ public final class GradleProjectResolverUtil {
IdeaProject ideaProject = gradleModule.getProject();
boolean isRootPath = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath);
if (GradleVersion.version(resolverCtx.getProjectGradleVersion()).compareTo(GradleVersion.version("8.0")) >= 0) {
if (ideaProject != resolverCtx.getModels().getModel(IdeaProject.class) || isRootPath) {
compositePrefix = buildCompositePrefixFromBuildNames(resolverCtx, gradleProject.getProjectIdentifier().getBuildIdentifier());
}
return compositePrefix + (StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? "" : gradlePath);
}
if (!isRootPath && ideaProject != resolverCtx.getModels().getModel(IdeaProject.class)) {
BuildIdentifier buildId = gradleModule.getGradleProject().getProjectIdentifier().getBuildIdentifier();
compositePrefix = buildCompositePrefixFromBuildNames(resolverCtx, buildId);
compositePrefix = ideaProject.getName();
} else if (!StringUtil.isEmpty(resolverCtx.getBuildSrcGroup())) {
compositePrefix = resolverCtx.getBuildSrcGroup() + (isRootPath ? ":" : ":buildSrc");
}
@@ -255,18 +269,48 @@ public final class GradleProjectResolverUtil {
}
@NotNull
private static String buildCompositePrefixFromBuildNames(@NotNull ProjectResolverContext resolverCtx, BuildIdentifier buildId) {
String compositePrefix;
List<String> list = Stream.iterate(
findIncludedBuild(resolverCtx, buildId),
Objects::nonNull,
b -> findIncludedBuild(resolverCtx, b.getParentBuildIdentifier())
)
.map(Build::getName)
.collect(Collectors.toList());
Collections.reverse(list);
compositePrefix = StringUtil.join(list, ".");
return compositePrefix;
private static String buildCompositePrefixFromBuildNames(@NotNull ProjectResolverContext resolverCtx, @NotNull BuildIdentifier buildId) {
String result = "";
Build build = findBuild(resolverCtx, buildId);
// if this build can not be found, return empty prefix
if (build == null) {
return result;
}
boolean isBuildSrc = build.getBuildIdentifier().getRootDir().getName().equals("buildSrc");
if (isBuildSrc) {
build = findBuild(resolverCtx, new BuildIdentifier() {
@Override
public File getRootDir() {
return buildId.getRootDir().getParentFile();
}
});
}
List<String> includedParentsList = Stream.iterate(
build,
Objects::nonNull,
b -> findIncludedBuild(resolverCtx, b.getParentBuildIdentifier())
).map(b -> {
var ideaProject = resolverCtx.getModels().getModel(b, IdeaProject.class);
if (ideaProject != null) {
return ideaProject.getName();
}
else {
return b.getName();
}
}).collect(Collectors.toList());
Collections.reverse(includedParentsList);
result = StringUtil.join(includedParentsList, ".");
if (isBuildSrc) {
result += ":buildSrc";
}
return result;
}
@Nullable
@@ -277,6 +321,14 @@ public final class GradleProjectResolverUtil {
return ContainerUtil.find(resolverCtx.getModels().getIncludedBuilds(), b -> FileUtil.filesEqual(b.getBuildIdentifier().getRootDir(), buildId.getRootDir()));
}
@Nullable
private static Build findBuild(@NotNull ProjectResolverContext resolverCtx, @Nullable BuildIdentifier buildId) {
if (buildId == null) {
return null;
}
return ContainerUtil.find(resolverCtx.getModels().getAllBuilds(), b -> FileUtil.filesEqual(b.getBuildIdentifier().getRootDir(), buildId.getRootDir()));
}
@NotNull
public static String getModuleId(String gradlePath, String moduleName) {
return StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? moduleName : gradlePath;
@@ -69,6 +69,9 @@ public interface ProjectResolverContext extends UserDataHolderEx {
@Nullable
String getBuildSrcGroup();
@Nullable
String getBuildSrcGroup(IdeaModule module);
@ApiStatus.Experimental
void report(@NotNull MessageEvent.Kind kind, @NotNull BuildIssue buildIssue);
}
@@ -19,6 +19,7 @@ import org.gradle.tooling.model.build.JavaEnvironment;
import org.gradle.tooling.model.gradle.BasicGradleProject;
import org.gradle.tooling.model.gradle.GradleBuild;
import org.gradle.tooling.model.idea.IdeaProject;
import org.gradle.util.GradleVersion;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -132,7 +133,7 @@ public class ProjectImportAction implements BuildAction<ProjectImportAction.AllM
assert myGradleBuild != null;
assert myModelConverter != null;
//We only need these later, but need to fetch them before fetching other models because of https://github.com/gradle/gradle/issues/20008
final Set<GradleBuild> nestedBuilds = getNestedBuilds(myGradleBuild);
final Set<GradleBuild> nestedBuilds = getNestedBuilds(myGradleBuild, controller);
final MyBuildController wrappedController = new MyBuildController(controller, myGradleBuild);
fetchProjectBuildModels(wrappedController, isProjectsLoadedAction, myGradleBuild);
addBuildModels(wrappedController, myAllModels, myGradleBuild, isProjectsLoadedAction);
@@ -218,7 +219,13 @@ public class ProjectImportAction implements BuildAction<ProjectImportAction.AllM
void accept(@NotNull GradleBuild build);
}
private Set<GradleBuild> getNestedBuilds(@NotNull GradleBuild rootBuild) {
private Set<GradleBuild> getNestedBuilds(@NotNull GradleBuild rootBuild, BuildController controller) {
BuildEnvironment environment = controller.getModel(BuildEnvironment.class);
GradleVersion envGradleVersion = null;
if (environment != null) {
// call to GradleVersion.current() will load version class from client classloader and return TAPI version number
envGradleVersion = GradleVersion.version(environment.getGradle().getGradleVersion());
}
if (!myIsCompositeBuildsSupported) {
return Collections.emptySet();
}
@@ -226,18 +233,32 @@ public class ProjectImportAction implements BuildAction<ProjectImportAction.AllM
Set<GradleBuild> nestedBuilds = new LinkedHashSet<>();
String rootBuildPath = rootBuild.getBuildIdentifier().getRootDir().getPath();
processedBuildsPaths.add(rootBuildPath);
Queue<GradleBuild> queue = new ArrayDeque<>(rootBuild.getIncludedBuilds());
Queue<GradleBuild> queue = new ArrayDeque<>(getEditableBuilds(rootBuild, envGradleVersion));
while (!queue.isEmpty()) {
GradleBuild includedBuild = queue.remove();
String includedBuildPath = includedBuild.getBuildIdentifier().getRootDir().getPath();
if (processedBuildsPaths.add(includedBuildPath)) {
nestedBuilds.add(includedBuild);
queue.addAll(includedBuild.getIncludedBuilds());
queue.addAll(getEditableBuilds(includedBuild, envGradleVersion));
}
}
return nestedBuilds;
}
/**
* Get nested builds to be imported by IDEA
* @param build parent build
* @return builds to be imported by IDEA. Before Gradle 8.0 - included builds, 8.0 and later - included and buildSrc builds
*/
private static DomainObjectSet<? extends GradleBuild> getEditableBuilds(@NotNull GradleBuild rootBuild,
@Nullable GradleVersion version) {
if (version != null && version.compareTo(GradleVersion.version("8.0")) >= 0) {
return rootBuild.getEditableBuilds();
} else {
return rootBuild.getIncludedBuilds();
}
}
private void fetchProjectBuildModels(BuildController controller, final boolean isProjectsLoadedAction, GradleBuild build) {
// Prepare nested build actions.
List<BuildAction<List<Runnable>>> buildActions = new ArrayList<>();
@@ -482,6 +503,13 @@ public class ProjectImportAction implements BuildAction<ProjectImportAction.AllM
return includedBuilds;
}
@NotNull
public List<Build> getAllBuilds() {
List<Build> result = new ArrayList<>(includedBuilds);
result.add(0, getMainBuild());
return result;
}
@Nullable
public BuildEnvironment getBuildEnvironment() {
return getModel(BuildEnvironment.class);