diff options
Diffstat (limited to 'jps')
15 files changed, 878 insertions, 683 deletions
diff --git a/jps/jps-builders/jps-builders.iml b/jps/jps-builders/jps-builders.iml index 993bfad6f750..cfafe90aa84a 100644 --- a/jps/jps-builders/jps-builders.iml +++ b/jps/jps-builders/jps-builders.iml @@ -31,7 +31,7 @@ <orderEntry type="library" name="Log4J" level="project" /> <orderEntry type="library" scope="TEST" name="JUnit4" level="project" /> <orderEntry type="module" module-name="testFramework" scope="TEST" /> - <orderEntry type="library" scope="RUNTIME" name="Eclipse" level="project" /> + <orderEntry type="library" scope="TEST" name="Eclipse" level="project" /> <orderEntry type="module" module-name="jps-model-api" /> <orderEntry type="module" module-name="jps-model-serialization" /> <orderEntry type="module" module-name="jps-model-impl" /> diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java b/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java index b1f36a69cb1e..223d57a532f9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java @@ -21,7 +21,6 @@ package org.jetbrains.jps.api; */ public interface GlobalOptions { String USE_MEMORY_TEMP_CACHE_OPTION = "use.memory.temp.cache"; - String USE_EXTERNAL_JAVAC_OPTION = "use.external.javac.process"; String GENERATE_CLASSPATH_INDEX_OPTION = "generate.classpath.index"; String COMPILE_PARALLEL_OPTION = "compile.parallel"; String COMPILE_PARALLEL_MAX_THREADS_OPTION = "compile.parallel.max.threads"; diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index 8634769222a0..befddeff6157 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -41,6 +41,7 @@ import org.jetbrains.jps.service.SharedThreadPool; import java.io.*; import java.net.InetSocketAddress; import java.util.UUID; +import java.util.concurrent.TimeUnit; /** * @author Eugene Zhuravlev @@ -214,7 +215,7 @@ public class BuildMain { public void run() { //noinspection finally try { - ourEventLoopGroup.shutdownGracefully(); + ourEventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS); } finally { System.exit(0); diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java index 3e5d2514c5a2..030f083652d9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java @@ -32,7 +32,7 @@ import net.n3.nanoxml.IXMLBuilder; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.java.JavaCompilingTool; import org.jetbrains.jps.builders.java.JavaSourceTransformer; -import org.jetbrains.jps.javac.JavacServer; +import org.jetbrains.jps.javac.ExternalJavacProcess; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.impl.JpsModelImpl; import org.jetbrains.jps.model.serialization.JpsProjectLoader; @@ -145,6 +145,8 @@ public class ClasspathBootstrap { cp.add(getResourcePath(NotNullVerifyingInstrumenter.class)); // not-null cp.add(getResourcePath(IXMLBuilder.class)); // nano-xml cp.add(getResourcePath(SequenceLock.class)); // jsr166 + cp.add(getJpsPluginSystemClassesPath().getAbsolutePath().replace('\\', '/')); + //don't forget to update layoutCommunityJps() in layouts.gant accordingly if (!isLauncherUsed) { @@ -176,9 +178,9 @@ public class ClasspathBootstrap { } } - public static List<File> getJavacServerClasspath(String sdkHome, JavaCompilingTool compilingTool) { + public static List<File> getExternalJavacProcessClasspath(String sdkHome, JavaCompilingTool compilingTool) { final Set<File> cp = new LinkedHashSet<File>(); - cp.add(getResourceFile(JavacServer.class)); // self + cp.add(getResourceFile(ExternalJavacProcess.class)); // self // util for (String path : PathManager.getUtilClassPath()) { cp.add(new File(path)); @@ -187,7 +189,8 @@ public class ClasspathBootstrap { cp.add(getResourceFile(JpsModelImpl.class)); // jps-model-impl cp.add(getResourceFile(Message.class)); // protobuf cp.add(getResourceFile(NetUtil.class)); // netty - + cp.add(getJpsPluginSystemClassesPath()); + final Class<StandardJavaFileManager> optimizedFileManagerClass = getOptimizedFileManagerClass(); if (optimizedFileManagerClass != null) { cp.add(getResourceFile(optimizedFileManagerClass)); // optimizedFileManager, if applicable @@ -201,38 +204,33 @@ public class ClasspathBootstrap { LOG.info(th); } - final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); - if (systemCompiler != null) { - try { - final String localJarPath = FileUtil.toSystemIndependentName(getResourceFile(systemCompiler.getClass()).getPath()); - final String localJavaHome = FileUtil.toSystemIndependentName(SystemProperties.getJavaHome()); - if (FileUtil.pathsEqual(localJavaHome, FileUtil.toSystemIndependentName(sdkHome))) { - cp.add(new File(localJarPath)); - } - else { - // sdkHome is not the same as the sdk used to run this process - final File candidate = new File(sdkHome, "lib/tools.jar"); - if (candidate.exists()) { - cp.add(candidate); - } - else { - // last resort - String relPath = FileUtil.getRelativePath(localJavaHome, localJarPath, '/'); + try { + final String localJavaHome = FileUtil.toSystemIndependentName(SystemProperties.getJavaHome()); + // sdkHome is not the same as the sdk used to run this process + final File candidate = new File(sdkHome, "lib/tools.jar"); + if (candidate.exists()) { + cp.add(candidate); + } + else { + // last resort + final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); + if (systemCompiler != null) { + final String localJarPath = FileUtil.toSystemIndependentName(getResourceFile(systemCompiler.getClass()).getPath()); + String relPath = FileUtil.getRelativePath(localJavaHome, localJarPath, '/'); + if (relPath != null) { + if (relPath.contains("..")) { + relPath = FileUtil.getRelativePath(FileUtil.toSystemIndependentName(new File(localJavaHome).getParent()), localJarPath, '/'); + } if (relPath != null) { - if (relPath.contains("..")) { - relPath = FileUtil.getRelativePath(FileUtil.toSystemIndependentName(new File(localJavaHome).getParent()), localJarPath, '/'); - } - if (relPath != null) { - final File targetFile = new File(sdkHome, relPath); - cp.add(targetFile); // tools.jar - } + final File targetFile = new File(sdkHome, relPath); + cp.add(targetFile); // tools.jar } } } } - catch (Throwable th) { - LOG.info(th); - } + } + catch (Throwable th) { + LOG.info(th); } cp.addAll(compilingTool.getAdditionalClasspath()); @@ -286,4 +284,16 @@ public class ClasspathBootstrap { public static File getResourceFile(Class aClass) { return new File(getResourcePath(aClass)); } + + private static File getJpsPluginSystemClassesPath() { + File classesRoot = new File(getResourcePath(ClasspathBootstrap.class)); + if (classesRoot.isDirectory()) { + //running from sources: load classes from .../out/production/jps-plugin-system + return new File(classesRoot.getParentFile(), "jps-plugin-system"); + } + else { + return new File(classesRoot.getParentFile(), "rt/jps-plugin-system.jar"); + } + } + } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java index 88fdef9eaad2..f13664ec3c8a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -36,7 +36,6 @@ import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.TimingLog; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.api.GlobalOptions; -import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.builders.*; import org.jetbrains.jps.builders.impl.BuildOutputConsumerImpl; import org.jetbrains.jps.builders.impl.BuildTargetChunk; @@ -50,12 +49,12 @@ 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.java.ExternalJavacDescriptor; import org.jetbrains.jps.incremental.messages.*; import org.jetbrains.jps.incremental.storage.BuildTargetConfiguration; import org.jetbrains.jps.incremental.storage.OneToManyPathsMapping; import org.jetbrains.jps.incremental.storage.OutputToTargetRegistry; import org.jetbrains.jps.indices.ModuleExcludeIndex; +import org.jetbrains.jps.javac.ExternalJavacServer; import org.jetbrains.jps.javac.JavacMain; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; @@ -313,17 +312,10 @@ public class IncProjectBuilder { pd.timestamps.getStorage().force(); pd.dataManager.flush(false); } - final ExternalJavacDescriptor descriptor = ExternalJavacDescriptor.KEY.get(context); - if (descriptor != null) { - try { - final RequestFuture future = descriptor.client.sendShutdownRequest(); - future.waitFor(500L, TimeUnit.MILLISECONDS); - } - finally { - // ensure process is not running - descriptor.process.destroyProcess(); - } - ExternalJavacDescriptor.KEY.set(context, null); + final ExternalJavacServer server = ExternalJavacServer.KEY.get(context); + if (server != null) { + server.stop(); + ExternalJavacServer.KEY.set(context, null); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java index ba43cc7ea05e..c5ce16567be4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java @@ -181,7 +181,7 @@ public class JarsBuilder { packedFilePaths, -1); } else { - LOG.debug("nested jar file " + relativePath + " for " + jar.getPresentableDestination() + " not found"); + LOG.debug("nested JAR file " + relativePath + " for " + jar.getPresentableDestination() + " not found"); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java deleted file mode 100644 index b8882decdf4d..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java +++ /dev/null @@ -1,36 +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.java; - -import com.intellij.execution.process.BaseOSProcessHandler; -import org.jetbrains.jps.incremental.GlobalContextKey; -import org.jetbrains.jps.javac.JavacServerClient; - -/** - * @author Eugene Zhuravlev - * Date: 1/24/12 - */ -public class ExternalJavacDescriptor { - public static final GlobalContextKey<ExternalJavacDescriptor> KEY = GlobalContextKey.create("_external_javac_descriptor_"); - - public final BaseOSProcessHandler process; - public final JavacServerClient client; - - public ExternalJavacDescriptor(BaseOSProcessHandler process, JavacServerClient client) { - this.process = process; - this.client = client; - } -} 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 77538c6b6881..94b00d9838a1 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 @@ -15,7 +15,6 @@ */ package org.jetbrains.jps.incremental.java; -import com.intellij.execution.process.BaseOSProcessHandler; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; @@ -32,8 +31,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.ProjectPaths; -import org.jetbrains.jps.api.GlobalOptions; -import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.builders.FileProcessor; @@ -63,13 +60,11 @@ import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.model.module.JpsModuleType; import org.jetbrains.jps.service.JpsServiceManager; -import javax.tools.Diagnostic; -import javax.tools.JavaFileObject; +import javax.tools.*; import java.io.*; import java.net.ServerSocket; import java.util.*; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** @@ -81,7 +76,6 @@ public class JavaBuilder extends ModuleLevelBuilder { public static final String BUILDER_NAME = "java"; private static final String JAVA_EXTENSION = "java"; private static final String DOT_JAVA_EXTENSION = "." + JAVA_EXTENSION; - public static final boolean USE_EMBEDDED_JAVAC = System.getProperty(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null; private static final Key<Integer> JAVA_COMPILER_VERSION_KEY = Key.create("_java_compiler_version_"); public static final Key<Boolean> IS_ENABLED = Key.create("_java_compiler_enabled_"); private static final Key<JavaCompilingTool> COMPILING_TOOL = Key.create("_java_compiling_tool_"); @@ -396,7 +390,7 @@ public class JavaBuilder extends ModuleLevelBuilder { } try { final boolean rc; - if (USE_EMBEDDED_JAVAC) { + if (!shouldForkCompilerProcess(context, chunk, compilingTool)) { final Collection<File> _platformCp = calcEffectivePlatformCp(platformCp, options, compilingTool); if (_platformCp == null) { diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, @@ -409,16 +403,18 @@ public class JavaBuilder extends ModuleLevelBuilder { ); } else { - final JavacServerClient client = ensureJavacServerLaunched(context, compilingTool); - final RequestFuture<JavacServerResponseHandler> future = client.sendCompileRequest( - options, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer - ); - while (!future.waitFor(100L, TimeUnit.MILLISECONDS)) { - if (context.getCancelStatus().isCanceled()) { - future.cancel(false); - } + // fork external javac + final String sdkHome = getChunkSdkHome(chunk); + if (sdkHome == null) { + diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, "Cannot start javac process for " + chunk.getName() + ": unknown JDK home path.\nPlease check project configuration.")); + return true; } - rc = future.getMessageHandler().isTerminatedSuccessfully(); + + final List<String> vmOptions = getCompilationVMOptions(context, compilingTool); + final ExternalJavacServer server = ensureJavacServerStarted(context); + rc = server.forkJavac( + context, options, vmOptions, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer, sdkHome, compilingTool + ); } return rc; } @@ -426,6 +422,20 @@ public class JavaBuilder extends ModuleLevelBuilder { counter.await(); } } + + private static boolean shouldForkCompilerProcess(CompileContext context, ModuleChunk chunk, JavaCompilingTool tool) { + final int compilerSdkVersion = getCompilerSdkVersion(context); + if (compilerSdkVersion < 9) { + // javac up to version 9 supports all previous releases + return false; + } + final int chunkSdkVersion = getChunkSdkVersion(chunk); + if (chunkSdkVersion < 0) { + return false; // was not able to determine jdk version, assuming in-process compiler + } + // according to JEP 182: Retiring javac "one plus three back" policy + return Math.abs(compilerSdkVersion - chunkSdkVersion) > 3; + } // If platformCp of the build process is the same as the target plafform, do not specify platformCp explicitly // this will allow javac to resolve against ct.sym file, which is required for the "compilation profiles" feature @@ -482,31 +492,16 @@ public class JavaBuilder extends ModuleLevelBuilder { }); } - private static synchronized JavacServerClient ensureJavacServerLaunched(@NotNull CompileContext context, @NotNull JavaCompilingTool compilingTool) throws Exception { - final ExternalJavacDescriptor descriptor = ExternalJavacDescriptor.KEY.get(context); - if (descriptor != null) { - return descriptor.client; + private static synchronized ExternalJavacServer ensureJavacServerStarted(@NotNull CompileContext context) throws Exception { + ExternalJavacServer server = ExternalJavacServer.KEY.get(context); + if (server != null) { + return server; } - // start server here - final int port = findFreePort(); - final int heapSize = getJavacServerHeapSize(context); - - // use the same jdk that used to run the build process - final String javaHome = SystemProperties.getJavaHome(); - - final BaseOSProcessHandler processHandler = JavacServerBootstrap.launchJavacServer( - javaHome, heapSize, port, Utils.getSystemRoot(), getCompilationVMOptions(context, compilingTool), compilingTool - ); - final JavacServerClient client = new JavacServerClient(); - try { - client.connect("127.0.0.1", port); - } - catch (Throwable ex) { - processHandler.destroyProcess(); - throw new Exception("Failed to connect to external javac process: ", ex); - } - ExternalJavacDescriptor.KEY.set(context, new ExternalJavacDescriptor(processHandler, client)); - return client; + final int listenPort = findFreePort(); + server = new ExternalJavacServer(); + server.start(listenPort); + ExternalJavacServer.KEY.set(context, server); + return server; } private static int convertToNumber(String ver) { @@ -564,17 +559,10 @@ public class JavaBuilder extends ModuleLevelBuilder { } catch (IOException e) { e.printStackTrace(System.err); - return JavacServer.DEFAULT_SERVER_PORT; + return ExternalJavacServer.DEFAULT_SERVER_PORT; } } - private static int getJavacServerHeapSize(CompileContext context) { - final JpsProject project = context.getProjectDescriptor().getProject(); - final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); - final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions(); - return options.MAXIMUM_HEAP_SIZE; - } - private static final Key<List<String>> JAVAC_OPTIONS = Key.create("_javac_options_"); private static final Key<List<String>> JAVAC_VM_OPTIONS = Key.create("_javac_vm_options_"); private static final Key<String> USER_DEFINED_BYTECODE_TARGET = Key.create("_user_defined_bytecode_target_"); @@ -628,19 +616,12 @@ public class JavaBuilder extends ModuleLevelBuilder { options.add(langLevel); } - JpsJavaCompilerConfiguration compilerConfiguration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration( - context.getProjectDescriptor().getProject()); + final JpsJavaCompilerConfiguration compilerConfiguration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration( + context.getProjectDescriptor().getProject() + ); + String bytecodeTarget = null; - int chunkSdkVersion = -1; for (JpsModule module : chunk.getModules()) { - final JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE); - if (sdk != null) { - final int moduleSdkVersion = convertToNumber(sdk.getVersionString()); - if (moduleSdkVersion != 0 /*could determine the version*/&& (chunkSdkVersion < 0 || chunkSdkVersion > moduleSdkVersion)) { - chunkSdkVersion = moduleSdkVersion; - } - } - final String moduleTarget = compilerConfiguration.getByteCodeTargetLevel(module.getName()); if (moduleTarget == null) { continue; @@ -662,6 +643,7 @@ public class JavaBuilder extends ModuleLevelBuilder { } final int compilerSdkVersion = getCompilerSdkVersion(context); + final int chunkSdkVersion = getChunkSdkVersion(chunk); if (bytecodeTarget != null) { options.add("-target"); @@ -721,17 +703,19 @@ public class JavaBuilder extends ModuleLevelBuilder { } private static String getLanguageLevel(JpsModule module) { - LanguageLevel level = JpsJavaExtensionService.getInstance().getLanguageLevel(module); - if (level == null) return null; - switch (level) { - case JDK_1_3: return "1.3"; - case JDK_1_4: return "1.4"; - case JDK_1_5: return "1.5"; - case JDK_1_6: return "1.6"; - case JDK_1_7: return "1.7"; - case JDK_1_8: return "8"; - default: return null; + final LanguageLevel level = JpsJavaExtensionService.getInstance().getLanguageLevel(module); + if (level != null) { + switch (level) { + case JDK_1_3: return "1.3"; + case JDK_1_4: return "1.4"; + case JDK_1_5: return "1.5"; + case JDK_1_6: return "1.6"; + case JDK_1_7: return "1.7"; + case JDK_1_8: return "8"; + case JDK_1_9: return "9"; + } } + return null; } private static boolean isEncodingSet(List<String> options) { @@ -743,7 +727,6 @@ public class JavaBuilder extends ModuleLevelBuilder { return false; } - private static int getCompilerSdkVersion(CompileContext context) { final Integer cached = JAVA_COMPILER_VERSION_KEY.get(context); if (cached != null) { @@ -754,6 +737,30 @@ public class JavaBuilder extends ModuleLevelBuilder { return javaVersion; } + private static int getChunkSdkVersion(ModuleChunk chunk) { + int chunkSdkVersion = -1; + for (JpsModule module : chunk.getModules()) { + final JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE); + if (sdk != null) { + final int moduleSdkVersion = convertToNumber(sdk.getVersionString()); + if (moduleSdkVersion != 0 /*could determine the version*/&& (chunkSdkVersion < 0 || chunkSdkVersion > moduleSdkVersion)) { + chunkSdkVersion = moduleSdkVersion; + } + } + } + return chunkSdkVersion; + } + + private static String getChunkSdkHome(ModuleChunk chunk) { + for (JpsModule module : chunk.getModules()) { + final JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE); + if (sdk != null) { + return sdk.getHomePath(); + } + } + return null; + } + private static void loadCommonJavacOptions(@NotNull CompileContext context, @NotNull JavaCompilingTool compilingTool) { final List<String> options = new ArrayList<String>(); final List<String> vmOptions = new ArrayList<String>(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java new file mode 100644 index 000000000000..fd5bc65d1e5b --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java @@ -0,0 +1,230 @@ +/* + * 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.javac; + +import com.google.protobuf.ByteString; +import com.google.protobuf.MessageLite; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.incremental.BinaryContent; + +import javax.tools.*; +import java.io.File; +import java.net.URI; +import java.util.Collection; +import java.util.Locale; + +/** + * @author Eugene Zhuravlev + * Date: 1/22/12 + */ +public class ExternalJavacMessageHandler { + private final DiagnosticOutputConsumer myDiagnosticSink; + private final OutputFileConsumer myOutputSink; + @Nullable + private final String myEncodingName; + private volatile boolean myTerminatedSuccessfully; + + public ExternalJavacMessageHandler(DiagnosticOutputConsumer diagnosticSink, + OutputFileConsumer outputSink, + @Nullable final String encodingName) { + myDiagnosticSink = diagnosticSink; + myOutputSink = outputSink; + myEncodingName = encodingName; + } + + public boolean handleMessage(MessageLite message) { + try { + final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)message; + final JavacRemoteProto.Message.Type messageType = msg.getMessageType(); + + if (messageType == JavacRemoteProto.Message.Type.RESPONSE) { + final JavacRemoteProto.Message.Response response = msg.getResponse(); + final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType(); + + if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_MESSAGE) { + final JavacRemoteProto.Message.Response.CompileMessage compileMessage = response.getCompileMessage(); + final JavacRemoteProto.Message.Response.CompileMessage.Kind messageKind = compileMessage.getKind(); + + if (messageKind == JavacRemoteProto.Message.Response.CompileMessage.Kind.STD_OUT) { + if (compileMessage.hasText()) { + myDiagnosticSink.outputLineAvailable(compileMessage.getText()); + } + } + else { + final String sourceUri = compileMessage.hasSourceUri()? compileMessage.getSourceUri() : null; + final JavaFileObject srcFileObject = sourceUri != null? new DummyJavaFileObject(URI.create(sourceUri)) : null; + myDiagnosticSink.report(new DummyDiagnostic(convertKind(messageKind), srcFileObject, compileMessage)); + } + + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.OUTPUT_OBJECT) { + final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); + final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind(); + + final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null; + final File outputRootFile = outputRoot != null? new File(outputRoot) : null; + + final BinaryContent fileObjectContent; + final ByteString content = outputObject.hasContent()? outputObject.getContent() : null; + if (content != null) { + final byte[] bytes = content.toByteArray(); + fileObjectContent = new BinaryContent(bytes, 0, bytes.length); + } + else { + fileObjectContent = null; + } + + final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null; + final URI srcUri = sourceUri != null? URI.create(sourceUri) : null; + final OutputFileObject fileObject = new OutputFileObject( + null, + outputRootFile, + outputObject.hasRelativePath()? outputObject.getRelativePath() : null, + new File(outputObject.getFilePath()), + convertKind(kind), + outputObject.hasClassName()? outputObject.getClassName() : null, + srcUri, + myEncodingName, fileObjectContent + ); + + myOutputSink.save(fileObject); + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) { + final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); + final File file = new File(outputObject.getFilePath()); + myDiagnosticSink.javaFileLoaded(file); + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.CLASS_DATA) { + final JavacRemoteProto.Message.Response.ClassData data = response.getClassData(); + final String className = data.getClassName(); + final Collection<String> imports = data.getImportStatementList(); + final Collection<String> staticImports = data.getStaticImportList(); + myDiagnosticSink.registerImports(className, imports, staticImports); + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_COMPLETED) { + if (response.hasCompletionStatus()) { + myTerminatedSuccessfully = response.getCompletionStatus(); + } + return true; + } + + throw new Exception("Unsupported response type: " + responseType.name()); + } + + if (messageType == JavacRemoteProto.Message.Type.FAILURE) { + final JavacRemoteProto.Message.Failure failure = msg.getFailure(); + final StringBuilder buf = new StringBuilder(); + if (failure.hasDescription()) { + buf.append(failure.getDescription()); + } + if (failure.hasStacktrace()) { + if (buf.length() > 0) { + buf.append("\n"); + } + buf.append(failure.getStacktrace()); + } + myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buf.toString())); + return true; + } + + throw new Exception("Unsupported message type: " + messageType.name()); + } + catch (Throwable e) { + myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage())); + return true; + } + } + + public boolean isTerminatedSuccessfully() { + return myTerminatedSuccessfully; + } + + private static Diagnostic.Kind convertKind(JavacRemoteProto.Message.Response.CompileMessage.Kind kind) { + switch (kind) { + case ERROR: return Diagnostic.Kind.ERROR; + case WARNING: return Diagnostic.Kind.WARNING; + case MANDATORY_WARNING: return Diagnostic.Kind.MANDATORY_WARNING; + case NOTE: return Diagnostic.Kind.NOTE; + default : return Diagnostic.Kind.OTHER; + } + } + + private static OutputFileObject.Kind convertKind(JavacRemoteProto.Message.Response.OutputObject.Kind kind) { + switch (kind) { + case CLASS: return JavaFileObject.Kind.CLASS; + case HTML: return JavaFileObject.Kind.HTML; + case SOURCE: return JavaFileObject.Kind.SOURCE; + default : return JavaFileObject.Kind.OTHER; + } + } + + private static class DummyDiagnostic implements Diagnostic<JavaFileObject> { + + private final Kind myMessageKind; + private final JavaFileObject mySrcFileObject; + private final JavacRemoteProto.Message.Response.CompileMessage myCompileMessage; + + public DummyDiagnostic(final Kind messageKind, JavaFileObject srcFileObject, JavacRemoteProto.Message.Response.CompileMessage compileMessage) { + myMessageKind = messageKind; + mySrcFileObject = srcFileObject; + myCompileMessage = compileMessage; + } + + public Kind getKind() { + return myMessageKind; + } + + public JavaFileObject getSource() { + return mySrcFileObject; + } + + public long getPosition() { + return myCompileMessage.hasProblemLocationOffset()? myCompileMessage.getProblemLocationOffset() : -1; + } + + public long getStartPosition() { + return myCompileMessage.hasProblemBeginOffset()? myCompileMessage.getProblemBeginOffset() : -1; + } + + public long getEndPosition() { + return myCompileMessage.hasProblemEndOffset()? myCompileMessage.getProblemEndOffset() : -1; + } + + public long getLineNumber() { + return myCompileMessage.hasLine()? myCompileMessage.getLine() : -1; + } + + public long getColumnNumber() { + return myCompileMessage.hasColumn()? myCompileMessage.getColumn() : -1; + } + + public String getCode() { + return null; + } + + public String getMessage(Locale locale) { + return myCompileMessage.hasText()? myCompileMessage.getText() : null; + } + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java index 0ecfe7617160..d01ae7af6b4d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -15,18 +15,19 @@ */ package org.jetbrains.jps.javac; -import io.netty.bootstrap.ServerBootstrap; +import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.ChannelGroupFuture; -import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; -import io.netty.util.concurrent.ImmediateEventExecutor; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.Log4JLoggerFactory; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.Level; +import org.apache.log4j.PatternLayout; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.builders.impl.java.JavacCompilerTool; @@ -37,85 +38,109 @@ import org.jetbrains.jps.service.SharedThreadPool; import javax.tools.*; import java.io.File; import java.util.*; +import java.util.concurrent.TimeUnit; /** * @author Eugene Zhuravlev * Date: 1/22/12 */ -@SuppressWarnings("UseOfSystemOutOrSystemErr") -public class JavacServer { - public static final int DEFAULT_SERVER_PORT = 7878; - public static final String SERVER_SUCCESS_START_MESSAGE = "Javac server started successfully. Listening on port: "; - public static final String SERVER_ERROR_START_MESSAGE = "Error starting Javac Server: "; +public class ExternalJavacProcess { public static final String JPS_JAVA_COMPILING_TOOL_PROPERTY = "jps.java.compiling.tool"; - - private ChannelRegistrar myChannelRegistrar; - - public void start(int listenPort) { - final ServerBootstrap bootstrap = new ServerBootstrap().group(new NioEventLoopGroup(1, SharedThreadPool.getInstance())).channel(NioServerSocketChannel.class); - bootstrap.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true); - myChannelRegistrar = new ChannelRegistrar(); - final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler(); - bootstrap.childHandler(new ChannelInitializer() { + private final ChannelInitializer myChannelInitializer; + private final EventLoopGroup myEventLoopGroup; + private volatile ChannelFuture myConnectFuture; + private volatile CancelHandler myCancelHandler; + + static { + org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger(); + if (!root.getAllAppenders().hasMoreElements()) { + root.setLevel(Level.INFO); + root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN))); + } + InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); + } + + public ExternalJavacProcess() { + final JavacRemoteProto.Message msgDefaultInstance = JavacRemoteProto.Message.getDefaultInstance(); + + myEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance()); + myChannelInitializer = new ChannelInitializer() { @Override protected void initChannel(Channel channel) throws Exception { - channel.pipeline().addLast(myChannelRegistrar, - new ProtobufVarint32FrameDecoder(), - new ProtobufDecoder(JavacRemoteProto.Message.getDefaultInstance()), + channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(), + new ProtobufDecoder(msgDefaultInstance), new ProtobufVarint32LengthFieldPrepender(), new ProtobufEncoder(), - compilationRequestsHandler); + new CompilationRequestsHandler() + ); } - }); - myChannelRegistrar.add(bootstrap.bind(listenPort).syncUninterruptibly().channel()); - } - - public void stop() { - myChannelRegistrar.close().awaitUninterruptibly(); + }; } - + + //static volatile long myGlobalStart; + public static void main(String[] args) { - JavacServer server = null; - try { - int port = DEFAULT_SERVER_PORT; - if (args.length > 0) { - try { - port = Integer.parseInt(args[0]); - } - catch (NumberFormatException e) { - System.err.println("Error parsing port: " + e.getMessage()); - System.exit(-1); - } + //myGlobalStart = System.currentTimeMillis(); + UUID uuid = null; + String host = null; + int port = -1; + if (args.length > 0) { + try { + uuid = UUID.fromString(args[0]); } - - server = new JavacServer(); - server.start(port); - final JavacServer finalServer = server; - Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook thread") { - @Override - public void run() { - finalServer.stop(); - } - }); - - System.out.println("Server classpath: " + System.getProperty("java.class.path")); - System.err.println(SERVER_SUCCESS_START_MESSAGE + port); - } - catch (Throwable e) { - System.err.println(SERVER_ERROR_START_MESSAGE + e.getMessage()); - e.printStackTrace(System.err); + catch (Exception e) { + System.err.println("Error parsing session id: " + e.getMessage()); + System.exit(-1); + } + + host = args[1]; + try { - if (server != null) { - server.stop(); - } + port = Integer.parseInt(args[2]); } - finally { + catch (NumberFormatException e) { + System.err.println("Error parsing port: " + e.getMessage()); System.exit(-1); } } + else { + System.err.println("Insufficient parameters"); + System.exit(-1); + } + + final ExternalJavacProcess process = new ExternalJavacProcess(); + try { + //final long connectStart = System.currentTimeMillis(); + if (process.connect(host, port)) { + //final long connectEnd = System.currentTimeMillis(); + //System.err.println("Connected in " + (connectEnd - connectStart) + " ms; since start: " + (connectEnd - myGlobalStart)); + process.myConnectFuture.channel().writeAndFlush( + JavacProtoUtil.toMessage(uuid, JavacProtoUtil.createRequestAckResponse()) + ); + } + else { + System.err.println("Failed to connect to parent process"); + System.exit(-1); + } + } + catch (Throwable throwable) { + throwable.printStackTrace(System.err); + System.exit(-1); + } } - public static JavacRemoteProto.Message compile(final ChannelHandlerContext context, + private boolean connect(final String host, final int port) throws Throwable { + final Bootstrap bootstrap = new Bootstrap().group(myEventLoopGroup).channel(NioSocketChannel.class).handler(myChannelInitializer); + bootstrap.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true); + final ChannelFuture future = bootstrap.connect(host, port).syncUninterruptibly(); + if (future.isSuccess()) { + myConnectFuture = future; + return true; + } + return false; + } + + private static JavacRemoteProto.Message compile(final ChannelHandlerContext context, final UUID sessionId, List<String> options, Collection<File> files, @@ -124,6 +149,8 @@ public class JavacServer { Collection<File> sourcePath, Map<File, Set<File>> outs, final CanceledStatus canceledStatus) { + //final long compileStart = System.currentTimeMillis(); + //System.err.println("Compile start; since global start: " + (compileStart - myGlobalStart)); final DiagnosticOutputConsumer diagnostic = new DiagnosticOutputConsumer() { @Override public void javaFileLoaded(File file) { @@ -157,7 +184,8 @@ public class JavacServer { try { JavaCompilingTool tool = getCompilingTool(); - final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus, tool); + final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus, + tool); return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createBuildCompletedResponse(rc)); } catch (Throwable e) { @@ -165,8 +193,12 @@ public class JavacServer { e.printStackTrace(System.err); return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure(e.getMessage(), e)); } + //finally { + // final long compileEnd = System.currentTimeMillis(); + // System.err.println("Compiled in " + (compileEnd - compileStart) + " ms; since global start: " + (compileEnd - myGlobalStart)); + //} } - + private static JavaCompilingTool getCompilingTool() { String property = System.getProperty(JPS_JAVA_COMPILING_TOOL_PROPERTY); if (property != null) { @@ -178,24 +210,6 @@ public class JavacServer { return new JavacCompilerTool(); } - private final Set<CancelHandler> myCancelHandlers = Collections.synchronizedSet(new HashSet<CancelHandler>()); - - public void cancelBuilds() { - synchronized (myCancelHandlers) { - for (CancelHandler handler : myCancelHandlers) { - handler.cancel(); - } - } - } - - private static List<File> toFiles(List<String> paths) { - final List<File> files = new ArrayList<File>(paths.size()); - for (String path : paths) { - files.add(new File(path)); - } - return files; - } - @ChannelHandler.Sharable private class CompilationRequestsHandler extends SimpleChannelInboundHandler<JavacRemoteProto.Message> { @Override @@ -210,52 +224,50 @@ public class JavacServer { final JavacRemoteProto.Message.Request request = message.getRequest(); final JavacRemoteProto.Message.Request.Type requestType = request.getRequestType(); if (requestType == JavacRemoteProto.Message.Request.Type.COMPILE) { - final List<String> options = request.getOptionList(); - final List<File> files = toFiles(request.getFileList()); - final List<File> cp = toFiles(request.getClasspathList()); - final List<File> platformCp = toFiles(request.getPlatformClasspathList()); - final List<File> srcPath = toFiles(request.getSourcepathList()); - - final Map<File, Set<File>> outs = new HashMap<File, Set<File>>(); - for (JavacRemoteProto.Message.Request.OutputGroup outputGroup : request.getOutputList()) { - final Set<File> srcRoots = new HashSet<File>(); - for (String root : outputGroup.getSourceRootList()) { - srcRoots.add(new File(root)); + if (myCancelHandler == null) { // if not running yet + final List<String> options = request.getOptionList(); + final List<File> files = toFiles(request.getFileList()); + final List<File> cp = toFiles(request.getClasspathList()); + final List<File> platformCp = toFiles(request.getPlatformClasspathList()); + final List<File> srcPath = toFiles(request.getSourcepathList()); + + final Map<File, Set<File>> outs = new HashMap<File, Set<File>>(); + for (JavacRemoteProto.Message.Request.OutputGroup outputGroup : request.getOutputList()) { + final Set<File> srcRoots = new HashSet<File>(); + for (String root : outputGroup.getSourceRootList()) { + srcRoots.add(new File(root)); + } + outs.put(new File(outputGroup.getOutputRoot()), srcRoots); } - outs.put(new File(outputGroup.getOutputRoot()), srcRoots); - } - final CancelHandler cancelHandler = new CancelHandler(); - myCancelHandlers.add(cancelHandler); - SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() { - @Override - public void run() { - try { - context.channel() - .writeAndFlush(compile(context, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler)); + final CancelHandler cancelHandler = new CancelHandler(); + myCancelHandler = cancelHandler; + SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + context.channel().writeAndFlush( + compile(context, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler) + ).awaitUninterruptibly(); + } + finally { + myCancelHandler = null; + ExternalJavacProcess.this.stop(); + } } - finally { - myCancelHandlers.remove(cancelHandler); - } - } - }); + }); + } } else if (requestType == JavacRemoteProto.Message.Request.Type.CANCEL){ - cancelBuilds(); - reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createRequestAckResponse()); + cancelBuild(); } else if (requestType == JavacRemoteProto.Message.Request.Type.SHUTDOWN){ - cancelBuilds(); + cancelBuild(); new Thread("StopThread") { @Override public void run() { //noinspection finally - try { - JavacServer.this.stop(); - } - finally { - System.exit(0); - } + ExternalJavacProcess.this.stop(); } }.start(); } @@ -274,46 +286,38 @@ public class JavacServer { } } } - - @ChannelHandler.Sharable - private static final class ChannelRegistrar extends ChannelInboundHandlerAdapter { - private final ChannelGroup openChannels = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); - - public boolean isEmpty() { - return openChannels.isEmpty(); + + public void stop() { + try { + //final long stopStart = System.currentTimeMillis(); + //System.err.println("Exiting. Since global start " + (stopStart - myGlobalStart)); + final ChannelFuture future = myConnectFuture; + if (future != null) { + future.channel().close().await(); + } + myEventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS).await(); + //final long stopEnd = System.currentTimeMillis(); + //System.err.println("Stop completed in " + (stopEnd - stopStart) + "ms; since global start: " + ((stopEnd - myGlobalStart))); + System.exit(0); } - - public void add(@NotNull Channel serverChannel) { - assert serverChannel instanceof ServerChannel; - openChannels.add(serverChannel); + catch (Throwable e) { + e.printStackTrace(System.err); + System.exit(-1); } + } - @Override - public void channelActive(ChannelHandlerContext context) throws Exception { - // we don't need to remove channel on close - ChannelGroup do it - openChannels.add(context.channel()); - - super.channelActive(context); + private static List<File> toFiles(List<String> paths) { + final List<File> files = new ArrayList<File>(paths.size()); + for (String path : paths) { + files.add(new File(path)); } - - public ChannelGroupFuture close() { - EventLoopGroup eventLoopGroup = null; - for (Channel channel : openChannels) { - if (channel instanceof ServerChannel) { - eventLoopGroup = channel.eventLoop().parent(); - break; - } - } - - ChannelGroupFuture future; - try { - future = openChannels.close(); - } - finally { - assert eventLoopGroup != null; - eventLoopGroup.shutdownGracefully(); - } - return future; + return files; + } + + public void cancelBuild() { + final CancelHandler cancelHandler = myCancelHandler; + if (cancelHandler != null) { + cancelHandler.cancel(); } } @@ -332,4 +336,4 @@ public class JavacServer { return myIsCanceled; } } -}
\ No newline at end of file +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java new file mode 100644 index 000000000000..f5ff5b175bd8 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java @@ -0,0 +1,310 @@ +/* + * 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.javac; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.concurrency.Semaphore; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.ChannelGroupFuture; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.protobuf.ProtobufDecoder; +import io.netty.handler.codec.protobuf.ProtobufEncoder; +import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; +import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; +import io.netty.util.AttributeKey; +import io.netty.util.concurrent.ImmediateEventExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.java.JavaCompilingTool; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.GlobalContextKey; +import org.jetbrains.jps.incremental.Utils; +import org.jetbrains.jps.model.JpsProject; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; +import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; +import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions; +import org.jetbrains.jps.service.SharedThreadPool; + +import javax.tools.*; +import java.io.File; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author Eugene Zhuravlev + * Date: 1/22/12 + */ +@SuppressWarnings("UseOfSystemOutOrSystemErr") +public class ExternalJavacServer { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.javac.ExternalJavacServer"); + public static final GlobalContextKey<ExternalJavacServer> KEY = GlobalContextKey.create("_external_javac_server_"); + + public static final int DEFAULT_SERVER_PORT = 7878; + private static final AttributeKey<JavacProcessDescriptor> SESSION_DESCRIPTOR = AttributeKey.valueOf("ExternalJavacServer.JavacProcessDescriptor"); + + private ChannelRegistrar myChannelRegistrar; + private final Map<UUID, JavacProcessDescriptor> myMessageHandlers = new HashMap<UUID, JavacProcessDescriptor>(); + private int myListenPort = DEFAULT_SERVER_PORT; + + public void start(int listenPort) { + final ServerBootstrap bootstrap = new ServerBootstrap().group(new NioEventLoopGroup(1, SharedThreadPool.getInstance())).channel(NioServerSocketChannel.class); + bootstrap.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true); + myChannelRegistrar = new ChannelRegistrar(); + final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler(); + bootstrap.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(myChannelRegistrar, + new ProtobufVarint32FrameDecoder(), + new ProtobufDecoder(JavacRemoteProto.Message.getDefaultInstance()), + new ProtobufVarint32LengthFieldPrepender(), + new ProtobufEncoder(), + compilationRequestsHandler); + } + }); + myChannelRegistrar.add(bootstrap.bind(listenPort).syncUninterruptibly().channel()); + myListenPort = listenPort; + } + + private static int getExternalJavacHeapSize(CompileContext context) { + final JpsProject project = context.getProjectDescriptor().getProject(); + final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); + final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions(); + return options.MAXIMUM_HEAP_SIZE; + } + + + public boolean forkJavac(CompileContext context, List<String> options, + List<String> vmOptions, Collection<File> files, + Collection<File> classpath, + Collection<File> platformCp, + Collection<File> sourcePath, + Map<File, Set<File>> outs, + DiagnosticOutputConsumer diagnosticSink, + OutputFileConsumer outputSink, + final String javaHome, final JavaCompilingTool compilingTool) { + final ExternalJavacMessageHandler rh = new ExternalJavacMessageHandler(diagnosticSink, outputSink, getEncodingName(options)); + final JavacRemoteProto.Message.Request request = JavacProtoUtil.createCompilationRequest(options, files, classpath, platformCp, sourcePath, outs); + final UUID uuid = UUID.randomUUID(); + final JavacProcessDescriptor processDescriptor = new JavacProcessDescriptor(uuid, rh, request); + synchronized (myMessageHandlers) { + myMessageHandlers.put(uuid, processDescriptor); + } + try { + final JavacServerBootstrap.ExternalJavacProcessHandler processHandler = JavacServerBootstrap.launchExternalJavacProcess( + uuid, javaHome, getExternalJavacHeapSize(context), myListenPort, Utils.getSystemRoot(), vmOptions, compilingTool + ); + + while (!processDescriptor.waitFor(300L)) { + if (processHandler.isProcessTerminated() && processDescriptor.channel == null && processHandler.getExitCode() != 0) { + // process terminated abnormally and no communication took place + processDescriptor.setDone(); + break; + } + if (context.getCancelStatus().isCanceled()) { + processDescriptor.cancelBuild(); + } + } + + return rh.isTerminatedSuccessfully(); + } + catch (Throwable e) { + LOG.info(e); + diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage())); + } + finally { + unregisterMessageHandler(uuid); + } + return false; + } + + private void unregisterMessageHandler(UUID uuid) { + final JavacProcessDescriptor descriptor; + synchronized (myMessageHandlers) { + descriptor = myMessageHandlers.remove(uuid); + } + if (descriptor != null) { + descriptor.setDone(); + } + } + + @Nullable + private static String getEncodingName(List<String> options) { + boolean found = false; + for (String option : options) { + if (found) { + return option; + } + if ("-encoding".equalsIgnoreCase(option)) { + found = true; + } + } + return null; + } + + public void stop() { + myChannelRegistrar.close().awaitUninterruptibly(); + } + + @ChannelHandler.Sharable + private class CompilationRequestsHandler extends SimpleChannelInboundHandler<JavacRemoteProto.Message> { + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + JavacProcessDescriptor descriptor = ctx.attr(SESSION_DESCRIPTOR).get(); + if (descriptor != null) { + descriptor.setDone(); + } + super.channelUnregistered(ctx); + } + + @Override + public void channelRead0(final ChannelHandlerContext context, JavacRemoteProto.Message message) throws Exception { + JavacProcessDescriptor descriptor = context.attr(SESSION_DESCRIPTOR).get(); + + UUID sessionId; + if (descriptor == null) { + // this is the first message for this session, so fill session data with missing info + sessionId = JavacProtoUtil.fromProtoUUID(message.getSessionId()); + + descriptor = myMessageHandlers.get(sessionId); + if (descriptor != null) { + descriptor.channel = context.channel(); + context.attr(SESSION_DESCRIPTOR).set(descriptor); + } + } + else { + sessionId = descriptor.sessionId; + } + + final ExternalJavacMessageHandler handler = descriptor != null? descriptor.handler : null; + + final JavacRemoteProto.Message.Type messageType = message.getMessageType(); + + JavacRemoteProto.Message reply = null; + try { + if (messageType == JavacRemoteProto.Message.Type.RESPONSE) { + final JavacRemoteProto.Message.Response response = message.getResponse(); + final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType(); + if (handler != null) { + if (responseType == JavacRemoteProto.Message.Response.Type.REQUEST_ACK) { + final JavacRemoteProto.Message.Request request = descriptor.request; + if (request != null) { + reply = JavacProtoUtil.toMessage(sessionId, request); + descriptor.request = null; + } + } + else { + final boolean terminateOk = handler.handleMessage(message); + if (terminateOk) { + descriptor.setDone(); + } + } + } + else { + reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createCancelRequest()); + } + } + else { + reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported message: " + messageType.name(), null)); + } + } + finally { + if (reply != null) { + context.channel().writeAndFlush(reply); + } + } + } + } + + @ChannelHandler.Sharable + private static final class ChannelRegistrar extends ChannelInboundHandlerAdapter { + private final ChannelGroup openChannels = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); + + public boolean isEmpty() { + return openChannels.isEmpty(); + } + + public void add(@NotNull Channel serverChannel) { + assert serverChannel instanceof ServerChannel; + openChannels.add(serverChannel); + } + + @Override + public void channelActive(ChannelHandlerContext context) throws Exception { + // we don't need to remove channel on close - ChannelGroup do it + openChannels.add(context.channel()); + super.channelActive(context); + } + + public ChannelGroupFuture close() { + EventLoopGroup eventLoopGroup = null; + for (Channel channel : openChannels) { + if (channel instanceof ServerChannel) { + eventLoopGroup = channel.eventLoop().parent(); + break; + } + } + + ChannelGroupFuture future; + try { + future = openChannels.close(); + } + finally { + assert eventLoopGroup != null; + eventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS); + } + return future; + } + } + + private static class JavacProcessDescriptor { + @NotNull + final UUID sessionId; + @NotNull + final ExternalJavacMessageHandler handler; + volatile JavacRemoteProto.Message.Request request; + volatile Channel channel; + private final Semaphore myDone = new Semaphore(); + + public JavacProcessDescriptor(@NotNull UUID sessionId, @NotNull ExternalJavacMessageHandler handler, @NotNull JavacRemoteProto.Message.Request request) { + this.sessionId = sessionId; + this.handler = handler; + this.request = request; + myDone.down(); + } + + public void cancelBuild() { + if (channel != null) { + channel.writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createCancelRequest())); + } + } + + public void setDone() { + myDone.up(); + } + + + public boolean waitFor(long timeout) { + return myDone.waitFor(timeout); + } + + } + +}
\ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java index bb66e9c6b09f..ecf53395eb53 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java @@ -20,11 +20,9 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.concurrency.Semaphore; import org.jetbrains.jps.builders.java.JavaCompilingTool; import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.service.SharedThreadPool; @@ -32,6 +30,7 @@ import org.jetbrains.jps.service.SharedThreadPool; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.Future; /** @@ -40,15 +39,15 @@ import java.util.concurrent.Future; */ public class JavacServerBootstrap { - public static BaseOSProcessHandler launchJavacServer(String sdkHomePath, - int heapSize, - int port, - File workingDir, - List<String> vmOptions, - JavaCompilingTool compilingTool) throws Exception { + public static ExternalJavacProcessHandler launchExternalJavacProcess(UUID uuid, String sdkHomePath, + int heapSize, + int port, + File workingDir, + List<String> vmOptions, + JavaCompilingTool compilingTool) throws Exception { final List<String> cmdLine = new ArrayList<String>(); appendParam(cmdLine, getVMExecutablePath(sdkHomePath)); - appendParam(cmdLine, "-XX:MaxPermSize=150m"); + //appendParam(cmdLine, "-XX:MaxPermSize=150m"); //appendParam(cmdLine, "-XX:ReservedCodeCacheSize=64m"); appendParam(cmdLine, "-Djava.awt.headless=true"); final int xms = heapSize / 2; @@ -59,7 +58,7 @@ public class JavacServerBootstrap { // debugging //appendParam(cmdLine, "-XX:+HeapDumpOnOutOfMemoryError"); - //appendParam(cmdLine, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5009"); + //appendParam(cmdLine, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5009"); // javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language final String encoding = System.getProperty("file.encoding"); @@ -83,18 +82,20 @@ public class JavacServerBootstrap { appendParam(cmdLine, "-Duser.region=" + region); } - appendParam(cmdLine, "-D" + JavacServer.JPS_JAVA_COMPILING_TOOL_PROPERTY + "=" + compilingTool.getId()); + appendParam(cmdLine, "-D" + ExternalJavacProcess.JPS_JAVA_COMPILING_TOOL_PROPERTY + "=" + compilingTool.getId()); // this will disable standard extensions to ensure javac is loaded from the right tools.jar appendParam(cmdLine, "-Djava.ext.dirs="); - + + appendParam(cmdLine, "-Dlog4j.defaultInitOverride=true"); + for (String option : vmOptions) { appendParam(cmdLine, option); } appendParam(cmdLine, "-classpath"); - final List<File> cp = ClasspathBootstrap.getJavacServerClasspath(sdkHomePath, compilingTool); + final List<File> cp = ClasspathBootstrap.getExternalJavacProcessClasspath(sdkHomePath, compilingTool); final StringBuilder classpath = new StringBuilder(); for (File file : cp) { if (classpath.length() > 0) { @@ -104,7 +105,9 @@ public class JavacServerBootstrap { } appendParam(cmdLine, classpath.toString()); - appendParam(cmdLine, org.jetbrains.jps.javac.JavacServer.class.getName()); + appendParam(cmdLine, org.jetbrains.jps.javac.ExternalJavacProcess.class.getName()); + appendParam(cmdLine, uuid.toString()); + appendParam(cmdLine, "127.0.0.1"); appendParam(cmdLine, Integer.toString(port)); workingDir.mkdirs(); @@ -115,68 +118,31 @@ public class JavacServerBootstrap { builder.directory(workingDir); final Process process = builder.start(); - final BaseOSProcessHandler processHandler = new BaseOSProcessHandler(process, null, null) { + final ExternalJavacProcessHandler processHandler = new ExternalJavacProcessHandler(process); + processHandler.addProcessListener(new ProcessAdapter() { @Override - protected Future<?> executeOnPooledThread(Runnable task) { - return SharedThreadPool.getInstance().executeOnPooledThread(task); + public void processTerminated(ProcessEvent event) { + processHandler.setExitCode(event.getExitCode()); } - }; - configureProcessHandler(processHandler); - - return processHandler; - } - private static void configureProcessHandler(final BaseOSProcessHandler processHandler) throws Exception { - processHandler.addProcessListener(new ProcessAdapter() { public void onTextAvailable(ProcessEvent event, Key outputType) { final String text = event.getText(); if (!StringUtil.isEmptyOrSpaces(text)) { if (outputType == ProcessOutputTypes.STDOUT) { - System.out.print("JAVAC_SERVER: " + text); - } - else if (outputType == ProcessOutputTypes.STDERR){ - System.err.print("JAVAC_SERVER: " + text); + System.out.print("JAVAC_PROCESS: " + text); } - } - } - }); - final Semaphore semaphore = new Semaphore(); - semaphore.down(); - final Ref<String> serverStartMessage = new Ref<String>(null); - processHandler.addProcessListener(new ProcessAdapter() { - public void processTerminated(ProcessEvent event) { - try { - processHandler.removeProcessListener(this); - } - finally { - semaphore.up(); - } - } - - public void onTextAvailable(ProcessEvent event, Key outputType) { - if (outputType == ProcessOutputTypes.STDERR) { - final String text = event.getText(); - if (text != null && (text.contains(JavacServer.SERVER_SUCCESS_START_MESSAGE) || text.contains(JavacServer.SERVER_ERROR_START_MESSAGE))) { - try { - processHandler.removeProcessListener(this); - serverStartMessage.set(text); - } - finally { - semaphore.up(); - } + else if (outputType == ProcessOutputTypes.STDERR) { + System.err.print("JAVAC_PROCESS: " + text); } } } }); processHandler.startNotify(); - semaphore.waitFor(); - - final String startupMsg = serverStartMessage.get(); - if (startupMsg == null || !startupMsg.contains(JavacServer.SERVER_SUCCESS_START_MESSAGE)) { - throw new Exception("Server startup failed: " + startupMsg); - } + return processHandler; } + + private static void appendParam(List<String> cmdLine, String param) { if (SystemInfo.isWindows) { if (param.contains("\"")) { @@ -192,4 +158,25 @@ public class JavacServerBootstrap { public static String getVMExecutablePath(String sdkHome) { return sdkHome + "/bin/java"; } + + public static class ExternalJavacProcessHandler extends BaseOSProcessHandler { + private volatile int myExitCode; + + ExternalJavacProcessHandler(Process process) { + super(process, null, null); + } + + @Override + protected Future<?> executeOnPooledThread(Runnable task) { + return SharedThreadPool.getInstance().executeOnPooledThread(task); + } + + void setExitCode(int exitCode) { + myExitCode = exitCode; + } + + public int getExitCode() { + return myExitCode; + } + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java deleted file mode 100644 index 8085068b56e6..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java +++ /dev/null @@ -1,78 +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.javac; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.api.RequestFuture; -import org.jetbrains.jps.client.SimpleProtobufClient; -import org.jetbrains.jps.client.UUIDGetter; -import org.jetbrains.jps.service.SharedThreadPool; - -import java.io.File; -import java.util.*; - -/** - * @author Eugene Zhuravlev - * Date: 1/22/12 - */ -public class JavacServerClient extends SimpleProtobufClient<JavacServerResponseHandler>{ - - public JavacServerClient() { - super(JavacRemoteProto.Message.getDefaultInstance(), SharedThreadPool.getInstance(), new UUIDGetter() { - @Override - @NotNull - public UUID getSessionUUID(@NotNull JavacRemoteProto.Message message) { - final JavacRemoteProto.Message.UUID uuid = message.getSessionId(); - return new UUID(uuid.getMostSigBits(), uuid.getLeastSigBits()); - } - }); - } - - public RequestFuture<JavacServerResponseHandler> sendCompileRequest(List<String> options, Collection<File> files, Collection<File> classpath, Collection<File> platformCp, Collection<File> sourcePath, Map<File, Set<File>> outs, DiagnosticOutputConsumer diagnosticSink, OutputFileConsumer outputSink) { - final JavacServerResponseHandler rh = new JavacServerResponseHandler(diagnosticSink, outputSink, getEncodingName(options)); - final JavacRemoteProto.Message.Request request = JavacProtoUtil.createCompilationRequest(options, files, classpath, platformCp, sourcePath, outs); - return sendRequest(request, rh, new RequestFuture.CancelAction<JavacServerResponseHandler>() { - @Override - public void cancel(RequestFuture<JavacServerResponseHandler> javacServerResponseHandlerRequestFuture) throws Exception { - sendRequest(JavacProtoUtil.createCancelRequest(), null, null); - } - }); - } - - public RequestFuture sendShutdownRequest() { - return sendRequest(JavacProtoUtil.createShutdownRequest(), null, null); - } - - private RequestFuture<JavacServerResponseHandler> sendRequest(final JavacRemoteProto.Message.Request request, final JavacServerResponseHandler responseHandler, final RequestFuture.CancelAction<JavacServerResponseHandler> cancelAction) { - final UUID requestId = UUID.randomUUID(); - return sendMessage(requestId, JavacProtoUtil.toMessage(requestId, request), responseHandler, cancelAction); - } - - @Nullable - private static String getEncodingName(List<String> options) { - boolean found = false; - for (String option : options) { - if (found) { - return option; - } - if ("-encoding".equalsIgnoreCase(option)) { - found = true; - } - } - return null; - } -} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java deleted file mode 100644 index 688bf0596ac4..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java +++ /dev/null @@ -1,231 +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.javac; - -import com.google.protobuf.ByteString; -import com.google.protobuf.MessageLite; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.client.ProtobufResponseHandler; -import org.jetbrains.jps.incremental.BinaryContent; - -import javax.tools.Diagnostic; -import javax.tools.JavaFileObject; -import java.io.File; -import java.net.URI; -import java.util.Collection; -import java.util.Locale; - -/** - * @author Eugene Zhuravlev - * Date: 1/22/12 - */ -public class JavacServerResponseHandler implements ProtobufResponseHandler{ - private final DiagnosticOutputConsumer myDiagnosticSink; - private final OutputFileConsumer myOutputSink; - @Nullable - private final String myEncodingName; - private volatile boolean myTerminatedSuccessfully; - - public JavacServerResponseHandler(DiagnosticOutputConsumer diagnosticSink, OutputFileConsumer outputSink, @Nullable final String encodingName) { - myDiagnosticSink = diagnosticSink; - myOutputSink = outputSink; - myEncodingName = encodingName; - } - - public boolean handleMessage(MessageLite message) throws Exception { - final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)message; - final JavacRemoteProto.Message.Type messageType = msg.getMessageType(); - - if (messageType == JavacRemoteProto.Message.Type.RESPONSE) { - final JavacRemoteProto.Message.Response response = msg.getResponse(); - final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType(); - - if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_MESSAGE) { - final JavacRemoteProto.Message.Response.CompileMessage compileMessage = response.getCompileMessage(); - final JavacRemoteProto.Message.Response.CompileMessage.Kind messageKind = compileMessage.getKind(); - - if (messageKind == JavacRemoteProto.Message.Response.CompileMessage.Kind.STD_OUT) { - if (compileMessage.hasText()) { - myDiagnosticSink.outputLineAvailable(compileMessage.getText()); - } - } - else { - final String sourceUri = compileMessage.hasSourceUri()? compileMessage.getSourceUri() : null; - final JavaFileObject srcFileObject = sourceUri != null? new DummyJavaFileObject(URI.create(sourceUri)) : null; - myDiagnosticSink.report(new DummyDiagnostic(convertKind(messageKind), srcFileObject, compileMessage)); - } - - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.OUTPUT_OBJECT) { - final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); - final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind(); - - final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null; - final File outputRootFile = outputRoot != null? new File(outputRoot) : null; - - final BinaryContent fileObjectContent; - final ByteString content = outputObject.hasContent()? outputObject.getContent() : null; - if (content != null) { - final byte[] bytes = content.toByteArray(); - fileObjectContent = new BinaryContent(bytes, 0, bytes.length); - } - else { - fileObjectContent = null; - } - - final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null; - final URI srcUri = sourceUri != null? URI.create(sourceUri) : null; - final OutputFileObject fileObject = new OutputFileObject( - null, - outputRootFile, - outputObject.hasRelativePath()? outputObject.getRelativePath() : null, - new File(outputObject.getFilePath()), - convertKind(kind), - outputObject.hasClassName()? outputObject.getClassName() : null, - srcUri, - myEncodingName, fileObjectContent - ); - - myOutputSink.save(fileObject); - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) { - final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); - final File file = new File(outputObject.getFilePath()); - myDiagnosticSink.javaFileLoaded(file); - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.CLASS_DATA) { - final JavacRemoteProto.Message.Response.ClassData data = response.getClassData(); - final String className = data.getClassName(); - final Collection<String> imports = data.getImportStatementList(); - final Collection<String> staticImports = data.getStaticImportList(); - myDiagnosticSink.registerImports(className, imports, staticImports); - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_COMPLETED) { - if (response.hasCompletionStatus()) { - myTerminatedSuccessfully = response.getCompletionStatus(); - } - return true; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.REQUEST_ACK) { - return true; - } - - throw new Exception("Unsupported response type: " + responseType.name()); - } - - if (messageType == JavacRemoteProto.Message.Type.FAILURE) { - final JavacRemoteProto.Message.Failure failure = msg.getFailure(); - final StringBuilder buf = new StringBuilder(); - if (failure.hasDescription()) { - buf.append(failure.getDescription()); - } - if (failure.hasStacktrace()) { - if (buf.length() > 0) { - buf.append("\n"); - } - buf.append(failure.getStacktrace()); - } - myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buf.toString())); - return true; - } - - throw new Exception("Unsupported message type: " + messageType.name()); - } - - public boolean isTerminatedSuccessfully() { - return myTerminatedSuccessfully; - } - - private static Diagnostic.Kind convertKind(JavacRemoteProto.Message.Response.CompileMessage.Kind kind) { - switch (kind) { - case ERROR: return Diagnostic.Kind.ERROR; - case WARNING: return Diagnostic.Kind.WARNING; - case MANDATORY_WARNING: return Diagnostic.Kind.MANDATORY_WARNING; - case NOTE: return Diagnostic.Kind.NOTE; - default : return Diagnostic.Kind.OTHER; - } - } - - private static OutputFileObject.Kind convertKind(JavacRemoteProto.Message.Response.OutputObject.Kind kind) { - switch (kind) { - case CLASS: return JavaFileObject.Kind.CLASS; - case HTML: return JavaFileObject.Kind.HTML; - case SOURCE: return JavaFileObject.Kind.SOURCE; - default : return JavaFileObject.Kind.OTHER; - } - } - - public void sessionTerminated() { - } - - private static class DummyDiagnostic implements Diagnostic<JavaFileObject> { - - private final Kind myMessageKind; - private final JavaFileObject mySrcFileObject; - private final JavacRemoteProto.Message.Response.CompileMessage myCompileMessage; - - public DummyDiagnostic(final Kind messageKind, JavaFileObject srcFileObject, JavacRemoteProto.Message.Response.CompileMessage compileMessage) { - myMessageKind = messageKind; - mySrcFileObject = srcFileObject; - myCompileMessage = compileMessage; - } - - public Kind getKind() { - return myMessageKind; - } - - public JavaFileObject getSource() { - return mySrcFileObject; - } - - public long getPosition() { - return myCompileMessage.hasProblemLocationOffset()? myCompileMessage.getProblemLocationOffset() : -1; - } - - public long getStartPosition() { - return myCompileMessage.hasProblemBeginOffset()? myCompileMessage.getProblemBeginOffset() : -1; - } - - public long getEndPosition() { - return myCompileMessage.hasProblemEndOffset()? myCompileMessage.getProblemEndOffset() : -1; - } - - public long getLineNumber() { - return myCompileMessage.hasLine()? myCompileMessage.getLine() : -1; - } - - public long getColumnNumber() { - return myCompileMessage.hasColumn()? myCompileMessage.getColumn() : -1; - } - - public String getCode() { - return null; - } - - public String getMessage(Locale locale) { - return myCompileMessage.hasText()? myCompileMessage.getText() : null; - } - } -} diff --git a/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java b/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java index 689776eafda0..3d9b302d38cf 100644 --- a/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java +++ b/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java @@ -19,5 +19,5 @@ package org.jetbrains.jps.model.java; * @author nik */ public enum LanguageLevel { - JDK_1_3, JDK_1_4, JDK_1_5, JDK_1_6, JDK_1_7, JDK_1_8 + JDK_1_3, JDK_1_4, JDK_1_5, JDK_1_6, JDK_1_7, JDK_1_8, JDK_1_9 } |