mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
jps model: support excluding file from content roots by pattern (IDEA-127753)
Currently only file/directory names, not relative paths, are matched.
This commit is contained in:
@@ -15,13 +15,17 @@
|
||||
*/
|
||||
package org.jetbrains.jps.indices.impl;
|
||||
|
||||
import com.intellij.openapi.fileTypes.impl.FileTypeAssocTable;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.jps.indices.ModuleExcludeIndex;
|
||||
import org.jetbrains.jps.model.JpsExcludePattern;
|
||||
import org.jetbrains.jps.model.JpsModel;
|
||||
import org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory;
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.java.JpsJavaModuleExtension;
|
||||
import org.jetbrains.jps.model.java.JpsJavaProjectExtension;
|
||||
@@ -41,10 +45,12 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex {
|
||||
private final Set<File> myTopLevelContentRoots = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
|
||||
private final Map<JpsModule, ArrayList<File>> myModuleToExcludesMap = new THashMap<>();
|
||||
private final Map<JpsModule, List<File>> myModuleToContentMap = new THashMap<>();
|
||||
private final Map<File, FileTypeAssocTable<Boolean>> myExcludeFromContentRootTables = new THashMap<>(FileUtil.FILE_HASHING_STRATEGY);
|
||||
|
||||
public ModuleExcludeIndexImpl(JpsModel model) {
|
||||
final Collection<JpsModule> allModules = model.getProject().getModules();
|
||||
Map<File, JpsModule> contentToModule = new THashMap<>(FileUtil.FILE_HASHING_STRATEGY);
|
||||
MultiMap<String, String> excludePatterns = MultiMap.createLinked();
|
||||
for (final JpsModule module : allModules) {
|
||||
final ArrayList<File> moduleExcludes = new ArrayList<>();
|
||||
for (String url : module.getExcludeRootsList().getUrls()) {
|
||||
@@ -61,6 +67,9 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex {
|
||||
moduleExcludes.add(JpsPathUtil.urlToFile(testOutputUrl));
|
||||
}
|
||||
}
|
||||
for (JpsExcludePattern pattern : module.getExcludePatterns()) {
|
||||
excludePatterns.putValue(pattern.getBaseDirUrl(), pattern.getPattern());
|
||||
}
|
||||
List<String> contentUrls = module.getContentRootsList().getUrls();
|
||||
final List<File> moduleContent = new ArrayList<>(contentUrls.size());
|
||||
for (String contentUrl : contentUrls) {
|
||||
@@ -78,6 +87,15 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex {
|
||||
myExcludedRoots.addAll(moduleExcludes);
|
||||
}
|
||||
|
||||
FileNameMatcherFactory factory = FileNameMatcherFactory.getInstance();
|
||||
for (Map.Entry<String, Collection<String>> entry : excludePatterns.entrySet()) {
|
||||
FileTypeAssocTable<Boolean> table = new FileTypeAssocTable<>();
|
||||
for (String pattern : entry.getValue()) {
|
||||
table.addAssociation(factory.createMatcher(pattern), Boolean.TRUE);
|
||||
}
|
||||
myExcludeFromContentRootTables.put(JpsPathUtil.urlToFile(entry.getKey()), table);
|
||||
}
|
||||
|
||||
JpsJavaProjectExtension projectExtension = JpsJavaExtensionService.getInstance().getProjectExtension(model.getProject());
|
||||
if (projectExtension != null) {
|
||||
String url = projectExtension.getOutputUrl();
|
||||
@@ -168,7 +186,7 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex {
|
||||
|
||||
private enum FileLocation { IN_CONTENT, EXCLUDED, NOT_IN_PROJECT }
|
||||
|
||||
private static FileLocation determineFileLocation(File file, Collection<File> roots, Collection<File> excluded) {
|
||||
private FileLocation determineFileLocation(File file, Collection<File> roots, Collection<File> excluded) {
|
||||
if (roots.isEmpty() && excluded.isEmpty()) {
|
||||
return FileLocation.NOT_IN_PROJECT; // optimization
|
||||
}
|
||||
@@ -177,6 +195,10 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex {
|
||||
if (excluded.contains(current)) {
|
||||
return FileLocation.EXCLUDED;
|
||||
}
|
||||
FileTypeAssocTable<Boolean> table = myExcludeFromContentRootTables.get(current);
|
||||
if (table != null && isExcludedByPattern(file, current, table)) {
|
||||
return FileLocation.EXCLUDED;
|
||||
}
|
||||
if (roots.contains(current)) {
|
||||
return FileLocation.IN_CONTENT;
|
||||
}
|
||||
@@ -185,6 +207,18 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex {
|
||||
return FileLocation.NOT_IN_PROJECT;
|
||||
}
|
||||
|
||||
private static boolean isExcludedByPattern(File file, File root, FileTypeAssocTable<Boolean> table) {
|
||||
File current = file;
|
||||
//noinspection FileEqualsUsage it's ok to compare files by 'equals' here be because these files are produced by the same 'getParentFile' calls
|
||||
while (current != null && !current.equals(root)) {
|
||||
if (table.findAssociatedFileType(current.getName()) != null) {
|
||||
return true;
|
||||
}
|
||||
current = FileUtilRt.getParentFile(current);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<File> getModuleExcludes(JpsModule module) {
|
||||
return myModuleToExcludesMap.get(module);
|
||||
|
||||
@@ -153,6 +153,23 @@ public class ModuleExcludeIndexTest extends JpsJavaModelTestCase {
|
||||
assertExcluded(src);
|
||||
}
|
||||
|
||||
public void testExcludeByPattern() {
|
||||
File root1 = new File(myRoot, "root1");
|
||||
File root2 = new File(myRoot, "root2");
|
||||
JpsModule module = addModule();
|
||||
addContentRoot(module, root1);
|
||||
addContentRoot(module, root2);
|
||||
module.addExcludePattern(JpsPathUtil.pathToUrl(root1.getAbsolutePath()), "*.txt");
|
||||
module.addExcludePattern(JpsPathUtil.pathToUrl(root2.getAbsolutePath()), "out");
|
||||
assertExcluded(new File(root1, "a.txt"));
|
||||
assertExcluded(new File(root1, "dir/a.txt"));
|
||||
assertNotExcluded(new File(root1, "A.java"));
|
||||
assertNotExcluded(new File(root2, "a.txt"));
|
||||
assertExcluded(new File(root2, "out"));
|
||||
assertExcluded(new File(root2, "out/A.java"));
|
||||
assertExcluded(new File(root2, "dir/out/A.java"));
|
||||
}
|
||||
|
||||
private static void addSourceRoot(JpsModule module, File src) {
|
||||
module.addSourceRoot(JpsPathUtil.pathToUrl(src.getAbsolutePath()), JavaSourceRootType.SOURCE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 org.jetbrains.jps.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Specifies a pattern for names of files which should be excluded from a module. If name of a file under {@link #getBaseDirUrl() the base directory}
|
||||
* matches {@link #getPattern() the pattern} it'll be excluded from the containing module, if name of a directory matches the pattern the directory
|
||||
* and all of its contents will be excluded. '?' and '*' wildcards are supported.
|
||||
*
|
||||
* @author nik
|
||||
*/
|
||||
public interface JpsExcludePattern extends JpsElement {
|
||||
@NotNull
|
||||
String getBaseDirUrl();
|
||||
|
||||
@NotNull
|
||||
String getPattern();
|
||||
}
|
||||
@@ -56,6 +56,14 @@ public interface JpsModule extends JpsNamedElement, JpsReferenceableElement<JpsM
|
||||
|
||||
void removeSourceRoot(@NotNull String url, @NotNull JpsModuleSourceRootType rootType);
|
||||
|
||||
/**
|
||||
* Add a {@link JpsExcludePattern pattern} for names of files which should be excluded from this module.
|
||||
*/
|
||||
void addExcludePattern(@NotNull String baseDirUrl, @NotNull String pattern);
|
||||
void removeExcludePattern(@NotNull String baseDirUrl, @NotNull String pattern);
|
||||
|
||||
List<JpsExcludePattern> getExcludePatterns();
|
||||
|
||||
JpsDependenciesList getDependenciesList();
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 org.jetbrains.jps.model.impl;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.JpsExcludePattern;
|
||||
import org.jetbrains.jps.model.ex.JpsElementBase;
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
public class JpsExcludePatternImpl extends JpsElementBase<JpsExcludePatternImpl> implements JpsExcludePattern {
|
||||
private final String myBaseDirUrl;
|
||||
private final String myPattern;
|
||||
|
||||
public JpsExcludePatternImpl(@NotNull String baseDirUrl, @NotNull String pattern) {
|
||||
myBaseDirUrl = baseDirUrl;
|
||||
myPattern = pattern;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getBaseDirUrl() {
|
||||
return myBaseDirUrl;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPattern() {
|
||||
return myPattern;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JpsExcludePatternImpl createCopy() {
|
||||
return new JpsExcludePatternImpl(myBaseDirUrl, myPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyChanges(@NotNull JpsExcludePatternImpl modified) {
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.*;
|
||||
import org.jetbrains.jps.model.ex.JpsElementChildRoleBase;
|
||||
import org.jetbrains.jps.model.ex.JpsElementCollectionRole;
|
||||
import org.jetbrains.jps.model.ex.JpsNamedCompositeElementBase;
|
||||
import org.jetbrains.jps.model.impl.JpsExcludePatternImpl;
|
||||
import org.jetbrains.jps.model.impl.JpsUrlListRole;
|
||||
import org.jetbrains.jps.model.library.JpsLibrary;
|
||||
import org.jetbrains.jps.model.library.JpsLibraryCollection;
|
||||
@@ -32,6 +34,7 @@ import org.jetbrains.jps.model.library.sdk.JpsSdkType;
|
||||
import org.jetbrains.jps.model.library.sdk.JpsSdkReference;
|
||||
import org.jetbrains.jps.model.module.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -41,6 +44,7 @@ public class JpsModuleImpl<P extends JpsElement> extends JpsNamedCompositeElemen
|
||||
private static final JpsUrlListRole CONTENT_ROOTS_ROLE = new JpsUrlListRole("content roots");
|
||||
private static final JpsUrlListRole EXCLUDED_ROOTS_ROLE = new JpsUrlListRole("excluded roots");
|
||||
private static final JpsElementChildRole<JpsDependenciesListImpl> DEPENDENCIES_LIST_CHILD_ROLE = JpsElementChildRoleBase.create("dependencies");
|
||||
private static final JpsElementCollectionRole<JpsExcludePattern> EXCLUDE_PATTERNS_ROLE = JpsElementCollectionRole.create(JpsElementChildRoleBase.create("exclude patterns"));
|
||||
private final JpsModuleType<P> myModuleType;
|
||||
private final JpsLibraryCollection myLibraryCollection;
|
||||
|
||||
@@ -140,6 +144,29 @@ public class JpsModuleImpl<P extends JpsElement> extends JpsNamedCompositeElemen
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExcludePattern(@NotNull String baseDirUrl, @NotNull String pattern) {
|
||||
myContainer.getOrSetChild(EXCLUDE_PATTERNS_ROLE).addChild(new JpsExcludePatternImpl(baseDirUrl, pattern));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExcludePattern(@NotNull String baseDirUrl, @NotNull String pattern) {
|
||||
JpsElementCollection<JpsExcludePattern> child = myContainer.getChild(EXCLUDE_PATTERNS_ROLE);
|
||||
if (child != null) {
|
||||
for (JpsExcludePattern excludePattern : child.getElements()) {
|
||||
if (excludePattern.getBaseDirUrl().equals(baseDirUrl) && excludePattern.getPattern().equals(pattern)) {
|
||||
child.removeChild(excludePattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpsExcludePattern> getExcludePatterns() {
|
||||
JpsElementCollection<JpsExcludePattern> child = myContainer.getChild(EXCLUDE_PATTERNS_ROLE);
|
||||
return child != null ? child.getElements() : Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JpsDependenciesList getDependenciesList() {
|
||||
|
||||
@@ -52,6 +52,14 @@ public class JpsModuleTest extends JpsModelTestCase {
|
||||
assertSameElements(ContainerUtil.newArrayList(modules), module);
|
||||
}
|
||||
|
||||
public void testExcludedPatterns() {
|
||||
JpsModule module = myProject.addModule("m", JpsJavaModuleType.INSTANCE);
|
||||
module.addExcludePattern("file://url", "*.class");
|
||||
JpsExcludePattern pattern = assertOneElement(module.getExcludePatterns());
|
||||
assertEquals("file://url", pattern.getBaseDirUrl());
|
||||
assertEquals("*.class", pattern.getPattern());
|
||||
}
|
||||
|
||||
public void testModifiableModel() {
|
||||
final JpsModule module = myProject.addModule("m", JpsJavaModuleType.INSTANCE);
|
||||
final JpsModuleSourceRoot root0 = module.addSourceRoot("url1", JavaSourceRootType.SOURCE);
|
||||
|
||||
+11
-4
@@ -21,10 +21,7 @@ import com.intellij.util.text.UniqueNameGenerator;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.JpsCompositeElement;
|
||||
import org.jetbrains.jps.model.JpsElement;
|
||||
import org.jetbrains.jps.model.JpsElementFactory;
|
||||
import org.jetbrains.jps.model.JpsElementReference;
|
||||
import org.jetbrains.jps.model.*;
|
||||
import org.jetbrains.jps.model.java.JpsJavaSdkType;
|
||||
import org.jetbrains.jps.model.java.JpsJavaSdkTypeWrapper;
|
||||
import org.jetbrains.jps.model.library.JpsLibrary;
|
||||
@@ -54,6 +51,8 @@ public class JpsModuleRootModelSerializer {
|
||||
public static final String PACKAGE_PREFIX_ATTRIBUTE = "packagePrefix";
|
||||
public static final String IS_TEST_SOURCE_ATTRIBUTE = "isTestSource";
|
||||
public static final String EXCLUDE_FOLDER_TAG = "excludeFolder";
|
||||
public static final String EXCLUDE_PATTERN_TAG = "excludePattern";
|
||||
public static final String EXCLUDE_PATTERN_ATTRIBUTE = "pattern";
|
||||
public static final String ORDER_ENTRY_TAG = "orderEntry";
|
||||
public static final String TYPE_ATTRIBUTE = "type";
|
||||
public static final String SOURCE_FOLDER_TYPE = "sourceFolder";
|
||||
@@ -85,6 +84,9 @@ public class JpsModuleRootModelSerializer {
|
||||
for (Element excludeElement : getChildren(contentElement, EXCLUDE_FOLDER_TAG)) {
|
||||
module.getExcludeRootsList().addUrl(excludeElement.getAttributeValue(URL_ATTRIBUTE));
|
||||
}
|
||||
for (Element excludePatternElement : getChildren(contentElement, EXCLUDE_PATTERN_TAG)) {
|
||||
module.addExcludePattern(url, excludePatternElement.getAttributeValue(EXCLUDE_PATTERN_ATTRIBUTE));
|
||||
}
|
||||
}
|
||||
|
||||
final JpsDependenciesList dependenciesList = module.getDependenciesList();
|
||||
@@ -202,6 +204,11 @@ public class JpsModuleRootModelSerializer {
|
||||
contentElement.addContent(element);
|
||||
}
|
||||
}
|
||||
for (JpsExcludePattern pattern : module.getExcludePatterns()) {
|
||||
if (pattern.getBaseDirUrl().equals(url)) {
|
||||
contentElement.addContent(new Element(EXCLUDE_PATTERN_TAG).setAttribute(EXCLUDE_PATTERN_ATTRIBUTE, pattern.getPattern()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludePattern pattern="*.class" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/excludePatterns.iml" filepath="$PROJECT_DIR$/excludePatterns.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.4" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
+11
@@ -21,6 +21,7 @@ import org.jdom.Element;
|
||||
import org.jetbrains.jps.model.JpsDummyElement;
|
||||
import org.jetbrains.jps.model.JpsEncodingConfigurationService;
|
||||
import org.jetbrains.jps.model.JpsEncodingProjectConfiguration;
|
||||
import org.jetbrains.jps.model.JpsExcludePattern;
|
||||
import org.jetbrains.jps.model.artifact.JpsArtifactService;
|
||||
import org.jetbrains.jps.model.java.*;
|
||||
import org.jetbrains.jps.model.library.JpsLibrary;
|
||||
@@ -117,6 +118,16 @@ public class JpsProjectSerializationTest extends JpsSerializationTestCase {
|
||||
assertSame(productionModule, testModuleProperties.getProductionModule());
|
||||
}
|
||||
|
||||
public void testExcludePatterns() {
|
||||
String projectPath = "/jps/model-serialization/testData/excludePatterns";
|
||||
loadProject(projectPath + "/excludePatterns.ipr");
|
||||
JpsModule module = assertOneElement(myProject.getModules());
|
||||
JpsExcludePattern pattern = assertOneElement(module.getExcludePatterns());
|
||||
assertEquals("*.class", pattern.getPattern());
|
||||
assertEquals(assertOneElement(module.getContentRootsList().getUrls()), pattern.getBaseDirUrl());
|
||||
doTestSaveModule(module, projectPath + "/excludePatterns.iml");
|
||||
}
|
||||
|
||||
public void testProjectSdkWithoutType() {
|
||||
loadProject("/jps/model-serialization/testData/projectSdkWithoutType/projectSdkWithoutType.ipr");
|
||||
JpsSdkReference<JpsDummyElement> reference = myProject.getSdkReferencesTable().getSdkReference(JpsJavaSdkType.INSTANCE);
|
||||
|
||||
Reference in New Issue
Block a user