diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsComponentLoader.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsComponentLoader.java index 4e4647dc7d47..e481f22ea95d 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsComponentLoader.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsComponentLoader.java @@ -25,11 +25,15 @@ public class JpsComponentLoader { protected final @Nullable Path myExternalConfigurationDirectory; private final JpsMacroExpander myMacroExpander; - public JpsComponentLoader(JpsMacroExpander macroExpander, @Nullable Path externalConfigurationDirectory) { + public JpsComponentLoader(@NotNull JpsMacroExpander macroExpander, @Nullable Path externalConfigurationDirectory) { myMacroExpander = macroExpander; myExternalConfigurationDirectory = externalConfigurationDirectory; } + public @NotNull JpsMacroExpander getMacroExpander() { + return myMacroExpander; + } + /** * Returns null if file doesn't exist */ @@ -37,10 +41,10 @@ public class JpsComponentLoader { return loadRootElement(file, myMacroExpander); } - protected void loadComponents(@NotNull Path dir, - @NotNull Path defaultConfigFile, - JpsElementExtensionSerializerBase serializer, - final E element) { + public void loadComponents(@NotNull Path dir, + @NotNull Path defaultConfigFile, + JpsElementExtensionSerializerBase serializer, + E element) { String fileName = serializer.getConfigFileName(); Path configFile = fileName == null ? defaultConfigFile : dir.resolve(fileName); Runnable timingLog = TimingLog.startActivity("loading: " + configFile.getFileName() + ":" + serializer.getComponentName()); diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectConfigurationLoading.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectConfigurationLoading.java index 2c14c6ed37c0..f4c2a57f5993 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectConfigurationLoading.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectConfigurationLoading.java @@ -1,7 +1,9 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.jps.model.serialization; +import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; import org.jetbrains.annotations.ApiStatus; @@ -14,13 +16,12 @@ import org.jetbrains.jps.model.serialization.impl.JpsProjectSerializationDataExt import org.jetbrains.jps.model.serialization.runConfigurations.JpsRunConfigurationSerializer; import org.jetbrains.jps.util.JpsPathUtil; +import java.io.File; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Contains helper methods which are used to load parts of the project configuration which aren't stored in the workspace model. @@ -136,9 +137,29 @@ public final class JpsProjectConfigurationLoading { String sdkName = rootManagerElement.getAttributeValue("project-jdk-name"); String sdkTypeId = rootManagerElement.getAttributeValue("project-jdk-type"); if (sdkName != null) { - sdkTypeIdAndName = new Pair<>(sdkTypeId, sdkName); + sdkTypeIdAndName = new Pair<>(Objects.requireNonNullElse(sdkTypeId, "JavaSDK"), sdkName); } } return sdkTypeIdAndName; } + + public static JpsMacroExpander createModuleMacroExpander(final Map pathVariables, @NotNull Path moduleFile) { + JpsMacroExpander expander = new JpsMacroExpander(pathVariables); + String moduleDirPath = PathMacroUtil.getModuleDir(moduleFile.toAbsolutePath().toString()); + if (moduleDirPath != null) { + expander.addFileHierarchyReplacements(PathMacroUtil.MODULE_DIR_MACRO_NAME, new File(FileUtilRt.toSystemDependentName(moduleDirPath))); + } + return expander; + } + + public static @NotNull Set readNamesOfUnloadedModules(@NotNull Path workspaceFile, @NotNull JpsComponentLoader componentLoader) { + Set unloadedModules = new HashSet<>(); + if (workspaceFile.toFile().exists()) { + Element unloadedModulesList = JDomSerializationUtil.findComponent(componentLoader.loadRootElement(workspaceFile), "UnloadedModulesList"); + for (Element element : JDOMUtil.getChildren(unloadedModulesList, "module")) { + unloadedModules.add(element.getAttributeValue("name")); + } + } + return unloadedModules; + } } diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java index c1d5b4fb4e4f..0a5573a58cff 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java @@ -29,7 +29,6 @@ import org.jetbrains.jps.model.serialization.module.JpsModulePropertiesSerialize import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer; import org.jetbrains.jps.service.SharedThreadPool; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -248,13 +247,8 @@ public final class JpsProjectLoader { return; } - Set unloadedModules = new HashSet<>(); - if (!myLoadUnloadedModules && workspaceFile.toFile().exists()) { - Element unloadedModulesList = JDomSerializationUtil.findComponent(myComponentLoader.loadRootElement(workspaceFile), "UnloadedModulesList"); - for (Element element : JDOMUtil.getChildren(unloadedModulesList, "module")) { - unloadedModules.add(element.getAttributeValue("name")); - } - } + Set unloadedModules = !myLoadUnloadedModules ? JpsProjectConfigurationLoading.readNamesOfUnloadedModules(workspaceFile, myComponentLoader) + : Collections.emptySet(); final Set foundFiles = CollectionFactory.createSmallMemoryFootprintSet(); final List moduleFiles = new ArrayList<>(); @@ -297,7 +291,7 @@ public final class JpsProjectLoader { for (Path file : moduleFiles) { futureModuleFilesContents.add(CompletableFuture.supplyAsync(() -> { - JpsMacroExpander expander = createModuleMacroExpander(pathVariables, file); + JpsMacroExpander expander = JpsProjectConfigurationLoading.createModuleMacroExpander(pathVariables, file); Element data = JpsComponentLoader.loadRootElement(file, expander); if (externalModuleDir != null) { @@ -409,7 +403,7 @@ public final class JpsProjectLoader { JpsModuleClasspathSerializer classpathSerializer = extension.getClasspathSerializer(); if (classpathSerializer != null && classpathSerializer.getClasspathId().equals(classpath)) { String classpathDir = moduleRoot.getAttributeValue(CLASSPATH_DIR_ATTRIBUTE); - final JpsMacroExpander expander = createModuleMacroExpander(pathVariables, file); + final JpsMacroExpander expander = JpsProjectConfigurationLoading.createModuleMacroExpander(pathVariables, file); classpathSerializer.loadClasspath(module, classpathDir, baseModulePath, expander, paths, projectSdkType); } } @@ -434,15 +428,6 @@ public final class JpsProjectLoader { return FileUtilRt.getNameWithoutExtension(file.getFileName().toString()); } - static JpsMacroExpander createModuleMacroExpander(final Map pathVariables, @NotNull Path moduleFile) { - JpsMacroExpander expander = new JpsMacroExpander(pathVariables); - String moduleDirPath = PathMacroUtil.getModuleDir(moduleFile.toAbsolutePath().toString()); - if (moduleDirPath != null) { - expander.addFileHierarchyReplacements(PathMacroUtil.MODULE_DIR_MACRO_NAME, new File(FileUtilRt.toSystemDependentName(moduleDirPath))); - } - return expander; - } - private static

JpsModule createModule(String name, Element moduleRoot, JpsModulePropertiesSerializer

loader) { String componentName = loader.getComponentName(); Element component = componentName != null ? JDomSerializationUtil.findComponent(moduleRoot, componentName) : null; diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java index 6c910abf2ec1..7e624c463518 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java @@ -27,7 +27,7 @@ public final class JpsSerializationManagerImpl extends JpsSerializationManager { boolean loadUnloadedModules) throws IOException { JpsSerializationViaWorkspaceModel serializationViaWorkspaceModel = JpsSerializationViaWorkspaceModel.getInstance(); if (serializationViaWorkspaceModel != null) { - return serializationViaWorkspaceModel.loadModel(projectPath, optionsPath, loadUnloadedModules); + return serializationViaWorkspaceModel.loadModel(projectPath, externalConfigurationDirectory, optionsPath, loadUnloadedModules); } JpsModel model = JpsElementFactory.getInstance().createModel(); @@ -61,7 +61,7 @@ public final class JpsSerializationManagerImpl extends JpsSerializationManager { boolean loadUnloadedModules) throws IOException { JpsSerializationViaWorkspaceModel serializationViaWorkspaceModel = JpsSerializationViaWorkspaceModel.getInstance(); if (serializationViaWorkspaceModel != null) { - return serializationViaWorkspaceModel.loadProject(projectPath, pathVariables, loadUnloadedModules); + return serializationViaWorkspaceModel.loadProject(projectPath, externalConfigurationDirectory, pathVariables, loadUnloadedModules); } JpsModel model = JpsElementFactory.getInstance().createModel(); diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationViaWorkspaceModel.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationViaWorkspaceModel.java index 9f13791ee379..89eeba0e5bbe 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationViaWorkspaceModel.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationViaWorkspaceModel.java @@ -26,8 +26,10 @@ public interface JpsSerializationViaWorkspaceModel { return null; } - @NotNull JpsModel loadModel(@NotNull Path projectPath, @Nullable Path optionsPath, boolean loadUnloadedModules) throws IOException; + @NotNull JpsModel loadModel(@NotNull Path projectPath, + @Nullable Path externalConfigurationDirectory, + @Nullable Path optionsPath, boolean loadUnloadedModules) throws IOException; - @NotNull JpsProject loadProject(@NotNull Path projectPath, @NotNull Map pathVariables, + @NotNull JpsProject loadProject(@NotNull Path projectPath, @Nullable Path externalConfigurationDirectory, @NotNull Map pathVariables, boolean loadUnloadedModules) throws IOException; } diff --git a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsModelBridge.kt b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsModelBridge.kt index b5fcd700f173..024360341f9d 100644 --- a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsModelBridge.kt +++ b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsModelBridge.kt @@ -15,7 +15,7 @@ internal class JpsModelBridge( private val project: JpsProjectBridge = JpsProjectBridge(this, projectStorage, projectAdditionalData); private val global: JpsGlobalBridge = JpsGlobalBridge(this, globalStorage); - override fun getProject(): JpsProject = project + override fun getProject(): JpsProjectBridge = project - override fun getGlobal(): JpsGlobal = global + override fun getGlobal(): JpsGlobalBridge = global } \ No newline at end of file diff --git a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsProjectBridge.kt b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsProjectBridge.kt index 89a2cc472213..189b92577de1 100644 --- a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsProjectBridge.kt +++ b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/JpsProjectBridge.kt @@ -33,7 +33,7 @@ internal class JpsProjectBridge(modelBridge: JpsModelBridge, } private val sdkReferencesTable = JpsSdkReferencesTableBridge(additionalData.projectSdkId, this) - override fun getModules(): List = modules + override fun getModules(): List = modules override fun

getModules(type: JpsModuleType

): Iterable> { return modules.asSequence() diff --git a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/DirectJpsFileContentReader.kt b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/GlobalDirectJpsFileContentReader.kt similarity index 83% rename from platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/DirectJpsFileContentReader.kt rename to platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/GlobalDirectJpsFileContentReader.kt index 29db61dc50eb..91747ef48c18 100644 --- a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/DirectJpsFileContentReader.kt +++ b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/GlobalDirectJpsFileContentReader.kt @@ -10,7 +10,10 @@ import org.jetbrains.jps.model.serialization.JpsMacroExpander import org.jetbrains.jps.util.JpsPathUtil import kotlin.io.path.Path -internal class DirectJpsFileContentReader(private val macroExpander: JpsMacroExpander) : JpsFileContentReader { +/** + * Loads global settings used in the workspace model directly from XML configuration files. + */ +internal class GlobalDirectJpsFileContentReader(private val macroExpander: JpsMacroExpander) : JpsFileContentReader { private val componentLoader = JpsComponentLoader(macroExpander, null) override fun loadComponent(fileUrl: String, componentName: String, customModuleFilePath: String?): Element? { diff --git a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/JpsSerializationViaWorkspaceModelImpl.kt b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/JpsSerializationViaWorkspaceModelImpl.kt index 743cf074fd34..53fa89589918 100644 --- a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/JpsSerializationViaWorkspaceModelImpl.kt +++ b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/JpsSerializationViaWorkspaceModelImpl.kt @@ -2,70 +2,114 @@ package com.intellij.platform.workspace.jps.bridge.impl.serialization import com.intellij.openapi.util.io.FileUtil +import com.intellij.platform.workspace.jps.JpsProjectConfigLocation +import com.intellij.platform.workspace.jps.JpsProjectFileEntitySource import com.intellij.platform.workspace.jps.UnloadedModulesNameHolder import com.intellij.platform.workspace.jps.bridge.impl.JpsModelBridge import com.intellij.platform.workspace.jps.bridge.impl.JpsProjectAdditionalData +import com.intellij.platform.workspace.jps.bridge.impl.JpsProjectBridge import com.intellij.platform.workspace.jps.bridge.impl.library.sdk.JpsSdkLibraryBridge -import com.intellij.platform.workspace.jps.serialization.impl.ErrorReporter -import com.intellij.platform.workspace.jps.serialization.impl.JpsGlobalEntitiesSerializers -import com.intellij.platform.workspace.jps.serialization.impl.JpsProjectEntitiesLoader +import com.intellij.platform.workspace.jps.bridge.impl.module.JpsModuleBridge +import com.intellij.platform.workspace.jps.entities.SdkId +import com.intellij.platform.workspace.jps.entities.customImlData +import com.intellij.platform.workspace.jps.entities.exModuleOptions +import com.intellij.platform.workspace.jps.serialization.impl.* import com.intellij.platform.workspace.jps.serialization.impl.toConfigLocation import com.intellij.platform.workspace.storage.EntityStorage import com.intellij.platform.workspace.storage.MutableEntityStorage import com.intellij.platform.workspace.storage.impl.url.VirtualFileUrlManagerImpl import com.intellij.platform.workspace.storage.url.VirtualFileUrl import kotlinx.coroutines.runBlocking +import org.jdom.Element import org.jetbrains.jps.model.JpsModel import org.jetbrains.jps.model.JpsProject -import org.jetbrains.jps.model.serialization.JpsGlobalLoader -import org.jetbrains.jps.model.serialization.JpsGlobalSettingsLoading -import org.jetbrains.jps.model.serialization.JpsMacroExpander +import org.jetbrains.jps.model.serialization.* +import org.jetbrains.jps.model.serialization.JpsProjectConfigurationLoading.* +import org.jetbrains.jps.model.serialization.impl.JpsModuleSerializationDataExtensionImpl import org.jetbrains.jps.model.serialization.impl.JpsSerializationViaWorkspaceModel import java.nio.file.Path -import kotlin.io.path.Path internal class JpsSerializationViaWorkspaceModelImpl : JpsSerializationViaWorkspaceModel { - override fun loadModel(projectPath: Path, optionsPath: Path?, loadUnloadedModules: Boolean): JpsModel { + override fun loadModel(projectPath: Path, externalConfigurationDirectory: Path?, optionsPath: Path?, loadUnloadedModules: Boolean): JpsModel { val virtualFileUrlManager = VirtualFileUrlManagerImpl() - val errorReporter = object : ErrorReporter { - override fun reportError(message: String, file: VirtualFileUrl) { - error(message) - } - } - + val errorReporter = createErrorReporter() val globalStorage = MutableEntityStorage.create() - val globalMacroExpander = if (optionsPath != null) { + val (globalMacroExpander, pathVariables) = if (optionsPath != null) { loadGlobalStorage(optionsPath, virtualFileUrlManager, errorReporter, globalStorage) } else { - null + null to emptyMap() } - - val (projectStorage, additionalData) = loadProjectStorage(projectPath, virtualFileUrlManager, errorReporter) - val model = JpsModelBridge(projectStorage, globalStorage, additionalData) - + + val model = loadProject(projectPath, externalConfigurationDirectory, virtualFileUrlManager, pathVariables, errorReporter, globalStorage, + loadUnloadedModules) + if (optionsPath != null) { val globalLoader = JpsGlobalLoader(globalMacroExpander!!, model.global, arrayOf(JpsGlobalLoader.FILE_TYPES_SERIALIZER)) globalLoader.load(optionsPath) } + return model } - private fun loadProjectStorage(projectPath: Path, virtualFileUrlManager: VirtualFileUrlManagerImpl, errorReporter: ErrorReporter): Pair { + private fun loadProject( + projectPath: Path, + externalConfigurationDirectory: Path?, + virtualFileUrlManager: VirtualFileUrlManagerImpl, + pathVariables: Map, + errorReporter: ErrorReporter, + globalStorage: EntityStorage, + loadUnloadedModules: Boolean, + ): JpsModelBridge { val configLocation = toConfigLocation(projectPath, virtualFileUrlManager) - val externalStoragePath = Path("")//todo - val context = SerializationContextImpl(virtualFileUrlManager) + val contentReader = ProjectDirectJpsFileContentReader(configLocation.baseDirectoryUrl.toPath(), externalConfigurationDirectory, pathVariables) + val (projectStorage, additionalData) = loadProjectStorage(virtualFileUrlManager, errorReporter, configLocation, contentReader, loadUnloadedModules) + val model = JpsModelBridge(projectStorage, globalStorage, additionalData) + + loadOtherProjectComponents(model.project, contentReader.projectComponentLoader, configLocation, externalConfigurationDirectory) + loadOtherModuleComponents(model.project) + return model + } + + private fun createErrorReporter() = object : ErrorReporter { + override fun reportError(message: String, file: VirtualFileUrl) { + throw CannotLoadJpsModelException(file.toPath().toFile(), message, null) + } + } + + private fun loadProjectStorage( + virtualFileUrlManager: VirtualFileUrlManagerImpl, errorReporter: ErrorReporter, + configLocation: JpsProjectConfigLocation, fileContentReader: ProjectDirectJpsFileContentReader, loadUnloadedModules: Boolean, + ): Pair { + /* JpsProjectEntitiesLoader requires non-null value of externalStoragePath even if the external storage is not used, so use some + artificial path if it isn't specified; externalStoragePath will be eliminated when IJPL-10518 is fixed + */ + val externalStoragePath = fileContentReader.externalConfigurationDirectory + ?: configLocation.baseDirectoryUrl.toPath().resolve("fake_external_build_system") + + val context = SerializationContextImpl(virtualFileUrlManager, fileContentReader) val serializers = JpsProjectEntitiesLoader.createProjectSerializers(configLocation, externalStoragePath, context) val mainStorage = MutableEntityStorage.create() val orphanageStorage = MutableEntityStorage.create() val unloadedStorage = MutableEntityStorage.create() - val unloadedModuleNames = UnloadedModulesNameHolder.DUMMY + val unloadedModuleNames = if (loadUnloadedModules) UnloadedModulesNameHolder.DUMMY else JpsUnloadedModulesNameHolder(readNamesOfUnloadedModules(configLocation.workspaceFile, fileContentReader.projectComponentLoader)) @Suppress("SSBasedInspection") runBlocking { serializers.loadAll(context.fileContentReader, mainStorage, orphanageStorage, unloadedStorage, unloadedModuleNames, errorReporter) } - - return mainStorage to JpsProjectAdditionalData(TODO(), TODO()) + + val projectName = when (configLocation) { + is JpsProjectConfigLocation.DirectoryBased -> getDirectoryBaseProjectName(configLocation.ideaFolder.toPath()) + is JpsProjectConfigLocation.FileBased -> FileUtil.getNameWithoutExtension(configLocation.iprFile.fileName) + } + val projectRootComponentFileElement = when (configLocation) { + is JpsProjectConfigLocation.DirectoryBased -> fileContentReader.projectComponentLoader.loadRootElement(configLocation.ideaFolder.toPath().resolve("misc.xml")) + is JpsProjectConfigLocation.FileBased -> fileContentReader.projectComponentLoader.loadRootElement(configLocation.iprFile.toPath()) + } + val projectSdkId = readProjectSdkTypeAndName(projectRootComponentFileElement)?.let { + SdkId(name = it.second, type = it.first) + } + return mainStorage to JpsProjectAdditionalData(projectName, projectSdkId) } private fun loadGlobalStorage( @@ -73,9 +117,10 @@ internal class JpsSerializationViaWorkspaceModelImpl : JpsSerializationViaWorksp virtualFileUrlManager: VirtualFileUrlManagerImpl, errorReporter: ErrorReporter, globalStorage: MutableEntityStorage, - ): JpsMacroExpander { - val macroExpander = JpsMacroExpander(JpsGlobalSettingsLoading.computeAllPathVariables(optionsPath)) - val reader = DirectJpsFileContentReader(macroExpander) + ): Pair> { + val pathVariables = JpsGlobalSettingsLoading.computeAllPathVariables(optionsPath) + val macroExpander = JpsMacroExpander(pathVariables) + val reader = GlobalDirectJpsFileContentReader(macroExpander) val rootsTypes = JpsSdkLibraryBridge.serializers.map { it.typeId } val serializers = JpsGlobalEntitiesSerializers.createApplicationSerializers(virtualFileUrlManager, rootsTypes) for (serializer in serializers) { @@ -83,10 +128,79 @@ internal class JpsSerializationViaWorkspaceModelImpl : JpsSerializationViaWorksp serializer.checkAndAddToBuilder(globalStorage, globalStorage, loaded.data) loaded.exception?.let { throw it } } - return macroExpander + return macroExpander to pathVariables } - override fun loadProject(projectPath: Path, pathVariables: Map, loadUnloadedModules: Boolean): JpsProject { - TODO("not implemented") + override fun loadProject(projectPath: Path, externalConfigurationDirectory: Path?, pathVariables: Map, loadUnloadedModules: Boolean): JpsProject { + val model = loadProject(projectPath, externalConfigurationDirectory, VirtualFileUrlManagerImpl(), pathVariables, createErrorReporter(), + MutableEntityStorage.create(), loadUnloadedModules) + return model.project } + + private fun loadOtherProjectComponents(project: JpsProject, componentLoader: JpsComponentLoader, configLocation: JpsProjectConfigLocation, externalStoragePath: Path?) { + setupSerializationExtension(project, configLocation.baseDirectoryUrl.toPath()) + when (configLocation) { + is JpsProjectConfigLocation.DirectoryBased -> { + val dotIdea = configLocation.ideaFolder.toPath() + val defaultConfigFile = dotIdea.resolve("misc.xml") + for (extension in JpsModelSerializerExtension.getExtensions()) { + for (serializer in extension.projectExtensionSerializers) { + componentLoader.loadComponents(dotIdea, defaultConfigFile, serializer, project) + } + } + loadArtifactsFromDirectory(project, componentLoader, dotIdea, externalStoragePath) + loadRunConfigurationsFromDirectory(project, componentLoader, dotIdea, configLocation.workspaceFile) + } + is JpsProjectConfigLocation.FileBased -> { + val iprFile = configLocation.iprFile.toPath() + val iprRoot = componentLoader.loadRootElement(iprFile) + val iwsRoot = componentLoader.loadRootElement(configLocation.workspaceFile) + loadProjectExtensionsFromIpr(project, iprRoot, iwsRoot) + loadArtifactsFromIpr(project, iprRoot) + loadRunConfigurationsFromIpr(project, iprRoot, iwsRoot) + } + } + } + + private fun loadOtherModuleComponents(project: JpsProjectBridge) { + project.modules.forEach { module -> + loadOtherModuleComponents(module) + } + } + + private fun loadOtherModuleComponents(module: JpsModuleBridge) { + val moduleEntity = module.entity + val entitySource = moduleEntity.entitySource + if (entitySource is JpsProjectFileEntitySource.FileInDirectory) { + module.container.setChild(JpsModuleSerializationDataExtensionImpl.ROLE, + JpsModuleSerializationDataExtensionImpl(entitySource.directory.toPath())) + } + + for (serializerExtension in JpsModelSerializerExtension.getExtensions()) { + val rootElement = Element("module") + //todo is it enough? + moduleEntity.customImlData?.customModuleOptions?.forEach { (key, value) -> + rootElement.setAttribute(key, value) + } + moduleEntity.exModuleOptions?.let { externalSystemOptions -> + externalSystemOptions.externalSystem?.let { + rootElement.setAttribute("external.system.id", it) + rootElement.setAttribute("ExternalSystem", it) + } + } + serializerExtension.loadModuleOptions(module, rootElement) + } + } + + private val JpsProjectConfigLocation.workspaceFile: Path + get() = when (this) { + is JpsProjectConfigLocation.DirectoryBased -> ideaFolder.toPath().resolve("workspace.xml") + is JpsProjectConfigLocation.FileBased -> iprFileParent.toPath().resolve("${iprFile.fileName.substringBeforeLast('.')}.iws") + } } + +private class JpsUnloadedModulesNameHolder(private val unloadedModuleNames: Set) : UnloadedModulesNameHolder { + override fun isUnloaded(name: String): Boolean = unloadedModuleNames.contains(name) + + override fun hasUnloaded(): Boolean = unloadedModuleNames.isNotEmpty() +} \ No newline at end of file diff --git a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/ProjectDirectJpsFileContentReader.kt b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/ProjectDirectJpsFileContentReader.kt new file mode 100644 index 000000000000..73a36ff5f853 --- /dev/null +++ b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/ProjectDirectJpsFileContentReader.kt @@ -0,0 +1,57 @@ +// 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.platform.workspace.jps.bridge.impl.serialization + +import com.intellij.openapi.components.ExpandMacroToPathMap +import com.intellij.platform.workspace.jps.serialization.impl.JpsFileContentReader +import org.jdom.Element +import org.jetbrains.jps.model.serialization.JDomSerializationUtil +import org.jetbrains.jps.model.serialization.JpsComponentLoader +import org.jetbrains.jps.model.serialization.JpsProjectConfigurationLoading +import org.jetbrains.jps.model.serialization.JpsProjectConfigurationLoading.createProjectMacroExpander +import org.jetbrains.jps.util.JpsPathUtil +import java.nio.file.Path +import kotlin.io.path.Path + +/** + * Loads data of module-level and project-level components directly from XML and iml configuration files. + */ +internal class ProjectDirectJpsFileContentReader( + projectBaseDir: Path, + val externalConfigurationDirectory: Path?, + private val pathVariables: Map +) : JpsFileContentReader { + + private val projectMacroExpander = createProjectMacroExpander(pathVariables, projectBaseDir) + val projectComponentLoader = JpsComponentLoader(projectMacroExpander, externalConfigurationDirectory) + + override fun loadComponent(fileUrl: String, componentName: String, customModuleFilePath: String?): Element? { + if (fileUrl.endsWith(".iml")) { + //todo support external storage + val loader = getModuleLoader(fileUrl) + return JDomSerializationUtil.findComponent(loader.loadRootElement(Path(JpsPathUtil.urlToPath(fileUrl))), componentName) + } + return loadProjectLevelComponent(fileUrl, componentName) + } + + private fun loadProjectLevelComponent(fileUrl: String, componentName: String): Element? { + val rootElement = projectComponentLoader.loadRootElement(Path(JpsPathUtil.urlToPath(fileUrl))) ?: return null + if (rootElement.name == JDomSerializationUtil.COMPONENT_ELEMENT && JDomSerializationUtil.isComponent(componentName, rootElement)) { + return rootElement + } + return JDomSerializationUtil.findComponent(rootElement, componentName) + } + + override fun getExpandMacroMap(fileUrl: String): ExpandMacroToPathMap { + if (fileUrl.endsWith(".iml")) { + return getModuleLoader(fileUrl).macroExpander.expandMacroMap + } + return projectMacroExpander.expandMacroMap + } + + private fun getModuleLoader(imlFileUrl: String): JpsComponentLoader { + val moduleFile = Path(JpsPathUtil.urlToPath(imlFileUrl)) + val macroExpander = JpsProjectConfigurationLoading.createModuleMacroExpander(pathVariables, moduleFile) + //todo cache + return JpsComponentLoader(macroExpander, null) + } +} diff --git a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/SerializationContextImpl.kt b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/SerializationContextImpl.kt index 1d136fc5fc45..bfd1c48dd447 100644 --- a/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/SerializationContextImpl.kt +++ b/platform/workspace/jps/src/com/intellij/platform/workspace/jps/bridge/impl/serialization/SerializationContextImpl.kt @@ -7,23 +7,24 @@ import com.intellij.platform.workspace.jps.serialization.impl.* import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager internal class SerializationContextImpl( - override val virtualFileUrlManager: VirtualFileUrlManager + override val virtualFileUrlManager: VirtualFileUrlManager, + override val fileContentReader: JpsFileContentReader, ) : SerializationContext { - override val fileContentReader: JpsFileContentReader - get() = TODO("not implemented") - override val isExternalStorageEnabled: Boolean get() = false //todo override val fileInDirectorySourceNames: FileInDirectorySourceNames get() = FileInDirectorySourceNames.empty() override val isJavaPluginPresent: Boolean - get() = true //todo? + get() = true + override val customModuleComponentSerializers: List get() = emptyList() //todo + override val customModuleRootsSerializers: List get() = emptyList() //todo + override val customFacetRelatedEntitySerializers: List> get() = listOf(DefaultFacetEntitySerializer()) //todo }