diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 3346cb92cead..2c63f5effed2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -81,13 +81,10 @@ public class JavaBuilder extends ModuleLevelBuilder { private final Executor myTaskRunner; private int myTasksInProgress = 0; private final Object myCounterLock = new Object(); - private final List myClassProcessors = new ArrayList(); + private static final List ourClassProcessors = new ArrayList(); - public JavaBuilder(Executor tasksExecutor) { - super(BuilderCategory.TRANSLATOR); - myTaskRunner = tasksExecutor; - //add here class processors in the sequence they should be executed - myClassProcessors.add(new ClassPostProcessor() { + static { + registerClassPostProcessor(new ClassPostProcessor() { public void process(CompileContext context, OutputFileObject out) { final OutputFileObject.Content content = out.getContent(); final File srcFile = out.getSourceFile(); @@ -121,6 +118,16 @@ public class JavaBuilder extends ModuleLevelBuilder { }); } + public static void registerClassPostProcessor(ClassPostProcessor processor) { + ourClassProcessors.add(processor); + } + + public JavaBuilder(Executor tasksExecutor) { + super(BuilderCategory.TRANSLATOR); + myTaskRunner = tasksExecutor; + //add here class processors in the sequence they should be executed + } + @Override public String getName() { return BUILDER_NAME; @@ -1193,7 +1200,7 @@ public class JavaBuilder extends ModuleLevelBuilder { submitAsyncTask(new Runnable() { public void run() { try { - for (ClassPostProcessor processor : myClassProcessors) { + for (ClassPostProcessor processor : ourClassProcessors) { processor.process(myCompileContext, fileObject); } } 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 95ce628ef628..ff553cc8327d 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 @@ -1,293 +1,343 @@ -package org.jetbrains.jps.incremental.groovy; - - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Consumer; -import com.intellij.util.SystemProperties; -import groovy.util.CharsetToolkit; -import org.jetbrains.asm4.ClassReader; -import org.jetbrains.ether.dependencyView.Callbacks; -import org.jetbrains.ether.dependencyView.Mappings; -import org.jetbrains.groovy.compiler.rt.GroovyCompilerWrapper; -import org.jetbrains.jps.*; -import org.jetbrains.jps.incremental.*; -import org.jetbrains.jps.incremental.fs.RootDescriptor; -import org.jetbrains.jps.incremental.messages.CompilerMessage; -import org.jetbrains.jps.incremental.messages.FileGeneratedEvent; -import org.jetbrains.jps.incremental.messages.ProgressMessage; -import org.jetbrains.jps.incremental.storage.SourceToOutputMapping; -import org.jetbrains.jps.server.ClasspathBootstrap; - -import java.io.File; -import java.io.IOException; -import java.util.*; - -/** - * @author Eugene Zhuravlev - * Date: 10/25/11 - */ -public class GroovyBuilder extends ModuleLevelBuilder { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.groovy.GroovyBuilder"); - public static final String BUILDER_NAME = "groovy"; - private static final Key CHUNK_REBUILD_ORDERED = Key.create("CHUNK_REBUILD_ORDERED"); - private final boolean myForStubs; - private final String myBuilderName; - - public GroovyBuilder(boolean forStubs) { - super(forStubs ? BuilderCategory.SOURCE_GENERATOR : BuilderCategory.OVERWRITING_TRANSLATOR); - myForStubs = forStubs; - myBuilderName = BUILDER_NAME + (forStubs ? "-stubs" : "-classes"); - } - - public String getName() { - return myBuilderName; - } - - public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException { - try { - final List toCompile = collectChangedFiles(context, chunk); - if (Utils.IS_TEST_MODE) { - LOG.info((myForStubs ? "stubs" : "groovyc") + ", toCompile=" + toCompile); - } - if (toCompile.isEmpty()) { - return ExitCode.NOTHING_DONE; - } - - 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, 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(finalOutputs.get(chunk.representativeModule())), class2Src, encoding, patchers - ); - - //todo xmx - final List cmd = ExternalProcessUtil.buildJavaCommandLine( - getJavaExecutable(chunk), - "org.jetbrains.groovy.compiler.rt.GroovycRunner", - Collections.emptyList(), new ArrayList(generateClasspath(context, chunk)), - Arrays.asList("-Xmx384m", - "-Dfile.encoding=" + CharsetToolkit.getDefaultSystemCharset().name()/*, - "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/), - Arrays.asList(myForStubs ? "stubs" : "groovyc", - tempFile.getPath()) - ); - - final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd)); - GroovycOSProcessHandler handler = GroovycOSProcessHandler.runGroovyc(process, new Consumer() { - public void consume(String s) { - context.processMessage(new ProgressMessage(s)); - } - }); - - if (!context.isProjectRebuild() && handler.shouldRetry()) { - if (CHUNK_REBUILD_ORDERED.get(context) != null) { - CHUNK_REBUILD_ORDERED.set(context, null); - } else { - CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE); - LOG.info("Order chunk rebuild"); - return ExitCode.CHUNK_REBUILD_REQUIRED; - } - } - - if (myForStubs) { - for (Module module : generationOutputs.keySet()) { - File root = new File(generationOutputs.get(module)); - context.getRootsIndex().associateRoot(root, module, context.isCompilingTests(), true, true); - } - } - - for (CompilerMessage message : handler.getCompilerMessages()) { - if (Utils.IS_TEST_MODE) { - LOG.info(message.toString()); - } - context.processMessage(message); - } - - - List compiled = new ArrayList(); - for (GroovycOSProcessHandler.OutputItem item : handler.getSuccessfullyCompiled()) { - if (Utils.IS_TEST_MODE) { - LOG.info("Compiled " + item); - } - compiled.add(ensureCorrectOutput(context, chunk, item, generationOutputs, compilerOutput)); - } - - if (!myForStubs && updateDependencies(context, chunk, toCompile, generationOutputs, compiled)) { - return ExitCode.ADDITIONAL_PASS_REQUIRED; - } - return ExitCode.OK; - } - catch (Exception e) { - throw new ProjectBuildException(e); - } - } - - 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) { - return ((JavaSdk)sdk).getJavaExecutable(); - } - return SystemProperties.getJavaHome() + "/bin/java"; - } - - @Override - public boolean shouldHonorFileEncodingForCompilation(File file) { - return isGroovyFile(file.getAbsolutePath()); - } - - private static List collectChangedFiles(CompileContext context, ModuleChunk chunk) throws IOException { - final List toCompile = new ArrayList(); - context.processFilesToRecompile(chunk, new FileProcessor() { - public boolean apply(Module module, File file, String sourceRoot) throws IOException { - final String path = file.getPath(); - if (isGroovyFile(path)) { //todo file type check - toCompile.add(file); - } - return true; - } - }); - return toCompile; - } - - private boolean updateDependencies(CompileContext context, - ModuleChunk chunk, - List toCompile, - Map generationOutputs, - List successfullyCompiled) throws IOException { - final Mappings delta = context.createDelta(); - final List successfullyCompiledFiles = new ArrayList(); - if (!successfullyCompiled.isEmpty()) { - - final Callbacks.Backend callback = delta.getCallback(); - final FileGeneratedEvent generatedEvent = new FileGeneratedEvent(); - - for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) { - final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath); - final String outputPath = FileUtil.toSystemIndependentName(item.outputPath); - final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath)); - 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)); - } - - context.processMessage(generatedEvent); - } - - - return updateMappings(context, delta, chunk, toCompile, successfullyCompiledFiles); - } - - private static List generateClasspath(CompileContext context, ModuleChunk chunk) { - final Set cp = new LinkedHashSet(); - //groovy_rt.jar - // IMPORTANT! must be the first in classpath - cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath()); - - for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.compile(context.isCompilingTests()), false)) { - cp.add(FileUtil.toCanonicalPath(file.getPath())); - } - for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.runtime(context.isCompilingTests()), false)) { - cp.add(FileUtil.toCanonicalPath(file.getPath())); - } - return new ArrayList(cp); - } - - private static boolean isGroovyFile(String path) { - return path.endsWith(".groovy") || path.endsWith(".gpp"); - } - - 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) && - !context.getProject().getCompilerConfiguration().getExcludes().isExcluded(new File(src))) { - final Collection outs = srcToOut.getState(src); - if (outs != null) { - for (String out : outs) { - if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) { - final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()).replace('/', '.'); - class2Src.put(className, src); - } - } - } - } - } - } - return class2Src; - } - - @Override - public String toString() { - return "GroovyBuilder{" + - "myForStubs=" + myForStubs + - '}'; - } - - public String getDescription() { - return "Groovy builder"; - } - -} +package org.jetbrains.jps.incremental.groovy; + + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Consumer; +import com.intellij.util.SystemProperties; +import groovy.util.CharsetToolkit; +import org.jetbrains.asm4.ClassReader; +import org.jetbrains.ether.dependencyView.Callbacks; +import org.jetbrains.ether.dependencyView.Mappings; +import org.jetbrains.groovy.compiler.rt.GroovyCompilerWrapper; +import org.jetbrains.jps.*; +import org.jetbrains.jps.incremental.*; +import org.jetbrains.jps.incremental.fs.RootDescriptor; +import org.jetbrains.jps.incremental.java.ClassPostProcessor; +import org.jetbrains.jps.incremental.java.JavaBuilder; +import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.incremental.messages.FileGeneratedEvent; +import org.jetbrains.jps.incremental.messages.ProgressMessage; +import org.jetbrains.jps.incremental.storage.SourceToOutputMapping; +import org.jetbrains.jps.javac.OutputFileObject; +import org.jetbrains.jps.server.ClasspathBootstrap; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Eugene Zhuravlev + * Date: 10/25/11 + */ +public class GroovyBuilder extends ModuleLevelBuilder { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.groovy.GroovyBuilder"); + public static final String BUILDER_NAME = "groovy"; + private static final Key CHUNK_REBUILD_ORDERED = Key.create("CHUNK_REBUILD_ORDERED"); + private static final Key> STUB_TO_SRC = Key.create("STUB_TO_SRC"); + private final boolean myForStubs; + private final String myBuilderName; + + public GroovyBuilder(boolean forStubs) { + super(forStubs ? BuilderCategory.SOURCE_GENERATOR : BuilderCategory.OVERWRITING_TRANSLATOR); + myForStubs = forStubs; + myBuilderName = BUILDER_NAME + (forStubs ? "-stubs" : "-classes"); + } + + static { + JavaBuilder.registerClassPostProcessor(new RecompileStubSources()); + } + + public String getName() { + return myBuilderName; + } + + public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException { + try { + final List toCompile = collectChangedFiles(context, chunk); + if (Utils.IS_TEST_MODE) { + LOG.info((myForStubs ? "stubs" : "groovyc") + ", toCompile=" + toCompile); + } + if (toCompile.isEmpty()) { + return ExitCode.NOTHING_DONE; + } + + 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, 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(finalOutputs.get(chunk.representativeModule())), class2Src, encoding, patchers + ); + + //todo xmx + final List cmd = ExternalProcessUtil.buildJavaCommandLine( + getJavaExecutable(chunk), + "org.jetbrains.groovy.compiler.rt.GroovycRunner", + Collections.emptyList(), new ArrayList(generateClasspath(context, chunk)), + Arrays.asList("-Xmx384m", + "-Dfile.encoding=" + CharsetToolkit.getDefaultSystemCharset().name()/*, + "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/), + Arrays.asList(myForStubs ? "stubs" : "groovyc", + tempFile.getPath()) + ); + + final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd)); + GroovycOSProcessHandler handler = GroovycOSProcessHandler.runGroovyc(process, new Consumer() { + public void consume(String s) { + context.processMessage(new ProgressMessage(s)); + } + }); + + if (!context.isProjectRebuild() && handler.shouldRetry()) { + if (CHUNK_REBUILD_ORDERED.get(context) != null) { + CHUNK_REBUILD_ORDERED.set(context, null); + } else { + CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE); + LOG.info("Order chunk rebuild"); + return ExitCode.CHUNK_REBUILD_REQUIRED; + } + } + + if (myForStubs) { + for (Module module : generationOutputs.keySet()) { + File root = new File(generationOutputs.get(module)); + context.getRootsIndex().associateRoot(root, module, context.isCompilingTests(), true, true); + } + } + + for (CompilerMessage message : handler.getCompilerMessages()) { + if (Utils.IS_TEST_MODE) { + LOG.info(message.toString()); + } + context.processMessage(message); + } + + + List compiled = new ArrayList(); + for (GroovycOSProcessHandler.OutputItem item : handler.getSuccessfullyCompiled()) { + if (Utils.IS_TEST_MODE) { + LOG.info("Compiled " + item); + } + compiled.add(ensureCorrectOutput(context, chunk, item, generationOutputs, compilerOutput)); + } + + if (myForStubs) { + Map stubToSrc = STUB_TO_SRC.get(context); + if (stubToSrc == null) { + STUB_TO_SRC.set(context, stubToSrc = new ConcurrentHashMap()); + } + for (GroovycOSProcessHandler.OutputItem item : handler.getSuccessfullyCompiled()) { + stubToSrc.put(FileUtil.toSystemIndependentName(item.outputPath), item.sourcePath); + } + } + + if (!myForStubs && updateDependencies(context, chunk, toCompile, generationOutputs, compiled)) { + return ExitCode.ADDITIONAL_PASS_REQUIRED; + } + return ExitCode.OK; + } + catch (Exception e) { + throw new ProjectBuildException(e); + } + } + + @Override + public void cleanupResources(CompileContext context, ModuleChunk chunk) { + super.cleanupResources(context, chunk); + STUB_TO_SRC.set(context, null); + } + + 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) { + return ((JavaSdk)sdk).getJavaExecutable(); + } + return SystemProperties.getJavaHome() + "/bin/java"; + } + + @Override + public boolean shouldHonorFileEncodingForCompilation(File file) { + return isGroovyFile(file.getAbsolutePath()); + } + + private static List collectChangedFiles(CompileContext context, ModuleChunk chunk) throws IOException { + final List toCompile = new ArrayList(); + context.processFilesToRecompile(chunk, new FileProcessor() { + public boolean apply(Module module, File file, String sourceRoot) throws IOException { + final String path = file.getPath(); + if (isGroovyFile(path)) { //todo file type check + toCompile.add(file); + } + return true; + } + }); + return toCompile; + } + + private boolean updateDependencies(CompileContext context, + ModuleChunk chunk, + List toCompile, + Map generationOutputs, + List successfullyCompiled) throws IOException { + final Mappings delta = context.createDelta(); + final List successfullyCompiledFiles = new ArrayList(); + if (!successfullyCompiled.isEmpty()) { + + final Callbacks.Backend callback = delta.getCallback(); + final FileGeneratedEvent generatedEvent = new FileGeneratedEvent(); + + for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) { + final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath); + final String outputPath = FileUtil.toSystemIndependentName(item.outputPath); + final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath)); + 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)); + } + + context.processMessage(generatedEvent); + } + + + return updateMappings(context, delta, chunk, toCompile, successfullyCompiledFiles); + } + + private static List generateClasspath(CompileContext context, ModuleChunk chunk) { + final Set cp = new LinkedHashSet(); + //groovy_rt.jar + // IMPORTANT! must be the first in classpath + cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath()); + + for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.compile(context.isCompilingTests()), false)) { + cp.add(FileUtil.toCanonicalPath(file.getPath())); + } + for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.runtime(context.isCompilingTests()), false)) { + cp.add(FileUtil.toCanonicalPath(file.getPath())); + } + return new ArrayList(cp); + } + + private static boolean isGroovyFile(String path) { + return path.endsWith(".groovy") || path.endsWith(".gpp"); + } + + 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) && + !context.getProject().getCompilerConfiguration().getExcludes().isExcluded(new File(src))) { + final Collection outs = srcToOut.getState(src); + if (outs != null) { + for (String out : outs) { + if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) { + final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()).replace('/', '.'); + class2Src.put(className, src); + } + } + } + } + } + } + return class2Src; + } + + @Override + public String toString() { + return "GroovyBuilder{" + + "myForStubs=" + myForStubs + + '}'; + } + + public String getDescription() { + return "Groovy builder"; + } + + private static class RecompileStubSources implements ClassPostProcessor { + + public void process(CompileContext context, OutputFileObject out) { + Map stubToSrc = STUB_TO_SRC.get(context); + if (stubToSrc == null) { + return; + } + + File src = out.getSourceFile(); + if (src == null) { + return; + } + String groovy = stubToSrc.get(FileUtil.toSystemIndependentName(src.getPath())); + if (groovy == null) { + return; + } + + try { + context.markDirty(new File(groovy)); + } + catch (IOException e) { + LOG.error(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 ed4858856906..22c83c8afc90 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy @@ -1,786 +1,793 @@ -/* - * Copyright 2000-2009 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.plugins.groovy.compiler; - - -import com.intellij.compiler.CompileServerManager -import com.intellij.compiler.CompilerConfiguration -import com.intellij.compiler.CompilerConfigurationImpl -import com.intellij.openapi.application.PathManager -import com.intellij.openapi.compiler.options.ExcludeEntryDescription -import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration -import com.intellij.openapi.module.Module -import com.intellij.openapi.roots.ModuleRootModificationUtil -import com.intellij.openapi.util.io.FileUtil -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.openapi.vfs.newvfs.BulkFileListener -import com.intellij.openapi.vfs.newvfs.events.VFileEvent -import com.intellij.psi.PsiFile -import com.intellij.testFramework.TestLoggerFactory -import junit.framework.AssertionFailedError - -/** - * @author peter - */ -public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { - @Override protected void setUp() { - super.setUp(); - addGroovyLibrary(myModule); - } - - public void testPlainGroovy() throws Throwable { - myFixture.addFileToProject("A.groovy", "println '239'"); - assertEmpty(make()); - assertOutput("A", "239"); - } - - public void testJavaDependsOnGroovy() throws Throwable { - myFixture.addClass("public class Foo {" + - "public static void main(String[] args) { " + - " System.out.println(new Bar().foo());" + - "}" + - "}"); - myFixture.addFileToProject("Bar.groovy", "class Bar {" + - " def foo() {" + - " 239" + - " }" + - "}"); - make(); - assertOutput("Foo", "239"); - } - - public void testCorrectFailAndCorrect() throws Exception { - myFixture.addClass("public class Foo {" + - "public static void main(String[] args) { " + - " System.out.println(new Bar().foo());" + - "}" + - "}"); - final String barText = "class Bar {" + " def foo() { 239 }" + "}"; - final PsiFile file = myFixture.addFileToProject("Bar.groovy", barText); - make() - assertOutput("Foo", "239"); - - setFileText(file, "class Bar {}"); - shouldFail { make() } - - setFileText(file, barText); - make(); - assertOutput("Foo", "239"); - } - - private void shouldFail(Closure action) { - try { - action() - fail("Make should fail"); - } - catch (RuntimeException e) { - if (!(e.getCause() instanceof AssertionFailedError)) { - throw e; - } - } - } - - public void testRenameToJava() throws Throwable { - myFixture.addClass("public class Foo {" + - "public static void main(String[] args) { " + - " System.out.println(new Bar().foo());" + - "}" + - "}"); - - final PsiFile bar = - myFixture.addFileToProject("Bar.groovy", "public class Bar {" + "public int foo() { " + " return 239;" + "}" + "}"); - - make(); - assertOutput("Foo", "239"); - - setFileName bar, "Bar.java" - - make(); - assertOutput("Foo", "239"); - } - - public void testTransitiveJavaDependency() throws Throwable { - final VirtualFile ifoo = myFixture.addClass("public interface IFoo { int foo(); }").getContainingFile().getVirtualFile(); - myFixture.addClass("public class Foo implements IFoo {" + - " public int foo() { return 239; }" + - "}"); - final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" + - "Foo foo\n" + - "public static void main(String[] args) { " + - " System.out.println(new Foo().foo());" + - "}" + - "}"); - assertEmpty(make()); - assertOutput("Bar", "239"); - - touch(ifoo); - touch(bar.getVirtualFile()); - - //assertTrue(assertOneElement(make()).contains("WARNING: Groovyc stub generation failed")); - assertEmpty make() - assertOutput("Bar", "239"); - } - - public void testTransitiveJavaDependencyThroughGroovy() throws Throwable { - myFixture.addClass("public class IFoo { void foo() {} }").getContainingFile().getVirtualFile(); - myFixture.addFileToProject("Foo.groovy", "class Foo {\n" + - " static IFoo f\n" + - " public int foo() { return 239; }\n" + - "}"); - final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar extends Foo {" + - "public static void main(String[] args) { " + - " System.out.println(new Foo().foo());" + - "}" + - "}"); - assertEmpty(make()); - assertOutput("Bar", "239"); - - deleteClassFile("IFoo"); - touch(bar.getVirtualFile()); - - //assertTrue(assertOneElement(make()).contains("WARNING: Groovyc error")); - assertEmpty make() - assertOutput("Bar", "239"); - } - - public void testTransitiveGroovyDependency() throws Throwable { - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo {} ') - def bar = myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo {}') - def goo = myFixture.addFileToProject('Goo.groovy', 'class Goo extends Bar {}') - assertEmpty(make()); - - touch(foo.virtualFile) - touch(goo.virtualFile) - assertEmpty(make()); - } - - public void testJavaDependsOnGroovyEnum() throws Throwable { - myFixture.addFileToProject("Foo.groovy", "enum Foo { FOO }") - myFixture.addClass("class Bar { Foo f; }") - assertEmpty(make()) - } - - public void testDeleteTransitiveJavaClass() throws Throwable { - myFixture.addClass("public interface IFoo { int foo(); }"); - myFixture.addClass("public class Foo implements IFoo {" + - " public int foo() { return 239; }" + - "}"); - final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" + - "Foo foo\n" + - "public static void main(String[] args) { " + - " System.out.println(new Foo().foo());" + - "}" + - "}"); - assertEmpty(make()); - assertOutput("Bar", "239"); - - deleteClassFile("IFoo"); - touch(bar.getVirtualFile()); - - //assertTrue(assertOneElement(make()).contains("WARNING: Groovyc stub generation failed")); - assertEmpty make() - assertOutput("Bar", "239"); - } - - public void testGroovyDependsOnGroovy() throws Throwable { - myFixture.addClass("public class JustToMakeGroovyGenerateStubs {}"); - myFixture.addFileToProject("Foo.groovy", "class Foo { }"); - final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" + - "def foo(Foo f) {}\n" + - "public static void main(String[] args) { " + - " System.out.println(239);" + - "}" + - "}"); - assertEmpty(make()); - assertOutput("Bar", "239"); - - touch(bar.getVirtualFile()); - - assertEmpty(make()); - assertOutput("Bar", "239"); - } - - @Override - void runBare() { - def ideaLog = new File(TestLoggerFactory.testLogDir, "idea.log") - def makeLog = new File(PathManager.systemPath, "compile-server/server.log") - if (ideaLog.exists()) { - FileUtil.delete(ideaLog) - } - if (makeLog.exists()) { - FileUtil.delete(makeLog) - } - - try { - super.runBare() - } - catch (Throwable e) { - if (ideaLog.exists()) { - println "Idea Log:" - println ideaLog.text - } - if (makeLog.exists()) { - println "Server Log:" - println makeLog.text - } - throw e - } - finally { - System.out.flush() - } - } - - public void testMakeInTests() throws Throwable { - setupTestSources(); - myFixture.project.messageBus.connect(testRootDisposable).subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { - @Override - void before(List events) { - } - - @Override - void after(List events) { - println ("" + Thread.currentThread() + " " + events) - } - }) - myFixture.addFileToProject("tests/Super.groovy", "class Super {}"); - assertEmpty(make()); - - println 'after first make ' + Thread.currentThread() - - def sub = myFixture.addFileToProject("tests/Sub.groovy", "class Sub {\n" + - " Super xxx() {}\n" + - " static void main(String[] args) {" + - " println 'hello'" + - " }" + - "}"); - - def javaFile = myFixture.addFileToProject("tests/Java.java", "public class Java {}"); - edt { println 'before second make' } - - assertEmpty(make()); - assertOutput("Sub", "hello"); - } - - public void testTestsDependOnProduction() throws Throwable { - setupTestSources(); - myFixture.addFileToProject("src/com/Bar.groovy", "package com\n" + - "class Bar {}"); - myFixture.addFileToProject("src/com/ToGenerateStubs.java", "package com;\n" + - "public class ToGenerateStubs {}"); - myFixture.addFileToProject("tests/com/BarTest.groovy", "package com\n" + - "class BarTest extends Bar {}"); - assertEmpty(make()); - } - - public void testStubForGroovyExtendingJava() throws Exception { - myFixture.addClass("public class Foo {}"); - myFixture.addFileToProject("Bar.groovy", "class Bar extends Foo {}"); - myFixture.addClass("public class Goo extends Bar {}"); - - 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.* -import org.codehaus.groovy.control.* -import org.codehaus.groovy.transform.* -@GroovyASTTransformation(phase = CompilePhase.CONVERSION) -public class Transf implements ASTTransformation { - void visit(ASTNode[] nodes, SourceUnit sourceUnit) { - ModuleNode module = nodes[0] - for (clazz in module.classes) { - if (clazz.name.contains('Bar')) { - module.addStaticStarImport('Foo', ClassHelper.makeWithoutCaching(Foo.class)) - } - } - } -}"""); - - 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); - } - - public void testIndirectDependencies() throws Exception { - myFixture.addFileToProject("dependent1/Bar1.groovy", "class Bar1 {}"); - myFixture.addFileToProject("dependent2/Bar2.groovy", "class Bar2 extends Bar1 {}"); - PsiFile main = myFixture.addFileToProject("Main.groovy", "class Main extends Bar2 {}"); - - Module dep1 = addModule('dependent1') - Module dep2 = addModule('dependent2') - ModuleRootModificationUtil.addDependency dep2, dep1 - ModuleRootModificationUtil.addDependency myModule, dep2 - - addGroovyLibrary(dep1); - addGroovyLibrary(dep2); - - assertEmpty(make()) - - touch(main.virtualFile) - assertEmpty(make()) - } - - public void testExtendFromGroovyAbstractClass() throws Exception { - myFixture.addFileToProject "Super.groovy", "abstract class Super {}" - myFixture.addFileToProject "AJava.java", "public class AJava {}" - assertEmpty make() - - myFixture.addFileToProject "Sub.groovy", "class Sub extends Super {}" - assertEmpty make() - } - - public void test1_7InnerClass() throws Exception { - myFixture.addFileToProject "Foo.groovy", """ -class Foo { - static class Bar {} -}""" - def javaFile = myFixture.addFileToProject("AJava.java", "public class AJava extends Foo.Bar {}") - assertEmpty make() - - touch(javaFile.virtualFile) - assertEmpty make() - } - - public void testRecompileDependentClass() throws Exception { - def cloud = myFixture.addFileToProject("Cloud.groovy", """ -class Cloud { - def accessFooProperty(Foo c) { - c.prop = 2 - } -} -""") - myFixture.addFileToProject "Foo.groovy", """ -class Foo { - def withGooParameter(Goo x) {} -}""" - def goo = myFixture.addFileToProject("Goo.groovy", "class Goo {}") - - assertEmpty make() - - touch(cloud.virtualFile) - touch(goo.virtualFile) - assertEmpty make() - } - - public void testRecompileExpressionReferences() throws Exception { - def rusCon = myFixture.addFileToProject('RusCon.groovy', ''' -interface RusCon { - Closure foo = { Seq.foo() } -}''') - myFixture.addFileToProject "Seq.groovy", """ -class Seq implements RusCon { - static def foo() { } -}""" - assertEmpty make() - - touch(rusCon.virtualFile) - assertEmpty make() - } - - public void testRecompileImportedClass() throws Exception { - def bar = myFixture.addFileToProject("pack/Bar.groovy", """ -package pack -import pack.Foo -class Bar {} -""") - myFixture.addFileToProject "pack/Foo.groovy", """ -package pack -class Foo extends Goo { -}""" - def goo = myFixture.addFileToProject("pack/Goo.groovy", """ -package pack -class Goo {}""") - - assertEmpty make() - - touch(bar.virtualFile) - touch(goo.virtualFile) - assertEmpty make() - } - - public void testRecompileDependentClassesWithOnlyOneChanged() throws Exception { - def bar = myFixture.addFileToProject("Bar.groovy", """ -class Bar { - Foo f -} -""") - myFixture.addFileToProject "Foo.groovy", """ -class Foo extends Bar { -}""" - - assertEmpty make() - - touch(bar.virtualFile) - assertEmpty make() - } - - public void testDollarGroovyInnerClassUsagesInStubs() throws Exception { - def javaFile = myFixture.addClass(""" - public class JavaClass { - public static class InnerJavaClass {} - } -""") - myFixture.addFileToProject("WithInner.groovy", """ -class WithInner { - static class Inner {} -} -""") - assertEmpty make() - - myFixture.addFileToProject("Usage.groovy", """ -class Usage { - def foo(WithInner.Inner i) {} - def foo(JavaClass.InnerJavaClass i) {} -} -""") - - touch(javaFile.containingFile.virtualFile) - assertEmpty make() - } - - public void testDollarGroovyInnerClassUsagesInStubs2() throws Exception { - myFixture.addClass(""" public class JavaClass { } """) - myFixture.addFileToProject("WithInner.groovy", """ -class WithInner { - static class Inner {} -} -""") - - myFixture.addFileToProject("Usage.groovy", """ -class Usage { - def foo(WithInner.Inner i) {} -} -""") - - assertEmpty make() - } - - public void testGroovyAnnotations() { - myFixture.addClass 'public @interface Anno { Class[] value(); }' - myFixture.addFileToProject 'Foo.groovy', '@Anno([String]) class Foo {}' - myFixture.addFileToProject 'Bar.java', 'class Bar extends Foo {}' - - assertEmpty make() - } - - public void testGenericStubs() { - myFixture.addFileToProject 'Foo.groovy', 'class Foo { List list }' - myFixture.addFileToProject 'Bar.java', 'class Bar {{ for (String s : new Foo().getList()) {} }}' - assertEmpty make() - } - - public void testDuplicateClassDuringCompilation() throws Exception { - def base = myFixture.addFileToProject('p/Base.groovy', 'package p; class Base { }').virtualFile - myFixture.addFileToProject('p/Indirect.groovy', '''package p -class Indirect { - private static class Inner { Base b } - - private Indirect.Inner foo(Indirect.Inner g1, Inner g2, Base b) {} - }''').virtualFile - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo { p.Indirect foo() {} }').virtualFile - assertEmpty make() - - touch(foo) - touch(base) - assertEmpty make() - } - - public void testDontRecompileUnneeded() { - myFixture.addFileToProject('Base.groovy', 'class Base { }') - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo extends Base { }').virtualFile - myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo { }') - def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Bar { }').virtualFile - assertEmpty make() - long oldBaseStamp = findClassFile("Base").modificationStamp - long oldMainStamp = findClassFile("Main").modificationStamp - - touch(main) - touch(foo) - assertEmpty make() - assert oldMainStamp != findClassFile("Main").modificationStamp - assert oldBaseStamp == findClassFile("Base").modificationStamp - } - - public void testPartialCrossRecompile() { - def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') - def java = myFixture.addFileToProject('Java.java', 'class Java { void foo(Used used) {} }') - def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Java { }').virtualFile - - assertEmpty compileModule(myModule) - if (!useJps()) { - assertEmpty compileFiles(used.virtualFile, main) - } - - touch(used.virtualFile) - touch(main) - assertEmpty make() - - assertEmpty compileModule(myModule) - assertEmpty compileModule(myModule) - - setFileText(used, 'class Used2 {}') - shouldFail { make() } - assert findClassFile('Used') == null - - setFileText(used, 'class Used3 {}') - setFileText(java, 'class Java { void foo(Used3 used) {} }') - assertEmpty make() - - assert findClassFile('Used2') == null - } - - public void testClassLoadingDuringBytecodeGeneration() { - def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') - def java = myFixture.addFileToProject('Java.java', ''' -abstract class Java { - Object getProp() { return null; } - abstract void foo(Used used); -}''') - def main = myFixture.addFileToProject('Main.groovy', ''' -class Main { - def foo(Java j) { - return j.prop - } -}''').virtualFile - - assertEmpty make() - - touch(used.virtualFile) - touch(main) - assertEmpty make() - } - - public void testMakeInDependentModuleAfterChunkRebuild() { - def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') - def java = myFixture.addFileToProject('Java.java', 'class Java { void foo(Used used) {} }') - def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Java { }').virtualFile - - addGroovyLibrary(addDependentModule()) - - def dep = myFixture.addFileToProject("dependent/Dep.java", "class Dep { }") - - assertEmpty make() - - setFileText(used, 'class Used { String prop }') - touch(main) - setFileText(dep, 'class Dep { String prop = new Used().getProp(); }') - - assertEmpty make() - } - - public void "test module cycle"() { - def dep = addDependentModule() - ModuleRootModificationUtil.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 make().join('\n') - checkClassFiles() - - println make().join('\n') - checkClassFiles() - - assertOutput('Foo', 'Hello from Foo', myModule) - assertOutput('Bar', 'Hello from Bar', dep) - - checkClassFiles() - } - - public void testCompileTimeConstants() { - myFixture.addFileToProject 'Gr.groovy', ''' -interface Gr { - String HELLO = "Hello" - int MAGIC = 239 - Boolean BOOL = true - boolean bool = true -}''' - myFixture.addFileToProject 'Main.java', ''' -public class Main { - public static void main(String[] args) { - System.out.println(Gr.HELLO + ", " + Gr.BOOL + Gr.bool + Gr.MAGIC); - } -} -''' - make() - assertOutput 'Main', 'Hello, truetrue239' - } - - public void "test reporting rebuild errors caused by missing files excluded from compilation"() { - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo {}') - myFixture.addFileToProject 'Bar.groovy', 'class Bar extends Foo {}' - - make() - - excludeFromCompilation(foo) - - shouldFail { rebuild() } - } - - private void excludeFromCompilation(PsiFile foo) { - final ExcludedEntriesConfiguration configuration = - ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getExcludedEntriesConfiguration() - configuration.addExcludeEntryDescription(new ExcludeEntryDescription(foo.virtualFile, false, true, testRootDisposable)) - CompileServerManager.instance.shutdownServer() - } - - public void "test make stub-level error and correct it"() { - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo { }') - myFixture.addFileToProject('Bar.java', 'class Bar extends Foo {}') - - assertEmpty make() - - setFileText(foo, 'class Foo implements Runnabl {}') - - shouldFail { make() } - - setFileText(foo, 'class Foo {}') - - assertEmpty make() - } - - //todo jeka: when recompiling module, delete all class files including those with excluded source - public void "_test reporting module compile errors caused by missing files excluded from compilation"() { - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo {}') - myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo {}') - - make() - - excludeFromCompilation(foo) - - shouldFail { compileModule(myModule) } - } - - public void "test stubs generated while processing groovy class file dependencies"() { - def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo { }') - def bar = myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo { }') - def client = myFixture.addFileToProject('Client.groovy', 'class Client { Bar bar = new Bar() }') - def java = myFixture.addFileToProject('Java.java', 'class Java extends Client { String getName(Bar bar) { return bar.toString(); } }') - - assertEmpty(make()) - - setFileText(bar, 'class Bar { }') - - assertEmpty(make()) - assert findClassFile("Client") - } - - public void "test ignore groovy internal non-existent interface helper inner class"() { - myFixture.addFileToProject 'Foo.groovy', ''' -interface Foo {} - -class Zoo { - Foo foo() {} - static class Inner implements Foo {} -} - -''' - def bar = myFixture.addFileToProject('Bar.groovy', 'class Bar { def foo = new Zoo.Inner() {} }') - - assertEmpty make() - assertEmpty compileFiles(bar.virtualFile) - } - - public void "test multiline strings"() { - myFixture.addFileToProject 'Foo.groovy', '''class Foo { - public static final String s = """ -multi -line -string -""" - } ''' - myFixture.addFileToProject 'Bar.java', 'class Bar extends Foo {} ' - - assertEmpty make() - } - - public static class IdeaModeTest extends GroovyCompilerTest { - @Override protected boolean useJps() { false } - } - - public static class JpsModeTest extends GroovyCompilerTest { - @Override protected boolean useJps() { true } - - @Override - protected void tearDown() { - File systemRoot = CompileServerManager.getInstance().getCompileServerSystemRoot() - try { - super.tearDown() - } - finally { - File[] files = systemRoot.listFiles() - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - FileUtil.delete(file); - } - } - } - } - } - } -} +/* + * Copyright 2000-2009 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.plugins.groovy.compiler; + + +import com.intellij.compiler.CompileServerManager +import com.intellij.compiler.CompilerConfiguration +import com.intellij.compiler.CompilerConfigurationImpl +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.compiler.options.ExcludeEntryDescription +import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.newvfs.BulkFileListener +import com.intellij.openapi.vfs.newvfs.events.VFileEvent +import com.intellij.psi.PsiFile +import com.intellij.testFramework.TestLoggerFactory +import junit.framework.AssertionFailedError + +/** + * @author peter + */ +public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { + @Override protected void setUp() { + super.setUp(); + addGroovyLibrary(myModule); + } + + public void testPlainGroovy() throws Throwable { + myFixture.addFileToProject("A.groovy", "println '239'"); + assertEmpty(make()); + assertOutput("A", "239"); + } + + public void testJavaDependsOnGroovy() throws Throwable { + myFixture.addClass("public class Foo {" + + "public static void main(String[] args) { " + + " System.out.println(new Bar().foo());" + + "}" + + "}"); + myFixture.addFileToProject("Bar.groovy", "class Bar {" + + " def foo() {" + + " 239" + + " }" + + "}"); + make(); + assertOutput("Foo", "239"); + } + + public void testCorrectFailAndCorrect() throws Exception { + myFixture.addClass("public class Foo {" + + "public static void main(String[] args) { " + + " System.out.println(new Bar().foo());" + + "}" + + "}"); + final String barText = "class Bar {" + " def foo() { 239 }" + "}"; + final PsiFile file = myFixture.addFileToProject("Bar.groovy", barText); + make() + assertOutput("Foo", "239"); + + setFileText(file, "class Bar {}"); + shouldFail { make() } + + setFileText(file, barText); + make(); + assertOutput("Foo", "239"); + } + + private void shouldFail(Closure action) { + try { + action() + fail("Make should fail"); + } + catch (RuntimeException e) { + if (!(e.getCause() instanceof AssertionFailedError)) { + throw e; + } + } + } + + public void testRenameToJava() throws Throwable { + myFixture.addClass("public class Foo {" + + "public static void main(String[] args) { " + + " System.out.println(new Bar().foo());" + + "}" + + "}"); + + final PsiFile bar = + myFixture.addFileToProject("Bar.groovy", "public class Bar {" + "public int foo() { " + " return 239;" + "}" + "}"); + + make(); + assertOutput("Foo", "239"); + + setFileName bar, "Bar.java" + + make(); + assertOutput("Foo", "239"); + } + + public void testTransitiveJavaDependency() throws Throwable { + final VirtualFile ifoo = myFixture.addClass("public interface IFoo { int foo(); }").getContainingFile().getVirtualFile(); + myFixture.addClass("public class Foo implements IFoo {" + + " public int foo() { return 239; }" + + "}"); + final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" + + "Foo foo\n" + + "public static void main(String[] args) { " + + " System.out.println(new Foo().foo());" + + "}" + + "}"); + assertEmpty(make()); + assertOutput("Bar", "239"); + + touch(ifoo); + touch(bar.getVirtualFile()); + + //assertTrue(assertOneElement(make()).contains("WARNING: Groovyc stub generation failed")); + assertEmpty make() + assertOutput("Bar", "239"); + } + + public void testTransitiveJavaDependencyThroughGroovy() throws Throwable { + myFixture.addClass("public class IFoo { void foo() {} }").getContainingFile().getVirtualFile(); + myFixture.addFileToProject("Foo.groovy", "class Foo {\n" + + " static IFoo f\n" + + " public int foo() { return 239; }\n" + + "}"); + final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar extends Foo {" + + "public static void main(String[] args) { " + + " System.out.println(new Foo().foo());" + + "}" + + "}"); + assertEmpty(make()); + assertOutput("Bar", "239"); + + deleteClassFile("IFoo"); + touch(bar.getVirtualFile()); + + //assertTrue(assertOneElement(make()).contains("WARNING: Groovyc error")); + assertEmpty make() + assertOutput("Bar", "239"); + } + + public void testTransitiveGroovyDependency() throws Throwable { + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo {} ') + def bar = myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo {}') + def goo = myFixture.addFileToProject('Goo.groovy', 'class Goo extends Bar {}') + assertEmpty(make()); + + touch(foo.virtualFile) + touch(goo.virtualFile) + assertEmpty(make()); + } + + public void testJavaDependsOnGroovyEnum() throws Throwable { + myFixture.addFileToProject("Foo.groovy", "enum Foo { FOO }") + myFixture.addClass("class Bar { Foo f; }") + assertEmpty(make()) + } + + public void testDeleteTransitiveJavaClass() throws Throwable { + myFixture.addClass("public interface IFoo { int foo(); }"); + myFixture.addClass("public class Foo implements IFoo {" + + " public int foo() { return 239; }" + + "}"); + final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" + + "Foo foo\n" + + "public static void main(String[] args) { " + + " System.out.println(new Foo().foo());" + + "}" + + "}"); + assertEmpty(make()); + assertOutput("Bar", "239"); + + deleteClassFile("IFoo"); + touch(bar.getVirtualFile()); + + //assertTrue(assertOneElement(make()).contains("WARNING: Groovyc stub generation failed")); + assertEmpty make() + assertOutput("Bar", "239"); + } + + public void testGroovyDependsOnGroovy() throws Throwable { + myFixture.addClass("public class JustToMakeGroovyGenerateStubs {}"); + myFixture.addFileToProject("Foo.groovy", "class Foo { }"); + final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" + + "def foo(Foo f) {}\n" + + "public static void main(String[] args) { " + + " System.out.println(239);" + + "}" + + "}"); + assertEmpty(make()); + assertOutput("Bar", "239"); + + touch(bar.getVirtualFile()); + + assertEmpty(make()); + assertOutput("Bar", "239"); + } + + @Override + void runBare() { + def ideaLog = new File(TestLoggerFactory.testLogDir, "idea.log") + def makeLog = new File(PathManager.systemPath, "compile-server/server.log") + if (ideaLog.exists()) { + FileUtil.delete(ideaLog) + } + if (makeLog.exists()) { + FileUtil.delete(makeLog) + } + + try { + super.runBare() + } + catch (Throwable e) { + if (ideaLog.exists()) { + println "Idea Log:" + println ideaLog.text + } + if (makeLog.exists()) { + println "Server Log:" + println makeLog.text + } + throw e + } + finally { + System.out.flush() + } + } + + public void testMakeInTests() throws Throwable { + setupTestSources(); + myFixture.project.messageBus.connect(testRootDisposable).subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { + @Override + void before(List events) { + } + + @Override + void after(List events) { + println ("" + Thread.currentThread() + " " + events) + } + }) + myFixture.addFileToProject("tests/Super.groovy", "class Super {}"); + assertEmpty(make()); + + println 'after first make ' + Thread.currentThread() + + def sub = myFixture.addFileToProject("tests/Sub.groovy", "class Sub {\n" + + " Super xxx() {}\n" + + " static void main(String[] args) {" + + " println 'hello'" + + " }" + + "}"); + + def javaFile = myFixture.addFileToProject("tests/Java.java", "public class Java {}"); + edt { println 'before second make' } + + assertEmpty(make()); + assertOutput("Sub", "hello"); + } + + public void testTestsDependOnProduction() throws Throwable { + setupTestSources(); + myFixture.addFileToProject("src/com/Bar.groovy", "package com\n" + + "class Bar {}"); + myFixture.addFileToProject("src/com/ToGenerateStubs.java", "package com;\n" + + "public class ToGenerateStubs {}"); + myFixture.addFileToProject("tests/com/BarTest.groovy", "package com\n" + + "class BarTest extends Bar {}"); + assertEmpty(make()); + } + + public void testStubForGroovyExtendingJava() throws Exception { + def foo = myFixture.addFileToProject("Foo.groovy", "class Foo extends Goo { }"); + myFixture.addFileToProject("Goo.groovy", "class Goo extends Main { void bar() { println 'hello' } }"); + def main = myFixture.addClass("public class Main { public static void main(String[] args) { new Goo().bar(); } }"); + + assertEmpty(make()); + assertOutput 'Main', 'hello' + + touch(foo.virtualFile) + touch(main.containingFile.virtualFile) + assertEmpty(make()); + + assertOutput 'Main', 'hello' + } + + 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.* +import org.codehaus.groovy.control.* +import org.codehaus.groovy.transform.* +@GroovyASTTransformation(phase = CompilePhase.CONVERSION) +public class Transf implements ASTTransformation { + void visit(ASTNode[] nodes, SourceUnit sourceUnit) { + ModuleNode module = nodes[0] + for (clazz in module.classes) { + if (clazz.name.contains('Bar')) { + module.addStaticStarImport('Foo', ClassHelper.makeWithoutCaching(Foo.class)) + } + } + } +}"""); + + 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); + } + + public void testIndirectDependencies() throws Exception { + myFixture.addFileToProject("dependent1/Bar1.groovy", "class Bar1 {}"); + myFixture.addFileToProject("dependent2/Bar2.groovy", "class Bar2 extends Bar1 {}"); + PsiFile main = myFixture.addFileToProject("Main.groovy", "class Main extends Bar2 {}"); + + Module dep1 = addModule('dependent1') + Module dep2 = addModule('dependent2') + ModuleRootModificationUtil.addDependency dep2, dep1 + ModuleRootModificationUtil.addDependency myModule, dep2 + + addGroovyLibrary(dep1); + addGroovyLibrary(dep2); + + assertEmpty(make()) + + touch(main.virtualFile) + assertEmpty(make()) + } + + public void testExtendFromGroovyAbstractClass() throws Exception { + myFixture.addFileToProject "Super.groovy", "abstract class Super {}" + myFixture.addFileToProject "AJava.java", "public class AJava {}" + assertEmpty make() + + myFixture.addFileToProject "Sub.groovy", "class Sub extends Super {}" + assertEmpty make() + } + + public void test1_7InnerClass() throws Exception { + myFixture.addFileToProject "Foo.groovy", """ +class Foo { + static class Bar {} +}""" + def javaFile = myFixture.addFileToProject("AJava.java", "public class AJava extends Foo.Bar {}") + assertEmpty make() + + touch(javaFile.virtualFile) + assertEmpty make() + } + + public void testRecompileDependentClass() throws Exception { + def cloud = myFixture.addFileToProject("Cloud.groovy", """ +class Cloud { + def accessFooProperty(Foo c) { + c.prop = 2 + } +} +""") + myFixture.addFileToProject "Foo.groovy", """ +class Foo { + def withGooParameter(Goo x) {} +}""" + def goo = myFixture.addFileToProject("Goo.groovy", "class Goo {}") + + assertEmpty make() + + touch(cloud.virtualFile) + touch(goo.virtualFile) + assertEmpty make() + } + + public void testRecompileExpressionReferences() throws Exception { + def rusCon = myFixture.addFileToProject('RusCon.groovy', ''' +interface RusCon { + Closure foo = { Seq.foo() } +}''') + myFixture.addFileToProject "Seq.groovy", """ +class Seq implements RusCon { + static def foo() { } +}""" + assertEmpty make() + + touch(rusCon.virtualFile) + assertEmpty make() + } + + public void testRecompileImportedClass() throws Exception { + def bar = myFixture.addFileToProject("pack/Bar.groovy", """ +package pack +import pack.Foo +class Bar {} +""") + myFixture.addFileToProject "pack/Foo.groovy", """ +package pack +class Foo extends Goo { +}""" + def goo = myFixture.addFileToProject("pack/Goo.groovy", """ +package pack +class Goo {}""") + + assertEmpty make() + + touch(bar.virtualFile) + touch(goo.virtualFile) + assertEmpty make() + } + + public void testRecompileDependentClassesWithOnlyOneChanged() throws Exception { + def bar = myFixture.addFileToProject("Bar.groovy", """ +class Bar { + Foo f +} +""") + myFixture.addFileToProject "Foo.groovy", """ +class Foo extends Bar { +}""" + + assertEmpty make() + + touch(bar.virtualFile) + assertEmpty make() + } + + public void testDollarGroovyInnerClassUsagesInStubs() throws Exception { + def javaFile = myFixture.addClass(""" + public class JavaClass { + public static class InnerJavaClass {} + } +""") + myFixture.addFileToProject("WithInner.groovy", """ +class WithInner { + static class Inner {} +} +""") + assertEmpty make() + + myFixture.addFileToProject("Usage.groovy", """ +class Usage { + def foo(WithInner.Inner i) {} + def foo(JavaClass.InnerJavaClass i) {} +} +""") + + touch(javaFile.containingFile.virtualFile) + assertEmpty make() + } + + public void testDollarGroovyInnerClassUsagesInStubs2() throws Exception { + myFixture.addClass(""" public class JavaClass { } """) + myFixture.addFileToProject("WithInner.groovy", """ +class WithInner { + static class Inner {} +} +""") + + myFixture.addFileToProject("Usage.groovy", """ +class Usage { + def foo(WithInner.Inner i) {} +} +""") + + assertEmpty make() + } + + public void testGroovyAnnotations() { + myFixture.addClass 'public @interface Anno { Class[] value(); }' + myFixture.addFileToProject 'Foo.groovy', '@Anno([String]) class Foo {}' + myFixture.addFileToProject 'Bar.java', 'class Bar extends Foo {}' + + assertEmpty make() + } + + public void testGenericStubs() { + myFixture.addFileToProject 'Foo.groovy', 'class Foo { List list }' + myFixture.addFileToProject 'Bar.java', 'class Bar {{ for (String s : new Foo().getList()) {} }}' + assertEmpty make() + } + + public void testDuplicateClassDuringCompilation() throws Exception { + def base = myFixture.addFileToProject('p/Base.groovy', 'package p; class Base { }').virtualFile + myFixture.addFileToProject('p/Indirect.groovy', '''package p +class Indirect { + private static class Inner { Base b } + + private Indirect.Inner foo(Indirect.Inner g1, Inner g2, Base b) {} + }''').virtualFile + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo { p.Indirect foo() {} }').virtualFile + assertEmpty make() + + touch(foo) + touch(base) + assertEmpty make() + } + + public void testDontRecompileUnneeded() { + myFixture.addFileToProject('Base.groovy', 'class Base { }') + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo extends Base { }').virtualFile + myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo { }') + def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Bar { }').virtualFile + assertEmpty make() + long oldBaseStamp = findClassFile("Base").modificationStamp + long oldMainStamp = findClassFile("Main").modificationStamp + + touch(main) + touch(foo) + assertEmpty make() + assert oldMainStamp != findClassFile("Main").modificationStamp + assert oldBaseStamp == findClassFile("Base").modificationStamp + } + + public void testPartialCrossRecompile() { + def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') + def java = myFixture.addFileToProject('Java.java', 'class Java { void foo(Used used) {} }') + def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Java { }').virtualFile + + assertEmpty compileModule(myModule) + if (!useJps()) { + assertEmpty compileFiles(used.virtualFile, main) + } + + touch(used.virtualFile) + touch(main) + assertEmpty make() + + assertEmpty compileModule(myModule) + assertEmpty compileModule(myModule) + + setFileText(used, 'class Used2 {}') + shouldFail { make() } + assert findClassFile('Used') == null + + setFileText(used, 'class Used3 {}') + setFileText(java, 'class Java { void foo(Used3 used) {} }') + assertEmpty make() + + assert findClassFile('Used2') == null + } + + public void testClassLoadingDuringBytecodeGeneration() { + def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') + def java = myFixture.addFileToProject('Java.java', ''' +abstract class Java { + Object getProp() { return null; } + abstract void foo(Used used); +}''') + def main = myFixture.addFileToProject('Main.groovy', ''' +class Main { + def foo(Java j) { + return j.prop + } +}''').virtualFile + + assertEmpty make() + + touch(used.virtualFile) + touch(main) + assertEmpty make() + } + + public void testMakeInDependentModuleAfterChunkRebuild() { + def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') + def java = myFixture.addFileToProject('Java.java', 'class Java { void foo(Used used) {} }') + def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Java { }').virtualFile + + addGroovyLibrary(addDependentModule()) + + def dep = myFixture.addFileToProject("dependent/Dep.java", "class Dep { }") + + assertEmpty make() + + setFileText(used, 'class Used { String prop }') + touch(main) + setFileText(dep, 'class Dep { String prop = new Used().getProp(); }') + + assertEmpty make() + } + + public void "test module cycle"() { + def dep = addDependentModule() + ModuleRootModificationUtil.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 make().join('\n') + checkClassFiles() + + println make().join('\n') + checkClassFiles() + + assertOutput('Foo', 'Hello from Foo', myModule) + assertOutput('Bar', 'Hello from Bar', dep) + + checkClassFiles() + } + + public void testCompileTimeConstants() { + myFixture.addFileToProject 'Gr.groovy', ''' +interface Gr { + String HELLO = "Hello" + int MAGIC = 239 + Boolean BOOL = true + boolean bool = true +}''' + myFixture.addFileToProject 'Main.java', ''' +public class Main { + public static void main(String[] args) { + System.out.println(Gr.HELLO + ", " + Gr.BOOL + Gr.bool + Gr.MAGIC); + } +} +''' + make() + assertOutput 'Main', 'Hello, truetrue239' + } + + public void "test reporting rebuild errors caused by missing files excluded from compilation"() { + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo {}') + myFixture.addFileToProject 'Bar.groovy', 'class Bar extends Foo {}' + + make() + + excludeFromCompilation(foo) + + shouldFail { rebuild() } + } + + private void excludeFromCompilation(PsiFile foo) { + final ExcludedEntriesConfiguration configuration = + ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getExcludedEntriesConfiguration() + configuration.addExcludeEntryDescription(new ExcludeEntryDescription(foo.virtualFile, false, true, testRootDisposable)) + CompileServerManager.instance.shutdownServer() + } + + public void "test make stub-level error and correct it"() { + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo { }') + myFixture.addFileToProject('Bar.java', 'class Bar extends Foo {}') + + assertEmpty make() + + setFileText(foo, 'class Foo implements Runnabl {}') + + shouldFail { make() } + + setFileText(foo, 'class Foo {}') + + assertEmpty make() + } + + //todo jeka: when recompiling module, delete all class files including those with excluded source + public void "_test reporting module compile errors caused by missing files excluded from compilation"() { + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo {}') + myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo {}') + + make() + + excludeFromCompilation(foo) + + shouldFail { compileModule(myModule) } + } + + public void "test stubs generated while processing groovy class file dependencies"() { + def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo { }') + def bar = myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo { }') + def client = myFixture.addFileToProject('Client.groovy', 'class Client { Bar bar = new Bar() }') + def java = myFixture.addFileToProject('Java.java', 'class Java extends Client { String getName(Bar bar) { return bar.toString(); } }') + + assertEmpty(make()) + + setFileText(bar, 'class Bar { }') + + assertEmpty(make()) + assert findClassFile("Client") + } + + public void "test ignore groovy internal non-existent interface helper inner class"() { + myFixture.addFileToProject 'Foo.groovy', ''' +interface Foo {} + +class Zoo { + Foo foo() {} + static class Inner implements Foo {} +} + +''' + def bar = myFixture.addFileToProject('Bar.groovy', 'class Bar { def foo = new Zoo.Inner() {} }') + + assertEmpty make() + assertEmpty compileFiles(bar.virtualFile) + } + + public void "test multiline strings"() { + myFixture.addFileToProject 'Foo.groovy', '''class Foo { + public static final String s = """ +multi +line +string +""" + } ''' + myFixture.addFileToProject 'Bar.java', 'class Bar extends Foo {} ' + + assertEmpty make() + } + + public static class IdeaModeTest extends GroovyCompilerTest { + @Override protected boolean useJps() { false } + } + + public static class JpsModeTest extends GroovyCompilerTest { + @Override protected boolean useJps() { true } + + @Override + protected void tearDown() { + File systemRoot = CompileServerManager.getInstance().getCompileServerSystemRoot() + try { + super.tearDown() + } + finally { + File[] files = systemRoot.listFiles() + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + FileUtil.delete(file); + } + } + } + } + } + } +}