diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java index 592b07186e2c..1a958c85ad78 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java @@ -26,6 +26,7 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.impl.ExternalModuleListStorage; import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; @@ -60,6 +61,8 @@ import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerConfig import java.io.File; import java.util.*; +import static com.intellij.compiler.ExternalCompilerConfigurationStorageKt.*; +import static com.intellij.util.JdomKt.element; import static org.jetbrains.jps.model.java.impl.compiler.ResourcePatterns.normalizeWildcards; import static org.jetbrains.jps.model.java.impl.compiler.ResourcePatterns.optimize; import static org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerConfigurationSerializer.DEFAULT_WILDCARD_PATTERNS; @@ -153,7 +156,6 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements @Override public Element getState() { - Element state = new Element("state"); XmlSerializer.serializeInto(myState, state, new SkipDefaultValuesSerializationFilters() { @Override @@ -207,30 +209,17 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements state.addContent(annotationProcessingSettings); } - if (!StringUtil.isEmpty(myBytecodeTargetLevel) || !myModuleBytecodeTarget.isEmpty()) { - final Element bytecodeTarget = addChild(state, JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL); + List moduleNames = getFilteredModuleNameList(myProject, myModuleBytecodeTarget, false); + if (!StringUtil.isEmpty(myBytecodeTargetLevel) || !moduleNames.isEmpty()) { + final Element bytecodeTarget = element(state, JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL); if (!StringUtil.isEmpty(myBytecodeTargetLevel)) { bytecodeTarget.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, myBytecodeTargetLevel); } - if (!myModuleBytecodeTarget.isEmpty()) { - final List moduleNames = new ArrayList<>(myModuleBytecodeTarget.keySet()); - Collections.sort(moduleNames, String.CASE_INSENSITIVE_ORDER); - for (String name : moduleNames) { - final Element moduleElement = addChild(bytecodeTarget, JpsJavaCompilerConfigurationSerializer.MODULE); - moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, name); - final String value = myModuleBytecodeTarget.get(name); - moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, value != null ? value : ""); - } - } + writeBytecodeTarget(moduleNames, myModuleBytecodeTarget, bytecodeTarget); } return state; } - @Override - public void loadState(Element state) { - readExternal(state); - } - @Override public int getBuildProcessHeapSize(final int javacPreferredHeapSize) { final int heapSize = myState.BUILD_PROCESS_HEAP_SIZE; @@ -702,8 +691,8 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements return true; } - - public void readExternal(@NotNull Element parentNode) { + @Override + public void loadState(@NotNull Element parentNode) { myState = XmlSerializer.deserialize(parentNode, State.class); if (!myProject.isDefault()) { for (Element option : parentNode.getChildren("option")) { @@ -785,20 +774,16 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements myBytecodeTargetLevel = null; myModuleBytecodeTarget.clear(); - final Element bytecodeTargetElement = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL); + + Element bytecodeTargetElement = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL); if (bytecodeTargetElement != null) { myBytecodeTargetLevel = bytecodeTargetElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE); - for (Element elem : bytecodeTargetElement.getChildren(JpsJavaCompilerConfigurationSerializer.MODULE)) { - final String name = elem.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME); - if (name == null) { - continue; - } - final String target = elem.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE); - if (target == null) { - continue; - } - myModuleBytecodeTarget.put(name, target); - } + readByteTargetLevel(parentNode); + } + + Map externalState = myProject.getComponent(ExternalCompilerConfigurationStorage.class).getLoadedState(); + if (externalState != null) { + myModuleBytecodeTarget.putAll(externalState); } } diff --git a/java/compiler/impl/src/com/intellij/compiler/ExternalCompilerConfigurationStorage.kt b/java/compiler/impl/src/com/intellij/compiler/ExternalCompilerConfigurationStorage.kt new file mode 100644 index 000000000000..14f7feab2f0c --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/ExternalCompilerConfigurationStorage.kt @@ -0,0 +1,85 @@ +// Copyright 2000-2017 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 com.intellij.compiler + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.module.impl.ModuleManagerImpl +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.isExternalStorageEnabled +import com.intellij.openapi.roots.ExternalProjectSystemRegistry +import com.intellij.openapi.roots.ProjectModelElement +import com.intellij.openapi.roots.ProjectModelExternalSource +import com.intellij.util.element +import gnu.trove.THashMap +import org.jdom.Element +import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerConfigurationSerializer +import java.util.* + +@State(name = "ExternalCompilerConfigurationStorage", storages = arrayOf(Storage("compiler.xml")), externalStorageOnly = true) +internal class ExternalCompilerConfigurationStorage(private val project: Project) : PersistentStateComponent, ProjectModelElement { + var loadedState: Map? = null + private set + + override fun getState(): Element { + val e = Element("state") + if (!project.isExternalStorageEnabled) { + return e + } + + val map = (CompilerConfigurationImpl.getInstance(project) as CompilerConfigurationImpl).modulesBytecodeTargetMap + val moduleNames = getFilteredModuleNameList(project, map, true) + if (moduleNames.isNotEmpty()) { + writeBytecodeTarget(moduleNames, map, e.element(JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL)) + } + return e + } + + override fun loadState(state: Element) { + loadedState = readByteTargetLevel(state) + } + + override fun getExternalSource(): ProjectModelExternalSource? { + val externalProjectSystemRegistry = ExternalProjectSystemRegistry.getInstance() + for (module in ModuleManagerImpl.getInstanceImpl(project).modules) { + externalProjectSystemRegistry.getExternalSource(module)?.let { + return it + } + } + return null + } +} + +internal fun getFilteredModuleNameList(project: Project, map: Map, isExternal: Boolean): List { + if (!project.isExternalStorageEnabled) { + return map.keys.toList() + } + + val moduleManager = ModuleManagerImpl.getInstanceImpl(project) + val externalProjectSystemRegistry = ExternalProjectSystemRegistry.getInstance() + return map.keys.filter { + // if no module and !isExternal - return true because CompilerConfigurationImpl saves module name as is without module existence check and this logic is preserved + val module = moduleManager.findModuleByName(it) ?: return@filter !isExternal + (externalProjectSystemRegistry.getExternalSource(module) != null) == isExternal + } +} + +internal fun writeBytecodeTarget(moduleNames: List, map: Map, element: Element) { + Collections.sort(moduleNames, String.CASE_INSENSITIVE_ORDER) + for (name in moduleNames) { + val moduleElement = element.element(JpsJavaCompilerConfigurationSerializer.MODULE) + moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, name) + moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, map.get(name) ?: "") + } +} + +internal fun readByteTargetLevel(parentNode: Element): Map { + val result = THashMap() + val bytecodeTargetElement = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL) ?: return result + for (element in bytecodeTargetElement.getChildren(JpsJavaCompilerConfigurationSerializer.MODULE)) { + val name = element.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME) ?: continue + val target = element.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE) ?: continue + result.put(name, target) + } + return result +} \ No newline at end of file diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java index 2211f06e6872..ebe2263a932c 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 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.jps.model.serialization; import com.intellij.openapi.diagnostic.Logger; @@ -57,7 +43,7 @@ public abstract class JpsLoaderBase { String fileName = serializer.getConfigFileName(); Path configFile = fileName == null ? defaultConfigFile : dir.resolve(fileName); Runnable timingLog = TimingLog.startActivity("loading: " + configFile.getFileName() + ":" + serializer.getComponentName()); - Element componentTag = JDomSerializationUtil.findComponent(loadRootElement(configFile), serializer.getComponentName()); + Element componentTag = loadComponentData(serializer, configFile); if (componentTag != null) { serializer.loadExtension(element, componentTag); } @@ -67,6 +53,11 @@ public abstract class JpsLoaderBase { timingLog.run(); } + @Nullable + private Element loadComponentData(@NotNull JpsElementExtensionSerializerBase serializer, Path configFile) { + return JDomSerializationUtil.findComponent(loadRootElement(configFile), serializer.getComponentName()); + } + /** * Returns null if file doesn't exist */ 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 c975881354e5..73ae4db6bab1 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 @@ -110,6 +110,12 @@ public class JpsProjectLoader extends JpsLoaderBase { return dir.getParent().getFileName().toString(); } + @Nullable + @Override + protected Element loadRootElement(@NotNull Path file) { + return super.loadRootElement(file); + } + private void loadFromDirectory(@NotNull Path dir) { myProject.setName(getDirectoryBaseProjectName(dir)); Path defaultConfigFile = dir.resolve("misc.xml"); diff --git a/platform/configuration-store-impl/src/ModuleStoreImpl.kt b/platform/configuration-store-impl/src/ModuleStoreImpl.kt index 37ef72ee11d6..3ddcd88e3ef3 100644 --- a/platform/configuration-store-impl/src/ModuleStoreImpl.kt +++ b/platform/configuration-store-impl/src/ModuleStoreImpl.kt @@ -36,7 +36,7 @@ private open class ModuleStoreImpl(module: Module, private val pathMacroManager: override fun getStorageSpecs(component: PersistentStateComponent, stateSpec: State, operation: StateStorageOperation): List { val result = super.getStorageSpecs(component, stateSpec, operation) return StreamProviderFactory.EP_NAME.getExtensions(project).computeIfAny { - LOG.runAndLogException { it.customizeStorageSpecs(component, storageManager.componentManager!!, result, operation) } + LOG.runAndLogException { it.customizeStorageSpecs(component, storageManager.componentManager!!, stateSpec, result, operation) } } ?: result } } diff --git a/platform/configuration-store-impl/src/ProjectStoreImpl.kt b/platform/configuration-store-impl/src/ProjectStoreImpl.kt index 8cbb8b9eb5f6..0aafad519394 100644 --- a/platform/configuration-store-impl/src/ProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/ProjectStoreImpl.kt @@ -201,7 +201,7 @@ abstract class ProjectStoreBase(override final val project: ProjectImpl) : Compo else { result!!.sortWith(deprecatedComparator) StreamProviderFactory.EP_NAME.getExtensions(project).computeIfAny { - LOG.runAndLogException { it.customizeStorageSpecs(component, project, result!!, operation) } + LOG.runAndLogException { it.customizeStorageSpecs(component, project, stateSpec, result!!, operation) } }?.let { // yes, DEPRECATED_PROJECT_FILE_STORAGE_ANNOTATION is not added in this case return it diff --git a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt index 98a1a70e7bab..3cc27b26aab7 100644 --- a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt +++ b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt @@ -174,7 +174,14 @@ open class StateStorageManagerImpl(private val rootTagName: String, key = normalizedCollapsedPath } else { - key = storageClass.name!! + val storageClassName = storageClass.name!! + // we cannot change this ancient logic for now, so, detect this case manually + if (storageClassName === "com.intellij.openapi.externalSystem.configurationStore.ExternalProjectStorage") { + key = "$normalizedCollapsedPath@ExternalProjectStorage" + } + else { + key = storageClassName + } } val storage = storageLock.read { storages.get(key) } ?: return storageLock.write { diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorage.kt b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorage.kt index 890e4102a600..24e40a1a3f52 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorage.kt +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorage.kt @@ -33,12 +33,7 @@ internal open class ExternalProjectStorage(fileSpec: String, project: Project, s override fun createSaveSession(states: StateMap) = object : XmlElementStorageSaveSession(states, this) { override fun saveLocally(element: Element?) { - if (element == null) { - manager.fileStorage.remove(fileSpec) - } - else { - manager.fileStorage.write(fileSpec, element) - } + manager.fileStorage.write(fileSpec, element) } } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStreamProviderFactory.kt b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStreamProviderFactory.kt index c9f391121733..a455b5a6debe 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStreamProviderFactory.kt +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStreamProviderFactory.kt @@ -63,7 +63,7 @@ internal class ExternalSystemStreamProviderFactory(private val project: Project) }) } - override fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, storages: List, operation: StateStorageOperation): List? { + override fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, stateSpec: State, storages: List, operation: StateStorageOperation): List? { val project = componentManager as? Project ?: (componentManager as Module).project // we store isExternalStorageEnabled option in the project workspace file, so, for such components external storage is always disabled and not applicable if ((storages.size == 1 && storages.first().value == StoragePathMacros.WORKSPACE_FILE) || !project.isExternalStorageEnabled) { @@ -94,14 +94,21 @@ internal class ExternalSystemStreamProviderFactory(private val project: Project) // so, we just add our storage as first and default storages in the end as fallback // on write default storages also returned, because default FileBasedStorage will remove data if component has external source - - val result = ArrayList(storages.size + 1) + val annotation: FileStorageAnnotation if (componentManager is Project) { - result.add(FileStorageAnnotation(storages.get(0).value, false, ExternalProjectStorage::class.java)) + val fileSpec = storages.get(0).value + annotation = FileStorageAnnotation(fileSpec, false, ExternalProjectStorage::class.java) } else { - result.add(EXTERNAL_MODULE_STORAGE_ANNOTATION) + annotation = EXTERNAL_MODULE_STORAGE_ANNOTATION } + + if (stateSpec.externalStorageOnly) { + return listOf(annotation) + } + + val result = ArrayList(storages.size + 1) + result.add(annotation) result.addAll(storages) return result } diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/State.java b/platform/projectModel-api/src/com/intellij/openapi/components/State.java index 1e59be363c5f..456a17d2edd2 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/State.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/State.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 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 com.intellij.openapi.components; import com.intellij.openapi.util.Getter; @@ -60,6 +46,11 @@ public @interface State { Class presentableName() default NameGetter.class; + /** + * Is this component intended to store data only in the external storage. + */ + boolean externalStorageOnly() default false; + abstract class NameGetter implements Getter { } } diff --git a/platform/projectModel-impl/src/com/intellij/configurationStore/StreamProviderFactory.kt b/platform/projectModel-impl/src/com/intellij/configurationStore/StreamProviderFactory.kt index 86675c12dbdd..226bf230936b 100644 --- a/platform/projectModel-impl/src/com/intellij/configurationStore/StreamProviderFactory.kt +++ b/platform/projectModel-impl/src/com/intellij/configurationStore/StreamProviderFactory.kt @@ -1,24 +1,7 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 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 com.intellij.configurationStore -import com.intellij.openapi.components.ComponentManager -import com.intellij.openapi.components.PersistentStateComponent -import com.intellij.openapi.components.StateStorageOperation -import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.* import com.intellij.openapi.extensions.ExtensionPointName /** @@ -38,5 +21,5 @@ interface StreamProviderFactory { * `storages` are preprocessed by component store - not raw from state spec. * @return null if not applicable */ - fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, storages: List, operation: StateStorageOperation): List? = null + fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, stateSpec: State, storages: List, operation: StateStorageOperation): List? = null } \ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ExternalModuleListStorage.kt b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ExternalModuleListStorage.kt index 2509f15e7528..285fcc567962 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ExternalModuleListStorage.kt +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ExternalModuleListStorage.kt @@ -12,7 +12,7 @@ import com.intellij.openapi.roots.ProjectModelElement import com.intellij.openapi.roots.ProjectModelExternalSource import org.jdom.Element -@State(name = "ExternalModuleListStorage", storages = arrayOf(Storage("modules.xml"))) +@State(name = "ExternalModuleListStorage", storages = arrayOf(Storage("modules.xml")), externalStorageOnly = true) internal class ExternalModuleListStorage(private val project: Project) : PersistentStateComponent, ProjectModelElement { var loadedState: Set? = null private set diff --git a/resources/src/componentSets/Compiler.xml b/resources/src/componentSets/Compiler.xml index 4350344eb250..0bb2c4f67d0d 100644 --- a/resources/src/componentSets/Compiler.xml +++ b/resources/src/componentSets/Compiler.xml @@ -13,6 +13,10 @@ com.intellij.packaging.artifacts.ArtifactManager com.intellij.packaging.impl.artifacts.ArtifactManagerImpl + + com.intellij.compiler.ExternalCompilerConfigurationStorage + + com.intellij.compiler.CompilerConfiguration com.intellij.compiler.CompilerConfigurationImpl