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 bb53179b3e7b..46ea89b856f2 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 @@ -24,6 +24,7 @@ import org.codehaus.groovy.tools.javac.JavaAwareResolveVisitor; import org.codehaus.groovy.tools.javac.JavaStubGenerator; import java.io.*; +import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; @@ -39,6 +40,7 @@ public class GroovycRunner { public static final String PATCHERS = "patchers"; public static final String ENCODING = "encoding"; public static final String OUTPUTPATH = "outputpath"; + public static final String FINAL_OUTPUTPATH = "final_outputpath"; public static final String END = "end"; public static final String SRC_FILE = "src_file"; @@ -112,11 +114,12 @@ public class GroovycRunner { final List srcFiles = new ArrayList(); final Map class2File = new HashMap(); - fillFromArgsFile(argsFile, compilerConfiguration, patchers, compilerMessages, srcFiles, class2File); + final String[] finalOutput = new String[1]; + fillFromArgsFile(argsFile, compilerConfiguration, patchers, compilerMessages, srcFiles, class2File, finalOutput); if (srcFiles.isEmpty()) return; System.out.println(PRESENTABLE_MESSAGE + "Groovy compiler: loading sources..."); - final CompilationUnit unit = createCompilationUnit(forStubs, compilerConfiguration); + final CompilationUnit unit = createCompilationUnit(forStubs, compilerConfiguration, finalOutput[0]); addSources(forStubs, srcFiles, unit); runPatchers(patchers, compilerMessages, class2File, unit); @@ -165,7 +168,7 @@ public class GroovycRunner { } private static String fillFromArgsFile(File argsFile, CompilerConfiguration compilerConfiguration, List patchers, List compilerMessages, - List srcFiles, Map class2File) { + List srcFiles, Map class2File, String[] finalOutput) { String moduleClasspath = null; BufferedReader reader = null; @@ -207,14 +210,15 @@ public class GroovycRunner { } } } - - if (line.startsWith(ENCODING)) { + else if (line.startsWith(ENCODING)) { compilerConfiguration.setSourceEncoding(reader.readLine()); } - - if (line.startsWith(OUTPUTPATH)) { + else if (line.startsWith(OUTPUTPATH)) { compilerConfiguration.setTargetDirectory(reader.readLine()); } + else if (line.startsWith(FINAL_OUTPUTPATH)) { + finalOutput[0] = reader.readLine(); + } line = reader.readLine(); } @@ -324,11 +328,40 @@ public class GroovycRunner { compilerMessages.add(new CompilerMessage(CompilerMessage.WARNING, message + ":\n" + writer, "", -1, -1)); } - private static CompilationUnit createCompilationUnit(final boolean forStubs, final CompilerConfiguration config) { + private static CompilationUnit createCompilationUnit(final boolean forStubs, final CompilerConfiguration config, String finalOutput) { config.setClasspathList(Collections.EMPTY_LIST); final GroovyClassLoader classLoader = buildClassLoaderFor(config); - final CompilationUnit unit = new CompilationUnit(config, null, classLoader) { + + final String localGlobalTransforms = new File(finalOutput + File.separator + + "META-INF" + File.separator + + "services" + File.separator + + "org.codehaus.groovy.transform.ASTTransformation") + .getAbsolutePath(); + + final GroovyClassLoader transformLoader = new GroovyClassLoader(classLoader) { + public Enumeration getResources(String name) throws IOException { + if (name.endsWith("org.codehaus.groovy.transform.ASTTransformation")) { + if (forStubs) { + //commenting the next line (it shouldn't be there) will result in GroovyCompilerTest failure + //meaning that stub generation for some module (A) may require already compiled classes from another module (B) + //where A depends on B, of course + return Collections.enumeration(Collections.EMPTY_LIST); + } + + final Enumeration resources = super.getResources(name); + final ArrayList list = Collections.list(resources); + for (Iterator iterator = list.iterator(); iterator.hasNext();) { + if (localGlobalTransforms.equals(((URL)iterator.next()).getFile())) { + iterator.remove(); + } + } + return Collections.enumeration(list); + } + return super.getResources(name); + } + }; + final CompilationUnit unit = new CompilationUnit(config, null, classLoader, transformLoader) { public void gotoPhase(int phase) throws CompilationFailedException { super.gotoPhase(phase); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompiler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompiler.java index 281632408cab..1346be937ad8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompiler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompiler.java @@ -57,9 +57,8 @@ public class GroovyCompiler extends GroovyCompilerBase { } @Override - protected void compileFiles(CompileContext compileContext, Module module, List toCompile, VirtualFile outputDir, OutputSink sink, - boolean tests) { - runGroovycCompiler(compileContext, module, toCompile, false, outputDir, sink, tests); + protected void compileFiles(CompileContext compileContext, Module module, List toCompile, OutputSink sink, boolean tests) { + runGroovycCompiler(compileContext, module, toCompile, false, getMainOutput(compileContext, module, tests), sink, tests); } public boolean validateConfiguration(CompileScope compileScope) { 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 83e97f3674ed..afb713c505cb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java @@ -57,7 +57,6 @@ import com.intellij.util.PathUtil; import com.intellij.util.PathsList; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.groovy.compiler.rt.CompilerMessage; import org.jetbrains.groovy.compiler.rt.GroovycRunner; import org.jetbrains.plugins.groovy.GroovyFileType; @@ -67,7 +66,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import java.io.*; import java.nio.charset.Charset; import java.util.*; -import java.util.regex.Pattern; /** * @author peter @@ -75,8 +73,6 @@ import java.util.regex.Pattern; public abstract class GroovyCompilerBase implements TranslatingCompiler { private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.compiler.GroovyCompilerBase"); protected final Project myProject; - @NonNls private static final String GROOVYC_RUNNER_REQUIRED = ".*(groovy|asm|antlr|junit|jline|ant|commons).*\\.jar"; - private static final Pattern NONTRADITIONAL_GROOVYC_RUNNER_REQUIRED = Pattern.compile(".*(groovy|junit|jline|ant).*\\.jar"); public GroovyCompilerBase(Project project) { myProject = project; @@ -142,7 +138,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { try { File fileWithParameters = File.createTempFile("toCompile", ""); - fillFileWithGroovycParameters(toCompile, fileWithParameters, outputDir, patchers); + fillFileWithGroovycParameters(toCompile, fileWithParameters, outputDir, patchers, getMainOutput(compileContext, module, tests)); commandLine.addParameter(forStubs ? "stubs" : "groovyc"); commandLine.addParameter(fileWithParameters.getPath()); @@ -211,6 +207,10 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { } } + protected static VirtualFile getMainOutput(CompileContext compileContext, Module module, boolean tests) { + return tests ? compileContext.getModuleOutputDirectoryForTests(module) : compileContext.getModuleOutputDirectory(module); + } + private static CompilerMessageCategory getMessageCategory(CompilerMessage compilerMessage) { String category; category = compilerMessage.getCategory(); @@ -223,7 +223,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { return CompilerMessageCategory.ERROR; } - private void fillFileWithGroovycParameters(List virtualFiles, File f, VirtualFile outputDir, final List patchers) { + private void fillFileWithGroovycParameters(List virtualFiles, File f, VirtualFile outputDir, final List patchers, VirtualFile finalOutputDir) { if (LOG.isDebugEnabled()) { LOG.debug("Running groovyc on: " + virtualFiles.toString()); } @@ -269,9 +269,13 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { printer.println(ideCharset.name()); } - //production output printer.println(GroovycRunner.OUTPUTPATH); printer.println(PathUtil.getLocalPath(outputDir)); + + printer.println(GroovycRunner.FINAL_OUTPUTPATH); + printer.println(PathUtil.getLocalPath(finalOutputDir)); + + printer.close(); } @@ -314,10 +318,10 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { } if (!toCompile.isEmpty()) { - compileFiles(compileContext, module, toCompile, compileContext.getModuleOutputDirectory(module), sink, false); + compileFiles(compileContext, module, toCompile, sink, false); } if (!toCompileTests.isEmpty()) { - compileFiles(compileContext, module, toCompileTests, compileContext.getModuleOutputDirectoryForTests(module), sink, true); + compileFiles(compileContext, module, toCompileTests, sink, true); } } @@ -325,9 +329,7 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { } protected abstract void compileFiles(CompileContext compileContext, Module module, - List toCompile, - VirtualFile outputDir, - OutputSink sink, boolean tests); + List toCompile, OutputSink sink, boolean tests); public boolean isCompilableFile(VirtualFile file, CompileContext context) { final boolean result = GroovyFileType.GROOVY_FILE_TYPE.equals(file.getFileType()); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/generator/GroovycStubGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/generator/GroovycStubGenerator.java index 4118415ded64..dcb5a25cfd33 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/generator/GroovycStubGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/generator/GroovycStubGenerator.java @@ -80,7 +80,7 @@ public class GroovycStubGenerator extends GroovyCompilerBase { @Override protected void compileFiles(CompileContext compileContext, Module module, - final List toCompile, VirtualFile outputDir, OutputSink sink, boolean tests) { + final List toCompile, OutputSink sink, boolean tests) { boolean hasGroovy = false; boolean hasJava = false; for (final VirtualFile file : toCompile) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyCompilerTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyCompilerTest.java index 63150cc6b89d..eedb0ba565ed 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyCompilerTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyCompilerTest.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang; +import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerManagerImpl; import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; @@ -38,17 +39,18 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.compiler.*; -import com.intellij.openapi.roots.CompilerModuleExtension; -import com.intellij.openapi.roots.ContentEntry; -import com.intellij.openapi.roots.ModifiableRootModel; -import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.module.ModifiableModuleModel; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.StdModuleTypes; +import com.intellij.openapi.roots.*; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; -import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; +import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.testFramework.fixtures.TempDirTestFixture; import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl; @@ -77,6 +79,17 @@ public class GroovyCompilerTest extends JavaCodeInsightFixtureTestCase { super.setUp(); getProject().getComponent(GroovyCompilerLoader.class).projectOpened(); CompilerManagerImpl.testSetup(); + + CompilerProjectExtension.getInstance(getProject()).setCompilerOutputUrl(myMainOutput.findOrCreateDir("out").getUrl()); + + addGroovyLibrary(myModule); + } + + private static void addGroovyLibrary(final Module to) { + final String root = PathManager.getHomePath() + "/community/lib/"; + final File[] groovyJars = GroovyUtils.getFilesInDirectoryByPattern(root, GroovyConfigUtils.GROOVY_ALL_JAR_PATTERN); + assert groovyJars.length == 1; + PsiTestUtil.addLibrary(to, "groovy", root, groovyJars[0].getName()); } @Override @@ -86,17 +99,6 @@ public class GroovyCompilerTest extends JavaCodeInsightFixtureTestCase { super.tearDown(); } - @Override - protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception { - super.tuneFixture(moduleBuilder); - moduleBuilder.setOutputPath(myMainOutput.getTempDirPath() + "/out/production"); - moduleBuilder.setTestOutputPath(myMainOutput.getTempDirPath() + "/out/tests"); - final File[] groovyJars = GroovyUtils.getFilesInDirectoryByPattern(PathManager.getHomePath() + "/community/lib", GroovyConfigUtils.GROOVY_ALL_JAR_PATTERN); - assert groovyJars.length == 1; - moduleBuilder.addLibrary("Groovy", groovyJars[0].getPath()); - //moduleBuilder.addJdk(CompilerConfigurationImpl.getTestsExternalCompilerHome()); - } - public void testPlainGroovy() throws Throwable { myFixture.addFileToProject("A.groovy", "println '239'"); assertEmpty(make()); @@ -298,6 +300,85 @@ public class GroovyCompilerTest extends JavaCodeInsightFixtureTestCase { assertEmpty(make()); } + public void testDontApplyTransformsFromSameModule() throws Exception { + addTransform(); + + myFixture.addClass("public class JavaClassToGenerateStubs {}"); + + assertEmpty(make()); + + } + + private void addTransform() throws IOException { + myFixture.addFileToProject("Transf.groovy", + "import org.codehaus.groovy.ast.*\n" + + "import org.codehaus.groovy.control.*\n" + + "import org.codehaus.groovy.transform.*\n" + + "@GroovyASTTransformation(phase = CompilePhase.CONVERSION)\n" + + "public class Transf implements ASTTransformation {\n" + + " void visit(ASTNode[] nodes, SourceUnit sourceUnit) {\n" + + " ModuleNode module = nodes[0]\n" + + " for (clazz in module.classes) {\n" + + " if (clazz.name.contains('Bar')) " + + " module.addStaticImportClass('Foo', ClassHelper.makeWithoutCaching(Foo.class));\n" + + " }\n" + + " }\n" + + "}"); + + myFixture.addFileToProject("Foo.groovy", "class Foo {\n" + + "static def autoImported() { 239 }\n" + + "}"); + + CompilerConfiguration.getInstance(getProject()).addResourceFilePattern("*.ASTTransformation"); + + myFixture.addFileToProject("META-INF/services/org.codehaus.groovy.transform.ASTTransformation", "Transf"); + } + + public void testApplyTransformsFromDependencies() throws Exception { + addTransform(); + + myFixture.addFileToProject("dependent/Bar.groovy", "class Bar {\n" + + " static Object zzz = autoImported()\n" + + " static void main(String[] args) {\n" + + " println zzz\n" + + " }\n" + + "}"); + + myFixture.addFileToProject("dependent/AJavaClass.java", "class AJavaClass {}"); + + Module dep = addDependentModule(); + + addGroovyLibrary(dep); + + assertEmpty(make()); + assertOutput("Bar", "239", dep); + } + + private Module addDependentModule() { + Module dep = new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final ModifiableModuleModel moduleModel = ModuleManager.getInstance(getProject()).getModifiableModel(); + moduleModel.newModule("dependent/dependent.iml", StdModuleTypes.JAVA); + moduleModel.commit(); + + final Module dep = ModuleManager.getInstance(getProject()).findModuleByName("dependent"); + final ModifiableRootModel model = ModuleRootManager.getInstance(dep).getModifiableModel(); + model.addModuleOrderEntry(myModule); + final VirtualFile depRoot = myFixture.getTempDirFixture().getFile("dependent"); + final ContentEntry entry = model.addContentEntry(depRoot); + entry.addSourceFolder(depRoot, false); + model.setSdk(ModuleRootManager.getInstance(myModule).getSdk()); + + //model.getModuleExtension(CompilerModuleExtension.class).inheritCompilerOutputPath(true); + + model.commit(); + result.setResult(dep); + } + }.execute().getResultObject(); + return dep; + } + private void deleteClassFile(final String className) throws IOException { new WriteCommandAction(getProject()) { protected void run(Result result) throws Throwable { @@ -349,9 +430,13 @@ public class GroovyCompilerTest extends JavaCodeInsightFixtureTestCase { */ private void assertOutput(String className, String output) throws ExecutionException { + assertOutput(className, output, myModule); + } + + private void assertOutput(String className, String output, final Module module) throws ExecutionException { final ApplicationConfiguration configuration = new ApplicationConfiguration("app", getProject(), ApplicationConfigurationType.getInstance()); - configuration.setModule(myModule); + configuration.setModule(module); configuration.setMainClassName(className); final DefaultRunExecutor extension = Executor.EXECUTOR_EXTENSION_NAME.findExtension(DefaultRunExecutor.class); final ExecutionEnvironment environment = new ExecutionEnvironment(configuration, new RunnerSettings(null, null),null, DataManager.getInstance().getDataContext());