mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
force recompilation for all the Groovy classes that can fail groovyc, i.e. reference the changed classes and are referenced by them (IDEA-51924)
This commit is contained in:
@@ -16,36 +16,44 @@
|
||||
|
||||
package org.jetbrains.plugins.groovy.compiler;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.openapi.compiler.CompileScope;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.JavaModuleType;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkType;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ContentIterator;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.roots.ui.configuration.ClasspathEditor;
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle;
|
||||
import org.jetbrains.plugins.groovy.GroovyFileType;
|
||||
import org.jetbrains.plugins.groovy.GroovyFileTypeLoader;
|
||||
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.util.LibrariesUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry.Krasilschikov
|
||||
*/
|
||||
|
||||
public class GroovyCompiler extends GroovyCompilerBase {
|
||||
private static final String GROOVY_COMPILER = "groovy compiler";
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.compiler.GroovyCompiler");
|
||||
|
||||
public GroovyCompiler(Project project) {
|
||||
super(project);
|
||||
@@ -53,12 +61,145 @@ public class GroovyCompiler extends GroovyCompilerBase {
|
||||
|
||||
@NotNull
|
||||
public String getDescription() {
|
||||
return GROOVY_COMPILER;
|
||||
return "groovy compiler";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compileFiles(CompileContext compileContext, Module module, List<VirtualFile> toCompile, OutputSink sink, boolean tests) {
|
||||
runGroovycCompiler(compileContext, module, toCompile, false, getMainOutput(compileContext, module, tests), sink, tests);
|
||||
protected void compileFiles(final CompileContext context, final Module module, List<VirtualFile> toCompile, OutputSink sink, boolean tests) {
|
||||
final Set<VirtualFile> allToCompile = new LinkedHashSet<VirtualFile>(toCompile);
|
||||
|
||||
// groovyc may fail if we don't also recompile files like B such that A depends on B and B depends on C, where A & C \in toCompile
|
||||
// see http://jira.codehaus.org/browse/GROOVY-4024
|
||||
// this is important only if >1 files have been changed
|
||||
if (toCompile.size() > 1 && !"false".equals(System.getProperty("compile.groovy.dependencies", "true"))) {
|
||||
context.getProgressIndicator().checkCanceled();
|
||||
context.getProgressIndicator().setText("Enumerating Groovy classes...");
|
||||
|
||||
final Map<PsiClass, VirtualFile> moduleClasses = new THashMap<PsiClass, VirtualFile>();
|
||||
int groovyFiles = enumerateGroovyClasses(module, moduleClasses);
|
||||
|
||||
if (toCompile.size() < groovyFiles) {
|
||||
context.getProgressIndicator().checkCanceled();
|
||||
context.getProgressIndicator().setText("Processing Groovy dependencies...");
|
||||
|
||||
addIntermediateGroovyClasses(allToCompile, moduleClasses);
|
||||
}
|
||||
}
|
||||
|
||||
context.getProgressIndicator().checkCanceled();
|
||||
context.getProgressIndicator().setText(GroovycOSProcessHandler.GROOVY_COMPILER_IN_OPERATION);
|
||||
|
||||
runGroovycCompiler(context, module, new ArrayList<VirtualFile>(allToCompile), false, getMainOutput(context, module, tests), sink, tests);
|
||||
}
|
||||
|
||||
private void addIntermediateGroovyClasses(Set<VirtualFile> allToCompile, final Map<PsiClass, VirtualFile> moduleClasses) {
|
||||
final Set<PsiClass> initialClasses = new THashSet<PsiClass>();
|
||||
for (VirtualFile file : allToCompile) {
|
||||
initialClasses.addAll(getAllClasses(file));
|
||||
}
|
||||
|
||||
final THashSet<PsiClass> dirty = new THashSet<PsiClass>(initialClasses);
|
||||
|
||||
final THashSet<PsiClass> visited = new THashSet<PsiClass>();
|
||||
for (PsiClass aClass : initialClasses) {
|
||||
if (visited.add(aClass)) {
|
||||
goForIntermediateClasses(aClass, dirty, new FactoryMap<PsiClass, Set<PsiClass>>() {
|
||||
@Override
|
||||
protected Set<PsiClass> create(final PsiClass key) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Set<PsiClass>>() {
|
||||
public Set<PsiClass> compute() {
|
||||
return calcCodeReferenceDependencies(key, moduleClasses);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, visited);
|
||||
}
|
||||
}
|
||||
for (PsiClass psiClass : dirty) {
|
||||
allToCompile.add(moduleClasses.get(psiClass));
|
||||
}
|
||||
}
|
||||
|
||||
private int enumerateGroovyClasses(final Module module, final Map<PsiClass, VirtualFile> moduleClasses) {
|
||||
final Set<VirtualFile> groovyFiles = new THashSet<VirtualFile>();
|
||||
ModuleRootManager.getInstance(module).getFileIndex().iterateContent(new ContentIterator() {
|
||||
public boolean processFile(final VirtualFile vfile) {
|
||||
if (!vfile.isDirectory() && GroovyFileType.GROOVY_FILE_TYPE.equals(vfile.getFileType())) {
|
||||
final List<PsiClass> classes = getAllClasses(vfile);
|
||||
if (!classes.isEmpty()) {
|
||||
groovyFiles.add(vfile);
|
||||
for (PsiClass psiClass : classes) {
|
||||
moduleClasses.put(psiClass, vfile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return groovyFiles.size();
|
||||
}
|
||||
|
||||
private List<PsiClass> getAllClasses(final VirtualFile vfile) {
|
||||
final ArrayList<PsiClass> classes = new ArrayList<PsiClass>();
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
public void run() {
|
||||
final PsiFile file = PsiManager.getInstance(myProject).findFile(vfile);
|
||||
if (file instanceof GroovyFile) {
|
||||
addClassesFrom(((GroovyFile)file).getClasses(), classes);
|
||||
}
|
||||
}
|
||||
|
||||
private void addClassesFrom(final PsiClass[] classes, List<PsiClass> result) {
|
||||
for (PsiClass aClass : classes) {
|
||||
result.add(aClass);
|
||||
addClassesFrom(aClass.getInnerClasses(), result);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
||||
private static void goForIntermediateClasses(PsiClass from, Set<PsiClass> dirty, FactoryMap<PsiClass, Set<PsiClass>> deps, Set<PsiClass> visited) {
|
||||
final Set<PsiClass> set = deps.get(from);
|
||||
for (PsiClass psiClass : set) {
|
||||
if (visited.add(psiClass)) {
|
||||
goForIntermediateClasses(psiClass, dirty, deps, visited);
|
||||
}
|
||||
if (dirty.contains(psiClass)) {
|
||||
dirty.add(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<PsiClass> calcCodeReferenceDependencies(PsiClass psiClass, final Map<PsiClass, VirtualFile> moduleClasses) {
|
||||
final Set<PsiClass> deps = new THashSet<PsiClass>();
|
||||
psiClass.acceptChildren(new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (element instanceof PsiClass) {
|
||||
return;
|
||||
}
|
||||
if (element instanceof GrCodeReferenceElement) {
|
||||
GrCodeReferenceElement referenceElement = (GrCodeReferenceElement)element;
|
||||
try {
|
||||
final PsiElement target = referenceElement.resolve();
|
||||
if (target instanceof PsiClass && moduleClasses.containsKey(target)) {
|
||||
deps.add((PsiClass)target);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
//prevent our PSI errors from failing the entire compilation
|
||||
}
|
||||
}
|
||||
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
});
|
||||
return deps;
|
||||
}
|
||||
|
||||
public boolean validateConfiguration(CompileScope compileScope) {
|
||||
|
||||
+2
-1
@@ -44,6 +44,7 @@ public class GroovycOSProcessHandler extends OSProcessHandler {
|
||||
private final CompileContext myContext;
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.compiler.GroovycOSProcessHandler");
|
||||
public static final String GROOVY_COMPILER_IN_OPERATION = "Groovy compiler in operation...";
|
||||
|
||||
public GroovycOSProcessHandler(CompileContext context, Process process, String s) {
|
||||
super(process, s);
|
||||
@@ -73,7 +74,7 @@ public class GroovycOSProcessHandler extends OSProcessHandler {
|
||||
}
|
||||
|
||||
if (GroovycRunner.CLEAR_PRESENTABLE.equals(text)) {
|
||||
myContext.getProgressIndicator().setText("Groovy compiler in operation...");
|
||||
myContext.getProgressIndicator().setText(GROOVY_COMPILER_IN_OPERATION);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -287,5 +287,26 @@ class Foo {
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user