notify IDE about unprocessed sources that were changed during build

This commit is contained in:
Eugene Zhuravlev
2015-09-28 14:26:18 +02:00
parent dcff8b2739
commit 6e472a9459
8 changed files with 126 additions and 4 deletions
@@ -73,6 +73,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jps.api.CmdlineProtoUtil;
import org.jetbrains.jps.api.CmdlineRemoteProto;
import org.jetbrains.jps.api.GlobalOptions;
import org.jetbrains.jps.api.TaskFuture;
import org.jetbrains.jps.model.java.JavaSourceRootType;
@@ -318,6 +319,7 @@ public class CompileDriver {
ArtifactsCompiler.addWrittenPaths(compileContext, writtenArtifactOutputPaths);
}
break;
case BUILD_COMPLETED:
ExitStatus status = ExitStatus.SUCCESS;
if (event.hasCompletionStatus()) {
@@ -339,6 +341,19 @@ public class CompileDriver {
}
compileContext.putUserDataIfAbsent(COMPILE_SERVER_BUILD_STATUS, status);
break;
case CUSTOM_BUILDER_MESSAGE:
if (event.hasCustomBuilderMessage()) {
final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.CustomBuilderMessage message = event.getCustomBuilderMessage();
if (GlobalOptions.JPS_SYSTEM_BUILDER_ID.equals(message.getBuilderId()) && GlobalOptions.JPS_UNPROCESSED_FS_CHANGES_MESSAGE_ID.equals(message.getMessageType())) {
final String text = message.getMessageText();
if (!StringUtil.isEmpty(text)) {
compileContext.addMessage(CompilerMessageCategory.INFORMATION, text, null, -1, -1);
}
}
}
break;
}
}
});
@@ -31,6 +31,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.problems.Problem;
import com.intellij.problems.WolfTheProblemSolver;
import org.jetbrains.jps.api.CmdlineRemoteProto;
import org.jetbrains.jps.api.GlobalOptions;
import java.util.Collections;
import java.util.UUID;
@@ -44,6 +45,7 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
private CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status myBuildStatus;
private final Project myProject;
private final WolfTheProblemSolver myWolf;
private volatile boolean myUnprocessedFSChangesDetected = false;
public AutoMakeMessageHandler(Project project) {
super(project);
@@ -52,6 +54,10 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
myWolf = WolfTheProblemSolver.getInstance(project);
}
public boolean unprocessedFSChangesDetected() {
return myUnprocessedFSChangesDetected;
}
@Override
public void buildStarted(UUID sessionId) {
}
@@ -77,6 +83,15 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
}
return;
case CUSTOM_BUILDER_MESSAGE:
if (event.hasCustomBuilderMessage()) {
final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.CustomBuilderMessage message = event.getCustomBuilderMessage();
if (GlobalOptions.JPS_SYSTEM_BUILDER_ID.equals(message.getBuilderId()) && GlobalOptions.JPS_UNPROCESSED_FS_CHANGES_MESSAGE_ID.equals(message.getMessageType())) {
myUnprocessedFSChangesDetected = true;
}
}
return;
default:
return;
}
@@ -516,6 +516,9 @@ public class BuildManager implements Disposable {
}
finally {
myAutomakeFutures.remove(future);
if (handler.unprocessedFSChangesDetected()) {
scheduleAutoMake();
}
}
}
}
@@ -26,4 +26,9 @@ public interface GlobalOptions {
String COMPILE_PARALLEL_MAX_THREADS_OPTION = "compile.parallel.max.threads";
String REBUILD_ON_DEPENDENCY_CHANGE_OPTION = "rebuild.on.dependency.change";
String LOG_DIR_OPTION = "jps.log.dir";
// builder ID for all global build messages sent to the controlling IDE
String JPS_SYSTEM_BUILDER_ID = "JPS";
// notification about the files changed during compilation, but not compiled in current compilation session
String JPS_UNPROCESSED_FS_CHANGES_MESSAGE_ID = "!unprocessed_fs_changes_detected!";
}
@@ -62,7 +62,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
myScope = scope;
myDelegateMessageHandler = delegateMessageHandler;
}
// todo: add timestamp-setting code
@Override
public long getCompilationStartStamp(BuildTarget<?> target) {
synchronized (myCompilationStartStamp) {
@@ -193,9 +193,11 @@ public class IncProjectBuilder {
runBuild(context, forceCleanCaches);
myProjectDescriptor.dataManager.saveVersion();
reportRebuiltModules(context);
reportUnprocessedChanges(context);
}
catch (StopBuildException e) {
reportRebuiltModules(context);
reportUnprocessedChanges(context);
// some builder decided to stop the build
// report optional progress message if any
final String msg = e.getMessage();
@@ -309,6 +311,17 @@ public class IncProjectBuilder {
context.processMessage(new CompilerMessage("", BuildMessage.Kind.INFO, message.toString()));
}
private static void reportUnprocessedChanges(CompileContextImpl context) {
final ProjectDescriptor pd = context.getProjectDescriptor();
final BuildFSState fsState = pd.fsState;
for (BuildTarget<?> target : pd.getBuildTargetIndex().getAllTargets()) {
if (fsState.hasUnprocessedChanges(context, target)) {
context.processMessage(new UnprocessedFSChangesNotification());
break;
}
}
}
private static void flushContext(CompileContext context) {
if (context != null) {
final ProjectDescriptor pd = context.getProjectDescriptor();
@@ -109,7 +109,9 @@ public class BuildFSState {
}
public long getEventRegistrationStamp(File file) {
return myRegistrationStamps.get(file);
synchronized (myRegistrationStamps) {
return myRegistrationStamps.get(file);
}
}
public boolean hasWorkToDo(BuildTarget<?> target) {
@@ -120,6 +122,38 @@ public class BuildFSState {
return delta != null && delta.hasChanges();
}
/**
* @return true if there were changed files reported for the specified target, _after_ the target compilation had been started
*/
public boolean hasUnprocessedChanges(@NotNull CompileContext context, @NotNull BuildTarget<?> target) {
if (!myInitialScanPerformed.contains(target)) {
return false;
}
final FilesDelta delta = myDeltas.get(target);
if (delta == null) {
return false;
}
final long targetBuildStart = context.getCompilationStartStamp(target);
if (targetBuildStart <= 0L) {
return false;
}
final CompileScope scope = context.getScope();
try {
delta.lockData();
for (Set<File> files : delta.getSourcesToRecompile().values()) {
for (File file : files) {
if ((getEventRegistrationStamp(file) > targetBuildStart || FileSystemUtil.lastModified(file) > targetBuildStart) && scope.isAffected(target, file)) {
return true;
}
}
}
}
finally {
delta.unlockData();
}
return false;
}
public void markInitialScanPerformed(BuildTarget<?> target) {
myInitialScanPerformed.add(target);
}
@@ -211,7 +245,10 @@ public class BuildFSState {
LOG.debug(rd.getTarget() + ": MARKED DIRTY: " + file.getPath());
}
if (saveEventStamp) {
myRegistrationStamps.put(file, System.currentTimeMillis());
final long eventStamp = System.currentTimeMillis();
synchronized (myRegistrationStamps) {
myRegistrationStamps.put(file, eventStamp);
}
}
if (tsStorage != null) {
tsStorage.removeStamp(file, rd.getTarget());
@@ -258,7 +295,9 @@ public class BuildFSState {
clearContextChunk(null);
myInitialScanPerformed.clear();
myDeltas.clear();
myRegistrationStamps.clear();
synchronized (myRegistrationStamps) {
myRegistrationStamps.clear();
}
}
public void clearContextRoundData(@Nullable CompileContext context) {
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2015 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.messages;
import org.jetbrains.jps.api.GlobalOptions;
/**
* @author Eugene Zhuravlev
* Date: 26-Sep-15
*/
public class UnprocessedFSChangesNotification extends CustomBuilderMessage{
public UnprocessedFSChangesNotification() {
super(
GlobalOptions.JPS_SYSTEM_BUILDER_ID,
GlobalOptions.JPS_UNPROCESSED_FS_CHANGES_MESSAGE_ID,
"Some files were changed during the build. Additional compilation may be required."
);
}
}