IDEA-CR-43650: do not request full reimport when build delegation option has been changed IDEA-189463

This commit is contained in:
Vladislav.Soroka
2019-02-21 16:08:59 +03:00
parent aab7d693ed
commit abd0a37dc2
6 changed files with 172 additions and 63 deletions
@@ -36,5 +36,7 @@ public interface ExternalProjectInfo {
long getLastImportTimestamp();
String getBuildNumber();
ExternalProjectInfo copy();
}
@@ -22,6 +22,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
private static final long serialVersionUID = 1L;
@NotNull private final Map<ExternalSystemSourceType, String> myCompileOutputPaths = ContainerUtil.newHashMap();
@NotNull private final Map<ExternalSystemSourceType, String> myExternalCompilerOutputPaths = ContainerUtil.newHashMap();
@Nullable private Map<String, String> myProperties;
@NotNull private final String myId;
@NotNull private final String myModuleTypeId;
@@ -39,6 +40,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
@Nullable private ProjectCoordinate myPublication;
private boolean myInheritProjectCompileOutputPath = true;
private boolean myUseExternalCompilerOutput;
public ModuleData(@NotNull String id,
@NotNull ProjectSystemId owner,
@@ -122,15 +124,22 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
*/
@Nullable
public String getCompileOutputPath(@NotNull ExternalSystemSourceType type) {
return myCompileOutputPaths.get(type);
//noinspection ConstantConditions
return myUseExternalCompilerOutput && myExternalCompilerOutputPaths != null
? myExternalCompilerOutputPaths.get(type)
: myCompileOutputPaths.get(type);
}
public void setCompileOutputPath(@NotNull ExternalSystemSourceType type, @Nullable String path) {
if (path == null) {
myCompileOutputPaths.remove(type);
return;
}
myCompileOutputPaths.put(type, ExternalSystemApiUtil.toCanonicalPath(path));
updatePath(myCompileOutputPaths, type, path);
}
public void setExternalCompilerOutputPath(@NotNull ExternalSystemSourceType type, @Nullable String path) {
updatePath(myExternalCompilerOutputPaths, type, path);
}
public void useExternalCompilerOutput(boolean useExternalCompilerOutput) {
myUseExternalCompilerOutput = useExternalCompilerOutput;
}
@Nullable
@@ -284,4 +293,15 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
public String toString() {
return getId();
}
private static void updatePath(Map<ExternalSystemSourceType, String> paths,
@NotNull ExternalSystemSourceType type,
@Nullable String path) {
if (paths == null) return;
if (path == null) {
paths.remove(type);
return;
}
paths.put(type, ExternalSystemApiUtil.toCanonicalPath(path));
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.externalSystem.model.internal;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
@@ -22,10 +23,7 @@ import com.intellij.openapi.externalSystem.model.project.ProjectData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.*;
/**
* @author Vladislav.Soroka
@@ -42,6 +40,7 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
private DataNode<ProjectData> myExternalProjectStructure;
private long lastSuccessfulImportTimestamp = -1;
private long lastImportTimestamp = -1;
private String myBuildNumber;
public InternalExternalProjectInfo(@NotNull ProjectSystemId projectSystemId,
@NotNull String externalProjectPath,
@@ -49,6 +48,7 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
myProjectSystemId = projectSystemId;
myExternalProjectPath = externalProjectPath;
myExternalProjectStructure = externalProjectStructure;
myBuildNumber = ApplicationInfo.getInstance().getBuild().asString();
}
@Override
@@ -84,10 +84,6 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
return lastImportTimestamp;
}
public void setExternalProjectStructure(@Nullable DataNode<ProjectData> externalProjectStructure) {
myExternalProjectStructure = externalProjectStructure;
}
public void setLastSuccessfulImportTimestamp(long lastSuccessfulImportTimestamp) {
this.lastSuccessfulImportTimestamp = lastSuccessfulImportTimestamp;
}
@@ -96,6 +92,11 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
this.lastImportTimestamp = lastImportTimestamp;
}
@Override
public String getBuildNumber() {
return myBuildNumber;
}
@Override
public ExternalProjectInfo copy() {
InternalExternalProjectInfo copy = new InternalExternalProjectInfo(
@@ -125,6 +126,7 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
out.writeObject(myExternalProjectStructure);
out.writeLong(lastSuccessfulImportTimestamp);
out.writeLong(lastImportTimestamp);
out.writeObject(myBuildNumber);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
@@ -134,5 +136,10 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
myExternalProjectStructure = (DataNode<ProjectData>)in.readObject();
lastSuccessfulImportTimestamp = in.readLong();
lastImportTimestamp = in.readLong();
try {
myBuildNumber = (String)in.readObject();
}
catch (OptionalDataException ignore) {
}
}
}
@@ -21,6 +21,7 @@ import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.SearchScopeProvider;
import com.intellij.execution.configurations.SimpleJavaParameters;
import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.Extensions;
@@ -37,6 +38,7 @@ import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExe
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver;
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager;
import com.intellij.openapi.externalSystem.service.project.autoimport.CachingExternalSystemAutoImportAware;
@@ -44,12 +46,17 @@ import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjec
import com.intellij.openapi.externalSystem.service.ui.DefaultExternalSystemUiAware;
import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.startup.StartupActivity;
@@ -88,6 +95,7 @@ import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.findAll;
import static com.intellij.openapi.util.io.FileUtil.pathsEqual;
/**
@@ -441,7 +449,39 @@ public class GradleManager
@Override
public void onBuildDelegationChange(boolean delegatedBuild, @NotNull String linkedProjectPath) {
ExternalProjectsManager.getInstance(project).getExternalProjectsWatcher().markDirty(linkedProjectPath);
if (!updateOutputRoots(delegatedBuild, linkedProjectPath)) {
ExternalProjectsManager.getInstance(project).getExternalProjectsWatcher().markDirty(linkedProjectPath);
}
}
private boolean updateOutputRoots(boolean delegatedBuild, @NotNull String linkedProjectPath) {
ExternalProjectInfo projectInfo =
ProjectDataManager.getInstance().getExternalProjectData(project, GradleConstants.SYSTEM_ID, linkedProjectPath);
if (projectInfo == null) return false;
String buildNumber = projectInfo.getBuildNumber();
if (buildNumber == null) return false;
final DataNode<ProjectData> projectStructure = projectInfo.getExternalProjectStructure();
if (projectStructure == null) return false;
String title = ExternalSystemBundle.message("progress.refresh.text", projectStructure.getData().getExternalName(),
projectInfo.getProjectSystemId().getReadableName());
ProgressManager.getInstance().run(new Task.Backgroundable(project, title, false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
DumbService.getInstance(project).suspendIndexingAndRun(title, () -> {
for (DataNode<ModuleData> moduleDataNode : findAll(projectStructure, ProjectKeys.MODULE)) {
moduleDataNode.getData().useExternalCompilerOutput(delegatedBuild);
for (DataNode<GradleSourceSetData> sourceSetDataNode : findAll(projectStructure, GradleSourceSetData.KEY)) {
sourceSetDataNode.getData().useExternalCompilerOutput(delegatedBuild);
}
}
ServiceManager.getService(ProjectDataManager.class).importData(projectStructure, project, true);
});
}
});
return true;
}
});
@@ -41,7 +41,10 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.*;
import com.intellij.util.Consumer;
import com.intellij.util.PathUtil;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.net.HttpConfigurable;
@@ -74,7 +77,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -438,6 +440,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
public void populateModuleCompileOutputSettings(@NotNull IdeaModule gradleModule,
@NotNull DataNode<ModuleData> ideModule) {
ModuleData moduleData = ideModule.getData();
moduleData.useExternalCompilerOutput(resolverCtx.isDelegatedBuild());
File ideaOutDir = new File(moduleData.getLinkedExternalProjectPath(), "out");
ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
@@ -456,20 +460,24 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
gradleOutputMap = MultiMap.create();
dataNode.putUserData(GradleProjectResolver.GRADLE_OUTPUTS, gradleOutputMap);
}
final ModuleData moduleData = dataNode.getData();
moduleData.useExternalCompilerOutput(resolverCtx.isDelegatedBuild());
for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> directorySetEntry : sourceSet.getSources().entrySet()) {
ExternalSystemSourceType sourceType = ExternalSystemSourceType.from(directorySetEntry.getKey());
ExternalSourceDirectorySet sourceDirectorySet = directorySetEntry.getValue();
final ModuleData moduleData = dataNode.getData();
File outputDir = getOutputDir(sourceDirectorySet);
moduleData.setCompileOutputPath(sourceType, outputDir == null ? null : outputDir.getAbsolutePath());
File ideOutputDir = getIdeOutputDir(sourceDirectorySet);
File gradleOutputDir = getGradleOutputDir(sourceDirectorySet);
File outputDir = resolverCtx.isDelegatedBuild() ? gradleOutputDir : ideOutputDir;
moduleData.setCompileOutputPath(sourceType, ideOutputDir == null ? null : ideOutputDir.getAbsolutePath());
moduleData.setExternalCompilerOutputPath(sourceType, gradleOutputDir == null ? null : gradleOutputDir.getAbsolutePath());
moduleData.setInheritProjectCompileOutputPath(sourceDirectorySet.isCompilerOutputPathInherited());
if (outputDir != null) {
outputDirs.add(outputDir.getPath());
for (File gradleOutputDir : sourceDirectorySet.getGradleOutputDirs()) {
String gradleOutputPath = ExternalSystemApiUtil.toCanonicalPath(gradleOutputDir.getAbsolutePath());
for (File file : sourceDirectorySet.getGradleOutputDirs()) {
String gradleOutputPath = ExternalSystemApiUtil.toCanonicalPath(file.getAbsolutePath());
gradleOutputMap.putValue(sourceType, gradleOutputPath);
if (!gradleOutputDir.getPath().equals(outputDir.getPath())) {
if (!file.getPath().equals(outputDir.getPath())) {
moduleOutputsMap.put(gradleOutputPath, Pair.create(moduleData.getId(), sourceType));
}
}
@@ -484,66 +492,76 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
}
IdeaCompilerOutput moduleCompilerOutput = gradleModule.getCompilerOutput();
Map<ExternalSystemSourceType, File> compileOutputPaths = ContainerUtil.newHashMap();
boolean inheritOutputDirs = moduleCompilerOutput != null && moduleCompilerOutput.getInheritOutputDirs();
if (moduleCompilerOutput != null) {
File outputDir = moduleCompilerOutput.getOutputDir();
if (outputDir == null && resolverCtx.isDelegatedBuild() && externalProject != null) {
outputDir = getOutputDir(externalProject, "main", ExternalSystemSourceType.SOURCE);
if (outputDir != null) {
moduleData.setCompileOutputPath(ExternalSystemSourceType.SOURCE, outputDir.getAbsolutePath());
moduleData.setCompileOutputPath(ExternalSystemSourceType.RESOURCE, outputDir.getAbsolutePath());
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.SOURCE, outputDir.getAbsolutePath());
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.RESOURCE, outputDir.getAbsolutePath());
}
else {
moduleData.setCompileOutputPath(ExternalSystemSourceType.SOURCE, new File(ideaOutDir, "production/classes").getAbsolutePath());
moduleData.setCompileOutputPath(ExternalSystemSourceType.RESOURCE, new File(ideaOutDir, "production/resources").getAbsolutePath());
if (externalProject != null) {
File gradleOutputDir = getGradleOutputDir(externalProject, "main", ExternalSystemSourceType.SOURCE);
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.SOURCE,
gradleOutputDir == null ? null : gradleOutputDir.getAbsolutePath());
File gradleResourceOutputDir = getGradleOutputDir(externalProject, "main", ExternalSystemSourceType.RESOURCE);
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.RESOURCE,
gradleResourceOutputDir == null ? null : gradleResourceOutputDir.getAbsolutePath());
}
}
File classesOutputDir = ObjectUtils.chooseNotNull(outputDir, new File(ideaOutDir, "production/classes"));
compileOutputPaths.put(ExternalSystemSourceType.SOURCE, classesOutputDir);
File resourcesOutputDir = ObjectUtils.chooseNotNull(outputDir, new File(ideaOutDir, "production/resources"));
compileOutputPaths.put(ExternalSystemSourceType.RESOURCE, resourcesOutputDir);
File testOutputDir = moduleCompilerOutput.getTestOutputDir();
if (testOutputDir == null && resolverCtx.isDelegatedBuild() && externalProject != null) {
testOutputDir = getOutputDir(externalProject, "test", ExternalSystemSourceType.TEST);
if (testOutputDir != null) {
moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST, testOutputDir.getAbsolutePath());
moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST_RESOURCE, testOutputDir.getAbsolutePath());
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.TEST, testOutputDir.getAbsolutePath());
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.TEST_RESOURCE, testOutputDir.getAbsolutePath());
}
else {
moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST, new File(ideaOutDir, "test/classes").getAbsolutePath());
moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST_RESOURCE, new File(ideaOutDir, "test/resources").getAbsolutePath());
if (externalProject != null) {
File gradleOutputDir = getGradleOutputDir(externalProject, "test", ExternalSystemSourceType.TEST);
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.TEST,
gradleOutputDir == null ? null : gradleOutputDir.getAbsolutePath());
File gradleResourceOutputDir = getGradleOutputDir(externalProject, "test", ExternalSystemSourceType.TEST_RESOURCE);
moduleData.setExternalCompilerOutputPath(ExternalSystemSourceType.TEST_RESOURCE,
gradleResourceOutputDir == null ? null : gradleResourceOutputDir.getAbsolutePath());
}
}
File testClassesOutputDir = ObjectUtils.chooseNotNull(testOutputDir, new File(ideaOutDir, "test/classes"));
compileOutputPaths.put(ExternalSystemSourceType.TEST, testClassesOutputDir);
File testResourcesOutputDir = ObjectUtils.chooseNotNull(testOutputDir, new File(ideaOutDir, "test/resources"));
compileOutputPaths.put(ExternalSystemSourceType.TEST_RESOURCE, testResourcesOutputDir);
if (!inheritOutputDirs && (outputDir == null || testOutputDir == null)) {
if (!resolverCtx.isDelegatedBuild() && !inheritOutputDirs && (outputDir == null || testOutputDir == null)) {
excludeOutDir(ideModule, ideaOutDir);
}
}
for (Map.Entry<ExternalSystemSourceType, File> sourceTypeFileEntry : compileOutputPaths.entrySet()) {
final File outputPath = sourceTypeFileEntry.getValue();
if (outputPath != null) {
moduleData.setCompileOutputPath(sourceTypeFileEntry.getKey(), outputPath.getAbsolutePath());
}
}
moduleData.setInheritProjectCompileOutputPath(inheritOutputDirs);
}
@Nullable
private File getOutputDir(@NotNull ExternalProject externalProject,
@NotNull String sourceSetName,
@NotNull ExternalSystemSourceType sourceType) {
private static File getGradleOutputDir(@NotNull ExternalProject externalProject,
@NotNull String sourceSetName,
@NotNull ExternalSystemSourceType sourceType) {
ExternalSourceSet sourceSet = externalProject.getSourceSets().get(sourceSetName);
if (sourceSet == null) return null;
return getOutputDir(sourceSet.getSources().get(sourceType));
return getGradleOutputDir(sourceSet.getSources().get(sourceType));
}
@Nullable
private File getOutputDir(@Nullable ExternalSourceDirectorySet sourceDirectorySet) {
private static File getIdeOutputDir(@Nullable ExternalSourceDirectorySet sourceDirectorySet) {
if (sourceDirectorySet == null) return null;
return sourceDirectorySet.getOutputDir();
}
AtomicReference<File> result = new AtomicReference<>();
if (resolverCtx.isDelegatedBuild()) {
sourceDirectorySet.getGradleOutputDirs().stream()
.findFirst()
.ifPresent(file -> result.set(file));
}
else {
result.set(sourceDirectorySet.getOutputDir());
}
return result.get();
@Nullable
private static File getGradleOutputDir(@Nullable ExternalSourceDirectorySet sourceDirectorySet) {
if (sourceDirectorySet == null) return null;
return sourceDirectorySet.getGradleOutputDirs().stream().findFirst().orElse(null);
}
private static void excludeOutDir(@NotNull DataNode<ModuleData> ideModule, File ideaOutDir) {
@@ -26,13 +26,18 @@ import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.plugins.gradle.GradleManager;
import org.jetbrains.plugins.gradle.service.settings.GradleSettingsService;
import org.jetbrains.plugins.gradle.settings.GradleSettings;
import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.getManager;
/**
* @author Vladislav.Soroka
*/
@@ -45,7 +50,24 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
importProject(
"apply plugin: 'java'"
);
assertNotDelegatedBaseJavaProject();
getCurrentExternalProjectSettings().setResolveModulePerSourceSet(false);
importProject();
assertNotDelegatedMergedBaseJavaProject();
getCurrentExternalProjectSettings().setDelegatedBuild(ThreeState.YES);
importProject();
assertDelegatedMergedBaseJavaProject();
getCurrentExternalProjectSettings().setDelegatedBuild(ThreeState.NO);
// subscribe to the GradleSettings changes topic
((GradleManager)getManager(GradleConstants.SYSTEM_ID)).runActivity(myProject);
GradleSettings.getInstance(myProject).getPublisher().onBuildDelegationChange(false, getProjectPath());
assertNotDelegatedMergedBaseJavaProject();
}
private void assertNotDelegatedBaseJavaProject() {
assertModules("project", "project.main", "project.test");
assertContentRoots("project", getProjectPath());
@@ -63,8 +85,9 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
assertModuleOutput("project.main", getProjectPath() + mainClassesOutputPath, "");
assertModuleOutput("project.test", "", getProjectPath() + testClassesOutputPath);
}
importProjectUsingSingeModulePerGradleProject();
private void assertNotDelegatedMergedBaseJavaProject() {
assertModules("project");
assertDefaultGradleJavaProjectFoldersForMergedModule("project");
@@ -75,10 +98,9 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
getProjectPath() + "/out/test/resources");
assertModuleOutput("project", getProjectPath() + "/out/production/classes", getProjectPath() + "/out/test/classes");
}
getCurrentExternalProjectSettings().setDelegatedBuild(ThreeState.YES);
importProject();
private void assertDelegatedMergedBaseJavaProject() {
if (isGradle40orNewer()) {
assertModuleOutputs("project",
getProjectPath() + "/build/classes/java/main",