From c5c5c3ffc090f5dea1270fde2548c27454fed7a6 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Sun, 26 Feb 2017 10:54:04 +0300 Subject: [PATCH] gradle: deduplicate module names across IDE project (IDEA-168284, IDEA-159811, IDEA-167974, IDEA-164489, IDEA-133950) --- .../model/project/ModuleData.java | 7 +- .../project/IdeModelsProviderImpl.java | 40 ++++++-- .../project/IdeModifiableModelsProvider.java | 4 + .../AbstractIdeModifiableModelsProvider.java | 18 ++++ .../manage/AbstractDependencyDataService.java | 12 ++- .../manage/AbstractModuleDataService.java | 21 +---- .../manage/ModuleDependencyDataService.java | 7 +- .../gradle/model/data/BuildParticipant.java | 10 ++ .../BaseGradleProjectResolverExtension.java | 37 +++++--- .../DefaultProjectResolverContext.java | 4 +- .../project/GradleProjectResolver.java | 86 ++++++++++++------ .../project/GradleProjectResolverUtil.java | 33 +++++-- .../GradleProjectCompositeSelectorDialog.java | 15 +-- .../GradleCompositeImportingTest.java | 91 +++++++++++++++++++ .../plugins/gradle/model/ExternalProject.java | 3 + .../gradle/model/ProjectImportAction.java | 28 ++++-- .../gradle/model/DefaultExternalProject.java | 15 ++- .../builder/ExternalProjectBuilderImpl.groovy | 10 +- 18 files changed, 332 insertions(+), 109 deletions(-) diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java index c2c06d218043..bd9ad5180310 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java @@ -209,6 +209,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf ModuleData that = (ModuleData)o; + if (!myId.equals(that.myId)) return false; if (myGroup != null ? !myGroup.equals(that.myGroup) : that.myGroup != null) return false; if (!myModuleTypeId.equals(that.myModuleTypeId)) return false; if (myVersion != null ? !myVersion.equals(that.myVersion) : that.myVersion != null) return false; @@ -220,6 +221,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf @Override public int hashCode() { int result = super.hashCode(); + result = 31 * result + myId.hashCode(); result = 31 * result + myModuleTypeId.hashCode(); result = 31 * result + (myGroup != null ? myGroup.hashCode() : 0); result = 31 * result + (myVersion != null ? myVersion.hashCode() : 0); @@ -229,9 +231,6 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf @Override public String toString() { - return String.format("module '%s:%s:%s'", - myGroup == null ? "" : myGroup, - getExternalName(), - myVersion == null ? "" : myVersion); + return getId(); } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java index 56aa9449c796..61b206ad9d51 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java @@ -24,20 +24,20 @@ import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Function; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.List; import java.util.Set; import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.*; +import static com.intellij.openapi.util.io.FileUtil.pathsEqual; /** * @author Vladislav.Soroka @@ -75,19 +75,39 @@ public class IdeModelsProviderImpl implements IdeModelsProvider { @Nullable @Override public Module findIdeModule(@NotNull ModuleData module) { - final Module ideModule = findIdeModule(module.getInternalName()); - return isExternalSystemAwareModule(module.getOwner(), ideModule) ? ideModule : null; + for (String candidate : suggestModuleNameCandidates(module)) { + Module ideModule = findIdeModule(candidate); + if (ideModule != null && isApplicableIdeModule(module, ideModule)) { + return ideModule; + } + } + return null; + } + + protected String[] suggestModuleNameCandidates(@NotNull ModuleData module) { + String prefix = module.getGroup(); + File modulePath = new File(module.getLinkedExternalProjectPath()); + if(modulePath.isFile()) { + modulePath = modulePath.getParentFile(); + } + if (modulePath.getParentFile() != null) { + prefix = modulePath.getParentFile().getName(); + } + return new String[]{ + module.getInternalName(), + prefix + '-' + module.getInternalName(), + prefix + '-' + module.getInternalName() + "~1"}; + } + + private static boolean isApplicableIdeModule(@NotNull ModuleData moduleData, @NotNull Module ideModule) { + return isExternalSystemAwareModule(moduleData.getOwner(), ideModule) && + pathsEqual(getExternalProjectPath(ideModule), moduleData.getLinkedExternalProjectPath()); } @Nullable @Override public Module findIdeModule(@NotNull String ideModuleName) { - for (Module module : getModules()) { - if (ideModuleName.equals(module.getName())) { - return module; - } - } - return null; + return ModuleManager.getInstance(myProject).findModuleByName(ideModuleName); } @Nullable diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java index a72dc7cc601c..218e2490c6c6 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java @@ -17,6 +17,7 @@ package com.intellij.openapi.externalSystem.service.project; import com.intellij.facet.ModifiableFacetModel; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ModifiableRootModel; @@ -35,6 +36,9 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider { @NotNull Module newModule(@NotNull @NonNls String filePath, final String moduleTypeId); + @NotNull + Module newModule(@NotNull ModuleData moduleData); + @NotNull ModifiableModuleModel getModifiableModuleModel(); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java index 45b171e20b67..6a98ae509460 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java @@ -19,9 +19,11 @@ import com.intellij.facet.Facet; import com.intellij.facet.FacetModel; import com.intellij.facet.FacetTypeId; import com.intellij.facet.ModifiableFacetModel; +import com.intellij.ide.highlighter.ModuleFileType; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.project.LibraryData; +import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleWithNameAlreadyExists; @@ -56,6 +58,7 @@ import java.io.File; import java.util.*; import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.isRelated; +import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.toCanonicalPath; public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProviderImpl implements IdeModifiableModelsProvider { private static final Logger LOG = Logger.getInstance(AbstractIdeModifiableModelsProvider.class); @@ -131,6 +134,21 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi return module; } + @NotNull + @Override + public Module newModule(@NotNull ModuleData moduleData) { + String filePath = moduleData.getModuleFilePath(); + String moduleTypeId = moduleData.getModuleTypeId(); + for (String candidate : suggestModuleNameCandidates(moduleData)) { + Module module = findIdeModule(candidate); + if (module == null) { + filePath = toCanonicalPath(moduleData.getModuleFileDirectoryPath() + "/" + candidate + ModuleFileType.DOT_DEFAULT_EXTENSION); + break; + } + } + return newModule(filePath, moduleTypeId); + } + @Nullable @Override public Module findIdeModule(@NotNull String ideModuleName) { diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java index 379057d379c3..97d869729a77 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java @@ -95,7 +95,17 @@ public abstract class AbstractDependencyDataService byModuleName = MultiMap.create(); for (DataNode node : toImport) { final AbstractDependencyData data = node.getData(); - byModuleName.putValue(data.getOwnerModule().getInternalName(), getInternalName(data)); + Module ownerModule = modelsProvider.findIdeModule(data.getOwnerModule()); + assert ownerModule != null; + String depName; + if(data instanceof ModuleDependencyData) { + Module targetModule = modelsProvider.findIdeModule(((ModuleDependencyData)data).getTarget()); + assert targetModule != null; + depName = targetModule.getName(); + } else { + depName = getInternalName(data); + } + byModuleName.putValue(ownerModule.getName(), depName); } final ModifiableModuleModel modifiableModuleModel = modelsProvider.getModifiableModuleModel(); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java index 07ed220fef5d..cefac479cc51 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java @@ -33,14 +33,12 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleWithNameAlreadyExists; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.pom.java.LanguageLevel; import com.intellij.ui.CheckBoxList; @@ -48,7 +46,6 @@ import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; -import com.intellij.util.Function; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; @@ -57,7 +54,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.io.File; import java.util.*; import java.util.List; @@ -116,7 +112,7 @@ public abstract class AbstractModuleDataService extends Ab @NotNull Project project) { for (final DataNode module : toCreate) { ModuleData data = module.getData(); - final Module created = modelsProvider.newModule(data.getModuleFilePath(), data.getModuleTypeId()); + final Module created = modelsProvider.newModule(data); module.putUserData(MODULE_KEY, created); Set orphanFiles = project.getUserData(ORPHAN_MODULE_FILES); if (orphanFiles != null) { @@ -154,23 +150,12 @@ public abstract class AbstractModuleDataService extends Ab Collection> result = ContainerUtilRt.newArrayList(); for (DataNode node : modules) { ModuleData moduleData = node.getData(); - Module module = modelsProvider.findIdeModule(moduleData.getInternalName()); + Module module = modelsProvider.findIdeModule(moduleData); if (module == null) { result.add(node); } else { - if (!FileUtil.pathsEqual(ExternalSystemApiUtil.getExternalProjectPath(module), moduleData.getLinkedExternalProjectPath())) { - modelsProvider.getModifiableModuleModel().disposeModule(module); - result.add(node); - Set orphanFiles = project.getUserData(ORPHAN_MODULE_FILES); - if (orphanFiles == null) { - project.putUserData(ORPHAN_MODULE_FILES, orphanFiles = ContainerUtil.newHashSet()); - } - orphanFiles.add(module.getModuleFilePath()); - } - else { - node.putUserData(MODULE_KEY, module); - } + node.putUserData(MODULE_KEY, module); } } return result; diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDependencyDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDependencyDataService.java index 2752d312331a..31e4b100e3c1 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDependencyDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDependencyDataService.java @@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.Key; import com.intellij.openapi.externalSystem.model.ProjectKeys; +import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData; import com.intellij.openapi.externalSystem.model.project.OrderAware; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; @@ -97,8 +98,8 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService ideDependencyModule == null - ? modifiableRootModel.addInvalidModuleEntry(moduleName) + ? modifiableRootModel.addInvalidModuleEntry(moduleData.getInternalName()) : modifiableRootModel.addModuleOrderEntry(ideDependencyModule)); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java index e2228afcb465..cb3e00db3f64 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java @@ -31,9 +31,19 @@ import java.util.Set; */ @Tag("build") public class BuildParticipant implements Serializable { + private String myRootProjectName; private String myRootPath; @NotNull private Set myProjects = new HashSet<>(); + @Attribute("name") + public String getRootProjectName() { + return myRootProjectName; + } + + public void setRootProjectName(String rootProjectName) { + myRootProjectName = rootProjectName; + } + @Attribute("path") public String getRootPath() { return myRootPath; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 9755dbea54e7..480b4b1a8e85 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -68,7 +68,7 @@ import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData; import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData; import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService; import org.jetbrains.plugins.gradle.service.project.data.GradleExtensionsDataService; -import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings; +import org.jetbrains.plugins.gradle.settings.GradleExecutionWorkspace; import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl; import org.jetbrains.plugins.gradle.tooling.internal.init.Init; import org.jetbrains.plugins.gradle.util.GradleBundle; @@ -187,7 +187,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver mainModuleData.setIdeModuleGroup(isRootModule ? null : moduleGroup); for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) { - final String moduleId = getModuleId(externalProject, sourceSet); + final String moduleId = getModuleId(resolverCtx, gradleModule, sourceSet); final String moduleExternalName = gradleModule.getName() + ":" + sourceSet.getName(); final String moduleInternalName = getInternalModuleName(gradleModule, sourceSet.getName()); @@ -301,7 +301,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver @NotNull DataNode ideModule) { ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class); if (externalProject != null) { - processSourceSets(externalProject, ideModule, new SourceSetsProcessor() { + processSourceSets(resolverCtx, gradleModule, externalProject, ideModule, new SourceSetsProcessor() { @Override public void process(@NotNull DataNode dataNode, @NotNull ExternalSourceSet sourceSet) { for (Map.Entry directorySetEntry : sourceSet.getSources().entrySet()) { @@ -350,7 +350,9 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver } } - private static void processSourceSets(@NotNull ExternalProject externalProject, + private static void processSourceSets(@NotNull ProjectResolverContext resolverCtx, + @NotNull IdeaModule gradleModule, + @NotNull ExternalProject externalProject, @NotNull DataNode ideModule, @NotNull SourceSetsProcessor processor) { Map> sourceSetsMap = ContainerUtil.newHashMap(); @@ -361,7 +363,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) { if (sourceSet == null || sourceSet.getSources().isEmpty()) continue; - final String moduleId = getModuleId(externalProject, sourceSet); + final String moduleId = getModuleId(resolverCtx, gradleModule, sourceSet); final DataNode moduleDataNode = sourceSetsMap.isEmpty() ? ideModule : sourceSetsMap.get(moduleId); if (moduleDataNode == null) continue; @@ -380,7 +382,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver final Map> moduleOutputsMap = projectDataNode.getUserData(MODULES_OUTPUTS); assert moduleOutputsMap != null; - processSourceSets(externalProject, ideModule, new SourceSetsProcessor() { + processSourceSets(resolverCtx, gradleModule, externalProject, ideModule, new SourceSetsProcessor() { @Override public void process(@NotNull DataNode dataNode, @NotNull ExternalSourceSet sourceSet) { for (Map.Entry directorySetEntry : sourceSet.getSources().entrySet()) { @@ -470,7 +472,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver if (resolverCtx.isResolveModulePerSourceSet()) { assert sourceSetMap != null; - processSourceSets(externalProject, ideModule, new SourceSetsProcessor() { + processSourceSets(resolverCtx, gradleModule, externalProject, ideModule, new SourceSetsProcessor() { @Override public void process(@NotNull DataNode dataNode, @NotNull ExternalSourceSet sourceSet) { buildDependencies(resolverCtx, sourceSetMap, artifactsMap, dataNode, sourceSet.getDependencies(), ideProject); @@ -493,7 +495,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver DependencyScope scope = parseScope(dependency.getScope()); if (dependency instanceof IdeaModuleDependency) { - ModuleDependencyData d = buildDependency(resolverCtx.getSettings(), ideModule, (IdeaModuleDependency)dependency, ideProject); + ModuleDependencyData d = buildDependency(resolverCtx, ideModule, (IdeaModuleDependency)dependency, ideProject); d.setExported(dependency.getExported()); if (scope != null) { d.setScope(scope); @@ -826,23 +828,30 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver } @NotNull - private static ModuleDependencyData buildDependency(@Nullable GradleExecutionSettings executionSettings, + private static ModuleDependencyData buildDependency(@NotNull ProjectResolverContext resolverContext, @NotNull DataNode ownerModule, @NotNull IdeaModuleDependency dependency, @NotNull DataNode ideProject) throws IllegalStateException { IdeaModule module = dependency.getDependencyModule(); if (module == null) { - if (executionSettings != null) { + if (resolverContext.getSettings() != null) { String moduleName = dependency.getTargetModuleName(); - ModuleData moduleData = executionSettings.getExecutionWorkspace().findModuleDataByName(moduleName); + GradleExecutionWorkspace executionWorkspace = resolverContext.getSettings().getExecutionWorkspace(); + ModuleData moduleData = executionWorkspace.findModuleDataByName(moduleName); if (moduleData != null) { return new ModuleDependencyData(ownerModule.getData(), moduleData); } - else if (StringUtil.isNotEmpty(moduleName)) { - return new ModuleDependencyData( - ownerModule.getData(), new ModuleData("", GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName, "", "")); + else { + for (IdeaProject project : resolverContext.getModels().getIncludedBuilds()) { + moduleData = executionWorkspace.findModuleDataByName(project.getName() + ':' + moduleName); + if (moduleData != null) { + return new ModuleDependencyData(ownerModule.getData(), moduleData); + } + } } + return new ModuleDependencyData( + ownerModule.getData(), new ModuleData("", GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName, "", "")); } throw new IllegalStateException( String.format("Can't parse gradle module dependency '%s'. Reason: referenced module is null", dependency) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/DefaultProjectResolverContext.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/DefaultProjectResolverContext.java index 1b99465d3327..35c293d59edb 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/DefaultProjectResolverContext.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/DefaultProjectResolverContext.java @@ -141,13 +141,13 @@ public class DefaultProjectResolverContext extends UserDataHolderBase implements @Nullable @Override public T getExtraProject(Class modelClazz) { - return myModels.getExtraProject(null, modelClazz); + return myModels.getExtraProject((IdeaModule)null, modelClazz); } @Nullable @Override public T getExtraProject(@Nullable IdeaModule module, Class modelClazz) { - return myModels.getExtraProject(module, modelClazz); + return myModels.getExtraProject(module != null ? module.getGradleProject() : null, modelClazz); } @NotNull diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index df308930c2d5..78daf36171c5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -38,6 +38,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; +import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.MultiMap; @@ -288,8 +289,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver gradleModulesWithIncludedBuilds = exposeCompositeBuild(allModels, projectDataNode, gradleModules); - + Collection includedModules = exposeCompositeBuild(allModels, projectDataNode); final Map, IdeaModule>> moduleMap = ContainerUtilRt.newHashMap(); final Map, ExternalSourceSet>> sourceSetsMap = ContainerUtil.newHashMap(); projectDataNode.putUserData(RESOLVED_SOURCE_SETS, sourceSetsMap); @@ -322,7 +321,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver moduleDataNode = projectResolverChain.createModule(gradleModule, projectDataNode); - String mainModuleId = getModuleId(gradleModule); + String mainModuleId = getModuleId(resolverCtx, gradleModule); + + if (moduleMap.containsKey(mainModuleId)) { + // we should ensure deduplicated module names in the scope of single import + throw new IllegalStateException("Duplicate modules names detected: " + gradleModule); + } moduleMap.put(mainModuleId, Pair.create(moduleDataNode, gradleModule)); } @@ -415,18 +419,21 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver exposeCompositeBuild(ProjectImportAction.AllModels allModels, - DataNode projectDataNode, - DomainObjectSet gradleModules) { - List gradleModulesWithIncludedBuilds = ContainerUtil.newArrayList(gradleModules.getAll()); + private static Collection exposeCompositeBuild(ProjectImportAction.AllModels allModels, + DataNode projectDataNode) { + CompositeBuildData compositeBuildData = null; + List gradleIncludedModules = new SmartList<>(); List includedBuilds = allModels.getIncludedBuilds(); if (!includedBuilds.isEmpty()) { ProjectData projectData = projectDataNode.getData(); - CompositeBuildData compositeBuildData = new CompositeBuildData(projectData.getLinkedExternalProjectPath()); + compositeBuildData = new CompositeBuildData(projectData.getLinkedExternalProjectPath()); for (IdeaProject project : includedBuilds) { if (!project.getModules().isEmpty()) { + String rootProjectName = project.getName(); BuildParticipant buildParticipant = new BuildParticipant(); - gradleModulesWithIncludedBuilds.addAll(project.getModules()); + for (IdeaModule ideaModule : project.getModules()) { + gradleIncludedModules.add(ideaModule); + } GradleProject gradleProject = project.getModules().getAt(0).getGradleProject(); String projectPath = null; do { @@ -439,6 +446,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver projectDataNode, @@ -626,33 +634,53 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver extractExternalProjectModels(ProjectImportAction.AllModels models, boolean isPreview) { - final Class modelClazz = isPreview ? ExternalProjectPreview.class : ExternalProject.class; - final ExternalProject externalRootProject = models.getExtraProject(null, modelClazz); - if (externalRootProject == null) return Collections.emptyMap(); + private static void extractExternalProjectModels(@NotNull ProjectImportAction.AllModels models, + @NotNull ProjectResolverContext resolverCtx) { + resolverCtx.setModels(models); + final Class modelClazz = resolverCtx.isPreviewMode() ? ExternalProjectPreview.class : ExternalProject.class; + final ExternalProject externalRootProject = models.getExtraProject((IdeaModule)null, modelClazz); + if (externalRootProject == null) return; final DefaultExternalProject wrappedExternalRootProject = new DefaultExternalProject(externalRootProject); models.addExtraProject(wrappedExternalRootProject, ExternalProject.class); - final Map externalProjectsMap = createExternalProjectsMap(wrappedExternalRootProject); + final Map externalProjectsMap = createExternalProjectsMap(null, wrappedExternalRootProject); DomainObjectSet gradleModules = models.getIdeaProject().getModules(); if (gradleModules != null && !gradleModules.isEmpty()) { - List gradleModulesWithIncludedBuilds = ContainerUtil.newArrayList(gradleModules.getAll()); - for (IdeaProject project : models.getIncludedBuilds()) { - gradleModulesWithIncludedBuilds.addAll(project.getModules()); - } - for (IdeaModule ideaModule : gradleModulesWithIncludedBuilds) { - final ExternalProject externalProject = externalProjectsMap.get(getModuleId(ideaModule)); + for (IdeaModule ideaModule : gradleModules) { + final ExternalProject externalProject = externalProjectsMap.get(getModuleId(resolverCtx, ideaModule)); if (externalProject != null) { - models.addExtraProject(externalProject, ExternalProject.class, ideaModule); + models.addExtraProject(externalProject, ExternalProject.class, ideaModule.getGradleProject()); } } } + for (IdeaProject project : models.getIncludedBuilds()) { + DomainObjectSet ideaModules = project.getModules(); + if (ideaModules.isEmpty()) continue; - return externalProjectsMap; + GradleProject gradleProject = ideaModules.getAt(0).getGradleProject(); + while (gradleProject.getParent() != null) { + gradleProject = gradleProject.getParent(); + } + final ExternalProject externalIncludedRootProject = models.getExtraProject(gradleProject, modelClazz); + if (externalIncludedRootProject == null) continue; + final DefaultExternalProject wrappedExternalIncludedRootProject = new DefaultExternalProject(externalIncludedRootProject); + wrappedExternalRootProject.getChildProjects().put(wrappedExternalIncludedRootProject.getName(), wrappedExternalIncludedRootProject); + models.addExtraProject(wrappedExternalIncludedRootProject, ExternalProject.class); + String compositePrefix = project.getName(); + final Map externalIncludedProjectsMap = + createExternalProjectsMap(compositePrefix, wrappedExternalIncludedRootProject); + for (IdeaModule ideaModule : ideaModules) { + final ExternalProject externalProject = externalIncludedProjectsMap.get(getModuleId(resolverCtx, ideaModule)); + if (externalProject != null) { + models.addExtraProject(externalProject, ExternalProject.class, ideaModule.getGradleProject()); + } + } + } } - private static Map createExternalProjectsMap(@Nullable final ExternalProject rootExternalProject) { + private static Map createExternalProjectsMap(@Nullable String compositePrefix, + @Nullable final ExternalProject rootExternalProject) { final Map externalProjectMap = ContainerUtilRt.newHashMap(); if (rootExternalProject == null) return externalProjectMap; @@ -666,6 +694,9 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver compositeParticipants = new HashSet<>(); - for (Pair pair : pairs) { - compositeParticipants.add(pair.second.toString()); - } - if (compositeParticipants.isEmpty()) { + Pair[] compositeParticipants = myTree.getCheckedNodes(Pair.class, null); + if (compositeParticipants.length == 0) { myCompositeRootSettings.setCompositeBuild(null); } else { GradleProjectSettings.CompositeBuild compositeBuild = new GradleProjectSettings.CompositeBuild(); compositeBuild.setCompositeDefinitionSource(CompositeDefinitionSource.IDE); - for (String participant : compositeParticipants) { + for (Pair participant : compositeParticipants) { BuildParticipant buildParticipant = new BuildParticipant(); - buildParticipant.setRootPath(participant); + buildParticipant.setRootProjectName(participant.first.toString()); + buildParticipant.setRootPath(participant.second.toString()); compositeBuild.getCompositeParticipants().add(buildParticipant); } myCompositeRootSettings.setCompositeBuild(compositeBuild); diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java index b8bf57004772..4d05583e1037 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java @@ -15,6 +15,10 @@ */ package org.jetbrains.plugins.gradle.importing; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl; +import com.intellij.openapi.module.StdModuleTypes; import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions; import org.junit.Test; @@ -80,4 +84,91 @@ public class GradleCompositeImportingTest extends GradleImportingTestCase { assertModuleModuleDepScope("my-app_main", "string-utils_main", COMPILE); assertModuleLibDepScope("my-app_main", "Gradle: org.apache.commons:commons-lang3:3.4", COMPILE); } + + @Test + @TargetVersions("3.3+") + public void testCompositeBuildWithProjectNameDuplicates() throws Exception { + IdeModifiableModelsProvider modelsProvider = new IdeModifiableModelsProviderImpl(myProject); + modelsProvider.newModule(getProjectPath() + "/api.iml", StdModuleTypes.JAVA.getId()); + modelsProvider.newModule(getProjectPath() + "/my-app-api.iml", StdModuleTypes.JAVA.getId()); + modelsProvider.newModule(getProjectPath() + "/my-app-api_main.iml", StdModuleTypes.JAVA.getId()); + edt(() -> ApplicationManager.getApplication().runWriteAction(modelsProvider::commit)); + + createSettingsFile("rootProject.name='adhoc'\n" + + "\n" + + "includeBuild '../my-app'\n" + + "includeBuild '../my-utils'"); + + createProjectSubFile("../my-app/settings.gradle", "rootProject.name = 'my-app'\n" + + "include 'api'\n"); + createProjectSubFile("../my-app/build.gradle", + "apply plugin: 'java'\n" + + "group 'org.sample'\n" + + "version '1.0'\n" + + "\n" + + "dependencies {\n" + + " compile 'org.sample:number-utils:1.0'\n" + + " compile 'org.sample:string-utils:1.0'\n" + + "}\n" + + "project(':api') {\n" + + " apply plugin: 'java'\n" + + " dependencies {\n" + + " compile 'commons-lang:commons-lang:2.6'\n" + + " }\n" + + "}\n"); + + createProjectSubFile("../my-utils/settings.gradle", + "rootProject.name = 'my-utils'\n" + + "include 'number-utils', 'string-utils', 'api'"); + createProjectSubFile("../my-utils/build.gradle", + "subprojects {\n" + + " apply plugin: 'java'\n" + + "\n" + + " group 'org.sample'\n" + + " version '1.0'\n" + + "}\n" + + "\n" + + "project(':string-utils') {\n" + + " dependencies {\n" + + " compile 'org.apache.commons:commons-lang3:3.4'\n" + + " }\n" + + "}\n" + + "project(':api') {\n" + + " dependencies {\n" + + " compile 'junit:junit:4.11'\n" + + " }\n" + + "}"); + + importProject(); + + assertModules("adhoc", "api", "api_main", "api_test", + "my-app", "my-app_main", "my-app_test", + "my-app-api", "my-app-api~1", "my-app-api_main", + "my-utils", + "string-utils", "string-utils_main", "string-utils_test", + "number-utils", "number-utils_main", "number-utils_test", + "my-utils-api", "my-utils-api_main", "my-utils-api_test"); + + String[] emptyModules = + new String[]{"api", "adhoc", "my-app", "my-app-api", "my-app-api_main", "my-app-api~1", "my-utils", "my-utils-api", "string-utils", + "number-utils"}; + for (String rootModule : emptyModules) { + assertModuleLibDeps(rootModule); + assertModuleModuleDeps(rootModule); + } + assertModuleModuleDeps("my-app_main", "number-utils_main", "string-utils_main"); + assertModuleModuleDepScope("my-app_main", "number-utils_main", COMPILE); + assertModuleModuleDepScope("my-app_main", "string-utils_main", COMPILE); + assertModuleLibDepScope("my-app_main", "Gradle: org.apache.commons:commons-lang3:3.4", COMPILE); + + // my-app api project + assertModuleModuleDeps("api_main"); + assertModuleLibDeps("api_main", "Gradle: commons-lang:commons-lang:2.6"); + assertModuleLibDepScope("api_main", "Gradle: commons-lang:commons-lang:2.6", COMPILE); + + assertModuleModuleDeps("my-utils-api_main"); + //assertModuleLibDeps("my-utils-api_main", "Gradle: junit:junit:4.11"); + assertModuleLibDepScope("my-utils-api_main", "Gradle: junit:junit:4.11", COMPILE); + //assertModuleLibDepScope("my-utils-api_main", "Gradle: org.hamcrest:hamcrest-core:1.3", COMPILE); + } } diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProject.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProject.java index e8a3c449e953..40c22a1be611 100644 --- a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProject.java +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProject.java @@ -34,6 +34,9 @@ public interface ExternalProject extends Model, Serializable { @NotNull String getExternalSystemId(); + @NotNull + String getId(); + @NotNull String getName(); diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java index afe2827ca764..b06bbcc7a3ed 100644 --- a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java @@ -20,12 +20,12 @@ import org.gradle.tooling.BuildAction; import org.gradle.tooling.BuildController; import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter; import org.gradle.tooling.internal.adapter.TargetTypeProvider; +import org.gradle.tooling.model.GradleProject; import org.gradle.tooling.model.build.BuildEnvironment; import org.gradle.tooling.model.gradle.GradleBuild; import org.gradle.tooling.model.idea.BasicIdeaProject; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.tooling.model.idea.IdeaProject; -import org.gradle.util.GradleVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -122,7 +122,7 @@ public class ProjectImportAction implements BuildAction { + public static class AllModels extends ModelsHolder { private List includedBuilds = new ArrayList(); private boolean isGradleProjectDirSupported; @@ -167,13 +167,27 @@ public class ProjectImportAction implements BuildAction T getExtraProject(@Nullable IdeaModule model, Class modelClazz) { + return super.getExtraProject(model != null ? model.getGradleProject() : null, modelClazz); + } + + public void addExtraProject(@NotNull Object project, @NotNull Class modelClazz, @Nullable IdeaModule subPropject) { + super.addExtraProject(project, modelClazz, subPropject != null ? subPropject.getGradleProject() : null); + } + + @NotNull + protected String extractMapKey(Class modelClazz, @Nullable IdeaModule module) { + return extractMapKey(modelClazz, module); + } + @NotNull @Override - protected String extractMapKey(Class modelClazz, @Nullable IdeaModule module) { - if (module != null) { + protected String extractMapKey(Class modelClazz, @Nullable GradleProject gradleProject) { + if (gradleProject != null) { String id = isGradleProjectDirSupported ? - module.getGradleProject().getProjectDirectory().getPath() : - module.getGradleProject().getPath(); + gradleProject.getProjectDirectory().getPath() : + gradleProject.getPath(); return modelClazz.getName() + '@' + id; } else { diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProject.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProject.java index fd26ee43342e..7e59e7c5db58 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProject.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProject.java @@ -29,6 +29,8 @@ public class DefaultExternalProject implements ExternalProject, ExternalProjectP private static final long serialVersionUID = 1L; + @NotNull + private String myId; @NotNull private String myName; @NotNull @@ -74,6 +76,7 @@ public class DefaultExternalProject implements ExternalProject, ExternalProjectP public DefaultExternalProject(@NotNull ExternalProject externalProject) { this(); + myId = externalProject.getId(); myName = externalProject.getName(); myQName = externalProject.getQName(); myVersion = externalProject.getVersion(); @@ -109,6 +112,16 @@ public class DefaultExternalProject implements ExternalProject, ExternalProjectP return myExternalSystemId; } + @NotNull + @Override + public String getId() { + return myId; + } + + public void setId(@NotNull String id) { + myId = id; + } + public void setExternalSystemId(@NotNull String externalSystemId) { myExternalSystemId = externalSystemId; } @@ -271,6 +284,6 @@ public class DefaultExternalProject implements ExternalProject, ExternalProjectP @Override public String toString() { - return "project '" + myQName + "'"; + return "project '" + myId + "'"; } } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy index 7510e7c4c7fe..c5fc91b32206 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy @@ -82,7 +82,15 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { DefaultExternalProject defaultExternalProject = new DefaultExternalProject() defaultExternalProject.externalSystemId = "GRADLE" defaultExternalProject.name = project.name - defaultExternalProject.QName = ":" == project.path ? project.name : project.path + def qName = ":" == project.path ? project.name : project.path + defaultExternalProject.QName = qName + final IdeaPlugin ideaPlugin = project.getPlugins().findPlugin(IdeaPlugin.class) + def ideaPluginModule = ideaPlugin?.model?.module + def parentBuildRootProject = project.gradle.parent?.rootProject + def compositePrefix = parentBuildRootProject && !project.rootProject.is(parentBuildRootProject) && ":" != project.path ? + (ideaPlugin?.model?.project?.name ?: project.rootProject.name) : ""; + def ideaModuleName = ideaPluginModule?.name ?: project.name + defaultExternalProject.id = compositePrefix + (":" == project.path ? ideaModuleName : qName) defaultExternalProject.version = wrap(project.version) defaultExternalProject.description = project.description defaultExternalProject.buildDir = project.buildDir