From eb470cecb68ec41d534b56287f4b8117be74ac09 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 24 Mar 2014 12:52:24 +0100 Subject: [PATCH] IDEA-120443 Groovy: Idea can't compile valid code in implicitly typed statically compiled extension module --- .../jps/incremental/groovy/GroovyBuilder.java | 4 +- .../groovy/GroovycOSProcessHandler.java | 4 +- .../compiler/rt/DependentGroovycRunner.java | 86 +++++++++---------- .../groovy/compiler/GroovyCompilerBase.java | 20 +++-- .../groovy/compiler/GroovyCompilerTest.groovy | 41 +++++++-- 5 files changed, 90 insertions(+), 65 deletions(-) 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 af83972c7ec5..4388c59b229b 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 @@ -124,8 +124,6 @@ public class GroovyBuilder extends ModuleLevelBuilder { Map generationOutputs = myForStubs ? getStubGenerationOutputs(chunk, context) : finalOutputs; String compilerOutput = generationOutputs.get(chunk.representativeTarget()); - String finalOutput = FileUtil.toSystemDependentName(finalOutputs.get(chunk.representativeTarget())); - Collection classpath = generateClasspath(context, chunk); if (LOG.isDebugEnabled()) { LOG.debug("Optimized class loading: " + optimizeClassLoading); @@ -133,7 +131,7 @@ public class GroovyBuilder extends ModuleLevelBuilder { } final File tempFile = GroovycOSProcessHandler.fillFileWithGroovycParameters( - compilerOutput, toCompilePaths, finalOutput, class2Src, encoding, patchers, + compilerOutput, toCompilePaths, finalOutputs.values(), class2Src, encoding, patchers, optimizeClassLoading ? StringUtil.join(classpath, File.pathSeparator) : "" ); final GroovycOSProcessHandler handler = runGroovyc(context, chunk, tempFile, settings, classpath, optimizeClassLoading); diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOSProcessHandler.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOSProcessHandler.java index a03067d99e61..f075cb7a8e21 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOSProcessHandler.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovycOSProcessHandler.java @@ -246,7 +246,7 @@ public class GroovycOSProcessHandler extends BaseOSProcessHandler { public static File fillFileWithGroovycParameters(final String outputDir, final Collection changedSources, - String finalOutput, + Collection finalOutputs, Map class2Src, @Nullable final String encoding, List patchers, @@ -284,7 +284,7 @@ public class GroovycOSProcessHandler extends BaseOSProcessHandler { writer.write(outputDir); writer.write("\n"); writer.write(GroovyRtConstants.FINAL_OUTPUTPATH + "\n"); - writer.write(finalOutput); + writer.write(StringUtil.join(finalOutputs, File.pathSeparator)); writer.write("\n"); } finally { diff --git a/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/DependentGroovycRunner.java b/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/DependentGroovycRunner.java index bb374cdfcdf4..c7af72f99929 100644 --- a/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/DependentGroovycRunner.java +++ b/plugins/groovy/rt/src/org/jetbrains/groovy/compiler/rt/DependentGroovycRunner.java @@ -29,8 +29,6 @@ import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit; import java.io.*; import java.lang.reflect.*; -import java.net.URI; -import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; @@ -39,6 +37,9 @@ import java.util.*; * @author peter */ public class DependentGroovycRunner { + public static final String TEMP_RESOURCE_SUFFIX = "___" + new Random().nextInt() + "_neverHappen"; + public static final String[] RESOURCES_TO_MASK = {"META-INF/services/org.codehaus.groovy.transform.ASTTransformation", "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule"}; + public static boolean runGroovyc(boolean forStubs, String argsPath) { File argsFile = new File(argsPath); final CompilerConfiguration config = new CompilerConfiguration(); @@ -51,8 +52,8 @@ public class DependentGroovycRunner { final List srcFiles = new ArrayList(); final Map class2File = new HashMap(); - final String[] finalOutput = new String[1]; - fillFromArgsFile(argsFile, config, patchers, compilerMessages, srcFiles, class2File, finalOutput); + final String[] finalOutputRef = new String[1]; + fillFromArgsFile(argsFile, config, patchers, compilerMessages, srcFiles, class2File, finalOutputRef); if (srcFiles.isEmpty()) return true; if (forStubs) { @@ -63,23 +64,32 @@ public class DependentGroovycRunner { } System.out.println(GroovyRtConstants.PRESENTABLE_MESSAGE + "Groovyc: loading sources..."); - final AstAwareResourceLoader resourceLoader = new AstAwareResourceLoader(class2File); - final CompilationUnit unit = createCompilationUnit(forStubs, config, finalOutput[0], buildClassLoaderFor(config, resourceLoader)); - unit.addPhaseOperation(new CompilationUnit.SourceUnitOperation() { - public void call(SourceUnit source) throws CompilationFailedException { - File file = new File(source.getName()); - for (ClassNode aClass : source.getAST().getClasses()) { - resourceLoader.myClass2File.put(aClass.getName(), file); + String[] finalOutputs = finalOutputRef[0].split(File.pathSeparator); + renameResources(finalOutputs, "", TEMP_RESOURCE_SUFFIX); + + final List compiledFiles; + try { + final AstAwareResourceLoader resourceLoader = new AstAwareResourceLoader(class2File); + final CompilationUnit unit = createCompilationUnit(forStubs, config, buildClassLoaderFor(config, resourceLoader)); + unit.addPhaseOperation(new CompilationUnit.SourceUnitOperation() { + public void call(SourceUnit source) throws CompilationFailedException { + File file = new File(source.getName()); + for (ClassNode aClass : source.getAST().getClasses()) { + resourceLoader.myClass2File.put(aClass.getName(), file); + } } - } - }, Phases.CONVERSION); + }, Phases.CONVERSION); - addSources(forStubs, srcFiles, unit); - runPatchers(patchers, compilerMessages, unit, resourceLoader, srcFiles); + addSources(forStubs, srcFiles, unit); + runPatchers(patchers, compilerMessages, unit, resourceLoader, srcFiles); - System.out.println(GroovyRtConstants.PRESENTABLE_MESSAGE + "Groovyc: compiling..."); - final List compiledFiles = new GroovyCompilerWrapper(compilerMessages, forStubs).compile(unit); - System.out.println(GroovyRtConstants.CLEAR_PRESENTABLE); + System.out.println(GroovyRtConstants.PRESENTABLE_MESSAGE + "Groovyc: compiling..."); + compiledFiles = new GroovyCompilerWrapper(compilerMessages, forStubs).compile(unit); + } + finally { + renameResources(finalOutputs, TEMP_RESOURCE_SUFFIX, ""); + System.out.println(GroovyRtConstants.CLEAR_PRESENTABLE); + } System.out.println(); reportCompiledItems(compiledFiles); @@ -103,8 +113,19 @@ public class DependentGroovycRunner { return false; } + private static void renameResources(String[] finalOutputs, String removeSuffix, String addSuffix) { + for (String output : finalOutputs) { + for (String res : RESOURCES_TO_MASK) { + File file = new File(output, res + removeSuffix); + if (file.exists()) { + file.renameTo(new File(output, res + addSuffix)); + } + } + } + } + private static String fillFromArgsFile(File argsFile, CompilerConfiguration compilerConfiguration, List patchers, List compilerMessages, - List srcFiles, Map class2File, String[] finalOutput) { + List srcFiles, Map class2File, String[] finalOutputs) { String moduleClasspath = null; BufferedReader reader = null; @@ -157,7 +178,7 @@ public class DependentGroovycRunner { compilerConfiguration.setTargetDirectory(reader.readLine()); } else if (line.startsWith(GroovyRtConstants.FINAL_OUTPUTPATH)) { - finalOutput[0] = reader.readLine(); + finalOutputs[0] = reader.readLine(); } line = reader.readLine(); @@ -256,30 +277,9 @@ public class DependentGroovycRunner { private static CompilationUnit createCompilationUnit(final boolean forStubs, final CompilerConfiguration config, - final String finalOutput, final GroovyClassLoader classLoader) { + final GroovyClassLoader classLoader) { - final GroovyClassLoader transformLoader = new GroovyClassLoader(classLoader) { - public Enumeration getResources(String name) throws IOException { - if (name.endsWith("org.codehaus.groovy.transform.ASTTransformation")) { - final Enumeration resources = super.getResources(name); - final ArrayList list = Collections.list(resources); - for (Iterator iterator = list.iterator(); iterator.hasNext();) { - final URL url = (URL)iterator.next(); - try { - final String file = new File(new URI(url.toString())).getCanonicalPath(); - if (file.startsWith(finalOutput) || file.startsWith("/" + finalOutput)) { - iterator.remove(); - } - } - catch (Exception ignored) { - System.out.println("Invalid URI syntax: " + url.toString()); - } - } - return Collections.enumeration(list); - } - return super.getResources(name); - } - }; + final GroovyClassLoader transformLoader = new GroovyClassLoader(classLoader); try { if (forStubs) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java index 505d88b0e4dc..2296fbc216e1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java @@ -55,7 +55,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingProjectManager; import com.intellij.psi.PsiFile; @@ -166,10 +166,14 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { parameters.setMainClass(GroovycRunner.class.getName()); - final VirtualFile finalOutputDir = getMainOutput(compileContext, module, tests); - if (finalOutputDir == null) { - compileContext.addMessage(CompilerMessageCategory.ERROR, "No output directory for module " + module.getName() + (tests ? " tests" : " production"), null, -1, -1); - return; + List finalOutputs = ContainerUtil.newArrayList(); + for (Module eachModule : chunk.getModules()) { + final VirtualFile finalOutputDir = getMainOutput(compileContext, eachModule, tests); + if (finalOutputDir == null) { + compileContext.addMessage(CompilerMessageCategory.ERROR, "No output directory for module " + eachModule.getName() + (tests ? " tests" : " production"), null, -1, -1); + return; + } + finalOutputs.add(FileUtil.toSystemDependentName(finalOutputDir.getPath())); } final Charset ideCharset = EncodingProjectManager.getInstance(myProject).getDefaultCharset(); @@ -193,7 +197,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { final File fileWithParameters; try { fileWithParameters = GroovycOSProcessHandler - .fillFileWithGroovycParameters(outputDir.getPath(), paths2Compile, FileUtil.toSystemDependentName(finalOutputDir.getPath()), + .fillFileWithGroovycParameters(outputDir.getPath(), paths2Compile, finalOutputs, class2Src, encoding, patchers, ""); } catch (IOException e) { @@ -228,7 +232,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { for (CompilerMessage compilerMessage : processHandler.getCompilerMessages(module.getName())) { final String url = compilerMessage.getSourcePath(); compileContext.addMessage(getMessageCategory(compilerMessage), compilerMessage.getMessageText(), - url == null ? null : VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(url)), + url == null ? null : VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(url)), (int)compilerMessage.getLine(), (int)compilerMessage.getColumn()); } @@ -280,7 +284,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { } } - sink.add(outputDir.getPath(), items, VfsUtil.toVirtualFileArray(toRecompile)); + sink.add(outputDir.getPath(), items, VfsUtilCore.toVirtualFileArray(toRecompile)); } catch (ExecutionException e) { LOG.info(e); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy index 9be76d2096db..67ceec5d3c1f 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy @@ -17,7 +17,6 @@ package org.jetbrains.plugins.groovy.compiler import com.intellij.compiler.CompilerConfiguration import com.intellij.compiler.CompilerConfigurationImpl -import com.intellij.compiler.impl.TranslatingCompilerFilesMonitor import com.intellij.compiler.server.BuildManager import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.compiler.CompilerMessage @@ -41,12 +40,6 @@ public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { addGroovyLibrary(myModule); } - @Override - protected void tearDown() throws Exception { - TranslatingCompilerFilesMonitor.ourDebugMode = false - super.tearDown() - } - public void testPlainGroovy() throws Throwable { myFixture.addFileToProject("A.groovy", "println '239'"); assertEmpty(make()); @@ -134,8 +127,6 @@ public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { } public void testTransitiveJavaDependencyThroughGroovy() throws Throwable { - TranslatingCompilerFilesMonitor.ourDebugMode = true - myFixture.addClass("public class IFoo { void foo() {} }").getContainingFile().getVirtualFile(); myFixture.addFileToProject("Foo.groovy", "class Foo {\n" + " static IFoo f\n" + @@ -796,6 +787,38 @@ string assertEmpty make() } + public void "test compiling static extension"() { + setupTestSources() + myFixture.addFileToProject "src/extension/Extension.groovy", """ +package extension +import groovy.transform.CompileStatic + +@CompileStatic class Extension { + static T test2(List self) { + self.first() + } +}""" + myFixture.addFileToProject "src/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule", """ +moduleName=extension-verify +moduleVersion=1.0-test +extensionClasses=extension.Extension +staticExtensionClasses= +""" + myFixture.addFileToProject "tests/AppTest.groovy", """ +class AppTest { + @groovy.transform.CompileStatic + static main(args) { + List list = new ArrayList<>() + list.add("b") + list.add("c") + println list.test2() + } +} +""" + assertEmpty make() + assertOutput 'AppTest', 'b' + } + public static class IdeaModeTest extends GroovyCompilerTest { @Override protected boolean useJps() { false } }