1 package org.jetbrains.jps.incremental;
3 import com.intellij.openapi.diagnostic.Logger;
4 import com.intellij.openapi.util.Key;
5 import org.jetbrains.ether.dependencyView.Mappings;
6 import org.jetbrains.jps.Module;
7 import org.jetbrains.jps.ModuleChunk;
10 import java.io.IOException;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.HashSet;
17 * @author Eugene Zhuravlev
20 public abstract class ModuleLevelBuilder extends Builder {
21 private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.Builder");
23 private static final Key<Set<File>> ALL_AFFECTED_FILES_KEY = Key.create("_all_affected_files_");
24 private static final Key<Set<File>> ALL_COMPILED_FILES_KEY = Key.create("_all_compiled_files_");
26 public static enum ExitCode {
27 OK, ABORT, ADDITIONAL_PASS_REQUIRED
30 public abstract ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException;
32 public void cleanupResources(CompileContext context, ModuleChunk chunk) {
33 ALL_AFFECTED_FILES_KEY.set(context, null);
34 ALL_COMPILED_FILES_KEY.set(context, null);
41 * @param filesToCompile files compiled in this round
42 * @param successfullyCompiled
43 * @return true if additional compilation pass is required, false otherwise
46 public final boolean updateMappings(CompileContext context, final Mappings delta, ModuleChunk chunk, Collection<File> filesToCompile, Collection<File> successfullyCompiled) throws Exception {
48 boolean additionalPassRequired = false;
50 final Set<String> removedPaths = getRemovedPaths(context);
52 final Mappings globalMappings = context.getDataManager().getMappings();
54 //noinspection SynchronizationOnLocalVariableOrMethodParameter
55 synchronized (globalMappings) {
56 if (!context.isProjectRebuild() && context.shouldDifferentiate(chunk, context.isCompilingTests())) {
57 final Set<File> allCompiledFiles = getAllCompiledFilesContainer(context);
58 final Set<File> allAffectedFiles = getAllAffectedFilesContainer(context);
60 // mark as affected all files that were dirty before compilation
61 allAffectedFiles.addAll(filesToCompile);
62 // accumulate all successfully compiled in this round
63 allCompiledFiles.addAll(successfullyCompiled);
64 // unmark as affected all successfully compiled
65 allAffectedFiles.removeAll(successfullyCompiled);
67 final HashSet<File> affectedBeforeDif = new HashSet<File>(allAffectedFiles);
69 final boolean incremental = globalMappings.differentiate(
70 delta, removedPaths, filesToCompile, allCompiledFiles, allAffectedFiles
73 if (LOG.isDebugEnabled()) {
74 LOG.debug("Differentiate Results:");
75 LOG.debug(" Compiled Files:");
76 for (final File c : allCompiledFiles) {
77 LOG.debug(" " + c.getAbsolutePath());
79 LOG.debug(" Affected Files:");
80 for (final File c : allAffectedFiles) {
81 LOG.debug(" " + c.getAbsolutePath());
83 LOG.debug("End Of Differentiate Results.");
87 final Set<File> newlyAffectedFiles = new HashSet<File>(allAffectedFiles);
88 newlyAffectedFiles.removeAll(affectedBeforeDif);
89 newlyAffectedFiles.removeAll(allCompiledFiles); // the diff operation may have affected the class already compiled in thic compilation round
91 if (!newlyAffectedFiles.isEmpty()) {
92 for (File file : newlyAffectedFiles) {
93 context.markDirty(file);
95 additionalPassRequired = context.isMake() && chunkContainsAffectedFiles(context, chunk, newlyAffectedFiles);
99 additionalPassRequired = context.isMake();
100 context.markDirtyRecursively(chunk);
104 globalMappings.integrate(delta, successfullyCompiled, removedPaths);
107 return additionalPassRequired;
109 catch(RuntimeException e) {
110 final Throwable cause = e.getCause();
111 if (cause instanceof IOException) {
112 throw ((IOException)cause);
118 private static boolean chunkContainsAffectedFiles(CompileContext context, ModuleChunk chunk, final Set<File> affected) throws Exception {
119 final Set<Module> chunkModules = new HashSet<Module>(chunk.getModules());
120 if (!chunkModules.isEmpty()) {
121 for (File file : affected) {
122 final RootDescriptor moduleAndRoot = context.getModuleAndRoot(file);
123 if (moduleAndRoot != null && chunkModules.contains(moduleAndRoot.module)) {
131 private static Set<File> getAllAffectedFilesContainer(CompileContext context) {
132 Set<File> allAffectedFiles = ALL_AFFECTED_FILES_KEY.get(context);
133 if (allAffectedFiles == null) {
134 allAffectedFiles = new HashSet<File>();
135 ALL_AFFECTED_FILES_KEY.set(context, allAffectedFiles);
137 return allAffectedFiles;
140 private static Set<File> getAllCompiledFilesContainer(CompileContext context) {
141 Set<File> allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context);
142 if (allCompiledFiles == null) {
143 allCompiledFiles = new HashSet<File>();
144 ALL_COMPILED_FILES_KEY.set(context, allCompiledFiles);
146 return allCompiledFiles;
149 private static Set<String> getRemovedPaths(CompileContext context) {
150 final Set<String> removed = Paths.CHUNK_REMOVED_SOURCES_KEY.get(context);
151 return removed != null? removed : Collections.<String>emptySet();