mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[workspace model] eclipse: support changing storage format for existing modules (IDEA-253364)
When ClasspathStorageProvider is changed, it also updates entity source accordingly. A test is added and tests are updated to manually force save in round-trip tests only. GitOrigin-RevId: a10b0ac0f548a8b6e538b84ef1d91766e7e05330
This commit is contained in:
committed by
intellij-monorepo-bot
parent
fcc760a307
commit
d23b168d15
+2
-19
@@ -14,6 +14,7 @@ import com.intellij.workspaceModel.storage.WorkspaceEntityStorageDiffBuilder
|
||||
import com.intellij.workspaceModel.ide.JpsFileEntitySource
|
||||
import com.intellij.workspaceModel.ide.JpsImportedEntitySource
|
||||
import com.intellij.workspaceModel.ide.WorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleManagerComponentBridge
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleManagerComponentBridge.Companion.findModuleEntity
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.ModuleBridge
|
||||
import com.intellij.workspaceModel.storage.bridgeEntities.*
|
||||
@@ -61,25 +62,7 @@ class ExternalSystemModulePropertyManagerBridge(private val module: Module) : Ex
|
||||
val internalFile = entitySource as? JpsFileEntitySource ?: (entitySource as JpsImportedEntitySource).internalFile
|
||||
JpsImportedEntitySource(internalFile, externalSystemId, module.project.isExternalStorageEnabled)
|
||||
}
|
||||
|
||||
fun changeSources(diffBuilder: WorkspaceEntityStorageDiffBuilder, storage: WorkspaceEntityStorage) {
|
||||
val entitiesMap = storage.entitiesBySource { it == entitySource }
|
||||
entitiesMap.values.asSequence().flatMap { it.values.asSequence().flatten() }.forEach {
|
||||
if (it !is FacetEntity) {
|
||||
diffBuilder.changeSource(it, newSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val diff = module.diff
|
||||
if (diff != null) {
|
||||
changeSources(diff, storage)
|
||||
}
|
||||
else {
|
||||
WorkspaceModel.getInstance(module.project).updateProjectModel { builder ->
|
||||
changeSources(builder, builder)
|
||||
}
|
||||
}
|
||||
ModuleManagerComponentBridge.changeModuleEntitySource(module, newSource)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -248,9 +248,12 @@ public final class ClasspathStorage extends StateStorageBase<Boolean> {
|
||||
provider.detach(module);
|
||||
}
|
||||
|
||||
provider = getProvider(storageId);
|
||||
module.setOption(JpsProjectLoader.CLASSPATH_ATTRIBUTE, provider == null ? null : storageId);
|
||||
module.setOption(JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE, provider == null ? null : provider.getContentRoot(model));
|
||||
ClasspathStorageProvider newProvider = getProvider(storageId);
|
||||
module.setOption(JpsProjectLoader.CLASSPATH_ATTRIBUTE, newProvider == null ? null : storageId);
|
||||
module.setOption(JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE, newProvider == null ? null : newProvider.getContentRoot(model));
|
||||
if (newProvider != null) {
|
||||
newProvider.attach(module);
|
||||
}
|
||||
}
|
||||
|
||||
public static void modulePathChanged(@NotNull Module module) {
|
||||
|
||||
+3
@@ -34,6 +34,9 @@ public interface ClasspathStorageProvider {
|
||||
|
||||
void detach(@NotNull Module module);
|
||||
|
||||
default void attach(@NotNull Module module) {
|
||||
}
|
||||
|
||||
default void moduleRenamed(@NotNull Module module, @NotNull String oldName, @NotNull String newName) {
|
||||
}
|
||||
|
||||
|
||||
+26
@@ -600,6 +600,8 @@ class ModuleManagerComponentBridge(private val project: Project) : ModuleManager
|
||||
get() = getExternalMapping(INDEX_ID)
|
||||
internal val WorkspaceEntityStorageDiffBuilder.mutableModuleMap: MutableExternalEntityMapping<ModuleBridge>
|
||||
get() = getMutableExternalMapping(INDEX_ID)
|
||||
|
||||
@JvmStatic
|
||||
fun WorkspaceEntityStorage.findModuleEntity(module: ModuleBridge) =
|
||||
moduleMap.getEntities(module).firstOrNull() as ModuleEntity?
|
||||
|
||||
@@ -613,6 +615,30 @@ class ModuleManagerComponentBridge(private val project: Project) : ModuleManager
|
||||
DFSTBuilder(buildModuleGraph(storage, true)).comparator()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun changeModuleEntitySource(module: ModuleBridge, newSource: EntitySource) {
|
||||
val storage = module.entityStorage.current
|
||||
val oldEntitySource = storage.findModuleEntity(module)?.entitySource ?: return
|
||||
fun changeSources(diffBuilder: WorkspaceEntityStorageDiffBuilder, storage: WorkspaceEntityStorage) {
|
||||
val entitiesMap = storage.entitiesBySource { it == oldEntitySource }
|
||||
entitiesMap.values.asSequence().flatMap { it.values.asSequence().flatten() }.forEach {
|
||||
if (it !is FacetEntity) {
|
||||
diffBuilder.changeSource(it, newSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val diff = module.diff
|
||||
if (diff != null) {
|
||||
changeSources(diff, storage)
|
||||
}
|
||||
else {
|
||||
WorkspaceModel.getInstance(module.project).updateProjectModel { builder ->
|
||||
changeSources(builder, builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildModuleGraph(storage: WorkspaceEntityStorage, includeTests: Boolean): Graph<Module> {
|
||||
return GraphGenerator.generate(CachingSemiGraph.cache(object : InboundSemiGraph<Module> {
|
||||
override fun getNodes(): Collection<Module> {
|
||||
|
||||
+1
@@ -363,6 +363,7 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod
|
||||
if (serializer != null) {
|
||||
val customDir = moduleOptions[JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE]
|
||||
serializer.saveRoots(module, entities, writer, customDir, fileUrl, storage, virtualFileManager)
|
||||
writer.saveComponent(fileUrl.url, MODULE_ROOT_MANAGER_COMPONENT_NAME, null)
|
||||
}
|
||||
else {
|
||||
LOG.warn("Classpath storage provider $customSerializerId not found")
|
||||
|
||||
+33
@@ -28,6 +28,14 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.workspaceModel.ide.JpsFileEntitySource;
|
||||
import com.intellij.workspaceModel.ide.VirtualFileUrlManagerUtil;
|
||||
import com.intellij.workspaceModel.ide.WorkspaceModel;
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleManagerComponentBridge;
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.ModuleBridge;
|
||||
import com.intellij.workspaceModel.storage.EntitySource;
|
||||
import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity;
|
||||
import com.intellij.workspaceModel.storage.url.VirtualFileUrlManager;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -39,6 +47,7 @@ import org.jetbrains.idea.eclipse.conversion.EclipseClasspathWriter;
|
||||
import org.jetbrains.jps.eclipse.model.JpsEclipseClasspathSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Kaznacheev
|
||||
@@ -90,6 +99,30 @@ public class EclipseClasspathStorageProvider implements ClasspathStorageProvider
|
||||
@Override
|
||||
public void detach(@NotNull Module module) {
|
||||
EclipseModuleManagerImpl.getInstance(module).setDocumentSet(null);
|
||||
updateEntitySource(module, source -> ((EclipseProjectFile)source).getInternalSource());
|
||||
}
|
||||
|
||||
private static void updateEntitySource(Module module, Function<? super EntitySource, ? extends EntitySource> updateSource) {
|
||||
if (WorkspaceModel.isEnabled()) {
|
||||
WriteAction.run(() -> {
|
||||
ModuleBridge moduleBridge = (ModuleBridge)module;
|
||||
ModuleEntity moduleEntity = ModuleManagerComponentBridge.findModuleEntity(moduleBridge.getEntityStorage().getCurrent(), moduleBridge);
|
||||
if (moduleEntity != null) {
|
||||
EntitySource entitySource = moduleEntity.getEntitySource();
|
||||
ModuleManagerComponentBridge.changeModuleEntitySource(moduleBridge, updateSource.apply(entitySource));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach(@NotNull Module module) {
|
||||
updateEntitySource(module, source -> {
|
||||
VirtualFileUrlManager virtualFileUrlManager = VirtualFileUrlManagerUtil.getInstance(VirtualFileUrlManager.Companion, module.getProject());
|
||||
String contentRoot = getContentRoot(ModuleRootManager.getInstance(module));
|
||||
String classpathFileUrl = VfsUtilCore.pathToUrl(contentRoot) + "/" + EclipseXml.CLASSPATH_FILE;
|
||||
return new EclipseProjectFile(virtualFileUrlManager.fromUrl(classpathFileUrl), (JpsFileEntitySource)source);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<output url="file://$MODULE_DIR$/bin" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>test</name>
|
||||
<comment/>
|
||||
<projects/>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments/>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" />
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.idea.eclipse
|
||||
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.impl.storage.ClassPathStorageUtil
|
||||
import com.intellij.openapi.roots.impl.storage.ClasspathStorage
|
||||
import com.intellij.testFramework.ApplicationRule
|
||||
import com.intellij.testFramework.rules.ProjectModelRule
|
||||
import com.intellij.testFramework.rules.TempDirectory
|
||||
import com.intellij.util.io.copy
|
||||
import com.intellij.util.io.div
|
||||
import org.jetbrains.jps.eclipse.model.JpsEclipseClasspathSerializer
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestName
|
||||
|
||||
class ChangeStorageTypeTest {
|
||||
@JvmField
|
||||
@Rule
|
||||
val tempDirectory = TempDirectory()
|
||||
|
||||
@JvmField
|
||||
@Rule
|
||||
val testName = TestName()
|
||||
|
||||
@Test
|
||||
fun `switch to default storage`() {
|
||||
val commonRoot = eclipseTestDataRoot / "common" / "testModuleWithClasspathStorage"
|
||||
val classpathRoot = eclipseTestDataRoot / "storageType" / "eclipse"
|
||||
loadEditSaveAndCheck(listOf(commonRoot, classpathRoot), tempDirectory, false, listOf("test" to "test/test"),
|
||||
{ switchStorage(it, ClassPathStorageUtil.DEFAULT_STORAGE) },
|
||||
{ (eclipseTestDataRoot / "storageType" / "default" / "test.iml").copy(it / "test" / "test.iml")})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `switch to classpath storage`() {
|
||||
assumeTrue(ProjectModelRule.isWorkspaceModelEnabled)
|
||||
val commonRoot = eclipseTestDataRoot / "common" / "testModuleWithClasspathStorage"
|
||||
val defaultRoot = eclipseTestDataRoot / "storageType" / "default"
|
||||
loadEditSaveAndCheck(listOf(commonRoot, defaultRoot), tempDirectory, false, listOf("test" to "test/test"),
|
||||
{ switchStorage(it, JpsEclipseClasspathSerializer.CLASSPATH_STORAGE_ID) },
|
||||
{
|
||||
(eclipseTestDataRoot / "common" / "testModuleWithClasspathStorage" / "test.iml").copy(it / "test" / "test.iml")
|
||||
(eclipseTestDataRoot / "storageType" / "eclipse" / "test" / ".classpath").copy(it / "test" / ".classpath")
|
||||
})
|
||||
}
|
||||
|
||||
private fun switchStorage(project: Project, storageId: String) {
|
||||
val module = ModuleManager.getInstance(project).modules.single()
|
||||
ClasspathStorage.setStorageType(ModuleRootManager.getInstance(module), storageId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
@ClassRule
|
||||
val appRule = ApplicationRule()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import com.intellij.openapi.application.runWriteActionAndWait
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.impl.storage.ClassPathStorageUtil
|
||||
import com.intellij.openapi.roots.impl.storage.ClasspathStorage
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
@@ -28,7 +31,7 @@ internal fun checkLoadSaveRoundTrip(testDataDirs: List<Path>,
|
||||
tempDirectory: TempDirectory,
|
||||
setupPathVariables: Boolean = false,
|
||||
imlFilePaths: List<Pair<String, String>>) {
|
||||
loadEditSaveAndCheck(testDataDirs, tempDirectory, setupPathVariables, imlFilePaths, {}, {})
|
||||
loadEditSaveAndCheck(testDataDirs, tempDirectory, setupPathVariables, imlFilePaths, ::forceSave, {})
|
||||
}
|
||||
|
||||
internal fun checkEmlFileGeneration(testDataDirs: List<Path>,
|
||||
@@ -48,8 +51,7 @@ internal fun checkConvertToStandardStorage(testDataDirs: List<Path>,
|
||||
fun edit(project: Project) {
|
||||
val moduleName = imlFilePaths.first().second.substringAfterLast('/')
|
||||
val module = ModuleManager.getInstance(project).findModuleByName(moduleName) ?: error("Cannot find module '$moduleName'")
|
||||
module.clearOption(JpsProjectLoader.CLASSPATH_ATTRIBUTE)
|
||||
module.clearOption(JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE)
|
||||
ClasspathStorage.setStorageType(ModuleRootManager.getInstance(module), ClassPathStorageUtil.DEFAULT_STORAGE)
|
||||
}
|
||||
|
||||
fun updateExpectedDir(projectDir: Path) {
|
||||
@@ -105,13 +107,6 @@ internal fun loadEditSaveAndCheck(testDataDirs: List<Path>,
|
||||
loadProject(projectDir) { project ->
|
||||
runWriteActionAndWait {
|
||||
edit(project)
|
||||
ModuleManager.getInstance(project).modules.forEach {
|
||||
it.moduleFile!!.delete(this)
|
||||
it.stateStore.clearCaches()
|
||||
}
|
||||
if (WorkspaceModel.isEnabled) {
|
||||
JpsProjectModelSynchronizer.getInstance(project)!!.markAllEntitiesAsDirty()
|
||||
}
|
||||
}
|
||||
project.stateStore.save(true)
|
||||
projectDir.assertMatches(directoryContentOf(originalProjectDir), filePathFilter = { path ->
|
||||
@@ -128,4 +123,14 @@ internal fun loadEditSaveAndCheck(testDataDirs: List<Path>,
|
||||
PathMacros.getInstance().setMacro(it, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun forceSave(project: Project) {
|
||||
ModuleManager.getInstance(project).modules.forEach {
|
||||
it.moduleFile!!.delete(project)
|
||||
it.stateStore.clearCaches()
|
||||
}
|
||||
if (WorkspaceModel.isEnabled) {
|
||||
JpsProjectModelSynchronizer.getInstance(project)!!.markAllEntitiesAsDirty()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user