diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/ExternalSystemUiAware.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/ExternalSystemUiAware.java index e503e8314e6e..e3a6d19a71ae 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/ExternalSystemUiAware.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/ExternalSystemUiAware.java @@ -16,6 +16,7 @@ package com.intellij.openapi.externalSystem; import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,6 +31,11 @@ public interface ExternalSystemUiAware { @NotNull String getProjectRepresentationName(@NotNull String targetProjectPath, @Nullable String rootProjectPath); + @NotNull + default String getProjectRepresentationName(@NotNull Project project, @NotNull String targetProjectPath, @Nullable String rootProjectPath){ + return getProjectRepresentationName(targetProjectPath, rootProjectPath); + } + @Nullable FileChooserDescriptor getExternalProjectConfigDescriptor(); diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/ExternalProjectSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/ExternalProjectSettings.java index 2e8f9616291e..ba97513e8a31 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/ExternalProjectSettings.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/ExternalProjectSettings.java @@ -101,7 +101,7 @@ public abstract class ExternalProjectSettings implements Comparable(myModules); + receiver.myModules = myModules != null ? new HashSet<>(myModules) : new HashSet<>(); receiver.myUseAutoImport = myUseAutoImport; receiver.myCreateEmptyContentRootDirectories = myCreateEmptyContentRootDirectories; } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/AbstractExternalSystemTaskConfigurationType.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/AbstractExternalSystemTaskConfigurationType.java index 61fa1d609587..ed4862a01687 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/AbstractExternalSystemTaskConfigurationType.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/AbstractExternalSystemTaskConfigurationType.java @@ -12,6 +12,7 @@ import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo; import com.intellij.openapi.externalSystem.service.ui.DefaultExternalSystemUiAware; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemBundle; +import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NotNullLazyValue; @@ -145,7 +146,8 @@ public abstract class AbstractExternalSystemTaskConfigurationType implements Con projectName = null; } else { - projectName = ExternalSystemApiUtil.getProjectRepresentationName(externalProjectPath, rootProjectPath); + final ExternalSystemUiAware uiAware = ExternalSystemUiUtil.getUiAware(externalSystemId); + projectName = uiAware.getProjectRepresentationName(project, externalProjectPath, rootProjectPath); } if (!StringUtil.isEmptyOrSpaces(projectName)) { buffer.append(projectName); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectPathField.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectPathField.java index b36f708795bd..8d2c9d1013a8 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectPathField.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectPathField.java @@ -219,12 +219,12 @@ public class ExternalProjectPathField extends ComponentWithBrowseButton> entry : settings.getAvailableProjects().entrySet()) { if (entry.getKey().getPath().equals(rawText)) { - collapse(editor, uiAware.getProjectRepresentationName(entry.getKey().getPath(), null)); + collapse(editor, uiAware.getProjectRepresentationName(project, entry.getKey().getPath(), null)); return; } for (ExternalProjectPojo pojo : entry.getValue()) { if (pojo.getPath().equals(rawText)) { - collapse(editor, uiAware.getProjectRepresentationName(pojo.getPath(), entry.getKey().getPath())); + collapse(editor, uiAware.getProjectRepresentationName(project, pojo.getPath(), entry.getKey().getPath())); return; } } diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index a511ea10b310..012485f07132 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -129,6 +129,7 @@ + diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java index 1edb7f0ce80d..041a7bcda8d7 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java @@ -58,6 +58,7 @@ import icons.GradleIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter; +import org.jetbrains.plugins.gradle.model.data.BuildParticipant; import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData; import org.jetbrains.plugins.gradle.service.GradleInstallationManager; import org.jetbrains.plugins.gradle.service.project.GradleAutoImportAware; @@ -75,6 +76,8 @@ import java.io.IOException; import java.net.URL; import java.util.*; +import static com.intellij.openapi.util.io.FileUtil.pathsEqual; + /** * @author Denis Zhdanov * @since 4/10/13 1:19 PM @@ -180,7 +183,7 @@ public class GradleManager result.setResolveModulePerSourceSet(projectLevelSettings.isResolveModulePerSourceSet()); } - configureExecutionWorkspace(projectLevelSettings, settings, result, project); + configureExecutionWorkspace(projectLevelSettings, settings, result, project, pair.second); return result; }; } @@ -191,12 +194,31 @@ public class GradleManager private static void configureExecutionWorkspace(@Nullable GradleProjectSettings compositeRootSettings, GradleSettings settings, GradleExecutionSettings result, - Project project) { - if(compositeRootSettings == null) return; + Project project, + String projectPath) { + if (compositeRootSettings == null || compositeRootSettings.getCompositeBuild() == null) return; + + GradleProjectSettings.CompositeBuild compositeBuild = compositeRootSettings.getCompositeBuild(); + if (compositeBuild.getCompositeDefinitionSource() == CompositeDefinitionSource.SCRIPT) { + if (pathsEqual(compositeRootSettings.getExternalProjectPath(), projectPath)) return; + + for (BuildParticipant buildParticipant : compositeBuild.getCompositeParticipants()) { + if (pathsEqual(buildParticipant.getRootPath(), projectPath)) continue; + if (buildParticipant.getProjects().stream().anyMatch(path -> pathsEqual(path, projectPath))) { + continue; + } + result.getExecutionWorkspace().addBuildParticipant(new GradleBuildParticipant(buildParticipant.getRootPath())); + } + return; + } for (GradleProjectSettings projectSettings : settings.getLinkedProjectsSettings()) { if (projectSettings == compositeRootSettings) continue; - if (!compositeRootSettings.getCompositeParticipants().contains(projectSettings.getExternalProjectPath())) continue; + if (compositeBuild.getCompositeParticipants() + .stream() + .noneMatch(participant -> pathsEqual(participant.getRootPath(), projectSettings.getExternalProjectPath()))) { + continue; + } GradleBuildParticipant buildParticipant = new GradleBuildParticipant(projectSettings.getExternalProjectPath()); ExternalProjectInfo projectData = ProjectDataManager.getInstance() @@ -287,6 +309,22 @@ public class GradleManager return ExternalSystemApiUtil.getProjectRepresentationName(targetProjectPath, rootProjectPath); } + @NotNull + @Override + public String getProjectRepresentationName(@NotNull Project project, + @NotNull String targetProjectPath, + @Nullable String rootProjectPath) { + GradleProjectSettings projectSettings = GradleSettings.getInstance(project).getLinkedProjectSettings(targetProjectPath); + if (projectSettings != null && projectSettings.getCompositeBuild() != null) { + for (BuildParticipant buildParticipant : projectSettings.getCompositeBuild().getCompositeParticipants()) { + if (buildParticipant.getProjects().contains(targetProjectPath)) { + return ExternalSystemApiUtil.getProjectRepresentationName(targetProjectPath, buildParticipant.getRootPath()); + } + } + } + return ExternalSystemApiUtil.getProjectRepresentationName(targetProjectPath, rootProjectPath); + } + @Nullable @Override public String getAffectedExternalProjectPath(@NotNull String changedFileOrDirPath, @NotNull Project project) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenProjectCompositeConfigurationAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenProjectCompositeConfigurationAction.java index 768f56104801..d02bf47feec3 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenProjectCompositeConfigurationAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenProjectCompositeConfigurationAction.java @@ -20,6 +20,8 @@ import com.intellij.openapi.externalSystem.action.ExternalSystemAction; import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys; import com.intellij.openapi.externalSystem.view.ProjectNode; import com.intellij.openapi.project.Project; +import org.jetbrains.plugins.gradle.settings.CompositeDefinitionSource; +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; import org.jetbrains.plugins.gradle.settings.GradleSettings; import org.jetbrains.plugins.gradle.ui.GradleProjectCompositeSelectorDialog; @@ -41,7 +43,19 @@ public class GradleOpenProjectCompositeConfigurationAction extends ExternalSyste protected boolean isVisible(AnActionEvent e) { final Project project = getProject(e); if (project == null) return false; - return GradleSettings.getInstance(project).getLinkedProjectsSettings().size() > 1; + if (GradleSettings.getInstance(project).getLinkedProjectsSettings().size() > 1) { + final ProjectNode projectNode = ExternalSystemDataKeys.SELECTED_PROJECT_NODE.getData(e.getDataContext()); + if (projectNode == null || projectNode.getData() == null) return false; + + GradleProjectSettings projectSettings = + GradleSettings.getInstance(project).getLinkedProjectSettings(projectNode.getData().getLinkedExternalProjectPath()); + GradleProjectSettings.CompositeBuild compositeBuild = null; + if (projectSettings != null) { + compositeBuild = projectSettings.getCompositeBuild(); + } + if (compositeBuild == null || compositeBuild.getCompositeDefinitionSource() == CompositeDefinitionSource.IDE) return true; + } + return false; } @Override diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java new file mode 100644 index 000000000000..e2228afcb465 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildParticipant.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2017 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.gradle.model.data; + +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.util.xmlb.annotations.OptionTag; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 2/18/2017 + */ +@Tag("build") +public class BuildParticipant implements Serializable { + private String myRootPath; + @NotNull private Set myProjects = new HashSet<>(); + + @Attribute("path") + public String getRootPath() { + return myRootPath; + } + + public void setRootPath(String rootPath) { + myRootPath = rootPath; + } + + @AbstractCollection(surroundWithTag = false, elementTag = "project", elementValueAttribute = "path") + @OptionTag(tag = "projects", nameAttribute = "") + @NotNull + public Set getProjects() { + return myProjects; + } + + public void setProjects(@NotNull Set projects) { + myProjects = projects; + } + + public BuildParticipant copy() { + BuildParticipant result = new BuildParticipant(); + result.myRootPath = myRootPath; + result.myProjects = new HashSet<>(myProjects); + return result; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/CompositeBuildData.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/CompositeBuildData.java new file mode 100644 index 000000000000..4b9788ca173b --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/CompositeBuildData.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2017 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.gradle.model.data; + +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.ProjectKeys; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 2/17/2017 + */ +public class CompositeBuildData { + + @NotNull + public static final Key KEY = Key.create(CompositeBuildData.class, ProjectKeys.PROJECT.getProcessingWeight() + 1); + + private final String myRootProjectPath; + @NotNull private List myCompositeParticipants = ContainerUtil.newArrayList(); + + public CompositeBuildData(String rootProjectPath) { + myRootProjectPath = rootProjectPath; + } + + public String getRootProjectPath() { + return myRootProjectPath; + } + + @NotNull + public List getCompositeParticipants() { + return myCompositeParticipants; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index e05b0a9dcad0..9755dbea54e7 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -76,6 +76,7 @@ import org.jetbrains.plugins.gradle.util.GradleConstants; import org.slf4j.impl.Log4jLoggerFactory; import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -539,7 +540,17 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver final String moduleConfigPath = ideModule.getData().getLinkedExternalProjectPath(); ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class); - final String rootProjectPath = ideProject.getData().getLinkedExternalProjectPath(); + String rootProjectPath = ideProject.getData().getLinkedExternalProjectPath(); + try { + GradleBuild build = resolverCtx.getExtraProject(gradleModule, GradleBuild.class); + if (build != null) { + rootProjectPath = ExternalSystemApiUtil.toCanonicalPath(build.getRootProject().getProjectDirectory().getCanonicalPath()); + } + } + catch (IOException e) { + LOG.warn("construction of the canonical path for the module fails", e); + } + final boolean isFlatProject = !FileUtil.isAncestor(rootProjectPath, moduleConfigPath, false); if (externalProject != null) { for (ExternalTask task : externalProject.getTasks().values()) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 56a4526c5a8f..df308930c2d5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -43,6 +43,7 @@ import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.MultiMap; import org.gradle.tooling.*; import org.gradle.tooling.model.DomainObjectSet; +import org.gradle.tooling.model.GradleProject; import org.gradle.tooling.model.build.BuildEnvironment; import org.gradle.tooling.model.gradle.GradleBuild; import org.gradle.tooling.model.idea.BasicIdeaProject; @@ -52,6 +53,8 @@ import org.gradle.util.GradleVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.model.*; +import org.jetbrains.plugins.gradle.model.data.BuildParticipant; +import org.jetbrains.plugins.gradle.model.data.CompositeBuildData; import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData; import org.jetbrains.plugins.gradle.remote.impl.GradleLibraryNamesMixer; import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper; @@ -62,6 +65,7 @@ import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings; import org.jetbrains.plugins.gradle.util.GradleConstants; import java.io.File; +import java.io.IOException; import java.util.*; import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.attachGradleSdkSources; @@ -160,7 +164,18 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver= 0; + isCompositeBuildsSupported = isGradleProjectDirSupported && gradleVersion.compareTo(GradleVersion.version("3.1")) >= 0; + } + final ProjectImportAction projectImportAction = + new ProjectImportAction(resolverCtx.isPreviewMode(), isGradleProjectDirSupported, isCompositeBuildsSupported); final List> extraJvmArgs = new ArrayList<>(); final List commandLineArgs = ContainerUtil.newArrayList(); @@ -212,12 +227,6 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver buildActionExecutor = resolverCtx.getConnection().action(projectImportAction); File initScript = GradleExecutionHelper.generateInitScript(isBuildSrcProject, toolingExtensionClasses); @@ -299,6 +308,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver gradleModulesWithIncludedBuilds = exposeCompositeBuild(allModels, projectDataNode, gradleModules); + final Map, IdeaModule>> moduleMap = ContainerUtilRt.newHashMap(); final Map, ExternalSourceSet>> sourceSetsMap = ContainerUtil.newHashMap(); projectDataNode.putUserData(RESOLVED_SOURCE_SETS, sourceSetsMap); @@ -311,7 +322,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver, IdeaModule> pair : moduleMap.values()) { @@ -401,6 +414,52 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver exposeCompositeBuild(ProjectImportAction.AllModels allModels, + DataNode projectDataNode, + DomainObjectSet gradleModules) { + List gradleModulesWithIncludedBuilds = ContainerUtil.newArrayList(gradleModules.getAll()); + List includedBuilds = allModels.getIncludedBuilds(); + if (!includedBuilds.isEmpty()) { + ProjectData projectData = projectDataNode.getData(); + CompositeBuildData compositeBuildData = new CompositeBuildData(projectData.getLinkedExternalProjectPath()); + for (IdeaProject project : includedBuilds) { + if (!project.getModules().isEmpty()) { + BuildParticipant buildParticipant = new BuildParticipant(); + gradleModulesWithIncludedBuilds.addAll(project.getModules()); + GradleProject gradleProject = project.getModules().getAt(0).getGradleProject(); + String projectPath = null; + do { + try { + projectPath = ExternalSystemApiUtil.toCanonicalPath(gradleProject.getProjectDirectory().getCanonicalPath()); + } + catch (IOException e) { + LOG.warn("construction of the canonical path for the module fails", e); + } + } + while ((gradleProject = gradleProject.getParent()) != null); + if (projectPath != null) { + buildParticipant.setRootPath(projectPath); + for (IdeaModule module : project.getModules()) { + try { + String modulePath = + ExternalSystemApiUtil.toCanonicalPath(module.getGradleProject().getProjectDirectory().getCanonicalPath()); + buildParticipant.getProjects().add(modulePath); + } + catch (IOException e) { + LOG.warn("construction of the canonical path for the module fails", e); + } + } + + compositeBuildData.getCompositeParticipants().add(buildParticipant); + } + } + } + projectDataNode.createChild(CompositeBuildData.KEY, compositeBuildData); + } + return gradleModulesWithIncludedBuilds; + } + private static void mergeLibraryAndModuleDependencyData(DataNode projectDataNode, @Nullable File gradleHomeDir, @Nullable GradleVersion gradleVersion) { @@ -419,8 +478,6 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver libraryDependencyDataNode : libraryDependencies) { - if (!libraryDependencyDataNode.getChildren().isEmpty()) continue; - final DataNode libraryNodeParent = libraryDependencyDataNode.getParent(); if (libraryNodeParent == null) continue; @@ -430,6 +487,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver unprocessedPaths = ContainerUtil.newLinkedList(libraryPaths); while (!unprocessedPaths.isEmpty()) { final String path = unprocessedPaths.remove(); @@ -505,7 +564,12 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver moduleDependencyNode = libraryNodeParent.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData); + if(projectDependencyCandidate) { + for (DataNode node : libraryDependencyDataNode.getChildren()) { + moduleDependencyNode.addChild(node); + } + } } libraryPaths.removeAll(targetModuleOutputPaths); unprocessedPaths.removeAll(targetModuleOutputPaths); @@ -564,7 +628,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver extractExternalProjectModels(ProjectImportAction.AllModels models, boolean isPreview) { final Class modelClazz = isPreview ? ExternalProjectPreview.class : ExternalProject.class; - final ExternalProject externalRootProject = models.getExtraProject(null, modelClazz); + final ExternalProject externalRootProject = models.getExtraProject(null, modelClazz); if (externalRootProject == null) return Collections.emptyMap(); final DefaultExternalProject wrappedExternalRootProject = new DefaultExternalProject(externalRootProject); @@ -573,7 +637,11 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver gradleModules = models.getIdeaProject().getModules(); if (gradleModules != null && !gradleModules.isEmpty()) { - for (IdeaModule ideaModule : gradleModules) { + List gradleModulesWithIncludedBuilds = ContainerUtil.newArrayList(gradleModules.getAll()); + for (IdeaProject project : models.getIncludedBuilds()) { + gradleModulesWithIncludedBuilds.addAll(project.getModules()); + } + for (IdeaModule ideaModule : gradleModulesWithIncludedBuilds) { final ExternalProject externalProject = externalProjectsMap.get(getModuleId(ideaModule)); if (externalProject != null) { models.addExtraProject(externalProject, ExternalProject.class, ideaModule); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/CompositeBuildDataService.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/CompositeBuildDataService.java new file mode 100644 index 000000000000..005c2a23da8f --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/CompositeBuildDataService.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2017 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.gradle.service.project.data; + +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; +import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.Order; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.data.BuildParticipant; +import org.jetbrains.plugins.gradle.model.data.CompositeBuildData; +import org.jetbrains.plugins.gradle.settings.CompositeDefinitionSource; +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; +import org.jetbrains.plugins.gradle.settings.GradleSettings; + +import java.util.Collection; + +/** + * @author Vladislav.Soroka + * @since 2/17/2017 + */ +@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER) +public class CompositeBuildDataService extends AbstractProjectDataService { + + @NotNull + @Override + public Key getTargetDataKey() { + return CompositeBuildData.KEY; + } + + public void importData(@NotNull final Collection> toImport, + @Nullable ProjectData projectData, + @NotNull final Project project, + @NotNull IdeModifiableModelsProvider modelsProvider) { + if (toImport.isEmpty()) { + if (projectData != null) { + GradleProjectSettings projectSettings = + GradleSettings.getInstance(project).getLinkedProjectSettings(projectData.getLinkedExternalProjectPath()); + if (projectSettings != null) { + projectSettings.setCompositeBuild(null); + } + } + return; + } + if (toImport.size() != 1) { + throw new IllegalArgumentException( + String.format("Expected to get a single composite data node but got %d: %s", toImport.size(), toImport)); + } + CompositeBuildData compositeBuildData = toImport.iterator().next().getData(); + GradleProjectSettings projectSettings = + GradleSettings.getInstance(project).getLinkedProjectSettings(compositeBuildData.getRootProjectPath()); + if (projectSettings != null) { + GradleProjectSettings.CompositeBuild compositeBuild = new GradleProjectSettings.CompositeBuild(); + compositeBuild.setCompositeDefinitionSource(CompositeDefinitionSource.SCRIPT); + for (BuildParticipant buildParticipant : compositeBuildData.getCompositeParticipants()) { + compositeBuild.getCompositeParticipants().add(buildParticipant.copy()); + } + projectSettings.setCompositeBuild(compositeBuild); + } + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java index 07dd91f0bd9e..4548f8209adf 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java @@ -334,7 +334,7 @@ public class IdeaGradleProjectSettingsControlBuilder implements GradleProjectSet @Override public void apply(GradleProjectSettings settings) { - settings.setCompositeParticipants(myInitialSettings.getCompositeParticipants()); + settings.setCompositeBuild(myInitialSettings.getCompositeBuild()); if (myGradleHomePathField != null) { String gradleHomePath = FileUtil.toCanonicalPath(myGradleHomePathField.getText()); if (StringUtil.isEmpty(gradleHomePath)) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/CompositeDefinitionSource.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/CompositeDefinitionSource.java new file mode 100644 index 000000000000..224acd3adff3 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/CompositeDefinitionSource.java @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2017 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.gradle.settings; + +/** + * @author Vladislav.Soroka + * @since 2/17/2017 + */ +public enum CompositeDefinitionSource { + IDE, SCRIPT +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExecutionWorkspace.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExecutionWorkspace.java index 6473b9a3215e..d42321b0882c 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExecutionWorkspace.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExecutionWorkspace.java @@ -15,8 +15,11 @@ */ package org.jetbrains.plugins.gradle.settings; +import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.util.Pair; import com.intellij.util.containers.ContainerUtil; +import org.gradle.tooling.model.idea.IdeaModule; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; @@ -25,6 +28,7 @@ import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; /** * @author Vladislav.Soroka @@ -35,6 +39,7 @@ public class GradleExecutionWorkspace implements Serializable { @NotNull private final List myBuildParticipants = ContainerUtil.newArrayList(); + private Map, IdeaModule>> myModuleMap; public void addBuildParticipant(GradleBuildParticipant participant) { myBuildParticipants.add(participant); @@ -57,10 +62,23 @@ public class GradleExecutionWorkspace implements Serializable { public ModuleData findModuleDataByName(String moduleName) { ModuleData result = null; + + Pair, IdeaModule> modulePair = myModuleMap.get(moduleName); + if(modulePair == null) { + modulePair = myModuleMap.get(":" + moduleName); + } + if (modulePair != null) { + return modulePair.first.getData(); + } + for (GradleBuildParticipant buildParticipant : myBuildParticipants) { result = buildParticipant.findModuleDataByName(moduleName); if (result != null) break; } return result; } + + public void addModuleMap(Map, IdeaModule>> moduleMap) { + myModuleMap = moduleMap; + } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleProjectSettings.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleProjectSettings.java index caa14a2a0b5e..be59c6679fed 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleProjectSettings.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleProjectSettings.java @@ -17,12 +17,17 @@ package org.jetbrains.plugins.gradle.settings; import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil; import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; +import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.data.BuildParticipant; -import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /** * @author Denis Zhdanov @@ -31,11 +36,43 @@ import java.util.Set; public class GradleProjectSettings extends ExternalProjectSettings { @Nullable private String myGradleHome; - @Nullable private String myGradleJvm = ExternalSystemJdkUtil.USE_PROJECT_JDK;; + @Nullable private String myGradleJvm = ExternalSystemJdkUtil.USE_PROJECT_JDK; @Nullable private DistributionType distributionType; private boolean disableWrapperSourceDistributionNotification; private boolean resolveModulePerSourceSet = true; - @Nullable private Set myCompositeParticipants = new HashSet<>(); + @Nullable private CompositeBuild myCompositeBuild; + @Nullable private Set myNonCompositeModules = new HashSet<>(); + + @OptionTag("modules") + @Nullable + public Set getNonCompositeModules() { + Set modules = getModules(); + if (myCompositeBuild != null && !modules.isEmpty()) { + Set compositePaths = myCompositeBuild.getCompositeParticipants().stream() + .flatMap(participant -> participant.getProjects().stream()).collect(Collectors.toSet()); + return modules.stream().filter(m -> !compositePaths.contains(m)).collect(Collectors.toSet()); + } + else { + return myNonCompositeModules; + } + } + + public void setNonCompositeModules(@Nullable Set modules) { + myNonCompositeModules = modules; + } + + @Transient + @NotNull + @Override + public Set getModules() { + return super.getModules(); + } + + @Override + public void setModules(@Nullable Set modules) { + super.setModules(modules); + setNonCompositeModules(modules); + } @Nullable public String getGradleHome() { @@ -80,13 +117,14 @@ public class GradleProjectSettings extends ExternalProjectSettings { this.resolveModulePerSourceSet = useIdeModulePerSourceSet; } - @NotNull - public Set getCompositeParticipants() { - return myCompositeParticipants == null ? Collections.emptySet() : myCompositeParticipants; + @OptionTag(tag = "compositeConfiguration", nameAttribute = "") + @Nullable + public CompositeBuild getCompositeBuild() { + return myCompositeBuild; } - public void setCompositeParticipants(@Nullable Set compositeParticipants) { - myCompositeParticipants = compositeParticipants; + public void setCompositeBuild(@Nullable CompositeBuild compositeBuild) { + myCompositeBuild = compositeBuild; } @NotNull @@ -99,7 +137,45 @@ public class GradleProjectSettings extends ExternalProjectSettings { result.distributionType = distributionType; result.disableWrapperSourceDistributionNotification = disableWrapperSourceDistributionNotification; result.resolveModulePerSourceSet = resolveModulePerSourceSet; - result.myCompositeParticipants = myCompositeParticipants != null ? new HashSet<>(myCompositeParticipants) : null; + result.myCompositeBuild = myCompositeBuild != null ? myCompositeBuild.copy() : null; return result; } + + @Tag("compositeBuild") + public static class CompositeBuild { + @Nullable private CompositeDefinitionSource myCompositeDefinitionSource; + private List myCompositeParticipants = new SmartList<>(); + + @Attribute + @Nullable + public CompositeDefinitionSource getCompositeDefinitionSource() { + return myCompositeDefinitionSource; + } + + public void setCompositeDefinitionSource(@Nullable CompositeDefinitionSource compositeDefinitionSource) { + myCompositeDefinitionSource = compositeDefinitionSource; + } + + @AbstractCollection(surroundWithTag = false, elementTag = "build") + @OptionTag(tag = "builds", nameAttribute = "") + @NotNull + public List getCompositeParticipants() { + return myCompositeParticipants; + } + + public void setCompositeParticipants(List compositeParticipants) { + myCompositeParticipants = compositeParticipants == null ? new SmartList<>() : ContainerUtil.newArrayList(compositeParticipants); + } + + @NotNull + public CompositeBuild copy() { + CompositeBuild result = new CompositeBuild(); + result.myCompositeParticipants = ContainerUtil.newArrayList(); + for (BuildParticipant participant : myCompositeParticipants) { + result.myCompositeParticipants.add(participant.copy()); + } + result.myCompositeDefinitionSource = myCompositeDefinitionSource; + return result; + } + } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectCompositeSelectorDialog.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectCompositeSelectorDialog.java index dbee928e0083..e1954167fa9f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectCompositeSelectorDialog.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectCompositeSelectorDialog.java @@ -32,6 +32,8 @@ import com.intellij.util.containers.HashSet; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.data.BuildParticipant; +import org.jetbrains.plugins.gradle.settings.CompositeDefinitionSource; import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; import org.jetbrains.plugins.gradle.settings.GradleSettings; import org.jetbrains.plugins.gradle.util.GradleConstants; @@ -43,6 +45,8 @@ import javax.swing.tree.TreeNode; import java.util.List; import java.util.Set; +import static com.intellij.openapi.util.io.FileUtil.pathsEqual; + /** * @author Vladislav.Soroka * @since 10/6/2016 @@ -92,7 +96,19 @@ public class GradleProjectCompositeSelectorDialog extends DialogWrapper { for (Pair pair : pairs) { compositeParticipants.add(pair.second.toString()); } - myCompositeRootSettings.setCompositeParticipants(compositeParticipants.isEmpty() ? null : compositeParticipants); + if (compositeParticipants.isEmpty()) { + myCompositeRootSettings.setCompositeBuild(null); + } + else { + GradleProjectSettings.CompositeBuild compositeBuild = new GradleProjectSettings.CompositeBuild(); + compositeBuild.setCompositeDefinitionSource(CompositeDefinitionSource.IDE); + for (String participant : compositeParticipants) { + BuildParticipant buildParticipant = new BuildParticipant(); + buildParticipant.setRootPath(participant); + compositeBuild.getCompositeParticipants().add(buildParticipant); + } + myCompositeRootSettings.setCompositeBuild(compositeBuild); + } } super.doOKAction(); } @@ -113,7 +129,14 @@ public class GradleProjectCompositeSelectorDialog extends DialogWrapper { List nodes = ContainerUtil.newArrayList(); for (GradleProjectSettings projectSettings : GradleSettings.getInstance(myProject).getLinkedProjectsSettings()) { if (projectSettings == myCompositeRootSettings) continue; - boolean added = myCompositeRootSettings.getCompositeParticipants().contains(projectSettings.getExternalProjectPath()); + if (projectSettings.getCompositeBuild() != null && + projectSettings.getCompositeBuild().getCompositeDefinitionSource() == CompositeDefinitionSource.SCRIPT) { + continue; + } + + GradleProjectSettings.CompositeBuild compositeBuild = myCompositeRootSettings.getCompositeBuild(); + boolean added = compositeBuild != null && compositeBuild.getCompositeParticipants().stream() + .anyMatch(participant -> pathsEqual(participant.getRootPath(), projectSettings.getExternalProjectPath())); String representationName = myExternalSystemUiAware.getProjectRepresentationName( projectSettings.getExternalProjectPath(), projectSettings.getExternalProjectPath()); diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java new file mode 100644 index 000000000000..b8bf57004772 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleCompositeImportingTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2017 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.gradle.importing; + +import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions; +import org.junit.Test; + +import static com.intellij.openapi.roots.DependencyScope.COMPILE; + +/** + * @author Vladislav.Soroka + * @since 2/20/2017 + */ +@SuppressWarnings("JUnit4AnnotatedMethodInJUnit3TestCase") +public class GradleCompositeImportingTest extends GradleImportingTestCase { + @Test + @TargetVersions("3.3+") + public void testBasicCompositeBuild() throws Exception { + createSettingsFile("rootProject.name='adhoc'\n" + + "\n" + + "includeBuild '../my-app'\n" + + "includeBuild '../my-utils'"); + + createProjectSubFile("../my-app/settings.gradle", "rootProject.name = 'my-app'\n"); + createProjectSubFile("../my-app/build.gradle", + "apply plugin: 'java'\n" + + "group 'org.sample'\n" + + "version '1.0'\n" + + "\n" + + "dependencies {\n" + + " compile 'org.sample:number-utils:1.0'\n" + + " compile 'org.sample:string-utils:1.0'\n" + + "}\n"); + + createProjectSubFile("../my-utils/settings.gradle", + "rootProject.name = 'my-utils'\n" + + "include 'number-utils', 'string-utils' "); + createProjectSubFile("../my-utils/build.gradle", + "subprojects {\n" + + " apply plugin: 'java'\n" + + "\n" + + " group 'org.sample'\n" + + " version '1.0'\n" + + "}\n" + + "\n" + + "project(':string-utils') {\n" + + " dependencies {\n" + + " compile 'org.apache.commons:commons-lang3:3.4'\n" + + " }\n" + + "} "); + + importProject(); + + assertModules("adhoc", + "my-app", "my-app_main", "my-app_test", + "my-utils", + "string-utils", "string-utils_test", "string-utils_main", + "number-utils", "number-utils_main", "number-utils_test"); + + String[] rootModules = new String[]{"adhoc", "my-app", "my-utils", "string-utils", "number-utils"}; + for (String rootModule : rootModules) { + assertModuleLibDeps(rootModule); + assertModuleModuleDeps(rootModule); + } + assertModuleModuleDeps("my-app_main", "number-utils_main", "string-utils_main"); + assertModuleModuleDepScope("my-app_main", "number-utils_main", COMPILE); + assertModuleModuleDepScope("my-app_main", "string-utils_main", COMPILE); + assertModuleLibDepScope("my-app_main", "Gradle: org.apache.commons:commons-lang3:3.4", COMPILE); + } +} diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java index 18769829378e..afe2827ca764 100644 --- a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java @@ -21,9 +21,11 @@ import org.gradle.tooling.BuildController; import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter; import org.gradle.tooling.internal.adapter.TargetTypeProvider; import org.gradle.tooling.model.build.BuildEnvironment; +import org.gradle.tooling.model.gradle.GradleBuild; import org.gradle.tooling.model.idea.BasicIdeaProject; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.tooling.model.idea.IdeaProject; +import org.gradle.util.GradleVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,9 +41,17 @@ public class ProjectImportAction implements BuildAction myExtraProjectModelClasses = new HashSet(); private final boolean myIsPreviewMode; + private final boolean myIsGradleProjectDirSupported; + private final boolean myIsCompositeBuildsSupported; public ProjectImportAction(boolean isPreviewMode) { + this(isPreviewMode, false, false); + } + + public ProjectImportAction(boolean isPreviewMode, boolean isGradleProjectDirSupported, boolean isCompositeBuildsSupported) { myIsPreviewMode = isPreviewMode; + myIsGradleProjectDirSupported= isGradleProjectDirSupported; + myIsCompositeBuildsSupported = isCompositeBuildsSupported; } public void addExtraProjectModelClasses(@NotNull Set projectModelClasses) { @@ -60,11 +70,25 @@ public class ProjectImportAction implements BuildAction { + private List includedBuilds = new ArrayList(); + private boolean isGradleProjectDirSupported; + public AllModels(@NotNull IdeaProject ideaProject) { super(ideaProject); } @@ -120,6 +147,11 @@ public class ProjectImportAction implements BuildAction getIncludedBuilds() { + return includedBuilds; + } + @Nullable public BuildEnvironment getBuildEnvironment() { return getExtraProject(BuildEnvironment.class); @@ -131,11 +163,22 @@ public class ProjectImportAction implements BuildAction() - private final myTasksFactory = new TasksFactory() - private SourceSetCachedFinder mySourceSetFinder - @Override boolean canBuild(String modelName) { return ExternalProject.name == modelName || ExternalProjectPreview.name == modelName @@ -59,10 +53,29 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { @Nullable @Override Object buildAll(final String modelName, final Project project) { - ExternalProject externalProject = cache[project.path] - if (externalProject != null) return externalProject + def cache = getOrSetExt(project, 'projects cache: ' + ExternalProject.name, { new HashMap()}) as Map + def tasksFactory = getOrSetExt(project , 'tasks cache: ' + ExternalProject.name, { new TasksFactory()}) as TasksFactory + def sourceSetFinder = getOrSetExt(project , 'sourceSets finder: ' + ExternalProject.name, { new SourceSetCachedFinder(project)}) as SourceSetCachedFinder + return doBuild(modelName, project, cache, tasksFactory, sourceSetFinder) + } - if(!mySourceSetFinder) mySourceSetFinder = new SourceSetCachedFinder(project) + private static getOrSetExt(final Project project, String name, Closure valueProvider) { + def rootProject = project.getRootProject() + def extraProperties = rootProject.extensions.extraProperties + if(!extraProperties.has(name)) { + extraProperties.set(name, valueProvider()) + } + return extraProperties.get(name) + } + + @Nullable + private static Object doBuild(final String modelName, + final Project project, + Map cache, + TasksFactory tasksFactory, + SourceSetCachedFinder sourceSetFinder) { + ExternalProject externalProject = cache[project] + if (externalProject != null) return externalProject def resolveSourceSetDependencies = System.properties.'idea.resolveSourceSetDependencies' as boolean def isPreview = ExternalProjectPreview.name == modelName @@ -76,8 +89,8 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { defaultExternalProject.buildFile = project.buildFile defaultExternalProject.group = wrap(project.group) defaultExternalProject.projectDir = project.projectDir - defaultExternalProject.sourceSets = getSourceSets(project, isPreview, resolveSourceSetDependencies) - defaultExternalProject.tasks = getTasks(project) + defaultExternalProject.sourceSets = getSourceSets(project, isPreview, resolveSourceSetDependencies, sourceSetFinder) + defaultExternalProject.tasks = getTasks(project, tasksFactory) defaultExternalProject.plugins = getPlugins(project) //defaultExternalProject.setProperties(project.getProperties()) @@ -86,13 +99,13 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { final Map childProjects = new HashMap(project.getChildProjects().size()) for (Map.Entry projectEntry : project.getChildProjects().entrySet()) { - final Object externalProjectChild = buildAll(modelName, projectEntry.getValue()) + final Object externalProjectChild = doBuild(modelName, projectEntry.getValue(), cache, tasksFactory, sourceSetFinder) if (externalProjectChild instanceof ExternalProject) { childProjects.put(projectEntry.getKey(), (ExternalProject)externalProjectChild) } } defaultExternalProject.setChildProjects(childProjects) - cache.put(project.getPath(), defaultExternalProject) + cache.put(project, defaultExternalProject) defaultExternalProject } @@ -127,10 +140,10 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { result } - Map getTasks(Project project) { + static Map getTasks(Project project, TasksFactory tasksFactory) { def result = [:] as Map - myTasksFactory.getTasks(project).each { Task task -> + tasksFactory.getTasks(project).each { Task task -> DefaultExternalTask externalTask = result.get(task.name) if (externalTask == null) { externalTask = new DefaultExternalTask() @@ -150,7 +163,7 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { result } - private Map getSourceSets(Project project, boolean isPreview, boolean resolveSourceSetDependencies) { + private static Map getSourceSets(Project project, boolean isPreview, boolean resolveSourceSetDependencies, SourceSetCachedFinder sourceSetFinder) { final IdeaPlugin ideaPlugin = project.getPlugins().findPlugin(IdeaPlugin.class) def ideaPluginModule = ideaPlugin?.model?.module boolean inheritOutputDirs = ideaPluginModule?.inheritOutputDirs ?: false @@ -341,7 +354,7 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { } if(resolveSourceSetDependencies) { - def dependencies = new DependencyResolverImpl(project, isPreview, downloadJavadoc, downloadSources, mySourceSetFinder).resolveDependencies(sourceSet) + def dependencies = new DependencyResolverImpl(project, isPreview, downloadJavadoc, downloadSources, sourceSetFinder).resolveDependencies(sourceSet) externalSourceSet.dependencies.addAll(dependencies) } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/init/init.gradle b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/init/init.gradle index 1687b232a624..c3fe1b749afd 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/init/init.gradle +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/init/init.gradle @@ -13,33 +13,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.gradle.api.internal.project.ProjectInternal import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder -@SuppressWarnings("GrPackage") -class JetGradlePlugin implements Plugin { - private final ToolingModelBuilderRegistry registry - - @javax.inject.Inject - public JetGradlePlugin(ToolingModelBuilderRegistry registry) { - this.registry = registry - } - - public void apply(Project project) { - registry.register(project.extraModelBuilder) - } -} - System.setProperty("idea.active", "true") - initscript { dependencies { classpath files(${EXTENSIONS_JARS_PATH}) } } -def extraModelBuilderInstance = new ExtraModelBuilder() -allprojects { - ext.extraModelBuilder = extraModelBuilderInstance - apply plugin: JetGradlePlugin -} \ No newline at end of file +apply plugin: JetGradlePlugin + +@SuppressWarnings("GrPackage") +class JetGradlePlugin implements Plugin { + void apply(Gradle gradle) { + def extraModelBuilderInstance = new ExtraModelBuilder() + def isCompositeBuildsSupported = GradleVersion.current() >= GradleVersion.version("3.1") + gradle.allprojects { project -> + ((ProjectInternal)project).services.getAll(ToolingModelBuilderRegistry.class).each { it.register(extraModelBuilderInstance) } + try { + if (isCompositeBuildsSupported) { + project.gradle.includedBuilds.each { + it.configuredBuild.services.getAll(ToolingModelBuilderRegistry.class).each { + it.register(extraModelBuilderInstance) + } + } + } + } + catch (ignore) { + } + } + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy index b7aac477dd4d..63fb57f6c90f 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy @@ -242,6 +242,9 @@ class DependencyResolverImpl implements DependencyResolver { dependencies.each {((AbstractExternalDependency)it).scope = compileScope} } else { + if(dependencies) { + ((AbstractExternalDependency)it).scope = compileScope + } resolvedRuntimeMap.put(resolve(it), it) } } diff --git a/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java b/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java index 2c93bdb2c219..a169a5620d0a 100644 --- a/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java +++ b/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java @@ -136,7 +136,8 @@ public abstract class AbstractModelBuilderTest { GradleConnector connector = GradleConnector.newConnector(); - final URI distributionUri = new DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion)); + GradleVersion _gradleVersion = GradleVersion.version(gradleVersion); + final URI distributionUri = new DistributionLocator().getDistributionFor(_gradleVersion); connector.useDistribution(distributionUri); connector.forProjectDirectory(testDir); int daemonMaxIdleTime = 10; @@ -149,7 +150,10 @@ public abstract class AbstractModelBuilderTest { ProjectConnection connection = connector.connect(); try { - final ProjectImportAction projectImportAction = new ProjectImportAction(false); + boolean isGradleProjectDirSupported = _gradleVersion.compareTo(GradleVersion.version("2.4")) >= 0; + boolean isCompositeBuildsSupported = isGradleProjectDirSupported && _gradleVersion.compareTo(GradleVersion.version("3.1")) >= 0; + final ProjectImportAction projectImportAction = new ProjectImportAction(false, isGradleProjectDirSupported, + isCompositeBuildsSupported); projectImportAction.addExtraProjectModelClasses(getModels()); BuildActionExecuter buildActionExecutor = connection.action(projectImportAction); File initScript = GradleExecutionHelper.generateInitScript(false, getToolingExtensionClasses());