From a91b27c19150c09ba4bca283d63f9cde19199e67 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 18 Feb 2015 21:39:43 +0100 Subject: [PATCH] split in-process and forked groovyc running into different classes --- .../jps/incremental/groovy/ForkedGroovyc.java | 122 ++++++++++++++++++ .../jps/incremental/groovy/GroovyBuilder.java | 112 ++-------------- .../jps/incremental/groovy/GroovycFlavor.java | 30 +++++ .../groovy/GroovycOutputParser.java | 16 ++- .../incremental/groovy/InProcessGroovyc.java | 37 ++++-- .../groovy/compiler/rt/GroovycRunner.java | 11 +- 6 files changed, 213 insertions(+), 115 deletions(-) create mode 100644 plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/ForkedGroovyc.java create mode 100644 plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycFlavor.java diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/ForkedGroovyc.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/ForkedGroovyc.java new file mode 100644 index 000000000000..eae07062c817 --- /dev/null +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/ForkedGroovyc.java @@ -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 compilationClassPath, + boolean forStubs, + JpsGroovySettings settings, + File tempFile, + final GroovycOutputParser parser) + throws Exception { + List classpath = new ArrayList(); + 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 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 cmd = ExternalProcessUtil.buildJavaCommandLine( + getJavaExecutable(myChunk), + "org.jetbrains.groovy.compiler.rt.GroovycRunner", + Collections.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 getProgramParams(File tempFile, JpsGroovySettings settings, boolean forStubs) { + List 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"; + } + +} diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index cb4e84708466..d700514d612d 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.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 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 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> 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 compilationClassPath, - boolean optimizeClassLoading, boolean inProcess, Collection outputs) throws IOException { - List 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 compilationClassPath, - boolean optimizeClassLoading, List programParams, final GroovycOutputParser parser) - throws IOException { - List classpath = new ArrayList(); - 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 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 cmd = ExternalProcessUtil.buildJavaCommandLine( - getJavaExecutable(chunk), - "org.jetbrains.groovy.compiler.rt.GroovycRunner", - Collections.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 getJdk(ModuleChunk chunk) { + static JpsSdk getJdk(ModuleChunk chunk) { return chunk.getModules().iterator().next().getSdk(JpsJavaSdkType.INSTANCE); } diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycFlavor.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycFlavor.java new file mode 100644 index 000000000000..f0c0ed21883d --- /dev/null +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycFlavor.java @@ -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 compilationClassPath, + boolean stubs, + JpsGroovySettings settings, + File tempFile, + GroovycOutputParser parser) throws Exception; +} diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java index c85d5e8bd92e..a94564e47c16 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOutputParser.java @@ -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 myCompiledItems = new ArrayList(); private final List compilerMessages = new ArrayList(); 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(); diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/InProcessGroovyc.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/InProcessGroovyc.java index e1da33e0aa36..49081b8adc8d 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/InProcessGroovyc.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/InProcessGroovyc.java @@ -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> ourParentLoaderCache; private static final UrlClassLoader.CachePool ourLoaderCachePool = UrlClassLoader.createCachePool(); + private static final Object ourInProcessGroovycLock = new Object(); + private final Collection myOutputs; + + InProcessGroovyc(Collection outputs) { + myOutputs = outputs; + } + + @Override + public void runGroovyc(Collection 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 compilationClassPath, - final Collection outputs, - List programParams, - final GroovycOutputParser parser) + private void runGroovycInThisProcess(Collection 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.create(groovyAll, wrapper)); return wrapper; } diff --git a/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/GroovycRunner.java b/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/GroovycRunner.java index 9794fd9a1efe..8f5daa314e82 100644 --- a/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/GroovycRunner.java +++ b/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/GroovycRunner.java @@ -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;