IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes

1. Started work for supporting content roots at the 'sync project structures';
2. Explicitly defining java executable to be used by the gradle daemon;
This commit is contained in:
Denis.Zhdanov
2012-02-27 15:39:26 +04:00
parent 8561fe1406
commit 19a33c7a05
19 changed files with 206 additions and 43 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -27,6 +27,7 @@ import com.intellij.util.containers.ComparatorUtil;
import static com.intellij.util.containers.ContainerUtil.map;
import static com.intellij.util.containers.ContainerUtil.skipNulls;
import com.intellij.util.containers.Convertor;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
@@ -50,10 +51,12 @@ public class PathUtilEx {
}
};
@Nullable
public static Sdk getAnyJdk(Project project) {
return chooseJdk(project, Arrays.asList(ModuleManager.getInstance(project).getModules()));
}
@Nullable
public static Sdk chooseJdk(Project project, Collection<Module> modules) {
Sdk projectJdk = ProjectRootManager.getInstance(project).getProjectSdk();
if (projectJdk != null) {
@@ -62,6 +65,7 @@ public class PathUtilEx {
return chooseJdk(modules);
}
@Nullable
public static Sdk chooseJdk(Collection<Module> modules) {
List<Sdk> jdks = skipNulls(map(skipNulls(modules), MODULE_JDK));
if (jdks.isEmpty()) {
@@ -0,0 +1,35 @@
package org.jetbrains.plugins.gradle.diff;
import com.intellij.openapi.roots.ContentEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.gradle.GradleContentRoot;
import org.jetbrains.plugins.gradle.model.id.GradleContentRootId;
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper;
import org.jetbrains.plugins.gradle.util.GradleBundle;
/**
* @author Denis Zhdanov
* @since 2/22/12 5:12 PM
*/
public class GradleContentRootPresenceChange extends GradleEntityPresenceChange<GradleContentRootId> {
public GradleContentRootPresenceChange(@Nullable GradleContentRoot gradleEntity, @Nullable ContentEntry intellijEntity)
throws IllegalArgumentException
{
super(GradleBundle.message("gradle.import.structure.tree.node.content.root"), of(gradleEntity), of(intellijEntity));
}
@Nullable
private static GradleContentRootId of(@Nullable Object contentRoot) {
if (contentRoot == null) {
return null;
}
return GradleEntityIdMapper.mapEntityToId(contentRoot);
}
@Override
public void invite(@NotNull GradleProjectStructureChangeVisitor visitor) {
visitor.visit(this);
}
}
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.Nullable;
/**
* Defines common interface for a change that indicates that particular entity has been added/removed at Gradle or IntelliJ IDEA.
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 11/17/11 12:43 PM
@@ -9,6 +9,8 @@ import org.jetbrains.plugins.gradle.model.id.GradleLibraryDependencyId;
import org.jetbrains.plugins.gradle.util.GradleBundle;
/**
* Thread-safe.
*
* @author Denis Zhdanov
* @since 1/24/12 9:48 AM
*/
@@ -26,15 +28,7 @@ public class GradleLibraryDependencyPresenceChange extends GradleEntityPresenceC
}
@Nullable
private static GradleLibraryDependencyId of(@Nullable GradleLibraryDependency dependency) {
if (dependency == null) {
return null;
}
return GradleEntityIdMapper.mapEntityToId(dependency);
}
@Nullable
private static GradleLibraryDependencyId of(@Nullable LibraryOrderEntry dependency) {
private static GradleLibraryDependencyId of(@Nullable Object dependency) {
if (dependency == null) {
return null;
}
@@ -26,15 +26,7 @@ public class GradleModuleDependencyPresenceChange extends GradleEntityPresenceCh
}
@Nullable
private static GradleModuleDependencyId of(@Nullable GradleModuleDependency dependency) {
if (dependency == null) {
return null;
}
return GradleEntityIdMapper.mapEntityToId(dependency);
}
@Nullable
private static GradleModuleDependencyId of(@Nullable ModuleOrderEntry dependency) {
private static GradleModuleDependencyId of(@Nullable Object dependency) {
if (dependency == null) {
return null;
}
@@ -18,8 +18,8 @@ package org.jetbrains.plugins.gradle.diff;
import com.intellij.openapi.module.Module;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.GradleEntityOwner;
import org.jetbrains.plugins.gradle.model.gradle.GradleModule;
import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper;
import org.jetbrains.plugins.gradle.model.id.GradleModuleId;
import org.jetbrains.plugins.gradle.util.GradleBundle;
@@ -41,18 +41,10 @@ public class GradleModulePresenceChange extends GradleEntityPresenceChange<Gradl
}
@Nullable
private static GradleModuleId of(@Nullable GradleModule module) {
private static GradleModuleId of(@Nullable Object module) {
if (module == null) {
return null;
}
return new GradleModuleId(GradleEntityOwner.GRADLE, module.getName());
}
@Nullable
private static GradleModuleId of(@Nullable Module module) {
if (module == null) {
return null;
}
return new GradleModuleId(GradleEntityOwner.GRADLE, module.getName());
return GradleEntityIdMapper.mapEntityToId(module);
}
}
@@ -27,6 +27,7 @@ public interface GradleProjectStructureChangeVisitor {
void visit(@NotNull GradleProjectRenameChange change);
void visit(@NotNull GradleLanguageLevelChange change);
void visit(@NotNull GradleModulePresenceChange change);
void visit(@NotNull GradleContentRootPresenceChange change);
void visit(@NotNull GradleLibraryDependencyPresenceChange change);
void visit(@NotNull GradleModuleDependencyPresenceChange change);
void visit(@NotNull GradleMismatchedLibraryPathChange change);
@@ -5,6 +5,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.GradleEntityOwner;
@@ -91,7 +92,11 @@ public class GradleEntityIdMapper {
@Override
public void visit(@NotNull ModuleAwareContentRoot contentRoot) {
result.set(new GradleContentRootId(GradleEntityOwner.INTELLIJ, contentRoot.getModule().getName(), contentRoot.getUrl()));
final VirtualFile file = contentRoot.getFile();
if (file == null) {
return;
}
result.set(new GradleContentRootId(GradleEntityOwner.INTELLIJ, contentRoot.getModule().getName(), file.getPath()));
}
@Override
@@ -293,6 +293,8 @@ public class GradleApiFacadeManager {
GradleLog.LOG.warn("Incorrect remote process ttl value detected. Expected to find number, found '" + ttlAsString + "'");
}
}
GradleJavaHelper javaHelper = new GradleJavaHelper();
result.setJavaHome(javaHelper.getJdkHome());
return result;
}
@@ -0,0 +1,23 @@
package org.jetbrains.plugins.gradle.remote;
import org.jetbrains.annotations.Nullable;
/**
* Encapsulates functionality of deciding what java should be used by the gradle process.
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 2/27/12 2:20 PM
*/
public class GradleJavaHelper {
public static final String GRADLE_JAVA_HOME_KEY = "gradle.java.home";
@SuppressWarnings("MethodMayBeStatic")
@Nullable
public String getJdkHome() {
// TODO den implement
return null;
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.plugins.gradle.remote;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
@@ -19,6 +20,7 @@ public class RemoteGradleProcessSettings implements Serializable {
private final String myGradleHome;
private long myTtlInMs;
private String myJavaHome;
public RemoteGradleProcessSettings(@NotNull String gradleHome) {
myGradleHome = gradleHome;
@@ -40,6 +42,15 @@ public class RemoteGradleProcessSettings implements Serializable {
myTtlInMs = ttlInMs;
}
@Nullable
public String getJavaHome() {
return myJavaHome;
}
public void setJavaHome(@Nullable String javaHome) {
myJavaHome = javaHome;
}
@Override
public String toString() {
return "home: " + myGradleHome;
@@ -95,6 +95,13 @@ public class GradleProjectResolverImpl extends RemoteObject implements GradlePro
progressManager.onStart(id);
ProjectConnection connection = getConnection(projectPath);
ModelBuilder<? extends IdeaProject> modelBuilder = connection.model(downloadLibraries ? IdeaProject.class : BasicIdeaProject.class);
final RemoteGradleProcessSettings settings = mySettings.get();
if (settings != null) {
final String javaHome = settings.getJavaHome();
if (javaHome != null && new File(javaHome).isDirectory()) {
modelBuilder.setJavaHome(new File(javaHome));
}
}
modelBuilder.addProgressListener(new ProgressListener() {
@Override
public void statusChanged(ProgressEvent event) {
@@ -8,13 +8,16 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.util.Alarm;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.importing.GradleProjectEntityImportListener;
import org.jetbrains.plugins.gradle.task.GradleTaskManager;
import org.jetbrains.plugins.gradle.task.GradleTaskType;
import org.jetbrains.plugins.gradle.util.GradleUtil;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -26,16 +29,33 @@ import java.util.concurrent.atomic.AtomicLong;
public class GradleProjectStructureChangesDetector extends AbstractProjectComponent {
private static final int REFRESH_DELAY_MILLIS = (int)TimeUnit.SECONDS.toMillis(2);
private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
private final AtomicLong myStartRefreshTime = new AtomicLong();
private final RefreshRequest myRequest = new RefreshRequest();
private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
private final AtomicLong myStartRefreshTime = new AtomicLong();
private final RefreshRequest myRequest = new RefreshRequest();
private final AtomicInteger myImportCounter = new AtomicInteger();
public GradleProjectStructureChangesDetector(@NotNull Project project) {
super(project);
subscribeToGradleImport(project);
subscribeToRootChanges(project);
}
private void subscribeToGradleImport(@NotNull Project project) {
MessageBusConnection connection = project.getMessageBus().connect(project);
connection.subscribe(GradleProjectEntityImportListener.TOPIC, new GradleProjectEntityImportListener() {
@Override
public void onImportStart(@NotNull Object entity) {
myImportCounter.incrementAndGet();
}
@Override
public void onImportEnd(@NotNull Object entity) {
myImportCounter.incrementAndGet();
}
});
}
private void subscribeToRootChanges(@NotNull Project project) {
project.getMessageBus().connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
@Override
@@ -44,7 +64,9 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
@Override
public void rootsChanged(ModuleRootEvent event) {
scheduleUpdate();
if (myImportCounter.incrementAndGet() <= 0) {
scheduleUpdate();
}
}
});
}
@@ -58,6 +80,10 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
private class RefreshRequest implements Runnable {
@Override
public void run() {
if (myProject.isDisposed()) {
myAlarm.cancelAllRequests();
return;
}
if (!myProject.isInitialized()) {
return;
}
@@ -66,7 +92,7 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
if (taskManager != null && taskManager.hasTaskOfTypeInProgress(GradleTaskType.RESOLVE_PROJECT)) {
return;
}
final long diff = System.currentTimeMillis() - myStartRefreshTime.get();
if (diff < 0) {
myAlarm.cancelAllRequests();
@@ -85,7 +111,7 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
return;
}
GradleUtil.refreshProject(myProject);
GradleUtil.refreshProject(myProject);
}
});
}
@@ -280,6 +280,10 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
final GradleProjectStructureNode<GradleModuleId> moduleNode = getModuleNode(id);
moduleNode.setAttributes(key);
}
private void processNewContentRootPresenceChange(@NotNull GradleContentRootPresenceChange change) {
// TODO den implement
}
private void processObsoleteProjectRenameChange(@NotNull GradleProjectRenameChange change) {
// TODO den implement
@@ -388,6 +392,10 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
moduleNode.setAttributes(GradleTextAttributes.GRADLE_NO_CHANGE);
}
}
private void processObsoleteContentRootPresenceChange(@NotNull GradleContentRootPresenceChange change) {
// TODO den implement
}
private class NodeListener implements GradleProjectStructureNode.Listener {
@@ -422,7 +430,9 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
@Override public void visit(@NotNull GradleProjectRenameChange change) { processNewProjectRenameChange(change); }
@Override public void visit(@NotNull GradleLanguageLevelChange change) { processNewLanguageLevelChange(change); }
@Override public void visit(@NotNull GradleModulePresenceChange change) { processNewModulePresenceChange(change); }
@Override public void visit(@NotNull GradleLibraryDependencyPresenceChange change) { processNewLibraryDependencyPresenceChange(change); }
@Override public void visit(@NotNull GradleContentRootPresenceChange change) { processNewContentRootPresenceChange(change); }
@Override public void visit(@NotNull GradleLibraryDependencyPresenceChange change) { processNewLibraryDependencyPresenceChange(change); }
@Override public void visit(@NotNull GradleModuleDependencyPresenceChange change) { processNewModuleDependencyPresenceChange(change); }
@Override public void visit(@NotNull GradleMismatchedLibraryPathChange change) { processNewMismatchedLibraryPathChange(change); }
}
@@ -431,6 +441,7 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel {
@Override public void visit(@NotNull GradleProjectRenameChange change) { processObsoleteProjectRenameChange(change); }
@Override public void visit(@NotNull GradleLanguageLevelChange change) { processObsoleteLanguageLevelChange(change); }
@Override public void visit(@NotNull GradleModulePresenceChange change) { processObsoleteModulePresenceChange(change); }
@Override public void visit(@NotNull GradleContentRootPresenceChange change) { processObsoleteContentRootPresenceChange(change); }
@Override public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
processObsoleteLibraryDependencyPresenceChange(change);
}
@@ -551,4 +551,35 @@ public class GradleProjectStructureChangesModelTest extends AbstractGradleTest {
lib1('conflict')
} } } }
}
// TODO den uncomment
//@Test
public void "local content root importing"() {
init(
gradle: {
project {
module {
contentRoot('1')
contentRoot('2')
} } },
intellij: {
project {
module {
contentRoot('2')
contentRoot('3')
} } }
)
checkChanges {
presence {
contentRoot(gradle: gradle.contentRoots.values().find { it.rootPath == '1' })
contentRoot(intellij: intellij.contentRoots.values().find { it.file.path == '3' })
} }
checkTree {
project {
module {
"content-root:1"('gradle')
"content-root:2"()
"content-root:3"('intellij')
} } }
}
}
@@ -65,6 +65,10 @@ public abstract class AbstractProjectBuilder extends BuilderSupport {
case "project":
clear()
return project = createProject(attributes.name?: same, attributes.langLevel?: LanguageLevel.JDK_1_6)
case "contentRoot":
def contentRoot = createContentRoot(current, attributes.name, attributes)
contentRoots[current] << contentRoot
return contentRoot
case "module":
case "library":
def n = StringUtil.capitalize(name)
@@ -82,6 +86,7 @@ public abstract class AbstractProjectBuilder extends BuilderSupport {
protected abstract def createProject(String name, LanguageLevel languageLevel)
protected abstract def createModule(String name)
protected abstract def registerModule(module)
protected abstract def createContentRoot(module, rootPath, Map paths)
protected abstract def createLibrary(String name, Map paths)
protected abstract def applyLibraryPaths(library, Map paths)
protected abstract def createLibraryDependency(module, library)
@@ -7,6 +7,7 @@ import com.intellij.openapi.roots.libraries.Library
import org.jetbrains.plugins.gradle.util.GradleUtil
import org.jetbrains.plugins.gradle.diff.GradleModulePresenceChange
import org.jetbrains.plugins.gradle.diff.GradleModuleDependencyPresenceChange
import org.jetbrains.plugins.gradle.diff.GradleContentRootPresenceChange
/**
* @author Denis Zhdanov
@@ -53,6 +54,10 @@ public class ChangeBuilder extends BuilderSupport {
throw new IllegalArgumentException("No entity is defined for the library conflict change. Known attributes: $attributes")
}
return library
case "contentRoot":
changes.addAll attributes.gradle.collect { new GradleContentRootPresenceChange(it, null)}
changes.addAll attributes.intellij.collect { new GradleContentRootPresenceChange(null, it)}
return changes
case "binaryPath":
// Assuming that we're processing library binary path conflict here
register(new GradleMismatchedLibraryPathChange(
@@ -7,6 +7,7 @@ import org.jetbrains.plugins.gradle.model.gradle.GradleLibrary
import org.jetbrains.plugins.gradle.model.gradle.LibraryPathType
import com.intellij.pom.java.LanguageLevel
import org.jetbrains.plugins.gradle.model.gradle.GradleModuleDependency
import org.jetbrains.plugins.gradle.model.gradle.GradleContentRoot
/**
* @author Denis Zhdanov
@@ -33,6 +34,13 @@ class GradleProjectBuilder extends AbstractProjectBuilder {
module
}
@Override
protected createContentRoot(module, rootPath, Map paths) {
def result = new GradleContentRoot(module, rootPath)
module.addContentRoot(result)
return result
}
@Override
protected createModuleDependency(ownerModule, targetModule) {
def result = new GradleModuleDependency(ownerModule, targetModule)
@@ -14,6 +14,8 @@ import com.intellij.openapi.roots.OrderRootType
import org.jetbrains.plugins.gradle.util.GradleUtil
import com.intellij.openapi.roots.libraries.LibraryTable
import com.intellij.openapi.roots.ModuleOrderEntry
import org.jetbrains.plugins.gradle.model.intellij.ModuleAwareContentRoot
import com.intellij.openapi.roots.ContentEntry
/**
* @author Denis Zhdanov
@@ -56,6 +58,11 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
@Override
protected registerModule(Object module) { }
@Override
protected createContentRoot(module, rootPath, Map paths) {
new ModuleAwareContentRoot(module, [:] as ContentEntry)
}
@Override
protected createModuleDependency(ownerModule, targetModule) {
def stub = [:]
@@ -74,10 +81,8 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
getName: { name },
getPresentableName: { name },
getFiles: {
type -> (libraryPaths[name])[LIBRARY_ENTRY_TYPES[type]].findAll { it }.collect {
String path = it
[getPath: { GradleUtil.toCanonicalPath(path) }] as VirtualFile
}.toArray(DUMMY_VIRTUAL_FILE_ARRAY)
type ->
(libraryPaths[name])[LIBRARY_ENTRY_TYPES[type]].findAll { it }.collect { asVirtualFile(it) }.toArray(DUMMY_VIRTUAL_FILE_ARRAY)
}
] as Library
}
@@ -100,4 +105,8 @@ class IntellijProjectBuilder extends AbstractProjectBuilder {
@Override
protected reset() { }
private def asVirtualFile(path) {
[getPath: { GradleUtil.toCanonicalPath(path) }] as VirtualFile
}
}