mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[ExternalSystem|Sync] cleanup: extract ModifiableWorkspaceModel#updateSubstitutions
Encapsulate all dependency substitution functionality inside the ModifiableWorkspaceModel class. ### Issues * IDEA-134885 Support substitution of library dependency with module dependency when the module is a part of another Maven or Gradle project GitOrigin-RevId: 6c8817ca2e0eee703137244d747fb07c4cb0775f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
869a9954be
commit
4be8b382f4
@@ -46,21 +46,13 @@ c:com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
|
||||
- com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent
|
||||
- <init>(com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId,com.intellij.build.events.BuildEvent):V
|
||||
- getBuildEvent():com.intellij.build.events.BuildEvent
|
||||
*:com.intellij.openapi.externalSystem.service.project.ExternalSystemCoordinateContributor
|
||||
- *sf:Companion:com.intellij.openapi.externalSystem.service.project.ExternalSystemCoordinateContributor$Companion
|
||||
- findModuleCoordinate(com.intellij.openapi.module.Module):com.intellij.openapi.externalSystem.model.project.ProjectCoordinate
|
||||
*f:com.intellij.openapi.externalSystem.service.project.ExternalSystemCoordinateContributor$Companion
|
||||
com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver
|
||||
- *:resolveProjectInfo(com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId,java.lang.String,Z,com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings,com.intellij.openapi.externalSystem.importing.ProjectResolverPolicy,com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener):com.intellij.openapi.externalSystem.model.DataNode
|
||||
com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
- com.intellij.openapi.externalSystem.service.project.IdeModelsProvider
|
||||
- com.intellij.openapi.util.UserDataHolder
|
||||
- *a:findModifiableModel(java.lang.Class):com.intellij.openapi.externalSystem.service.project.ModifiableModel
|
||||
- *a:findModuleByPublication(com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):java.lang.String
|
||||
- *a:getModifiableModel(java.lang.Class):com.intellij.openapi.externalSystem.service.project.ModifiableModel
|
||||
- *a:isSubstituted(java.lang.String):Z
|
||||
- *a:registerModulePublication(com.intellij.openapi.module.Module,com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):V
|
||||
- *a:trySubstitute(com.intellij.openapi.module.Module,com.intellij.openapi.roots.LibraryOrderEntry,com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):com.intellij.openapi.roots.ModuleOrderEntry
|
||||
*:com.intellij.openapi.externalSystem.service.project.ModifiableModel
|
||||
- a:commit():V
|
||||
- a:dispose():V
|
||||
|
||||
@@ -1012,6 +1012,10 @@ com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallba
|
||||
- onFailure(java.lang.String,java.lang.String):V
|
||||
- onSuccess(com.intellij.openapi.externalSystem.model.DataNode):V
|
||||
- onSuccess(com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId,com.intellij.openapi.externalSystem.model.DataNode):V
|
||||
com.intellij.openapi.externalSystem.service.project.ExternalSystemCoordinateContributor
|
||||
- sf:Companion:com.intellij.openapi.externalSystem.service.project.ExternalSystemCoordinateContributor$Companion
|
||||
- findModuleCoordinate(com.intellij.openapi.module.Module):com.intellij.openapi.externalSystem.model.project.ProjectCoordinate
|
||||
f:com.intellij.openapi.externalSystem.service.project.ExternalSystemCoordinateContributor$Companion
|
||||
com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver
|
||||
- a:cancelTask(com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId,com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener):Z
|
||||
- resolveProjectInfo(com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId,java.lang.String,Z,com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings,com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener):com.intellij.openapi.externalSystem.model.DataNode
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.jetbrains.annotations.ApiStatus
|
||||
* The import can use that mapping for binary dependencies substitution on module dependencies
|
||||
* across unrelated projects based on any build system or another kind of project modules generator.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
interface ExternalSystemCoordinateContributor {
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,12 +19,9 @@ import com.intellij.facet.ModifiableFacetModel;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
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;
|
||||
@@ -51,6 +48,10 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider, UserData
|
||||
@NotNull
|
||||
ModifiableModuleModel getModifiableModuleModel();
|
||||
|
||||
@NotNull
|
||||
@ApiStatus.Internal
|
||||
ModifiableWorkspaceModel getModifiableWorkspaceModel();
|
||||
|
||||
@NotNull
|
||||
ModifiableRootModel getModifiableRootModel(Module module);
|
||||
|
||||
@@ -86,18 +87,4 @@ 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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.externalSystem.service.project
|
||||
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface ModifiableWorkspaceModel {
|
||||
|
||||
fun updateLibrarySubstitutions()
|
||||
|
||||
fun isLibrarySubstituted(library: Library): Boolean
|
||||
|
||||
fun commit()
|
||||
|
||||
companion object {
|
||||
|
||||
val NOP = object : ModifiableWorkspaceModel {
|
||||
override fun updateLibrarySubstitutions() = Unit
|
||||
override fun isLibrarySubstituted(library: Library): Boolean = false
|
||||
override fun commit() = Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,15 +68,6 @@ c:com.intellij.openapi.externalSystem.service.execution.ExternalSystemProcessHan
|
||||
- a:getPathMapper():com.intellij.util.PathMapper
|
||||
com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationExtension
|
||||
- *:isInternalError(java.lang.Throwable):Z
|
||||
*f:com.intellij.openapi.externalSystem.service.project.ModifiableWorkspace
|
||||
- addSubstitution(java.lang.String,java.lang.String,java.lang.String,com.intellij.openapi.roots.DependencyScope):V
|
||||
- commit():V
|
||||
- findModule(com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):java.lang.String
|
||||
- getSubstitutedLibrary(java.lang.String):java.lang.String
|
||||
- isSubstituted(java.lang.String):Z
|
||||
- isSubstitution(java.lang.String,java.lang.String,com.intellij.openapi.roots.DependencyScope):Z
|
||||
- register(com.intellij.openapi.externalSystem.model.project.ProjectCoordinate,com.intellij.openapi.module.Module):V
|
||||
- removeSubstitution(java.lang.String,java.lang.String,java.lang.String,com.intellij.openapi.roots.DependencyScope):V
|
||||
f:com.intellij.openapi.externalSystem.service.ui.ExternalSystemJdkComboBox
|
||||
- com.intellij.openapi.ui.ComboBoxWithWidePopup
|
||||
- *:select(java.lang.String):V
|
||||
|
||||
@@ -520,7 +520,6 @@ a:com.intellij.openapi.externalSystem.service.project.AbstractIdeModifiableModel
|
||||
- findIdeLibrary(com.intellij.openapi.externalSystem.model.project.LibraryData):com.intellij.openapi.roots.libraries.Library
|
||||
- findIdeModule(java.lang.String):com.intellij.openapi.module.Module
|
||||
- findModifiableModel(java.lang.Class):com.intellij.openapi.externalSystem.service.project.ModifiableModel
|
||||
- findModuleByPublication(com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):java.lang.String
|
||||
- getAllDependentModules(com.intellij.openapi.module.Module):java.util.List
|
||||
- getAllLibraries():com.intellij.openapi.roots.libraries.Library[]
|
||||
- getContentRoots(com.intellij.openapi.module.Module):com.intellij.openapi.vfs.VirtualFile[]
|
||||
@@ -539,15 +538,11 @@ a:com.intellij.openapi.externalSystem.service.project.AbstractIdeModifiableModel
|
||||
- getSourceRoots(com.intellij.openapi.module.Module):com.intellij.openapi.vfs.VirtualFile[]
|
||||
- getSourceRoots(com.intellij.openapi.module.Module,Z):com.intellij.openapi.vfs.VirtualFile[]
|
||||
- getUserData(com.intellij.openapi.util.Key):java.lang.Object
|
||||
- isSubstituted(java.lang.String):Z
|
||||
- newModule(com.intellij.openapi.externalSystem.model.project.ModuleData):com.intellij.openapi.module.Module
|
||||
- newModule(java.lang.String,java.lang.String):com.intellij.openapi.module.Module
|
||||
- putUserData(com.intellij.openapi.util.Key,java.lang.Object):V
|
||||
- registerModulePublication(com.intellij.openapi.module.Module,com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):V
|
||||
- removeLibrary(com.intellij.openapi.roots.libraries.Library):V
|
||||
- setTestModuleProperties(com.intellij.openapi.module.Module,java.lang.String):V
|
||||
- trySubstitute(com.intellij.openapi.module.Module,com.intellij.openapi.roots.LibraryOrderEntry,com.intellij.openapi.externalSystem.model.project.ProjectCoordinate):com.intellij.openapi.roots.ModuleOrderEntry
|
||||
- p:updateSubstitutions():V
|
||||
pc:com.intellij.openapi.externalSystem.service.project.AbstractIdeModifiableModelsProvider$MyUserDataHolderBase
|
||||
- com.intellij.openapi.util.UserDataHolderBase
|
||||
- p:<init>():V
|
||||
|
||||
@@ -7,17 +7,11 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
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.*;
|
||||
@@ -32,11 +26,11 @@ import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ClassMap;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.graph.CachingSemiGraph;
|
||||
import com.intellij.util.graph.Graph;
|
||||
import com.intellij.util.graph.GraphGenerator;
|
||||
import com.intellij.util.graph.InboundSemiGraph;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -47,12 +41,12 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
|
||||
private static final Logger LOG = Logger.getInstance(AbstractIdeModifiableModelsProvider.class);
|
||||
|
||||
protected ModifiableModuleModel myModifiableModuleModel;
|
||||
private ModifiableWorkspaceModel myModifiableWorkspaceModel;
|
||||
protected final Map<Module, ModifiableRootModel> myModifiableRootModels = new HashMap<>();
|
||||
protected final Map<Module, ModifiableFacetModel> myModifiableFacetModels = new HashMap<>();
|
||||
protected final Map<Module, String> myProductionModulesForTestModules = new HashMap<>();
|
||||
protected final Map<Library, Library.ModifiableModel> myModifiableLibraryModels = new IdentityHashMap<>();
|
||||
protected final ClassMap<ModifiableModel> myModifiableModels = new ClassMap<>();
|
||||
private @Nullable ModifiableWorkspace myModifiableWorkspace;
|
||||
protected final MyUserDataHolderBase myUserData;
|
||||
private volatile boolean myDisposed;
|
||||
|
||||
@@ -173,6 +167,18 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
|
||||
return myModifiableModuleModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ApiStatus.Internal
|
||||
public @NotNull ModifiableWorkspaceModel getModifiableWorkspaceModel() {
|
||||
if (myModifiableWorkspaceModel == null) {
|
||||
myModifiableWorkspaceModel = ReadAction.compute(() -> {
|
||||
var workspace = ExternalProjectsWorkspace.getInstance(myProject);
|
||||
return workspace.getModifiableModel(this);
|
||||
});
|
||||
}
|
||||
return myModifiableWorkspaceModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ModifiableRootModel getModifiableRootModel(Module module) {
|
||||
return (ModifiableRootModel)getRootModel(module);
|
||||
@@ -217,16 +223,6 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
|
||||
return myModifiableLibraryModels.computeIfAbsent(library, k -> doGetModifiableLibraryModel(library));
|
||||
}
|
||||
|
||||
private @Nullable ModifiableWorkspace getModifiableWorkspace() {
|
||||
if (!ExternalProjectsWorkspaceImpl.isDependencySubstitutionEnabled()) {
|
||||
return null;
|
||||
}
|
||||
if (myModifiableWorkspace == null) {
|
||||
myModifiableWorkspace = doGetModifiableWorkspace();
|
||||
}
|
||||
return myModifiableWorkspace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String @NotNull [] getLibraryUrls(@NotNull Library library, @NotNull OrderRootType type) {
|
||||
final Library.ModifiableModel model = myModifiableLibraryModels.get(library);
|
||||
@@ -251,11 +247,6 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
|
||||
return list;
|
||||
}
|
||||
|
||||
private ModifiableWorkspace doGetModifiableWorkspace() {
|
||||
return ReadAction.compute(() -> myProject.getService(ExternalProjectsWorkspaceImpl.class)
|
||||
.createModifiableWorkspace(() -> Arrays.asList(getModules())));
|
||||
}
|
||||
|
||||
private Graph<Module> getModuleGraph() {
|
||||
return GraphGenerator.generate(CachingSemiGraph.cache(new InboundSemiGraph<>() {
|
||||
@Override
|
||||
@@ -315,44 +306,6 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
|
||||
return myProductionModulesForTestModules.get(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleOrderEntry trySubstitute(Module ownerModule, LibraryOrderEntry libraryOrderEntry, ProjectCoordinate publicationId) {
|
||||
ModifiableWorkspace workspace = getModifiableWorkspace();
|
||||
String workspaceModuleCandidate = workspace == null ? null : workspace.findModule(publicationId);
|
||||
Module workspaceModule = workspaceModuleCandidate == null ? null : findIdeModule(workspaceModuleCandidate);
|
||||
if (workspaceModule == null) {
|
||||
return null;
|
||||
}
|
||||
ModifiableRootModel modifiableRootModel = getModifiableRootModel(ownerModule);
|
||||
ModuleOrderEntry moduleOrderEntry = modifiableRootModel.findModuleOrderEntry(workspaceModule);
|
||||
if (moduleOrderEntry == null) { // if that module exists already (after re-import)
|
||||
moduleOrderEntry = modifiableRootModel.addModuleOrderEntry(workspaceModule);
|
||||
}
|
||||
moduleOrderEntry.setScope(libraryOrderEntry.getScope());
|
||||
moduleOrderEntry.setExported(libraryOrderEntry.isExported());
|
||||
workspace.addSubstitution(ownerModule.getName(),
|
||||
workspaceModule.getName(),
|
||||
libraryOrderEntry.getLibraryName(),
|
||||
libraryOrderEntry.getScope());
|
||||
modifiableRootModel.removeOrderEntry(libraryOrderEntry);
|
||||
return moduleOrderEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerModulePublication(Module module, ProjectCoordinate modulePublication) {
|
||||
ModifiableWorkspace workspace = getModifiableWorkspace();
|
||||
if (workspace != null) {
|
||||
workspace.register(modulePublication, module);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubstituted(String libraryName) {
|
||||
ModifiableWorkspace workspace = getModifiableWorkspace();
|
||||
if (workspace == null) return false;
|
||||
return workspace.isSubstituted(libraryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T> T getUserData(@NotNull Key<T> key) {
|
||||
return myUserData.getUserData(key);
|
||||
@@ -362,93 +315,4 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
myUserData.putUserData(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String findModuleByPublication(ProjectCoordinate publicationId) {
|
||||
ModifiableWorkspace workspace = getModifiableWorkspace();
|
||||
return workspace == null ? null : workspace.findModule(publicationId);
|
||||
}
|
||||
|
||||
protected void updateSubstitutions() {
|
||||
ModifiableWorkspace workspace = getModifiableWorkspace();
|
||||
if (workspace == null) return;
|
||||
|
||||
final List<String> oldModules = ContainerUtil.map(ModuleManager.getInstance(myProject).getModules(), module -> module.getName());
|
||||
final List<String> newModules = ContainerUtil.map(myModifiableModuleModel.getModules(), module -> module.getName());
|
||||
|
||||
final Collection<String> removedModules = new HashSet<>(oldModules);
|
||||
removedModules.removeAll(newModules);
|
||||
|
||||
|
||||
Map<String, String> toSubstitute = new HashMap<>();
|
||||
ProjectDataManager projectDataManager = ProjectDataManager.getInstance();
|
||||
ExternalSystemManager.EP_NAME.forEachExtensionSafe(manager -> {
|
||||
Collection<ExternalProjectInfo> projectsData = projectDataManager.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 = workspace.findModule(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 (workspace.isSubstitution(module.getName(), workspaceModule, scope)) {
|
||||
String libraryName = workspace.getSubstitutedLibrary(workspaceModule);
|
||||
if (libraryName != null) {
|
||||
Library library = getLibraryByName(libraryName);
|
||||
if (library != null) {
|
||||
modifiableRootModel.removeOrderEntry(orderEntry);
|
||||
entries[i] = modifiableRootModel.addLibraryEntry(library);
|
||||
changed = true;
|
||||
workspace.removeSubstitution(module.getName(), workspaceModule, libraryName, scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(orderEntry instanceof LibraryOrderEntry libraryOrderEntry)) continue;
|
||||
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;
|
||||
workspace.addSubstitution(module.getName(), workspaceModule,
|
||||
libraryOrderEntry.getLibraryName(),
|
||||
libraryOrderEntry.getScope());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
modifiableRootModel.rearrangeOrderEntries(entries);
|
||||
}
|
||||
}
|
||||
|
||||
workspace.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.externalSystem.service.project
|
||||
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.components.StoragePathMacros.CACHE_FILE
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.util.concurrency.annotations.RequiresReadLock
|
||||
import com.intellij.util.xmlb.annotations.MapAnnotation
|
||||
import com.intellij.util.xmlb.annotations.Property
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Service(Service.Level.PROJECT)
|
||||
@State(name = "ExternalProjectsWorkspace", storages = [Storage(CACHE_FILE)])
|
||||
class ExternalProjectsWorkspace(
|
||||
private val project: Project,
|
||||
) : SimplePersistentStateComponent<ExternalProjectsWorkspace.State>(State()) {
|
||||
|
||||
class State : BaseState() {
|
||||
|
||||
@get:Property(surroundWithTag = false)
|
||||
@get:MapAnnotation(
|
||||
surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false,
|
||||
entryTagName = "module", keyAttributeName = "name", valueAttributeName = "library")
|
||||
var librarySubstitutions by map<String, String>()
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
fun getModifiableModel(modelProvider: IdeModifiableModelsProvider): ModifiableWorkspaceModel {
|
||||
if (!Registry.`is`("external.system.substitute.library.dependencies")) {
|
||||
return ModifiableWorkspaceModel.NOP
|
||||
}
|
||||
return ModifiableWorkspaceModelImpl(project, state, modelProvider)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): ExternalProjectsWorkspace {
|
||||
return project.service<ExternalProjectsWorkspace>()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.externalSystem.service.project;
|
||||
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
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.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
@Service(Service.Level.PROJECT)
|
||||
@ApiStatus.Internal
|
||||
@State(name = "externalSubstitutions", storages = @Storage(StoragePathMacros.WORKSPACE_FILE))
|
||||
final class ExternalProjectsWorkspaceImpl implements PersistentStateComponent<ExternalProjectsWorkspaceImpl.State> {
|
||||
|
||||
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();
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull State state) {
|
||||
myState = state;
|
||||
}
|
||||
|
||||
public static boolean isDependencySubstitutionEnabled() {
|
||||
return Registry.is("external.system.substitute.library.dependencies");
|
||||
}
|
||||
|
||||
public ModifiableWorkspace createModifiableWorkspace(Supplier<? extends List<Module>> modulesSupplier) {
|
||||
return new ModifiableWorkspace(myState, modulesSupplier);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,9 @@ public class IdeModifiableModelsProviderImpl extends AbstractIdeModifiableModels
|
||||
private void workspaceModelCommit() {
|
||||
ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(() -> {
|
||||
|
||||
updateSubstitutions();
|
||||
var workspaceModel = getModifiableWorkspaceModel();
|
||||
workspaceModel.updateLibrarySubstitutions();
|
||||
workspaceModel.commit();
|
||||
|
||||
LibraryTable.ModifiableModel projectLibrariesModel = getModifiableProjectLibrariesModel();
|
||||
for (Map.Entry<Library, Library.ModifiableModel> entry: myModifiableLibraryModels.entrySet()) {
|
||||
@@ -136,7 +138,7 @@ public class IdeModifiableModelsProviderImpl extends AbstractIdeModifiableModels
|
||||
else if (fromLibrary.getTable() != null && libraryName != null && projectLibrariesModel.getLibraryByName(libraryName) == null) {
|
||||
Disposer.dispose(modifiableModel);
|
||||
}
|
||||
else if (isSubstituted(fromLibrary.getName())) {
|
||||
else if (workspaceModel.isLibrarySubstituted(fromLibrary)) {
|
||||
Disposer.dispose(modifiableModel);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.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.CollectionFactory;
|
||||
import com.intellij.util.containers.HashingStrategy;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public final class ModifiableWorkspace {
|
||||
private final Map<ProjectCoordinate, String> myModuleMappingById = CollectionFactory.createCustomHashingStrategyMap(new HashingStrategy<>() {
|
||||
@Override
|
||||
public int hashCode(ProjectCoordinate object) {
|
||||
if (object == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String groupId = object.getGroupId();
|
||||
String artifactId = object.getArtifactId();
|
||||
String version = object.getVersion();
|
||||
int result1 = (groupId != null ? groupId.hashCode() : 0);
|
||||
result1 = 31 * result1 + (artifactId != null ? artifactId.hashCode() : 0);
|
||||
result1 = 31 * result1 + (version != null ? version.hashCode() : 0);
|
||||
return result1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(ProjectCoordinate o1, ProjectCoordinate o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
if (o1 == null || o2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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 ExternalProjectsWorkspaceImpl.State myState;
|
||||
private final MultiMap<String/* module owner */, String /* substitution modules */> mySubstitutions = MultiMap.createSet();
|
||||
private final Map<String /* module name */, String /* library name */> myNamesMap = new HashMap<>();
|
||||
private final Supplier<? extends List<Module>> myModulesSupplier;
|
||||
|
||||
|
||||
ModifiableWorkspace(ExternalProjectsWorkspaceImpl.State state,
|
||||
Supplier<? extends List<Module>> modulesSupplier) {
|
||||
myModulesSupplier = modulesSupplier;
|
||||
Set<String> existingModules = new HashSet<>();
|
||||
for (Module module : modulesSupplier.get()) {
|
||||
register(module);
|
||||
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 = new HashSet<>();
|
||||
myModulesSupplier.get().stream().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<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.containsValue(libraryName);
|
||||
}
|
||||
|
||||
public String getSubstitutedLibrary(String moduleName) {
|
||||
return myNamesMap.get(moduleName);
|
||||
}
|
||||
|
||||
public @Nullable 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) {
|
||||
Arrays.stream(ExternalSystemCoordinateContributor.EP_NAME.getExtensions())
|
||||
.map(contributor -> contributor.findModuleCoordinate(module))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.ifPresent(id -> register(id, module));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.externalSystem.service.project
|
||||
|
||||
import com.intellij.openapi.externalSystem.ExternalSystemManager
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectCoordinate
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.roots.ModuleOrderEntry
|
||||
import com.intellij.openapi.roots.OrderEntry
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.util.concurrency.annotations.RequiresWriteLock
|
||||
import com.intellij.util.containers.CollectionFactory
|
||||
import com.intellij.util.containers.HashingStrategy
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.util.*
|
||||
|
||||
@ApiStatus.Internal
|
||||
class ModifiableWorkspaceModelImpl internal constructor(
|
||||
private val project: Project,
|
||||
private val state: ExternalProjectsWorkspace.State,
|
||||
private val modelsProvider: IdeModifiableModelsProvider,
|
||||
) : ModifiableWorkspaceModel {
|
||||
|
||||
private val librarySubstitutions = state.librarySubstitutions.toMutableMap()
|
||||
|
||||
@RequiresWriteLock
|
||||
override fun commit() {
|
||||
state.librarySubstitutions = librarySubstitutions.toMutableMap()
|
||||
}
|
||||
|
||||
override fun isLibrarySubstituted(library: Library): Boolean {
|
||||
return library.name != null && librarySubstitutions.containsValue(library.name)
|
||||
}
|
||||
|
||||
override fun updateLibrarySubstitutions() {
|
||||
val libraryToModuleMap = buildLibraryToModuleMap()
|
||||
|
||||
for (module in modelsProvider.modules) {
|
||||
val modifiableModule = modelsProvider.getModifiableRootModel(module)
|
||||
|
||||
val entries = modifiableModule.orderEntries
|
||||
var changed = false
|
||||
|
||||
for (i in entries.indices) {
|
||||
val orderEntry = entries[i]
|
||||
val newOrderEntry = updateLibrarySubstitution(modifiableModule, orderEntry, libraryToModuleMap)
|
||||
if (newOrderEntry !== orderEntry) {
|
||||
entries[i] = newOrderEntry
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
modifiableModule.rearrangeOrderEntries(entries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLibraryToCoordinateMap(): Map<String, ProjectCoordinate> {
|
||||
val result = HashMap<String, ProjectCoordinate>()
|
||||
val projectDataManager = ProjectDataManager.getInstance()
|
||||
ExternalSystemManager.EP_NAME.forEachExtensionSafe { manager ->
|
||||
val projectsData = projectDataManager.getExternalProjectsData(project, manager.systemId)
|
||||
for (projectInfo in projectsData) {
|
||||
val projectStructure = projectInfo.externalProjectStructure ?: continue
|
||||
val libraryNodes = ExternalSystemApiUtil.findAll(projectStructure, ProjectKeys.LIBRARY)
|
||||
for (libraryNode in libraryNodes) {
|
||||
val libraryData = libraryNode.getData()
|
||||
result.put(libraryData.internalName, libraryData)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun buildCoordinateToModuleMap(): Map<ProjectCoordinate, String> {
|
||||
val result = CollectionFactory.createCustomHashingStrategyMap<ProjectCoordinate, String>(ProjectCoordinateHashingStrategy())
|
||||
ExternalSystemCoordinateContributor.EP_NAME.forEachExtensionSafe { contributor ->
|
||||
for (module in modelsProvider.modules) {
|
||||
val moduleCoordinate = contributor.findModuleCoordinate(module) ?: continue
|
||||
result.put(moduleCoordinate, module.name)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun buildLibraryToModuleMap(): Map<String, String> {
|
||||
val libraryToCoordinateMap = buildLibraryToCoordinateMap()
|
||||
val coordinateToModuleMap = buildCoordinateToModuleMap()
|
||||
val result = HashMap<String, String>()
|
||||
for ((libraryName, libraryCoordinate) in libraryToCoordinateMap) {
|
||||
val moduleName = coordinateToModuleMap[libraryCoordinate] ?: continue
|
||||
result[libraryName] = moduleName
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun updateLibrarySubstitution(
|
||||
modifiableModule: ModifiableRootModel,
|
||||
orderEntry: OrderEntry,
|
||||
libraryToModuleMap: Map<String, String>,
|
||||
): OrderEntry {
|
||||
var newOrderEntry = orderEntry
|
||||
if (newOrderEntry is ModuleOrderEntry) {
|
||||
newOrderEntry = replaceModuleWithLibraryOrNull(modifiableModule, newOrderEntry, libraryToModuleMap)
|
||||
?: newOrderEntry
|
||||
}
|
||||
if (newOrderEntry is LibraryOrderEntry) {
|
||||
newOrderEntry = replaceLibraryWithModuleOrNull(modifiableModule, newOrderEntry, libraryToModuleMap)
|
||||
?: newOrderEntry
|
||||
}
|
||||
return newOrderEntry
|
||||
}
|
||||
|
||||
private fun replaceModuleWithLibraryOrNull(
|
||||
modifiableModule: ModifiableRootModel,
|
||||
moduleOrderEntry: ModuleOrderEntry,
|
||||
libraryToModuleMap: Map<String, String>,
|
||||
): LibraryOrderEntry? {
|
||||
val ownerModuleName = modifiableModule.module.name
|
||||
val moduleName = moduleOrderEntry.moduleName
|
||||
val moduleScope = moduleOrderEntry.scope
|
||||
val moduleScopeName = moduleScope.displayName
|
||||
val isModuleExported = moduleOrderEntry.isExported
|
||||
val substitutionId = getLibrarySubstitutionId(ownerModuleName, moduleName, moduleScopeName)
|
||||
|
||||
val libraryName = librarySubstitutions[substitutionId] ?: return null
|
||||
|
||||
if (libraryToModuleMap[libraryName] == moduleName) {
|
||||
return null
|
||||
}
|
||||
|
||||
val library = modelsProvider.getLibraryByName(libraryName) ?: return null
|
||||
val libraryOrderEntry = modifiableModule.addLibraryEntry(library)
|
||||
libraryOrderEntry.setScope(moduleScope)
|
||||
libraryOrderEntry.setExported(isModuleExported)
|
||||
|
||||
modifiableModule.removeOrderEntry(moduleOrderEntry)
|
||||
|
||||
librarySubstitutions.remove(substitutionId)
|
||||
|
||||
return libraryOrderEntry
|
||||
}
|
||||
|
||||
private fun replaceLibraryWithModuleOrNull(
|
||||
modifiableModule: ModifiableRootModel,
|
||||
libraryOrderEntry: LibraryOrderEntry,
|
||||
libraryToModuleMap: Map<String, String>,
|
||||
): ModuleOrderEntry? {
|
||||
|
||||
if (libraryOrderEntry.isModuleLevel) {
|
||||
return null
|
||||
}
|
||||
|
||||
val ownerModuleName = modifiableModule.module.name
|
||||
val libraryName = libraryOrderEntry.libraryName ?: return null
|
||||
val libraryScope = libraryOrderEntry.scope
|
||||
val libraryScopeName = libraryScope.displayName
|
||||
val isLibraryExported = libraryOrderEntry.isExported
|
||||
|
||||
val moduleName = libraryToModuleMap[libraryName] ?: return null
|
||||
|
||||
val module = modelsProvider.findIdeModule(moduleName) ?: return null
|
||||
val moduleOrderEntry = modifiableModule.addModuleOrderEntry(module)
|
||||
moduleOrderEntry.setScope(libraryScope)
|
||||
moduleOrderEntry.setExported(isLibraryExported)
|
||||
|
||||
modifiableModule.removeOrderEntry(libraryOrderEntry)
|
||||
|
||||
val substitutionId: String = getLibrarySubstitutionId(ownerModuleName, moduleName, libraryScopeName)
|
||||
librarySubstitutions.put(substitutionId, libraryName)
|
||||
|
||||
return moduleOrderEntry
|
||||
}
|
||||
|
||||
private class ProjectCoordinateHashingStrategy : HashingStrategy<ProjectCoordinate> {
|
||||
|
||||
override fun equals(o1: ProjectCoordinate, o2: ProjectCoordinate?): Boolean {
|
||||
return o2 != null &&
|
||||
o1.groupId == o2.groupId &&
|
||||
o1.artifactId == o2.artifactId &&
|
||||
o1.version == o2.version
|
||||
}
|
||||
|
||||
override fun hashCode(o: ProjectCoordinate): Int {
|
||||
return Objects.hash(o.groupId, o.artifactId, o.version)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun getLibrarySubstitutionId(ownerModuleName: String, moduleName: String, scopeName: String): String {
|
||||
return ownerModuleName + "_" + moduleName + '_' + scopeName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,10 +84,6 @@ 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);
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.intellij.openapi.externalSystem.model.project.LibraryData;
|
||||
import com.intellij.openapi.externalSystem.model.project.LibraryPathType;
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData;
|
||||
import com.intellij.openapi.externalSystem.service.project.ExternalLibraryPathTypeMapper;
|
||||
import com.intellij.openapi.externalSystem.service.project.ModifiableWorkspaceModel;
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
|
||||
@@ -200,6 +201,7 @@ public final class LibraryDataService extends AbstractProjectDataService<Library
|
||||
|
||||
final List<Library> orphanIdeLibraries = new SmartList<>();
|
||||
final LibraryTable.ModifiableModel librariesModel = modelsProvider.getModifiableProjectLibrariesModel();
|
||||
final ModifiableWorkspaceModel workspaceModel = modelsProvider.getModifiableWorkspaceModel();
|
||||
final Map<String, Library> namesToLibs = new HashMap<>();
|
||||
final Set<Library> potentialOrphans = new HashSet<>();
|
||||
RootPolicy<Void> excludeUsedLibraries = new RootPolicy<>() {
|
||||
@@ -231,8 +233,8 @@ public final class LibraryDataService extends AbstractProjectDataService<Library
|
||||
}
|
||||
}
|
||||
|
||||
for (Library lib: potentialOrphans) {
|
||||
if (!modelsProvider.isSubstituted(lib.getName())) {
|
||||
for (Library lib : potentialOrphans) {
|
||||
if (!workspaceModel.isLibrarySubstituted(lib)) {
|
||||
orphanIdeLibraries.add(lib);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class LibraryDependencyDataService extends AbstractDependencyDataSe
|
||||
modelsProvider);
|
||||
}
|
||||
else if (entry instanceof LibraryOrderEntry libraryOrderEntry) {
|
||||
processingResult = importLibraryOrderEntry(libraryOrderEntry, toImport.projectLibraries, modifiableRootModel, modelsProvider,
|
||||
processingResult = importLibraryOrderEntry(libraryOrderEntry, toImport.projectLibraries, modifiableRootModel,
|
||||
toImport.hasUnresolvedLibraries);
|
||||
if (processingResult != null) {
|
||||
libraryOrderEntry.setExported(processingResult.isExported());
|
||||
@@ -127,18 +127,12 @@ public final class LibraryDependencyDataService extends AbstractDependencyDataSe
|
||||
@NotNull LibraryOrderEntry entry,
|
||||
@NotNull Map<String/* library name + scope */, LibraryDependencyData> projectLibrariesToImport,
|
||||
@NotNull ModifiableRootModel modifiableRootModel,
|
||||
@NotNull IdeModifiableModelsProvider modelsProvider,
|
||||
boolean hasUnresolvedLibraries
|
||||
) {
|
||||
String libraryName = entry.getLibraryName();
|
||||
LibraryDependencyData existing = projectLibrariesToImport.remove(libraryName + entry.getScope().name());
|
||||
if (existing != null) {
|
||||
if (modelsProvider.findModuleByPublication(existing.getTarget()) == null) {
|
||||
return existing;
|
||||
}
|
||||
else {
|
||||
modifiableRootModel.removeOrderEntry(entry);
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
else if (!hasUnresolvedLibraries) {
|
||||
// There is a possible case that a project has been successfully imported from external model and after
|
||||
@@ -205,13 +199,7 @@ public final class LibraryDependencyDataService extends AbstractDependencyDataSe
|
||||
}
|
||||
LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(projectLib);
|
||||
setLibraryScope(orderEntry, projectLib, module, dependencyData);
|
||||
ModuleOrderEntry substitutionEntry = modelsProvider.trySubstitute(module, orderEntry, libraryData);
|
||||
if (substitutionEntry != null) {
|
||||
return substitutionEntry;
|
||||
}
|
||||
else {
|
||||
return orderEntry;
|
||||
}
|
||||
return orderEntry;
|
||||
}
|
||||
|
||||
private static void setLibraryScope(@NotNull LibraryOrderEntry orderEntry,
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.externalSystem.service.project
|
||||
|
||||
import com.intellij.platform.backend.workspace.workspaceModel
|
||||
import com.intellij.platform.testFramework.assertion.moduleAssertion.DependencyAssertions
|
||||
import com.intellij.platform.testFramework.assertion.moduleAssertion.ModuleAssertions
|
||||
import com.intellij.platform.workspace.jps.entities.*
|
||||
import com.intellij.platform.workspace.jps.entities.DependencyScope.*
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@TestApplication
|
||||
class ExternalProjectsWorkspaceTest : ExternalProjectsWorkspaceTestCase() {
|
||||
|
||||
@Test
|
||||
fun `test add library substitution`(): Unit = timeoutRunBlocking {
|
||||
coordinates.modules["lib-module"] = "org.example:library:1.0"
|
||||
coordinates.libraries["library"] = "org.example:library:1.0"
|
||||
project.workspaceModel.update { storage ->
|
||||
storage addEntity LibraryEntity("library", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("app-module", emptyList(), ENTITY_SOURCE) {
|
||||
dependencies += LibraryDependency(LibraryId("library", LibraryTableId.ProjectLibraryTableId), false, COMPILE)
|
||||
}
|
||||
storage addEntity ModuleEntity("lib-module", emptyList(), ENTITY_SOURCE)
|
||||
}
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "lib-module")
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module") { dependency ->
|
||||
Assertions.assertEquals(false, dependency.exported)
|
||||
Assertions.assertEquals(COMPILE, dependency.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test update library substitution`() = timeoutRunBlocking {
|
||||
coordinates.modules["lib-module"] = "org.example:library:1.0"
|
||||
coordinates.libraries["library"] = "org.example:library:1.0"
|
||||
project.workspaceModel.update { storage ->
|
||||
storage addEntity LibraryEntity("library", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("app-module", emptyList(), ENTITY_SOURCE) {
|
||||
dependencies += LibraryDependency(LibraryId("library", LibraryTableId.ProjectLibraryTableId), false, COMPILE)
|
||||
}
|
||||
storage addEntity ModuleEntity("lib-module", emptyList(), ENTITY_SOURCE)
|
||||
}
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
coordinates.modules["library.main"] = coordinates.modules.remove("lib-module")!!
|
||||
project.workspaceModel.update { storage ->
|
||||
storage addEntity ModuleEntity("library.main", emptyList(), ENTITY_SOURCE)
|
||||
}
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "library.main")
|
||||
DependencyAssertions.assertModuleDependency(module, "library.main") { moduleDependency ->
|
||||
Assertions.assertEquals(false, moduleDependency.exported)
|
||||
Assertions.assertEquals(COMPILE, moduleDependency.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test remove library substitution`(): Unit = timeoutRunBlocking {
|
||||
coordinates.modules["lib-module"] = "org.example:library:1.0"
|
||||
coordinates.libraries["library"] = "org.example:library:1.0"
|
||||
project.workspaceModel.update { storage ->
|
||||
storage addEntity LibraryEntity("library", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("app-module", emptyList(), ENTITY_SOURCE) {
|
||||
dependencies += LibraryDependency(LibraryId("library", LibraryTableId.ProjectLibraryTableId), false, COMPILE)
|
||||
}
|
||||
storage addEntity ModuleEntity("lib-module", emptyList(), ENTITY_SOURCE)
|
||||
}
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
coordinates.modules.clear()
|
||||
coordinates.libraries.clear()
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "library")
|
||||
DependencyAssertions.assertLibraryDependency(module, "library") { dependency ->
|
||||
Assertions.assertEquals(false, dependency.exported)
|
||||
Assertions.assertEquals(COMPILE, dependency.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test library substitution scope`(): Unit = timeoutRunBlocking {
|
||||
coordinates.modules["lib-module-compile"] = "org.example:library-compile:1.0"
|
||||
coordinates.modules["lib-module-test"] = "org.example:library-test:1.0"
|
||||
coordinates.modules["lib-module-runtime"] = "org.example:library-runtime:1.0"
|
||||
coordinates.modules["lib-module-provided"] = "org.example:library-provided:1.0"
|
||||
coordinates.libraries["library-compile"] = "org.example:library-compile:1.0"
|
||||
coordinates.libraries["library-test"] = "org.example:library-test:1.0"
|
||||
coordinates.libraries["library-runtime"] = "org.example:library-runtime:1.0"
|
||||
coordinates.libraries["library-provided"] = "org.example:library-provided:1.0"
|
||||
project.workspaceModel.update { storage ->
|
||||
storage addEntity LibraryEntity("library-compile", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity LibraryEntity("library-test", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity LibraryEntity("library-runtime", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity LibraryEntity("library-provided", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("app-module", emptyList(), ENTITY_SOURCE) {
|
||||
dependencies += LibraryDependency(LibraryId("library-compile", LibraryTableId.ProjectLibraryTableId), false, COMPILE)
|
||||
dependencies += LibraryDependency(LibraryId("library-test", LibraryTableId.ProjectLibraryTableId), false, TEST)
|
||||
dependencies += LibraryDependency(LibraryId("library-runtime", LibraryTableId.ProjectLibraryTableId), false, RUNTIME)
|
||||
dependencies += LibraryDependency(LibraryId("library-provided", LibraryTableId.ProjectLibraryTableId), false, PROVIDED)
|
||||
}
|
||||
storage addEntity ModuleEntity("lib-module-compile", emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("lib-module-test", emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("lib-module-runtime", emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("lib-module-provided", emptyList(), ENTITY_SOURCE)
|
||||
}
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "lib-module-compile", "lib-module-test", "lib-module-runtime", "lib-module-provided")
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module-compile") { dependency ->
|
||||
Assertions.assertEquals(COMPILE, dependency.scope)
|
||||
}
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module-test") { dependency ->
|
||||
Assertions.assertEquals(TEST, dependency.scope)
|
||||
}
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module-runtime") { dependency ->
|
||||
Assertions.assertEquals(RUNTIME, dependency.scope)
|
||||
}
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module-provided") { dependency ->
|
||||
Assertions.assertEquals(PROVIDED, dependency.scope)
|
||||
}
|
||||
}
|
||||
|
||||
coordinates.modules.clear()
|
||||
coordinates.libraries.clear()
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "library-compile", "library-test", "library-runtime", "library-provided")
|
||||
DependencyAssertions.assertLibraryDependency(module, "library-compile") { dependency ->
|
||||
Assertions.assertEquals(COMPILE, dependency.scope)
|
||||
}
|
||||
DependencyAssertions.assertLibraryDependency(module, "library-test") { dependency ->
|
||||
Assertions.assertEquals(TEST, dependency.scope)
|
||||
}
|
||||
DependencyAssertions.assertLibraryDependency(module, "library-runtime") { dependency ->
|
||||
Assertions.assertEquals(RUNTIME, dependency.scope)
|
||||
}
|
||||
DependencyAssertions.assertLibraryDependency(module, "library-provided") { dependency ->
|
||||
Assertions.assertEquals(PROVIDED, dependency.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test library substitution exported`(): Unit = timeoutRunBlocking {
|
||||
coordinates.modules["lib-module"] = "org.example:library:1.0"
|
||||
coordinates.modules["lib-module-exported"] = "org.example:library-exported:1.0"
|
||||
coordinates.libraries["library"] = "org.example:library:1.0"
|
||||
coordinates.libraries["library-exported"] = "org.example:library-exported:1.0"
|
||||
project.workspaceModel.update { storage ->
|
||||
storage addEntity LibraryEntity("library", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity LibraryEntity("library-exported", LibraryTableId.ProjectLibraryTableId, emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("app-module", emptyList(), ENTITY_SOURCE) {
|
||||
dependencies += LibraryDependency(LibraryId("library", LibraryTableId.ProjectLibraryTableId), false, COMPILE)
|
||||
dependencies += LibraryDependency(LibraryId("library-exported", LibraryTableId.ProjectLibraryTableId), true, COMPILE)
|
||||
}
|
||||
storage addEntity ModuleEntity("lib-module", emptyList(), ENTITY_SOURCE)
|
||||
storage addEntity ModuleEntity("lib-module-exported", emptyList(), ENTITY_SOURCE)
|
||||
}
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "lib-module", "lib-module-exported")
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module") { dependency ->
|
||||
Assertions.assertEquals(false, dependency.exported)
|
||||
}
|
||||
DependencyAssertions.assertModuleDependency(module, "lib-module-exported") { dependency ->
|
||||
Assertions.assertEquals(true, dependency.exported)
|
||||
}
|
||||
}
|
||||
|
||||
coordinates.modules.clear()
|
||||
coordinates.libraries.clear()
|
||||
|
||||
updateLibrarySubstitutions()
|
||||
|
||||
ModuleAssertions.assertModuleEntity(project, "app-module") { module ->
|
||||
Assertions.assertEquals(ENTITY_SOURCE, module.entitySource)
|
||||
DependencyAssertions.assertDependencies(module, "library", "library-exported")
|
||||
DependencyAssertions.assertLibraryDependency(module, "library") { dependency ->
|
||||
Assertions.assertEquals(false, dependency.exported)
|
||||
}
|
||||
DependencyAssertions.assertLibraryDependency(module, "library-exported") { dependency ->
|
||||
Assertions.assertEquals(true, dependency.exported)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.externalSystem.service.project
|
||||
|
||||
import com.intellij.openapi.application.writeAction
|
||||
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.libraries.Library
|
||||
import com.intellij.platform.backend.workspace.WorkspaceModel
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.extensionPointFixture
|
||||
import com.intellij.testFramework.junit5.fixture.projectFixture
|
||||
|
||||
@TestApplication
|
||||
abstract class ExternalProjectsWorkspaceTestCase {
|
||||
|
||||
val project by projectFixture()
|
||||
val coordinates by extensionPointFixture(ExternalSystemCoordinateContributor.EP_NAME, ::TestCoordinateContributor)
|
||||
|
||||
suspend fun updateLibrarySubstitutions() {
|
||||
writeAction {
|
||||
IdeModifiableModelsProviderImpl(project)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun WorkspaceModel.update(updater: (MutableEntityStorage) -> Unit) =
|
||||
update("Test description", updater)
|
||||
|
||||
class TestCoordinateContributor : ExternalSystemCoordinateContributor {
|
||||
|
||||
val modules = HashMap<String, String>()
|
||||
val libraries = HashMap<String, String>()
|
||||
|
||||
override fun findModuleCoordinate(module: Module): ProjectCoordinate? =
|
||||
modules[module.name]?.toProjectCoordinate()
|
||||
|
||||
override fun findLibraryCoordinate(library: Library): ProjectCoordinate? =
|
||||
libraries[library.name]?.toProjectCoordinate()
|
||||
|
||||
private fun String.toProjectCoordinate(): ProjectCoordinate {
|
||||
val (groupId, artifactId, version) = split(":")
|
||||
return ProjectId(groupId, artifactId, version)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val ENTITY_SOURCE = object : EntitySource {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.testFramework.assertion.moduleAssertion
|
||||
|
||||
import com.intellij.platform.testFramework.assertion.collectionAssertion.CollectionAssertions.assertEqualsUnordered
|
||||
import com.intellij.platform.workspace.jps.entities.*
|
||||
import org.junit.jupiter.api.Assertions
|
||||
|
||||
object DependencyAssertions {
|
||||
|
||||
val INHERITED_SDK = InheritedSdkDependency::class.simpleName!!
|
||||
val MODULE_SOURCE = ModuleSourceDependency::class.simpleName!!
|
||||
|
||||
fun assertDependencies(module: ModuleEntity, vararg expectedNames: String) {
|
||||
assertDependencies(module, expectedNames.asList())
|
||||
}
|
||||
|
||||
fun assertDependencies(module: ModuleEntity, expectedNames: List<String>) {
|
||||
val actualNames = module.dependencies.map { dependency ->
|
||||
when (dependency) {
|
||||
InheritedSdkDependency -> INHERITED_SDK
|
||||
ModuleSourceDependency -> MODULE_SOURCE
|
||||
is LibraryDependency -> dependency.library.name
|
||||
is ModuleDependency -> dependency.module.name
|
||||
is SdkDependency -> dependency.sdk.name
|
||||
}
|
||||
}
|
||||
assertEqualsUnordered(expectedNames, actualNames)
|
||||
}
|
||||
|
||||
fun assertLibraryDependency(module: ModuleEntity, name: String, assertion: (LibraryDependency) -> Unit) {
|
||||
module.dependencies.filterIsInstance<LibraryDependency>()
|
||||
.find { it.library.name == name }
|
||||
.let { dependency ->
|
||||
Assertions.assertNotNull(dependency, "Cannot find '$name' library dependency in '${module.name}' module")
|
||||
Assertions.assertEquals(name, dependency!!.library.name)
|
||||
assertion(dependency)
|
||||
}
|
||||
}
|
||||
|
||||
fun assertModuleDependency(module: ModuleEntity, name: String, assertion: (ModuleDependency) -> Unit) {
|
||||
module.dependencies.filterIsInstance<ModuleDependency>()
|
||||
.find { it.module.name == name }
|
||||
.let { dependency ->
|
||||
Assertions.assertNotNull(dependency, "Cannot find '$name' module dependency in '${module.name}' module")
|
||||
Assertions.assertEquals(name, dependency!!.module.name)
|
||||
assertion(dependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.intellij.platform.testFramework.assertion.collectionAssertion.Collect
|
||||
import com.intellij.platform.workspace.jps.entities.ModuleEntity
|
||||
import com.intellij.platform.workspace.storage.EntityStorage
|
||||
import com.intellij.platform.workspace.storage.entities
|
||||
import org.junit.jupiter.api.Assertions
|
||||
|
||||
object ModuleAssertions {
|
||||
|
||||
@@ -54,4 +55,17 @@ object ModuleAssertions {
|
||||
val actualNames = storage.entities<ModuleEntity>().map { it.name }.toList()
|
||||
assertContainsUnordered(expectedNames, actualNames)
|
||||
}
|
||||
|
||||
fun assertModuleEntity(project: Project, name: String, assertion: (ModuleEntity) -> Unit) =
|
||||
assertModuleEntity(project.workspaceModel.currentSnapshot, name, assertion)
|
||||
|
||||
fun assertModuleEntity(storage: EntityStorage, name: String, assertion: (ModuleEntity) -> Unit) {
|
||||
storage.entities<ModuleEntity>()
|
||||
.find { it.name == name }
|
||||
.let { module ->
|
||||
Assertions.assertNotNull(module, "Cannot find '$name' module")
|
||||
Assertions.assertEquals(name, module!!.name)
|
||||
assertion(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user