diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2013-02-08 15:14:04 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2013-02-08 15:14:04 -0800 |
commit | 9edc8f6b58f71ec510ba36b838f115718d9a174d (patch) | |
tree | 06f6df92024fa534ff27e1c0b5fc8b2002848093 /jps | |
parent | b56ea2a18f232d79481e778085fd64e8ae486fc3 (diff) | |
download | idea-9edc8f6b58f71ec510ba36b838f115718d9a174d.tar.gz |
Snapshot of commit 84dc01e773388c2c72a1fc437f313dd5747e7809
from branch master of git://git.jetbrains.org/idea/community.git
Diffstat (limited to 'jps')
74 files changed, 2010 insertions, 406 deletions
diff --git a/jps/jps-builders/src/META-INF/services/org.jetbrains.jps.builders.java.JavaBuilderExtension b/jps/jps-builders/src/META-INF/services/org.jetbrains.jps.builders.java.JavaBuilderExtension new file mode 100644 index 000000000000..d01e292381fc --- /dev/null +++ b/jps/jps-builders/src/META-INF/services/org.jetbrains.jps.builders.java.JavaBuilderExtension @@ -0,0 +1 @@ +org.jetbrains.jps.builders.java.JavaBuilderExtensionImpl
\ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/CmdlineProtoUtil.java b/jps/jps-builders/src/org/jetbrains/jps/api/CmdlineProtoUtil.java index bc71808cf346..46397f75509b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/CmdlineProtoUtil.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/CmdlineProtoUtil.java @@ -127,7 +127,9 @@ public class CmdlineProtoUtil { public static CmdlineRemoteProto.Message.Failure createFailure(String description, @Nullable Throwable cause) { final CmdlineRemoteProto.Message.Failure.Builder builder = CmdlineRemoteProto.Message.Failure.newBuilder(); - builder.setDescription(description); + if (description != null) { + builder.setDescription(description); + } if (cause != null) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final PrintStream stream = new PrintStream(baos); @@ -137,7 +139,11 @@ public class CmdlineProtoUtil { finally { stream.close(); } - builder.setStacktrace(new String(baos.toByteArray())); + final String stacktrace = new String(baos.toByteArray()); + builder.setStacktrace(stacktrace); + if (description == null) { + builder.setDescription(stacktrace); + } } return builder.build(); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootDescriptor.java index 001c955249c0..1c1f9aa31fff 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootDescriptor.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.builders; +import com.intellij.openapi.util.io.FileUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.cmdline.ProjectDescriptor; @@ -33,7 +34,17 @@ public abstract class BuildRootDescriptor { public abstract BuildTarget<?> getTarget(); - public abstract FileFilter createFileFilter(@NotNull ProjectDescriptor descriptor); + /** + * @deprecated override {@link #createFileFilter()} instead + */ + public FileFilter createFileFilter(@NotNull ProjectDescriptor descriptor) { + return null; + } + + @NotNull + public FileFilter createFileFilter() { + return FileUtilRt.ALL_FILES; + } /** * @return the set of excluded directories under this root diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootIndex.java b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootIndex.java index b8065df7acc6..a02d3520a68a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootIndex.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildRootIndex.java @@ -18,7 +18,6 @@ package org.jetbrains.jps.builders; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; -import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.CompileContext; import java.io.File; @@ -62,5 +61,9 @@ public interface BuildRootIndex { JavaSourceRootDescriptor findJavaRootDescriptor(@Nullable CompileContext context, File file); @NotNull - FileFilter getRootFilter(@NotNull BuildRootDescriptor descriptor, @NotNull ProjectDescriptor projectDescriptor); + FileFilter getRootFilter(@NotNull BuildRootDescriptor descriptor); + + boolean isDirectoryAccepted(@NotNull File dir, @NotNull BuildRootDescriptor descriptor); + + boolean isFileAccepted(@NotNull File file, @NotNull BuildRootDescriptor descriptor); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootDescriptorImpl.java b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootDescriptorImpl.java index 2143410c82ef..84a12e514248 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootDescriptorImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootDescriptorImpl.java @@ -16,13 +16,10 @@ package org.jetbrains.jps.builders.impl; import com.intellij.openapi.util.io.FileUtilRt; -import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.BuildTarget; -import org.jetbrains.jps.cmdline.ProjectDescriptor; import java.io.File; -import java.io.FileFilter; public class BuildRootDescriptorImpl extends BuildRootDescriptor { private final File myRoot; @@ -57,16 +54,6 @@ public class BuildRootDescriptorImpl extends BuildRootDescriptor { } @Override - public FileFilter createFileFilter(@NotNull ProjectDescriptor descriptor) { - return new FileFilter() { - @Override - public boolean accept(@NotNull File pathname) { - return true; - } - }; - } - - @Override public boolean canUseFileCache() { return myCanUseFileCache; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootIndexImpl.java b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootIndexImpl.java index bb89a37158a7..b40da51805a0 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootIndexImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildRootIndexImpl.java @@ -27,10 +27,8 @@ import org.jetbrains.jps.builders.*; import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import org.jetbrains.jps.builders.storage.BuildDataPaths; -import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.BuilderRegistry; import org.jetbrains.jps.incremental.CompileContext; -import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.indices.IgnoredFileIndex; import org.jetbrains.jps.indices.ModuleExcludeIndex; import org.jetbrains.jps.model.JpsModel; @@ -47,12 +45,14 @@ import java.util.concurrent.ConcurrentMap; public class BuildRootIndexImpl implements BuildRootIndex { private static final Key<Map<File, BuildRootDescriptor>> ROOT_DESCRIPTOR_MAP = Key.create("_root_to_descriptor_map"); private static final Key<Map<BuildTarget<?>, List<? extends BuildRootDescriptor>>> TEMP_TARGET_ROOTS_MAP = Key.create("_module_to_root_map"); + private final IgnoredFileIndex myIgnoredFileIndex; private HashMap<BuildTarget<?>, List<? extends BuildRootDescriptor>> myRootsByTarget; private THashMap<File,List<BuildRootDescriptor>> myRootToDescriptors; private ConcurrentMap<BuildRootDescriptor, FileFilter> myFileFilters; public BuildRootIndexImpl(BuildTargetIndex targetIndex, JpsModel model, ModuleExcludeIndex index, BuildDataPaths dataPaths, final IgnoredFileIndex ignoredFileIndex) { + myIgnoredFileIndex = ignoredFileIndex; myRootsByTarget = new HashMap<BuildTarget<?>, List<? extends BuildRootDescriptor>>(); myRootToDescriptors = new THashMap<File, List<BuildRootDescriptor>>(FileUtil.FILE_HASHING_STRATEGY); myFileFilters = new ConcurrentHashMap<BuildRootDescriptor, FileFilter>(); @@ -96,22 +96,6 @@ public class BuildRootIndexImpl implements BuildRootIndex { list.add(descriptor); } - private static Map<String, List<ModuleBuildTarget>> buildModuleNameToTargetsMap(BuildTargetIndex targetIndex) { - final Map<String, List<ModuleBuildTarget>> moduleNameToTargetsMap = new HashMap<String, List<ModuleBuildTarget>>(); - for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) { - for (ModuleBuildTarget target : targetIndex.getAllTargets(type)) { - final String moduleName = target.getModule().getName(); - List<ModuleBuildTarget> targets = moduleNameToTargetsMap.get(moduleName); - if (targets == null) { - targets = new ArrayList<ModuleBuildTarget>(); - moduleNameToTargetsMap.put(moduleName, targets); - } - targets.add(target); - } - } - return moduleNameToTargetsMap; - } - @NotNull @Override public <R extends BuildRootDescriptor> List<R> getRootDescriptors(@NotNull File root, @@ -203,27 +187,28 @@ public class BuildRootIndexImpl implements BuildRootIndex { public <R extends BuildRootDescriptor> R findParentDescriptor(@NotNull File file, @NotNull Collection<? extends BuildTargetType<? extends BuildTarget<R>>> types, @Nullable CompileContext context) { File current = file; + int depth = 0; while (current != null) { - final List<R> descriptors = getRootDescriptors(current, types, context); + final List<R> descriptors = filterDescriptorsByFile(getRootDescriptors(current, types, context), file, depth); if (!descriptors.isEmpty()) { return descriptors.get(0); } current = FileUtilRt.getParentFile(current); + depth++; } return null; } - - @Override @NotNull public <R extends BuildRootDescriptor> Collection<R> findAllParentDescriptors(@NotNull File file, @Nullable Collection<? extends BuildTargetType<? extends BuildTarget<R>>> types, @Nullable CompileContext context) { File current = file; - Collection<R> result = null; + List<R> result = null; + int depth = 0; while (current != null) { - List<R> descriptors = getRootDescriptors(current, types, context); + List<R> descriptors = filterDescriptorsByFile(getRootDescriptors(current, types, context), file, depth); if (!descriptors.isEmpty()) { if (result == null) { result = descriptors; @@ -234,11 +219,43 @@ public class BuildRootIndexImpl implements BuildRootIndex { } } current = FileUtilRt.getParentFile(current); + depth++; } return result != null ? result : Collections.<R>emptyList(); } @NotNull + private <R extends BuildRootDescriptor> List<R> filterDescriptorsByFile(@NotNull List<R> descriptors, File file, int parentsToCheck) { + List<R> result = descriptors; + for (int i = 0; i < descriptors.size(); i++) { + R descriptor = descriptors.get(i); + if (isFileAccepted(file, descriptor) && isParentDirectoriesAccepted(file, parentsToCheck, descriptor)) { + if (result != descriptors) { + result.add(descriptor); + } + } + else if (result == descriptors) { + result = new ArrayList<R>(descriptors.size() - 1); + for (int j = 0; j < i; j++) { + result.add(descriptors.get(j)); + } + } + } + return result; + } + + private boolean isParentDirectoriesAccepted(File file, int parentsToCheck, BuildRootDescriptor descriptor) { + File current = file; + while (parentsToCheck-- > 0) { + current = FileUtil.getParentFile(current); + if (!isDirectoryAccepted(current, descriptor)) { + return false; + } + } + return true; + } + + @NotNull @Override public <R extends BuildRootDescriptor> Collection<R> findAllParentDescriptors(@NotNull File file, @Nullable CompileContext context) { return findAllParentDescriptors(file, null, context); @@ -265,12 +282,22 @@ public class BuildRootIndexImpl implements BuildRootIndex { @NotNull @Override - public FileFilter getRootFilter(@NotNull BuildRootDescriptor descriptor, @NotNull ProjectDescriptor projectDescriptor) { + public FileFilter getRootFilter(@NotNull BuildRootDescriptor descriptor) { FileFilter filter = myFileFilters.get(descriptor); if (filter == null) { - filter = descriptor.createFileFilter(projectDescriptor); + filter = descriptor.createFileFilter(); myFileFilters.put(descriptor, filter); } return filter; } + + @Override + public boolean isFileAccepted(@NotNull File file, @NotNull BuildRootDescriptor descriptor) { + return !myIgnoredFileIndex.isIgnored(file.getName()) && getRootFilter(descriptor).accept(file); + } + + @Override + public boolean isDirectoryAccepted(@NotNull File dir, @NotNull BuildRootDescriptor descriptor) { + return !myIgnoredFileIndex.isIgnored(dir.getName()) && !descriptor.getExcludedRoots().contains(dir); + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderExtension.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderExtension.java new file mode 100644 index 000000000000..72bade77d284 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderExtension.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2013 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.builders.java; + +import org.jetbrains.jps.model.module.JpsModuleType; + +import java.io.File; +import java.util.Collections; +import java.util.Set; + +/** + * @author nik + */ +public abstract class JavaBuilderExtension { + + public boolean shouldHonorFileEncodingForCompilation(File file) { + return true; + } + + public Set<? extends JpsModuleType<?>> getCompilableModuleTypes() { + return Collections.emptySet(); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderExtensionImpl.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderExtensionImpl.java new file mode 100644 index 000000000000..28f2c88a19f1 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderExtensionImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2013 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.builders.java; + +import org.jetbrains.jps.incremental.java.JavaBuilder; +import org.jetbrains.jps.model.java.JpsJavaModuleType; +import org.jetbrains.jps.model.module.JpsModuleType; + +import java.io.File; +import java.util.Collections; +import java.util.Set; + +/** + * @author nik + */ +public class JavaBuilderExtensionImpl extends JavaBuilderExtension { + @Override + public boolean shouldHonorFileEncodingForCompilation(File file) { + return JavaBuilder.JAVA_SOURCES_FILTER.accept(file); + } + + @Override + public Set<? extends JpsModuleType<?>> getCompilableModuleTypes() { + return Collections.singleton(JpsJavaModuleType.INSTANCE); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceRootDescriptor.java index 3cae0f0aedfd..6964b09c14e1 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceRootDescriptor.java @@ -18,7 +18,7 @@ package org.jetbrains.jps.builders.java; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildRootDescriptor; -import org.jetbrains.jps.cmdline.ProjectDescriptor; +import org.jetbrains.jps.incremental.BuilderRegistry; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.compiler.JpsCompilerExcludes; @@ -90,13 +90,15 @@ public class JavaSourceRootDescriptor extends BuildRootDescriptor { return target; } + @NotNull @Override - public FileFilter createFileFilter(@NotNull ProjectDescriptor descriptor) { + public FileFilter createFileFilter() { final JpsCompilerExcludes excludes = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(target.getModule().getProject()).getCompilerExcludes(); + final FileFilter baseFilter = BuilderRegistry.getInstance().getModuleBuilderFileFilter(); return new FileFilter() { @Override public boolean accept(File file) { - return !excludes.isExcluded(file); + return baseFilter.accept(file) && !excludes.isExcluded(file); } }; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceTransformer.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceTransformer.java new file mode 100644 index 000000000000..82efc8ef46e5 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaSourceTransformer.java @@ -0,0 +1,28 @@ +package org.jetbrains.jps.builders.java; + +import java.io.File; +import java.io.IOException; + +/** + * @author Eugene Zhuravlev + * Date: 1/7/13 + */ +public abstract class JavaSourceTransformer { + + public static abstract class TransformError extends IOException { + protected TransformError(String message) { + super(message); + } + + protected TransformError(String message, Throwable cause) { + super(message, cause); + } + + protected TransformError(Throwable cause) { + super(cause); + } + } + + + public abstract CharSequence transform(File sourceFile, CharSequence content) throws TransformError; +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/ResourceRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/ResourceRootDescriptor.java index 949fbc2b4fb4..f3357f0c0c1a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/ResourceRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/ResourceRootDescriptor.java @@ -18,11 +18,11 @@ package org.jetbrains.jps.builders.java; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildRootDescriptor; -import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.ResourcesTarget; import org.jetbrains.jps.model.JpsProject; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.compiler.JpsCompilerExcludes; +import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; import java.io.File; import java.io.FileFilter; @@ -74,14 +74,16 @@ public final class ResourceRootDescriptor extends BuildRootDescriptor { return myPackagePrefix; } + @NotNull @Override - public FileFilter createFileFilter(@NotNull ProjectDescriptor descriptor) { + public FileFilter createFileFilter() { final JpsProject project = myTarget.getModule().getProject(); - final JpsCompilerExcludes excludes = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project).getCompilerExcludes(); + final JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); + final JpsCompilerExcludes excludes = configuration.getCompilerExcludes(); return new FileFilter() { @Override public boolean accept(File file) { - return !excludes.isExcluded(file); + return !excludes.isExcluded(file) && configuration.isResourceFile(file, getRootFile()); } }; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java index d0df19162937..83b1ce253569 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java @@ -80,6 +80,10 @@ public class ClassRepr extends Proto { return myUsages.add(usage); } + public boolean isInterface() { + return (access & Opcodes.ACC_INTERFACE) != 0; + } + public abstract static class Diff extends Difference { public abstract Specifier<TypeRepr.AbstractType> interfaces(); @@ -288,10 +292,6 @@ public class ClassRepr extends Proto { } } - public boolean isAnnotation() { - return (access & Opcodes.ACC_ANNOTATION) > 0; - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java index 0c59a0d0789a..e95258d74823 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java @@ -25,9 +25,6 @@ import java.util.*; * Date: 01.03.11 */ abstract class Difference { - public static boolean isPackageLocal(final int access) { - return (access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0; - } public static boolean weakerAccess(final int me, final int then) { return ((me & Opcodes.ACC_PRIVATE) > 0 && (then & Opcodes.ACC_PRIVATE) == 0) || @@ -35,6 +32,10 @@ abstract class Difference { (isPackageLocal(me) && (then & Opcodes.ACC_PROTECTED) > 0); } + private static boolean isPackageLocal(final int access) { + return (access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0; + } + public static final int NONE = 0; public static final int ACCESS = 1; public static final int TYPE = 2; diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java index 5c402023c165..fe3f28831ea1 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java @@ -695,8 +695,8 @@ public class Mappings { } private static boolean isVisibleIn(final ClassRepr c, final ProtoMember m, final ClassRepr scope) { - final boolean privacy = ((m.access & Opcodes.ACC_PRIVATE) > 0) && c.name != scope.name; - final boolean packageLocality = Difference.isPackageLocal(m.access) && !c.getPackageName().equals(scope.getPackageName()); + final boolean privacy = m.isPrivate() && c.name != scope.name; + final boolean packageLocality = m.isPackageLocal() && !c.getPackageName().equals(scope.getPackageName()); return !privacy && !packageLocality; } @@ -733,13 +733,13 @@ public class Mappings { final Util self = new Util(); // Public branch --- hopeless - if ((member.access & Opcodes.ACC_PUBLIC) > 0) { + if (member.isPublic()) { debug("Public access, switching to a non-incremental mode"); return false; } // Protected branch - if ((member.access & Opcodes.ACC_PROTECTED) > 0) { + if (member.isProtected()) { debug("Protected access, softening non-incremental decision: adding all relevant subclasses for a recompilation"); debug("Root class: ", owner); @@ -1008,16 +1008,14 @@ public class Mappings { Ref<ClassRepr> oldItRef = null; for (final MethodRepr m : added) { debug("Method: ", m.name); - if ((it.access & Opcodes.ACC_INTERFACE) > 0 || - (it.access & Opcodes.ACC_ABSTRACT) > 0 || - (m.access & Opcodes.ACC_ABSTRACT) > 0) { + if (it.isInterface() || it.isAbstract() || m.isAbstract()) { debug("Class is abstract, or is interface, or added method in abstract => affecting all subclasses"); myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false); } TIntHashSet propagated = null; - if ((m.access & Opcodes.ACC_PRIVATE) == 0 && m.name != myInitName) { + if (!m.isPrivate() && m.name != myInitName) { if (oldItRef == null) { oldItRef = new Ref<ClassRepr>(getReprByName(null, it.name)); // lazy init } @@ -1035,7 +1033,7 @@ public class Mappings { } } - if ((m.access & Opcodes.ACC_PRIVATE) == 0) { + if (!m.isPrivate()) { final Collection<Pair<MethodRepr, ClassRepr>> affectedMethods = myFuture.findAllMethodsBySpecificity(m, it); final MethodRepr.Predicate overrides = MethodRepr.equalByJavaRules(m); @@ -1147,7 +1145,7 @@ public class Mappings { for (final Pair<MethodRepr, ClassRepr> overriden : overridenMethods) { final MethodRepr mm = overriden.first; - if (mm == MOCK_METHOD || !mm.myType.equals(m.myType) || !isEmpty(mm.signature) || !isEmpty(m.signature)) { + if (mm == MOCK_METHOD || !mm.myType.equals(m.myType) || !isEmpty(mm.signature) || !isEmpty(m.signature) || m.isMoreAccessibleThan(mm)) { clear = false; break loop; } @@ -1171,7 +1169,7 @@ public class Mappings { } } - if ((m.access & Opcodes.ACC_ABSTRACT) == 0) { + if (!m.isAbstract()) { propagated.forEach(new TIntProcedure() { @Override public boolean execute(int p) { @@ -1199,7 +1197,7 @@ public class Mappings { } visited = true; - allAbstract = ((pp.first.access & Opcodes.ACC_ABSTRACT) > 0) || ((cc.access & Opcodes.ACC_INTERFACE) > 0); + allAbstract = pp.first.isAbstract() || cc.isInterface(); if (!allAbstract) { break; @@ -1345,12 +1343,7 @@ public class Mappings { for (final FieldRepr f : added) { debug("Field: ", f.name); - final boolean fPrivate = (f.access & Opcodes.ACC_PRIVATE) > 0; - final boolean fProtected = (f.access & Opcodes.ACC_PROTECTED) > 0; - final boolean fPublic = (f.access & Opcodes.ACC_PUBLIC) > 0; - final boolean fPLocal = !fPrivate && !fProtected && !fPublic; - - if (!fPrivate) { + if (!f.isPrivate()) { final TIntHashSet subClasses = getAllSubclasses(classRepr.name); subClasses.forEach(new TIntProcedure() { @Override @@ -1394,28 +1387,23 @@ public class Mappings { final FieldRepr ff = p.first; final ClassRepr cc = p.second; - final boolean ffPrivate = (ff.access & Opcodes.ACC_PRIVATE) > 0; - final boolean ffProtected = (ff.access & Opcodes.ACC_PROTECTED) > 0; - final boolean ffPublic = (ff.access & Opcodes.ACC_PUBLIC) > 0; - final boolean ffPLocal = Difference.isPackageLocal(ff.access); - - if (!ffPrivate) { + if (!ff.isPrivate()) { final TIntHashSet propagated = myPresent.propagateFieldAccess(ff.name, cc.name); final Set<UsageRepr.Usage> localUsages = new HashSet<UsageRepr.Usage>(); debug("Affecting usages of overridden field in class ", cc.name); myFuture.affectFieldUsages(ff, propagated, ff.createUsage(myContext, cc.name), localUsages, state.myDependants); - if (fPrivate || (fPublic && (ffPublic || ffPLocal)) || (fProtected && ffProtected) || (fPLocal && ffPLocal)) { - + if (f.isPrivate() || (f.isPublic() && (ff.isPublic() || ff.isPackageLocal())) || (f.isProtected() && ff.isProtected()) || (f.isPackageLocal() && ff.isPackageLocal())) { + // nothing } else { Util.UsageConstraint constaint; - if ((ffProtected && fPublic) || (fProtected && ffPublic) || (ffPLocal && fProtected)) { + if ((ff.isProtected() && f.isPublic()) || (f.isProtected() && ff.isPublic()) || (ff.isPackageLocal() && f.isProtected())) { constaint = myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)); } - else if (ffPublic && ffPLocal) { + else if (ff.isPublic() && ff.isPackageLocal()) { constaint = myFuture.new NegationConstraint(myFuture.new PackageConstraint(cc.getPackageName())); } else { @@ -1449,7 +1437,7 @@ public class Mappings { for (final FieldRepr f : removed) { debug("Field: ", f.name); - if ((f.access & Opcodes.ACC_PRIVATE) == 0 && (f.access & DESPERATE_MASK) == DESPERATE_MASK && f.hasValue()) { + if (!f.isPrivate() && (f.access & DESPERATE_MASK) == DESPERATE_MASK && f.hasValue()) { debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); if (myConstantSearch != null) { myDelayedWorks.addConstantWork(it.name, f, true, false); @@ -1483,7 +1471,7 @@ public class Mappings { debug("Field: ", field.name); - if ((field.access & Opcodes.ACC_PRIVATE) == 0 && (field.access & DESPERATE_MASK) == DESPERATE_MASK) { + if (!field.isPrivate() && (field.access & DESPERATE_MASK) == DESPERATE_MASK) { final int changedModifiers = d.addedModifiers() | d.removedModifiers(); final boolean harmful = (changedModifiers & (Opcodes.ACC_STATIC | Opcodes.ACC_FINAL)) > 0; final boolean accessChanged = (changedModifiers & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) > 0; @@ -1761,13 +1749,35 @@ public class Mappings { debug("End of removed classes processing."); } - private void processAddedClasses(final DiffState state) { + private void processAddedClasses(final DiffState state, File srcFile) { final Collection<ClassRepr> addedClasses = state.myClassDiff.added(); if (addedClasses.isEmpty()) { return; } debug("Processing added classes:"); + + if (!myEasyMode) { + // checking if this newly added class duplicates already existing one + for (ClassRepr c : addedClasses) { + if (!c.isLocal() && !c.isAnonymous() && isEmpty(c.getOuterClassName())) { + final File currentlyMappedTo = myClassToSourceFile.get(c.name); + if (currentlyMappedTo != null && !FileUtil.filesEqual(currentlyMappedTo, srcFile) && currentlyMappedTo.exists()) { + if (myFilter == null || myFilter.accept(currentlyMappedTo)) { + // Same classes from different source files. + // Schedule for recompilation both to make possible 'duplicate sources' error evident + debug("Scheduling for recompilation duplicated sources: ", currentlyMappedTo.getPath() + "; " + srcFile.getPath()); + myAffectedFiles.add(currentlyMappedTo); + myAffectedFiles.add(srcFile); + myCompiledFiles.remove(srcFile); // this will force sending the file to compilation again + return; // do not process this file because it should not be integrated + } + } + break; + } + } + } + for (final ClassRepr c : addedClasses) { debug("Class name: ", c.name); myDelta.addChangedClass(c.name); @@ -1898,7 +1908,7 @@ public class Mappings { } processRemovedClases(state); - processAddedClasses(state); + processAddedClasses(state, fileName); if (!myEasyMode) { calculateAffectedFiles(state); diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Proto.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Proto.java index 7a4351542bde..593128edb982 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Proto.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Proto.java @@ -60,6 +60,64 @@ class Proto implements RW.Savable, Streamable { } } + public final boolean isPublic() { + return (Opcodes.ACC_PUBLIC & access) != 0; + } + + public final boolean isProtected() { + return (Opcodes.ACC_PROTECTED & access) != 0; + } + + public final boolean isPackageLocal() { + return (access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0; + } + + public final boolean isPrivate() { + return (Opcodes.ACC_PRIVATE & access) != 0; + } + + public final boolean isAbstract() { + return (Opcodes.ACC_ABSTRACT & access) != 0; + } + + public final boolean isBridge() { + return (Opcodes.ACC_BRIDGE & access) != 0; + } + + public final boolean isSynthetic() { + return (Opcodes.ACC_SYNTHETIC & access) != 0; + } + + public final boolean isAnnotation() { + return (Opcodes.ACC_ANNOTATION & access) != 0; + } + + public final boolean isFinal() { + return (Opcodes.ACC_FINAL & access) != 0; + } + + public final boolean isStatic() { + return (Opcodes.ACC_STATIC & access) != 0; + } + + /** + * tests if the accessibility of this Proto is less restricted than the accessibility of the given Proto + * @return true means this Proto is less restricted than the proto passed as parameter <br> + * false means this Proto has more restricted access than the parameter Proto or they have equal accessibility + */ + public final boolean isMoreAccessibleThan(Proto anotherProto) { + if (anotherProto.isPrivate()) { + return this.isPackageLocal() || this.isProtected() || this.isPublic(); + } + if (anotherProto.isPackageLocal()) { + return this.isProtected() || this.isPublic(); + } + if (anotherProto.isProtected()) { + return this.isPublic(); + } + return false; + } + public Difference difference(final Proto past) { int diff = Difference.NONE; @@ -96,10 +154,7 @@ class Proto implements RW.Savable, Streamable { @Override public boolean packageLocalOn() { - return ((past.access & Opcodes.ACC_PRIVATE) != 0 || - (past.access & Opcodes.ACC_PUBLIC) != 0 || - (past.access & Opcodes.ACC_PROTECTED) != 0) && - Difference.isPackageLocal(access); + return (past.isPrivate() || past.isPublic() || past.isProtected()) && Proto.this.isPackageLocal(); } @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java index 03a527fdb2d5..034e85120859 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java @@ -31,6 +31,7 @@ import org.jboss.netty.util.Version; import org.jetbrains.annotations.Nullable; import org.jetbrains.asm4.ClassVisitor; import org.jetbrains.asm4.ClassWriter; +import org.jetbrains.jps.builders.java.JavaSourceTransformer; import org.jetbrains.jps.javac.JavacServer; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.impl.JpsModelImpl; @@ -189,6 +190,12 @@ public class ClasspathBootstrap { } } + final Class<JavaSourceTransformer> transformerClass = JavaSourceTransformer.class; + final ServiceLoader<JavaSourceTransformer> loader = ServiceLoader.load(transformerClass, transformerClass.getClassLoader()); + for (JavaSourceTransformer t : loader) { + cp.add(getResourceFile(t.getClass())); + } + return new ArrayList<File>(cp); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java index 218a1013bcf2..7b2436ee58ac 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java @@ -16,10 +16,16 @@ package org.jetbrains.jps.incremental; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildTargetType; import org.jetbrains.jps.service.JpsServiceManager; +import java.io.File; +import java.io.FileFilter; import java.util.*; /** @@ -34,6 +40,7 @@ public class BuilderRegistry { private final Map<BuilderCategory, List<ModuleLevelBuilder>> myModuleLevelBuilders = new HashMap<BuilderCategory, List<ModuleLevelBuilder>>(); private final List<TargetBuilder<?,?>> myTargetBuilders = new ArrayList<TargetBuilder<?,?>>(); private final Map<String, BuildTargetType<?>> myTargetTypes = new LinkedHashMap<String, BuildTargetType<?>>(); + private final FileFilter myModuleBuilderFileFilter; public static BuilderRegistry getInstance() { return Holder.ourInstance; @@ -44,10 +51,19 @@ public class BuilderRegistry { myModuleLevelBuilders.put(category, new ArrayList<ModuleLevelBuilder>()); } + Set<String> compilableFileExtensions = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); for (BuilderService service : JpsServiceManager.getInstance().getExtensions(BuilderService.class)) { myTargetBuilders.addAll(service.createBuilders()); final List<? extends ModuleLevelBuilder> moduleLevelBuilders = service.createModuleLevelBuilders(); for (ModuleLevelBuilder builder : moduleLevelBuilders) { + List<String> extensions = builder.getCompilableFileExtensions(); + if (extensions == null) { + LOG.info(builder.getClass().getName() + " builder returns 'null' from 'getCompilableFileExtensions' method so files for module-level builders won't be filtered"); + compilableFileExtensions = null; + } + else if (compilableFileExtensions != null) { + compilableFileExtensions.addAll(extensions); + } myModuleLevelBuilders.get(builder.getCategory()).add(builder); } for (BuildTargetType<?> type : service.getTargetTypes()) { @@ -58,6 +74,18 @@ public class BuilderRegistry { } } } + if (compilableFileExtensions == null) { + myModuleBuilderFileFilter = FileUtilRt.ALL_FILES; + } + else { + final Set<String> finalCompilableFileExtensions = compilableFileExtensions; + myModuleBuilderFileFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return finalCompilableFileExtensions.contains(FileUtilRt.getExtension(file.getName())); + } + }; + } } @Nullable @@ -65,6 +93,11 @@ public class BuilderRegistry { return myTargetTypes.get(typeId); } + @NotNull + public FileFilter getModuleBuilderFileFilter() { + return myModuleBuilderFileFilter; + } + public Collection<BuildTargetType<?>> getTargetTypes() { return myTargetTypes.values(); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java index b38230e862d9..ef74b261b00c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java @@ -23,11 +23,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.BuildRootIndex; +import org.jetbrains.jps.builders.java.JavaBuilderExtension; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import org.jetbrains.jps.model.JpsEncodingConfigurationService; import org.jetbrains.jps.model.JpsEncodingProjectConfiguration; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.service.JpsServiceManager; import org.jetbrains.jps.util.JpsPathUtil; import java.io.File; @@ -58,12 +60,12 @@ public class CompilerEncodingConfiguration { private Map<JpsModule, Set<String>> computeModuleCharsetMap() { final Map<JpsModule, Set<String>> map = new THashMap<JpsModule, Set<String>>(); - final List<ModuleLevelBuilder> builders = BuilderRegistry.getInstance().getModuleLevelBuilders(); + final Iterable<JavaBuilderExtension> builderExtensions = JpsServiceManager.getInstance().getExtensions(JavaBuilderExtension.class); for (Map.Entry<String, String> entry : myUrlToCharset.entrySet()) { final String fileUrl = entry.getKey(); final String charset = entry.getValue(); File file = JpsPathUtil.urlToFile(fileUrl); - if (charset == null || (!file.isDirectory() && !shouldHonorEncodingForCompilation(builders, file))) continue; + if (charset == null || (!file.isDirectory() && !shouldHonorEncodingForCompilation(builderExtensions, file))) continue; final JavaSourceRootDescriptor rootDescriptor = myRootsIndex.findJavaRootDescriptor(null, file); if (rootDescriptor == null) continue; @@ -97,9 +99,9 @@ public class CompilerEncodingConfiguration { return map; } - private static boolean shouldHonorEncodingForCompilation(List<ModuleLevelBuilder> builders, File file) { - for (ModuleLevelBuilder builder : builders) { - if (builder.shouldHonorFileEncodingForCompilation(file)) { + private static boolean shouldHonorEncodingForCompilation(Iterable<JavaBuilderExtension> builders, File file) { + for (JavaBuilderExtension extension : builders) { + if (extension.shouldHonorFileEncodingForCompilation(file)) { return true; } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java index 3fd4fc9d0099..545cdc762c04 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java @@ -15,7 +15,6 @@ */ package org.jetbrains.jps.incremental; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import gnu.trove.THashSet; @@ -23,6 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.BuildRootDescriptor; +import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.BuildTarget; import org.jetbrains.jps.builders.FileProcessor; import org.jetbrains.jps.builders.impl.BuildTargetChunk; @@ -44,7 +44,7 @@ import java.util.Set; * Date: 7/8/12 */ public class FSOperations { - public static final Key<Set<File>> ALL_OUTPUTS_KEY = Key.create("_all_project_output_dirs_"); + public static final GlobalContextKey<Set<File>> ALL_OUTPUTS_KEY = GlobalContextKey.create("_all_project_output_dirs_"); public static void markDirty(CompileContext context, final File file) throws IOException { final JavaSourceRootDescriptor rd = context.getProjectDescriptor().getBuildRootIndex().findJavaRootDescriptor(context, file); @@ -161,19 +161,17 @@ public class FSOperations { @NotNull final Timestamps tsStorage, final boolean forceDirty, @Nullable Set<File> currentFiles, @Nullable FileFilter filter, @NotNull FSCache fsCache) throws IOException { - if (context.getProjectDescriptor().getIgnoredFileIndex().isIgnored(file.getName())) { - return; - } + BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex(); final File[] children = fsCache.getChildren(file); if (children != null) { // is directory - if (children.length > 0 && !rd.getExcludedRoots().contains(file)) { + if (children.length > 0 && rootIndex.isDirectoryAccepted(file, rd)) { for (File child : children) { traverseRecursively(context, rd, child, tsStorage, forceDirty, currentFiles, filter, fsCache); } } } else { // is file - if (filter == null || filter.accept(file)) { + if (rootIndex.isFileAccepted(file, rd) && (filter == null || filter.accept(file))) { boolean markDirty = forceDirty; if (!markDirty) { markDirty = tsStorage.getStamp(file, rd.getTarget()) != FileSystemUtil.lastModified(file); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/GlobalContextKey.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/GlobalContextKey.java new file mode 100644 index 000000000000..e5ec324c6e02 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/GlobalContextKey.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2013 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.incremental; + +import com.intellij.openapi.util.Key; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + This is type of the key for data that must be visible to all threads + */ +public final class GlobalContextKey<T> extends Key<T> { + public GlobalContextKey(@NotNull @NonNls String name) { + super(name); + } + + public static <T> GlobalContextKey<T> create(@NotNull @NonNls String name) { + return new GlobalContextKey<T>(name); + } + +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java index f2cf3f0c52fe..e16c78813312 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -16,7 +16,6 @@ package org.jetbrains.jps.incremental; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.LowMemoryWatcher; import com.intellij.openapi.util.UserDataHolder; import com.intellij.openapi.util.UserDataHolderBase; @@ -80,7 +79,7 @@ public class IncProjectBuilder { private static final String CLASSPATH_INDEX_FINE_NAME = "classpath.index"; private static final boolean GENERATE_CLASSPATH_INDEX = Boolean.parseBoolean(System.getProperty(GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION, "false")); - private static final Key<Set<BuildTarget<?>>> TARGET_WITH_CLEARED_OUTPUT = Key.create("_targets_with_cleared_output_"); + private static final GlobalContextKey<Set<BuildTarget<?>>> TARGET_WITH_CLEARED_OUTPUT = GlobalContextKey.create("_targets_with_cleared_output_"); private static final int MAX_BUILDER_THREADS; static { int maxThreads = 6; @@ -463,10 +462,16 @@ public class IncProjectBuilder { // do not delete output root itself to avoid lots of unnecessary "roots_changed" events in IDEA final File[] children = outputRoot.listFiles(); if (children != null) { - filesToDelete.addAll(Arrays.asList(children)); + for (File child : children) { + if (!child.delete()) { + filesToDelete.add(child); + } + } } - else if (outputRoot.isFile()) { - filesToDelete.add(outputRoot); + else { // the output root must be file + if (!outputRoot.delete()) { + filesToDelete.add(outputRoot); + } } registerTargetsWithClearedOutput(context, entry.getValue()); } @@ -1052,14 +1057,6 @@ public class IncProjectBuilder { BuildOperations.markTargetsUpToDate(context, chunk); } - private static final Set<Key> GLOBAL_CONTEXT_KEYS = new HashSet<Key>(); - static { - // keys for data that must be visible to all threads - GLOBAL_CONTEXT_KEYS.add(ExternalJavacDescriptor.KEY); - GLOBAL_CONTEXT_KEYS.add(FSOperations.ALL_OUTPUTS_KEY); - GLOBAL_CONTEXT_KEYS.add(TARGET_WITH_CLEARED_OUTPUT); - } - private static CompileContext createContextWrapper(final CompileContext delegate) { final ClassLoader loader = delegate.getClass().getClassLoader(); final UserDataHolderBase localDataHolder = new UserDataHolderBase(); @@ -1072,8 +1069,7 @@ public class IncProjectBuilder { final Class<?> declaringClass = method.getDeclaringClass(); if (dataHolderInterface.equals(declaringClass)) { final Object firstArgument = args[0]; - final boolean isGlobalContextKey = firstArgument instanceof Key && GLOBAL_CONTEXT_KEYS.contains((Key)firstArgument); - if (!isGlobalContextKey) { + if (!(firstArgument instanceof GlobalContextKey)) { final boolean isWriteOperation = args.length == 2 /*&& void.class.equals(method.getReturnType())*/; if (isWriteOperation) { if (args[1] == null) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java index 07095333470c..394ce12e8042 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java @@ -25,6 +25,7 @@ import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import java.io.File; import java.io.IOException; import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -64,10 +65,22 @@ public abstract class ModuleLevelBuilder extends Builder { OutputConsumer outputConsumer) throws ProjectBuildException, IOException; + /** + * @deprecated use {@link org.jetbrains.jps.builders.java.JavaBuilderExtension#shouldHonorFileEncodingForCompilation(java.io.File)} instead + */ public boolean shouldHonorFileEncodingForCompilation(File file) { return false; } + /** + * <strong>DO NOT RETURN {@code null}</strong> from implementation of this method. If some of builders returns {@code null} no filtering + * will be performed for compatibility reasons. + * @return list of extensions (without dot) of files which can be compiled by the builder + */ + public List<String> getCompilableFileExtensions() { + return null; + } + public final BuilderCategory getCategory() { return myCategory; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ResourcesTarget.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ResourcesTarget.java index 13ee713b2617..15e9a48e2c43 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ResourcesTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ResourcesTarget.java @@ -17,7 +17,6 @@ package org.jetbrains.jps.incremental; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.SmartList; -import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildRootIndex; @@ -35,7 +34,6 @@ import org.jetbrains.jps.model.JpsSimpleElement; import org.jetbrains.jps.model.java.JavaSourceRootProperties; import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.java.JpsJavaExtensionService; -import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot; import org.jetbrains.jps.service.JpsServiceManager; @@ -96,8 +94,6 @@ public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDesc JavaSourceRootType type = isTests() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE; Iterable<ExcludedJavaSourceRootProvider> excludedRootProviders = JpsServiceManager.getInstance().getExtensions(ExcludedJavaSourceRootProvider.class); - final THashSet<File> addedRoots = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY); - roots_loop: for (JpsTypedModuleSourceRoot<JpsSimpleElement<JavaSourceRootProperties>> sourceRoot : myModule.getSourceRoots(type)) { for (ExcludedJavaSourceRootProvider provider : excludedRootProviders) { @@ -108,7 +104,6 @@ public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDesc final String packagePrefix = sourceRoot.getProperties().getData().getPackagePrefix(); final File rootFile = sourceRoot.getFile(); roots.add(new ResourceRootDescriptor(rootFile, this, false, packagePrefix, computeRootExcludes(rootFile, index))); - addedRoots.add(rootFile); } return roots; @@ -122,12 +117,7 @@ public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDesc @Override public void writeConfiguration(PrintWriter out, BuildDataPaths dataPaths, BuildRootIndex buildRootIndex) { - final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(getModule().getProject()); int fingerprint = 0; - final List<String> patterns = config.getResourcePatterns(); - for (String pattern : patterns) { - fingerprint += pattern.hashCode(); - } final List<ResourceRootDescriptor> roots = buildRootIndex.getTargetRoots(this, null); for (ResourceRootDescriptor root : roots) { fingerprint += FileUtil.fileHashCode(root.getRootFile()); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactOutputToSourceMapping.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactOutputToSourceMapping.java index 067a383acf40..572433552d65 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactOutputToSourceMapping.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactOutputToSourceMapping.java @@ -15,14 +15,17 @@ */ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.SmartList; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.IOUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.incremental.storage.AbstractStateStorage; import org.jetbrains.jps.incremental.storage.PathStringDescriptor; import java.io.*; +import java.util.Collections; import java.util.List; /** @@ -35,12 +38,37 @@ public class ArtifactOutputToSourceMapping extends AbstractStateStorage<String, super(storePath, new PathStringDescriptor(), new SourcePathListExternalizer()); } + @Override + public void update(String path, @Nullable List<SourcePathAndRootIndex> state) throws IOException { + super.update(FileUtil.toSystemIndependentName(path), state); + } + + @Override + public void appendData(String path, List<SourcePathAndRootIndex> data) throws IOException { + super.appendData(FileUtil.toSystemIndependentName(path), data); + } + + public void appendData(String outputPath, int rootIndex, String sourcePath) throws IOException { + super.appendData(outputPath, Collections.singletonList(new SourcePathAndRootIndex(sourcePath, rootIndex))); + } + + @Override + public void remove(String path) throws IOException { + super.remove(FileUtil.toSystemIndependentName(path)); + } + + @Nullable + @Override + public List<SourcePathAndRootIndex> getState(String path) throws IOException { + return super.getState(FileUtil.toSystemIndependentName(path)); + } + public static class SourcePathAndRootIndex { private final String myPath; private final int myRootIndex; - public SourcePathAndRootIndex(String path, int rootIndex) { - myPath = path; + private SourcePathAndRootIndex(String path, int rootIndex) { + myPath = FileUtil.toSystemIndependentName(path); myRootIndex = rootIndex; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java index 207fa138fd6d..6dc56ef587c6 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -47,6 +48,7 @@ import java.util.*; * @author nik */ public class IncArtifactBuilder extends TargetBuilder<ArtifactRootDescriptor, ArtifactBuildTarget> { + private static final Logger LOG = Logger.getInstance(IncArtifactBuilder.class); public static final String BUILDER_NAME = "Artifacts builder"; public IncArtifactBuilder() { @@ -77,7 +79,10 @@ public class IncArtifactBuilder extends TargetBuilder<ArtifactRootDescriptor, Ar try { final Collection<String> deletedFiles = holder.getRemovedFiles(target); - context.processMessage(new ProgressMessage("Building artifact '" + artifact.getName() + "'...")); + String messageText = "Building artifact '" + artifact.getName() + "'..."; + context.processMessage(new ProgressMessage(messageText)); + LOG.debug(messageText); + runArtifactTasks(context, target.getArtifact(), ArtifactBuildTaskProvider.ArtifactBuildPhase.PRE_PROCESSING); final SourceToOutputMapping srcOutMapping = pd.dataManager.getSourceToOutputMap(target); final ArtifactOutputToSourceMapping outSrcMapping = pd.dataManager.getStorage(target, ArtifactOutToSourceStorageProvider.INSTANCE); @@ -142,6 +147,12 @@ public class IncArtifactBuilder extends TargetBuilder<ArtifactRootDescriptor, Ar if (sourcePaths == null) continue; for (String sourcePath : sourcePaths) { + if (!descriptor.getFilter().shouldBeCopied(sourcePath, pd)) { + if (LOG.isDebugEnabled()) { + LOG.debug("File " + sourcePath + " will be skipped because it isn't accepted by filter"); + } + continue; + } DestinationInfo destination = descriptor.getDestinationInfo(); if (destination instanceof ExplodedDestinationInfo) { descriptor.copyFromRoot(sourcePath, descriptor.getRootIndex(), destination.getOutputPath(), context, @@ -210,10 +221,13 @@ public class IncArtifactBuilder extends TargetBuilder<ArtifactRootDescriptor, Ar boolean deleted = deletedPaths.contains(filePath); if (!deleted) { - deleted = FileUtil.delete(new File(FileUtil.toSystemDependentName(filePath))); + deleted = FileUtil.delete(new File(filePath)); } if (deleted) { + if (LOG.isDebugEnabled()) { + LOG.debug("Outdated output file deleted: " + filePath); + } outSrcMapping.remove(filePath); deletedPaths.add(filePath); for (String sourcePath : filesToDelete.get(filePath)) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java index 2916a6b6b903..ba43cc7ea05e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java @@ -169,8 +169,7 @@ public class JarsBuilder { else { final String filePath = FileUtil.toSystemIndependentName(descriptor.getRootFile().getAbsolutePath()); packedFilePaths.add(filePath); - myOutSrcMapping.appendData(targetJarPath, Collections - .singletonList(new ArtifactOutputToSourceMapping.SourcePathAndRootIndex(filePath, rootIndex))); + myOutSrcMapping.appendData(targetJarPath, rootIndex, filePath); extractFileAndAddToJar(jarOutputStream, (JarBasedArtifactRootDescriptor)descriptor, relativePath, writtenPaths); } } @@ -329,7 +328,7 @@ public class JarsBuilder { List<String> packedFilePaths, int rootIndex) throws IOException { final String filePath = FileUtil.toSystemIndependentName(file.getAbsolutePath()); - if (!filter.accept(filePath, myContext.getProjectDescriptor())) { + if (!filter.accept(filePath) || !filter.shouldBeCopied(filePath, myContext.getProjectDescriptor())) { return; } @@ -350,7 +349,7 @@ public class JarsBuilder { final boolean added = ZipUtil.addFileToZip(jarOutputStream, file, relativePath, writtenItemRelativePaths, null); if (rootIndex != -1) { - myOutSrcMapping.appendData(targetJarPath, Collections.singletonList(new ArtifactOutputToSourceMapping.SourcePathAndRootIndex(filePath, rootIndex))); + myOutSrcMapping.appendData(targetJarPath, rootIndex, filePath); if (added) { packedFilePaths.add(filePath); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java index 89c64aefa236..ee1ee1798f55 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java @@ -119,8 +119,8 @@ public abstract class ArtifactCompilerInstructionCreatorBase implements Artifact } @Override - public boolean accept(@NotNull String fullFilePath, ProjectDescriptor projectDescriptor) throws IOException { - if (myBaseFilter != null && !myBaseFilter.accept(fullFilePath, projectDescriptor)) return false; + public boolean accept(@NotNull String fullFilePath) { + if (myBaseFilter != null && !myBaseFilter.accept(fullFilePath)) return false; if (myIgnoredFileIndex.isIgnored(PathUtilRt.getFileName(fullFilePath))) { return false; @@ -134,5 +134,10 @@ public abstract class ArtifactCompilerInstructionCreatorBase implements Artifact } return true; } + + @Override + public boolean shouldBeCopied(@NotNull String fullFilePath, ProjectDescriptor projectDescriptor) throws IOException { + return myBaseFilter == null || myBaseFilter.shouldBeCopied(fullFilePath, projectDescriptor); + } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java index 06b1bcd7eabc..ae9993d7214e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java @@ -80,6 +80,6 @@ public class ArtifactInstructionsBuilderImpl implements ArtifactInstructionsBuil public JarBasedArtifactRootDescriptor createJarBasedRoot(@NotNull File jarFile, @NotNull String pathInJar, @NotNull SourceFileFilter filter, final DestinationInfo destinationInfo) { - return new JarBasedArtifactRootDescriptor(jarFile, pathInJar, filter, myRootIndex, myBuildTarget, destinationInfo); + return new JarBasedArtifactRootDescriptor(jarFile, pathInJar, filter, myRootIndex++, myBuildTarget, destinationInfo); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java index 5fe2c1c947a3..24849efae998 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java @@ -18,7 +18,6 @@ package org.jetbrains.jps.incremental.artifacts.instructions; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildOutputConsumer; import org.jetbrains.jps.builders.BuildRootDescriptor; -import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ProjectBuildException; import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTarget; @@ -33,12 +32,6 @@ import java.io.PrintWriter; * @author nik */ public abstract class ArtifactRootDescriptor extends BuildRootDescriptor { - private static final FileFilter ALL_FILES_FILTER = new FileFilter() { - @Override - public boolean accept(File file) { - return true; - } - }; protected final File myRoot; private final SourceFileFilter myFilter; private final int myRootIndex; @@ -77,17 +70,13 @@ public abstract class ArtifactRootDescriptor extends BuildRootDescriptor { return myTarget; } + @NotNull @Override - public FileFilter createFileFilter(@NotNull final ProjectDescriptor descriptor) { + public FileFilter createFileFilter() { return new FileFilter() { @Override public boolean accept(File pathname) { - try { - return myFilter.accept(pathname.getAbsolutePath(), descriptor); - } - catch (IOException e) { - throw new RuntimeException(e); - } + return myFilter.accept(pathname.getAbsolutePath()); } }; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java index ee42cc4da78b..71eb66d1155b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.incremental.artifacts.instructions; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildOutputConsumer; @@ -34,6 +35,7 @@ import java.util.Collections; * @author nik */ public class FileBasedArtifactRootDescriptor extends ArtifactRootDescriptor { + private static final Logger LOG = Logger.getInstance(FileBasedArtifactRootDescriptor.class); public FileBasedArtifactRootDescriptor(@NotNull File file, @NotNull SourceFileFilter filter, int index, @@ -79,6 +81,9 @@ public class FileBasedArtifactRootDescriptor extends ArtifactRootDescriptor { FileUtil.copyContent(file, targetFile); outputConsumer.registerOutputFile(targetFile, Collections.singletonList(filePath)); } - outSrcMapping.appendData(targetPath, Collections.singletonList(new ArtifactOutputToSourceMapping.SourcePathAndRootIndex(filePath, rootIndex))); + else if (LOG.isDebugEnabled()) { + LOG.debug("Target path " + targetPath + " is already registered so " + filePath + " won't be copied"); + } + outSrcMapping.appendData(targetPath, rootIndex, filePath); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java index 7c31153b7058..8485d28901c2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java @@ -91,7 +91,7 @@ public class JarBasedArtifactRootDescriptor extends ArtifactRootDescriptor { processEntries(new EntryProcessor() { @Override public void process(@Nullable InputStream inputStream, @NotNull String relativePath, ZipEntry entry) throws IOException { - final String fullOutputPath = FileUtil.toSystemDependentName(JpsArtifactPathUtil.appendToPath(outputPath, relativePath)); + final String fullOutputPath = JpsArtifactPathUtil.appendToPath(outputPath, relativePath); final File outputFile = new File(fullOutputPath); FileUtil.createParentDirs(outputFile); @@ -99,7 +99,6 @@ public class JarBasedArtifactRootDescriptor extends ArtifactRootDescriptor { outputFile.mkdir(); } else { - String fullSourcePath = filePath + JarPathUtil.JAR_SEPARATOR + relativePath; if (outSrcMapping.getState(fullOutputPath) == null) { final BufferedInputStream from = new BufferedInputStream(inputStream); final BufferedOutputStream to = new BufferedOutputStream(new FileOutputStream(outputFile)); @@ -112,7 +111,7 @@ public class JarBasedArtifactRootDescriptor extends ArtifactRootDescriptor { } outputConsumer.registerOutputFile(outputFile, Collections.singletonList(filePath)); } - outSrcMapping.appendData(fullOutputPath, Collections.singletonList(new ArtifactOutputToSourceMapping.SourcePathAndRootIndex(fullSourcePath, rootIndex))); + outSrcMapping.appendData(fullOutputPath, rootIndex, filePath); } } }); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SourceFileFilter.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SourceFileFilter.java index 60964afdf726..2e707c712ec9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SourceFileFilter.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SourceFileFilter.java @@ -26,10 +26,20 @@ import java.io.IOException; public abstract class SourceFileFilter { public static final SourceFileFilter ALL = new SourceFileFilter() { @Override - public boolean accept(@NotNull String fullFilePath, ProjectDescriptor projectDescriptor) { + public boolean accept(@NotNull String fullFilePath) { + return true; + } + + @Override + public boolean shouldBeCopied(@NotNull String fullFilePath, ProjectDescriptor projectDescriptor) throws IOException { return true; } }; - public abstract boolean accept(@NotNull String fullFilePath, ProjectDescriptor projectDescriptor) throws IOException; + public abstract boolean accept(@NotNull String fullFilePath); + + /** + * This additional check is performed during the build phase so this method can use caches generated by previously processed targets + */ + public abstract boolean shouldBeCopied(@NotNull String fullFilePath, ProjectDescriptor projectDescriptor) throws IOException; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java index a2942a64d348..e3d974cf5f0e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java @@ -22,7 +22,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.BuildRootDescriptor; -import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.BuildTarget; import org.jetbrains.jps.builders.FileProcessor; import org.jetbrains.jps.builders.impl.BuildTargetChunk; @@ -33,7 +32,6 @@ import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.incremental.storage.Timestamps; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.util.Collections; import java.util.Map; @@ -140,15 +138,13 @@ public class BuildFSState extends FSState { public <R extends BuildRootDescriptor, T extends BuildTarget<R>> boolean processFilesToRecompile(CompileContext context, final T target, final FileProcessor<R, T> processor) throws IOException { final Map<BuildRootDescriptor, Set<File>> data = getSourcesToRecompile(context, target); - BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex(); final CompileScope scope = context.getScope(); synchronized (data) { for (Map.Entry<BuildRootDescriptor, Set<File>> entry : data.entrySet()) { //noinspection unchecked R root = (R)entry.getKey(); - FileFilter filter = rootIndex.getRootFilter(root, context.getProjectDescriptor()); for (File file : entry.getValue()) { - if (!scope.isAffected(target, file) || !filter.accept(file)) { + if (!scope.isAffected(target, file)) { continue; } if (!processor.apply(target, file, root)) { @@ -168,35 +164,29 @@ public class BuildFSState extends FSState { final FilesDelta delta = getDelta(rd.getTarget()); final Set<File> files = delta.clearRecompile(rd); if (files != null) { - FileFilter filter = context.getProjectDescriptor().getBuildRootIndex().getRootFilter(rd, context.getProjectDescriptor()); CompileScope scope = context.getScope(); final long compilationStartStamp = context.getCompilationStartStamp(); for (File file : files) { - if (filter.accept(file)) { - if (scope.isAffected(rd.getTarget(), file)) { - final long currentFileStamp = FileSystemUtil.lastModified(file); - if (!rd.isGenerated() && (currentFileStamp > compilationStartStamp || getEventRegistrationStamp(file) > compilationStartStamp)) { - // if the file was modified after the compilation had started, - // do not save the stamp considering file dirty - if (Utils.IS_TEST_MODE) { - LOG.info("Timestamp after compilation started; marking dirty again: " + file.getPath()); - } - delta.markRecompile(rd, file); - } - else { - marked = true; - stamps.saveStamp(file, rd.getTarget(), currentFileStamp); - } - } - else { + if (scope.isAffected(rd.getTarget(), file)) { + final long currentFileStamp = FileSystemUtil.lastModified(file); + if (!rd.isGenerated() && (currentFileStamp > compilationStartStamp || getEventRegistrationStamp(file) > compilationStartStamp)) { + // if the file was modified after the compilation had started, + // do not save the stamp considering file dirty if (Utils.IS_TEST_MODE) { - LOG.info("Not affected by compile scope; marking dirty again: " + file.getPath()); + LOG.info("Timestamp after compilation started; marking dirty again: " + file.getPath()); } delta.markRecompile(rd, file); } + else { + marked = true; + stamps.saveStamp(file, rd.getTarget(), currentFileStamp); + } } else { - stamps.removeStamp(file, rd.getTarget()); + if (Utils.IS_TEST_MODE) { + LOG.info("Not affected by compile scope; marking dirty again: " + file.getPath()); + } + delta.markRecompile(rd, file); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java index 79c6ab127ea7..689db8ac814f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java @@ -134,7 +134,8 @@ final class FilesDelta { isMarkedDeleted = !myDeletedPaths.isEmpty() && myDeletedPaths.contains(FileUtil.toCanonicalPath(file.getPath())); } if (!isMarkedDeleted) { - return _addToRecompiled(root, file); + _addToRecompiled(root, file); + return true; } return false; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java index 8131cfd62739..092b002f2384 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/instrumentation/ClassProcessingBuilder.java @@ -38,6 +38,8 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * @author Eugene Zhuravlev @@ -98,6 +100,11 @@ public abstract class ClassProcessingBuilder extends ModuleLevelBuilder { return exitCode; } + @Override + public List<String> getCompilableFileExtensions() { + return Collections.emptyList(); + } + protected abstract ExitCode performBuild(CompileContext context, ModuleChunk chunk, InstrumentationClassFinder finder, OutputConsumer outputConsumer); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java index 63e468da0af4..b8882decdf4d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java @@ -16,7 +16,7 @@ package org.jetbrains.jps.incremental.java; import com.intellij.execution.process.BaseOSProcessHandler; -import com.intellij.openapi.util.Key; +import org.jetbrains.jps.incremental.GlobalContextKey; import org.jetbrains.jps.javac.JavacServerClient; /** @@ -24,7 +24,7 @@ import org.jetbrains.jps.javac.JavacServerClient; * Date: 1/24/12 */ public class ExternalJavacDescriptor { - public static final Key<ExternalJavacDescriptor> KEY = Key.create("_external_javac_descriptor_"); + public static final GlobalContextKey<ExternalJavacDescriptor> KEY = GlobalContextKey.create("_external_javac_descriptor_"); public final BaseOSProcessHandler process; public final JavacServerClient client; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index b4870b523ff3..46921cf25856 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -36,6 +36,7 @@ import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.builders.FileProcessor; +import org.jetbrains.jps.builders.java.JavaBuilderExtension; import org.jetbrains.jps.builders.java.JavaBuilderUtil; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import org.jetbrains.jps.builders.java.dependencyView.Callbacks; @@ -55,6 +56,8 @@ import org.jetbrains.jps.model.java.LanguageLevel; import org.jetbrains.jps.model.java.compiler.*; import org.jetbrains.jps.model.library.sdk.JpsSdk; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.module.JpsModuleType; +import org.jetbrains.jps.service.JpsServiceManager; import javax.tools.*; import java.io.*; @@ -71,7 +74,8 @@ import java.util.concurrent.atomic.AtomicReference; public class JavaBuilder extends ModuleLevelBuilder { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.java.JavaBuilder"); public static final String BUILDER_NAME = "java"; - private static final String JAVA_EXTENSION = ".java"; + private static final String JAVA_EXTENSION = "java"; + private static final String DOT_JAVA_EXTENSION = "." + JAVA_EXTENSION; public static final boolean USE_EMBEDDED_JAVAC = System.getProperty(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null; private static final Key<Integer> JAVA_COMPILER_VERSION_KEY = Key.create("_java_compiler_version_"); private static final Key<Boolean> IS_ENABLED = Key.create("_java_compiler_enabled_"); @@ -84,21 +88,28 @@ public class JavaBuilder extends ModuleLevelBuilder { "-g", "-deprecation", "-nowarn", "-verbose", "-proc:none", "-proc:only", "-proceedOnError" )); - private static final FileFilter JAVA_SOURCES_FILTER = + public static final FileFilter JAVA_SOURCES_FILTER = SystemInfo.isFileSystemCaseSensitive? new FileFilter() { public boolean accept(File file) { - return file.getPath().endsWith(JAVA_EXTENSION); + return file.getPath().endsWith(DOT_JAVA_EXTENSION); } } : new FileFilter() { public boolean accept(File file) { - return StringUtil.endsWithIgnoreCase(file.getPath(), JAVA_EXTENSION); + return StringUtil.endsWithIgnoreCase(file.getPath(), DOT_JAVA_EXTENSION); } }; private final Executor myTaskRunner; private static final List<ClassPostProcessor> ourClassProcessors = new ArrayList<ClassPostProcessor>(); + private static final Set<JpsModuleType<?>> ourCompilableModuleTypes; + static { + ourCompilableModuleTypes = new HashSet<JpsModuleType<?>>(); + for (JavaBuilderExtension extension : JpsServiceManager.getInstance().getExtensions(JavaBuilderExtension.class)) { + ourCompilableModuleTypes.addAll(extension.getCompilableModuleTypes()); + } + } public static void registerClassPostProcessor(ClassPostProcessor processor) { ourClassProcessors.add(processor); @@ -136,6 +147,11 @@ public class JavaBuilder extends ModuleLevelBuilder { COMPILER_VERSION_INFO.set(context, new AtomicReference<String>(messageText)); } + @Override + public List<String> getCompilableFileExtensions() { + return Collections.singletonList(JAVA_EXTENSION); + } + public ExitCode build(final CompileContext context, final ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, @@ -144,12 +160,12 @@ public class JavaBuilder extends ModuleLevelBuilder { return ExitCode.NOTHING_DONE; } try { - final Map<File, ModuleBuildTarget> filesToCompile = new THashMap<File, ModuleBuildTarget>(FileUtil.FILE_HASHING_STRATEGY); + final Set<File> filesToCompile = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY); dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() { public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor descriptor) throws IOException { - if (JAVA_SOURCES_FILTER.accept(file)) { - filesToCompile.put(file, target); + if (JAVA_SOURCES_FILTER.accept(file) && ourCompilableModuleTypes.contains(target.getModule().getModuleType())) { + filesToCompile.add(file); } return true; } @@ -159,12 +175,12 @@ public class JavaBuilder extends ModuleLevelBuilder { final ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger(); if (logger.isEnabled()) { if (filesToCompile.size() > 0) { - logger.logCompiledFiles(filesToCompile.keySet(), BUILDER_NAME, "Compiling files:"); + logger.logCompiledFiles(filesToCompile, BUILDER_NAME, "Compiling files:"); } } } - return compile(context, chunk, dirtyFilesHolder, filesToCompile.keySet(), outputConsumer); + return compile(context, chunk, dirtyFilesHolder, filesToCompile, outputConsumer); } catch (ProjectBuildException e) { throw e; @@ -187,12 +203,6 @@ public class JavaBuilder extends ModuleLevelBuilder { } } - @Override - public boolean shouldHonorFileEncodingForCompilation(File file) { - return JAVA_SOURCES_FILTER.accept(file); - } - - private ExitCode compile(final CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, @@ -803,9 +813,11 @@ public class JavaBuilder extends ModuleLevelBuilder { default: kind = BuildMessage.Kind.INFO; } - final JavaFileObject source = diagnostic.getSource(); File sourceFile = null; try { + // for eclipse compiler just an attempt to call getSource() may lead to an NPE, + // so calling this method under try/catch to avoid induced compiler errors + final JavaFileObject source = diagnostic.getSource(); sourceFile = source != null ? Utils.convertToFile(source.toUri()) : null; } catch (Exception e) { @@ -845,6 +857,20 @@ public class JavaBuilder extends ModuleLevelBuilder { } public void save(@NotNull final OutputFileObject fileObject) { + if (JavaFileObject.Kind.CLASS != fileObject.getKind()) { + // generated sources or resources must be saved synchronously, because some compilers (e.g. eclipse) + // may want to read generated text for further compilation + try { + final BinaryContent content = fileObject.getContent(); + if (content != null) { + content.saveToFile(fileObject.getFile()); + } + } + catch (IOException e) { + myContext.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage())); + } + } + submitAsyncTask(myContext, new Runnable() { public void run() { try { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/OutputFilesSink.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/OutputFilesSink.java index 9508b86b842f..29ca799bb01c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/OutputFilesSink.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/OutputFilesSink.java @@ -15,7 +15,6 @@ */ package org.jetbrains.jps.incremental.java; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -40,8 +39,6 @@ import java.util.Set; * Date: 2/16/12 */ class OutputFilesSink implements OutputFileConsumer { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.java.OutputFilesSink"); - private final CompileContext myContext; private final ModuleLevelBuilder.OutputConsumer myOutputConsumer; private final Callbacks.Backend myMappingsCallback; @@ -92,11 +89,14 @@ class OutputFilesSink implements OutputFileConsumer { } } - try { - writeToDisk(fileObject, isTemp); - } - catch (IOException e) { - myContext.processMessage(new CompilerMessage(JavaBuilder.BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage())); + if (outKind == JavaFileObject.Kind.CLASS) { + // generated sources and resources are handled separately + try { + writeToDisk(fileObject, isTemp); + } + catch (IOException e) { + myContext.processMessage(new CompilerMessage(JavaBuilder.BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage())); + } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java index f07df2ec467c..37f8bfb09d4a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java @@ -18,14 +18,15 @@ package org.jetbrains.jps.incremental.resources; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildOutputConsumer; import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.builders.FileProcessor; import org.jetbrains.jps.builders.java.ResourceRootDescriptor; import org.jetbrains.jps.builders.java.ResourcesTargetType; -import org.jetbrains.jps.builders.storage.SourceToOutputMapping; -import org.jetbrains.jps.incremental.*; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.ProjectBuildException; +import org.jetbrains.jps.incremental.ResourcesTarget; +import org.jetbrains.jps.incremental.TargetBuilder; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.incremental.messages.ProgressMessage; @@ -54,11 +55,6 @@ public class ResourcesBuilder extends TargetBuilder<ResourceRootDescriptor, Reso @Override public void buildStarted(CompileContext context) { - // init patterns - ResourcePatterns patterns = ResourcePatterns.KEY.get(context); - if (patterns == null) { - ResourcePatterns.KEY.set(context, new ResourcePatterns(context.getProjectDescriptor().getProject())); - } } @Override @@ -71,18 +67,6 @@ public class ResourcesBuilder extends TargetBuilder<ResourceRootDescriptor, Reso return; } - @Nullable - final Map<ResourcesTarget, Set<File>> cleanedSources; - if (context.isProjectRebuild()) { - cleanedSources = null; - } - else { - cleanedSources = BuildOperations.cleanOutputsCorrespondingToChangedFiles(context, holder); - } - - final ResourcePatterns patterns = ResourcePatterns.KEY.get(context); - assert patterns != null; - try { holder.processDirtyFiles(new FileProcessor<ResourceRootDescriptor, ResourcesTarget>() { private final Map<ResourceRootDescriptor, Boolean> mySkippedRoots = new HashMap<ResourceRootDescriptor, Boolean>(); @@ -96,27 +80,17 @@ public class ResourcesBuilder extends TargetBuilder<ResourceRootDescriptor, Reso if (isSkipped.booleanValue()) { return true; } - if (patterns.isResourceFile(file, sourceRoot.getRootFile())) { - try { - copyResource(context, sourceRoot, file, outputConsumer); - } - catch (IOException e) { - LOG.info(e); - context.processMessage( - new CompilerMessage( - "resources", BuildMessage.Kind.ERROR, e.getMessage(), FileUtil.toSystemIndependentName(file.getPath()) - ) - ); - return false; - } - finally { - if (cleanedSources != null) { - final Set<File> files = cleanedSources.get(target); - if (files != null) { - files.remove(file); - } - } - } + try { + copyResource(context, sourceRoot, file, outputConsumer); + } + catch (IOException e) { + LOG.info(e); + context.processMessage( + new CompilerMessage( + "resources", BuildMessage.Kind.ERROR, e.getMessage(), FileUtil.toSystemIndependentName(file.getPath()) + ) + ); + return false; } return !context.getCancelStatus().isCanceled(); } @@ -124,21 +98,11 @@ public class ResourcesBuilder extends TargetBuilder<ResourceRootDescriptor, Reso context.checkCanceled(); - if (cleanedSources != null) { - // cleanup mapping for the files that were copied before but not copied now - for (Map.Entry<ResourcesTarget, Set<File>> entry : cleanedSources.entrySet()) { - final Set<File> files = entry.getValue(); - if (!files.isEmpty()) { - final SourceToOutputMapping mapping = context.getProjectDescriptor().dataManager.getSourceToOutputMap(entry.getKey()); - for (File file : files) { - mapping.remove(file.getPath()); - } - } - } - } - context.processMessage(new ProgressMessage("")); } + catch(ProjectBuildException e) { + throw e; + } catch (Exception e) { throw new ProjectBuildException(e.getMessage(), e); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildTargetConfiguration.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildTargetConfiguration.java index 1ed0edbb1bc8..8067aefacd5c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildTargetConfiguration.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildTargetConfiguration.java @@ -22,9 +22,13 @@ import com.intellij.util.SmartList; import gnu.trove.THashSet; import org.jetbrains.jps.builders.BuildTarget; import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.GlobalContextKey; import java.io.*; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; /** * @author nik @@ -35,6 +39,7 @@ public class BuildTargetConfiguration { private final BuildTargetsState myTargetsState; private String myConfiguration; private volatile String myCurrentState; + private static final GlobalContextKey<Set<File>> ALL_DELETED_ROOTS_KEY = GlobalContextKey.create("_all_deleted_output_roots_"); public BuildTargetConfiguration(BuildTarget<?> target, BuildTargetsState targetsState) { myTarget = target; @@ -121,9 +126,7 @@ public class BuildTargetConfiguration { } File file = getNonexistentOutputsFile(); if (nonexistentOutputRoots.isEmpty()) { - if (file.exists()) { - FileUtil.delete(file); - } + file.delete(); } else { FileUtil.writeToFile(file, StringUtil.join(nonexistentOutputRoots, "\n")); @@ -132,12 +135,31 @@ public class BuildTargetConfiguration { public boolean outputRootWasDeleted(CompileContext context) throws IOException { List<String> nonexistentOutputRoots = new SmartList<String>(); - for (File outputRoot : myTarget.getOutputRoots(context)) { - if (!outputRoot.exists()) { - nonexistentOutputRoots.add(outputRoot.getAbsolutePath()); + + final Collection<File> targetRoots = myTarget.getOutputRoots(context); + synchronized (ALL_DELETED_ROOTS_KEY) { + Set<File> allDeletedRoots = ALL_DELETED_ROOTS_KEY.get(context); + for (File outputRoot : targetRoots) { + boolean wasDeleted = allDeletedRoots != null && allDeletedRoots.contains(outputRoot); + if (!wasDeleted) { + wasDeleted = !outputRoot.exists(); + if (wasDeleted) { + if (allDeletedRoots == null) { // lazy init + allDeletedRoots = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY); + ALL_DELETED_ROOTS_KEY.set(context, allDeletedRoots); + } + allDeletedRoots.add(outputRoot); + } + } + if (wasDeleted) { + nonexistentOutputRoots.add(outputRoot.getAbsolutePath()); + } } } - if (nonexistentOutputRoots.isEmpty()) return false; + + if (nonexistentOutputRoots.isEmpty()) { + return false; + } Set<String> storedNonExistentOutputs; File file = getNonexistentOutputsFile(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/indices/impl/IgnoredFileIndexImpl.java b/jps/jps-builders/src/org/jetbrains/jps/indices/impl/IgnoredFileIndexImpl.java index fec3e344ed37..700f2cc7322d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/indices/impl/IgnoredFileIndexImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/indices/impl/IgnoredFileIndexImpl.java @@ -15,60 +15,22 @@ */ package org.jetbrains.jps.indices.impl; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.fileTypes.impl.IgnoredPatternSet; import org.jetbrains.jps.indices.IgnoredFileIndex; import org.jetbrains.jps.model.JpsModel; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - /** * @author nik */ public class IgnoredFileIndexImpl implements IgnoredFileIndex { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.IgnoredFilePatterns"); - private List<Pattern> myPatterns = new ArrayList<Pattern>(); + private IgnoredPatternSet myIgnoredPatterns = new IgnoredPatternSet(); public IgnoredFileIndexImpl(JpsModel model) { - loadFromString(model.getGlobal().getFileTypesConfiguration().getIgnoredPatternString()); - } - - private void loadFromString(String patterns) { - myPatterns.clear(); - StringTokenizer tokenizer = new StringTokenizer(patterns, ";"); - while (tokenizer.hasMoreTokens()) { - String pattern = tokenizer.nextToken(); - if (!StringUtil.isEmptyOrSpaces(pattern)) { - try { - myPatterns.add(Pattern.compile(convertToJavaPattern(pattern))); - } - catch (PatternSyntaxException e) { - LOG.info("Cannot load ignored file pattern " + pattern, e); - } - } - } + myIgnoredPatterns.setIgnoreMasks(model.getGlobal().getFileTypesConfiguration().getIgnoredPatternString()); } @Override public boolean isIgnored(String fileName) { - for (Pattern pattern : myPatterns) { - if (pattern.matcher(fileName).matches()) { - return true; - } - } - return false; - } - - private static String convertToJavaPattern(String wildcardPattern) { - wildcardPattern = StringUtil.replace(wildcardPattern, ".", "\\."); - wildcardPattern = StringUtil.replace(wildcardPattern, "*?", ".+"); - wildcardPattern = StringUtil.replace(wildcardPattern, "?*", ".+"); - wildcardPattern = StringUtil.replace(wildcardPattern, "*", ".*"); - wildcardPattern = StringUtil.replace(wildcardPattern, "?", "."); - return wildcardPattern; + return myIgnoredPatterns.isIgnored(fileName); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java index 5175787156b2..03b9d4170aa8 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacFileManager.java @@ -18,16 +18,14 @@ package org.jetbrains.jps.javac; import com.intellij.openapi.util.io.FileUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.java.JavaSourceTransformer; import org.jetbrains.jps.incremental.Utils; import javax.tools.*; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Eugene Zhuravlev @@ -36,6 +34,7 @@ import java.util.Set; class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> implements StandardJavaFileManager{ private final Context myContext; + private final Collection<JavaSourceTransformer> mySourceTransformers; private Map<File, Set<File>> myOutputsMap = Collections.emptyMap(); interface Context { @@ -48,9 +47,10 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager void reportMessage(final Diagnostic.Kind kind, String message); } - public JavacFileManager(Context context) { + public JavacFileManager(Context context, Collection<JavaSourceTransformer> transformers) { super(context.getStandardFileManager()); myContext = context; + mySourceTransformers = transformers; } public void setOutputDirectories(final Map<File, Set<File>> outputDirToSrcRoots) throws IOException{ @@ -66,19 +66,19 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager } public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) { - return getStdManager().getJavaFileObjectsFromFiles(files); + return wrapJavaFileObjects(getStdManager().getJavaFileObjectsFromFiles(files)); } public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { - return getStdManager().getJavaFileObjects(files); + return wrapJavaFileObjects(getStdManager().getJavaFileObjects(files)); } public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { - return getStdManager().getJavaFileObjectsFromStrings(names); + return wrapJavaFileObjects(getStdManager().getJavaFileObjectsFromStrings(names)); } public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { - return getStdManager().getJavaFileObjects(names); + return wrapJavaFileObjects(getStdManager().getJavaFileObjects(names)); } public Iterable<? extends File> getLocation(Location location) { @@ -104,6 +104,24 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager } @Override + public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { + final Iterable<JavaFileObject> objects = super.list(location, packageName, kinds, recurse); + //noinspection unchecked + return kinds.contains(JavaFileObject.Kind.SOURCE)? (Iterable<JavaFileObject>)wrapJavaFileObjects(objects) : objects; + } + + private Iterable<? extends JavaFileObject> wrapJavaFileObjects(Iterable<? extends JavaFileObject> originalObjects) { + if (mySourceTransformers.isEmpty()) { + return originalObjects; + } + final List<JavaFileObject> wrapped = new ArrayList<JavaFileObject>(); + for (JavaFileObject fo : originalObjects) { + wrapped.add(JavaFileObject.Kind.SOURCE.equals(fo.getKind())? new TransformableJavaFileObject(fo, mySourceTransformers) : fo); + } + return wrapped; + } + + @Override public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { checkCanceled(); final JavaFileObject fo = super.getJavaFileForInput(location, className, kind); @@ -111,7 +129,7 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager // workaround javac bug (missing null-check): throwing exception here instead of returning null throw new FileNotFoundException("Java resource does not exist : " + location + '/' + kind + '/' + className); } - return fo; + return mySourceTransformers.isEmpty()? fo : new TransformableJavaFileObject(fo, mySourceTransformers); } public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { @@ -202,10 +220,6 @@ class JavacFileManager extends ForwardingJavaFileManager<StandardJavaFileManager return fileManager; } - public Iterable<? extends JavaFileObject> toJavaFileObjects(Iterable<? extends File> files) { - return getStdManager().getJavaFileObjectsFromFiles(files); - } - @Override public void close() { try { diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java index f985b2f81699..f298026813a2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java @@ -18,6 +18,7 @@ package org.jetbrains.jps.javac; import com.intellij.openapi.util.SystemInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.api.CanceledStatus; +import org.jetbrains.jps.builders.java.JavaSourceTransformer; import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.incremental.LineOutputWriter; @@ -78,7 +79,10 @@ public class JavacMain { for (File outputDir : outputDirToRoots.keySet()) { outputDir.mkdirs(); } - final JavacFileManager fileManager = new JavacFileManager(new ContextImpl(compiler, outConsumer, outputSink, canceledStatus, nowUsingJavac)); + + final List<JavaSourceTransformer> transformers = getSourceTransformers(); + + final JavacFileManager fileManager = new JavacFileManager(new ContextImpl(compiler, outConsumer, outputSink, canceledStatus, nowUsingJavac), transformers); fileManager.handleOption("-bootclasspath", Collections.singleton("").iterator()); // this will clear cached stuff fileManager.handleOption("-extdirs", Collections.singleton("").iterator()); // this will clear cached stuff @@ -138,8 +142,16 @@ public class JavacMain { try { final Collection<String> _options = prepareOptions(options, nowUsingJavac); + + // to be on the safe side, we'll have to apply all options _before_ calling any of manager's methods + // i.e. getJavaFileObjectsFromFiles() + // This way the manager will be properly initialized. Namely, the encoding will be set correctly + for (Iterator<String> iterator = _options.iterator(); iterator.hasNext(); ) { + fileManager.handleOption(iterator.next(), iterator); + } + final JavaCompiler.CompilationTask task = compiler.getTask( - out, fileManager, outConsumer, _options, null, fileManager.toJavaFileObjects(sources) + out, fileManager, outConsumer, _options, null, fileManager.getJavaFileObjectsFromFiles(sources) ); //if (!IS_VM_6_VERSION) { //todo! @@ -162,6 +174,16 @@ public class JavacMain { return false; } + private static List<JavaSourceTransformer> getSourceTransformers() { + final Class<JavaSourceTransformer> transformerClass = JavaSourceTransformer.class; + final ServiceLoader<JavaSourceTransformer> loader = ServiceLoader.load(transformerClass, transformerClass.getClassLoader()); + final List<JavaSourceTransformer> transformers = new ArrayList<JavaSourceTransformer>(); + for (JavaSourceTransformer t : loader) { + transformers.add(t); + } + return transformers; + } + private static boolean isAnnotationProcessingEnabled(final Collection<String> options) { for (String option : options) { if ("-proc:none".equals(option)) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/TransformableJavaFileObject.java b/jps/jps-builders/src/org/jetbrains/jps/javac/TransformableJavaFileObject.java new file mode 100644 index 000000000000..673b6e376e45 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/TransformableJavaFileObject.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2013 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.javac; + +import org.jetbrains.jps.builders.java.JavaSourceTransformer; +import org.jetbrains.jps.incremental.Utils; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.*; +import java.io.*; +import java.net.URI; +import java.util.Collection; + +/** + * @author Eugene Zhuravlev + * Date: 1/9/13 + */ +public class TransformableJavaFileObject implements JavaFileObject { + private final JavaFileObject myOriginal; + private final Collection<JavaSourceTransformer> myTransformers; + + public TransformableJavaFileObject(JavaFileObject original, Collection<JavaSourceTransformer> transformers) { + myOriginal = original; + myTransformers = transformers; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + // todo: cache transformed content? + final File file = Utils.convertToFile(myOriginal.toUri()); + CharSequence content = myOriginal.getCharContent(ignoreEncodingErrors); + for (JavaSourceTransformer transformer : myTransformers) { + content = transformer.transform(file, content); + } + return content; + } + + @Override + public InputStream openInputStream() throws IOException { + return myOriginal.openInputStream(); + } + + @Override + public Kind getKind() { + return myOriginal.getKind(); + } + + @Override + public boolean isNameCompatible(String simpleName, Kind kind) { + return myOriginal.isNameCompatible(simpleName, kind); + } + + @Override + public NestingKind getNestingKind() { + return myOriginal.getNestingKind(); + } + + @Override + public Modifier getAccessLevel() { + return myOriginal.getAccessLevel(); + } + + @Override + public URI toUri() { + return myOriginal.toUri(); + } + + @Override + public String getName() { + return myOriginal.getName(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return myOriginal.openOutputStream(); + } + + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + return myOriginal.openReader(ignoreEncodingErrors); + } + + @Override + public Writer openWriter() throws IOException { + return myOriginal.openWriter(); + } + + @Override + public long getLastModified() { + return myOriginal.getLastModified(); + } + + @Override + public boolean delete() { + return myOriginal.delete(); + } +} diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java index 29fed9c6bbed..26f93b216c54 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java @@ -101,4 +101,8 @@ public class CommonTest extends IncrementalTestCase { public void testAddClass() throws Exception { doTest(); } + + public void testAddDuplicateClass() throws Exception { + doTest(); + } } diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/IncrementalTestCase.java b/jps/jps-builders/testSrc/org/jetbrains/ether/IncrementalTestCase.java index f1cefa112f80..6e38f95a7c52 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/IncrementalTestCase.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/IncrementalTestCase.java @@ -82,7 +82,8 @@ public abstract class IncrementalTestCase extends JpsBuildTestCase { return JpsPathUtil.pathToUrl(getAbsolutePath(pathRelativeToProjectRoot)); } - protected String getAbsolutePath(final String pathRelativeToProjectRoot) { + @Override + public String getAbsolutePath(final String pathRelativeToProjectRoot) { return FileUtil.toSystemIndependentName(workDir.getAbsolutePath()) + "/" + pathRelativeToProjectRoot; } diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/MemberChangeTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/MemberChangeTest.java index 1fd356da4a32..c1c1f81c3cca 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/MemberChangeTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/MemberChangeTest.java @@ -176,6 +176,10 @@ public class MemberChangeTest extends IncrementalTestCase { doTest(); } + public void testRemoveMoreAccessibleMethod() { + doTest(); + } + public void testRenameMethod() { doTest(); } diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/BuildResult.java b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/BuildResult.java index 56b936246fd5..4ada300cb65a 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/BuildResult.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/BuildResult.java @@ -18,11 +18,13 @@ package org.jetbrains.jps.builders; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.incremental.MessageHandler; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.DoneSomethingNotification; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -69,4 +71,9 @@ public class BuildResult implements MessageHandler { Assert.assertTrue("Build failed. \nErrors:\n" + StringUtil.join(myErrorMessages, toStringFunction, "\n") + "\nInfo messages:\n" + StringUtil.join(myInfoMessages, toStringFunction, "\n"), isSuccessful()); } + + @NotNull + public List<BuildMessage> getErrorMessages() { + return Collections.unmodifiableList(myErrorMessages); + } } diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java index 54528ada8d5e..5751878802f8 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java @@ -44,15 +44,15 @@ import org.jetbrains.jps.incremental.storage.ProjectTimestamps; import org.jetbrains.jps.indices.ModuleExcludeIndex; import org.jetbrains.jps.indices.impl.IgnoredFileIndexImpl; import org.jetbrains.jps.indices.impl.ModuleExcludeIndexImpl; -import org.jetbrains.jps.model.JpsDummyElement; -import org.jetbrains.jps.model.JpsElementFactory; -import org.jetbrains.jps.model.JpsModel; -import org.jetbrains.jps.model.JpsProject; +import org.jetbrains.jps.model.*; import org.jetbrains.jps.model.java.*; import org.jetbrains.jps.model.library.JpsOrderRootType; import org.jetbrains.jps.model.library.JpsTypedLibrary; import org.jetbrains.jps.model.library.sdk.JpsSdk; +import org.jetbrains.jps.model.library.sdk.JpsSdkReference; +import org.jetbrains.jps.model.library.sdk.JpsSdkType; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.module.JpsSdkReferencesTable; import org.jetbrains.jps.model.serialization.JpsProjectLoader; import org.jetbrains.jps.model.serialization.PathMacroUtil; import org.jetbrains.jps.util.JpsPathUtil; @@ -73,9 +73,11 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { protected JpsProject myProject; protected JpsModel myModel; private JpsSdk<JpsDummyElement> myJdk; - private File myDataStorageRoot; + protected File myDataStorageRoot; private TestProjectBuilderLogger myLogger; + protected Map<String, String> myBuildParams; + @Override protected void setUp() throws Exception { super.setUp(); @@ -83,6 +85,7 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { myProject = myModel.getProject(); myDataStorageRoot = FileUtil.createTempDirectory("compile-server-" + getProjectName(), null); myLogger = new TestProjectBuilderLogger(); + myBuildParams = new HashMap<String, String>(); } @Override @@ -215,15 +218,23 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { return null; } - protected JpsModule addModule(String moduleName, - String[] srcPaths, - @Nullable String outputPath, - @Nullable String testOutputPath, - JpsSdk<JpsDummyElement> jdk) { + protected <T extends JpsElement> JpsModule addModule(String moduleName, + String[] srcPaths, + @Nullable String outputPath, + @Nullable String testOutputPath, + JpsSdk<T> sdk) { JpsModule module = myProject.addModule(moduleName, JpsJavaModuleType.INSTANCE); - module.getSdkReferencesTable().setSdkReference(JpsJavaSdkType.INSTANCE, jdk.createReference()); - module.getDependenciesList().addSdkDependency(JpsJavaSdkType.INSTANCE); - if (srcPaths.length > 0) { + final JpsSdkType<T> sdkType = sdk.getSdkType(); + final JpsSdkReferencesTable sdkTable = module.getSdkReferencesTable(); + sdkTable.setSdkReference(sdkType, sdk.createReference()); + + if (sdkType instanceof JpsJavaSdkTypeWrapper) { + final JpsSdkReference<T> wrapperRef = sdk.createReference(); + sdkTable.setSdkReference(JpsJavaSdkType.INSTANCE, JpsJavaExtensionService. + getInstance().createWrappedJavaSdkReference((JpsJavaSdkTypeWrapper)sdkType, wrapperRef)); + } + module.getDependenciesList().addSdkDependency(sdkType); + if (srcPaths.length > 0 || outputPath != null) { for (String srcPath : srcPaths) { module.getContentRootsList().addUrl(JpsPathUtil.pathToUrl(srcPath)); module.addSourceRoot(JpsPathUtil.pathToUrl(srcPath), JavaSourceRootType.SOURCE); @@ -264,7 +275,7 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { } } - protected void assertCompiled(String builderName, String... paths) { + public void assertCompiled(String builderName, String... paths) { myLogger.assertCompiled(builderName, new File[]{myProjectDir, myDataStorageRoot}, paths); } @@ -273,7 +284,7 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { } protected BuildResult doBuild(final ProjectDescriptor descriptor, CompileScopeTestBuilder scopeBuilder) { - IncProjectBuilder builder = new IncProjectBuilder(descriptor, BuilderRegistry.getInstance(), Collections.<String, String>emptyMap(), CanceledStatus.NULL, null); + IncProjectBuilder builder = new IncProjectBuilder(descriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, null); BuildResult result = new BuildResult(); builder.addMessageHandler(result); try { @@ -289,7 +300,7 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { return createFile(relativePath, ""); } - protected String createFile(String relativePath, final String text) { + public String createFile(String relativePath, final String text) { try { File file = new File(getOrCreateProjectDir(), relativePath); FileUtil.writeToFile(file, text); @@ -301,7 +312,7 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { } protected String copyToProject(String relativeSourcePath, String relativeTargetPath) { - File source = PathManagerEx.findFileUnderProjectHome(relativeSourcePath, getClass()); + File source = findFindUnderProjectHome(relativeSourcePath); String fullTargetPath = getAbsolutePath(relativeTargetPath); File target = new File(fullTargetPath); try { @@ -318,7 +329,11 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { return fullTargetPath; } - private File getOrCreateProjectDir() { + protected File findFindUnderProjectHome(String relativeSourcePath) { + return PathManagerEx.findFileUnderProjectHome(relativeSourcePath, getClass()); + } + + public File getOrCreateProjectDir() { if (myProjectDir == null) { try { myProjectDir = doGetProjectDir(); @@ -334,11 +349,11 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { return FileUtil.createTempDirectory("prj", null); } - protected String getAbsolutePath(final String pathRelativeToProjectRoot) { + public String getAbsolutePath(final String pathRelativeToProjectRoot) { return FileUtil.toSystemIndependentName(new File(getOrCreateProjectDir(), pathRelativeToProjectRoot).getAbsolutePath()); } - protected JpsModule addModule(String moduleName, String... srcPaths) { + public JpsModule addModule(String moduleName, String... srcPaths) { if (myJdk == null) { myJdk = addJdk("1.6"); } diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderOverwriteTest.java b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderOverwriteTest.java index 0c27e91e532d..ee9ad9ad0d28 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderOverwriteTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderOverwriteTest.java @@ -15,9 +15,17 @@ */ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.PathUtil; +import com.intellij.util.text.CharsetUtil; import org.jetbrains.jps.model.artifact.JpsArtifact; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + import static com.intellij.util.io.TestFileSystemBuilder.fs; import static org.jetbrains.jps.incremental.artifacts.LayoutElementTestUtil.root; @@ -121,6 +129,65 @@ public class ArtifactBuilderOverwriteTest extends ArtifactBuilderTestCase { buildAllAndAssertUpToDate(); } + public void testOverwriteCopiedFileByExtracted() { + String jar = createArchive("x.jar", "x.txt", "1"); + String file = createFile("x.txt", "2"); + JpsArtifact a = addArtifact(root().extractedDir(jar, "").fileCopy(file)); + buildAll(); + assertOutput(a, fs().file("x.txt", "1")); + buildAllAndAssertUpToDate(); + + change(file, "3"); + buildAllAndAssertUpToDate(); + assertOutput(a, fs().file("x.txt", "1")); + + delete(jar); + createArchive("x.jar", "x.txt", "4"); + buildAll(); + assertOutput(a, fs().file("x.txt", "4")); + + delete(jar); + buildAll(); + assertOutput(a, fs().file("x.txt", "3")); + } + + public void testOverwriteExtractedFileByCopied() { + String file = createFile("x.txt", "1"); + String jar = createArchive("x.jar", "x.txt", "2"); + JpsArtifact a = addArtifact(root().fileCopy(file).extractedDir(jar, "")); + buildAll(); + assertOutput(a, fs().file("x.txt", "1")); + buildAllAndAssertUpToDate(); + + delete(jar); + createArchive("x.jar", "x.txt", "3"); + buildAll(); + assertOutput(a, fs().file("x.txt", "1")); + + delete(file); + buildAll(); + assertOutput(a, fs().file("x.txt", "3")); + } + + private String createArchive(String relativeArchivePath, String fileNameInArchive, String text) { + try { + File file = new File(getOrCreateProjectDir(), relativeArchivePath); + ZipOutputStream output = new ZipOutputStream(new FileOutputStream(file)); + try { + output.putNextEntry(new ZipEntry(fileNameInArchive)); + output.write(text.getBytes(CharsetUtil.UTF8)); + output.closeEntry(); + } + finally { + output.close(); + } + return FileUtil.toSystemIndependentName(file.getAbsolutePath()); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + public void testFileOrder() { final String firstFile = createFile("d1/xxx.txt", "first"); final String secondFile = createFile("d2/xxx.txt", "second"); diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTestCase.java b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTestCase.java index 473c09d606cb..239b561ec66c 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTestCase.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTestCase.java @@ -54,7 +54,7 @@ public abstract class ArtifactBuilderTestCase extends JpsBuildTestCase { super.tearDown(); } - protected JpsArtifact addArtifact(LayoutElementTestUtil.LayoutElementCreator root) { + public JpsArtifact addArtifact(LayoutElementTestUtil.LayoutElementCreator root) { Set<String> usedNames = getArtifactNames(); final String name = UniqueNameGenerator.generateUniqueName("a", usedNames); return addArtifact(name, root); diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ModuleBuilder.java b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ModuleBuilder.java new file mode 100644 index 000000000000..fe600f20a981 --- /dev/null +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ModuleBuilder.java @@ -0,0 +1,173 @@ +package org.jetbrains.jps.incremental.artifacts; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.JpsModuleRootModificationUtil; +import org.jetbrains.jps.model.artifact.JpsArtifact; +import org.jetbrains.jps.model.artifact.elements.JpsPackagingElement; +import org.jetbrains.jps.model.java.JpsJavaDependencyScope; +import org.jetbrains.jps.model.module.JpsModule; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.jetbrains.jps.incremental.artifacts.LayoutElementTestUtil.root; + +public class ModuleBuilder { + private static Map<ArtifactBuilderTestCase, Pair<AtomicInteger, AtomicInteger>> testCaseToNameCounter = new THashMap<ArtifactBuilderTestCase, Pair<AtomicInteger, AtomicInteger>>(); + + private final ArtifactBuilderTestCase testCase; + private final String builderName; + + private String name; + private JpsModule module; + private File sourceRoot; + private String sourceRootRelativeToProject; + + private String fromSourceRoot; + + private JpsArtifact mainArtifact; + + public ModuleBuilder(String builderName, ArtifactBuilderTestCase testCase) { + this(builderName, testCase, null); + } + + public ModuleBuilder(String builderName, ArtifactBuilderTestCase testCase, String fromSourceRoot) { + this.builderName = builderName; + this.testCase = testCase; + this.fromSourceRoot = fromSourceRoot; + } + + public static JpsArtifact createArtifact(ModuleBuilder... builders) { + LayoutElementTestUtil.LayoutElementCreator root = root(); + String name = ""; + for (ModuleBuilder builder : builders) { + root.element(builder.createPackagingElement(builder.get())); + name += "_" + builder.get().getName(); + } + return builders[0].testCase.addArtifact(name.substring(1), root); + } + + public String getName() { + return get().getName(); + } + + public ModuleBuilder name(@NotNull String name) { + this.name = name; + return this; + } + + public JpsModule get() { + if (module == null) { + module = testCase.addModule(getOrCreateModuleName(), FileUtilRt.toSystemIndependentName(getSourceRoot().getPath())); + moduleCreated(module); + } + return module; + } + + public JpsArtifact createArtifact() { + JpsArtifact artifact = testCase.addArtifact(generateName(false), root().element(createPackagingElement(get()))); + if (mainArtifact == null) { + mainArtifact = artifact; + } + return artifact; + } + + public ModuleBuilder artifact() { + createArtifact(); + return this; + } + + public JpsArtifact getArtifact() { + return mainArtifact; + } + + protected JpsPackagingElement createPackagingElement(JpsModule module) { + throw new AbstractMethodError(); + } + + protected void moduleCreated(JpsModule module) { + } + + public ModuleBuilder sourceRoot(String path) { + assert sourceRoot == null; + sourceRoot = new File(path); + sourceRootRelativeToProject = FileUtil.getRelativePath(testCase.getOrCreateProjectDir(), sourceRoot); + return this; + } + + private File getSourceRoot() { + if (sourceRoot == null) { + sourceRootRelativeToProject = getOrCreateModuleName() + "-src"; + sourceRoot = new File(testCase.getAbsolutePath(sourceRootRelativeToProject)); + } + return sourceRoot; + } + + private String getOrCreateModuleName() { + if (name == null) { + name = generateName(true); + } + return name; + } + + private String generateName(boolean module) { + Pair<AtomicInteger, AtomicInteger> counter = testCaseToNameCounter.get(testCase); + if (counter == null) { + counter = Pair.create(new AtomicInteger(0), new AtomicInteger(0)); + testCaseToNameCounter.put(testCase, counter); + Disposer.register(testCase.getTestRootDisposable(), new Disposable() { + @Override + public void dispose() { + testCaseToNameCounter.remove(testCase); + } + }); + } + return (module ? "m" : "a") + (module ? counter.first : counter.second).getAndIncrement(); + } + + public ModuleBuilder copy(String file) throws IOException { + FileUtil.copy(fromSourceRoot == null ? new File(file) : new File(fromSourceRoot, file), new File(getSourceRoot(), file)); + return this; + } + + public ModuleBuilder file(String pathRelativeToModuleSourceRoot, String text) throws IOException { + doCreateFile(pathRelativeToModuleSourceRoot, text); + return this; + } + + public String createFile(String pathRelativeToModuleSourceRoot, String text) throws IOException { + return FileUtil.toSystemIndependentName(doCreateFile(pathRelativeToModuleSourceRoot, text).getAbsolutePath()); + } + + private File doCreateFile(String pathRelativeToModuleSourceRoot, String text) throws IOException { + File file = new File(getSourceRoot(), pathRelativeToModuleSourceRoot); + FileUtil.writeToFile(file, text); + return file; + } + + public void assertCompiled(String... modulePaths) { + String[] paths = new String[modulePaths.length]; + for (int i = 0; i < modulePaths.length; i++) { + paths[i] = sourceRootRelativeToProject + '/' + modulePaths[i]; + } + testCase.assertCompiled(builderName, paths); + } + + public ModuleBuilder dependsOn(ModuleBuilder dependency) { + JpsModuleRootModificationUtil.addDependency(get(), dependency.get()); + return this; + } + + public ModuleBuilder dependsOnAndExports(ModuleBuilder dependency) { + JpsModuleRootModificationUtil.addDependency(get(), dependency.get(), JpsJavaDependencyScope.COMPILE, true); + return this; + } +} diff --git a/jps/model-api/src/com/intellij/openapi/fileTypes/ExactFileNameMatcher.java b/jps/model-api/src/com/intellij/openapi/fileTypes/ExactFileNameMatcher.java new file mode 100644 index 000000000000..117ab4087f52 --- /dev/null +++ b/jps/model-api/src/com/intellij/openapi/fileTypes/ExactFileNameMatcher.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2009 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. + */ + +/* + * @author max + */ +package com.intellij.openapi.fileTypes; + +import com.intellij.openapi.util.Comparing; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class ExactFileNameMatcher implements FileNameMatcher { + private final String myFileName; + private final boolean myIgnoreCase; + + public ExactFileNameMatcher(@NotNull @NonNls final String fileName) { + myFileName = fileName; + myIgnoreCase = false; + } + + public ExactFileNameMatcher(@NotNull @NonNls final String fileName, final boolean ignoreCase) { + myFileName = fileName; + myIgnoreCase = ignoreCase; + } + + public boolean accept(@NonNls @NotNull final String fileName) { + return Comparing.equal(fileName, myFileName, !myIgnoreCase); + } + + @NonNls + @NotNull + public String getPresentableString() { + return myFileName; + } + + public String getFileName() { + return myFileName; + } + + public boolean isIgnoreCase() { + return myIgnoreCase; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ExactFileNameMatcher that = (ExactFileNameMatcher)o; + + if (!myFileName.equals(that.myFileName)) return false; + + return true; + } + + @Override + public int hashCode() { + return myFileName.hashCode(); + } +}
\ No newline at end of file diff --git a/jps/model-api/src/com/intellij/openapi/fileTypes/ExtensionFileNameMatcher.java b/jps/model-api/src/com/intellij/openapi/fileTypes/ExtensionFileNameMatcher.java new file mode 100644 index 000000000000..fb1f1d8ab1b5 --- /dev/null +++ b/jps/model-api/src/com/intellij/openapi/fileTypes/ExtensionFileNameMatcher.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2009 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.fileTypes; + +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * @author max + */ +public class ExtensionFileNameMatcher implements FileNameMatcher { + private final String myExtension; + private final String myDotExtension; + + public ExtensionFileNameMatcher(@NotNull @NonNls String extension) { + myExtension = extension.toLowerCase(); + myDotExtension = "." + myExtension; + } + + public boolean accept(@NotNull @NonNls String fileName) { + return fileName.regionMatches(true, fileName.length() - myDotExtension.length(), myDotExtension, 0, myDotExtension.length()); + } + + @NonNls + @NotNull + public String getPresentableString() { + return "*." + myExtension; + } + + public String getExtension() { + return myExtension; + } + + + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ExtensionFileNameMatcher that = (ExtensionFileNameMatcher)o; + + if (!myExtension.equals(that.myExtension)) return false; + + return true; + } + + public int hashCode() { + return myExtension.hashCode(); + } +} diff --git a/jps/model-api/src/com/intellij/openapi/fileTypes/FileNameMatcher.java b/jps/model-api/src/com/intellij/openapi/fileTypes/FileNameMatcher.java new file mode 100644 index 000000000000..9b28a1622e10 --- /dev/null +++ b/jps/model-api/src/com/intellij/openapi/fileTypes/FileNameMatcher.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2009 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.fileTypes; + +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * @author max + */ +public interface FileNameMatcher { + boolean accept(@NonNls @NotNull String fileName); + + @NonNls @NotNull + String getPresentableString(); +} diff --git a/jps/model-api/src/org/jetbrains/jps/model/fileTypes/FileNameMatcherFactory.java b/jps/model-api/src/org/jetbrains/jps/model/fileTypes/FileNameMatcherFactory.java new file mode 100644 index 000000000000..2e7d8221f241 --- /dev/null +++ b/jps/model-api/src/org/jetbrains/jps/model/fileTypes/FileNameMatcherFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2013 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.fileTypes; + +import com.intellij.openapi.fileTypes.FileNameMatcher; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.service.JpsServiceManager; + +/** + * @author nik + */ +public abstract class FileNameMatcherFactory { + public static FileNameMatcherFactory getInstance() { + return JpsServiceManager.getInstance().getService(FileNameMatcherFactory.class); + } + + @NotNull + public abstract FileNameMatcher createMatcher(@NotNull String pattern); +} diff --git a/jps/model-api/src/org/jetbrains/jps/model/java/compiler/JpsJavaCompilerConfiguration.java b/jps/model-api/src/org/jetbrains/jps/model/java/compiler/JpsJavaCompilerConfiguration.java index 8aaf77b1687d..27f5e86053cd 100644 --- a/jps/model-api/src/org/jetbrains/jps/model/java/compiler/JpsJavaCompilerConfiguration.java +++ b/jps/model-api/src/org/jetbrains/jps/model/java/compiler/JpsJavaCompilerConfiguration.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.JpsElement; import org.jetbrains.jps.model.module.JpsModule; +import java.io.File; import java.util.Collection; import java.util.List; @@ -55,7 +56,7 @@ public interface JpsJavaCompilerConfiguration extends JpsElement { void addResourcePattern(String pattern); List<String> getResourcePatterns(); - + boolean isResourceFile(@NotNull File file, @NotNull File srcRoot); @Nullable String getByteCodeTargetLevel(String moduleName); diff --git a/jps/model-impl/src/META-INF/services/org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory b/jps/model-impl/src/META-INF/services/org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory new file mode 100644 index 000000000000..91405169453b --- /dev/null +++ b/jps/model-impl/src/META-INF/services/org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory @@ -0,0 +1 @@ +org.jetbrains.jps.model.fileTypes.impl.FileNameMatcherFactoryImpl
\ No newline at end of file diff --git a/jps/model-impl/src/com/intellij/openapi/fileTypes/WildcardFileNameMatcher.java b/jps/model-impl/src/com/intellij/openapi/fileTypes/WildcardFileNameMatcher.java new file mode 100644 index 000000000000..afbaaafbd292 --- /dev/null +++ b/jps/model-impl/src/com/intellij/openapi/fileTypes/WildcardFileNameMatcher.java @@ -0,0 +1,140 @@ +/* + * Copyright 2000-2009 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.fileTypes; + +import com.intellij.util.PatternUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Matcher; + +/** + * @author max + */ +public class WildcardFileNameMatcher implements FileNameMatcher { + private final String myPattern; + private final MaskMatcher myMatcher; + + private interface MaskMatcher { + boolean matches(String filename); + } + + private static final class RegexpMatcher implements MaskMatcher { + private final Matcher myMatcher; + + private RegexpMatcher(String pattern) { + myMatcher = PatternUtil.fromMask(pattern).matcher(""); + } + + public boolean matches(final String filename) { + synchronized (myMatcher) { + myMatcher.reset(filename); + return myMatcher.matches(); + } + } + } + + private static final class SuffixMatcher implements MaskMatcher { + private final String mySuffix; + + private SuffixMatcher(final String suffix) { + mySuffix = suffix; + } + + public boolean matches(final String filename) { + return filename.endsWith(mySuffix); + } + } + + private static final class PrefixMatcher implements MaskMatcher { + private final String myPrefix; + + private PrefixMatcher(final String prefix) { + myPrefix = prefix; + } + + public boolean matches(final String filename) { + return filename.startsWith(myPrefix); + } + } + + private static final class InfixMatcher implements MaskMatcher { + private final String myInfix; + + private InfixMatcher(final String infix) { + myInfix = infix; + } + + public boolean matches(final String filename) { + return filename.contains(myInfix); + } + } + + /** + * Use {@link org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory#createMatcher(String)} instead of direct call to constructor + */ + public WildcardFileNameMatcher(@NotNull @NonNls String pattern) { + myPattern = pattern; + myMatcher = createMatcher(pattern); + } + + private static MaskMatcher createMatcher(final String pattern) { + int len = pattern.length(); + if (len > 1 && pattern.indexOf('?') < 0) { + if (pattern.charAt(0) == '*' && pattern.indexOf('*', 1) < 0) { + return new SuffixMatcher(pattern.substring(1)); + } + if (pattern.indexOf('*') == len - 1) { + return new PrefixMatcher(pattern.substring(0, len - 1)); + } + if (len > 2 && pattern.charAt(0) == '*' && pattern.indexOf('*', 1) == len - 1) { + return new InfixMatcher(pattern.substring(1, len - 1)); + } + } + return new RegexpMatcher(pattern); + } + + public boolean accept(@NotNull String fileName) { + return myMatcher.matches(fileName); + } + + @NonNls + @NotNull + public String getPresentableString() { + return myPattern; + } + + + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final WildcardFileNameMatcher that = (WildcardFileNameMatcher)o; + + if (!myPattern.equals(that.myPattern)) return false; + + return true; + } + + public int hashCode() { + return myPattern.hashCode(); + } + + public String getPattern() { + return myPattern; + } +} diff --git a/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java b/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java new file mode 100644 index 000000000000..8f6e14b55471 --- /dev/null +++ b/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java @@ -0,0 +1,287 @@ +/* + * Copyright 2000-2009 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.fileTypes.impl; + +import com.intellij.openapi.fileTypes.ExactFileNameMatcher; +import com.intellij.openapi.fileTypes.ExtensionFileNameMatcher; +import com.intellij.openapi.fileTypes.FileNameMatcher; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.util.ArrayUtil; +import com.intellij.util.text.CaseInsensitiveStringHashingStrategy; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author max + */ +public class FileTypeAssocTable<T> { + private final Map<String, T> myExtensionMappings; + private final Map<String, T> myExactFileNameMappings; + private final Map<String, T> myExactFileNameAnyCaseMappings; + private boolean myHasAnyCaseExactMappings; + private final List<Pair<FileNameMatcher, T>> myMatchingMappings; + + private FileTypeAssocTable(Map<String, T> extensionMappings, Map<String, T> exactFileNameMappings, Map<String, T> exactFileNameAnyCaseMappings, List<Pair<FileNameMatcher, T>> matchingMappings) { + myExtensionMappings = new THashMap<String, T>(extensionMappings); + myExactFileNameMappings = new THashMap<String, T>(exactFileNameMappings); + myExactFileNameAnyCaseMappings = new THashMap<String, T>(exactFileNameAnyCaseMappings, CaseInsensitiveStringHashingStrategy.INSTANCE) { + @Override + public T remove(Object key) { + T removed = super.remove(key); + myHasAnyCaseExactMappings = size() > 0; + return removed; + } + + @Override + public T put(String key, T value) { + T result = super.put(key, value); + myHasAnyCaseExactMappings = true; + return result; + } + }; + myMatchingMappings = new ArrayList<Pair<FileNameMatcher, T>>(matchingMappings); + } + + public FileTypeAssocTable() { + this(Collections.<String, T>emptyMap(), Collections.<String, T>emptyMap(), Collections.<String, T>emptyMap(), Collections.<Pair<FileNameMatcher, T>>emptyList()); + } + + public boolean isAssociatedWith(T type, FileNameMatcher matcher) { + if (matcher instanceof ExtensionFileNameMatcher || matcher instanceof ExactFileNameMatcher) { + return findAssociatedFileType(matcher) == type; + } + + for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) { + if (matcher.equals(mapping.getFirst()) && type == mapping.getSecond()) return true; + } + + return false; + } + + public void addAssociation(FileNameMatcher matcher, T type) { + if (matcher instanceof ExtensionFileNameMatcher) { + myExtensionMappings.put(((ExtensionFileNameMatcher)matcher).getExtension(), type); + } + else if (matcher instanceof ExactFileNameMatcher) { + final ExactFileNameMatcher exactFileNameMatcher = (ExactFileNameMatcher)matcher; + + if (exactFileNameMatcher.isIgnoreCase()) { + myExactFileNameAnyCaseMappings.put(exactFileNameMatcher.getFileName(), type); + } else { + myExactFileNameMappings.put(exactFileNameMatcher.getFileName(), type); + } + } else { + myMatchingMappings.add(new Pair<FileNameMatcher, T>(matcher, type)); + } + } + + public boolean removeAssociation(FileNameMatcher matcher, T type) { + if (matcher instanceof ExtensionFileNameMatcher) { + String extension = ((ExtensionFileNameMatcher)matcher).getExtension(); + if (myExtensionMappings.get(extension) == type) { + myExtensionMappings.remove(extension); + return true; + } + return false; + } + + if (matcher instanceof ExactFileNameMatcher) { + final ExactFileNameMatcher exactFileNameMatcher = (ExactFileNameMatcher)matcher; + final Map<String, T> mapToUse; + String fileName = exactFileNameMatcher.getFileName(); + + if (exactFileNameMatcher.isIgnoreCase()) { + mapToUse = myExactFileNameAnyCaseMappings; + } else { + mapToUse = myExactFileNameMappings; + } + if(mapToUse.get(fileName) == type) { + mapToUse.remove(fileName); + return true; + } + return false; + } + + List<Pair<FileNameMatcher, T>> copy = new ArrayList<Pair<FileNameMatcher, T>>(myMatchingMappings); + for (Pair<FileNameMatcher, T> assoc : copy) { + if (matcher.equals(assoc.getFirst())) { + myMatchingMappings.remove(assoc); + return true; + } + } + + return false; + } + + public boolean removeAllAssociations(T type) { + boolean changed = removeAssociationsFromMap(myExtensionMappings, type, false); + + changed = removeAssociationsFromMap(myExactFileNameAnyCaseMappings, type, changed); + changed = removeAssociationsFromMap(myExactFileNameMappings, type, changed); + + List<Pair<FileNameMatcher, T>> copy = new ArrayList<Pair<FileNameMatcher, T>>(myMatchingMappings); + for (Pair<FileNameMatcher, T> assoc : copy) { + if (assoc.getSecond() == type) { + myMatchingMappings.remove(assoc); + changed = true; + } + } + + return changed; + } + + private boolean removeAssociationsFromMap(Map<String, T> extensionMappings, T type, boolean changed) { + Set<String> exts = extensionMappings.keySet(); + String[] extsStrings = ArrayUtil.toStringArray(exts); + for (String s : extsStrings) { + if (extensionMappings.get(s) == type) { + extensionMappings.remove(s); + changed = true; + } + } + return changed; + } + + @Nullable + public T findAssociatedFileType(@NotNull @NonNls String fileName) { + T t = myExactFileNameMappings.get(fileName); + if (t != null) return t; + + if (myHasAnyCaseExactMappings) { // even hash lookup with case insensitive hasher is costly for isIgnored checks during compile + t = myExactFileNameAnyCaseMappings.get(fileName); + if (t != null) return t; + } + + //noinspection ForLoopReplaceableByForEach + for (int i = 0, n = myMatchingMappings.size(); i < n; i++) { + final Pair<FileNameMatcher, T> mapping = myMatchingMappings.get(i); + if (mapping.getFirst().accept(fileName)) return mapping.getSecond(); + } + + return myExtensionMappings.get(FileUtilRt.getExtension(fileName).toLowerCase()); + } + + @Nullable + public T findAssociatedFileType(final FileNameMatcher matcher) { + if (matcher instanceof ExtensionFileNameMatcher) { + return myExtensionMappings.get(((ExtensionFileNameMatcher)matcher).getExtension()); + } + + if (matcher instanceof ExactFileNameMatcher) { + final ExactFileNameMatcher exactFileNameMatcher = (ExactFileNameMatcher)matcher; + + if (exactFileNameMatcher.isIgnoreCase()) { + return myExactFileNameAnyCaseMappings.get(exactFileNameMatcher.getFileName()); + } else { + return myExactFileNameMappings.get(exactFileNameMatcher.getFileName()); + } + } + + for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) { + if (matcher.equals(mapping.getFirst())) return mapping.getSecond(); + } + + return null; + } + + @Deprecated + @NotNull + public String[] getAssociatedExtensions(T type) { + Map<String, T> extMap = myExtensionMappings; + + List<String> exts = new ArrayList<String>(); + for (String ext : extMap.keySet()) { + if (extMap.get(ext) == type) { + exts.add(ext); + } + } + return ArrayUtil.toStringArray(exts); + } + + @NotNull + public FileTypeAssocTable<T> copy() { + return new FileTypeAssocTable<T>(myExtensionMappings, myExactFileNameMappings, myExactFileNameAnyCaseMappings, myMatchingMappings); + } + + @NotNull + public List<FileNameMatcher> getAssociations(final T type) { + List<FileNameMatcher> result = new ArrayList<FileNameMatcher>(); + for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) { + if (mapping.getSecond() == type) { + result.add(mapping.getFirst()); + } + } + + for (Map.Entry<String, T> entries : myExactFileNameMappings.entrySet()) { + if (entries.getValue() == type) { + result.add(new ExactFileNameMatcher(entries.getKey())); + } + } + + for (Map.Entry<String, T> entries : myExactFileNameAnyCaseMappings.entrySet()) { + if (entries.getValue() == type) { + result.add(new ExactFileNameMatcher(entries.getKey(), true)); + } + } + + for (Map.Entry<String, T> entries : myExtensionMappings.entrySet()) { + if (entries.getValue() == type) { + result.add(new ExtensionFileNameMatcher(entries.getKey())); + } + } + + return result; + } + + public boolean hasAssociationsFor(final T fileType) { + if (myExtensionMappings.values().contains(fileType)) return true; + if (myExactFileNameMappings.values().contains(fileType)) return true; + if (myExactFileNameAnyCaseMappings.values().contains(fileType)) return true; + for (Pair<FileNameMatcher, T> mapping : myMatchingMappings) { + if (mapping.getSecond() == fileType) return true; + } + return false; + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final FileTypeAssocTable that = (FileTypeAssocTable)o; + + if (!myExtensionMappings.equals(that.myExtensionMappings)) return false; + if (!myMatchingMappings.equals(that.myMatchingMappings)) return false; + if (!myExactFileNameMappings.equals(that.myExactFileNameMappings)) return false; + if (!myExactFileNameAnyCaseMappings.equals(that.myExactFileNameAnyCaseMappings)) return false; + + return true; + } + + public int hashCode() { + int result; + result = myExtensionMappings.hashCode(); + result = 31 * result + myMatchingMappings.hashCode(); + result = 31 * result + myExactFileNameMappings.hashCode(); + result = 31 * result + myExactFileNameAnyCaseMappings.hashCode(); + return result; + } +} diff --git a/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/IgnoredPatternSet.java b/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/IgnoredPatternSet.java new file mode 100644 index 000000000000..8fd023eba207 --- /dev/null +++ b/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/IgnoredPatternSet.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2011 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.fileTypes.impl; + +import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * @author peter + */ +public class IgnoredPatternSet { + private final Set<String> myMasks = new LinkedHashSet<String>(); + private final FileTypeAssocTable<Boolean> myIgnorePatterns = new FileTypeAssocTable<Boolean>().copy(); + + Set<String> getIgnoreMasks() { + return Collections.unmodifiableSet(myMasks); + } + + public void setIgnoreMasks(@NotNull String list) { + clearPatterns(); + + StringTokenizer tokenizer = new StringTokenizer(list, ";"); + while (tokenizer.hasMoreTokens()) { + String ignoredFile = tokenizer.nextToken(); + if (ignoredFile != null) { + addIgnoreMask(ignoredFile); + } + } + + } + + void addIgnoreMask(@NotNull String ignoredFile) { + if (myIgnorePatterns.findAssociatedFileType(ignoredFile) == null) { + myMasks.add(ignoredFile); + myIgnorePatterns.addAssociation(FileNameMatcherFactory.getInstance().createMatcher(ignoredFile), Boolean.TRUE); + } + } + + public boolean isIgnored(@NotNull String fileName) { + if (myIgnorePatterns.findAssociatedFileType(fileName) == Boolean.TRUE) { + return true; + } + + //Quite a hack, but still we need to have some name, which + //won't be catched by VFS for sure. + return fileName.endsWith(FileUtil.ASYNC_DELETE_EXTENSION); + } + + void clearPatterns() { + myMasks.clear(); + myIgnorePatterns.removeAllAssociations(Boolean.TRUE); + } +} diff --git a/jps/model-impl/src/org/jetbrains/jps/model/fileTypes/impl/FileNameMatcherFactoryImpl.java b/jps/model-impl/src/org/jetbrains/jps/model/fileTypes/impl/FileNameMatcherFactoryImpl.java new file mode 100644 index 000000000000..d3c1afcae509 --- /dev/null +++ b/jps/model-impl/src/org/jetbrains/jps/model/fileTypes/impl/FileNameMatcherFactoryImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2013 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.fileTypes.impl; + +import com.intellij.openapi.fileTypes.ExactFileNameMatcher; +import com.intellij.openapi.fileTypes.ExtensionFileNameMatcher; +import com.intellij.openapi.fileTypes.FileNameMatcher; +import com.intellij.openapi.fileTypes.WildcardFileNameMatcher; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory; + +/** + * @author nik + */ +public class FileNameMatcherFactoryImpl extends FileNameMatcherFactory { + @NotNull + public FileNameMatcher createMatcher(@NotNull String pattern) { + if (pattern.startsWith("*.") && + pattern.indexOf('*', 2) < 0 && + pattern.indexOf('.', 2) < 0 && + pattern.indexOf('?', 2) < 0) { + return new ExtensionFileNameMatcher(pattern.substring(2).toLowerCase()); + } + + if (pattern.contains("*") || pattern.contains("?")) { + return new WildcardFileNameMatcher(pattern); + } + + return new ExactFileNameMatcher(pattern); + } +} diff --git a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java index 12d01689ea0a..dfebfa2ce83c 100644 --- a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java +++ b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java @@ -26,6 +26,7 @@ import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions; import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile; import org.jetbrains.jps.model.module.JpsModule; +import java.io.File; import java.util.*; /** @@ -44,6 +45,7 @@ public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<Jp private Map<String, JpsJavaCompilerOptions> myCompilerOptions = new HashMap<String, JpsJavaCompilerOptions>(); private String myJavaCompilerId = "Javac"; private Map<JpsModule, ProcessorConfigProfile> myAnnotationProcessingProfileMap; + private ResourcePatterns myCompiledPatterns; public JpsJavaCompilerConfigurationImpl() { } @@ -107,6 +109,15 @@ public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<Jp } @Override + public boolean isResourceFile(@NotNull File file, @NotNull File srcRoot) { + ResourcePatterns patterns = myCompiledPatterns; + if (patterns == null) { + myCompiledPatterns = patterns = new ResourcePatterns(this); + } + return patterns.isResourceFile(file, srcRoot); + } + + @Override @Nullable public String getByteCodeTargetLevel(String moduleName) { String level = myModulesByteCodeTargetLevels.get(moduleName); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ResourcePatterns.java b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/ResourcePatterns.java index 100568f95d90..ed62fa4e6561 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ResourcePatterns.java +++ b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/ResourcePatterns.java @@ -13,18 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.jps.incremental; +package org.jetbrains.jps.model.java.impl.compiler; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.model.JpsProject; -import org.jetbrains.jps.model.java.JpsJavaExtensionService; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; import java.io.File; @@ -37,18 +34,15 @@ import java.util.regex.Pattern; * Date: 10/6/11 */ public class ResourcePatterns { - public static final Key<ResourcePatterns> KEY = Key.create("_resource_patterns_"); + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.model.java.impl.compiler.ResourcePatterns"); - private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.ResourcePatterns"); + private final List<CompiledPattern> myCompiledPatterns = new ArrayList<CompiledPattern>(); + private final List<CompiledPattern> myNegatedCompiledPatterns = new ArrayList<CompiledPattern>(); - private final List<Pair<Pattern, Pattern>> myCompiledPatterns = new ArrayList<Pair<Pattern, Pattern>>(); - private final List<Pair<Pattern, Pattern>> myNegatedCompiledPatterns = new ArrayList<Pair<Pattern, Pattern>>(); - - public ResourcePatterns(JpsProject project) { - JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); + public ResourcePatterns(final JpsJavaCompilerConfiguration configuration) { final List<String> patterns = configuration.getResourcePatterns(); for (String pattern : patterns) { - final Pair<Pattern, Pattern> regexp = convertToRegexp(pattern); + final CompiledPattern regexp = convertToRegexp(pattern); if (isPatternNegated(pattern)) { myNegatedCompiledPatterns.add(regexp); } @@ -63,14 +57,13 @@ public class ResourcePatterns { final String relativePathToParent; final String parentPath = file.getParent(); if (parentPath != null) { - relativePathToParent = "/" + FileUtil.getRelativePath(FileUtil.toSystemIndependentName(srcRoot.getAbsolutePath()), - FileUtil.toSystemIndependentName(parentPath), '/', SystemInfo.isFileSystemCaseSensitive); + relativePathToParent = "/" + FileUtilRt.getRelativePath(FileUtilRt.toSystemIndependentName(srcRoot.getAbsolutePath()), FileUtilRt.toSystemIndependentName(parentPath), '/', SystemInfo.isFileSystemCaseSensitive); } else { relativePathToParent = null; } - for (Pair<Pattern, Pattern> pair : myCompiledPatterns) { - if (matches(name, relativePathToParent, pair)) { + for (CompiledPattern pair : myCompiledPatterns) { + if (matches(name, relativePathToParent, srcRoot, pair)) { return true; } } @@ -81,25 +74,29 @@ public class ResourcePatterns { //noinspection ForLoopReplaceableByForEach for (int i = 0; i < myNegatedCompiledPatterns.size(); i++) { - if (matches(name, relativePathToParent, myNegatedCompiledPatterns.get(i))) { + if (matches(name, relativePathToParent, srcRoot, myNegatedCompiledPatterns.get(i))) { return false; } } return true; } - private boolean matches(String name, String parentRelativePath, Pair<Pattern, Pattern> nameDirPatternPair) { - if (!matches(name, nameDirPatternPair.getFirst())) { + private static boolean matches(String name, String parentRelativePath, @NotNull File srcRoot, CompiledPattern pattern) { + if (!matches(name, pattern.fileName)) { return false; } - final Pattern dirPattern = nameDirPatternPair.getSecond(); - if (dirPattern == null || parentRelativePath == null) { - return true; + if (parentRelativePath != null) { + if (pattern.dir != null && !matches(parentRelativePath, pattern.dir)) { + return false; + } + if (pattern.srcRoot != null && !matches(srcRoot.getName(), pattern.srcRoot)) { + return false; + } } - return matches(parentRelativePath, dirPattern); + return true; } - private boolean matches(String s, Pattern p) { + private static boolean matches(String s, Pattern p) { try { return p.matcher(s).matches(); } @@ -109,13 +106,20 @@ public class ResourcePatterns { } } - private static Pair<Pattern, Pattern> convertToRegexp(String wildcardPattern) { + private static CompiledPattern convertToRegexp(String wildcardPattern) { if (isPatternNegated(wildcardPattern)) { wildcardPattern = wildcardPattern.substring(1); } - wildcardPattern = FileUtil.toSystemIndependentName(wildcardPattern); + wildcardPattern = FileUtilRt.toSystemIndependentName(wildcardPattern); + String srcRoot = null; + int colon = wildcardPattern.indexOf(":"); + if (colon > 0) { + srcRoot = wildcardPattern.substring(0, colon); + wildcardPattern = wildcardPattern.substring(colon + 1); + } + String dirPattern = null; int slash = wildcardPattern.lastIndexOf('/'); if (slash >= 0) { @@ -138,7 +142,8 @@ public class ResourcePatterns { wildcardPattern = optimize(wildcardPattern); final Pattern dirCompiled = dirPattern == null ? null : compilePattern(dirPattern); - return Pair.create(compilePattern(wildcardPattern), dirCompiled); + final Pattern srcCompiled = srcRoot == null ? null : compilePattern(optimize(normalizeWildcards(srcRoot))); + return new CompiledPattern(compilePattern(wildcardPattern), dirCompiled, srcCompiled); } private static String optimize(String wildcardPattern) { @@ -162,4 +167,17 @@ public class ResourcePatterns { private static Pattern compilePattern(@NonNls String s) { return Pattern.compile(s, SystemInfo.isFileSystemCaseSensitive ? 0 : Pattern.CASE_INSENSITIVE); } + + private static class CompiledPattern { + @NotNull final Pattern fileName; + @Nullable final Pattern dir; + @Nullable final Pattern srcRoot; + + CompiledPattern(@NotNull Pattern fileName, @Nullable Pattern dir, @Nullable Pattern srcRoot) { + this.fileName = fileName; + this.dir = dir; + this.srcRoot = srcRoot; + } + } + } diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java index 2f2057082c4c..ef510e534a1e 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsGlobalLoader.java @@ -40,6 +40,7 @@ public class JpsGlobalLoader extends JpsLoaderBase { private static final JpsGlobalExtensionSerializer[] SERIALIZERS = { new GlobalLibrariesSerializer(), new SdkTableSerializer(), new FileTypesSerializer() }; + public static final String FILE_TYPES_COMPONENT_NAME_KEY = "jps.file.types.component.name"; private final JpsGlobal myGlobal; public JpsGlobalLoader(JpsGlobal global, Map<String, String> pathVariables) { @@ -108,7 +109,7 @@ public class JpsGlobalLoader extends JpsLoaderBase { private static class FileTypesSerializer extends JpsGlobalExtensionSerializer { private FileTypesSerializer() { - super("filetypes.xml", "FileTypeManager"); + super("filetypes.xml", System.getProperty(FILE_TYPES_COMPONENT_NAME_KEY, "FileTypeManager")); } @Override 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 bda75b69ac7a..a19e607f9161 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 @@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.jps.model.JpsElement; @@ -78,6 +79,6 @@ public abstract class JpsLoaderBase { } protected static boolean isXmlFile(File file) { - return file.isFile() && FileUtil.getExtension(file.getName()).equalsIgnoreCase("xml"); + return file.isFile() && FileUtilRt.extensionEquals(file.getName(), "xml"); } } diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/JpsJavaModelSerializerExtension.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/JpsJavaModelSerializerExtension.java index 90bfa354af09..a7a8a7cc6ed7 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/JpsJavaModelSerializerExtension.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/JpsJavaModelSerializerExtension.java @@ -82,7 +82,13 @@ public class JpsJavaModelSerializerExtension extends JpsModelSerializerExtension public void loadModuleDependencyProperties(JpsDependencyElement dependency, Element entry) { boolean exported = entry.getAttributeValue(EXPORTED_ATTRIBUTE) != null; String scopeName = entry.getAttributeValue(SCOPE_ATTRIBUTE); - JpsJavaDependencyScope scope = scopeName != null ? JpsJavaDependencyScope.valueOf(scopeName) : JpsJavaDependencyScope.COMPILE; + JpsJavaDependencyScope scope = null; + try { + scope = scopeName != null ? JpsJavaDependencyScope.valueOf(scopeName) : JpsJavaDependencyScope.COMPILE; + } + catch (IllegalArgumentException e) { + scope = JpsJavaDependencyScope.COMPILE; + } final JpsJavaDependencyExtension extension = getService().getOrCreateDependencyExtension(dependency); extension.setExported(exported); diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerConfigurationSerializer.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerConfigurationSerializer.java index b5cefb3b8b06..d1f6d058ad2d 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerConfigurationSerializer.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/java/compiler/JpsJavaCompilerConfigurationSerializer.java @@ -22,6 +22,7 @@ import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.JpsProject; import org.jetbrains.jps.model.java.JpsJavaExtensionService; +import org.jetbrains.jps.model.java.compiler.JpsCompilerExcludes; import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; import org.jetbrains.jps.model.serialization.JpsProjectExtensionSerializer; @@ -55,16 +56,7 @@ public class JpsJavaCompilerConfigurationSerializer extends JpsProjectExtensionS configuration.setAddNotNullAssertions(Boolean.parseBoolean(addNotNullTag.getAttributeValue(ENABLED, "true"))); } - Element excludeFromCompileTag = componentTag.getChild(EXCLUDE_FROM_COMPILE); - if (excludeFromCompileTag != null) { - for (Element fileTag : JDOMUtil.getChildren(excludeFromCompileTag, "file")) { - configuration.getCompilerExcludes().addExcludedFile(fileTag.getAttributeValue("url")); - } - for (Element directoryTag : JDOMUtil.getChildren(excludeFromCompileTag, "directory")) { - boolean recursively = Boolean.parseBoolean(directoryTag.getAttributeValue("includeSubdirectories")); - configuration.getCompilerExcludes().addExcludedDirectory(directoryTag.getAttributeValue("url"), recursively); - } - } + readExcludes(componentTag.getChild(EXCLUDE_FROM_COMPILE), configuration.getCompilerExcludes()); Element resourcePatternsTag = componentTag.getChild(WILDCARD_RESOURCE_PATTERNS); for (Element entry : JDOMUtil.getChildren(resourcePatternsTag, ENTRY)) { @@ -105,6 +97,18 @@ public class JpsJavaCompilerConfigurationSerializer extends JpsProjectExtensionS } } + public static void readExcludes(Element excludeFromCompileTag, JpsCompilerExcludes excludes) { + if (excludeFromCompileTag != null) { + for (Element fileTag : JDOMUtil.getChildren(excludeFromCompileTag, "file")) { + excludes.addExcludedFile(fileTag.getAttributeValue("url")); + } + for (Element directoryTag : JDOMUtil.getChildren(excludeFromCompileTag, "directory")) { + boolean recursively = Boolean.parseBoolean(directoryTag.getAttributeValue("includeSubdirectories")); + excludes.addExcludedDirectory(directoryTag.getAttributeValue("url"), recursively); + } + } + } + @Override public void saveExtension(@NotNull JpsProject project, @NotNull Element componentTag) { } diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/module/JpsModuleRootModelSerializer.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/module/JpsModuleRootModelSerializer.java index 2f281130f0ff..183ad94d0b8f 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/module/JpsModuleRootModelSerializer.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/module/JpsModuleRootModelSerializer.java @@ -17,6 +17,7 @@ package org.jetbrains.jps.model.serialization.module; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.text.UniqueNameGenerator; import org.jdom.Element; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.JpsCompositeElement; @@ -29,16 +30,14 @@ import org.jetbrains.jps.model.java.JpsJavaSdkType; import org.jetbrains.jps.model.java.JpsJavaSdkTypeWrapper; import org.jetbrains.jps.model.library.JpsLibrary; import org.jetbrains.jps.model.library.JpsLibraryReference; -import org.jetbrains.jps.model.library.sdk.JpsSdkType; import org.jetbrains.jps.model.library.sdk.JpsSdkReference; +import org.jetbrains.jps.model.library.sdk.JpsSdkType; import org.jetbrains.jps.model.module.*; import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension; import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer; import org.jetbrains.jps.model.serialization.library.JpsSdkTableSerializer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import static com.intellij.openapi.util.JDOMUtil.getChildren; @@ -89,7 +88,7 @@ public class JpsModuleRootModelSerializer { final JpsDependenciesList dependenciesList = module.getDependenciesList(); dependenciesList.clear(); final JpsElementFactory elementFactory = JpsElementFactory.getInstance(); - int moduleLibraryNum = 0; + UniqueNameGenerator nameGenerator = new UniqueNameGenerator(); for (Element orderEntry : getChildren(rootModelComponent, ORDER_ENTRY_TAG)) { String type = orderEntry.getAttributeValue(TYPE_ATTRIBUTE); if (SOURCE_FOLDER_TYPE.equals(type)) { @@ -120,14 +119,14 @@ public class JpsModuleRootModelSerializer { final Element moduleLibraryElement = orderEntry.getChild(LIBRARY_TAG); String name = moduleLibraryElement.getAttributeValue(NAME_ATTRIBUTE); if (name == null) { - name = GENERATED_LIBRARY_NAME_PREFIX + (moduleLibraryNum++); + name = GENERATED_LIBRARY_NAME_PREFIX; } - final JpsLibrary library = JpsLibraryTableSerializer.loadLibrary(moduleLibraryElement, name); + String uniqueName = nameGenerator.generateUniqueName(name); + final JpsLibrary library = JpsLibraryTableSerializer.loadLibrary(moduleLibraryElement, uniqueName); module.addModuleLibrary(library); final JpsLibraryDependency dependency = dependenciesList.addLibraryDependency(library); loadModuleDependencyProperties(dependency, orderEntry); - moduleLibraryNum++; } else if (MODULE_TYPE.equals(type)) { String name = orderEntry.getAttributeValue(MODULE_NAME_ATTRIBUTE); diff --git a/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.iml b/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.iml new file mode 100644 index 000000000000..fcefc008fcc5 --- /dev/null +++ b/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.iml @@ -0,0 +1,28 @@ +<?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$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library"> + <library name="xxx"> + <CLASSES> + <root url="file://$MODULE_DIR$/data/lib1" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="xxx"> + <CLASSES> + <root url="file://$MODULE_DIR$/data/lib2" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> + diff --git a/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.ipr b/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.ipr new file mode 100644 index 000000000000..356fca8c2362 --- /dev/null +++ b/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.ipr @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/duplicatedModuleLibrary.iml" filepath="$PROJECT_DIR$/duplicatedModuleLibrary.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> + diff --git a/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.iml b/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.iml new file mode 100644 index 000000000000..dd81f9b72227 --- /dev/null +++ b/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.iml @@ -0,0 +1,21 @@ +<?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$"> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library" scope="test"> + <library> + <CLASSES> + <root url="file://$MODULE_DIR$/lib/data" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> + diff --git a/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.ipr b/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.ipr new file mode 100644 index 000000000000..b28c35795b23 --- /dev/null +++ b/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.ipr @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/invalidDependencyScope.iml" filepath="$PROJECT_DIR$/invalidDependencyScope.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> + diff --git a/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java b/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java index 32719f02ef6d..3ed4336709c4 100644 --- a/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java +++ b/jps/model-serialization/testSrc/org/jetbrains/jps/model/serialization/JpsProjectSerializationTest.java @@ -23,9 +23,12 @@ import org.jetbrains.jps.model.JpsDummyElement; import org.jetbrains.jps.model.JpsEncodingConfigurationService; import org.jetbrains.jps.model.JpsEncodingProjectConfiguration; import org.jetbrains.jps.model.artifact.JpsArtifactService; +import org.jetbrains.jps.model.java.JpsJavaDependencyExtension; +import org.jetbrains.jps.model.java.JpsJavaDependencyScope; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.JpsJavaSdkType; import org.jetbrains.jps.model.library.JpsLibrary; +import org.jetbrains.jps.model.library.JpsOrderRootType; import org.jetbrains.jps.model.library.sdk.JpsSdkReference; import org.jetbrains.jps.model.module.*; import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer; @@ -108,6 +111,30 @@ public class JpsProjectSerializationTest extends JpsSerializationTestCase { assertEquals("1.6", reference.getSdkName()); } + public void testInvalidDependencyScope() { + loadProject("/jps/model-serialization/testData/invalidDependencyScope/invalidDependencyScope.ipr"); + JpsModule module = assertOneElement(myProject.getModules()); + List<JpsDependencyElement> dependencies = module.getDependenciesList().getDependencies(); + assertEquals(3, dependencies.size()); + JpsJavaDependencyExtension extension = JpsJavaExtensionService.getInstance().getDependencyExtension(dependencies.get(2)); + assertNotNull(extension); + assertEquals(JpsJavaDependencyScope.COMPILE, extension.getScope()); + } + + public void testDuplicatedModuleLibrary() { + loadProject("/jps/model-serialization/testData/duplicatedModuleLibrary/duplicatedModuleLibrary.ipr"); + JpsModule module = assertOneElement(myProject.getModules()); + List<JpsDependencyElement> dependencies = module.getDependenciesList().getDependencies(); + assertEquals(4, dependencies.size()); + JpsLibrary lib1 = assertInstanceOf(dependencies.get(2), JpsLibraryDependency.class).getLibrary(); + assertNotNull(lib1); + assertSameElements(lib1.getRootUrls(JpsOrderRootType.COMPILED), getUrl("data/lib1")); + JpsLibrary lib2 = assertInstanceOf(dependencies.get(3), JpsLibraryDependency.class).getLibrary(); + assertNotSame(lib1, lib2); + assertNotNull(lib2); + assertSameElements(lib2.getRootUrls(JpsOrderRootType.COMPILED), getUrl("data/lib2")); + } + public void testLoadEncoding() { loadProject(SAMPLE_PROJECT_PATH); JpsEncodingConfigurationService service = JpsEncodingConfigurationService.getInstance(); |