gradle: deduplicate module names across IDE project (IDEA-168284, IDEA-159811, IDEA-167974, IDEA-164489, IDEA-133950)

This commit is contained in:
Vladislav.Soroka
2017-02-26 10:54:04 +03:00
parent 1d83f8497f
commit c5c5c3ffc0
18 changed files with 332 additions and 109 deletions
@@ -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();
}
}
@@ -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
@@ -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();
@@ -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) {
@@ -95,7 +95,17 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
MultiMap<String /*module name*/, String /*dep name*/> byModuleName = MultiMap.create();
for (DataNode<E> 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();
@@ -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<E extends ModuleData> extends Ab
@NotNull Project project) {
for (final DataNode<E> 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<String> orphanFiles = project.getUserData(ORPHAN_MODULE_FILES);
if (orphanFiles != null) {
@@ -154,23 +150,12 @@ public abstract class AbstractModuleDataService<E extends ModuleData> extends Ab
Collection<DataNode<E>> result = ContainerUtilRt.newArrayList();
for (DataNode<E> 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<String> 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;
@@ -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<M
processed.add(dependencyData);
toRemove.remove(Pair.create(dependencyData.getInternalName(), dependencyData.getScope()));
final String moduleName = dependencyData.getInternalName();
Module ideDependencyModule = modelsProvider.findIdeModule(moduleName);
final ModuleData moduleData = dependencyData.getTarget();
Module ideDependencyModule = modelsProvider.findIdeModule(moduleData);
ModuleOrderEntry orderEntry;
if (module.equals(ideDependencyModule)) {
@@ -116,7 +117,7 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
if (orderEntry == null) {
orderEntry = ReadAction.compute(() ->
ideDependencyModule == null
? modifiableRootModel.addInvalidModuleEntry(moduleName)
? modifiableRootModel.addInvalidModuleEntry(moduleData.getInternalName())
: modifiableRootModel.addModuleOrderEntry(ideDependencyModule));
}
}
@@ -31,9 +31,19 @@ import java.util.Set;
*/
@Tag("build")
public class BuildParticipant implements Serializable {
private String myRootProjectName;
private String myRootPath;
@NotNull private Set<String> myProjects = new HashSet<>();
@Attribute("name")
public String getRootProjectName() {
return myRootProjectName;
}
public void setRootProjectName(String rootProjectName) {
myRootProjectName = rootProjectName;
}
@Attribute("path")
public String getRootPath() {
return myRootPath;
@@ -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<ModuleData> 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<? extends ModuleData> dataNode, @NotNull ExternalSourceSet sourceSet) {
for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> 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<ModuleData> ideModule,
@NotNull SourceSetsProcessor processor) {
Map<String, DataNode<GradleSourceSetData>> 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<? extends ModuleData> moduleDataNode = sourceSetsMap.isEmpty() ? ideModule : sourceSetsMap.get(moduleId);
if (moduleDataNode == null) continue;
@@ -380,7 +382,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
final Map<String, Pair<String, ExternalSystemSourceType>> 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<? extends ModuleData> dataNode, @NotNull ExternalSourceSet sourceSet) {
for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> 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<? extends ModuleData> 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<ModuleData> ownerModule,
@NotNull IdeaModuleDependency dependency,
@NotNull DataNode<ProjectData> 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)
@@ -141,13 +141,13 @@ public class DefaultProjectResolverContext extends UserDataHolderBase implements
@Nullable
@Override
public <T> T getExtraProject(Class<T> modelClazz) {
return myModels.getExtraProject(null, modelClazz);
return myModels.getExtraProject((IdeaModule)null, modelClazz);
}
@Nullable
@Override
public <T> T getExtraProject(@Nullable IdeaModule module, Class<T> modelClazz) {
return myModels.getExtraProject(module, modelClazz);
return myModels.getExtraProject(module != null ? module.getGradleProject() : null, modelClazz);
}
@NotNull
@@ -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<Grad
allModels.setBuildEnvironment(buildEnvironment);
final long startDataConversionTime = System.currentTimeMillis();
extractExternalProjectModels(allModels, resolverCtx.isPreviewMode());
resolverCtx.setModels(allModels);
extractExternalProjectModels(allModels, resolverCtx);
// import project data
ProjectData projectData = projectResolverChain.createProject();
@@ -308,8 +308,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
throw new IllegalStateException("No modules found for the target project: " + ideaProject);
}
List<IdeaModule> gradleModulesWithIncludedBuilds = exposeCompositeBuild(allModels, projectDataNode, gradleModules);
Collection<IdeaModule> includedModules = exposeCompositeBuild(allModels, projectDataNode);
final Map<String /* module id */, Pair<DataNode<ModuleData>, IdeaModule>> moduleMap = ContainerUtilRt.newHashMap();
final Map<String /* module id */, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetsMap = ContainerUtil.newHashMap();
projectDataNode.putUserData(RESOLVED_SOURCE_SETS, sourceSetsMap);
@@ -322,7 +321,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
projectDataNode.putUserData(CONFIGURATION_ARTIFACTS, artifactsMap);
// import modules data
for (IdeaModule gradleModule : gradleModulesWithIncludedBuilds) {
for (IdeaModule gradleModule : ContainerUtil.concat(gradleModules, includedModules)) {
if (gradleModule == null) {
continue;
}
@@ -338,7 +337,12 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
}
DataNode<ModuleData> 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<Grad
}
@NotNull
private static List<IdeaModule> exposeCompositeBuild(ProjectImportAction.AllModels allModels,
DataNode<ProjectData> projectDataNode,
DomainObjectSet<? extends IdeaModule> gradleModules) {
List<IdeaModule> gradleModulesWithIncludedBuilds = ContainerUtil.newArrayList(gradleModules.getAll());
private static Collection<IdeaModule> exposeCompositeBuild(ProjectImportAction.AllModels allModels,
DataNode<ProjectData> projectDataNode) {
CompositeBuildData compositeBuildData = null;
List<IdeaModule> gradleIncludedModules = new SmartList<>();
List<IdeaProject> 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<Grad
}
while ((gradleProject = gradleProject.getParent()) != null);
if (projectPath != null) {
buildParticipant.setRootProjectName(rootProjectName);
buildParticipant.setRootPath(projectPath);
for (IdeaModule module : project.getModules()) {
try {
@@ -457,7 +465,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
}
projectDataNode.createChild(CompositeBuildData.KEY, compositeBuildData);
}
return gradleModulesWithIncludedBuilds;
return gradleIncludedModules;
}
private static void mergeLibraryAndModuleDependencyData(DataNode<ProjectData> projectDataNode,
@@ -626,33 +634,53 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
}
}
private static Map<String, ExternalProject> extractExternalProjectModels(ProjectImportAction.AllModels models, boolean isPreview) {
final Class<? extends ExternalProject> 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<? extends ExternalProject> 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<String, ExternalProject> externalProjectsMap = createExternalProjectsMap(wrappedExternalRootProject);
final Map<String, ExternalProject> externalProjectsMap = createExternalProjectsMap(null, wrappedExternalRootProject);
DomainObjectSet<? extends IdeaModule> gradleModules = models.getIdeaProject().getModules();
if (gradleModules != null && !gradleModules.isEmpty()) {
List<IdeaModule> 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<? extends IdeaModule> 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<String, ExternalProject> 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<String, ExternalProject> createExternalProjectsMap(@Nullable final ExternalProject rootExternalProject) {
private static Map<String, ExternalProject> createExternalProjectsMap(@Nullable String compositePrefix,
@Nullable final ExternalProject rootExternalProject) {
final Map<String, ExternalProject> externalProjectMap = ContainerUtilRt.newHashMap();
if (rootExternalProject == null) return externalProjectMap;
@@ -666,6 +694,9 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
final String moduleName = externalProject.getName();
final String qName = externalProject.getQName();
String moduleId = StringUtil.isEmpty(qName) || ":".equals(qName) ? moduleName : qName;
if (compositePrefix != null && externalProject != rootExternalProject) {
moduleId = compositePrefix + moduleId;
}
externalProjectMap.put(moduleId, externalProject);
}
@@ -922,5 +953,4 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
ApplicationInfoEx appInfo = ApplicationInfoImpl.getShadowInstance();
return appInfo.getMajorVersion() + "." + appInfo.getMinorVersion();
}
}
@@ -93,9 +93,7 @@ public class GradleProjectResolverUtil {
));
}
String gradlePath = gradleModule.getGradleProject().getPath();
final boolean isRootModule = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath);
String mainModuleId = isRootModule ? moduleName : gradlePath;
String mainModuleId = getModuleId(resolverCtx, gradleModule);
final ModuleData moduleData =
new ModuleData(mainModuleId, GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName,
mainModuleFileDirectoryPath, mainModuleConfigPath);
@@ -165,14 +163,16 @@ public class GradleProjectResolverUtil {
}
@NotNull
public static String getModuleId(@NotNull IdeaModule gradleModule) {
public static String getModuleId(@NotNull ProjectResolverContext resolverCtx, @NotNull IdeaModule gradleModule) {
GradleProject gradleProject = gradleModule.getGradleProject();
return getModuleId(gradleProject.getPath(), gradleProject.getName());
}
@NotNull
public static String getModuleId(@NotNull ExternalProject externalProject) {
return getModuleId(externalProject.getQName(), externalProject.getName());
String gradlePath = gradleProject.getPath();
String compositePrefix = "";
if (gradleModule.getProject() != resolverCtx.getModels().getIdeaProject()) {
if (!StringUtil.isEmpty(gradlePath) && !":".equals(gradlePath)) {
compositePrefix = gradleModule.getProject().getName();
}
}
return compositePrefix + getModuleId(gradlePath, gradleModule.getName());
}
@NotNull
@@ -180,12 +180,25 @@ public class GradleProjectResolverUtil {
return StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? moduleName : gradlePath;
}
@NotNull
public static String getModuleId(@NotNull ExternalProject externalProject) {
return externalProject.getId();
}
@NotNull
public static String getModuleId(@NotNull ExternalProject externalProject, @NotNull ExternalSourceSet sourceSet) {
String mainModuleId = getModuleId(externalProject);
return mainModuleId + ":" + sourceSet.getName();
}
@NotNull
public static String getModuleId(@NotNull ProjectResolverContext resolverCtx,
@NotNull IdeaModule gradleModule,
@NotNull ExternalSourceSet sourceSet) {
String mainModuleId = getModuleId(resolverCtx, gradleModule);
return mainModuleId + ":" + sourceSet.getName();
}
@NotNull
public static String getModuleId(@NotNull ExternalProjectDependency projectDependency) {
DependencyScope dependencyScope = getDependencyScope(projectDependency.getScope());
@@ -28,7 +28,6 @@ import com.intellij.ui.*;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,7 +42,6 @@ import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import java.util.List;
import java.util.Set;
import static com.intellij.openapi.util.io.FileUtil.pathsEqual;
@@ -91,20 +89,17 @@ public class GradleProjectCompositeSelectorDialog extends DialogWrapper {
@Override
protected void doOKAction() {
if (myCompositeRootSettings != null) {
Pair[] pairs = myTree.getCheckedNodes(Pair.class, null);
Set<String> 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);
@@ -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);
}
}
@@ -34,6 +34,9 @@ public interface ExternalProject extends Model, Serializable {
@NotNull
String getExternalSystemId();
@NotNull
String getId();
@NotNull
String getName();
@@ -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<ProjectImportAction.AllM
try {
Object extraProject = controller.findModel(model, aClass);
if (extraProject == null) continue;
allModels.addExtraProject(extraProject, aClass, model);
allModels.addExtraProject(extraProject, aClass, model != null ? model.getGradleProject() : null);
}
catch (Exception e) {
// do not fail project import in a preview mode
@@ -133,7 +133,7 @@ public class ProjectImportAction implements BuildAction<ProjectImportAction.AllM
}
}
public static class AllModels extends ModelsHolder<IdeaProject, IdeaModule> {
public static class AllModels extends ModelsHolder<IdeaProject, GradleProject> {
private List<IdeaProject> includedBuilds = new ArrayList<IdeaProject>();
private boolean isGradleProjectDirSupported;
@@ -167,13 +167,27 @@ public class ProjectImportAction implements BuildAction<ProjectImportAction.AllM
isGradleProjectDirSupported = gradleProjectDirSupported;
}
@Nullable
public <T> T getExtraProject(@Nullable IdeaModule model, Class<T> 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 {
@@ -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 + "'";
}
}
@@ -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