mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
[Workspace Model] [IJPL-867] Refactor deletion of iml files for removed imported modules
`iml` files of imported modules are not automatically removed due to different circumstances. Because of this issue, the external build system did this manually for many years. Unfortunately, this also affects the serialization of files and caused IJPL-867. To fix the IJPL-867 issue, the following steps are taken: - Get rid of manual `iml` removal by the external build system (`AbstractModuleDataService`). - Update jps serializer to mark the imported iml for deletion. - The `.iml` should not be deleted by the component store, but it's not due to IJPL-926. Ideally, we fix IJPL-926, but for now do the workaround: remove the ` iml ` file on the level of JPS serialization. These steps fix the IJPL-867 issue, but a further fix for IJPL-926 is still needed. # Notes - Test `ExternalSystemProjectSaveTest` is created as a separate class that uses JUnit 4, however, this test can be placed in `ExternalSystemProjectTest`. The problem is that `ExternalSystemProjectTest` uses JUnit 3 and it freezez on `project.stateStore.save()` because of an unclear reason. - `JpsProjectSerializersImpl.saveEntities` has a complicated logic when the `iml` file should be removed. As there is no goal to refactor/refresh this logic, I tried to minimize the number of changes around this place. - `JpsProjectSerializersImpl.shouldDeleteImportedFile` has a corner case for broken configuration of the iml. This case is covered in `ExternalSystemStorageTest.multiple modules with the same name` test. - `ModuleImlFileEntitiesSerializer.manuallyRemoveImlFile` has a check for the correct casing. This test case is covered in `ExternalSystemStorageTest.multiple modules with the same name but different case`. - After the `iml` deletion, the VFS should technically be refreshed, but we don't do it because the module with JPS serialization does not have a dependency to the module with VFS. Because of that, the `iml` does not disappear immediately from the project tree if it's there. I consider that this problem is minor. GitOrigin-RevId: e6228e06017f6aef26fb78321a27a3ad05e1c22c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c95e1a8d2b
commit
7746460f32
@@ -671,18 +671,22 @@ class JpsProjectSerializersImpl(directorySerializersFactories: List<JpsDirectory
|
||||
val fileUrl = getActualFileUrl(source)
|
||||
if (fileUrl != null) {
|
||||
val affectedImportedSourceStoredExternally = when {
|
||||
source is JpsImportedEntitySource && source.storedExternally -> sourcesStoredInternally[source.internalFile]
|
||||
source is JpsImportedEntitySource && !source.storedExternally -> sourcesStoredExternally[source.internalFile]
|
||||
source is JpsFileEntitySource -> sourcesStoredExternally[source]
|
||||
else -> null
|
||||
}
|
||||
// When user removes module from project we don't delete corresponding *.iml file located under project directory by default
|
||||
// (because it may be included in other projects). However we do remove the module file if module actually wasn't removed, just
|
||||
// its storage has been changed, e.g. if module was marked as imported from external system, or the place where module imported
|
||||
// from external system was changed, or part of a module configuration is imported from external system and data stored in *.iml
|
||||
// file was removed.
|
||||
val deleteObsoleteFile = source in internalSourceConvertedToImported || (affectedImportedSourceStoredExternally != null &&
|
||||
affectedImportedSourceStoredExternally !in obsoleteSources)
|
||||
// When user removes module from the project, we don't delete corresponding *.iml file located under project directory by default
|
||||
// because it may be included in other projects.
|
||||
//
|
||||
// However, we do remove the module file if:
|
||||
// - Module is imported from the external build system (like maven).
|
||||
// - Module actually wasn't removed, just its storage has been changed, e.g: if module was marked as imported from the external system
|
||||
// (aka mavenize module).
|
||||
// - Imported module had user-configured information (like custom content roots). This additional information is stored in the local
|
||||
// `.iml` file, and the `.iml` should be removed in case all custom elements are removed.
|
||||
// - TO DO: Fill new cases if found!
|
||||
val deleteObsoleteFile = shouldDeleteImportedFile(source, fileUrl) ||
|
||||
source in internalSourceConvertedToImported ||
|
||||
(affectedImportedSourceStoredExternally != null && affectedImportedSourceStoredExternally !in obsoleteSources)
|
||||
processObsoleteSource(fileUrl, deleteObsoleteFile, writer, affectedEntityTypeSerializers, affectedModuleListSerializers, storage)
|
||||
val actualSource = if (source is JpsImportedEntitySource && !source.storedExternally) source.internalFile else source
|
||||
if (actualSource is JpsProjectFileEntitySource.FileInDirectory) {
|
||||
@@ -804,6 +808,17 @@ class JpsProjectSerializersImpl(directorySerializersFactories: List<JpsDirectory
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldDeleteImportedFile(source: EntitySource, fileUrl: String): Boolean {
|
||||
// Always remove files that were generated during importing from external build systems like gradle, maven, sbt.
|
||||
// Example: If module was imported from gradle, and then it was removed in gradle, we don't need to keep the `.iml` file for this module
|
||||
return source is JpsImportedEntitySource
|
||||
// Except a corner case: We load `iml` files with broken structure that has duplicated modules.
|
||||
// This may happen if we have two `modules.xml` files (one in `.idea` and the second in `external_build_system`) and they
|
||||
// both refer to the same `module.iml`. The duplicated module entity will be automatically removed, but the `module.iml` file
|
||||
// should not be removed as it contains information about the module.
|
||||
&& fileSerializersByUrl.getValues(fileUrl).size == 1
|
||||
}
|
||||
|
||||
override fun changeEntitySourcesToDirectoryBasedFormat(builder: MutableEntityStorage) {
|
||||
for (factory in directorySerializerFactoriesByUrl.values) {
|
||||
factory.changeEntitySourcesToDirectoryBasedFormat(builder, configLocation)
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import java.io.StringReader
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.exists
|
||||
|
||||
internal const val DEPRECATED_MODULE_MANAGER_COMPONENT_NAME = "DeprecatedModuleOptionManager"
|
||||
@@ -1061,6 +1063,20 @@ internal open class ModuleListSerializerImpl(override val fileUrl: String,
|
||||
writer.saveComponent(fileUrl, DEPRECATED_MODULE_MANAGER_COMPONENT_NAME, null)
|
||||
writer.saveComponent(fileUrl, TEST_MODULE_PROPERTIES_COMPONENT_NAME, null)
|
||||
writer.saveComponent(fileUrl, ADDITIONAL_MODULE_ELEMENTS_COMPONENT_NAME, null)
|
||||
|
||||
manuallyRemoveImlFile(fileUrl)
|
||||
}
|
||||
|
||||
// We manually remove the `.iml` file as it's not removed by component store due to IJPL-926
|
||||
// Probably there is no need to set `null` to the components, but let's do it just in case.
|
||||
// If IJPL-926 is fixed, this manual removal should go away and only `saveComponent(..., null)` should remain.
|
||||
private fun manuallyRemoveImlFile(fileUrl: String) {
|
||||
val path = Path(JpsPathUtil.urlToPath(fileUrl))
|
||||
// Check that `iml` with a correct case is removed on case-insensitive systems
|
||||
// `path.exists()` check should be done as `toRealPath` will break if the file doesn't exist.
|
||||
if (path.exists() && path.toString() == path.toRealPath().toString()) {
|
||||
path.deleteIfExists()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getModuleFileUrl(source: JpsProjectFileEntitySource.FileInDirectory,
|
||||
|
||||
Reference in New Issue
Block a user