diff --git a/ide-compiler-runner/ide-compiler-runner.iml b/ide-compiler-runner/ide-compiler-runner.iml index 8aa91671dfd..4707ed53d80 100644 --- a/ide-compiler-runner/ide-compiler-runner.iml +++ b/ide-compiler-runner/ide-compiler-runner.iml @@ -8,6 +8,8 @@ + + diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/CompilerEnvironment.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java similarity index 98% rename from idea/src/org/jetbrains/jet/plugin/compiler/CompilerEnvironment.java rename to ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java index cb6faad0bec..8a059e5cd69 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/CompilerEnvironment.java +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.jet.plugin.compiler; +package org.jetbrains.jet.compiler.runner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java new file mode 100644 index 00000000000..96e73d79ec8 --- /dev/null +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java @@ -0,0 +1,269 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.compiler.runner; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.cli.common.messages.MessageRenderer; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.*; +import java.lang.ref.SoftReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; + +import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION; +import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*; + +public class CompilerRunnerUtil { + private static SoftReference ourClassLoaderRef = new SoftReference(null); + + public static List kompilerClasspath(File kotlinHome, MessageCollector messageCollector) { + File libs = new File(kotlinHome, "lib"); + + if (!libs.exists() || libs.isFile()) { + messageCollector.report(ERROR, "Broken compiler at '" + libs.getAbsolutePath() + "'. Make sure plugin is properly installed", NO_LOCATION); + return Collections.emptyList(); + } + + ArrayList answer = new ArrayList(); + answer.add(new File(libs, "kotlin-compiler.jar")); + return answer; + } + + public static URLClassLoader getOrCreateClassLoader(File kotlinHome, MessageCollector messageCollector) { + URLClassLoader answer = ourClassLoaderRef.get(); + if (answer == null) { + answer = createClassloader(kotlinHome, messageCollector); + ourClassLoaderRef = new SoftReference(answer); + } + return answer; + } + + private static URLClassLoader createClassloader(File kotlinHome, MessageCollector messageCollector) { + List jars = kompilerClasspath(kotlinHome, messageCollector); + URL[] urls = new URL[jars.size()]; + for (int i = 0; i < urls.length; i++) { + try { + urls[i] = jars.get(i).toURI().toURL(); + } + catch (MalformedURLException e) { + throw new RuntimeException(e); // Checked exceptions are great! I love them, and I love brilliant library designers too! + } + } + return new URLClassLoader(urls, null); + } + + static void handleProcessTermination(int exitCode, MessageCollector messageCollector) { + if (exitCode != 0 && exitCode != 1) { + messageCollector.report(ERROR, "Compiler terminated with exit code: " + exitCode, NO_LOCATION); + } + } + + public static void parseCompilerMessagesFromReader(MessageCollector messageCollector, final Reader reader, OutputItemsCollector collector) { + // Sometimes the compiler can't output valid XML + // Example: error in command line arguments passed to the compiler + // having no -tags key (arguments are not parsed), the compiler doesn't know + // if it should put any tags in the output, so it will simply print the usage + // and the SAX parser will break. + // In this case, we want to read everything from this stream + // and report it as an IDE error. + final StringBuilder stringBuilder = new StringBuilder(); + //noinspection IOResourceOpenedButNotSafelyClosed + Reader wrappingReader = new Reader() { + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + int read = reader.read(cbuf, off, len); + stringBuilder.append(cbuf, off, len); + return read; + } + + @Override + public void close() throws IOException { + // Do nothing: + // If the SAX parser sees a syntax error, it throws an exception + // and calls close() on the reader. + // We prevent hte reader from being closed here, and close it later, + // when all the text is read from it + } + }; + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser parser = factory.newSAXParser(); + parser.parse(new InputSource(wrappingReader), new CompilerOutputSAXHandler(messageCollector, collector)); + } + catch (Throwable e) { + + // Load all the text into the stringBuilder + try { + // This will not close the reader (see the wrapper above) + FileUtil.loadTextAndClose(wrappingReader); + } + catch (IOException ioException) { + reportException(messageCollector, ioException); + } + String message = stringBuilder.toString(); + reportException(messageCollector, new IllegalStateException(message, e)); + messageCollector.report(ERROR, message, NO_LOCATION); + } + finally { + try { + reader.close(); + } + catch (IOException e) { + reportException(messageCollector, e); + } + } + } + + public static int getReturnCodeFromObject(Object rc) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { + if ("org.jetbrains.jet.cli.common.ExitCode".equals(rc.getClass().getCanonicalName())) { + return (Integer)rc.getClass().getMethod("getCode").invoke(rc); + } + else { + throw new IllegalStateException("Unexpected return: " + rc); + } + } + + public static Object invokeExecMethod(CompilerEnvironment environment, + PrintStream out, + MessageCollector messageCollector, String[] arguments, String name) throws Exception { + URLClassLoader loader = getOrCreateClassLoader(environment.getKotlinHome(), messageCollector); + Class kompiler = Class.forName(name, true, loader); + Method exec = kompiler.getMethod("exec", PrintStream.class, String[].class); + return exec.invoke(kompiler.newInstance(), out, arguments); + } + + public static void outputCompilerMessagesAndHandleExitCode(@NotNull MessageCollector messageCollector, + @NotNull OutputItemsCollector outputItemsCollector, + @NotNull Function compilerRun) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(outputStream); + + int exitCode = compilerRun.fun(out); + + BufferedReader reader = new BufferedReader(new StringReader(outputStream.toString())); + parseCompilerMessagesFromReader(messageCollector, reader, outputItemsCollector); + handleProcessTermination(exitCode, messageCollector); + } + + private static class CompilerOutputSAXHandler extends DefaultHandler { + private static final Map CATEGORIES = new ContainerUtil.ImmutableMapBuilder() + .put("error", ERROR) + .put("warning", WARNING) + .put("logging", LOGGING) + .put("exception", ERROR) + .put("info", INFO) + .put("messages", INFO) // Root XML element + .build(); + + private final MessageCollector messageCollector; + private final OutputItemsCollector collector; + + private final StringBuilder message = new StringBuilder(); + private Stack tags = new Stack(); + private String path; + private int line; + private int column; + + public CompilerOutputSAXHandler(MessageCollector messageCollector, OutputItemsCollector collector) { + this.messageCollector = messageCollector; + this.collector = collector; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + tags.push(qName); + + message.setLength(0); + + String rawPath = attributes.getValue("path"); + path = rawPath == null ? null : "file://" + rawPath; + line = safeParseInt(attributes.getValue("line"), -1); + column = safeParseInt(attributes.getValue("column"), -1); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (tags.size() == 1) { + // We're directly inside the root tag: + String message = new String(ch, start, length); + if (!message.trim().isEmpty()) { + messageCollector.report(ERROR, "Unhandled compiler output: " + message, NO_LOCATION); + } + } + else { + message.append(ch, start, length); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (tags.size() == 1) { + // We're directly inside the root tag: + return; + } + String qNameLowerCase = qName.toLowerCase(); + CompilerMessageSeverity category = CATEGORIES.get(qNameLowerCase); + if (category == null) { + messageCollector.report(ERROR, "Unknown compiler message tag: " + qName, NO_LOCATION); + category = INFO; + } + String text = message.toString(); + + if (category == LOGGING) { + collector.learn(text); + } + else { + messageCollector.report(category, text, CompilerMessageLocation.create(path, line, column)); + } + tags.pop(); + } + + private static int safeParseInt(@Nullable String value, int defaultValue) { + if (value == null) { + return defaultValue; + } + try { + return Integer.parseInt(value.trim()); + } + catch (NumberFormatException e) { + return defaultValue; + } + } + } + + public static void reportException(@NotNull MessageCollector messageCollector, @NotNull Throwable e) { + messageCollector.report(EXCEPTION, MessageRenderer.PLAIN.renderException(e), NO_LOCATION); + } +} diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java new file mode 100644 index 00000000000..ab0212ce7f0 --- /dev/null +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java @@ -0,0 +1,160 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.compiler.runner; + +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.configurations.SimpleJavaParameters; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.projectRoots.JavaSdkType; +import com.intellij.openapi.projectRoots.JdkUtil; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SimpleJavaSdkType; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.Function; +import com.intellij.util.SystemProperties; +import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.jet.cli.common.messages.MessageCollector; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.Arrays; + +public class KotlinCompilerRunner { + public static void runCompiler( + MessageCollector messageCollector, + CompilerEnvironment environment, + File scriptFile, + OutputItemsCollector collector, + boolean runOutOfProcess + ) { + if (runOutOfProcess) { + runOutOfProcess(messageCollector, collector, environment, scriptFile); + } + else { + runInProcess(messageCollector, collector, environment, scriptFile); + } + } + + private static void runInProcess(final MessageCollector messageCollector, + OutputItemsCollector collector, + final CompilerEnvironment environment, + final File scriptFile) { + CompilerRunnerUtil.outputCompilerMessagesAndHandleExitCode(messageCollector, collector, new Function() { + @Override + public Integer fun(PrintStream stream) { + return execInProcess(environment, scriptFile, stream, messageCollector); + } + }); + } + + private static int execInProcess(CompilerEnvironment environment, File scriptFile, PrintStream out, MessageCollector messageCollector) { + try { + String compilerClassName = "org.jetbrains.jet.cli.jvm.K2JVMCompiler"; + String[] arguments = commandLineArguments(environment.getOutput(), scriptFile); + messageCollector.report(CompilerMessageSeverity.INFO, + "Using kotlinHome=" + environment.getKotlinHome(), + CompilerMessageLocation.NO_LOCATION); + messageCollector.report(CompilerMessageSeverity.INFO, + "Invoking in-process compiler " + compilerClassName + " with arguments " + Arrays.asList(arguments), + CompilerMessageLocation.NO_LOCATION); + Object rc = CompilerRunnerUtil.invokeExecMethod(environment, out, messageCollector, arguments, compilerClassName); + // exec() returns a K2JVMCompiler.ExitCode object, that class is not accessible here, + // so we take it's contents through reflection + return CompilerRunnerUtil.getReturnCodeFromObject(rc); + } + catch (Throwable e) { + CompilerRunnerUtil.reportException(messageCollector, e); + return -1; + } + } + + private static String[] commandLineArguments(File outputDir, File scriptFile) { + return new String[]{ + "-module", scriptFile.getAbsolutePath(), + "-output", outputDir.getPath(), + "-tags", "-verbose", "-version", + "-noStdlib", "-noJdkAnnotations", "-noJdk"}; + } + + private static void runOutOfProcess( + final MessageCollector messageCollector, + final OutputItemsCollector itemCollector, + CompilerEnvironment environment, + File scriptFile + ) { + final SimpleJavaParameters params = new SimpleJavaParameters(); + params.setJdk(new SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome())); + params.setMainClass("org.jetbrains.jet.cli.jvm.K2JVMCompiler"); + + for (String arg : commandLineArguments(environment.getOutput(), scriptFile)) { + params.getProgramParametersList().add(arg); + } + + for (File jar : CompilerRunnerUtil.kompilerClasspath(environment.getKotlinHome(), messageCollector)) { + params.getClassPath().add(jar); + } + + params.getVMParametersList().addParametersString("-Djava.awt.headless=true -Xmx512m"); + // params.getVMParametersList().addParametersString("-agentlib:yjpagent=sampling"); + + Sdk sdk = params.getJdk(); + + final GeneralCommandLine commandLine = JdkUtil.setupJVMCommandLine( + ((JavaSdkType) sdk.getSdkType()).getVMExecutablePath(sdk), params, false); + + messageCollector.report(CompilerMessageSeverity.INFO, + "Invoking out-of-process compiler with arguments: " + commandLine, + CompilerMessageLocation.NO_LOCATION); + + try { + final Process process = commandLine.createProcess(); + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + CompilerRunnerUtil + .parseCompilerMessagesFromReader(messageCollector, new InputStreamReader(process.getInputStream()), itemCollector); + } + }); + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + FileUtil.loadBytes(process.getErrorStream()); + } + catch (IOException e) { + // Don't care + } + } + }); + + int exitCode = process.waitFor(); + CompilerRunnerUtil.handleProcessTermination(exitCode, messageCollector); + } + catch (Exception e) { + messageCollector.report(CompilerMessageSeverity.ERROR, + "[Internal Error] " + e.getLocalizedMessage(), + CompilerMessageLocation.NO_LOCATION); + return; + } + } + +} diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/OutputItemsCollector.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/OutputItemsCollector.java similarity index 94% rename from idea/src/org/jetbrains/jet/plugin/compiler/OutputItemsCollector.java rename to ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/OutputItemsCollector.java index 49bf2881b77..1212759691d 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/OutputItemsCollector.java +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/OutputItemsCollector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.jet.plugin.compiler; +package org.jetbrains.jet.compiler.runner; /** * @author abreslav diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/CompilerUtils.java b/idea/src/org/jetbrains/jet/plugin/compiler/CompilerUtils.java index c31a99c6327..e3b2f5bb3c0 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/CompilerUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/CompilerUtils.java @@ -16,166 +16,28 @@ package org.jetbrains.jet.plugin.compiler; -import com.google.common.collect.ImmutableMap; import com.intellij.compiler.impl.javaCompiler.OutputItemImpl; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.compiler.TranslatingCompiler; import com.intellij.openapi.module.Module; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import jet.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; -import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; -import org.jetbrains.jet.cli.common.messages.MessageCollector; -import org.jetbrains.jet.cli.common.messages.MessageRenderer; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; +import org.jetbrains.jet.compiler.runner.CompilerEnvironment; +import org.jetbrains.jet.compiler.runner.OutputItemsCollector; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import java.io.*; -import java.lang.ref.SoftReference; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.*; - -import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION; -import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*; +import java.io.File; +import java.util.ArrayList; +import java.util.List; /** * @author Pavel Talanov */ public final class CompilerUtils { - private static SoftReference ourClassLoaderRef = new SoftReference(null); - private CompilerUtils() { } - public static List kompilerClasspath(File kotlinHome, MessageCollector messageCollector) { - File libs = new File(kotlinHome, "lib"); - - if (!libs.exists() || libs.isFile()) { - messageCollector.report(ERROR, "Broken compiler at '" + libs.getAbsolutePath() + "'. Make sure plugin is properly installed", NO_LOCATION); - return Collections.emptyList(); - } - - ArrayList answer = new ArrayList(); - answer.add(new File(libs, "kotlin-compiler.jar")); - return answer; - } - - public static URLClassLoader getOrCreateClassLoader(File kotlinHome, MessageCollector messageCollector) { - URLClassLoader answer = ourClassLoaderRef.get(); - if (answer == null) { - answer = createClassloader(kotlinHome, messageCollector); - ourClassLoaderRef = new SoftReference(answer); - } - return answer; - } - - private static URLClassLoader createClassloader(File kotlinHome, MessageCollector messageCollector) { - List jars = kompilerClasspath(kotlinHome, messageCollector); - URL[] urls = new URL[jars.size()]; - for (int i = 0; i < urls.length; i++) { - try { - urls[i] = jars.get(i).toURI().toURL(); - } - catch (MalformedURLException e) { - throw new RuntimeException(e); // Checked exceptions are great! I love them, and I love brilliant library designers too! - } - } - return new URLClassLoader(urls, null); - } - - static void handleProcessTermination(int exitCode, MessageCollector messageCollector) { - if (exitCode != 0 && exitCode != 1) { - messageCollector.report(ERROR, "Compiler terminated with exit code: " + exitCode, NO_LOCATION); - } - } - - public static void parseCompilerMessagesFromReader(MessageCollector messageCollector, final Reader reader, OutputItemsCollector collector) { - // Sometimes the compiler can't output valid XML - // Example: error in command line arguments passed to the compiler - // having no -tags key (arguments are not parsed), the compiler doesn't know - // if it should put any tags in the output, so it will simply print the usage - // and the SAX parser will break. - // In this case, we want to read everything from this stream - // and report it as an IDE error. - final StringBuilder stringBuilder = new StringBuilder(); - //noinspection IOResourceOpenedButNotSafelyClosed - Reader wrappingReader = new Reader() { - - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - int read = reader.read(cbuf, off, len); - stringBuilder.append(cbuf, off, len); - return read; - } - - @Override - public void close() throws IOException { - // Do nothing: - // If the SAX parser sees a syntax error, it throws an exception - // and calls close() on the reader. - // We prevent hte reader from being closed here, and close it later, - // when all the text is read from it - } - }; - try { - SAXParserFactory factory = SAXParserFactory.newInstance(); - SAXParser parser = factory.newSAXParser(); - parser.parse(new InputSource(wrappingReader), new CompilerOutputSAXHandler(messageCollector, collector)); - } - catch (Throwable e) { - - // Load all the text into the stringBuilder - try { - // This will not close the reader (see the wrapper above) - FileUtil.loadTextAndClose(wrappingReader); - } - catch (IOException ioException) { - reportException(messageCollector, ioException); - } - String message = stringBuilder.toString(); - reportException(messageCollector, new IllegalStateException(message, e)); - messageCollector.report(ERROR, message, NO_LOCATION); - } - finally { - try { - reader.close(); - } - catch (IOException e) { - reportException(messageCollector, e); - } - } - } - - public static int getReturnCodeFromObject(Object rc) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { - if ("org.jetbrains.jet.cli.common.ExitCode".equals(rc.getClass().getCanonicalName())) { - return (Integer)rc.getClass().getMethod("getCode").invoke(rc); - } - else { - throw new IllegalStateException("Unexpected return: " + rc); - } - } - - public static Object invokeExecMethod(CompilerEnvironment environment, - PrintStream out, - MessageCollector messageCollector, String[] arguments, String name) throws Exception { - URLClassLoader loader = getOrCreateClassLoader(environment.getKotlinHome(), messageCollector); - Class kompiler = Class.forName(name, true, loader); - Method exec = kompiler.getMethod("exec", PrintStream.class, String[].class); - return exec.invoke(kompiler.newInstance(), out, arguments); - } - @NotNull public static CompilerEnvironment getEnvironmentFor(@NotNull CompileContext compileContext, @NotNull Module module, boolean tests) { VirtualFile mainOutput = compileContext.getModuleOutputDirectory(module); @@ -189,92 +51,6 @@ public final class CompilerUtils { return new File(file.getPath()); } - private static class CompilerOutputSAXHandler extends DefaultHandler { - private static final Map CATEGORIES = ImmutableMap.builder() - .put("error", ERROR) - .put("warning", WARNING) - .put("logging", LOGGING) - .put("exception", ERROR) - .put("info", INFO) - .put("messages", INFO) // Root XML element - .build(); - - private final MessageCollector messageCollector; - private final OutputItemsCollector collector; - - private final StringBuilder message = new StringBuilder(); - private Stack tags = new Stack(); - private String path; - private int line; - private int column; - - public CompilerOutputSAXHandler(MessageCollector messageCollector, OutputItemsCollector collector) { - this.messageCollector = messageCollector; - this.collector = collector; - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - tags.push(qName); - - message.setLength(0); - - String rawPath = attributes.getValue("path"); - path = rawPath == null ? null : "file://" + rawPath; - line = safeParseInt(attributes.getValue("line"), -1); - column = safeParseInt(attributes.getValue("column"), -1); - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - if (tags.size() == 1) { - // We're directly inside the root tag: - String message = new String(ch, start, length); - if (!message.trim().isEmpty()) { - messageCollector.report(ERROR, "Unhandled compiler output: " + message, NO_LOCATION); - } - } - else { - message.append(ch, start, length); - } - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (tags.size() == 1) { - // We're directly inside the root tag: - return; - } - String qNameLowerCase = qName.toLowerCase(); - CompilerMessageSeverity category = CATEGORIES.get(qNameLowerCase); - if (category == null) { - messageCollector.report(ERROR, "Unknown compiler message tag: " + qName, NO_LOCATION); - category = INFO; - } - String text = message.toString(); - - if (category == LOGGING) { - collector.learn(text); - } - else { - messageCollector.report(category, text, CompilerMessageLocation.create(path, line, column)); - } - tags.pop(); - } - - private static int safeParseInt(@Nullable String value, int defaultValue) { - if (value == null) { - return defaultValue; - } - try { - return Integer.parseInt(value.trim()); - } - catch (NumberFormatException e) { - return defaultValue; - } - } - } - public static class OutputItemsCollectorImpl implements OutputItemsCollector { private static final String FOR_SOURCE_PREFIX = "For source: "; private static final String EMITTING_PREFIX = "Emitting: "; @@ -313,22 +89,5 @@ public final class CompilerUtils { return sources; } } - - public static void outputCompilerMessagesAndHandleExitCode(@NotNull MessageCollector messageCollector, - @NotNull OutputItemsCollector outputItemsCollector, - @NotNull Function1 compilerRun) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PrintStream out = new PrintStream(outputStream); - - int exitCode = compilerRun.invoke(out); - - BufferedReader reader = new BufferedReader(new StringReader(outputStream.toString())); - parseCompilerMessagesFromReader(messageCollector, reader, outputItemsCollector); - handleProcessTermination(exitCode, messageCollector); - } - - static void reportException(@NotNull MessageCollector messageCollector, @NotNull Throwable e) { - messageCollector.report(EXCEPTION, MessageRenderer.PLAIN.renderException(e), NO_LOCATION); - } } diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java index cc410c7fdb6..4cebbe4adbb 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java @@ -17,40 +17,29 @@ package org.jetbrains.jet.plugin.compiler; import com.intellij.compiler.impl.javaCompiler.ModuleChunk; -import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.execution.configurations.SimpleJavaParameters; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.compiler.CompileScope; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.compiler.TranslatingCompiler; import com.intellij.openapi.compiler.ex.CompileContextEx; import com.intellij.openapi.module.Module; -import com.intellij.openapi.projectRoots.JavaSdkType; -import com.intellij.openapi.projectRoots.JdkUtil; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.projectRoots.SimpleJavaSdkType; import com.intellij.openapi.roots.AnnotationOrderRootType; import com.intellij.openapi.roots.OrderEnumerator; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Chunk; import com.intellij.util.Function; -import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; -import jet.Function1; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; -import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.compiler.runner.CompilerEnvironment; +import org.jetbrains.jet.compiler.runner.KotlinCompilerRunner; import org.jetbrains.jet.compiler.runner.KotlinModuleScriptGenerator; import org.jetbrains.jet.plugin.JetFileType; import org.jetbrains.jet.plugin.project.JsModuleDetector; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; import java.util.*; /** @@ -142,22 +131,7 @@ public class JetCompiler implements TranslatingCompiler { File scriptFile, CompilerUtils.OutputItemsCollectorImpl collector ) { - runCompiler(messageCollector, environment, scriptFile, collector, RUN_OUT_OF_PROCESS); - } - - private static void runCompiler( - MessageCollector messageCollector, - CompilerEnvironment environment, - File scriptFile, - CompilerUtils.OutputItemsCollectorImpl collector, - boolean runOutOfProcess - ) { - if (runOutOfProcess) { - runOutOfProcess(messageCollector, collector, environment, scriptFile); - } - else { - runInProcess(messageCollector, collector, environment, scriptFile); - } + KotlinCompilerRunner.runCompiler(messageCollector, environment, scriptFile, collector, RUN_OUT_OF_PROCESS); } public static File tryToWriteScriptFile( @@ -223,111 +197,6 @@ public class JetCompiler implements TranslatingCompiler { }; } - private static void runInProcess(final MessageCollector messageCollector, - OutputItemsCollector collector, - final CompilerEnvironment environment, - final File scriptFile) { - CompilerUtils.outputCompilerMessagesAndHandleExitCode(messageCollector, collector, new Function1() { - @Override - public Integer invoke(PrintStream stream) { - return execInProcess(environment, scriptFile, stream, messageCollector); - } - }); - } - - private static int execInProcess(CompilerEnvironment environment, File scriptFile, PrintStream out, MessageCollector messageCollector) { - try { - String compilerClassName = "org.jetbrains.jet.cli.jvm.K2JVMCompiler"; - String[] arguments = commandLineArguments(environment.getOutput(), scriptFile); - messageCollector.report(CompilerMessageSeverity.INFO, - "Using kotlinHome=" + environment.getKotlinHome(), - CompilerMessageLocation.NO_LOCATION); - messageCollector.report(CompilerMessageSeverity.INFO, - "Invoking in-process compiler " + compilerClassName + " with arguments " + Arrays.asList(arguments), - CompilerMessageLocation.NO_LOCATION); - Object rc = CompilerUtils.invokeExecMethod(environment, out, messageCollector, arguments, compilerClassName); - // exec() returns a K2JVMCompiler.ExitCode object, that class is not accessible here, - // so we take it's contents through reflection - return CompilerUtils.getReturnCodeFromObject(rc); - } - catch (Throwable e) { - CompilerUtils.reportException(messageCollector, e); - return -1; - } - } - - private static String[] commandLineArguments(File outputDir, File scriptFile) { - return new String[]{ - "-module", scriptFile.getAbsolutePath(), - "-output", outputDir.getPath(), - "-tags", "-verbose", "-version", - "-noStdlib", "-noJdkAnnotations", "-noJdk"}; - } - - private static void runOutOfProcess( - final MessageCollector messageCollector, - final OutputItemsCollector itemCollector, - CompilerEnvironment environment, - File scriptFile - ) { - final SimpleJavaParameters params = new SimpleJavaParameters(); - params.setJdk(new SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome())); - params.setMainClass("org.jetbrains.jet.cli.jvm.K2JVMCompiler"); - - for (String arg : commandLineArguments(environment.getOutput(), scriptFile)) { - params.getProgramParametersList().add(arg); - } - - for (File jar : CompilerUtils.kompilerClasspath(environment.getKotlinHome(), messageCollector)) { - params.getClassPath().add(jar); - } - - params.getVMParametersList().addParametersString("-Djava.awt.headless=true -Xmx512m"); - // params.getVMParametersList().addParametersString("-agentlib:yjpagent=sampling"); - - Sdk sdk = params.getJdk(); - - final GeneralCommandLine commandLine = JdkUtil.setupJVMCommandLine( - ((JavaSdkType)sdk.getSdkType()).getVMExecutablePath(sdk), params, false); - - messageCollector.report(CompilerMessageSeverity.INFO, - "Invoking out-of-process compiler with arguments: " + commandLine, - CompilerMessageLocation.NO_LOCATION); - - try { - final Process process = commandLine.createProcess(); - - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - CompilerUtils - .parseCompilerMessagesFromReader(messageCollector, new InputStreamReader(process.getInputStream()), itemCollector); - } - }); - - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - try { - FileUtil.loadBytes(process.getErrorStream()); - } - catch (IOException e) { - // Don't care - } - } - }); - - int exitCode = process.waitFor(); - CompilerUtils.handleProcessTermination(exitCode, messageCollector); - } - catch (Exception e) { - messageCollector.report(CompilerMessageSeverity.ERROR, - "[Internal Error] " + e.getLocalizedMessage(), - CompilerMessageLocation.NO_LOCATION); - return; - } - } - private static List getAnnotationRootPaths(ModuleChunk chunk) { List annotationPaths = ContainerUtil.newArrayList(); for (Module module : chunk.getModules()) { diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java index 0f098ba6b3f..2e58c4ad048 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java @@ -30,14 +30,16 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Chunk; +import com.intellij.util.Function; import com.intellij.util.StringBuilderSpinAllocator; import gnu.trove.THashSet; -import jet.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.compiler.runner.CompilerEnvironment; +import org.jetbrains.jet.compiler.runner.CompilerRunnerUtil; import org.jetbrains.jet.plugin.JetFileType; import org.jetbrains.jet.plugin.project.JsModuleDetector; @@ -47,8 +49,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -import static org.jetbrains.jet.plugin.compiler.CompilerUtils.invokeExecMethod; -import static org.jetbrains.jet.plugin.compiler.CompilerUtils.outputCompilerMessagesAndHandleExitCode; +import static org.jetbrains.jet.compiler.runner.CompilerRunnerUtil.invokeExecMethod; +import static org.jetbrains.jet.compiler.runner.CompilerRunnerUtil.outputCompilerMessagesAndHandleExitCode; /** * @author Pavel Talanov @@ -91,9 +93,9 @@ public final class K2JSCompiler implements TranslatingCompiler { private static void doCompile(@NotNull final MessageCollector messageCollector, @NotNull OutputSink sink, @NotNull final Module module, @NotNull final CompilerEnvironment environment) { CompilerUtils.OutputItemsCollectorImpl collector = new CompilerUtils.OutputItemsCollectorImpl(environment.getOutput().getPath()); - outputCompilerMessagesAndHandleExitCode(messageCollector, collector, new Function1() { + outputCompilerMessagesAndHandleExitCode(messageCollector, collector, new Function() { @Override - public Integer invoke(PrintStream stream) { + public Integer fun(PrintStream stream) { return execInProcess(messageCollector, environment, stream, module); } }); @@ -136,7 +138,7 @@ public final class K2JSCompiler implements TranslatingCompiler { assert virtualFile != null : "Virtual file not found for module output: " + outDir; virtualFile.refresh(false, true); } - return CompilerUtils.getReturnCodeFromObject(rc); + return CompilerRunnerUtil.getReturnCodeFromObject(rc); } @NotNull