mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
debugger to perform compilations in external process using target jdk
This commit is contained in:
@@ -111,7 +111,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
|
||||
private Map<Module, ProcessorConfigProfile> 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<String, String> myModuleBytecodeTarget = new HashMap<String, String>();
|
||||
|
||||
public CompilerConfigurationImpl(Project project) {
|
||||
|
||||
@@ -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<Sdk, JavaSdkVersion> getBuildProcessRuntimeSdk(Project project) {
|
||||
Sdk projectJdk = null;
|
||||
int sdkMinorVersion = 0;
|
||||
JavaSdkVersion sdkVersion = null;
|
||||
|
||||
final Set<Sdk> candidates = new HashSet<Sdk>();
|
||||
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<Pair<RequestFuture<PreloadedProcessMessageHandler>, 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<Sdk> candidates = new HashSet<Sdk>();
|
||||
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<Sdk, JavaSdkVersion> 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())) {
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
<orderEntry type="module" module-name="xdebugger-impl" />
|
||||
<orderEntry type="module" module-name="lang-api" />
|
||||
<orderEntry type="module" module-name="compiler-openapi" />
|
||||
<orderEntry type="module" module-name="compiler-impl" />
|
||||
<orderEntry type="module" module-name="java-runtime" />
|
||||
<orderEntry type="module" module-name="jsp-openapi" />
|
||||
<orderEntry type="module" module-name="java-impl" />
|
||||
<orderEntry type="module" module-name="platform-impl" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="diff-api" />
|
||||
<orderEntry type="module" module-name="jps-builders" />
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
+10
-61
@@ -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<OutputFileObject> classes = compile(sdkVersion != null ? sdkVersion.getDescription() : null);
|
||||
Collection<OutputFileObject> 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<OutputFileObject> 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<StandardJavaFileManager> {
|
||||
protected final Collection<OutputFileObject> classes = new ArrayList<OutputFileObject>();
|
||||
|
||||
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<OutputFileObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException;
|
||||
|
||||
}
|
||||
|
||||
+206
-45
@@ -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<OutputFileObject> 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<JavaFileObject> diagnostic = new DiagnosticCollector<JavaFileObject>();
|
||||
Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
|
||||
protected Collection<OutputFileObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException {
|
||||
final Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getBuildProcessRuntimeSdk(myEvaluationContext.getProject());
|
||||
final Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
|
||||
@Override
|
||||
public Module compute() {
|
||||
return ModuleUtilCore.findModuleForPsiElement(myPsiContext);
|
||||
}
|
||||
});
|
||||
List<String> options = new ArrayList<String>();
|
||||
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<String> options = new ArrayList<String>();
|
||||
options.add("-proc:none"); // for our purposes annotation processing is not needed
|
||||
options.add("-encoding");
|
||||
options.add("UTF-8");
|
||||
final List<File> platformClasspath = new ArrayList<File>();
|
||||
final List<File> classpath = new ArrayList<File>();
|
||||
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<LanguageLevel>() {
|
||||
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<File, Set<File>> output = Collections.singletonMap(srcDir, Collections.singleton(srcDir));
|
||||
DiagnosticCollector diagnostic = new DiagnosticCollector();
|
||||
final List<String> vmOptions = Collections.emptyList();
|
||||
final List<File> sourcePath = Collections.emptyList();
|
||||
final Set<File> 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<? extends JavaFileObject> 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<String>() {
|
||||
@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<String>() {
|
||||
private File generateTempSourceFile(File workingDir) throws IOException {
|
||||
final Pair<String, String> fileData = ApplicationManager.getApplication().runReadAction(new Computable<Pair<String, String>>() {
|
||||
@Override
|
||||
public String compute() {
|
||||
return FileUtil.getNameWithoutExtension(myData.getGeneratedInnerClass().getContainingFile().getName());
|
||||
public Pair<String, String> 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<ExternalJavacManager> 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<Diagnostic<? extends JavaFileObject>> myDiagnostics = new ArrayList<Diagnostic<? extends JavaFileObject>>();
|
||||
public void outputLineAvailable(String line) {
|
||||
// todo: do we need these messages?
|
||||
}
|
||||
|
||||
public void registerImports(String className, Collection<String> imports, Collection<String> staticImports) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void javaFileLoaded(File file) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
myDiagnostics.add(diagnostic);
|
||||
}
|
||||
|
||||
public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() {
|
||||
return myDiagnostics;
|
||||
}
|
||||
}
|
||||
|
||||
private static class OutputCollector implements OutputFileConsumer {
|
||||
private List<OutputFileObject> myClasses = new ArrayList<OutputFileObject>();
|
||||
|
||||
public void save(@NotNull OutputFileObject fileObject) {
|
||||
myClasses.add(fileObject);
|
||||
}
|
||||
|
||||
public List<OutputFileObject> getCompiledClasses() {
|
||||
return myClasses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<JavacProcessDescriptor> 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<UUID, JavacProcessDescriptor> myMessageHandlers = new HashMap<UUID, JavacProcessDescriptor>();
|
||||
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<String> 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user