diff --git a/java/testFramework/src/com/intellij/testFramework/IdeaTestUtil.java b/java/testFramework/src/com/intellij/testFramework/IdeaTestUtil.java index 2ae92ce833ac..98d260e93b6e 100644 --- a/java/testFramework/src/com/intellij/testFramework/IdeaTestUtil.java +++ b/java/testFramework/src/com/intellij/testFramework/IdeaTestUtil.java @@ -32,23 +32,22 @@ public class IdeaTestUtil extends PlatformTestUtil { public static void withLevel(final Module module, final LanguageLevel level, final Runnable r) { final LanguageLevelProjectExtension projectExt = LanguageLevelProjectExtension.getInstance(module.getProject()); - final LanguageLevelModuleExtension moduleExt = LanguageLevelModuleExtension.getInstance(module); final LanguageLevel projectLevel = projectExt.getLanguageLevel(); - final LanguageLevel moduleLevel = moduleExt.getLanguageLevel(); + final LanguageLevel moduleLevel = LanguageLevelModuleExtension.getInstance(module).getLanguageLevel(); try { projectExt.setLanguageLevel(level); - setModuleLanguageLevel(level, moduleExt); + setModuleLanguageLevel(module, level); r.run(); } finally { - setModuleLanguageLevel(moduleLevel, moduleExt); + setModuleLanguageLevel(module, moduleLevel); projectExt.setLanguageLevel(projectLevel); } } - private static void setModuleLanguageLevel(final LanguageLevel level, final LanguageLevelModuleExtension moduleExt) { - final LanguageLevelModuleExtension modifiable = (LanguageLevelModuleExtension)moduleExt.getModifiableModel(true); + public static void setModuleLanguageLevel(Module module, final LanguageLevel level) { + final LanguageLevelModuleExtension modifiable = (LanguageLevelModuleExtension)LanguageLevelModuleExtension.getInstance(module).getModifiableModel(true); modifiable.setLanguageLevel(level); modifiable.commit(); } diff --git a/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index 1cb782365946..7caedba573ac 100644 --- a/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java @@ -52,23 +52,23 @@ public class GroovyBuilder extends ModuleLevelBuilder { return ExitCode.NOTHING_DONE; } - String moduleOutput = getModuleOutput(context, chunk); - String compilerOutput = getCompilerOutput(moduleOutput); + Map finalOutputs = getCanonicalModuleOutputs(context, chunk); + Map generationOutputs = getGenerationOutputs(chunk, finalOutputs); final Set toCompilePaths = new LinkedHashSet(); for (File file : toCompile) { toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath())); } - Map class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutput); + Map class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, finalOutputs); final String encoding = context.getProjectDescriptor().getEncodingConfiguration().getPreferredModuleChunkEncoding(chunk); List patchers = Collections.emptyList(); //todo patchers + String compilerOutput = generationOutputs.get(chunk.representativeModule()); final File tempFile = GroovycOSProcessHandler.fillFileWithGroovycParameters( - compilerOutput, toCompilePaths, FileUtil.toSystemDependentName(moduleOutput), class2Src, encoding, patchers + compilerOutput, toCompilePaths, finalOutputs.get(chunk.representativeModule()), class2Src, encoding, patchers ); - //todo different outputs in a chunk //todo xmx final List cmd = ExternalProcessUtil.buildJavaCommandLine( getJavaExecutable(chunk), @@ -98,13 +98,24 @@ public class GroovyBuilder extends ModuleLevelBuilder { } if (myForStubs) { - JavaBuilder.addTempSourcePathRoot(context, new File(compilerOutput)); + for (Module module : generationOutputs.keySet()) { + File root = new File(generationOutputs.get(module)); + context.getRootsIndex().associateRoot(root, module, context.isCompilingTests(), true); + JavaBuilder.addTempSourcePathRoot(context, root); + } } for (CompilerMessage message : handler.getCompilerMessages()) { context.processMessage(message); } - if (!myForStubs && updateDependencies(context, chunk, toCompile, moduleOutput, handler.getSuccessfullyCompiled())) { + + + List compiled = new ArrayList(); + for (GroovycOSProcessHandler.OutputItem item : handler.getSuccessfullyCompiled()) { + compiled.add(ensureCorrectOutput(context, chunk, item, generationOutputs, compilerOutput)); + } + + if (!myForStubs && updateDependencies(context, chunk, toCompile, generationOutputs, compiled)) { return ExitCode.ADDITIONAL_PASS_REQUIRED; } return ExitCode.OK; @@ -114,6 +125,48 @@ public class GroovyBuilder extends ModuleLevelBuilder { } } + private Map getGenerationOutputs(ModuleChunk chunk, Map finalOutputs) throws IOException { + Map generationOutputs = new HashMap(); + for (Module module : chunk.getModules()) { + generationOutputs.put(module, myForStubs ? FileUtil.createTempDirectory("groovyStubs", module.getName()).getPath() : finalOutputs.get(module)); + } + return generationOutputs; + } + + private static Map getCanonicalModuleOutputs(CompileContext context, ModuleChunk chunk) { + Map finalOutputs = new HashMap(); + for (Module module : chunk.getModules()) { + File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(module, context.isCompilingTests()); + assert moduleOutputDir != null; + String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath()); + assert moduleOutputPath != null; + finalOutputs.put(module, moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/"); + } + return finalOutputs; + } + + private static GroovycOSProcessHandler.OutputItem ensureCorrectOutput(CompileContext context, + ModuleChunk chunk, + GroovycOSProcessHandler.OutputItem item, Map generationOutputs, String compilerOutput) throws IOException { + if (chunk.getModules().size() > 1) { + RootDescriptor descriptor = context.getModuleAndRoot(new File(item.sourcePath)); + if (descriptor != null) { + Module srcModule = context.getProject().getModules().get(descriptor.module); + if (srcModule != null && srcModule != chunk.representativeModule()) { + File output = new File(item.outputPath); + + //todo honor package prefixes + File correctRoot = new File(generationOutputs.get(srcModule)); + File correctOutput = new File(correctRoot, FileUtil.getRelativePath(new File(compilerOutput), output)); + + FileUtil.rename(output, correctOutput); + return new GroovycOSProcessHandler.OutputItem(correctOutput.getPath(), item.sourcePath); + } + } + } + return item; + } + private static String getJavaExecutable(ModuleChunk chunk) { Sdk sdk = chunk.getModules().iterator().next().getSdk(); if (sdk instanceof JavaSdk) { @@ -122,23 +175,11 @@ public class GroovyBuilder extends ModuleLevelBuilder { return SystemProperties.getJavaHome() + "/bin/java"; } - private static String getModuleOutput(CompileContext context, ModuleChunk chunk) { - final Module representativeModule = chunk.getModules().iterator().next(); - File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(representativeModule, context.isCompilingTests()); - assert moduleOutputDir != null; - String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath()); - return moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/"; - } - @Override public boolean shouldHonorFileEncodingForCompilation(File file) { return isGroovyFile(file.getAbsolutePath()); } - private String getCompilerOutput(String moduleOutputDir) throws IOException { - return FileUtil.toCanonicalPath((myForStubs ? FileUtil.createTempDirectory("groovyStubs", null) : new File(moduleOutputDir)).getPath()); - } - private static List collectChangedFiles(CompileContext context, ModuleChunk chunk) throws IOException { final List toCompile = new ArrayList(); context.processFilesToRecompile(chunk, new FileProcessor() { @@ -156,7 +197,7 @@ public class GroovyBuilder extends ModuleLevelBuilder { private boolean updateDependencies(CompileContext context, ModuleChunk chunk, List toCompile, - String moduleOutputPath, + Map generationOutputs, List successfullyCompiled) throws IOException { final Mappings delta = context.createDelta(); final List successfullyCompiledFiles = new ArrayList(); @@ -172,11 +213,11 @@ public class GroovyBuilder extends ModuleLevelBuilder { if (moduleAndRoot != null) { final String moduleName = moduleAndRoot.module; context.getDataManager().getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot).appendData(sourcePath, outputPath); + String moduleOutputPath = generationOutputs.get(context.getProject().getModules().get(moduleName)); + generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/')); } callback.associate(outputPath, sourcePath, new ClassReader(FileUtil.loadFileBytes(new File(outputPath)))); successfullyCompiledFiles.add(new File(sourcePath)); - - generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/')); } context.processMessage(generatedEvent); @@ -205,9 +246,10 @@ public class GroovyBuilder extends ModuleLevelBuilder { return path.endsWith(".groovy") || path.endsWith(".gpp"); } - private static Map buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set toCompilePaths, String moduleOutputPath) throws IOException { + private static Map buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set toCompilePaths, Map finalOutputs) throws IOException { final Map class2Src = new HashMap(); for (Module module : chunk.getModules()) { + String moduleOutputPath = finalOutputs.get(module); final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(module.getName(), context.isCompilingTests()); for (String src : srcToOut.getKeys()) { if (!toCompilePaths.contains(src) && isGroovyFile(src) && 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 d2ee4fc4125e..420bfae7cabf 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy @@ -567,6 +567,42 @@ class Main { assertEmpty make() } + public void "test module cycle"() { + def dep = addDependentModule() + PsiTestUtil.addDependency(myModule, dep) + addGroovyLibrary(dep) + + myFixture.addFileToProject('Foo.groovy', 'class Foo extends Bar { static void main(String[] args) { println "Hello from Foo" } }') + myFixture.addFileToProject('FooX.java', 'class FooX extends Bar { }') + myFixture.addFileToProject("dependent/Bar.groovy", "class Bar { Foo f; static void main(String[] args) { println 'Hello from Bar' } }") + myFixture.addFileToProject("dependent/BarX.java", "class BarX { Foo f; }") + + def checkClassFiles = { + assert findClassFile('Foo', myModule) + assert findClassFile('FooX', myModule) + assert findClassFile('Bar', dep) + assert findClassFile('BarX', dep) + + assert !findClassFile('Bar', myModule) + assert !findClassFile('BarX', myModule) + assert !findClassFile('Foo', dep) + assert !findClassFile('FooX', dep) + } + + println '1' + assertEmpty make() + checkClassFiles() + + println '2' + assertEmpty make() + checkClassFiles() + + assertOutput('Foo', 'Hello from Foo', myModule) + assertOutput('Bar', 'Hello from Bar', dep) + + checkClassFiles() + } + public void testCompileTimeConstants() { myFixture.addFileToProject 'Gr.groovy', ''' interface Gr { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java index 304540b8e427..d3109934d310 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java @@ -51,6 +51,7 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiFile; +import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; @@ -184,8 +185,10 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC final ContentEntry entry = model.addContentEntry(depRoot); entry.addSourceFolder(depRoot, false); model.setSdk(ModuleRootManager.getInstance(myModule).getSdk()); - model.commit(); + + IdeaTestUtil.setModuleLanguageLevel(dep, LanguageLevelModuleExtension.getInstance(myModule).getLanguageLevel()); + result.setResult(dep); } }.execute().getResultObject(); @@ -208,9 +211,15 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC } @Nullable protected VirtualFile findClassFile(String className) { - final CompilerModuleExtension extension = ModuleRootManager.getInstance(myModule).getModuleExtension(CompilerModuleExtension.class); + return findClassFile(className, myModule); + } + @Nullable protected VirtualFile findClassFile(String className, Module module) { //noinspection ConstantConditions - return extension.getCompilerOutputPath().findChild(className + ".class"); + VirtualFile path = ModuleRootManager.getInstance(module).getModuleExtension(CompilerModuleExtension.class).getCompilerOutputPath(); + path.getChildren(); + assert path != null; + path.refresh(false, true); + return path.findChild(className + ".class"); } protected static void touch(VirtualFile file) throws IOException {