restore event registration timestamp check to properly handle files changed during compilation

This commit is contained in:
Eugene Zhuravlev
2015-09-25 12:51:04 +02:00
parent 686bc520d3
commit 4c86da03ce
5 changed files with 27 additions and 13 deletions
@@ -210,7 +210,7 @@ public class BuildRunner {
}
fileSet.add(file);
if (targetTypesToForceBuild.contains(descriptor.getTarget().getTargetType())) {
pd.fsState.markDirty(null, file, descriptor, timestamps);
pd.fsState.markDirty(null, file, descriptor, timestamps, false);
}
}
}
@@ -253,7 +253,7 @@ final class BuildSession implements Runnable, CanceledStatus {
else {
// apply events to already loaded state
try {
applyFSEvent(pd, myInitialFSDelta);
applyFSEvent(pd, myInitialFSDelta, false);
}
catch (Throwable e) {
LOG.error(e);
@@ -269,7 +269,7 @@ final class BuildSession implements Runnable, CanceledStatus {
try {
try {
fsState.load(fsStateStream, pd.getModel(), pd.getBuildRootIndex());
applyFSEvent(pd, myInitialFSDelta);
applyFSEvent(pd, myInitialFSDelta, false);
TimingLog.LOG.debug("FS Delta loaded");
}
finally {
@@ -349,7 +349,7 @@ final class BuildSession implements Runnable, CanceledStatus {
@Override
public void run() {
try {
applyFSEvent(myProjectDescriptor, event);
applyFSEvent(myProjectDescriptor, event, true);
myLastEventOrdinal += 1;
}
catch (IOException e) {
@@ -378,7 +378,7 @@ final class BuildSession implements Runnable, CanceledStatus {
}
}
private static void applyFSEvent(ProjectDescriptor pd, @Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent event) throws IOException {
private static void applyFSEvent(ProjectDescriptor pd, @Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent event, final boolean saveEventStamp) throws IOException {
if (event == null) {
return;
}
@@ -425,7 +425,7 @@ final class BuildSession implements Runnable, CanceledStatus {
pd.getFSCache().clear();
cacheCleared = true;
}
pd.fsState.markDirty(null, file, descriptor, timestamps);
pd.fsState.markDirty(null, file, descriptor, timestamps, saveEventStamp);
}
else {
if (LOG.isDebugEnabled()) {
@@ -47,7 +47,7 @@ class ChainedTargetsBuildListener implements BuildListener {
Collection<BuildRootDescriptor> descriptors = rootsIndex.findAllParentDescriptors(file, null, myContext);
for (BuildRootDescriptor descriptor : descriptors) {
try {
fsState.markDirty(myContext, file, descriptor, myContext.getProjectDescriptor().timestamps.getStorage());
fsState.markDirty(myContext, file, descriptor, myContext.getProjectDescriptor().timestamps.getStorage(), false);
}
catch (IOException ignored) {
}
@@ -82,7 +82,7 @@ public class FSOperations {
final JavaSourceRootDescriptor rd = context.getProjectDescriptor().getBuildRootIndex().findJavaRootDescriptor(context, file);
if (rd != null) {
final ProjectDescriptor pd = context.getProjectDescriptor();
pd.fsState.markDirty(context, round, file, rd, pd.timestamps.getStorage());
pd.fsState.markDirty(context, round, file, rd, pd.timestamps.getStorage(), false);
}
}
@@ -245,7 +245,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, round, file, rd, marker);
context.getProjectDescriptor().fsState.markDirty(context, round, file, rd, marker, false);
}
if (currentFiles != null) {
currentFiles.add(file);
@@ -18,9 +18,11 @@ 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.SmartList;
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;
@@ -51,6 +53,7 @@ public class BuildFSState {
// 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) {
@@ -105,6 +108,10 @@ public class BuildFSState {
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;
@@ -185,11 +192,11 @@ public class BuildFSState {
* Note: marked file will well be visible as "dirty" only on the next compilation round!
* @throws IOException
*/
public final boolean markDirty(@Nullable CompileContext context, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage) throws IOException {
return markDirty(context, CompilationRound.NEXT, file, rd, tsStorage);
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) throws IOException {
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);
@@ -203,6 +210,9 @@ public class BuildFSState {
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());
}
@@ -248,6 +258,7 @@ public class BuildFSState {
clearContextChunk(null);
myInitialScanPerformed.clear();
myDeltas.clear();
myRegistrationStamps.clear();
}
public void clearContextRoundData(@Nullable CompileContext context) {
@@ -323,9 +334,12 @@ public class BuildFSState {
for (File file : files) {
if (scope.isAffected(target, file)) {
final long currentFileStamp = FileSystemUtil.lastModified(file);
if (!rd.isGenerated() && currentFileStamp > targetBuildStartStamp) {
if (!rd.isGenerated() && (currentFileStamp > targetBuildStartStamp || getEventRegistrationStamp(file) > targetBuildStartStamp)) {
// if the file was modified after the compilation had started,
// do not save the stamp considering file dirty
// Important!
// Event registration stamp check is essential for the files that were actually changed _before_ targetBuildStart,
// but corresponding change event was received and processed _after_ targetBuildStart
if (Utils.IS_TEST_MODE) {
LOG.info("Timestamp after compilation started; marking dirty again: " + file.getPath());
}