external storage: do not store bytecode taget level in the project files

This commit is contained in:
Vladimir Krivosheev
2017-11-17 13:33:32 +01:00
parent 2673736cb6
commit e71d3798cb
13 changed files with 152 additions and 98 deletions
@@ -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<String> 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<String> 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<String, String> externalState = myProject.getComponent(ExternalCompilerConfigurationStorage.class).getLoadedState();
if (externalState != null) {
myModuleBytecodeTarget.putAll(externalState);
}
}
@@ -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<Element>, ProjectModelElement {
var loadedState: Map<String, String>? = 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<String, String>, isExternal: Boolean): List<String> {
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<String>, map: Map<String, String>, 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<String, String> {
val result = THashMap<String, String>()
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
}
@@ -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 <E extends JpsElement> Element loadComponentData(@NotNull JpsElementExtensionSerializerBase<E> serializer, Path configFile) {
return JDomSerializationUtil.findComponent(loadRootElement(configFile), serializer.getComponentName());
}
/**
* Returns null if file doesn't exist
*/
@@ -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");
@@ -36,7 +36,7 @@ private open class ModuleStoreImpl(module: Module, private val pathMacroManager:
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): List<Storage> {
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
}
}
@@ -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
@@ -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 {
@@ -33,12 +33,7 @@ internal open class ExternalProjectStorage(fileSpec: String, project: Project, s
override fun createSaveSession(states: StateMap) = object : XmlElementStorageSaveSession<ExternalProjectStorage>(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)
}
}
}
@@ -63,7 +63,7 @@ internal class ExternalSystemStreamProviderFactory(private val project: Project)
})
}
override fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, storages: List<Storage>, operation: StateStorageOperation): List<Storage>? {
override fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, stateSpec: State, storages: List<Storage>, operation: StateStorageOperation): List<Storage>? {
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<Storage>(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<Storage>(storages.size + 1)
result.add(annotation)
result.addAll(storages)
return result
}
@@ -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<? extends NameGetter> 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<String> {
}
}
@@ -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<Storage>, operation: StateStorageOperation): List<Storage>? = null
fun customizeStorageSpecs(component: PersistentStateComponent<*>, componentManager: ComponentManager, stateSpec: State, storages: List<Storage>, operation: StateStorageOperation): List<Storage>? = null
}
@@ -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<Element>, ProjectModelElement {
var loadedState: Set<ModulePath>? = null
private set
+4
View File
@@ -13,6 +13,10 @@
<interface-class>com.intellij.packaging.artifacts.ArtifactManager</interface-class>
<implementation-class>com.intellij.packaging.impl.artifacts.ArtifactManagerImpl</implementation-class>
</component>
<component>
<implementation-class>com.intellij.compiler.ExternalCompilerConfigurationStorage</implementation-class>
<loadForDefaultProject/>
</component>
<component>
<interface-class>com.intellij.compiler.CompilerConfiguration</interface-class>
<implementation-class>com.intellij.compiler.CompilerConfigurationImpl</implementation-class>