Running java scratches (IDEA-140228)

This commit is contained in:
Eugene Zhuravlev
2015-09-11 12:05:18 +02:00
parent bfda7d73b5
commit d65d572071
9 changed files with 668 additions and 137 deletions
@@ -17,6 +17,7 @@ package com.intellij.compiler;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.compiler.impl.*;
import com.intellij.compiler.server.BuildManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
@@ -29,20 +30,38 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
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.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiManager;
import com.intellij.util.SmartList;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.net.NetUtils;
import gnu.trove.THashSet;
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.incremental.BinaryContent;
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.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.net.URI;
import java.util.*;
import java.util.concurrent.Semaphore;
@@ -58,6 +77,7 @@ public class CompilerManagerImpl extends CompilerManager {
private final Semaphore myCompilationSemaphore = new Semaphore(1, true);
private final Set<ModuleType> myValidationDisabledModuleTypes = new HashSet<ModuleType>();
private final Set<LocalFileSystem.WatchRequest> myWatchRoots;
private volatile ExternalJavacManager myExternalJavacManager;
public CompilerManagerImpl(final Project project, MessageBus messageBus) {
myProject = project;
@@ -85,6 +105,11 @@ public class CompilerManagerImpl extends CompilerManager {
myWatchRoots = lfs.addRootsToWatch(Collections.singletonList(FileUtil.toCanonicalPath(projectGeneratedSrcRoot.getPath())), true);
Disposer.register(project, new Disposable() {
public void dispose() {
final ExternalJavacManager manager = myExternalJavacManager;
myExternalJavacManager = null;
if (manager != null) {
manager.stop();
}
lfs.removeWatchedRoots(myWatchRoots);
if (ApplicationManager.getApplication().isUnitTestMode()) { // force cleanup for created compiler system directory with generated sources
FileUtil.delete(CompilerPaths.getCompilerSystemDirectory(project));
@@ -310,6 +335,144 @@ public class CompilerManagerImpl extends CompilerManager {
return !myValidationDisabledModuleTypes.contains(ModuleType.get(module));
}
@Override
public Collection<ClassObject> compileJavaCode(List<String> options,
Collection<File> platformCp,
Collection<File> classpath,
Collection<File> sourcePath,
Collection<File> files,
File outputDir) throws IOException, CompilationException {
final Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getBuildProcessRuntimeSdk(myProject);
String javaHome = null;
final Sdk sdk = runtime.getFirst();
final SdkTypeId type = sdk.getSdkType();
if (type instanceof JavaSdkType) {
javaHome = sdk.getHomePath();
}
if (javaHome == null) {
throw new IOException("Was not able to determine JDK for project " + myProject.getName());
}
final OutputCollector outputCollector = new OutputCollector();
DiagnosticCollector diagnostic = new DiagnosticCollector();
final Set<File> sourceRoots = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
if (!sourcePath.isEmpty()) {
for (File file : sourcePath) {
sourceRoots.add(file);
}
}
else {
for (File file : files) {
final File parentFile = file.getParentFile();
if (parentFile != null) {
sourceRoots.add(parentFile);
}
}
}
final Map<File, Set<File>> outs = Collections.singletonMap(outputDir, sourceRoots);
final ExternalJavacManager javacManager = getJavacManager();
boolean compiledOk = javacManager != null && javacManager.forkJavac(
javaHome, -1, Collections.<String>emptyList(), options, platformCp, classpath, sourcePath, files, outs, diagnostic, outputCollector,
new JavacCompilerTool(), CanceledStatus.NULL
);
if (!compiledOk) {
final List<CompilationException.Message> messages = new SmartList<CompilationException.Message>();
for (Diagnostic<? extends JavaFileObject> d : diagnostic.getDiagnostics()) {
final JavaFileObject source = d.getSource();
final URI uri = source != null ? source.toUri() : null;
messages.add(new CompilationException.Message(
kindToCategory(d.getKind()), d.getMessage(Locale.US), uri != null? uri.toURL().toString() : null, (int)d.getLineNumber(), (int)d.getColumnNumber()
));
}
throw new CompilationException("Compilation failed", messages);
}
final List<ClassObject> result = new ArrayList<ClassObject>();
for (OutputFileObject fileObject : outputCollector.getCompiledClasses()) {
final BinaryContent content = fileObject.getContent();
result.add(new CompiledClass(fileObject.getName(), fileObject.getClassName(), content != null ? content.toByteArray() : null));
}
return result;
}
private static CompilerMessageCategory kindToCategory(Diagnostic.Kind kind) {
switch (kind) {
case ERROR: return CompilerMessageCategory.ERROR;
case MANDATORY_WARNING: return CompilerMessageCategory.WARNING;
case WARNING: return CompilerMessageCategory.WARNING;
case NOTE: return CompilerMessageCategory.INFORMATION;
default:
return CompilerMessageCategory.INFORMATION;
}
}
@Nullable
private ExternalJavacManager getJavacManager() throws IOException {
ExternalJavacManager manager = myExternalJavacManager;
if (manager == null) {
synchronized (this) {
manager = myExternalJavacManager;
if (manager == null) {
final File compilerWorkingDir = getJavacCompilerWorkingDir();
if (compilerWorkingDir == null) {
return null; // should not happen for real projects
}
final int listenPort = NetUtils.findAvailableSocketPort();
manager = new ExternalJavacManager(compilerWorkingDir);
manager.start(listenPort);
myExternalJavacManager = manager;
}
}
}
return manager;
}
@Override
@Nullable
public File getJavacCompilerWorkingDir() {
final File projectBuildDir = BuildManager.getInstance().getProjectSystemDirectory(myProject);
if (projectBuildDir == null) {
return null;
}
projectBuildDir.mkdirs();
return projectBuildDir;
}
private static class CompiledClass implements ClassObject {
private final String myPath;
private final String myClassName;
private final byte[] myBytes;
public CompiledClass(String path, String className, byte[] bytes) {
myPath = path;
myClassName = className;
myBytes = bytes;
}
@Override
public String getPath() {
return myPath;
}
@Override
public String getClassName() {
return myClassName;
}
@Nullable
@Override
public byte[] getContent() {
return myBytes;
}
}
private class ListenerNotificator implements CompileStatusNotification {
private final @Nullable CompileStatusNotification myDelegate;
@@ -326,4 +489,42 @@ public class CompilerManagerImpl extends CompilerManager {
}
}
}
private static class DiagnosticCollector implements DiagnosticOutputConsumer {
private final List<Diagnostic<? extends JavaFileObject>> myDiagnostics = new ArrayList<Diagnostic<? extends JavaFileObject>>();
public void outputLineAvailable(String line) {
// for debugging purposes uncomment this line
//System.out.println(line);
}
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;
}
}
}
@@ -0,0 +1,27 @@
/*
* 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 com.intellij.openapi.compiler;
import org.jetbrains.annotations.Nullable;
public interface ClassObject {
String getPath();
String getClassName();
@Nullable
byte[] getContent();
}
@@ -0,0 +1,87 @@
/*
* 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 com.intellij.openapi.compiler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
public class CompilationException extends Exception {
private final Collection<Message> myMessages;
public static class Message {
@NotNull
private final CompilerMessageCategory myCategory;
@NotNull
private final String myMessage;
@Nullable
private final String myUrl;
private final int myLine;
private final int myColumn;
public Message(CompilerMessageCategory category, String message) {
this(category, message, null, -1, -1);
}
public Message(@NotNull CompilerMessageCategory category, @NotNull String message, @Nullable String url, int line, int column) {
myCategory = category;
myMessage = message;
myUrl = url;
myLine = line;
myColumn = column;
}
@NotNull
public CompilerMessageCategory getCategory() {
return myCategory;
}
@NotNull
public String getText() {
return myMessage;
}
@Nullable
public String getUrl() {
return myUrl;
}
public int getLine() {
return myLine;
}
public int getColumn() {
return myColumn;
}
}
public CompilationException(String message) {
this(message, Collections.<Message>emptyList());
}
public CompilationException(String message, Collection<Message> messages) {
super(message);
myMessages = messages;
}
@NotNull
public Collection<Message> getMessages() {
return myMessages;
}
}
@@ -27,6 +27,10 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
@@ -289,4 +293,14 @@ public abstract class CompilerManager {
public abstract void setValidationEnabled(ModuleType moduleType, boolean enabled);
public abstract boolean isValidationEnabled(Module moduleType);
public abstract Collection<ClassObject> compileJavaCode(List<String> options,
Collection<File> platformCp,
Collection<File> classpath,
Collection<File> sourcePath,
Collection<File> files,
File outputDir) throws IOException, CompilationException;
@Nullable
public abstract File getJavacCompilerWorkingDir();
}
@@ -27,6 +27,7 @@ import com.intellij.debugger.impl.ClassLoadingUtils;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.ClassObject;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JdkVersionUtil;
@@ -38,8 +39,6 @@ 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;
@@ -80,7 +79,7 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
ClassLoaderReference classLoader = ClassLoadingUtils.getClassLoader(evaluationContext, process);
String version = ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).version();
Collection<OutputFileObject> classes = compile(JdkVersionUtil.getVersion(version));
Collection<ClassObject> classes = compile(JdkVersionUtil.getVersion(version));
defineClasses(classes, evaluationContext, process, classLoader);
@@ -112,15 +111,15 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
}
}
private ClassType defineClasses(Collection<OutputFileObject> classes,
private ClassType defineClasses(Collection<ClassObject> classes,
EvaluationContext context,
DebugProcess process,
ClassLoaderReference classLoader) throws EvaluateException {
for (OutputFileObject cls : classes) {
if (cls.getName().contains(GEN_CLASS_NAME)) {
final BinaryContent content = cls.getContent();
for (ClassObject cls : classes) {
if (cls.getPath().contains(GEN_CLASS_NAME)) {
final byte[] content = cls.getContent();
if (content != null) {
byte[] bytes = changeSuperToMagicAccessor(content.toByteArray());
final byte[] bytes = changeSuperToMagicAccessor(content);
ClassLoadingUtils.defineClass(cls.getClassName(), bytes, context, process, classLoader);
}
}
@@ -165,6 +164,6 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
///////////////// Compiler stuff
@NotNull
protected abstract Collection<OutputFileObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException;
protected abstract Collection<ClassObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException;
}
@@ -16,39 +16,28 @@
package com.intellij.debugger.ui.impl.watch;
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.compiler.ClassObject;
import com.intellij.openapi.compiler.CompilationException;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.compiler.CompilerMessageCategory;
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.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
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.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.io.File;
import java.io.IOException;
import java.util.*;
@@ -65,23 +54,13 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
@Override
@NotNull
protected Collection<OutputFileObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException {
final Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getBuildProcessRuntimeSdk(myEvaluationContext.getProject());
protected Collection<ClassObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException {
final Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
@Override
public Module compute() {
return ModuleUtilCore.findModuleForPsiElement(myPsiContext);
}
});
String javaHome = null;
final Sdk sdk = runtime.getFirst();
final SdkTypeId type = sdk.getSdkType();
if (type instanceof JavaSdkType) {
javaHome = sdk.getHomePath();
}
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");
@@ -98,6 +77,7 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
}
}
final Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getBuildProcessRuntimeSdk(myEvaluationContext.getProject());
final JavaSdkVersion buildRuntimeVersion = runtime.getSecond();
// if compiler or debuggee version or both are unknown, let source and target be the compiler's defaults
if (buildRuntimeVersion != null && debuggeeVersion != null) {
@@ -109,36 +89,25 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
options.add(sourceOption);
}
final CompilerManager compilerManager = CompilerManager.getInstance(myEvaluationContext.getProject());
File sourceFile = null;
final OutputCollector outputSink = new OutputCollector();
try {
final ExternalJavacManager javacManager = getJavacManager();
if (javacManager == null) {
throw new EvaluateException("Cannot compile java code");
}
sourceFile = generateTempSourceFile(javacManager.getWorkingDir());
sourceFile = generateTempSourceFile(compilerManager.getJavacCompilerWorkingDir());
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()) {
if (d.getKind() == Diagnostic.Kind.ERROR) {
res.append(d.getMessage(Locale.US));
}
}
throw new EvaluateException(res.toString());
}
return compilerManager.compileJavaCode(options, platformClasspath, classpath, sourcePath, sources, srcDir);
}
catch (EvaluateException e) {
throw e;
catch (CompilationException e) {
final StringBuilder res = new StringBuilder("Compilation failed:\n");
for (CompilationException.Message m : e.getMessages()) {
if (m.getCategory() == CompilerMessageCategory.ERROR) {
res.append(m.getText()).append("\n");
}
}
throw new EvaluateException(res.toString());
}
catch (Exception e) {
throw new EvaluateException(e.getMessage());
@@ -148,7 +117,6 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
FileUtil.delete(sourceFile);
}
}
return outputSink.getCompiledClasses();
}
@NotNull
@@ -170,85 +138,8 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
if (fileData.second == null) {
throw new IOException("Class source code not specified");
}
final File file = new File(workingDir, "src/"+fileData.first);
final File file = new File(workingDir, "debugger/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) {
// for debugging purposes uncomment this line
//System.out.println(line);
}
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;
}
}
}
@@ -0,0 +1,170 @@
/*
* 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 com.intellij.execution.impl;
import com.intellij.compiler.options.CompileStepBeforeRun;
import com.intellij.execution.configurations.ModuleBasedConfiguration;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFileManager;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: 08-Sep-15
*/
public class JavaScratchCompilationSupport implements ProjectComponent, CompileTask{
public JavaScratchCompilationSupport(Project project, CompilerManager compileManager) {
compileManager.addAfterTask(this);
}
@Override
public boolean execute(CompileContext context) {
final Project project = context.getProject();
File outputDir = null;
File srcDir = null;
if (context.isRebuild()) {
// perform cleanup
outputDir = JavaScratchRunConfigurationExtension.getScratchOutputDirectory(project);
if (outputDir == null) { // should not happen for normal projects
return true;
}
FileUtil.delete(outputDir);
srcDir = JavaScratchRunConfigurationExtension.getScratchTempDirectory(project);
if (srcDir != null) {
FileUtil.delete(srcDir);
}
}
final RunConfiguration configuration = CompileStepBeforeRun.getRunConfiguration(context);
if (!(configuration instanceof ModuleBasedConfiguration)) {
return true;
}
final String scratchUrl = JavaScratchRunConfigurationExtension.getScratchFileUrl(configuration);
if (scratchUrl == null) {
return true;
}
final Module configModule = ((ModuleBasedConfiguration)configuration).getConfigurationModule().getModule();
if (configModule == null) {
return true; // todo: show error?
}
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(configModule);
final Sdk targetSdk = moduleRootManager.getSdk();
if (targetSdk == null || !(targetSdk.getSdkType() instanceof JavaSdkType)) {
return true; // todo: show error?
}
if (outputDir == null) {
outputDir = JavaScratchRunConfigurationExtension.getScratchOutputDirectory(project);
if (outputDir == null) { // should not happen for normal projects
return true;
}
}
try {
final File scratchFile = new File(VirtualFileManager.extractPath(scratchUrl));
File srcFile = scratchFile;
if (!StringUtil.endsWith(srcFile.getName(), ".java")) {
if (srcDir == null) {
srcDir = JavaScratchRunConfigurationExtension.getScratchTempDirectory(project);
if (srcDir == null) { // should not happen for normal projects
return true;
}
}
srcFile = new File(srcDir, FileUtil.getNameWithoutExtension(scratchFile) + ".java");
FileUtil.copy(scratchFile, srcFile);
}
final Collection<File> files = Collections.singleton(srcFile);
final Set<File> cp = new LinkedHashSet<File>();
for (String s : moduleRootManager.orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList().getPathList()) {
cp.add(new File(s));
}
final List<File> platformCp = new ArrayList<File>();
for (String s : moduleRootManager.orderEntries().compileOnly().sdkOnly().getPathsList().getPathList()) {
platformCp.add(new File(s));
}
final List<String> options = new ArrayList<String>();
final JavaSdkVersion sdkVersion = JavaSdk.getInstance().getVersion(targetSdk);
if (sdkVersion != null) {
final String langLevel = "1." + Integer.valueOf(3 + sdkVersion.getMaxLanguageLevel().ordinal());
options.add("-source");
options.add(langLevel);
options.add("-target");
options.add(langLevel);
}
options.add("-proc:none"); // disable annotation processing
final Collection<ClassObject> result = CompilerManager.getInstance(project).compileJavaCode(
options, platformCp, cp, Collections.<File>emptyList(), files, outputDir
);
for (ClassObject classObject : result) {
final byte[] bytes = classObject.getContent();
if (bytes != null) {
FileUtil.writeToFile(new File(classObject.getPath()), bytes);
}
}
}
catch (CompilationException e) {
for (CompilationException.Message m : e.getMessages()) {
context.addMessage(m.getCategory(), m.getText(), scratchUrl, m.getLine(), m.getColumn());
}
}
catch (IOException e) {
context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), scratchUrl, -1, -1);
}
return true;
}
@Override
public void projectOpened() {
}
@Override
public void projectClosed() {
}
@Override
public void initComponent() {
}
@Override
public void disposeComponent() {
}
@NotNull
@Override
public String getComponentName() {
return "JavaScratchCompilationSupport";
}
}
@@ -0,0 +1,137 @@
/*
* 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 com.intellij.execution.impl;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Location;
import com.intellij.execution.RunConfigurationExtension;
import com.intellij.execution.application.ApplicationConfiguration;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunConfigurationBase;
import com.intellij.execution.configurations.RunnerSettings;
import com.intellij.ide.scratch.ScratchFileType;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
/*
execution-impl -> lang-impl(scratches are here)
compiler-impl -> lang-impl
java-impl ->execution-impl, compiler-impl
*/
public class JavaScratchRunConfigurationExtension extends RunConfigurationExtension{
public static final Key<String> SCRATCH_FILE_URL = Key.create("_scratch_file_path_");
public void cleanUserData(RunConfigurationBase configuration) {
super.cleanUserData(configuration);
configuration.putCopyableUserData(SCRATCH_FILE_URL, null);
}
protected void extendCreatedConfiguration(@NotNull RunConfigurationBase configuration, @NotNull Location location) {
final VirtualFile vFile = location.getVirtualFile();
if (vFile != null && vFile.getFileType() == ScratchFileType.INSTANCE) {
final PsiFile psiFile = location.getPsiElement().getContainingFile();
if (psiFile != null && psiFile.getLanguage() == JavaLanguage.INSTANCE) {
configuration.putCopyableUserData(SCRATCH_FILE_URL, vFile.getUrl());
}
}
}
protected void validateConfiguration(@NotNull RunConfigurationBase configuration, boolean isExecution) throws Exception {
super.validateConfiguration(configuration, isExecution);
}
public <T extends RunConfigurationBase> void updateJavaParameters(T configuration, JavaParameters params, RunnerSettings runnerSettings) throws ExecutionException {
final File scrachesOutput = getScratchOutputDirectory(configuration.getProject());
if (scrachesOutput != null) {
params.getClassPath().add(scrachesOutput);
}
}
@NotNull
protected String getSerializationId() {
return "java-scratch-properties";
}
@Nullable
public static String getScratchFileUrl(RunConfiguration configuration) {
return configuration instanceof RunConfigurationBase? ((RunConfigurationBase)configuration).getCopyableUserData(SCRATCH_FILE_URL) : null;
}
@Nullable
public static File getScratchOutputDirectory(Project project) {
final File root = CompilerManager.getInstance(project).getJavacCompilerWorkingDir();
return root != null? new File(root, "scratches/out") : null;
}
@Nullable
public static File getScratchTempDirectory(Project project) {
final File root = CompilerManager.getInstance(project).getJavacCompilerWorkingDir();
return root != null? new File(root, "scratches/src") : null;
}
protected void readExternal(@NotNull RunConfigurationBase runConfiguration, @NotNull Element element) throws InvalidDataException {
final Element sourceElement = element.getChild("source");
if (sourceElement != null) {
final String scratchUrl = sourceElement.getAttributeValue("url");
if (scratchUrl != null) {
runConfiguration.putCopyableUserData(SCRATCH_FILE_URL, scratchUrl);
}
}
}
protected void writeExternal(@NotNull RunConfigurationBase runConfiguration, @NotNull Element element) throws WriteExternalException {
final String url = getScratchFileUrl(runConfiguration);
if (url == null) {
super.writeExternal(runConfiguration, element);
}
else {
final Element urlElement = new Element("source");
urlElement.setAttribute("url", url);
element.addContent(urlElement);
}
}
@Nullable
protected <P extends RunConfigurationBase> SettingsEditor<P> createEditor(@NotNull P configuration) {
return null;
}
@Nullable
protected String getEditorTitle() {
return null;
}
protected boolean isApplicableFor(@NotNull RunConfigurationBase configuration) {
return configuration instanceof ApplicationConfiguration;
}
}
+5
View File
@@ -66,6 +66,10 @@
<component>
<implementation-class>com.intellij.execution.testDiscovery.TestDiscoveryIndex</implementation-class>
</component>
<component>
<implementation-class>com.intellij.execution.impl.JavaScratchCompilationSupport</implementation-class>
</component>
</project-components>
<extensionPoints>
@@ -1616,6 +1620,7 @@
<applicationService serviceImplementation="com.intellij.execution.JavaRunConfigurationExtensionManager"/>
<runConfigurationExtension implementation="com.intellij.execution.testDiscovery.TestDiscoveryExtension"/>
<runConfigurationExtension implementation="com.intellij.execution.impl.JavaScratchRunConfigurationExtension"/>
<regExpLanguageHost forClass="com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl"
implementationClass="com.intellij.psi.impl.JavaRegExpHost"/>