Commit ae5f8e3b authored by Vladislav.Soroka's avatar Vladislav.Soroka
Browse files

External system: library dependency substitution support (IDEA-134885)

parent bab19297
Showing with 608 additions and 37 deletions
+608 -37
......@@ -5,6 +5,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.LinkedHashSet;
......@@ -17,13 +18,16 @@ import java.util.Set;
* @author Denis Zhdanov
* @since 8/24/11 4:50 PM
*/
public class LibraryData extends AbstractNamedData implements Named {
public class LibraryData extends AbstractNamedData implements Named, ProjectCoordinate {
private static final long serialVersionUID = 1L;
private final Map<LibraryPathType, Set<String>> myPaths = new HashMap<>();
private final boolean myUnresolved;
private String myGroup;
private String myArtifactId;
private String myVersion;
public LibraryData(@NotNull ProjectSystemId owner, @NotNull String name) {
this(owner, name, false);
......@@ -34,6 +38,36 @@ public class LibraryData extends AbstractNamedData implements Named {
myUnresolved = unresolved;
}
@Nullable
@Override
public String getGroupId() {
return myGroup;
}
public void setGroup(String group) {
myGroup = group;
}
@Nullable
@Override
public String getArtifactId() {
return myArtifactId;
}
public void setArtifactId(String artifactId) {
myArtifactId = artifactId;
}
@Nullable
@Override
public String getVersion() {
return myVersion;
}
public void setVersion(String version) {
myVersion = version;
}
public boolean isUnresolved() {
return myUnresolved;
}
......@@ -55,12 +89,15 @@ public class LibraryData extends AbstractNamedData implements Named {
public void forgetAllPaths() {
myPaths.clear();
}
@Override
public int hashCode() {
int result = myPaths.hashCode();
result = 31 * result + super.hashCode();
result = 31 * result + (myUnresolved ? 0 : 1);
result = 31 * result + (myGroup != null ? myGroup.hashCode() : 0);
result = 31 * result + (myArtifactId != null ? myArtifactId.hashCode() : 0);
result = 31 * result + (myVersion != null ? myVersion.hashCode() : 0);
return result;
}
......@@ -69,6 +106,9 @@ public class LibraryData extends AbstractNamedData implements Named {
if (!super.equals(o)) return false;
LibraryData that = (LibraryData)o;
if (myGroup != null ? !myGroup.equals(that.myGroup) : that.myGroup != null) return false;
if (myArtifactId != null ? !myArtifactId.equals(that.myArtifactId) : that.myArtifactId != null) return false;
if (myVersion != null ? !myVersion.equals(that.myVersion) : that.myVersion != null) return false;
return super.equals(that) && myUnresolved == that.myUnresolved && myPaths.equals(that.myPaths);
}
......
......@@ -33,6 +33,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
@Nullable private String mySourceCompatibility;
@Nullable private String myTargetCompatibility;
@Nullable private String myProductionModuleId;
@Nullable private ProjectCoordinate myPublication;
private boolean myInheritProjectCompileOutputPath = true;
......@@ -148,6 +149,15 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
this.myGroup = group;
}
@Nullable
public ProjectCoordinate getPublication() {
return myPublication;
}
public void setPublication(@Nullable ProjectCoordinate publication) {
myPublication = publication;
}
@Nullable
public String getVersion() {
return myVersion;
......
......@@ -18,15 +18,19 @@ 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.externalSystem.model.project.ProjectCoordinate;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.ProjectModelExternalSource;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.packaging.artifacts.ModifiableArtifactModel;
import com.intellij.packaging.elements.PackagingElementResolvingContext;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
......@@ -79,4 +83,18 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider, UserData
@Nullable
String getProductionModuleName(Module module);
@ApiStatus.Experimental
void registerModulePublication(Module module, ProjectCoordinate modulePublication);
@ApiStatus.Experimental
@Nullable
String findModuleByPublication(ProjectCoordinate publicationId);
@ApiStatus.Experimental
@Nullable
ModuleOrderEntry trySubstitute(Module ownerModule, LibraryOrderEntry libraryOrderEntry, ProjectCoordinate publicationId);
@ApiStatus.Experimental
boolean isSubstituted(String libraryName);
}
......@@ -21,11 +21,19 @@ 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.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.LibraryData;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectCoordinate;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleWithNameAlreadyExists;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
......@@ -53,11 +61,13 @@ import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphGenerator;
import com.intellij.util.graph.InboundSemiGraph;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.isRelated;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.toCanonicalPath;
......@@ -73,12 +83,20 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
private ModifiableArtifactModel myModifiableArtifactModel;
private AbstractIdeModifiableModelsProvider.MyPackagingElementResolvingContext myPackagingElementResolvingContext;
private final ArtifactExternalDependenciesImporter myArtifactExternalDependenciesImporter;
@NotNull private final MyUserDataHolderBase myUserData = new MyUserDataHolderBase();
@Nullable
private final ModifiableWorkspace myModifiableWorkspace;
private final MyUserDataHolderBase myUserData;
public AbstractIdeModifiableModelsProvider(@NotNull Project project) {
super(project);
myUserData = new MyUserDataHolderBase();
myArtifactExternalDependenciesImporter = new ArtifactExternalDependenciesImporterImpl();
if (ExternalProjectsWorkspaceImpl.isDependencySubstitutionEnabled()) {
myModifiableWorkspace = ServiceManager.getService(project, ExternalProjectsWorkspaceImpl.class).createModifiableWorkspace(this);
}
else {
myModifiableWorkspace = null;
}
}
protected abstract ModifiableArtifactModel doGetModifiableArtifactModel();
......@@ -385,6 +403,9 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
@Override
public void commit() {
ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(() -> {
if (ExternalProjectsWorkspaceImpl.isDependencySubstitutionEnabled()) {
updateSubstitutions();
}
processExternalArtifactDependencies();
for (Library.ModifiableModel each : myModifiableLibraryModels.values()) {
each.commit();
......@@ -457,6 +478,41 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
return myProductionModulesForTestModules.get(module);
}
@Override
public ModuleOrderEntry trySubstitute(Module ownerModule, LibraryOrderEntry libraryOrderEntry, ProjectCoordinate publicationId) {
String workspaceModuleCandidate = findModuleByPublication(publicationId);
Module workspaceModule = workspaceModuleCandidate == null ? null : findIdeModule(workspaceModuleCandidate);
if (workspaceModule == null) {
return null;
}
else {
ModifiableRootModel modifiableRootModel = getModifiableRootModel(ownerModule);
ModuleOrderEntry moduleOrderEntry = modifiableRootModel.addModuleOrderEntry(workspaceModule);
moduleOrderEntry.setScope(libraryOrderEntry.getScope());
moduleOrderEntry.setExported(libraryOrderEntry.isExported());
assert myModifiableWorkspace != null;
myModifiableWorkspace.addSubstitution(ownerModule.getName(),
workspaceModule.getName(),
libraryOrderEntry.getLibraryName(),
libraryOrderEntry.getScope());
modifiableRootModel.removeOrderEntry(libraryOrderEntry);
return moduleOrderEntry;
}
}
@Override
public void registerModulePublication(Module module, ProjectCoordinate modulePublication) {
if (myModifiableWorkspace != null) {
myModifiableWorkspace.register(modulePublication, module);
}
}
@Override
public boolean isSubstituted(String libraryName) {
if (myModifiableWorkspace == null) return false;
return myModifiableWorkspace.isSubstituted(libraryName);
}
@Nullable
@Override
public <T> T getUserData(@NotNull Key<T> key) {
......@@ -467,4 +523,94 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
myUserData.putUserData(key, value);
}
@Nullable
@Override
public String findModuleByPublication(ProjectCoordinate publicationId) {
return myModifiableWorkspace == null ? null : myModifiableWorkspace.findModule(publicationId);
}
private void updateSubstitutions() {
if (myModifiableWorkspace == null) return;
final List<String> oldModules = Arrays.stream(ModuleManager.getInstance(myProject).getModules())
.map(module -> module.getName()).collect(Collectors.toList());
final List<String> newModules = Arrays.stream(myModifiableModuleModel.getModules())
.map(module -> module.getName()).collect(Collectors.toList());
final Collection<String> removedModules = new THashSet<>(oldModules);
removedModules.removeAll(newModules);
Map<String, String> toSubstitute = ContainerUtil.newHashMap();
for (ExternalSystemManager<?, ?, ?, ?, ?> manager : ExternalSystemApiUtil.getAllManagers()) {
final Collection<ExternalProjectInfo> projectsData =
ProjectDataManager.getInstance().getExternalProjectsData(myProject, manager.getSystemId());
for (ExternalProjectInfo projectInfo : projectsData) {
if (projectInfo.getExternalProjectStructure() == null) {
continue;
}
Collection<DataNode<LibraryData>> libraryNodes =
ExternalSystemApiUtil.findAll(projectInfo.getExternalProjectStructure(), ProjectKeys.LIBRARY);
for (DataNode<LibraryData> libraryNode : libraryNodes) {
String substitutionModuleCandidate = findModuleByPublication(libraryNode.getData());
if (substitutionModuleCandidate != null) {
toSubstitute.put(libraryNode.getData().getInternalName(), substitutionModuleCandidate);
}
}
}
}
for (Module module : getModules()) {
ModifiableRootModel modifiableRootModel = getModifiableRootModel(module);
boolean changed = false;
OrderEntry[] entries = modifiableRootModel.getOrderEntries();
for (int i = 0, length = entries.length; i < length; i++) {
OrderEntry orderEntry = entries[i];
if (orderEntry instanceof ModuleOrderEntry) {
String workspaceModule = ((ModuleOrderEntry)orderEntry).getModuleName();
if (removedModules.contains(workspaceModule)) {
DependencyScope scope = ((ModuleOrderEntry)orderEntry).getScope();
if (myModifiableWorkspace.isSubstitution(module.getName(), workspaceModule, scope)) {
String libraryName = myModifiableWorkspace.getSubstitutedLibrary(workspaceModule);
if (libraryName != null) {
Library library = getLibraryByName(libraryName);
if (library != null) {
modifiableRootModel.removeOrderEntry(orderEntry);
entries[i] = modifiableRootModel.addLibraryEntry(library);
changed = true;
myModifiableWorkspace.removeSubstitution(module.getName(), workspaceModule, libraryName, scope);
}
}
}
}
}
if (!(orderEntry instanceof LibraryOrderEntry)) continue;
LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
if (!libraryOrderEntry.isModuleLevel() && libraryOrderEntry.getLibraryName() != null) {
String workspaceModule = toSubstitute.get(libraryOrderEntry.getLibraryName());
if (workspaceModule != null) {
Module ideModule = findIdeModule(workspaceModule);
if (ideModule != null) {
ModuleOrderEntry moduleOrderEntry = modifiableRootModel.addModuleOrderEntry(ideModule);
moduleOrderEntry.setScope(libraryOrderEntry.getScope());
modifiableRootModel.removeOrderEntry(orderEntry);
entries[i] = moduleOrderEntry;
changed = true;
myModifiableWorkspace.addSubstitution(module.getName(), workspaceModule,
libraryOrderEntry.getLibraryName(),
libraryOrderEntry.getScope());
}
}
}
}
if (changed) {
modifiableRootModel.rearrangeOrderEntries(entries);
}
}
myModifiableWorkspace.commit();
}
}
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.externalSystem.service.project;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.externalSystem.model.project.ProjectCoordinate;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.xmlb.annotations.MapAnnotation;
import com.intellij.util.xmlb.annotations.Property;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
/**
* @author Vladislav.Soroka
*/
@ApiStatus.Experimental
@State(name = "externalSubstitutions", storages = {@Storage(StoragePathMacros.WORKSPACE_FILE)})
public class ExternalProjectsWorkspaceImpl implements PersistentStateComponent<ExternalProjectsWorkspaceImpl.State> {
static final ExtensionPointName<ExternalProjectsWorkspaceImpl.Contributor> EP_NAME =
ExtensionPointName.create("com.intellij.externalSystemWorkspaceContributor");
@ApiStatus.Experimental
public interface Contributor {
@Nullable
ProjectCoordinate findProjectId(Module module, IdeModifiableModelsProvider modelsProvider);
}
static class State {
@Property(surroundWithTag = false)
@MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
keyAttributeName = "name", entryTagName = "module")
public Map<String, Set<String>> substitutions;
@Property(surroundWithTag = false)
@MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
keyAttributeName = "module", valueAttributeName = "lib")
public Map<String, String> names;
}
private State myState = new State();
public State getState() {
return myState;
}
public void loadState(State state) {
myState = state == null ? new State() : state;
}
public static boolean isDependencySubstitutionEnabled() {
return Registry.is("external.system.substitute.library.dependencies");
}
public ModifiableWorkspace createModifiableWorkspace(AbstractIdeModifiableModelsProvider modelsProvider) {
return new ModifiableWorkspace(myState, modelsProvider);
}
}
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.externalSystem.service.project;
import com.intellij.openapi.externalSystem.model.project.ProjectCoordinate;
import com.intellij.openapi.externalSystem.model.project.ProjectId;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Vladislav.Soroka
*/
@ApiStatus.Experimental
public class ModifiableWorkspace {
private final Map<ProjectCoordinate, String> myModuleMappingById = ContainerUtil.newTroveMap(
new TObjectHashingStrategy<ProjectCoordinate>() {
@Override
public int computeHashCode(ProjectCoordinate object) {
String groupId = object.getGroupId();
String artifactId = object.getArtifactId();
String version = object.getVersion();
int result = (groupId != null ? groupId.hashCode() : 0);
result = 31 * result + (artifactId != null ? artifactId.hashCode() : 0);
result = 31 * result + (version != null ? version.hashCode() : 0);
return result;
}
@Override
public boolean equals(ProjectCoordinate o1, ProjectCoordinate o2) {
if (o1.getGroupId() != null ? !o1.getGroupId().equals(o2.getGroupId()) : o2.getGroupId() != null) return false;
if (o1.getArtifactId() != null ? !o1.getArtifactId().equals(o2.getArtifactId()) : o2.getArtifactId() != null) return false;
if (o1.getVersion() != null ? !o1.getVersion().equals(o2.getVersion()) : o2.getVersion() != null) return false;
return true;
}
});
private final AbstractIdeModifiableModelsProvider myModelsProvider;
private final ExternalProjectsWorkspaceImpl.State myState;
private MultiMap<String/* module owner */, String /* substitution modules */> mySubstitutions = MultiMap.createSet();
private Map<String /* module name */, String /* library name */> myNamesMap = ContainerUtil.newHashMap();
public ModifiableWorkspace(ExternalProjectsWorkspaceImpl.State state,
AbstractIdeModifiableModelsProvider modelsProvider) {
myModelsProvider = modelsProvider;
Set<String> existingModules = ContainerUtil.newHashSet();
for (Module module : modelsProvider.getModules()) {
register(module, modelsProvider);
existingModules.add(module.getName());
}
myState = state;
if (myState.names != null) {
for (Map.Entry<String, String> entry : myState.names.entrySet()) {
if (existingModules.contains(entry.getKey())) {
myNamesMap.put(entry.getKey(), entry.getValue());
}
}
}
if (myState.substitutions != null) {
for (Map.Entry<String, Set<String>> entry : myState.substitutions.entrySet()) {
if (existingModules.contains(entry.getKey())) {
mySubstitutions.put(entry.getKey(), entry.getValue());
}
}
}
}
public void commit() {
Set<String> existingModules = ContainerUtil.newHashSet();
Arrays.stream(myModelsProvider.getModules()).map(Module::getName).forEach(existingModules::add);
myState.names = new HashMap<>();
myNamesMap.forEach((module, lib) -> {
if (existingModules.contains(module)) {
myState.names.put(module, lib);
}
});
myState.substitutions = new HashMap<>();
for (Map.Entry<String, Collection<String>> entry : mySubstitutions.entrySet()) {
if (!existingModules.contains(entry.getKey())) continue;
Collection<String> value = entry.getValue();
if (value != null && !value.isEmpty()) {
myState.substitutions.put(entry.getKey(), new TreeSet<>(value));
}
}
}
public void addSubstitution(String ownerModuleName,
String moduleName,
String libraryName,
DependencyScope scope) {
myNamesMap.put(moduleName, libraryName);
mySubstitutions.putValue(ownerModuleName, moduleName + '_' + scope.getDisplayName());
}
public void removeSubstitution(String ownerModuleName,
String moduleName,
String libraryName,
DependencyScope scope) {
mySubstitutions.remove(ownerModuleName, moduleName + '_' + scope.getDisplayName());
Collection<? extends String> substitutions = mySubstitutions.values();
for (DependencyScope dependencyScope : DependencyScope.values()) {
if (substitutions.contains(moduleName + '_' + dependencyScope.getDisplayName())) {
return;
}
}
myNamesMap.remove(moduleName, libraryName);
}
public boolean isSubstitution(String moduleOwner, String substitutionModule, DependencyScope scope) {
return mySubstitutions.get(moduleOwner).contains(substitutionModule + '_' + scope.getDisplayName());
}
public boolean isSubstituted(String libraryName) {
return myNamesMap.values().contains(libraryName);
}
public String getSubstitutedLibrary(String moduleName) {
return myNamesMap.get(moduleName);
}
@Nullable
public String findModule(@NotNull ProjectCoordinate id) {
if (StringUtil.isEmpty(id.getArtifactId())) return null;
String result = myModuleMappingById.get(id);
return result == null && id.getVersion() != null
? myModuleMappingById.get(new ProjectId(id.getGroupId(), id.getArtifactId(), null))
: result;
}
public void register(@NotNull ProjectCoordinate id, @NotNull Module module) {
myModuleMappingById.put(id, module.getName());
myModuleMappingById.put(new ProjectId(id.getGroupId(), id.getArtifactId(), null), module.getName());
}
private void register(@NotNull Module module, AbstractIdeModifiableModelsProvider modelsProvider) {
Arrays.stream(ExternalProjectsWorkspaceImpl.EP_NAME.getExtensions())
.map(contributor -> contributor.findProjectId(module, modelsProvider))
.filter(Objects::nonNull)
.findFirst()
.ifPresent(id -> register(id, module));
}
}
......@@ -23,10 +23,7 @@ import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.OrderAware;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.model.project.*;
import com.intellij.openapi.externalSystem.service.project.IdeModelsProvider;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
......@@ -89,6 +86,10 @@ public abstract class AbstractModuleDataService<E extends ModuleData> extends Ab
for (DataNode<E> node : toImport) {
Module module = node.getUserData(MODULE_KEY);
if (module != null) {
ProjectCoordinate publication = node.getData().getPublication();
if (publication != null){
modelsProvider.registerModulePublication(module, publication);
}
String productionModuleId = node.getData().getProductionModuleId();
modelsProvider.setTestModuleProperties(module, productionModuleId);
setModuleOptions(module, node);
......
......@@ -164,7 +164,7 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
final LibraryTable.ModifiableModel librariesModel = modelsProvider.getModifiableProjectLibrariesModel();
for (Library library : librariesModel.getLibraries()) {
if (!ExternalSystemApiUtil.isExternalSystemLibrary(library, projectData.getOwner())) continue;
if (isOrphanProjectLibrary(library, modelsProvider)) {
if (isOrphanProjectLibrary(library, modelsProvider) && !modelsProvider.isSubstituted(library.getName())) {
orphanIdeLibraries.add(library);
}
}
......
......@@ -33,12 +33,13 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* @author Denis Zhdanov
......@@ -146,8 +147,14 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
break;
}
LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(projectLib);
orderEntryDataMap.put(orderEntry, dependencyData);
setLibraryScope(orderEntry, projectLib, module, dependencyData);
ModuleOrderEntry substitutionEntry = modelsProvider.trySubstitute(module, orderEntry, libraryData);
if (substitutionEntry != null) {
orderEntryDataMap.put(substitutionEntry, dependencyData);
}
else {
orderEntryDataMap.put(orderEntry, dependencyData);
}
}
}
}
......@@ -201,10 +208,15 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
String libraryName = libraryOrderEntry.getLibraryName();
LibraryDependencyData existing = projectLibrariesToImport.remove(libraryName + libraryOrderEntry.getScope().name());
if (existing != null) {
toImport.remove(existing);
orderEntryDataMap.put(entry, existing);
libraryOrderEntry.setExported(existing.isExported());
libraryOrderEntry.setScope(existing.getScope());
String module = modelsProvider.findModuleByPublication(existing.getTarget());
if(module == null) {
toImport.remove(existing);
orderEntryDataMap.put(entry, existing);
libraryOrderEntry.setExported(existing.isExported());
libraryOrderEntry.setScope(existing.getScope());
} else {
moduleRootModel.removeOrderEntry(entry);
}
}
else if (!hasUnresolvedLibraries) {
// There is a possible case that a project has been successfully imported from external model and after
......
<idea-plugin>
<extensionPoints>
<extensionPoint name="externalSystemManager" interface="com.intellij.openapi.externalSystem.ExternalSystemManager"/>
<extensionPoint name="externalSystemWorkspaceContributor"
interface="com.intellij.openapi.externalSystem.service.project.ExternalProjectsWorkspaceImpl$Contributor"/>
<extensionPoint name="externalProjectDataService"
interface="com.intellij.openapi.externalSystem.service.project.manage.ProjectDataService"/>
<extensionPoint name="externalSystemConfigLocator"
......
......@@ -25,6 +25,7 @@
serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl"
order="first"/>
<projectService serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsDataStorage"/>
<projectService serviceImplementation="com.intellij.openapi.externalSystem.service.project.ExternalProjectsWorkspaceImpl"/>
<externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.project.manage.ProjectDataServiceImpl"/>
<externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.project.manage.LibraryDataService"/>
<externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.project.manage.ModuleDataService"/>
......
......@@ -940,6 +940,9 @@ project.structure.add.tools.jar.to.new.jdk.description=Automatically add tools.j
project.qualified.module.names=false
project.qualified.module.names.description=Organize module nodes accordingly to their qualified (dot-separated) names
external.system.substitute.library.dependencies=false
external.system.substitute.library.dependencies.description=Replace library dependencies with module dependencies imported from external build systems like Maven/Gradle
tfs.set.connection.timeout=false
application.deactivation.timeout=1500
......
......@@ -109,6 +109,7 @@
<externalSystemExecutionConsoleManager implementation="org.jetbrains.plugins.gradle.execution.GradleExecutionConsoleManager" order="last"/>
<externalSystemExecutionConsoleManager implementation="org.jetbrains.plugins.gradle.execution.test.runner.GradleTestsExecutionConsoleManager"/>
<externalSystemWorkspaceContributor implementation="org.jetbrains.plugins.gradle.service.project.GradleWorkspaceContributor"/>
<projectImportProvider implementation="org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportProvider"/>
<projectImportBuilder implementation="org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportBuilder"/>
<moduleBuilder builderClass="org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder"/>
......
......@@ -37,31 +37,44 @@ import java.util.Map;
* @since 5/14/2016
*/
public class CachedModuleDataFinder {
private Map<String, DataNode<ModuleData>> cache = ContainerUtil.newHashMap();
private Map<String, DataNode<? extends ModuleData>> cache = ContainerUtil.newHashMap();
@Nullable
public DataNode<ModuleData> findModuleData(final DataNode parentNode, final String projectPath) {
DataNode<ModuleData> node = cache.get(projectPath);
if (node != null) return node;
public DataNode<? extends ModuleData> findModuleData(final DataNode<ProjectData> projectNode, final String projectPath) {
DataNode<? extends ModuleData> cachedNode = cache.get(projectPath);
if (cachedNode != null) return cachedNode;
//noinspection unchecked
return (DataNode<ModuleData>)ExternalSystemApiUtil.findFirstRecursively(parentNode, node1 -> {
if ((ProjectKeys.MODULE.equals(node1.getKey()) ||
GradleSourceSetData.KEY.equals(node1.getKey())) && node1.getData() instanceof ModuleData) {
String externalProjectPath = ((ModuleData)node1.getData()).getLinkedExternalProjectPath();
//noinspection unchecked
DataNode<ModuleData> myNode = (DataNode<ModuleData>)node1;
cache.put(externalProjectPath, myNode);
return ExternalSystemApiUtil.find(projectNode, ProjectKeys.MODULE, node -> {
String externalProjectPath = node.getData().getLinkedExternalProjectPath();
cache.put(externalProjectPath, node);
return StringUtil.equals(projectPath, node.getData().getLinkedExternalProjectPath());
});
}
return StringUtil.equals(projectPath, ((ModuleData)node1.getData()).getLinkedExternalProjectPath());
}
@Nullable
public DataNode<? extends ModuleData> findModuleData(@NotNull Module module) {
DataNode<? extends ModuleData> mainModuleData = findMainModuleData(module);
if (mainModuleData == null) return null;
return false;
});
boolean isSourceSet = GradleConstants.GRADLE_SOURCE_SET_MODULE_TYPE_KEY.equals(ExternalSystemApiUtil.getExternalModuleType(module));
if (!isSourceSet) {
return mainModuleData;
}
else {
String projectId = ExternalSystemApiUtil.getExternalProjectId(module);
DataNode<? extends ModuleData> cachedNode = cache.get(projectId);
if (cachedNode != null) return cachedNode;
return ExternalSystemApiUtil.find(mainModuleData, GradleSourceSetData.KEY, node -> {
String id = node.getData().getId();
cache.put(id, node);
return StringUtil.equals(projectId, id);
});
}
}
@Nullable
public DataNode<ModuleData> findModuleData(@NotNull Module module) {
public DataNode<? extends ModuleData> findMainModuleData(@NotNull Module module) {
final String rootProjectPath = ExternalSystemApiUtil.getExternalRootProjectPath(module);
if (rootProjectPath == null) return null;
......
......@@ -195,7 +195,7 @@ public class GradleProjectTaskRunner extends ProjectTaskRunner {
final String externalProjectPath = ExternalSystemApiUtil.getExternalProjectPath(module);
if (externalProjectPath == null || StringUtil.endsWith(externalProjectPath, "buildSrc")) continue;
final DataNode<ModuleData> moduleDataNode = moduleDataFinder.findModuleData(module);
final DataNode<? extends ModuleData> moduleDataNode = moduleDataFinder.findMainModuleData(module);
if (moduleDataNode == null) continue;
List<String> gradleTasks = ContainerUtil.mapNotNull(ExternalSystemApiUtil.findAll(moduleDataNode, ProjectKeys.TASK),
......
......@@ -190,6 +190,11 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
moduleId, moduleExternalName, moduleInternalName, mainModuleFileDirectoryPath, mainModuleConfigPath);
sourceSetData.setGroup(externalProject.getGroup());
if ("main".equals(sourceSet.getName())) {
sourceSetData.setPublication(new ProjectId(externalProject.getGroup(),
externalProject.getName(),
externalProject.getVersion()));
}
sourceSetData.setVersion(externalProject.getVersion());
sourceSetData.setIdeModuleGroup(moduleGroup);
......@@ -967,6 +972,12 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
}
final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName, unresolved);
if(moduleVersion != null) {
library.setGroup(moduleVersion.getGroup());
library.setArtifactId(moduleVersion.getName());
library.setVersion(moduleVersion.getVersion());
}
if (!unresolved) {
library.addPath(LibraryPathType.BINARY, binaryPath.getAbsolutePath());
}
......
......@@ -109,6 +109,9 @@ public class GradleProjectResolverUtil {
moduleData.setDescription(externalProject.getDescription());
if (!resolverCtx.isResolveModulePerSourceSet()) {
moduleData.setArtifacts(externalProject.getArtifacts());
moduleData.setPublication(new ProjectId(externalProject.getGroup(),
externalProject.getName(),
externalProject.getVersion()));
}
}
......@@ -583,6 +586,9 @@ public class GradleProjectResolverUtil {
String libraryName = mergedDependency.getId().getPresentableName();
final LibraryLevel level = StringUtil.isNotEmpty(libraryName) ? LibraryLevel.PROJECT : LibraryLevel.MODULE;
final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName);
library.setArtifactId(mergedDependency.getId().getName());
library.setGroup(mergedDependency.getId().getGroup());
library.setVersion(mergedDependency.getId().getVersion());
LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level);
libraryDependencyData.setScope(dependencyScope);
libraryDependencyData.setOrder(mergedDependency.getClasspathOrder());
......@@ -607,6 +613,9 @@ public class GradleProjectResolverUtil {
final LibraryLevel level = LibraryLevel.MODULE;
String libraryName = mergedDependency.getId().getPresentableName();
final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName);
library.setArtifactId(mergedDependency.getId().getName());
library.setGroup(mergedDependency.getId().getGroup());
library.setVersion(mergedDependency.getId().getVersion());
LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level);
libraryDependencyData.setScope(dependencyScope);
libraryDependencyData.setOrder(mergedDependency.getClasspathOrder());
......
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.gradle.service.project;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectCoordinate;
import com.intellij.openapi.externalSystem.service.project.ExternalProjectsWorkspaceImpl;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.execution.build.CachedModuleDataFinder;
import org.jetbrains.plugins.gradle.util.GradleConstants;
/**
* @author Vladislav.Soroka
*/
public class GradleWorkspaceContributor implements ExternalProjectsWorkspaceImpl.Contributor {
private static final Key<CachedModuleDataFinder> MODULE_DATA_FINDER = Key.create("GradleModuleDataFinder");
@Nullable
@Override
public ProjectCoordinate findProjectId(Module module, IdeModifiableModelsProvider modelsProvider) {
if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) {
return null;
}
CachedModuleDataFinder moduleDataFinder = modelsProvider.getUserData(MODULE_DATA_FINDER);
if (moduleDataFinder == null) {
moduleDataFinder = new CachedModuleDataFinder();
modelsProvider.putUserData(MODULE_DATA_FINDER, moduleDataFinder);
}
DataNode<? extends ModuleData> moduleData = moduleDataFinder.findModuleData(module);
return moduleData != null ? moduleData.getData().getPublication() : null;
}
}
......@@ -21,6 +21,7 @@ import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.externalSystem.model.project.ProjectId;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
......@@ -275,7 +276,10 @@ public class MavenModuleImporter {
myRootModelAdapter.addSystemDependency(artifact, scope);
}
else {
myRootModelAdapter.addLibraryDependency(artifact, scope, myModifiableModelsProvider, myMavenProject);
LibraryOrderEntry libraryOrderEntry =
myRootModelAdapter.addLibraryDependency(artifact, scope, myModifiableModelsProvider, myMavenProject);
myModifiableModelsProvider.trySubstitute(
myModule, libraryOrderEntry, new ProjectId(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()));
}
}
......
......@@ -18,6 +18,7 @@ package org.jetbrains.idea.maven.importing;
import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.project.ProjectId;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
......@@ -36,13 +37,13 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.Stack;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.importing.configurers.MavenModuleConfigurer;
import org.jetbrains.idea.maven.model.MavenArtifact;
import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.project.*;
import org.jetbrains.idea.maven.utils.MavenLog;
import org.jetbrains.idea.maven.utils.MavenProcessCanceledException;
......@@ -437,7 +438,9 @@ public class MavenProjectImporter {
MavenProject project = each.getKey();
Module module = myMavenProjectToModule.get(project);
boolean isNewModule = projectsWithNewlyCreatedModules.contains(project);
MavenId mavenId = project.getMavenId();
myModelsProvider.registerModulePublication(
module, new ProjectId(mavenId.getGroupId(), mavenId.getArtifactId(), mavenId.getVersion()));
MavenModuleImporter moduleImporter = createModuleImporter(module, project, each.getValue());
modulesToMavenize.add(module);
importers.add(moduleImporter);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment