merge BuildFSState and FSState into one class;

explicit API to mark file dirty for the current or the next compilation round
This commit is contained in:
Eugene Zhuravlev
2014-11-22 19:09:00 +01:00
parent a1009ef1c6
commit 644b166edf
11 changed files with 240 additions and 301 deletions

View File

@@ -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 {

View File

@@ -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());

View File

@@ -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();

View File

@@ -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<File> currentFiles = new THashSet<File>(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;

View File

@@ -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 <b>current</b> 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 <b>next</b> 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<JpsModule> modules = chunk.getModules();
Set<ModuleBuildTarget> targets = chunk.getTargets();
final Set<ModuleBuildTarget> dirtyTargets = new HashSet<ModuleBuildTarget>(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<JpsModule> 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<File> 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);

View File

@@ -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;

View File

@@ -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<Set<? extends BuildTarget<?>>> CONTEXT_TARGETS_KEY = Key.create("_fssfate_context_targets_");
private static final Key<Key<FilesDelta>> DELTA_KEY_SELECTOR = Key.create("_round_delta_key_selector_");
private static final Key<FilesDelta> NEXT_ROUND_DELTA_KEY = Key.create("_current_round_delta_");
private static final Key<FilesDelta> 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<BuildTarget<?>> myInitialScanPerformed = Collections.synchronizedSet(new HashSet<BuildTarget<?>>());
private final TObjectLongHashMap<File> myRegistrationStamps = new TObjectLongHashMap<File>(FileUtil.FILE_HASHING_STRATEGY);
private final Map<BuildTarget<?>, FilesDelta> myDeltas = Collections.synchronizedMap(new HashMap<BuildTarget<?>, FilesDelta>());
public BuildFSState(boolean alwaysScanFS) {
myAlwaysScanFS = alwaysScanFS;
}
public enum CompilationRound {CURRENT, NEXT}
public CompilationRound selectTargetRound(CompileContext context, CompilationRound targetRound) {
final Key<FilesDelta> 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<BuildTargetType<?>, BuildTarget<?>> targetsByType = new MultiMap<BuildTargetType<?>, 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<BuildTarget<?>> 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<String> 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<BuildRootDescriptor, Set<File>> 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<FilesDelta> 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<FilesDelta> 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) {

View File

@@ -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
}

View File

@@ -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<BuildTarget<?>, FilesDelta> myDeltas = Collections.synchronizedMap(new HashMap<BuildTarget<?>, FilesDelta>());
private final Set<BuildTarget<?>> myInitialScanPerformed = Collections.synchronizedSet(new HashSet<BuildTarget<?>>());
private final TObjectLongHashMap<File> myRegistrationStamps = new TObjectLongHashMap<File>(FileUtil.FILE_HASHING_STRATEGY);
public void save(DataOutput out) throws IOException {
MultiMap<BuildTargetType<?>, BuildTarget<?>> targetsByType = new MultiMap<BuildTargetType<?>, 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<BuildTarget<?>> 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<BuildRootDescriptor, Set<File>> 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<String> 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<String> 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<File> findPossibleSourcesForClass(JavaSourceRootDescriptor rd, final @Nullable String boundClassName) throws IOException {
if (boundClassName == null) {
return Collections.emptyList();