From 65376ac0936fbf754a659810fd60af9f4b29337c Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 17 Jan 2012 20:04:43 +0100 Subject: [PATCH] event-based hotswap for compile server --- .../intellij/compiler/impl/CompileDriver.java | 11 + .../compiler/progress/CompilerTask.java | 2 +- .../compiler/CompilationStatusAdapter.java | 24 ++ .../compiler/CompilationStatusListener.java | 2 + .../debugger/impl/HotSwapManager.java | 39 +- .../intellij/debugger/ui/HotSwapUIImpl.java | 58 ++- jps/jps-builders/proto/jps_remote_proto.proto | 9 + .../org/jetbrains/jps/api/JpsRemoteProto.java | 390 ++++++++++++++++++ .../src/org/jetbrains/jps/api/ProtoUtil.java | 20 +- .../jps/incremental/groovy/GroovyBuilder.java | 8 + .../jps/incremental/java/JavaBuilder.java | 21 +- .../incremental/java/JavacFileManager.java | 2 +- .../incremental/java/OutputFileObject.java | 19 +- .../messages/FileGeneratedEvent.java | 27 ++ .../jps/server/ServerMessageHandler.java | 12 +- .../compiler/AndroidPrecompileTask.java | 4 +- 16 files changed, 605 insertions(+), 43 deletions(-) create mode 100644 java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/incremental/messages/FileGeneratedEvent.java diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 2b5e5351821b..34e56aa633af 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -88,6 +88,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.MultiMap; import com.intellij.util.containers.OrderedSet; +import com.intellij.util.messages.MessageBus; import gnu.trove.TIntHashSet; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NonNls; @@ -425,6 +426,7 @@ public class CompileDriver { } } final JpsServerManager jpsServerManager = JpsServerManager.getInstance(); + final MessageBus messageBus = myProject.getMessageBus(); return jpsServerManager.submitCompilationTask(myProject.getLocation(), compileContext.isRebuild(), compileContext.isMake(), moduleNames, paths, new JpsServerResponseHandlerAdapter() { public void handleCompileMessage(JpsRemoteProto.Message.Response.CompileMessage compilerMessage) { @@ -467,6 +469,15 @@ public class CompileDriver { case BUILD_STARTED: compileContext.getProgressIndicator().setText("Compilation started"); break; + case FILES_GENERATED: + final List generated = event.getGeneratedFilesList(); + final CompilationStatusListener publisher = messageBus.syncPublisher(CompilerTopics.COMPILATION_STATUS); + for (JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile generatedFile : generated) { + final String root = FileUtil.toSystemIndependentName(generatedFile.getOutputRoot()); + final String relativePath = FileUtil.toSystemIndependentName(generatedFile.getRelativePath()); + publisher.fileGenerated(root, relativePath); + } + break; case BUILD_COMPLETED: ExitStatus status = ExitStatus.SUCCESS; if (event.hasCompletionStatus()) { diff --git a/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java b/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java index a577352dedca..762f30457ddf 100644 --- a/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java +++ b/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java @@ -584,7 +584,7 @@ public class CompilerTask extends Task.Backgroundable { myUserAcceptedCancel = true; final MessageBusConnection connection = project.getMessageBus().connect(); - connection.subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusListener() { + connection.subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusAdapter() { public void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext) { connection.disconnect(); ProjectUtil.closeAndDispose(project); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java new file mode 100644 index 000000000000..263919458292 --- /dev/null +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java @@ -0,0 +1,24 @@ +/* + * 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 com.intellij.openapi.compiler; + +public class CompilationStatusAdapter implements CompilationStatusListener { + public void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext) { + } + + public void fileGenerated(String outputRoot, String relativePath) { + } +} diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java index a2edd4d9cca8..067509811f73 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java @@ -32,4 +32,6 @@ public interface CompilationStatusListener extends EventListener { * @param compileContext context for the finished compilation */ void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext); + + void fileGenerated(String outputRoot, String relativePath); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java index da688db25bd5..1838d9d21458 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/HotSwapManager.java @@ -23,6 +23,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -67,7 +68,7 @@ public class HotSwapManager extends AbstractProjectComponent { myTimeStamps.put(session, Long.valueOf(tStamp)); } - public Map getModifiedClasses(final DebuggerSession session, final HotSwapProgress progress) { + public Map scanForModifiedClasses(final DebuggerSession session, final HotSwapProgress progress) { DebuggerManagerThreadImpl.assertIsManagerThread(); final List outputRoots = new ArrayList(); @@ -125,7 +126,39 @@ public class HotSwapManager extends AbstractProjectComponent { setTimeStamp(session, newSwapTime); } - public static Map> getModifiedClasses(final List sessions, final HotSwapProgress swapProgress) { + public static Map> findModifiedClasses(List sessions, Map> generatedPaths) { + final Map> result = new java.util.HashMap>(); + List> sessionWithStamps = new ArrayList>(); + for (DebuggerSession session : sessions) { + sessionWithStamps.add(new Pair(session, getInstance(session.getProject()).getTimeStamp(session))); + } + for (Map.Entry> entry : generatedPaths.entrySet()) { + final File root = new File(entry.getKey()); + for (String relativePath : entry.getValue()) { + if (SystemInfo.isFileSystemCaseSensitive? StringUtil.endsWith(relativePath, CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(relativePath, CLASS_EXTENSION)) { + final String qualifiedName = relativePath.substring(0, relativePath.length() - CLASS_EXTENSION.length()).replace('/', '.'); + final HotSwapFile hotswapFile = new HotSwapFile(new File(root, relativePath)); + final long fileStamp = hotswapFile.file.lastModified(); + + for (Pair pair : sessionWithStamps) { + final DebuggerSession session = pair.first; + if (fileStamp > pair.second) { + Map container = result.get(session); + if (container == null) { + container = new java.util.HashMap(); + result.put(session, container); + } + container.put(qualifiedName, hotswapFile); + } + } + } + } + } + return result; + } + + + public static Map> scanForModifiedClasses(final List sessions, final HotSwapProgress swapProgress) { final Map> modifiedClasses = new HashMap>(); final MultiProcessCommand scanClassesCommand = new MultiProcessCommand(); @@ -141,7 +174,7 @@ public class HotSwapManager extends AbstractProjectComponent { scanClassesCommand.addCommand(debuggerSession.getProcess(), new DebuggerCommandImpl() { protected void action() throws Exception { swapProgress.setDebuggerSession(debuggerSession); - final Map sessionClasses = getInstance(swapProgress.getProject()).getModifiedClasses(debuggerSession, swapProgress); + final Map sessionClasses = getInstance(swapProgress.getProject()).scanForModifiedClasses(debuggerSession, swapProgress); if (!sessionClasses.isEmpty()) { modifiedClasses.put(debuggerSession, sessionClasses); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java index 5b2944a361e2..647cae5e97ce 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java @@ -41,12 +41,11 @@ import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; /** * User: lex @@ -62,7 +61,21 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ public HotSwapUIImpl(final Project project, MessageBus bus) { myProject = project; bus.connect().subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusListener() { + + private final AtomicReference>> myGeneratedPaths = new AtomicReference>>(new HashMap>()); + + public void fileGenerated(String outputRoot, String relativePath) { + final Map> map = myGeneratedPaths.get(); + List paths = map.get(outputRoot); + if (paths == null) { + paths = new ArrayList(); + map.put(outputRoot, paths); + } + paths.add(relativePath); + } + public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) { + final Map> generated = myGeneratedPaths.getAndSet(new HashMap>()); if (myProject.isDisposed()) { return; } @@ -82,7 +95,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ } } if (!sessions.isEmpty()) { - hotSwapSessions(sessions); + hotSwapSessions(sessions, generated); } } myPerformHotswapAfterThisCompilation = true; @@ -129,7 +142,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ return false; } - private void hotSwapSessions(final List sessions) { + private void hotSwapSessions(final List sessions, @Nullable final Map> generatedPaths) { final boolean shouldAskBeforeHotswap = myAskBeforeHotswap; myAskBeforeHotswap = true; @@ -148,7 +161,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { - final Map> modifiedClasses = getModifiedClasses(findClassesProgress, sessions); + final Map> modifiedClasses = generatedPaths == null? scanForModifiedClassesWithProgress(sessions, findClassesProgress) : HotSwapManager.findModifiedClasses(sessions, generatedPaths); final Application application = ApplicationManager.getApplication(); if (modifiedClasses.isEmpty()) { @@ -179,7 +192,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ @Override public Integer fun(Integer exitCode, JCheckBox cb) { settings.HOTSWAP_HANG_WARNING_ENABLED = !cb.isSelected(); - return exitCode == DialogWrapper.OK_EXIT_CODE? exitCode : DialogWrapper.CANCEL_EXIT_CODE; + return exitCode == DialogWrapper.OK_EXIT_CODE ? exitCode : DialogWrapper.CANCEL_EXIT_CODE; } } ); @@ -203,6 +216,21 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ }); } + private static Map> scanForModifiedClassesWithProgress(final List sessions, final HotSwapProgressImpl progress) { + final Ref>> result = Ref.create(null); + ProgressManager.getInstance().runProcess(new Runnable() { + public void run() { + try { + result.set(HotSwapManager.scanForModifiedClasses(sessions, progress)); + } + finally { + progress.finished(); + } + } + }, progress.getProgressIndicator()); + return result.get(); + } + private static void reloadModifiedClasses(final Map> modifiedClasses, final HotSwapProgressImpl progress) { ProgressManager.getInstance().runProcess(new Runnable() { public void run() { @@ -212,18 +240,6 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ }, progress.getProgressIndicator()); } - private static Map> getModifiedClasses(final HotSwapProgressImpl swapProgress, final List sessions) { - final Ref>> modifiedClasses = Ref.create(null); - ProgressManager.getInstance().runProcess(new Runnable() { - public void run() { - modifiedClasses.set(HotSwapManager.getModifiedClasses(sessions, swapProgress)); - swapProgress.finished(); - - } - }, swapProgress.getProgressIndicator()); - return modifiedClasses.get(); - } - public void reloadChangedClasses(final DebuggerSession session, boolean compileBeforeHotswap) { dontAskHotswapAfterThisCompilation(); if (compileBeforeHotswap) { @@ -233,7 +249,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{ if(session.isAttached()) { final List sessions = new ArrayList(1); sessions.add(session); - hotSwapSessions(sessions); + hotSwapSessions(sessions, null); } } } diff --git a/jps/jps-builders/proto/jps_remote_proto.proto b/jps/jps-builders/proto/jps_remote_proto.proto index c7de830ee2b6..025818b971d2 100644 --- a/jps/jps-builders/proto/jps_remote_proto.proto +++ b/jps/jps-builders/proto/jps_remote_proto.proto @@ -104,16 +104,25 @@ message Message { BUILD_STARTED = 1; BUILD_COMPLETED = 2; COMMAND_COMPLETED = 3; + FILES_GENERATED = 4; } + enum Status { CANCELED = 1; ERRORS = 2; SUCCESS = 3; UP_TO_DATE = 4; } + + message GeneratedFile { + required string output_root = 1; + required string relative_path = 2; + } + required Type event_type = 1; optional string description = 2; optional Status completion_status = 3; + repeated GeneratedFile generated_files = 4; } message CompileMessage { diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/JpsRemoteProto.java b/jps/jps-builders/src/org/jetbrains/jps/api/JpsRemoteProto.java index 6988cbb15548..e4524ebcc441 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/JpsRemoteProto.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/JpsRemoteProto.java @@ -4330,6 +4330,7 @@ public final class JpsRemoteProto { BUILD_STARTED(0, 1), BUILD_COMPLETED(1, 2), COMMAND_COMPLETED(2, 3), + FILES_GENERATED(3, 4), ; @@ -4340,6 +4341,7 @@ public final class JpsRemoteProto { case 1: return BUILD_STARTED; case 2: return BUILD_COMPLETED; case 3: return COMMAND_COMPLETED; + case 4: return FILES_GENERATED; default: return null; } } @@ -4409,6 +4411,305 @@ public final class JpsRemoteProto { // @@protoc_insertion_point(enum_scope:org.jetbrains.jpsservice.Message.Response.BuildEvent.Status) } + public static final class GeneratedFile extends + com.google.protobuf.GeneratedMessageLite { + // Use GeneratedFile.newBuilder() to construct. + private GeneratedFile() { + initFields(); + } + private GeneratedFile(boolean noInit) {} + + private static final GeneratedFile defaultInstance; + public static GeneratedFile getDefaultInstance() { + return defaultInstance; + } + + public GeneratedFile getDefaultInstanceForType() { + return defaultInstance; + } + + // required string output_root = 1; + public static final int OUTPUT_ROOT_FIELD_NUMBER = 1; + private boolean hasOutputRoot; + private java.lang.String outputRoot_ = ""; + public boolean hasOutputRoot() { return hasOutputRoot; } + public java.lang.String getOutputRoot() { return outputRoot_; } + + // required string relative_path = 2; + public static final int RELATIVE_PATH_FIELD_NUMBER = 2; + private boolean hasRelativePath; + private java.lang.String relativePath_ = ""; + public boolean hasRelativePath() { return hasRelativePath; } + public java.lang.String getRelativePath() { return relativePath_; } + + private void initFields() { + } + public final boolean isInitialized() { + if (!hasOutputRoot) return false; + if (!hasRelativePath) return false; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasOutputRoot()) { + output.writeString(1, getOutputRoot()); + } + if (hasRelativePath()) { + output.writeString(2, getRelativePath()); + } + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasOutputRoot()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(1, getOutputRoot()); + } + if (hasRelativePath()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(2, getRelativePath()); + } + memoizedSerializedSize = size; + return size; + } + + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessageLite.Builder< + org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile, Builder> { + private org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile result; + + // Construct using org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile(); + return builder; + } + + protected org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile getDefaultInstanceForType() { + return org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile other) { + if (other == org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.getDefaultInstance()) return this; + if (other.hasOutputRoot()) { + setOutputRoot(other.getOutputRoot()); + } + if (other.hasRelativePath()) { + setRelativePath(other.getRelativePath()); + } + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + return this; + default: { + if (!parseUnknownField(input, extensionRegistry, tag)) { + return this; + } + break; + } + case 10: { + setOutputRoot(input.readString()); + break; + } + case 18: { + setRelativePath(input.readString()); + break; + } + } + } + } + + + // required string output_root = 1; + public boolean hasOutputRoot() { + return result.hasOutputRoot(); + } + public java.lang.String getOutputRoot() { + return result.getOutputRoot(); + } + public Builder setOutputRoot(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasOutputRoot = true; + result.outputRoot_ = value; + return this; + } + public Builder clearOutputRoot() { + result.hasOutputRoot = false; + result.outputRoot_ = getDefaultInstance().getOutputRoot(); + return this; + } + + // required string relative_path = 2; + public boolean hasRelativePath() { + return result.hasRelativePath(); + } + public java.lang.String getRelativePath() { + return result.getRelativePath(); + } + public Builder setRelativePath(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasRelativePath = true; + result.relativePath_ = value; + return this; + } + public Builder clearRelativePath() { + result.hasRelativePath = false; + result.relativePath_ = getDefaultInstance().getRelativePath(); + return this; + } + + // @@protoc_insertion_point(builder_scope:org.jetbrains.jpsservice.Message.Response.BuildEvent.GeneratedFile) + } + + static { + defaultInstance = new GeneratedFile(true); + org.jetbrains.jps.api.JpsRemoteProto.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:org.jetbrains.jpsservice.Message.Response.BuildEvent.GeneratedFile) + } + // required .org.jetbrains.jpsservice.Message.Response.BuildEvent.Type event_type = 1; public static final int EVENT_TYPE_FIELD_NUMBER = 1; private boolean hasEventType; @@ -4430,12 +4731,27 @@ public final class JpsRemoteProto { public boolean hasCompletionStatus() { return hasCompletionStatus; } public org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.Status getCompletionStatus() { return completionStatus_; } + // repeated .org.jetbrains.jpsservice.Message.Response.BuildEvent.GeneratedFile generated_files = 4; + public static final int GENERATED_FILES_FIELD_NUMBER = 4; + private java.util.List generatedFiles_ = + java.util.Collections.emptyList(); + public java.util.List getGeneratedFilesList() { + return generatedFiles_; + } + public int getGeneratedFilesCount() { return generatedFiles_.size(); } + public org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile getGeneratedFiles(int index) { + return generatedFiles_.get(index); + } + private void initFields() { eventType_ = org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.Type.BUILD_STARTED; completionStatus_ = org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.Status.CANCELED; } public final boolean isInitialized() { if (!hasEventType) return false; + for (org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile element : getGeneratedFilesList()) { + if (!element.isInitialized()) return false; + } return true; } @@ -4451,6 +4767,9 @@ public final class JpsRemoteProto { if (hasCompletionStatus()) { output.writeEnum(3, getCompletionStatus().getNumber()); } + for (org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile element : getGeneratedFilesList()) { + output.writeMessage(4, element); + } } private int memoizedSerializedSize = -1; @@ -4471,6 +4790,10 @@ public final class JpsRemoteProto { size += com.google.protobuf.CodedOutputStream .computeEnumSize(3, getCompletionStatus().getNumber()); } + for (org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile element : getGeneratedFilesList()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, element); + } memoizedSerializedSize = size; return size; } @@ -4608,6 +4931,10 @@ public final class JpsRemoteProto { throw new IllegalStateException( "build() has already been called on this Builder."); } + if (result.generatedFiles_ != java.util.Collections.EMPTY_LIST) { + result.generatedFiles_ = + java.util.Collections.unmodifiableList(result.generatedFiles_); + } org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent returnMe = result; result = null; return returnMe; @@ -4624,6 +4951,12 @@ public final class JpsRemoteProto { if (other.hasCompletionStatus()) { setCompletionStatus(other.getCompletionStatus()); } + if (!other.generatedFiles_.isEmpty()) { + if (result.generatedFiles_.isEmpty()) { + result.generatedFiles_ = new java.util.ArrayList(); + } + result.generatedFiles_.addAll(other.generatedFiles_); + } return this; } @@ -4662,6 +4995,12 @@ public final class JpsRemoteProto { } break; } + case 34: { + org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.Builder subBuilder = org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addGeneratedFiles(subBuilder.buildPartial()); + break; + } } } } @@ -4730,6 +5069,57 @@ public final class JpsRemoteProto { return this; } + // repeated .org.jetbrains.jpsservice.Message.Response.BuildEvent.GeneratedFile generated_files = 4; + public java.util.List getGeneratedFilesList() { + return java.util.Collections.unmodifiableList(result.generatedFiles_); + } + public int getGeneratedFilesCount() { + return result.getGeneratedFilesCount(); + } + public org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile getGeneratedFiles(int index) { + return result.getGeneratedFiles(index); + } + public Builder setGeneratedFiles(int index, org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile value) { + if (value == null) { + throw new NullPointerException(); + } + result.generatedFiles_.set(index, value); + return this; + } + public Builder setGeneratedFiles(int index, org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.Builder builderForValue) { + result.generatedFiles_.set(index, builderForValue.build()); + return this; + } + public Builder addGeneratedFiles(org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile value) { + if (value == null) { + throw new NullPointerException(); + } + if (result.generatedFiles_.isEmpty()) { + result.generatedFiles_ = new java.util.ArrayList(); + } + result.generatedFiles_.add(value); + return this; + } + public Builder addGeneratedFiles(org.jetbrains.jps.api.JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.Builder builderForValue) { + if (result.generatedFiles_.isEmpty()) { + result.generatedFiles_ = new java.util.ArrayList(); + } + result.generatedFiles_.add(builderForValue.build()); + return this; + } + public Builder addAllGeneratedFiles( + java.lang.Iterable values) { + if (result.generatedFiles_.isEmpty()) { + result.generatedFiles_ = new java.util.ArrayList(); + } + super.addAll(values, result.generatedFiles_); + return this; + } + public Builder clearGeneratedFiles() { + result.generatedFiles_ = java.util.Collections.emptyList(); + return this; + } + // @@protoc_insertion_point(builder_scope:org.jetbrains.jpsservice.Message.Response.BuildEvent) } diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/ProtoUtil.java b/jps/jps-builders/src/org/jetbrains/jps/api/ProtoUtil.java index 16c01c3fdaf1..982aac28fb0b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/ProtoUtil.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/ProtoUtil.java @@ -1,5 +1,6 @@ package org.jetbrains.jps.api; +import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.incremental.messages.BuildMessage; @@ -116,18 +117,22 @@ public class ProtoUtil { } public static JpsRemoteProto.Message.Response createBuildStartedEvent(@Nullable String description) { - return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.BUILD_STARTED, description, null); + return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.BUILD_STARTED, description, null, null); } public static JpsRemoteProto.Message.Response createBuildCompletedEvent(@Nullable String description, final JpsRemoteProto.Message.Response.BuildEvent.Status status) { - return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.BUILD_COMPLETED, description, status); + return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.BUILD_COMPLETED, description, status, null); } public static JpsRemoteProto.Message.Response createCommandCompletedEvent(@Nullable String description) { - return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.COMMAND_COMPLETED, description, null); + return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.COMMAND_COMPLETED, description, null, null); } - public static JpsRemoteProto.Message.Response createBuildEvent(final JpsRemoteProto.Message.Response.BuildEvent.Type type, @Nullable String description, final JpsRemoteProto.Message.Response.BuildEvent.Status status) { + public static JpsRemoteProto.Message.Response createFileGeneratedEvent(final Collection> paths) { + return createBuildEvent(JpsRemoteProto.Message.Response.BuildEvent.Type.FILES_GENERATED, null, null, paths); + } + + public static JpsRemoteProto.Message.Response createBuildEvent(final JpsRemoteProto.Message.Response.BuildEvent.Type type, @Nullable String description, final JpsRemoteProto.Message.Response.BuildEvent.Status status, Collection> generatedPaths) { final JpsRemoteProto.Message.Response.BuildEvent.Builder builder = JpsRemoteProto.Message.Response.BuildEvent.newBuilder().setEventType(type); if (description != null) { builder.setDescription(description); @@ -135,6 +140,13 @@ public class ProtoUtil { if (status != null) { builder.setCompletionStatus(status); } + if (generatedPaths != null) { + for (Pair pair : generatedPaths) { + final JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.Builder fileBuilder = JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile.newBuilder(); + final JpsRemoteProto.Message.Response.BuildEvent.GeneratedFile generatedFile = fileBuilder.setOutputRoot(pair.first).setRelativePath(pair.second).build(); + builder.addGeneratedFiles(generatedFile); + } + } return JpsRemoteProto.Message.Response.newBuilder().setResponseType(JpsRemoteProto.Message.Response.Type.BUILD_EVENT).setBuildEvent(builder.build()).build(); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index 0ae43442e80a..31e857028f20 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java @@ -14,6 +14,7 @@ import org.jetbrains.jps.incremental.*; import org.jetbrains.jps.incremental.java.JavaBuilder; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.incremental.messages.FileGeneratedEvent; import org.jetbrains.jps.incremental.messages.ProgressMessage; import org.jetbrains.jps.incremental.storage.SourceToOutputMapping; import org.jetbrains.jps.server.ClasspathBootstrap; @@ -147,7 +148,9 @@ public class GroovyBuilder extends Builder { final Mappings delta = context.createDelta(); final List successfullyCompiledFiles = new ArrayList(); if (!successfullyCompiled.isEmpty()) { + final Callbacks.Backend callback = delta.getCallback(); + final FileGeneratedEvent generatedEvent = new FileGeneratedEvent(); for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) { final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath); @@ -159,9 +162,14 @@ public class GroovyBuilder extends Builder { } callback.associate(outputPath, Callbacks.getDefaultLookup(sourcePath), new ClassReader(FileUtil.loadFileBytes(new File(outputPath)))); successfullyCompiledFiles.add(new File(sourcePath)); + + generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/')); } + + context.processMessage(generatedEvent); } + final boolean needSecondPass = updateMappings(context, delta, chunk, toCompile.keySet(), successfullyCompiledFiles); if (needSecondPass) { exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index f0c6485d590b..2d41a4f6dfec 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -19,6 +19,7 @@ import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.incremental.*; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.incremental.messages.FileGeneratedEvent; import org.jetbrains.jps.incremental.messages.ProgressMessage; import org.jetbrains.jps.incremental.storage.BuildDataManager; import org.jetbrains.jps.incremental.storage.SourceToFormMapping; @@ -611,12 +612,24 @@ public class JavaBuilder extends Builder{ public void writePendingData() { try { - for (OutputFileObject file : myFileObjects) { + if (!myFileObjects.isEmpty()) { + final FileGeneratedEvent event = new FileGeneratedEvent(); try { - writeToDisk(file); + for (OutputFileObject fileObject : myFileObjects) { + try { + writeToDisk(fileObject); + final File rootFile = fileObject.getOutputRoot(); + if (rootFile != null) { + event.add(rootFile.getPath(), fileObject.getRelativePath()); + } + } + catch (IOException e) { + myContext.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage())); + } + } } - catch (IOException e) { - myContext.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage())); + finally { + myContext.processMessage(event); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavacFileManager.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavacFileManager.java index d4ed97dbb19a..8dfaafe0cb6a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavacFileManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavacFileManager.java @@ -110,7 +110,7 @@ class JavacFileManager extends ForwardingJavaFileManager> myPaths = new ArrayList>(); + + public FileGeneratedEvent() { + super("", Kind.INFO); + } + + public void add(String root, String relativePath) { + myPaths.add(new Pair(root, relativePath)); + } + + public Collection> getPaths() { + return myPaths; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/server/ServerMessageHandler.java b/jps/jps-builders/src/org/jetbrains/jps/server/ServerMessageHandler.java index ed0b5002b15e..4c1137d6a8d2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/server/ServerMessageHandler.java +++ b/jps/jps-builders/src/org/jetbrains/jps/server/ServerMessageHandler.java @@ -1,15 +1,13 @@ package org.jetbrains.jps.server; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import org.jboss.netty.channel.*; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.api.*; import org.jetbrains.jps.incremental.MessageHandler; -import org.jetbrains.jps.incremental.messages.BuildMessage; -import org.jetbrains.jps.incremental.messages.CompilerMessage; -import org.jetbrains.jps.incremental.messages.ProgressMessage; -import org.jetbrains.jps.incremental.messages.UptoDateFilesSavedEvent; +import org.jetbrains.jps.incremental.messages.*; import java.io.ByteArrayOutputStream; import java.io.File; @@ -203,7 +201,11 @@ class ServerMessageHandler extends SimpleChannelHandler { ServerState.getInstance().startBuild(myProjectPath, myModules, myPaths, myParams, new MessageHandler() { public void processMessage(BuildMessage buildMessage) { final JpsRemoteProto.Message.Response response; - if (buildMessage instanceof UptoDateFilesSavedEvent) { + if (buildMessage instanceof FileGeneratedEvent) { + final Collection> paths = ((FileGeneratedEvent)buildMessage).getPaths(); + response = !paths.isEmpty()? ProtoUtil.createFileGeneratedEvent(paths) : null; + } + else if (buildMessage instanceof UptoDateFilesSavedEvent) { markedFilesUptodate.set(true); response = null; } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java index 6f784713b4d5..f8355f3b7b68 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java @@ -109,7 +109,7 @@ public class AndroidPrecompileTask implements CompileTask { CompilerManager.getInstance(project).addCompilationStatusListener(new MyCompilationStatusListener(project, addedEntries), project); } - CompilerManager.getInstance(project).addCompilationStatusListener(new CompilationStatusListener() { + CompilerManager.getInstance(project).addCompilationStatusListener(new CompilationStatusAdapter() { @Override public void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext) { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @@ -199,7 +199,7 @@ public class AndroidPrecompileTask implements CompileTask { } } - private static class MyCompilationStatusListener implements CompilationStatusListener { + private static class MyCompilationStatusListener extends CompilationStatusAdapter { private final Project myProject; private final Set myEntriesToRemove;