From 520f7556b07d6ab4ecb784cb0435c9fcb0676176 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Thu, 18 Jun 2015 23:12:31 +0200 Subject: [PATCH] debugger to perform compilations in external process using target jdk --- .../compiler/CompilerConfigurationImpl.java | 2 +- .../compiler/server/BuildManager.java | 102 +++---- java/debugger/impl/debugger-impl.iml | 2 + .../ui/impl/watch/CompilingEvaluator.java | 71 +---- .../ui/impl/watch/CompilingEvaluatorImpl.java | 251 ++++++++++++++---- .../ui/impl/watch/EvaluationDescriptor.java | 2 +- .../jps/incremental/java/JavaBuilder.java | 11 +- .../jps/javac/ExternalJavacManager.java | 53 ++-- .../jps/service/SharedThreadPool.java | 6 +- .../jetbrains/jps/service/ThreadExecutor.java | 23 -- 10 files changed, 319 insertions(+), 204 deletions(-) delete mode 100644 jps/model-api/src/org/jetbrains/jps/service/ThreadExecutor.java diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java index 2b12302a0937..9f701721e4f0 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java @@ -111,7 +111,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements private Map myProcessorsProfilesMap = null; @Nullable - private String myBytecodeTargetLevel = null; // null means compiler default + private String myBytecodeTargetLevel = null; // null means same as effective language level private final Map myModuleBytecodeTarget = new HashMap(); public CompilerConfigurationImpl(Project project) { diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index 120b5960190e..79933a517a69 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -109,7 +109,8 @@ import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.model.serialization.JpsGlobalLoader; import javax.swing.*; -import javax.tools.*; +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; import java.awt.*; import java.io.File; import java.io.FileFilter; @@ -918,6 +919,58 @@ public class BuildManager implements ApplicationComponent{ return "com.intellij.compiler.server.BuildManager"; } + @NotNull + public static Pair getBuildProcessRuntimeSdk(Project project) { + Sdk projectJdk = null; + int sdkMinorVersion = 0; + JavaSdkVersion sdkVersion = null; + + final Set candidates = new HashSet(); + final Sdk defaultSdk = ProjectRootManager.getInstance(project).getProjectSdk(); + if (defaultSdk != null && defaultSdk.getSdkType() instanceof JavaSdkType) { + candidates.add(defaultSdk); + } + + for (Module module : ModuleManager.getInstance(project).getModules()) { + final Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) { + candidates.add(sdk); + } + } + + // now select the latest version from the sdks that are used in the project, but not older than the internal sdk version + final JavaSdk javaSdkType = JavaSdk.getInstance(); + for (Sdk candidate : candidates) { + final String vs = candidate.getVersionString(); + if (vs != null) { + final JavaSdkVersion candidateVersion = javaSdkType.getVersion(vs); + if (candidateVersion != null) { + final int candidateMinorVersion = getMinorVersion(vs); + if (projectJdk == null) { + sdkVersion = candidateVersion; + sdkMinorVersion = candidateMinorVersion; + projectJdk = candidate; + } + else { + final int result = candidateVersion.compareTo(sdkVersion); + if (result > 0 || (result == 0 && candidateMinorVersion > sdkMinorVersion)) { + sdkVersion = candidateVersion; + sdkMinorVersion = candidateMinorVersion; + projectJdk = candidate; + } + } + } + } + } + + final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); + if (projectJdk == null || sdkVersion == null || !sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_6)) { + projectJdk = internalJdk; + sdkVersion = javaSdkType.getVersion(internalJdk); + } + return Pair.create(projectJdk, sdkVersion); + } + private Future, OSProcessHandler>> launchPreloadedBuildProcess(final Project project, SequentialTaskExecutor projectTaskQueue) throws Exception { ensureListening(); @@ -955,52 +1008,11 @@ public class BuildManager implements ApplicationComponent{ if (StringUtil.isEmptyOrSpaces(forcedCompiledJdkHome)) { // choosing sdk with which the build process should be run - Sdk projectJdk = null; - int sdkMinorVersion = 0; - - final Set candidates = new HashSet(); - final Sdk defaultSdk = ProjectRootManager.getInstance(project).getProjectSdk(); - if (defaultSdk != null && defaultSdk.getSdkType() instanceof JavaSdkType) { - candidates.add(defaultSdk); - } - - for (Module module : ModuleManager.getInstance(project).getModules()) { - final Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); - if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) { - candidates.add(sdk); - } - } - - // now select the latest version from the sdks that are used in the project, but not older than the internal sdk version - final JavaSdk javaSdkType = JavaSdk.getInstance(); - for (Sdk candidate : candidates) { - final String vs = candidate.getVersionString(); - if (vs != null) { - final JavaSdkVersion candidateVersion = javaSdkType.getVersion(vs); - if (candidateVersion != null) { - final int candidateMinorVersion = getMinorVersion(vs); - if (projectJdk == null) { - sdkVersion = candidateVersion; - sdkMinorVersion = candidateMinorVersion; - projectJdk = candidate; - } - else { - final int result = candidateVersion.compareTo(sdkVersion); - if (result > 0 || (result == 0 && candidateMinorVersion > sdkMinorVersion)) { - sdkVersion = candidateVersion; - sdkMinorVersion = candidateMinorVersion; - projectJdk = candidate; - } - } - } - } - } + final Pair pair = getBuildProcessRuntimeSdk(project); + final Sdk projectJdk = pair.first; + sdkVersion = pair.second; final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); - if (projectJdk == null || sdkVersion == null || !sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_6)) { - projectJdk = internalJdk; - } - // validate tools.jar presence final JavaSdkType projectJdkType = (JavaSdkType)projectJdk.getSdkType(); if (FileUtil.pathsEqual(projectJdk.getHomePath(), internalJdk.getHomePath())) { diff --git a/java/debugger/impl/debugger-impl.iml b/java/debugger/impl/debugger-impl.iml index a18ddacc54f0..d9f4692e2124 100644 --- a/java/debugger/impl/debugger-impl.iml +++ b/java/debugger/impl/debugger-impl.iml @@ -15,12 +15,14 @@ + + diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java index 5d4aaed71106..a40a3ba71dc5 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java @@ -37,15 +37,14 @@ import com.sun.jdi.ClassLoaderReference; import com.sun.jdi.ClassType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.incremental.BinaryContent; +import org.jetbrains.jps.javac.OutputFileObject; import org.jetbrains.org.objectweb.asm.ClassReader; import org.jetbrains.org.objectweb.asm.ClassVisitor; import org.jetbrains.org.objectweb.asm.ClassWriter; import org.jetbrains.org.objectweb.asm.Opcodes; -import javax.tools.*; -import java.io.ByteArrayOutputStream; -import java.net.URI; -import java.util.ArrayList; import java.util.Collection; /** @@ -81,8 +80,7 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator { ClassLoaderReference classLoader = ClassLoadingUtils.getClassLoader(evaluationContext, process); String version = ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).version(); - JavaSdkVersion sdkVersion = JdkVersionUtil.getVersion(version); - Collection classes = compile(sdkVersion != null ? sdkVersion.getDescription() : null); + Collection classes = compile(JdkVersionUtil.getVersion(version)); defineClasses(classes, evaluationContext, process, classLoader); @@ -120,8 +118,11 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator { ClassLoaderReference classLoader) throws EvaluateException { for (OutputFileObject cls : classes) { if (cls.getName().contains(GEN_CLASS_NAME)) { - byte[] bytes = changeSuperToMagicAccessor(cls.toByteArray()); - ClassLoadingUtils.defineClass(cls.myOrigName, bytes, context, process, classLoader); + final BinaryContent content = cls.getContent(); + if (content != null) { + byte[] bytes = changeSuperToMagicAccessor(content.toByteArray()); + ClassLoadingUtils.defineClass(cls.getClassName(), bytes, context, process, classLoader); + } } } return (ClassType)process.findClass(context, getGenClassQName(), classLoader); @@ -164,58 +165,6 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator { ///////////////// Compiler stuff @NotNull - protected abstract Collection compile(String target) throws EvaluateException; - - private static URI getUri(String name, JavaFileObject.Kind kind) { - return URI.create("memo:///" + name.replace('.', '/') + kind.extension); - } - - protected static class SourceFileObject extends SimpleJavaFileObject { - private final String myContent; - - SourceFileObject(String name, Kind kind, String content) { - super(getUri(name, kind), kind); - myContent = content; - } - - @Override - public CharSequence getCharContent(boolean ignore) { - return myContent; - } - } - - protected static class OutputFileObject extends SimpleJavaFileObject { - private final ByteArrayOutputStream myStream = new ByteArrayOutputStream(); - private final String myOrigName; - - OutputFileObject(String name, Kind kind) { - super(getUri(name, kind), kind); - myOrigName = name; - } - - byte[] toByteArray() { - return myStream.toByteArray(); - } - - @Override - public ByteArrayOutputStream openOutputStream() { - return myStream; - } - } - - protected static class MemoryFileManager extends ForwardingJavaFileManager { - protected final Collection classes = new ArrayList(); - - MemoryFileManager(JavaCompiler compiler) { - super(compiler.getStandardFileManager(null, null, null)); - } - - @Override - public OutputFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) { - OutputFileObject mc = new OutputFileObject(name, kind); - classes.add(mc); - return mc; - } - } + protected abstract Collection compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java index e7e2b8d62e5b..1a781a0c85d6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java @@ -15,70 +15,144 @@ */ package com.intellij.debugger.ui.impl.watch; +import com.intellij.compiler.CompilerConfiguration; +import com.intellij.compiler.server.BuildManager; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebugProcessAdapter; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.EffectiveLanguageLevelUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.projectRoots.JavaSdkType; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkTypeId; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; -import com.intellij.util.PathsList; +import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.api.CanceledStatus; +import org.jetbrains.jps.builders.impl.java.JavacCompilerTool; +import org.jetbrains.jps.javac.DiagnosticOutputConsumer; +import org.jetbrains.jps.javac.ExternalJavacManager; +import org.jetbrains.jps.javac.OutputFileConsumer; +import org.jetbrains.jps.javac.OutputFileObject; -import javax.tools.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.io.File; +import java.io.IOException; +import java.util.*; +// todo: consider batching compilations in order not to start a separate process for every class that needs to be compiled public class CompilingEvaluatorImpl extends CompilingEvaluator { - public CompilingEvaluatorImpl(@NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) { + private final EvaluationContextImpl myEvaluationContext; + + public CompilingEvaluatorImpl(EvaluationContextImpl evaluationContext, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) { super(context, data); + myEvaluationContext = evaluationContext; } @Override @NotNull - protected Collection compile(String target) throws EvaluateException { - if (!SystemInfo.isJavaVersionAtLeast(target)) { - throw new EvaluateException("Unable to compile for target level " + target + ". Need to run IDEA on java version at least " + target + ", currently running on " + SystemInfo.JAVA_RUNTIME_VERSION); - } - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - MemoryFileManager manager = new MemoryFileManager(compiler); - DiagnosticCollector diagnostic = new DiagnosticCollector(); - Module module = ApplicationManager.getApplication().runReadAction(new Computable() { + protected Collection compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException { + final Pair runtime = BuildManager.getBuildProcessRuntimeSdk(myEvaluationContext.getProject()); + final Module module = ApplicationManager.getApplication().runReadAction(new Computable() { @Override public Module compute() { return ModuleUtilCore.findModuleForPsiElement(myPsiContext); } }); - List options = new ArrayList(); - if (module != null) { - options.add("-cp"); - PathsList cp = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList(); - options.add(cp.getPathsString()); + String javaHome = null; + final Sdk sdk = runtime.getFirst(); + final SdkTypeId type = sdk.getSdkType(); + if (type instanceof JavaSdkType) { + javaHome = sdk.getHomePath(); } - if (!StringUtil.isEmpty(target)) { + if (javaHome == null) { + throw new EvaluateException("Was not able to determine JDK for current evaluation context"); + } + final List options = new ArrayList(); + options.add("-proc:none"); // for our purposes annotation processing is not needed + options.add("-encoding"); + options.add("UTF-8"); + final List platformClasspath = new ArrayList(); + final List classpath = new ArrayList(); + if (module != null) { + final ModuleRootManager rootManager = ModuleRootManager.getInstance(module); + rootManager.orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList().addAllFiles(classpath); + rootManager.orderEntries().compileOnly().sdkOnly().getPathsList().addAllFiles(platformClasspath); + + final String sourceOption = getSourceOption(ApplicationManager.getApplication().runReadAction(new Computable() { + public LanguageLevel compute() { + return EffectiveLanguageLevelUtil.getEffectiveLanguageLevel(module); + } + })); options.add("-source"); - options.add(target); + options.add(sourceOption); + + String target = CompilerConfiguration.getInstance(module.getProject()).getBytecodeTargetLevel(module); + if (target == null) { + target = sourceOption; + } options.add("-target"); options.add(target); } + else { + if (debuggeeVersion != null) { + // if both module context and debuggee version are unknown, let source and target be the compiler's defaults + String sourceOption; + final JavaSdkVersion buildRuntimeVersion = runtime.getSecond(); + if (buildRuntimeVersion == null) { + sourceOption = getSourceOption(debuggeeVersion.getMaxLanguageLevel()); + } + else { + final JavaSdkVersion minVersion = buildRuntimeVersion.ordinal() > debuggeeVersion.ordinal() ? debuggeeVersion : buildRuntimeVersion; + sourceOption = getSourceOption(minVersion.getMaxLanguageLevel()); + } + options.add("-source"); + options.add(sourceOption); + options.add("-target"); + options.add(sourceOption); + } + } + File sourceFile = null; + final OutputCollector outputSink = new OutputCollector(); try { - if (!compiler.getTask(null, - manager, - diagnostic, - options, - null, - Collections.singletonList(new SourceFileObject(getMainClassName(), JavaFileObject.Kind.SOURCE, getClassCode())) - ).call()) { - StringBuilder res = new StringBuilder("Compilation failed:\n"); + final ExternalJavacManager javacManager = getJavacManager(); + if (javacManager == null) { + throw new EvaluateException("Cannot compile java code"); + } + sourceFile = generateTempSourceFile(javacManager.getWorkingDir()); + final File srcDir = sourceFile.getParentFile(); + final Map> output = Collections.singletonMap(srcDir, Collections.singleton(srcDir)); + DiagnosticCollector diagnostic = new DiagnosticCollector(); + final List vmOptions = Collections.emptyList(); + final List sourcePath = Collections.emptyList(); + final Set sources = Collections.singleton(sourceFile); + boolean compiledOK = javacManager.forkJavac( + javaHome, -1, vmOptions, options, platformClasspath, classpath, sourcePath, sources, output, diagnostic, outputSink, new JavacCompilerTool(), CanceledStatus.NULL + ); + + if (!compiledOK) { + final StringBuilder res = new StringBuilder("Compilation failed:\n"); for (Diagnostic d : diagnostic.getDiagnostics()) { - res.append(d); + if (d.getKind() == Diagnostic.Kind.ERROR) { + res.append(d.getMessage(Locale.US)); + } } throw new EvaluateException(res.toString()); } @@ -86,24 +160,111 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator { catch (Exception e) { throw new EvaluateException(e.getMessage()); } - return manager.classes; + finally { + if (sourceFile != null) { + FileUtil.delete(sourceFile); + } + } + return outputSink.getCompiledClasses(); } - protected String getClassCode() { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return myData.getGeneratedInnerClass().getContainingFile().getText(); - } - }); + @NotNull + private static String getSourceOption(@NotNull LanguageLevel languageLevel) { + return "1." + Integer.valueOf(3 + languageLevel.ordinal()); } - protected String getMainClassName() { - return ApplicationManager.getApplication().runReadAction(new Computable() { + private File generateTempSourceFile(File workingDir) throws IOException { + final Pair fileData = ApplicationManager.getApplication().runReadAction(new Computable>() { @Override - public String compute() { - return FileUtil.getNameWithoutExtension(myData.getGeneratedInnerClass().getContainingFile().getName()); + public Pair compute() { + final PsiFile file = myData.getGeneratedInnerClass().getContainingFile(); + return Pair.create(file.getName(), file.getText()); } }); + if (fileData.first == null) { + throw new IOException("Class file name not specified"); + } + if (fileData.second == null) { + throw new IOException("Class source code not specified"); + } + final File file = new File(workingDir, "src/"+fileData.first); + FileUtil.writeToFile(file, fileData.second); + return file; + } + + private static final Key JAVAC_MANAGER_KEY = Key.create("_external_java_compiler_manager_"); + + @Nullable + private ExternalJavacManager getJavacManager() throws IOException { + // need dedicated thread access to be able to cache the manager in the user data + DebuggerManagerThreadImpl.assertIsManagerThread(); + + final DebugProcessImpl debugProcess = myEvaluationContext.getDebugProcess(); + ExternalJavacManager manager = JAVAC_MANAGER_KEY.get(debugProcess); + if (manager == null && debugProcess.isAttached()) { + final File compilerWorkingDir = getCompilerWorkingDir(); + if (compilerWorkingDir == null) { + return null; // should not happen for real projects + } + final int listenPort = NetUtils.findAvailableSocketPort(); + manager = new ExternalJavacManager(compilerWorkingDir); + manager.start(listenPort); + final ExternalJavacManager _manager = manager; + debugProcess.addDebugProcessListener(new DebugProcessAdapter() { + public void processDetached(DebugProcess process, boolean closedByUser) { + if (process == debugProcess) { + _manager.stop(); + } + } + }); + JAVAC_MANAGER_KEY.set(debugProcess, manager); + } + return manager; + } + + @Nullable + private File getCompilerWorkingDir() { + final File projectBuildDir = BuildManager.getInstance().getProjectSystemDirectory(myEvaluationContext.getProject()); + if (projectBuildDir == null) { + return null; + } + final File root = new File(projectBuildDir, "debugger"); + root.mkdirs(); + return root; + } + + private static class DiagnosticCollector implements DiagnosticOutputConsumer { + private final List> myDiagnostics = new ArrayList>(); + public void outputLineAvailable(String line) { + // todo: do we need these messages? + } + + public void registerImports(String className, Collection imports, Collection staticImports) { + // ignore + } + + public void javaFileLoaded(File file) { + // ignore + } + + public void report(Diagnostic diagnostic) { + myDiagnostics.add(diagnostic); + } + + public List> getDiagnostics() { + return myDiagnostics; + } + } + + private static class OutputCollector implements OutputFileConsumer { + private List myClasses = new ArrayList(); + + public void save(@NotNull OutputFileObject fileObject) { + myClasses.add(fileObject); + } + + public List getCompiledClasses() { + return myClasses; + } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java index 62c6d225dcfe..620424e34457 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java @@ -99,7 +99,7 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{ ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(myProject, psiFile, fragment, CompilingEvaluator.getGeneratedClassName()); if (data != null) { - return new CompilingEvaluatorImpl(psiContext, data); + return new CompilingEvaluatorImpl(evaluationContext, psiContext, data); } } catch (PrepareFailedException e) { 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 a91de8dcb014..4a040b3a3ade 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 @@ -67,6 +67,7 @@ import java.io.*; import java.net.ServerSocket; import java.util.*; import java.util.concurrent.Executor; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; /** @@ -531,7 +532,15 @@ public class JavaBuilder extends ModuleLevelBuilder { return server; } final int listenPort = findFreePort(); - server = new ExternalJavacManager(Utils.getSystemRoot(), SharedThreadPool.getInstance()); + server = new ExternalJavacManager(Utils.getSystemRoot()) { + protected ExternalJavacProcessHandler createProcessHandler(Process process) { + return new ExternalJavacProcessHandler(process) { + protected Future executeOnPooledThread(Runnable task) { + return SharedThreadPool.getInstance().executeOnPooledThread(task); + } + }; + } + }; server.start(listenPort); ExternalJavacManager.KEY.set(context, server); return server; diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java index faa083d9743b..225bf030ebf5 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacManager.java @@ -45,12 +45,10 @@ import org.jetbrains.jps.builders.java.JavaCompilingTool; import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.incremental.GlobalContextKey; import org.jetbrains.jps.service.SharedThreadPool; -import org.jetbrains.jps.service.ThreadExecutor; import javax.tools.Diagnostic; import java.io.File; import java.util.*; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** @@ -66,22 +64,26 @@ public class ExternalJavacManager { public static final String STDOUT_LINE_PREFIX = "JAVAC_PROCESS[STDOUT]"; public static final String STDERR_LINE_PREFIX = "JAVAC_PROCESS[STDERR]"; private static final AttributeKey SESSION_DESCRIPTOR = AttributeKey.valueOf("ExternalJavacServer.JavacProcessDescriptor"); - private final File mySystemRoot; - private final ThreadExecutor myThreadExecutor; - - private ChannelRegistrar myChannelRegistrar; + @NotNull + private final File myWorkingDir; + @NotNull + private final ChannelRegistrar myChannelRegistrar; private final Map myMessageHandlers = new HashMap(); private int myListenPort = DEFAULT_SERVER_PORT; - public ExternalJavacManager(final File systemRoot, @NotNull ThreadExecutor threadExecutor) { - mySystemRoot = systemRoot; - myThreadExecutor = threadExecutor; + public ExternalJavacManager(@NotNull final File workingDir) { + myWorkingDir = workingDir; + myChannelRegistrar = new ChannelRegistrar(); + } + + @NotNull + public File getWorkingDir() { + return myWorkingDir; } 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 @@ -117,7 +119,7 @@ public class ExternalJavacManager { } try { final ExternalJavacProcessHandler processHandler = launchExternalJavacProcess( - uuid, javaHome, heapSize, myListenPort, mySystemRoot, vmOptions, compilingTool + uuid, javaHome, heapSize, myListenPort, myWorkingDir, vmOptions, compilingTool ); processHandler.addProcessListener(new ProcessAdapter() { public void onTextAvailable(ProcessEvent event, Key outputType) { @@ -200,11 +202,14 @@ public class ExternalJavacManager { //appendParam(cmdLine, "-XX:MaxPermSize=150m"); //appendParam(cmdLine, "-XX:ReservedCodeCacheSize=64m"); appendParam(cmdLine, "-Djava.awt.headless=true"); - final int xms = heapSize / 2; - if (xms > 32) { - appendParam(cmdLine, "-Xms" + xms + "m"); + if (heapSize > 0) { + // if the value is zero or negative, use JVM default memory settings + final int xms = heapSize / 2; + if (xms > 32) { + appendParam(cmdLine, "-Xms" + xms + "m"); + } + appendParam(cmdLine, "-Xmx" + heapSize + "m"); } - appendParam(cmdLine, "-Xmx" + heapSize + "m"); // debugging //appendParam(cmdLine, "-XX:+HeapDumpOnOutOfMemoryError"); @@ -268,7 +273,11 @@ public class ExternalJavacManager { builder.directory(workingDir); final Process process = builder.start(); - return new ExternalJavacProcessHandler(process, myThreadExecutor); + return createProcessHandler(process); + } + + protected ExternalJavacProcessHandler createProcessHandler(Process process) { + return new ExternalJavacProcessHandler(process); } private static void appendParam(List cmdLine, String param) { @@ -287,14 +296,11 @@ public class ExternalJavacManager { return sdkHome + "/bin/java"; } - private static class ExternalJavacProcessHandler extends BaseOSProcessHandler { - @NotNull - private final ThreadExecutor myExecutorService; + protected static class ExternalJavacProcessHandler extends BaseOSProcessHandler { private volatile int myExitCode; - ExternalJavacProcessHandler(Process process, @NotNull ThreadExecutor executorService) { + public ExternalJavacProcessHandler(Process process) { super(process, null, null); - myExecutorService = executorService; addProcessListener(new ProcessAdapter() { @Override public void processTerminated(ProcessEvent event) { @@ -303,11 +309,6 @@ public class ExternalJavacManager { }); } - @Override - protected Future executeOnPooledThread(Runnable task) { - return myExecutorService.executeOnPooledThread(task); - } - public int getExitCode() { return myExitCode; } diff --git a/jps/model-api/src/org/jetbrains/jps/service/SharedThreadPool.java b/jps/model-api/src/org/jetbrains/jps/service/SharedThreadPool.java index 429442cdc2cc..bb26dd49b031 100644 --- a/jps/model-api/src/org/jetbrains/jps/service/SharedThreadPool.java +++ b/jps/model-api/src/org/jetbrains/jps/service/SharedThreadPool.java @@ -15,12 +15,16 @@ */ package org.jetbrains.jps.service; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + /** * @author nik */ -public abstract class SharedThreadPool implements ThreadExecutor { +public abstract class SharedThreadPool implements Executor { public static SharedThreadPool getInstance() { return JpsServiceManager.getInstance().getService(SharedThreadPool.class); } + public abstract Future executeOnPooledThread(Runnable action); } diff --git a/jps/model-api/src/org/jetbrains/jps/service/ThreadExecutor.java b/jps/model-api/src/org/jetbrains/jps/service/ThreadExecutor.java deleted file mode 100644 index fba17fa182a1..000000000000 --- a/jps/model-api/src/org/jetbrains/jps/service/ThreadExecutor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.jps.service; - -import java.util.concurrent.Executor; -import java.util.concurrent.Future; - -public interface ThreadExecutor extends Executor { - Future executeOnPooledThread(Runnable action); -}