From 644b166edf092aa0cb2eee403f6a56d3138bfff3 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Sat, 22 Nov 2014 19:09:00 +0100 Subject: [PATCH] merge BuildFSState and FSState into one class; explicit API to mark file dirty for the current or the next compilation round --- .../jps/builders/java/JavaBuilderUtil.java | 5 +- .../org/jetbrains/jps/cmdline/BuildMain.java | 3 +- .../jetbrains/jps/cmdline/BuildSession.java | 7 +- .../jps/incremental/BuildOperations.java | 5 +- .../jps/incremental/FSOperations.java | 87 ++++---- .../jps/incremental/IncProjectBuilder.java | 5 +- .../jps/incremental/fs/BuildFSState.java | 196 ++++++++++++++---- .../jps/incremental/fs/CompilationRound.java | 24 +++ .../jetbrains/jps/incremental/fs/FSState.java | 193 ----------------- .../jps/incremental/groovy/GroovyBuilder.java | 5 +- .../compiler/FormsBindingManager.java | 11 +- 11 files changed, 240 insertions(+), 301 deletions(-) create mode 100644 jps/jps-builders/src/org/jetbrains/jps/incremental/fs/CompilationRound.java delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderUtil.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderUtil.java index 1f0cd41c7ffa..f6254cdf50cb 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderUtil.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaBuilderUtil.java @@ -30,6 +30,7 @@ import org.jetbrains.jps.builders.java.dependencyView.Callbacks; import org.jetbrains.jps.builders.java.dependencyView.Mappings; import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; import org.jetbrains.jps.incremental.*; +import org.jetbrains.jps.incremental.fs.CompilationRound; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.incremental.messages.ProgressMessage; @@ -140,7 +141,7 @@ public class JavaBuilderUtil { } for (File file : newlyAffectedFiles) { - FSOperations.markDirtyIfNotDeleted(context, file); + FSOperations.markDirtyIfNotDeleted(context, CompilationRound.NEXT, file); } additionalPassRequired = isCompileJavaIncrementally(context) && chunkContainsAffectedFiles(context, chunk, newlyAffectedFiles); } @@ -151,7 +152,7 @@ public class JavaBuilderUtil { context.processMessage(new ProgressMessage(messageText)); additionalPassRequired = isCompileJavaIncrementally(context); - FSOperations.markDirtyRecursively(context, chunk); + FSOperations.markDirtyRecursively(context, CompilationRound.NEXT, chunk); } } else { diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index f0e716318aa4..4f5d1030b224 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -43,7 +43,6 @@ import org.jetbrains.jps.incremental.BuilderRegistry; import org.jetbrains.jps.incremental.MessageHandler; import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.incremental.fs.BuildFSState; -import org.jetbrains.jps.incremental.fs.FSState; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.storage.BuildTargetsState; import org.jetbrains.jps.service.SharedThreadPool; @@ -154,7 +153,7 @@ public class BuildMain { final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile))); try { final int version = in.readInt(); - if (version == FSState.VERSION) { + if (version == BuildFSState.VERSION) { final long savedOrdinal = in.readLong(); final boolean hasWorkToDo = in.readBoolean();// must skip "has-work-to-do" flag fsState.load(in, pd.getModel(), pd.getBuildRootIndex()); diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java index 549e5cddb953..bece5fe363fa 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java @@ -36,7 +36,6 @@ import org.jetbrains.jps.incremental.MessageHandler; import org.jetbrains.jps.incremental.TargetTypeRegistry; import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.incremental.fs.BuildFSState; -import org.jetbrains.jps.incremental.fs.FSState; import org.jetbrains.jps.incremental.messages.*; import org.jetbrains.jps.incremental.storage.Timestamps; import org.jetbrains.jps.model.module.JpsModule; @@ -445,7 +444,7 @@ final class BuildSession implements Runnable, CanceledStatus { final BufferExposingByteArrayOutputStream bytes = new BufferExposingByteArrayOutputStream(); final DataOutputStream out = new DataOutputStream(bytes); try { - out.writeInt(FSState.VERSION); + out.writeInt(BuildFSState.VERSION); out.writeLong(ordinal); out.writeBoolean(false); while (true) { @@ -475,7 +474,7 @@ final class BuildSession implements Runnable, CanceledStatus { final BufferExposingByteArrayOutputStream bytes = new BufferExposingByteArrayOutputStream(); final DataOutputStream out = new DataOutputStream(bytes); try { - out.writeInt(FSState.VERSION); + out.writeInt(BuildFSState.VERSION); out.writeLong(myLastEventOrdinal); out.writeBoolean(hasWorkToDo(state, pd)); state.save(out); @@ -542,7 +541,7 @@ final class BuildSession implements Runnable, CanceledStatus { } final DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes)); final int version = in.readInt(); - if (version != FSState.VERSION) { + if (version != BuildFSState.VERSION) { return null; } final long savedOrdinal = in.readLong(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildOperations.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildOperations.java index 882ec81062be..3799217eee35 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildOperations.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildOperations.java @@ -26,6 +26,7 @@ import org.jetbrains.jps.builders.logging.ProjectBuilderLogger; import org.jetbrains.jps.builders.storage.SourceToOutputMapping; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.fs.BuildFSState; +import org.jetbrains.jps.incremental.fs.CompilationRound; import org.jetbrains.jps.incremental.messages.DoneSomethingNotification; import org.jetbrains.jps.incremental.messages.FileDeletedEvent; import org.jetbrains.jps.incremental.storage.BuildDataManager; @@ -49,7 +50,7 @@ public class BuildOperations { final Timestamps timestamps = pd.timestamps.getStorage(); final BuildTargetConfiguration configuration = pd.getTargetsState().getTargetConfiguration(target); if (context.isProjectRebuild()) { - FSOperations.markDirtyFiles(context, target, timestamps, true, null, null); + FSOperations.markDirtyFiles(context, target, CompilationRound.CURRENT, timestamps, true, null, null); pd.fsState.markInitialScanPerformed(target); configuration.save(context); } @@ -68,7 +69,7 @@ public class BuildOperations { final ProjectDescriptor pd = context.getProjectDescriptor(); final Timestamps timestamps = pd.timestamps.getStorage(); final THashSet currentFiles = new THashSet(FileUtil.FILE_HASHING_STRATEGY); - FSOperations.markDirtyFiles(context, target, timestamps, forceMarkDirty, currentFiles, null); + FSOperations.markDirtyFiles(context, target, CompilationRound.CURRENT, timestamps, forceMarkDirty, currentFiles, null); // handle deleted paths final BuildFSState fsState = pd.fsState; 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 25a4d902050b..0aefc2193c23 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java @@ -29,7 +29,7 @@ import org.jetbrains.jps.builders.impl.BuildTargetChunk; import org.jetbrains.jps.builders.java.JavaBuilderUtil; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import org.jetbrains.jps.cmdline.ProjectDescriptor; -import org.jetbrains.jps.incremental.fs.BuildFSState; +import org.jetbrains.jps.incremental.fs.CompilationRound; import org.jetbrains.jps.incremental.storage.Timestamps; import org.jetbrains.jps.model.java.JpsJavaClasspathKind; import org.jetbrains.jps.model.java.JpsJavaExtensionService; @@ -50,52 +50,51 @@ public class FSOperations { /** * @param context + * @param round * @param file - * @return true if file is marked as "dirty" in the current compilation round + * @return true if file is marked as "dirty" in the specified compilation round * @throws IOException */ - public static boolean isMarkedDirty(CompileContext context, final File file) throws IOException { + public static boolean isMarkedDirty(CompileContext context, final CompilationRound round, final File file) throws IOException { final JavaSourceRootDescriptor rd = context.getProjectDescriptor().getBuildRootIndex().findJavaRootDescriptor(context, file); if (rd != null) { final ProjectDescriptor pd = context.getProjectDescriptor(); - return pd.fsState.isMarkedForRecompilation(context, rd, file); + return pd.fsState.isMarkedForRecompilation(context, round, rd, file); } return false; } - + /** + * @deprecated use markDirty(CompileContext context, final CompilationRound round, final File file) + * * Note: marked file will well be visible as "dirty" only on the next compilation round! * @throws IOException + * */ public static void markDirty(CompileContext context, final File file) throws IOException { + markDirty(context, CompilationRound.NEXT, file); + } + + public static void markDirty(CompileContext context, final CompilationRound round, final File file) throws IOException { final JavaSourceRootDescriptor rd = context.getProjectDescriptor().getBuildRootIndex().findJavaRootDescriptor(context, file); if (rd != null) { final ProjectDescriptor pd = context.getProjectDescriptor(); - pd.fsState.markDirty(context, file, rd, pd.timestamps.getStorage(), false); + pd.fsState.markDirty(context, round, file, rd, pd.timestamps.getStorage(), false); } } /** - * Note: marked file will well be visible as "dirty" for the current compilation round, - * all builders that run in this compilation round after the one that marked the file, will see the 'dirty' state. - * @throws IOException + * @deprecated use markDirtyIfNotDeleted(CompileContext context, final CompilationRound round, final File file) */ - public static void markDirtyForCurrentRound(CompileContext context, final File file) throws IOException { - final ProjectDescriptor pd = context.getProjectDescriptor(); - final BuildFSState.CompilationRound previous = pd.fsState.selectTargetRound(context, BuildFSState.CompilationRound.CURRENT); - try { - markDirty(context, file); - } - finally { - pd.fsState.selectTargetRound(context, previous); - } + public static void markDirtyIfNotDeleted(CompileContext context, final File file) throws IOException { + markDirtyIfNotDeleted(context, CompilationRound.NEXT, file); } - public static void markDirtyIfNotDeleted(CompileContext context, final File file) throws IOException { + public static void markDirtyIfNotDeleted(CompileContext context, final CompilationRound round, final File file) throws IOException { final JavaSourceRootDescriptor rd = context.getProjectDescriptor().getBuildRootIndex().findJavaRootDescriptor(context, file); if (rd != null) { final ProjectDescriptor pd = context.getProjectDescriptor(); - pd.fsState.markDirtyIfNotDeleted(context, file, rd, pd.timestamps.getStorage()); + pd.fsState.markDirtyIfNotDeleted(context, round, file, rd, pd.timestamps.getStorage()); } } @@ -107,30 +106,28 @@ public class FSOperations { } } + /** + * @deprecated use markDirty(CompileContext context, final CompilationRound round, final ModuleChunk chunk, @Nullable FileFilter filter) + */ public static void markDirty(CompileContext context, final ModuleChunk chunk, @Nullable FileFilter filter) throws IOException { + markDirty(context, CompilationRound.NEXT, chunk, filter); + } + + public static void markDirty(CompileContext context, final CompilationRound round, final ModuleChunk chunk, @Nullable FileFilter filter) throws IOException { final ProjectDescriptor pd = context.getProjectDescriptor(); for (ModuleBuildTarget target : chunk.getTargets()) { - markDirtyFiles(context, target, pd.timestamps.getStorage(), true, null, filter); + markDirtyFiles(context, target, round, pd.timestamps.getStorage(), true, null, filter); } } /** - * Note: marked files will be visible as "dirty" for the current compilation round, - * all builders that run in this compilation round after the one that marked files, will see the 'dirty' state. - * @throws IOException + * @deprecated use markDirtyRecursively(CompileContext context, final CompilationRound round, ModuleChunk chunk) */ - public static void markDirtyForCurrentRound(CompileContext context, final ModuleChunk chunk, @Nullable FileFilter filter) throws IOException { - final ProjectDescriptor pd = context.getProjectDescriptor(); - final BuildFSState.CompilationRound previous = pd.fsState.selectTargetRound(context, BuildFSState.CompilationRound.CURRENT); - try { - markDirty(context, chunk, filter); - } - finally { - pd.fsState.selectTargetRound(context, previous); - } + public static void markDirtyRecursively(CompileContext context, ModuleChunk chunk) throws IOException { + markDirtyRecursively(context, CompilationRound.NEXT, chunk); } - public static void markDirtyRecursively(CompileContext context, ModuleChunk chunk) throws IOException { + public static void markDirtyRecursively(CompileContext context, final CompilationRound round, ModuleChunk chunk) throws IOException { Set modules = chunk.getModules(); Set targets = chunk.getTargets(); final Set dirtyTargets = new HashSet(targets); @@ -163,7 +160,7 @@ public class FSOperations { final Timestamps timestamps = context.getProjectDescriptor().timestamps.getStorage(); for (ModuleBuildTarget target : dirtyTargets) { - markDirtyFiles(context, target, timestamps, true, null, null); + markDirtyFiles(context, target, round, timestamps, true, null, null); } if (JavaBuilderUtil.isCompileJavaIncrementally(context)) { @@ -173,17 +170,7 @@ public class FSOperations { } } } - public static void markDirtyRecursivelyForCurrentRound(CompileContext context, ModuleChunk chunk) throws IOException { - final ProjectDescriptor pd = context.getProjectDescriptor(); - final BuildFSState.CompilationRound previous = pd.fsState.selectTargetRound(context, BuildFSState.CompilationRound.CURRENT); - try { - markDirtyRecursively(context, chunk); - } - finally { - pd.fsState.selectTargetRound(context, previous); - } - } - + private static Set getDependentModulesRecursively(final JpsModule module, final JpsJavaClasspathKind kind) { return JpsJavaExtensionService.dependencies(module).includedIn(kind).recursivelyExportedOnly().getModules(); } @@ -200,6 +187,7 @@ public class FSOperations { static void markDirtyFiles(CompileContext context, BuildTarget target, + final CompilationRound round, Timestamps timestamps, boolean forceMarkDirty, @Nullable THashSet currentFiles, @@ -214,12 +202,13 @@ public class FSOperations { context.getProjectDescriptor().fsState.clearRecompile(rd); } final FSCache fsCache = rd.canUseFileCache() ? context.getProjectDescriptor().getFSCache() : FSCache.NO_CACHE; - traverseRecursively(context, rd, rd.getRootFile(), timestamps, forceMarkDirty, currentFiles, filter, fsCache); + traverseRecursively(context, rd, round, rd.getRootFile(), timestamps, forceMarkDirty, currentFiles, filter, fsCache); } } private static void traverseRecursively(CompileContext context, final BuildRootDescriptor rd, + final CompilationRound round, final File file, @NotNull final Timestamps tsStorage, final boolean forceDirty, @@ -229,7 +218,7 @@ public class FSOperations { if (children != null) { // is directory if (children.length > 0 && rootIndex.isDirectoryAccepted(file, rd)) { for (File child : children) { - traverseRecursively(context, rd, child, tsStorage, forceDirty, currentFiles, filter, fsCache); + traverseRecursively(context, rd, round, child, tsStorage, forceDirty, currentFiles, filter, fsCache); } } } @@ -243,7 +232,7 @@ public class FSOperations { // if it is full project rebuild, all storages are already completely cleared; // so passing null because there is no need to access the storage to clear non-existing data final Timestamps marker = context.isProjectRebuild() ? null : tsStorage; - context.getProjectDescriptor().fsState.markDirty(context, file, rd, marker, false); + context.getProjectDescriptor().fsState.markDirty(context, round, file, rd, marker, false); } if (currentFiles != null) { currentFiles.add(file); 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 b5568aaa5c6d..97f64858b535 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -49,6 +49,7 @@ import org.jetbrains.jps.builders.storage.SourceToOutputMapping; import org.jetbrains.jps.cmdline.BuildRunner; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.fs.BuildFSState; +import org.jetbrains.jps.incremental.fs.CompilationRound; import org.jetbrains.jps.incremental.messages.*; import org.jetbrains.jps.incremental.storage.BuildTargetConfiguration; import org.jetbrains.jps.incremental.storage.OneToManyPathsMapping; @@ -1048,7 +1049,7 @@ public class IncProjectBuilder { for (String formPath : boundForms) { final File formFile = new File(formPath); if (formFile.exists()) { - FSOperations.markDirty(context, formFile); + FSOperations.markDirty(context, CompilationRound.CURRENT, formFile); } } sourceToFormMap.remove(deletedSource); @@ -1163,7 +1164,7 @@ public class IncProjectBuilder { try { // forcibly mark all files in the chunk dirty context.getProjectDescriptor().fsState.clearContextRoundData(context); - FSOperations.markDirty(context, chunk, null); + FSOperations.markDirty(context, CompilationRound.NEXT, chunk, null); // reverting to the beginning myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount; stageCount = myTotalModuleLevelBuilderCount; 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 2bb042066308..75d3d05d546d 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 @@ -18,61 +18,154 @@ package org.jetbrains.jps.incremental.fs; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileSystemUtil; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.io.IOUtil; +import gnu.trove.TObjectLongHashMap; 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.BuildTarget; -import org.jetbrains.jps.builders.FileProcessor; +import org.jetbrains.jps.builders.*; import org.jetbrains.jps.builders.impl.BuildTargetChunk; -import org.jetbrains.jps.incremental.CompileContext; -import org.jetbrains.jps.incremental.CompileScope; -import org.jetbrains.jps.incremental.ModuleBuildTarget; -import org.jetbrains.jps.incremental.Utils; +import org.jetbrains.jps.incremental.*; import org.jetbrains.jps.incremental.storage.Timestamps; +import org.jetbrains.jps.model.JpsModel; +import java.io.DataInputStream; +import java.io.DataOutput; import java.io.File; import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Eugene Zhuravlev * Date: 12/16/11 */ -public class BuildFSState extends FSState { +public class BuildFSState { + public static final int VERSION = 3; private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.BuildFSState"); private static final Key>> CONTEXT_TARGETS_KEY = Key.create("_fssfate_context_targets_"); - private static final Key> DELTA_KEY_SELECTOR = Key.create("_round_delta_key_selector_"); private static final Key NEXT_ROUND_DELTA_KEY = Key.create("_current_round_delta_"); private static final Key CURRENT_ROUND_DELTA_KEY = Key.create("_last_round_delta_"); // when true, will always determine dirty files by scanning FS and comparing timestamps // alternatively, when false, after first scan will rely on external notifications about changes private final boolean myAlwaysScanFS; + private final Set> myInitialScanPerformed = Collections.synchronizedSet(new HashSet>()); + private final TObjectLongHashMap myRegistrationStamps = new TObjectLongHashMap(FileUtil.FILE_HASHING_STRATEGY); + private final Map, FilesDelta> myDeltas = Collections.synchronizedMap(new HashMap, FilesDelta>()); public BuildFSState(boolean alwaysScanFS) { myAlwaysScanFS = alwaysScanFS; } - public enum CompilationRound {CURRENT, NEXT} - - public CompilationRound selectTargetRound(CompileContext context, CompilationRound targetRound) { - final Key previous = DELTA_KEY_SELECTOR.get(context); - DELTA_KEY_SELECTOR.set(context, targetRound == CompilationRound.CURRENT? CURRENT_ROUND_DELTA_KEY : null); - if (previous == null) { - return CompilationRound.NEXT; + public void save(DataOutput out) throws IOException { + MultiMap, BuildTarget> targetsByType = new MultiMap, BuildTarget>(); + for (BuildTarget target : myInitialScanPerformed) { + targetsByType.putValue(target.getTargetType(), target); + } + out.writeInt(targetsByType.size()); + for (BuildTargetType type : targetsByType.keySet()) { + IOUtil.writeString(type.getTypeId(), out); + Collection> targets = targetsByType.get(type); + out.writeInt(targets.size()); + for (BuildTarget target : targets) { + IOUtil.writeString(target.getId(), out); + getDelta(target).save(out); + } } - return previous == NEXT_ROUND_DELTA_KEY? CompilationRound.NEXT : CompilationRound.CURRENT; - } - - @Override - public boolean isInitialScanPerformed(BuildTarget target) { - return !myAlwaysScanFS && super.isInitialScanPerformed(target); } - @Override + public void load(DataInputStream in, JpsModel model, final BuildRootIndex buildRootIndex) throws IOException { + final TargetTypeRegistry registry = TargetTypeRegistry.getInstance(); + int typeCount = in.readInt(); + while (typeCount-- > 0) { + final String typeId = IOUtil.readString(in); + int targetCount = in.readInt(); + BuildTargetType type = registry.getTargetType(typeId); + BuildTargetLoader loader = type != null ? type.createLoader(model) : null; + while (targetCount-- > 0) { + final String id = IOUtil.readString(in); + boolean loaded = false; + if (loader != null) { + BuildTarget target = loader.createTarget(id); + if (target != null) { + getDelta(target).load(in, target, buildRootIndex); + myInitialScanPerformed.add(target); + loaded = true; + } + } + if (!loaded) { + LOG.info("Skipping unknown target (typeId=" + typeId + ", type=" + type + ", id=" + id + ")"); + FilesDelta.skip(in); + } + } + } + } + + public final void clearRecompile(final BuildRootDescriptor rd) { + getDelta(rd.getTarget()).clearRecompile(rd); + } + + public long getEventRegistrationStamp(File file) { + return myRegistrationStamps.get(file); + } + + public boolean hasWorkToDo(BuildTarget target) { + if (!myInitialScanPerformed.contains(target)) { + return true; + } + FilesDelta delta = myDeltas.get(target); + return delta != null && delta.hasChanges(); + } + + public void markInitialScanPerformed(BuildTarget target) { + myInitialScanPerformed.add(target); + } + + public void registerDeleted(BuildTarget target, final File file, @Nullable Timestamps tsStorage) throws IOException { + registerDeleted(target, file); + if (tsStorage != null) { + tsStorage.removeStamp(file, target); + } + } + + public void registerDeleted(BuildTarget target, File file) { + getDelta(target).addDeleted(file); + } + + public void clearDeletedPaths(BuildTarget target) { + final FilesDelta delta = myDeltas.get(target); + if (delta != null) { + delta.clearDeletedPaths(); + } + } + + public Collection getAndClearDeletedPaths(BuildTarget target) { + final FilesDelta delta = myDeltas.get(target); + if (delta != null) { + return delta.getAndClearDeletedPaths(); + } + return Collections.emptyList(); + } + + @NotNull + private FilesDelta getDelta(BuildTarget buildTarget) { + synchronized (myDeltas) { + FilesDelta delta = myDeltas.get(buildTarget); + if (delta == null) { + delta = new FilesDelta(); + myDeltas.put(buildTarget, delta); + } + return delta; + } + } + + + public boolean isInitialScanPerformed(BuildTarget target) { + return !myAlwaysScanFS && myInitialScanPerformed.contains(target); + } + public Map> getSourcesToRecompile(@NotNull CompileContext context, BuildTarget target) { if (target instanceof ModuleBuildTarget) { // multiple compilation rounds are applicable to ModuleBuildTarget only @@ -81,11 +174,11 @@ public class BuildFSState extends FSState { return lastRoundDelta.getSourcesToRecompile(); } } - return super.getSourcesToRecompile(context, target); + return getDelta(target).getSourcesToRecompile(); } - public boolean isMarkedForRecompilation(@Nullable CompileContext context, BuildRootDescriptor rd, File file) { - FilesDelta delta = getRoundDelta(CURRENT_ROUND_DELTA_KEY, context); + public boolean isMarkedForRecompilation(@Nullable CompileContext context, CompilationRound round, BuildRootDescriptor rd, File file) { + FilesDelta delta = getRoundDelta(round == CompilationRound.NEXT? NEXT_ROUND_DELTA_KEY : CURRENT_ROUND_DELTA_KEY, context); if (delta == null) { delta = getDelta(rd.getTarget()); } @@ -102,14 +195,34 @@ public class BuildFSState extends FSState { * Note: marked file will well be visible as "dirty" only on the next compilation round! * @throws IOException */ - @Override - public boolean markDirty(@Nullable CompileContext context, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage, boolean saveEventStamp) throws IOException { - final Key deltaKey = DELTA_KEY_SELECTOR.get(context, NEXT_ROUND_DELTA_KEY); - final FilesDelta roundDelta = getRoundDelta(deltaKey, context); + public final boolean markDirty(@Nullable CompileContext context, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage, boolean saveEventStamp) throws IOException { + return markDirty(context, CompilationRound.NEXT, file, rd, tsStorage, saveEventStamp); + } + + public boolean markDirty(@Nullable CompileContext context, CompilationRound round, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage, boolean saveEventStamp) throws IOException { + final FilesDelta roundDelta = getRoundDelta(round == CompilationRound.NEXT? NEXT_ROUND_DELTA_KEY : CURRENT_ROUND_DELTA_KEY, context); if (roundDelta != null && isInCurrentContextTargets(context, rd)) { roundDelta.markRecompile(rd, file); } - return super.markDirty(context, file, rd, tsStorage, saveEventStamp); + + final boolean marked = getDelta(rd.getTarget()).markRecompile(rd, file); + if (marked) { + if (LOG.isDebugEnabled()) { + LOG.debug(rd.getTarget() + ": MARKED DIRTY: " + file.getPath()); + } + if (saveEventStamp) { + myRegistrationStamps.put(file, System.currentTimeMillis()); + } + if (tsStorage != null) { + tsStorage.removeStamp(file, rd.getTarget()); + } + } + else { + if (LOG.isDebugEnabled()) { + LOG.debug(rd.getTarget() + ": NOT MARKED DIRTY: " + file.getPath()); + } + } + return marked; } private static boolean isInCurrentContextTargets(CompileContext context, BuildRootDescriptor rd) { @@ -120,12 +233,13 @@ public class BuildFSState extends FSState { return targets.contains(rd.getTarget()); } - @Override - public boolean markDirtyIfNotDeleted(@Nullable CompileContext context, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage) throws IOException { - final boolean marked = super.markDirtyIfNotDeleted(context, file, rd, tsStorage); + public boolean markDirtyIfNotDeleted(@Nullable CompileContext context, CompilationRound round, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage) throws IOException { + final boolean marked = getDelta(rd.getTarget()).markRecompileIfNotDeleted(rd, file); + if (marked && tsStorage != null) { + tsStorage.removeStamp(file, rd.getTarget()); + } if (marked) { - final Key deltaKey = DELTA_KEY_SELECTOR.get(context, NEXT_ROUND_DELTA_KEY); - final FilesDelta roundDelta = getRoundDelta(deltaKey, context); + final FilesDelta roundDelta = getRoundDelta(round == CompilationRound.NEXT? NEXT_ROUND_DELTA_KEY : CURRENT_ROUND_DELTA_KEY, context); if (roundDelta != null) { if (isInCurrentContextTargets(context, rd)) { roundDelta.markRecompile(rd, file); @@ -138,7 +252,9 @@ public class BuildFSState extends FSState { public void clearAll() { clearContextRoundData(null); clearContextChunk(null); - super.clearAll(); + myInitialScanPerformed.clear(); + myDeltas.clear(); + myRegistrationStamps.clear(); } public void clearContextRoundData(@Nullable CompileContext context) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/CompilationRound.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/CompilationRound.java new file mode 100644 index 000000000000..ac4336092ff9 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/CompilationRound.java @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2014 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.fs; + +/** +* @author Eugene Zhuravlev +* Date: 22-Nov-14 +*/ +public enum CompilationRound { + CURRENT, NEXT +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java deleted file mode 100644 index dde4f12065db..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2000-2012 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.fs; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.containers.MultiMap; -import com.intellij.util.io.IOUtil; -import gnu.trove.TObjectLongHashMap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.builders.*; -import org.jetbrains.jps.incremental.CompileContext; -import org.jetbrains.jps.incremental.TargetTypeRegistry; -import org.jetbrains.jps.incremental.storage.Timestamps; -import org.jetbrains.jps.model.JpsModel; - -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.File; -import java.io.IOException; -import java.util.*; - -/** - * @author Eugene Zhuravlev - * Date: 4/20/12 - */ -public class FSState { - public static final int VERSION = 3; - private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.FSState"); - private final Map, FilesDelta> myDeltas = Collections.synchronizedMap(new HashMap, FilesDelta>()); - private final Set> myInitialScanPerformed = Collections.synchronizedSet(new HashSet>()); - private final TObjectLongHashMap myRegistrationStamps = new TObjectLongHashMap(FileUtil.FILE_HASHING_STRATEGY); - - public void save(DataOutput out) throws IOException { - MultiMap, BuildTarget> targetsByType = new MultiMap, BuildTarget>(); - for (BuildTarget target : myInitialScanPerformed) { - targetsByType.putValue(target.getTargetType(), target); - } - out.writeInt(targetsByType.size()); - for (BuildTargetType type : targetsByType.keySet()) { - IOUtil.writeString(type.getTypeId(), out); - Collection> targets = targetsByType.get(type); - out.writeInt(targets.size()); - for (BuildTarget target : targets) { - IOUtil.writeString(target.getId(), out); - getDelta(target).save(out); - } - } - } - - public void load(DataInputStream in, JpsModel model, final BuildRootIndex buildRootIndex) throws IOException { - final TargetTypeRegistry registry = TargetTypeRegistry.getInstance(); - int typeCount = in.readInt(); - while (typeCount-- > 0) { - final String typeId = IOUtil.readString(in); - int targetCount = in.readInt(); - BuildTargetType type = registry.getTargetType(typeId); - BuildTargetLoader loader = type != null ? type.createLoader(model) : null; - while (targetCount-- > 0) { - final String id = IOUtil.readString(in); - boolean loaded = false; - if (loader != null) { - BuildTarget target = loader.createTarget(id); - if (target != null) { - getDelta(target).load(in, target, buildRootIndex); - myInitialScanPerformed.add(target); - loaded = true; - } - } - if (!loaded) { - LOG.info("Skipping unknown target (typeId=" + typeId + ", type=" + type + ", id=" + id + ")"); - FilesDelta.skip(in); - } - } - } - } - - public void clearAll() { - myInitialScanPerformed.clear(); - myDeltas.clear(); - myRegistrationStamps.clear(); - } - - public final void clearRecompile(final BuildRootDescriptor rd) { - getDelta(rd.getTarget()).clearRecompile(rd); - } - - public boolean markDirty(@Nullable CompileContext context, final File file, final BuildRootDescriptor rd, final @Nullable Timestamps tsStorage, boolean saveEventStamp) throws IOException { - final boolean marked = getDelta(rd.getTarget()).markRecompile(rd, file); - if (marked) { - if (LOG.isDebugEnabled()) { - LOG.debug(rd.getTarget() + ": MARKED DIRTY: " + file.getPath()); - } - if (saveEventStamp) { - myRegistrationStamps.put(file, System.currentTimeMillis()); - } - if (tsStorage != null) { - tsStorage.removeStamp(file, rd.getTarget()); - } - } - else { - if (LOG.isDebugEnabled()) { - LOG.debug(rd.getTarget() + ": NOT MARKED DIRTY: " + file.getPath()); - } - } - return marked; - } - - public long getEventRegistrationStamp(File file) { - return myRegistrationStamps.get(file); - } - - public boolean markDirtyIfNotDeleted(@Nullable CompileContext context, - final File file, - final BuildRootDescriptor rd, - final @Nullable Timestamps tsStorage) throws IOException { - final boolean marked = getDelta(rd.getTarget()).markRecompileIfNotDeleted(rd, file); - if (marked && tsStorage != null) { - tsStorage.removeStamp(file, rd.getTarget()); - } - return marked; - } - - public void registerDeleted(BuildTarget target, final File file, @Nullable Timestamps tsStorage) throws IOException { - registerDeleted(target, file); - if (tsStorage != null) { - tsStorage.removeStamp(file, target); - } - } - - public void registerDeleted(BuildTarget target, File file) { - getDelta(target).addDeleted(file); - } - - public Map> getSourcesToRecompile(@NotNull CompileContext context, BuildTarget target) { - return getDelta(target).getSourcesToRecompile(); - } - - public void clearDeletedPaths(BuildTarget target) { - final FilesDelta delta = myDeltas.get(target); - if (delta != null) { - delta.clearDeletedPaths(); - } - } - - public Collection getAndClearDeletedPaths(BuildTarget target) { - final FilesDelta delta = myDeltas.get(target); - if (delta != null) { - return delta.getAndClearDeletedPaths(); - } - return Collections.emptyList(); - } - - @NotNull - protected final FilesDelta getDelta(BuildTarget buildTarget) { - synchronized (myDeltas) { - FilesDelta delta = myDeltas.get(buildTarget); - if (delta == null) { - delta = new FilesDelta(); - myDeltas.put(buildTarget, delta); - } - return delta; - } - } - - public boolean hasWorkToDo(BuildTarget target) { - if (!myInitialScanPerformed.contains(target)) return true; - FilesDelta delta = myDeltas.get(target); - return delta != null && delta.hasChanges(); - } - - public void markInitialScanPerformed(BuildTarget target) { - myInitialScanPerformed.add(target); - } - - public boolean isInitialScanPerformed(BuildTarget target) { - return myInitialScanPerformed.contains(target); - } -} diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index 98542511bd57..fdc08aa7e676 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java @@ -45,6 +45,7 @@ import org.jetbrains.jps.builders.storage.SourceToOutputMapping; import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.*; +import org.jetbrains.jps.incremental.fs.CompilationRound; import org.jetbrains.jps.incremental.java.ClassPostProcessor; import org.jetbrains.jps.incremental.java.JavaBuilder; import org.jetbrains.jps.incremental.messages.BuildMessage; @@ -568,8 +569,8 @@ public class GroovyBuilder extends ModuleLevelBuilder { } try { final File groovyFile = new File(groovy); - if (!FSOperations.isMarkedDirty(context, groovyFile)) { - FSOperations.markDirty(context, groovyFile); + if (!FSOperations.isMarkedDirty(context, CompilationRound.CURRENT, groovyFile)) { + FSOperations.markDirty(context, CompilationRound.NEXT, groovyFile); FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, Boolean.TRUE); } } diff --git a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java index ff4e468b6ee2..5bbc280f40cc 100644 --- a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java +++ b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java @@ -29,6 +29,7 @@ import org.jetbrains.jps.builders.FileProcessor; import org.jetbrains.jps.builders.java.JavaBuilderUtil; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; import org.jetbrains.jps.incremental.*; +import org.jetbrains.jps.incremental.fs.CompilationRound; import org.jetbrains.jps.incremental.java.CopyResourcesUtil; import org.jetbrains.jps.incremental.java.FormsParsing; import org.jetbrains.jps.incremental.storage.OneToManyPathsMapping; @@ -89,7 +90,7 @@ public class FormsBindingManager extends FormsBuilder { @Override public List getCompilableFileExtensions() { - return Arrays.asList(FORM_EXTENSION); + return Collections.singletonList(FORM_EXTENSION); } @Override @@ -110,7 +111,7 @@ public class FormsBindingManager extends FormsBuilder { // force compilation of all forms, but only once per chunk if (!FORMS_REBUILD_FORCED.get(context, Boolean.FALSE)) { FORMS_REBUILD_FORCED.set(context, Boolean.TRUE); - FSOperations.markDirtyForCurrentRound(context, chunk, FORM_SOURCES_FILTER); + FSOperations.markDirty(context, CompilationRound.CURRENT, chunk, FORM_SOURCES_FILTER); } } @@ -138,7 +139,7 @@ public class FormsBindingManager extends FormsBuilder { for (File boundSource : sources) { if (!excludes.isExcluded(boundSource)) { addBinding(boundSource, form, srcToForms); - FSOperations.markDirtyForCurrentRound(context, boundSource); + FSOperations.markDirty(context, CompilationRound.CURRENT, boundSource); filesToCompile.put(boundSource, target); exitCode = ExitCode.OK; } @@ -156,7 +157,7 @@ public class FormsBindingManager extends FormsBuilder { final File formFile = new File(formPath); if (!excludes.isExcluded(formFile) && formFile.exists()) { addBinding(srcFile, formFile, srcToForms); - FSOperations.markDirtyForCurrentRound(context, formFile); + FSOperations.markDirty(context, CompilationRound.CURRENT, formFile); formsToCompile.put(formFile, target); exitCode = ExitCode.OK; } @@ -245,7 +246,7 @@ public class FormsBindingManager extends FormsBuilder { } } - @Nullable + @NotNull private static Collection findPossibleSourcesForClass(JavaSourceRootDescriptor rd, final @Nullable String boundClassName) throws IOException { if (boundClassName == null) { return Collections.emptyList();