JPS support for external storage of modules

externalModules renamed modules — to avoid boilerplate code, the same file name is used for both storages (project and external project)
This commit is contained in:
Vladimir Krivosheev
2017-11-03 15:36:43 +01:00
parent 11a4ce7de7
commit bc3d92b00a
4 changed files with 69 additions and 44 deletions
@@ -724,7 +724,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
}
public void readExternal(Element parentNode) {
public void readExternal(@NotNull Element parentNode) {
myState = XmlSerializer.deserialize(parentNode, State.class);
if (!myProject.isDefault()) {
for (Element option : parentNode.getChildren("option")) {
@@ -778,7 +778,6 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
LOG.error(e);
}
myModuleProcessorProfiles.clear();
myProcessorsProfilesMap = null;
@@ -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;
@@ -133,17 +119,35 @@ public class JpsProjectLoader extends JpsLoaderBase {
loadComponents(dir, defaultConfigFile, serializer, myProject);
}
}
Path externalConfigDir = resolveExternalProjectConfig("project");
if (externalConfigDir != null) {
LOG.info("External project config dir is used: " + externalConfigDir);
}
Element moduleData = JDomSerializationUtil.findComponent(loadRootElement(dir.resolve("modules.xml")), "ProjectModuleManager");
Element externalModuleData = externalConfigDir == null ? null : loadRootElement(externalConfigDir.resolve("modules.xml"));
if (externalModuleData != null) {
String componentName = externalModuleData.getAttributeValue("name");
LOG.assertTrue(componentName != null && componentName.startsWith("External"));
externalModuleData.setAttribute("name", componentName.substring("External".length()));
if (moduleData == null) {
moduleData = externalModuleData;
}
else {
JDOMUtil.deepMerge(moduleData, externalModuleData);
}
}
Path workspaceFile = dir.resolve("workspace.xml");
loadModules(loadRootElement(dir.resolve("modules.xml")), projectSdkType, workspaceFile);
loadModules(moduleData, projectSdkType, workspaceFile);
Runnable timingLog = TimingLog.startActivity("loading project libraries");
for (Path libraryFile : listXmlFiles(dir.resolve("libraries"))) {
loadProjectLibraries(loadRootElement(libraryFile));
}
Path externalConfigDir = resolveExternalProjectConfig("project");
if (externalConfigDir != null) {
LOG.info("External project config dir is used: " + externalConfigDir);
loadProjectLibraries(loadRootElement(externalConfigDir.resolve("libraries.xml")));
}
@@ -207,7 +211,7 @@ public class JpsProjectLoader extends JpsLoaderBase {
}
}
}
loadModules(iprRoot, projectSdkType, iwsFile);
loadModules(JDomSerializationUtil.findComponent(iprRoot, "ProjectModuleManager"), projectSdkType, iwsFile);
loadProjectLibraries(JDomSerializationUtil.findComponent(iprRoot, "libraryTable"));
loadArtifacts(JDomSerializationUtil.findComponent(iprRoot, "ArtifactManager"));
if (hasRunConfigurationSerializers()) {
@@ -239,10 +243,11 @@ public class JpsProjectLoader extends JpsLoaderBase {
JpsLibraryTableSerializer.loadLibraries(libraryTableElement, myProject.getLibraryCollection());
}
private void loadModules(@Nullable Element root, final @Nullable JpsSdkType<?> projectSdkType, Path workspaceFile) {
private void loadModules(@Nullable Element componentElement, final @Nullable JpsSdkType<?> projectSdkType, @NotNull Path workspaceFile) {
Runnable timingLog = TimingLog.startActivity("loading modules");
Element componentRoot = JDomSerializationUtil.findComponent(root, "ProjectModuleManager");
if (componentRoot == null) return;
if (componentElement == null) {
return;
}
Set<String> unloadedModules = new HashSet<>();
if (!myLoadUnloadedModules && Files.exists(workspaceFile)) {
@@ -254,7 +259,7 @@ public class JpsProjectLoader extends JpsLoaderBase {
final Set<Path> foundFiles = new THashSet<>();
final List<Path> moduleFiles = new ArrayList<>();
for (Element moduleElement : JDOMUtil.getChildren(componentRoot.getChild("modules"), "module")) {
for (Element moduleElement : JDOMUtil.getChildren(componentElement.getChild("modules"), "module")) {
final String path = moduleElement.getAttributeValue("filepath");
final Path file = Paths.get(path);
if (foundFiles.add(file) && !unloadedModules.contains(getModuleName(file))) {
@@ -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("externalModules.xml")))
@State(name = "ExternalModuleListStorage", storages = arrayOf(Storage("modules.xml")))
internal class ExternalModuleListStorage(private val project: Project) : PersistentStateComponent<Element>, ProjectModelElement {
var loadedState: Set<ModulePath>? = null
private set
@@ -1,18 +1,16 @@
/*
* 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.
//
// 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.
package com.intellij.openapi.util;
import com.intellij.openapi.diagnostic.Logger;
@@ -54,7 +52,7 @@ import java.util.List;
@SuppressWarnings({"HardCodedStringLiteral"})
public class JDOMUtil {
private static final ThreadLocal<SoftReference<SAXBuilder>> ourSaxBuilder = new ThreadLocal<SoftReference<SAXBuilder>>();
public static final Condition<Attribute> NOT_EMPTY_VALUE_CONDITION = new Condition<Attribute>() {
private static final Condition<Attribute> NOT_EMPTY_VALUE_CONDITION = new Condition<Attribute>() {
@Override
public boolean value(Attribute attribute) {
return !StringUtil.isEmpty(attribute.getValue());
@@ -104,7 +102,7 @@ public class JDOMUtil {
if (e1 == null || e2 == null) return false;
return Comparing.equal(e1.getName(), e2.getName())
&& attListsEqual(e1.getAttributes(), e2.getAttributes(), ignoreEmptyAttrValues)
&& isAttributesEqual(e1.getAttributes(), e2.getAttributes(), ignoreEmptyAttrValues)
&& contentListsEqual(e1.getContent(CONTENT_FILTER), e2.getContent(CONTENT_FILTER), ignoreEmptyAttrValues);
}
@@ -224,7 +222,7 @@ public class JDOMUtil {
return c1 instanceof Element && c2 instanceof Element && areElementsEqual((Element)c1, (Element)c2, ignoreEmptyAttrValues);
}
private static boolean attListsEqual(@NotNull List<Attribute> l1, @NotNull List<Attribute> l2, boolean ignoreEmptyAttrValues) {
private static boolean isAttributesEqual(@NotNull List<Attribute> l1, @NotNull List<Attribute> l2, boolean ignoreEmptyAttrValues) {
if (ignoreEmptyAttrValues) {
l1 = ContainerUtil.filter(l1, NOT_EMPTY_VALUE_CONDITION);
l2 = ContainerUtil.filter(l2, NOT_EMPTY_VALUE_CONDITION);
@@ -720,4 +718,27 @@ public class JDOMUtil {
to.setAttribute(attribute);
}
}
@NotNull
public static Element deepMerge(@NotNull Element to, @NotNull Element from) {
for (Iterator<Element> iterator = from.getChildren().iterator(); iterator.hasNext(); ) {
Element child = iterator.next();
iterator.remove();
Element existingChild = to.getChild(child.getName());
// if no children (e.g. `<module fileurl="value" />`), it means that element should be added as list item
if (existingChild == null || existingChild.getChildren().isEmpty() || !isAttributesEqual(existingChild.getAttributes(), child.getAttributes(), false)) {
to.addContent(child);
}
else {
deepMerge(existingChild, child);
}
}
for (Iterator<Attribute> iterator = from.getAttributes().iterator(); iterator.hasNext(); ) {
Attribute attribute = iterator.next();
iterator.remove();
to.setAttribute(attribute);
}
return to;
}
}