split in-process and forked groovyc running into different classes

This commit is contained in:
peter
2015-02-18 21:48:30 +01:00
parent 63c8f010ca
commit a91b27c191
6 changed files with 213 additions and 115 deletions
@@ -0,0 +1,122 @@
/*
* 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.incremental.groovy;
import com.intellij.execution.process.BaseOSProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.util.Key;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.lang.UrlClassLoader;
import gnu.trove.THashMap;
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.incremental.ExternalProcessUtil;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.service.SharedThreadPool;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
/**
* @author peter
*/
class ForkedGroovyc implements GroovycFlavor {
private final boolean myOptimizeClassLoading;
private final ModuleChunk myChunk;
ForkedGroovyc(boolean optimizeClassLoading, ModuleChunk chunk) {
myOptimizeClassLoading = optimizeClassLoading;
myChunk = chunk;
}
@Override
public void runGroovyc(Collection<String> compilationClassPath,
boolean forStubs,
JpsGroovySettings settings,
File tempFile,
final GroovycOutputParser parser)
throws Exception {
List<String> classpath = new ArrayList<String>();
if (myOptimizeClassLoading) {
classpath.add(GroovyBuilder.getGroovyRtRoot().getPath());
classpath.add(ClasspathBootstrap.getResourcePath(Function.class));
classpath.add(ClasspathBootstrap.getResourcePath(UrlClassLoader.class));
classpath.add(ClasspathBootstrap.getResourceFile(THashMap.class).getPath());
} else {
classpath.addAll(compilationClassPath);
}
List<String> vmParams = ContainerUtilRt.newArrayList();
vmParams.add("-Xmx" + settings.heapSize + "m");
vmParams.add("-Dfile.encoding=" + System.getProperty("file.encoding"));
//vmParams.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");
String grapeRoot = System.getProperty(GroovycOutputParser.GRAPE_ROOT);
if (grapeRoot != null) {
vmParams.add("-D" + GroovycOutputParser.GRAPE_ROOT + "=" + grapeRoot);
}
final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
getJavaExecutable(myChunk),
"org.jetbrains.groovy.compiler.rt.GroovycRunner",
Collections.<String>emptyList(), classpath,
vmParams,
getProgramParams(tempFile, settings, forStubs)
);
final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
ProcessHandler handler = new BaseOSProcessHandler(process, null, null) {
@Override
protected Future<?> executeOnPooledThread(Runnable task) {
return SharedThreadPool.getInstance().executeOnPooledThread(task);
}
@Override
public void notifyTextAvailable(String text, Key outputType) {
parser.notifyTextAvailable(text, outputType);
}
};
handler.startNotify();
handler.waitFor();
parser.notifyFinished(process.exitValue());
}
private List<String> getProgramParams(File tempFile, JpsGroovySettings settings, boolean forStubs) {
List<String> programParams = ContainerUtilRt.newArrayList(myOptimizeClassLoading ? GroovyRtConstants.OPTIMIZE : "do_not_optimize",
forStubs ? "stubs" : "groovyc",
tempFile.getPath());
if (settings.invokeDynamic) {
programParams.add("--indy");
}
return programParams;
}
private static String getJavaExecutable(ModuleChunk chunk) {
JpsSdk<?> sdk = GroovyBuilder.getJdk(chunk);
return sdk != null ? JpsJavaSdkType.getJavaExecutable(sdk) : SystemProperties.getJavaHome() + "/bin/java";
}
}
@@ -16,23 +16,15 @@
package org.jetbrains.jps.incremental.groovy;
import com.intellij.execution.process.BaseOSProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.lang.UrlClassLoader;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.builders.BuildRootIndex;
@@ -50,7 +42,6 @@ import org.jetbrains.jps.incremental.java.ClassPostProcessor;
import org.jetbrains.jps.incremental.java.JavaBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.javac.OutputFileObject;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
@@ -58,14 +49,13 @@ import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.service.JpsServiceManager;
import org.jetbrains.jps.service.SharedThreadPool;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Future;
/**
* @author Eugene Zhuravlev
@@ -79,7 +69,6 @@ public class GroovyBuilder extends ModuleLevelBuilder {
private static final Key<Boolean> FILES_MARKED_DIRTY_FOR_NEXT_ROUND = Key.create("SRC_MARKED_DIRTY");
private static final String GROOVY_EXTENSION = "groovy";
private static final String GPP_EXTENSION = "gpp";
private static final Object ourInProcessGroovycLock = new Object();
private final boolean myForStubs;
private final String myBuilderName;
@@ -94,7 +83,7 @@ public class GroovyBuilder extends ModuleLevelBuilder {
}
public ModuleLevelBuilder.ExitCode build(final CompileContext context,
ModuleChunk chunk,
final ModuleChunk chunk,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
OutputConsumer outputConsumer) throws ProjectBuildException {
if (GreclipseBuilder.useGreclipse(context)) return ExitCode.NOTHING_DONE;
@@ -147,7 +136,12 @@ public class GroovyBuilder extends ModuleLevelBuilder {
compilerOutput, toCompilePaths, finalOutputs.values(), class2Src, encoding, patchers,
optimizeClassLoading ? StringUtil.join(classpath, File.pathSeparator) : ""
);
final GroovycOutputParser parser = runGroovyc(context, chunk, tempFile, settings, classpath, optimizeClassLoading, inProcess, finalOutputs.values());
GroovycFlavor groovyc =
inProcess ? new InProcessGroovyc(finalOutputs.values()) : new ForkedGroovyc(optimizeClassLoading, chunk);
GroovycOutputParser parser = new GroovycOutputParser(chunk, context);
groovyc.runGroovyc(classpath, myForStubs, settings, tempFile, parser);
Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>>
compiled = processCompiledFiles(context, chunk, generationOutputs, compilerOutput, parser.getSuccessfullyCompiled());
@@ -198,87 +192,6 @@ public class GroovyBuilder extends ModuleLevelBuilder {
return toCompilePaths;
}
private GroovycOutputParser runGroovyc(final CompileContext context,
final ModuleChunk chunk,
File tempFile,
final JpsGroovySettings settings,
Collection<String> compilationClassPath,
boolean optimizeClassLoading, boolean inProcess, Collection<String> outputs) throws IOException {
List<String> programParams = ContainerUtilRt.newArrayList(optimizeClassLoading ? GroovyRtConstants.OPTIMIZE : "do_not_optimize",
myForStubs ? "stubs" : "groovyc",
tempFile.getPath());
if (settings.invokeDynamic) {
programParams.add("--indy");
}
final GroovycOutputParser parser = new GroovycOutputParser() {
@Override
protected void updateStatus(@NotNull String status) {
context.processMessage(new ProgressMessage(status + " [" + chunk.getPresentableShortName() + "]"));
}
};
if (inProcess) {
synchronized (ourInProcessGroovycLock) {
InProcessGroovyc.runGroovycInThisProcess(compilationClassPath, outputs, programParams, parser);
}
} else {
forkGroovycProcess(chunk, settings, compilationClassPath, optimizeClassLoading, programParams, parser);
}
return parser;
}
private static void forkGroovycProcess(ModuleChunk chunk,
JpsGroovySettings settings,
Collection<String> compilationClassPath,
boolean optimizeClassLoading, List<String> programParams, final GroovycOutputParser parser)
throws IOException {
List<String> classpath = new ArrayList<String>();
if (optimizeClassLoading) {
classpath.add(getGroovyRtRoot().getPath());
classpath.add(ClasspathBootstrap.getResourcePath(Function.class));
classpath.add(ClasspathBootstrap.getResourcePath(UrlClassLoader.class));
classpath.add(ClasspathBootstrap.getResourceFile(THashMap.class).getPath());
} else {
classpath.addAll(compilationClassPath);
}
List<String> vmParams = ContainerUtilRt.newArrayList();
vmParams.add("-Xmx" + settings.heapSize + "m");
vmParams.add("-Dfile.encoding=" + System.getProperty("file.encoding"));
//vmParams.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");
String grapeRoot = System.getProperty(GroovycOutputParser.GRAPE_ROOT);
if (grapeRoot != null) {
vmParams.add("-D" + GroovycOutputParser.GRAPE_ROOT + "=" + grapeRoot);
}
final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
getJavaExecutable(chunk),
"org.jetbrains.groovy.compiler.rt.GroovycRunner",
Collections.<String>emptyList(), classpath,
vmParams,
programParams
);
final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
ProcessHandler handler = new BaseOSProcessHandler(process, null, null) {
@Override
protected Future<?> executeOnPooledThread(Runnable task) {
return SharedThreadPool.getInstance().executeOnPooledThread(task);
}
@Override
public void notifyTextAvailable(String text, Key outputType) {
parser.notifyTextAvailable(text, outputType);
}
};
handler.startNotify();
handler.waitFor();
parser.notifyFinished(process.exitValue());
}
private static boolean checkChunkRebuildNeeded(CompileContext context, GroovycOutputParser parser) {
if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) || !parser.shouldRetry()) {
return false;
@@ -429,12 +342,7 @@ public class GroovyBuilder extends ModuleLevelBuilder {
return item.outputPath;
}
private static String getJavaExecutable(ModuleChunk chunk) {
JpsSdk<?> sdk = getJdk(chunk);
return sdk != null ? JpsJavaSdkType.getJavaExecutable(sdk) : SystemProperties.getJavaHome() + "/bin/java";
}
private static JpsSdk<JpsDummyElement> getJdk(ModuleChunk chunk) {
static JpsSdk<JpsDummyElement> getJdk(ModuleChunk chunk) {
return chunk.getModules().iterator().next().getSdk(JpsJavaSdkType.INSTANCE);
}
@@ -0,0 +1,30 @@
/*
* 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.incremental.groovy;
import java.io.File;
import java.util.Collection;
/**
* @author peter
*/
public interface GroovycFlavor {
void runGroovyc(Collection<String> compilationClassPath,
boolean stubs,
JpsGroovySettings settings,
File tempFile,
GroovycOutputParser parser) throws Exception;
}
@@ -27,8 +27,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.groovy.compiler.rt.GroovyCompilerMessageCategories;
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import java.io.*;
import java.util.*;
@@ -37,12 +40,19 @@ import java.util.*;
* @author: Dmitry.Krasilschikov
* @date: 16.04.2007
*/
public abstract class GroovycOutputParser {
public class GroovycOutputParser {
private static final String GROOVY_COMPILER_IN_OPERATION = "Groovy compiler in operation...";
public static final String GRAPE_ROOT = "grape.root";
private final List<OutputItem> myCompiledItems = new ArrayList<OutputItem>();
private final List<CompilerMessage> compilerMessages = new ArrayList<CompilerMessage>();
private final StringBuffer stdErr = new StringBuffer();
private final ModuleChunk myChunk;
private final CompileContext myContext;
public GroovycOutputParser(ModuleChunk chunk, CompileContext context) {
myChunk = chunk;
myContext = context;
}
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.groovy.GroovycOSProcessHandler");
private int myExitCode;
@@ -67,7 +77,9 @@ public abstract class GroovycOutputParser {
private final StringBuffer outputBuffer = new StringBuffer();
protected abstract void updateStatus(@NotNull String status);
private void updateStatus(@NotNull String status) {
myContext.processMessage(new ProgressMessage(status + " [" + myChunk.getPresentableShortName() + "]"));
}
private void parseOutput(String text) {
final String trimmed = text.trim();
@@ -23,7 +23,6 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.lang.UrlClassLoader;
import org.jetbrains.annotations.NotNull;
@@ -43,17 +42,35 @@ import java.util.regex.Pattern;
/**
* @author peter
*/
class InProcessGroovyc {
class InProcessGroovyc implements GroovycFlavor {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.groovy.InProcessGroovyc");
private static final Pattern GROOVY_ALL_JAR_PATTERN = Pattern.compile("groovy-all(-(.*))?\\.jar");
private static SoftReference<Pair<String, ClassLoader>> ourParentLoaderCache;
private static final UrlClassLoader.CachePool ourLoaderCachePool = UrlClassLoader.createCachePool();
private static final Object ourInProcessGroovycLock = new Object();
private final Collection<String> myOutputs;
InProcessGroovyc(Collection<String> outputs) {
myOutputs = outputs;
}
@Override
public void runGroovyc(Collection<String> compilationClassPath,
boolean forStubs,
JpsGroovySettings settings,
File tempFile,
GroovycOutputParser parser) throws Exception {
synchronized (ourInProcessGroovycLock) {
runGroovycInThisProcess(compilationClassPath, forStubs, settings, tempFile, parser);
}
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
static void runGroovycInThisProcess(Collection<String> compilationClassPath,
final Collection<String> outputs,
List<String> programParams,
final GroovycOutputParser parser)
private void runGroovycInThisProcess(Collection<String> compilationClassPath,
boolean forStubs,
JpsGroovySettings settings,
File tempFile,
final GroovycOutputParser parser)
throws MalformedURLException {
ClassLoader parent = obtainParentLoader(compilationClassPath);
@@ -65,7 +82,7 @@ class InProcessGroovyc {
public boolean shouldCacheData(@NotNull URL url) {
try {
String file = FileUtil.toCanonicalPath(new File(url.toURI()).getPath());
for (String output : outputs) {
for (String output : myOutputs) {
if (FileUtil.startsWith(output, file)) {
return false;
}
@@ -88,8 +105,8 @@ class InProcessGroovyc {
Thread.currentThread().setContextClassLoader(loader);
try {
Class<?> runnerClass = loader.loadClass("org.jetbrains.groovy.compiler.rt.GroovycRunner");
Method intMain = runnerClass.getDeclaredMethod("intMain", String[].class);
Integer exitCode = (Integer)intMain.invoke(null, new Object[]{ArrayUtil.toStringArray(programParams)});
Method intMain = runnerClass.getDeclaredMethod("intMain2", boolean.class, boolean.class, boolean.class, String.class);
Integer exitCode = (Integer)intMain.invoke(null, settings.invokeDynamic, false, forStubs, tempFile.getPath());
parser.notifyFinished(exitCode);
}
catch (Exception e) {
@@ -144,7 +161,7 @@ class InProcessGroovyc {
}
}
};
;
ourParentLoaderCache = new SoftReference<Pair<String, ClassLoader>>(Pair.create(groovyAll, wrapper));
return wrapper;
}
@@ -84,18 +84,27 @@ public class GroovycRunner {
}
public static int intMain(String[] args) {
boolean indy = false;
if (args.length != 3) {
if (args.length != 4 || !"--indy".equals(args[3])) {
System.err.println("There is no arguments for groovy compiler");
return 1;
}
System.setProperty("groovy.target.indy", "true");
indy = true;
}
final boolean optimize = GroovyRtConstants.OPTIMIZE.equals(args[0]);
final boolean forStubs = "stubs".equals(args[1]);
String argPath = args[2];
return intMain2(indy, optimize, forStubs, argPath);
}
public static int intMain2(boolean indy, boolean optimize, boolean forStubs, String argPath) {
if (indy) {
System.setProperty("groovy.target.indy", "true");
}
if (!new File(argPath).exists()) {
System.err.println("Arguments file for groovy compiler not found");
return 1;