IDEA-120443 Groovy: Idea can't compile valid code in implicitly typed statically compiled extension module

This commit is contained in:
peter
2014-03-24 12:53:38 +01:00
parent 938485b780
commit eb470cecb6
5 changed files with 90 additions and 65 deletions
@@ -124,8 +124,6 @@ public class GroovyBuilder extends ModuleLevelBuilder {
Map<ModuleBuildTarget, String> generationOutputs = myForStubs ? getStubGenerationOutputs(chunk, context) : finalOutputs;
String compilerOutput = generationOutputs.get(chunk.representativeTarget());
String finalOutput = FileUtil.toSystemDependentName(finalOutputs.get(chunk.representativeTarget()));
Collection<String> 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);
@@ -246,7 +246,7 @@ public class GroovycOSProcessHandler extends BaseOSProcessHandler {
public static File fillFileWithGroovycParameters(final String outputDir,
final Collection<String> changedSources,
String finalOutput,
Collection<String> finalOutputs,
Map<String, String> class2Src,
@Nullable final String encoding,
List<String> 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 {
@@ -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<File> srcFiles = new ArrayList<File>();
final Map<String, File> class2File = new HashMap<String, File>();
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<GroovyCompilerWrapper.OutputItem> 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<GroovyCompilerWrapper.OutputItem> 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<CompilationUnitPatcher> patchers, List<CompilerMessage> compilerMessages,
List<File> srcFiles, Map<String, File> class2File, String[] finalOutput) {
List<File> srcFiles, Map<String, File> 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<URL> getResources(String name) throws IOException {
if (name.endsWith("org.codehaus.groovy.transform.ASTTransformation")) {
final Enumeration<URL> resources = super.getResources(name);
final ArrayList<URL> 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) {
@@ -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<String> 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);
@@ -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> T test2(List<T> 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<String> 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 }
}