From ea7502be30fd6bb7c481a5e0b8aee5289432e3f0 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Fri, 11 Aug 2017 20:26:21 +0300 Subject: [PATCH] Gradle: allow to import IDE configuration from external systems IDEA-174087 --- .../options/CompilerUIConfigurable.java | 4 +- .../externalSystem/model/ProjectKeys.java | 7 +- .../model/project/ConfigurationData.java | 28 ++++ .../project/manage/ConfigurationHandler.java | 41 ++++++ .../model/ConfigurationDataImpl.java | 76 ++++++++++ .../manage/CompilerConfigurationHandler.java | 138 ++++++++++++++++++ .../manage/ConfigurationDataService.java | 99 +++++++++++++ .../ExternalSystemExtensionPoints.xml | 2 + .../src/META-INF/ExternalSystemExtensions.xml | 3 + .../BaseGradleProjectResolverExtension.java | 15 ++ .../gradle/model/IntelliJProjectSettings.java | 22 +++ .../gradle/model/IntelliJSettings.java | 25 ++++ ...plugins.gradle.tooling.ModelBuilderService | 2 + .../gradle/model/DefaultIntelliJSettings.java | 34 +++++ .../IntelliJProjectSettingsBuilder.groovy | 58 ++++++++ .../builder/IntelliJSettingsBuilder.groovy | 58 ++++++++ .../tooling/internal/DynamicSettings.groovy | 130 +++++++++++++++++ .../gradle/tooling/internal/init/init.gradle | 10 +- 18 files changed, 745 insertions(+), 7 deletions(-) create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ConfigurationData.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationHandler.java create mode 100644 platform/external-system-impl/src/com/intellij/openapi/externalSystem/model/ConfigurationDataImpl.java create mode 100644 platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/CompilerConfigurationHandler.java create mode 100644 platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationDataService.java create mode 100644 plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJProjectSettings.java create mode 100644 plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJSettings.java create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultIntelliJSettings.java create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJProjectSettingsBuilder.groovy create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJSettingsBuilder.groovy create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/DynamicSettings.groovy diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java index cb33c31dd9ed..f8b3c2736825 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java @@ -244,13 +244,13 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura if (!myDisabledSettings.contains(Setting.RESOURCE_PATTERNS)) { configuration.removeResourceFilePatterns(); String extensionString = myResourcePatternsField.getText().trim(); - applyResourcePatterns(extensionString, (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject)); + applyResourcePatterns(extensionString, configuration); } BuildManager.getInstance().clearState(myProject); } - private static void applyResourcePatterns(String extensionString, final CompilerConfigurationImpl configuration) + public static void applyResourcePatterns(String extensionString, final CompilerConfigurationImpl configuration) throws ConfigurationException { StringTokenizer tokenizer = new StringTokenizer(extensionString, ";", false); List errors = new ArrayList<>(); diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectKeys.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectKeys.java index c8d57d0cb26d..446174903533 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectKeys.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectKeys.java @@ -21,9 +21,6 @@ import org.jetbrains.annotations.NotNull; /** * Holds common project entity {@link Key keys}. - * - * @author Denis Zhdanov - * @since 4/12/13 11:52 AM */ public class ProjectKeys { @@ -34,7 +31,9 @@ public class ProjectKeys { @NotNull public static final Key MODULE_DEPENDENCY = Key.create(ModuleDependencyData.class, 130); @NotNull public static final Key LIBRARY_DEPENDENCY = Key.create(LibraryDependencyData.class, 150); - @NotNull public static final Key TASK = Key.create(TaskData.class, 250); + @NotNull public static final Key TASK = Key.create(TaskData.class, 250); + + @NotNull public static final Key CONFIGURATION = Key.create(ConfigurationData.class, 350); private ProjectKeys() { } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ConfigurationData.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ConfigurationData.java new file mode 100644 index 000000000000..ab290219471c --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ConfigurationData.java @@ -0,0 +1,28 @@ +/* + * 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 com.intellij.openapi.externalSystem.model.project; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vladislav.Soroka + */ +@ApiStatus.Experimental +public interface ConfigurationData extends ExternalEntityData { + + Object find(@NotNull String query); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationHandler.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationHandler.java new file mode 100644 index 000000000000..fe240b3f3520 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationHandler.java @@ -0,0 +1,41 @@ +/* + * 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 com.intellij.openapi.externalSystem.service.project.manage; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.externalSystem.model.project.ConfigurationData; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vladislav.Soroka + */ +@ApiStatus.Experimental +public interface ConfigurationHandler { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.externalSystemConfigurationHandler"); + + default void apply(@NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider, + @NotNull ConfigurationData configuration) {} + + + default void apply(@NotNull Module module, + @NotNull IdeModifiableModelsProvider modelsProvider, + @NotNull ConfigurationData configuration) {} +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/model/ConfigurationDataImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/model/ConfigurationDataImpl.java new file mode 100644 index 000000000000..d3ac3444cca8 --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/model/ConfigurationDataImpl.java @@ -0,0 +1,76 @@ +/* + * 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 com.intellij.openapi.externalSystem.model; + +import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData; +import com.intellij.openapi.externalSystem.model.project.ConfigurationData; +import com.intellij.openapi.util.text.StringUtil; +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.io.JsonReaderEx; +import org.jetbrains.io.JsonUtil; + +import java.util.Map; + +/** + * @author Vladislav.Soroka + */ +@ApiStatus.Experimental +public class ConfigurationDataImpl extends AbstractExternalEntityData implements ConfigurationData { + + private static final long serialVersionUID = 1L; + + @Language("JSON") @NotNull private final String myData; + @Nullable transient private volatile Object myJsonObject; + + public ConfigurationDataImpl(@NotNull ProjectSystemId owner, @Language("JSON") @NotNull String data) { + super(owner); + myData = data; + } + + @Language("JSON") + @NotNull + public String getJsonString() { + return myData; + } + + @Override + public Object find(@NotNull String query) { + if (StringUtil.isEmpty(query)) return null; + + Object jsonObject = getJsonObject(); + for (String part : StringUtil.split(query, ".")) { + if (jsonObject instanceof Map) { + jsonObject = ((Map)jsonObject).get(part); + } + else { + return null; + } + } + return jsonObject; + } + + public Object getJsonObject() { + if (myJsonObject == null) { + JsonReaderEx reader = new JsonReaderEx(myData); + reader.setLenient(true); + myJsonObject = JsonUtil.nextAny(reader); + } + return myJsonObject; + } +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/CompilerConfigurationHandler.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/CompilerConfigurationHandler.java new file mode 100644 index 000000000000..a09f1abd2e88 --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/CompilerConfigurationHandler.java @@ -0,0 +1,138 @@ +/* + * 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 com.intellij.openapi.externalSystem.service.project.manage; + +import com.intellij.compiler.CompilerConfiguration; +import com.intellij.compiler.CompilerConfigurationImpl; +import com.intellij.compiler.CompilerWorkspaceConfiguration; +import com.intellij.compiler.options.CompilerUIConfigurable; +import com.intellij.compiler.server.BuildManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.project.ConfigurationData; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Map; + +/** + * @author Vladislav.Soroka + */ +public class CompilerConfigurationHandler implements ConfigurationHandler { + + private static final Logger LOG = Logger.getInstance(CompilerConfigurationHandler.class); + + @Override + public void apply(@NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider, + @NotNull ConfigurationData configuration) { + Object obj = configuration.find("compiler"); + if (!(obj instanceof Map)) return; + Map configurationMap = ((Map)obj); + + ApplicationManager.getApplication().invokeLater(() -> { + final CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project); + final CompilerWorkspaceConfiguration workspaceConfiguration = CompilerWorkspaceConfiguration.getInstance(project); + + boolean changed = false; + String resourcePatterns = getString(configurationMap, "resourcePatterns"); + if (resourcePatterns != null) { + try { + String[] resourceFilePatterns = compilerConfiguration.getResourceFilePatterns(); + compilerConfiguration.removeResourceFilePatterns(); + CompilerUIConfigurable.applyResourcePatterns(resourcePatterns, compilerConfiguration); + if (!Arrays.equals(resourceFilePatterns, compilerConfiguration.getResourceFilePatterns())) { + changed = true; + } + } + catch (ConfigurationException e) { + LOG.warn("Unable to apply compiler resource patterns", e); + } + } + + Number processHeapSize = getNumber(configurationMap, "processHeapSize"); + if (processHeapSize != null && processHeapSize.intValue() != compilerConfiguration.getBuildProcessHeapSize(0)) { + compilerConfiguration.setBuildProcessHeapSize(processHeapSize.intValue()); + changed = true; + } + Boolean autoShowFirstErrorInEditor = getBoolean(configurationMap, "autoShowFirstErrorInEditor"); + if (autoShowFirstErrorInEditor != null && workspaceConfiguration.AUTO_SHOW_ERRORS_IN_EDITOR != autoShowFirstErrorInEditor) { + workspaceConfiguration.AUTO_SHOW_ERRORS_IN_EDITOR = autoShowFirstErrorInEditor; + changed = true; + } + Boolean displayNotificationPopup = getBoolean(configurationMap, "displayNotificationPopup"); + if (displayNotificationPopup != null && workspaceConfiguration.DISPLAY_NOTIFICATION_POPUP != displayNotificationPopup) { + workspaceConfiguration.DISPLAY_NOTIFICATION_POPUP = displayNotificationPopup; + changed = true; + } + Boolean clearOutputDirectory = getBoolean(configurationMap, "clearOutputDirectory"); + if (clearOutputDirectory != null && workspaceConfiguration.CLEAR_OUTPUT_DIRECTORY != clearOutputDirectory) { + workspaceConfiguration.CLEAR_OUTPUT_DIRECTORY = clearOutputDirectory; + changed = true; + } + Boolean addNotNullAssertions = getBoolean(configurationMap, "addNotNullAssertions"); + if (addNotNullAssertions != null && compilerConfiguration.isAddNotNullAssertions() != addNotNullAssertions) { + compilerConfiguration.setAddNotNullAssertions(addNotNullAssertions); + changed = true; + } + Boolean enableAutomake = getBoolean(configurationMap, "enableAutomake"); + if (enableAutomake != null && workspaceConfiguration.MAKE_PROJECT_ON_SAVE != enableAutomake) { + workspaceConfiguration.MAKE_PROJECT_ON_SAVE = enableAutomake; + changed = true; + } + Boolean parallelCompilation = getBoolean(configurationMap, "parallelCompilation"); + if (parallelCompilation != null && workspaceConfiguration.PARALLEL_COMPILATION != parallelCompilation) { + workspaceConfiguration.PARALLEL_COMPILATION = parallelCompilation; + changed = true; + } + Boolean rebuildOnDependencyChange = getBoolean(configurationMap, "rebuildModuleOnDependencyChange"); + if (rebuildOnDependencyChange != null && workspaceConfiguration.REBUILD_ON_DEPENDENCY_CHANGE != rebuildOnDependencyChange) { + workspaceConfiguration.REBUILD_ON_DEPENDENCY_CHANGE = rebuildOnDependencyChange; + changed = true; + } + String additionalVmOptions = getString(configurationMap, "additionalVmOptions"); + if (additionalVmOptions != null && workspaceConfiguration.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS != additionalVmOptions) { + workspaceConfiguration.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS = additionalVmOptions; + changed = true; + } + + if (changed) { + BuildManager.getInstance().clearState(project); + } + }); + } + + @Nullable + private static Boolean getBoolean(Map map, String key) { + Object o = map.get(key); + return o instanceof Boolean ? (Boolean)o : null; + } + + private static Number getNumber(Map map, String key) { + Object o = map.get(key); + return o instanceof Number ? (Number)o : null; + } + + @Nullable + private static String getString(Map map, String key) { + Object o = map.get(key); + return o instanceof String ? (String)o : null; + } +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationDataService.java new file mode 100644 index 000000000000..52674de7affb --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ConfigurationDataService.java @@ -0,0 +1,99 @@ +/* + * 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 com.intellij.openapi.externalSystem.service.project.manage; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.ProjectKeys; +import com.intellij.openapi.externalSystem.model.project.ConfigurationData; +import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.Order; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +import static com.intellij.openapi.externalSystem.service.project.manage.AbstractModuleDataService.MODULE_KEY; + +/** + * @author Vladislav.Soroka + */ +@ApiStatus.Experimental +@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER) +public class ConfigurationDataService extends AbstractProjectDataService { + private static final Logger LOG = Logger.getInstance(ConfigurationDataService.class); + + @NotNull + @Override + public Key getTargetDataKey() { + return ProjectKeys.CONFIGURATION; + } + + @Override + public void importData(@NotNull Collection> toImport, + @Nullable ProjectData projectData, + @NotNull Project project, + @NotNull IdeModifiableModelsProvider modelsProvider) { + if (toImport.isEmpty()) { + return; + } + + final DataNode javaProjectDataNode = toImport.iterator().next(); + final DataNode projectDataNode = ExternalSystemApiUtil.findParent(javaProjectDataNode, ProjectKeys.PROJECT); + + assert projectDataNode != null; + + DataNode projectConfigurationNode = ExternalSystemApiUtil.find(projectDataNode, ProjectKeys.CONFIGURATION); + if (projectConfigurationNode != null) { + if (ExternalSystemApiUtil.isOneToOneMapping(project, projectDataNode.getData())) { + LOG.warn("This external project are not the only project in the current IDE workspace, " + + "found project level configuration can override the configuration came from other external projects."); + } + + for (ConfigurationHandler handler : ConfigurationHandler.EP_NAME.getExtensions()) { + handler.apply(project, modelsProvider, projectConfigurationNode.getData()); + } + } + + for (DataNode node : toImport) { + if (node == projectConfigurationNode) continue; + + DataNode moduleDataNode = ExternalSystemApiUtil.findParent(node, ProjectKeys.MODULE); + if (moduleDataNode != null) { + Module module = moduleDataNode.getUserData(MODULE_KEY); + module = module != null ? module : modelsProvider.findIdeModule(moduleDataNode.getData()); + + if (module == null) { + LOG.warn(String.format( + "Can't import module level configuration. Reason: target module (%s) is not found at the ide", moduleDataNode)); + continue; + } + + for (ConfigurationHandler handler : ConfigurationHandler.EP_NAME.getExtensions()) { + handler.apply(module, modelsProvider, node.getData()); + } + } + } + } +} \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml b/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml index fbae0861c659..e2449214c515 100644 --- a/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml @@ -5,6 +5,8 @@ interface="com.intellij.openapi.externalSystem.service.project.ExternalProjectsWorkspaceImpl$Contributor"/> + + + + 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 39afc8ebb5e6..d233eb5cec2a 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 @@ -22,6 +22,7 @@ import com.intellij.execution.configurations.SimpleJavaParameters; import com.intellij.externalSystem.JavaProjectData; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.ConfigurationDataImpl; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.ExternalSystemException; import com.intellij.openapi.externalSystem.model.ProjectKeys; @@ -162,6 +163,12 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver ideProject.createChild(ExternalProjectDataService.KEY, externalProject); ideProject.getData().setDescription(externalProject.getDescription()); } + + final IntelliJSettings intellijSettings = resolverCtx.getExtraProject(IntelliJProjectSettings.class); + if (intellijSettings != null) { + ideProject.createChild(ProjectKeys.CONFIGURATION, + new ConfigurationDataImpl(GradleConstants.SYSTEM_ID, intellijSettings.getSettings())); + } } @NotNull @@ -299,6 +306,12 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver } ideModule.createChild(GradleExtensionsDataService.KEY, extensions); } + + final IntelliJSettings intellijSettings = resolverCtx.getExtraProject(gradleModule, IntelliJSettings.class); + if (intellijSettings != null) { + ideModule.createChild(ProjectKeys.CONFIGURATION, + new ConfigurationDataImpl(GradleConstants.SYSTEM_ID, intellijSettings.getSettings())); + } } @Override @@ -629,6 +642,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver result.add(BuildScriptClasspathModel.class); result.add(GradleExtensions.class); result.add(ExternalProject.class); + result.add(IntelliJProjectSettings.class); + result.add(IntelliJSettings.class); return result; } diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJProjectSettings.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJProjectSettings.java new file mode 100644 index 000000000000..b4da9a7ab37b --- /dev/null +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJProjectSettings.java @@ -0,0 +1,22 @@ +/* + * 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; + +/** + * @author Vladislav.Soroka + */ +public interface IntelliJProjectSettings extends IntelliJSettings { +} diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJSettings.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJSettings.java new file mode 100644 index 000000000000..da8ece68f3c9 --- /dev/null +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/IntelliJSettings.java @@ -0,0 +1,25 @@ +/* + * 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; + +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + */ +public interface IntelliJSettings extends Serializable { + String getSettings(); +} diff --git a/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService b/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService index f01a0d04aadb..c57c83b61309 100644 --- a/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService +++ b/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService @@ -20,3 +20,5 @@ org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImp org.jetbrains.plugins.gradle.tooling.builder.ScalaModelBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ExternalProjectBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ProjectExtensionsDataBuilderImpl +org.jetbrains.plugins.gradle.tooling.builder.IntelliJSettingsBuilder +org.jetbrains.plugins.gradle.tooling.builder.IntelliJProjectSettingsBuilder diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultIntelliJSettings.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultIntelliJSettings.java new file mode 100644 index 000000000000..280654d337d8 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultIntelliJSettings.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * @author Vladislav.Soroka + */ +public class DefaultIntelliJSettings implements IntelliJSettings { + private static final long serialVersionUID = 1L; + + private final String mySettings; + + public DefaultIntelliJSettings(String settings) { + mySettings = settings; + } + + @Override + public String getSettings() { + return mySettings; + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJProjectSettingsBuilder.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJProjectSettingsBuilder.groovy new file mode 100644 index 000000000000..f7f7fcb339fa --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJProjectSettingsBuilder.groovy @@ -0,0 +1,58 @@ +/* + * 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.tooling.builder + +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.gradle.plugins.ide.idea.IdeaPlugin +import org.jetbrains.annotations.NotNull +import org.jetbrains.plugins.gradle.model.DefaultIntelliJSettings +import org.jetbrains.plugins.gradle.model.IntelliJProjectSettings +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService +import org.jetbrains.plugins.gradle.tooling.internal.DynamicSettings + +/** + * @author Vladislav.Soroka + */ +class IntelliJProjectSettingsBuilder implements ModelBuilderService { + + @Override + boolean canBuild(String modelName) { + modelName == IntelliJProjectSettings.name + } + + @Override + Object buildAll(String modelName, Project project) { + if (project.parent == null) { + ExtensionAware extensionAware = project.plugins.findPlugin(IdeaPlugin.class)?.model?.project as ExtensionAware + if (extensionAware) { + def object = extensionAware.extensions.findByName("settings") + if (object instanceof DynamicSettings) { + return new DefaultIntelliJSettings(object.toString()) + } + } + } + return null + } + + @Override + ErrorMessageBuilder getErrorMessageBuilder(@NotNull Project project, @NotNull Exception e) { + return ErrorMessageBuilder + .create(project, e, "IntelliJ settings import errors") + .withDescription("Unable to build IntelliJ project settings") + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJSettingsBuilder.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJSettingsBuilder.groovy new file mode 100644 index 000000000000..ea4754aaad2e --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/IntelliJSettingsBuilder.groovy @@ -0,0 +1,58 @@ +/* + * 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.tooling.builder + +import groovy.transform.CompileStatic +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.gradle.plugins.ide.idea.IdeaPlugin +import org.jetbrains.annotations.NotNull +import org.jetbrains.plugins.gradle.model.DefaultIntelliJSettings +import org.jetbrains.plugins.gradle.model.IntelliJSettings +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService +import org.jetbrains.plugins.gradle.tooling.internal.DynamicSettings + +/** + * @author Vladislav.Soroka + */ +@CompileStatic +class IntelliJSettingsBuilder implements ModelBuilderService { + + @Override + boolean canBuild(String modelName) { + modelName == IntelliJSettings.name + } + + @Override + Object buildAll(String modelName, Project project) { + ExtensionAware extensionAware = project.plugins.findPlugin(IdeaPlugin.class)?.model?.module as ExtensionAware + if (extensionAware) { + def object = extensionAware.extensions.findByName("settings") + if (object instanceof DynamicSettings) { + return new DefaultIntelliJSettings(object.toString()) + } + } + return null + } + + @Override + ErrorMessageBuilder getErrorMessageBuilder(@NotNull Project project, @NotNull Exception e) { + return ErrorMessageBuilder + .create(project, e, "IntelliJ settings import errors") + .withDescription("Unable to build IntelliJ project settings") + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/DynamicSettings.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/DynamicSettings.groovy new file mode 100644 index 000000000000..93d80c33b278 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/DynamicSettings.groovy @@ -0,0 +1,130 @@ +/* + * 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.tooling.internal + +import groovy.json.JsonOutput + +/** + * @author Vladislav.Soroka + */ +class DynamicSettings extends GroovyObjectSupport { + def objects = [:] + + DynamicSettings() { + } + + def methodMissing(String name, def args) { + if (args.length == 1 && args[0] instanceof Closure) { + def value = JDelegate.cloneDelegateAndGetContent((Closure)args[0]) + objects.put(name, value) + return objects + } + } + + def propertyMissing(String name) { + def content = super.getContent() + if (content instanceof Map) return content.get(name) + } + + @Override + String toString() { + return JsonOutput.toJson(this.objects) +// return JsonOutput.prettyPrint(JsonOutput.toJson(this.objects)) + } +} + +class JDelegate extends GroovyObjectSupport { + private Object content + + JDelegate() { + } + + Object invokeMethod(String name, Object args) { + Object val = null + if (args != null && Object[].class.isAssignableFrom(args.getClass())) { + Object[] arr = (Object[])((Object[])args) + if (arr.length == 1) { + val = arr[0] + } + else if (isIterableOrArrayAndClosure(arr)) { + Closure closure = (Closure)arr[1] + Iterator iterator = arr[0] instanceof Iterable ? ((Iterable)arr[0]).iterator() : + Arrays.asList((Object[])((Object[])arr[0])).iterator() + ArrayList list = new ArrayList() + + while (iterator.hasNext()) { + list.add(curryDelegateAndGetContent(closure, iterator.next())) + } + + val = list + } + else { + val = Arrays.asList(arr) + } + } + + if (!content) { + content = new LinkedHashMap() + } + + if (content instanceof Map) { + if (content.containsKey(name)) { + def list = new ArrayList() + content.each { k, v -> list.add([k, v]) } + content = list + } + else { + this.content.put(name, val) + } + } + + if (content instanceof List) { + content.add([name, val]) + } + return val + } + + static boolean isIterableOrArrayAndClosure(Object[] args) { + if (args.length == 2 && args[1] instanceof Closure) { + return args[0] instanceof Iterable || args[0] != null && args[0].getClass().isArray() + } + else { + return false + } + } + + static Object cloneDelegateAndGetContent(Closure c) { + JDelegate delegate = new JDelegate() + Closure cloned = (Closure)c.clone() + cloned.setDelegate(delegate) + cloned.setResolveStrategy(1) + cloned.call() + return delegate.getContent() + } + + static Object curryDelegateAndGetContent(Closure c, Object o) { + JDelegate delegate = new JDelegate() + Closure curried = c.curry(o) + curried.setDelegate(delegate) + curried.setResolveStrategy(1) + curried.call() + return delegate.getContent() + } + + Object getContent() { + return this.content + } +} \ No newline at end of file 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 a08f515b09ac..e1a253dc6cd7 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 @@ -1,6 +1,6 @@ - import org.gradle.api.internal.project.ProjectInternal import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry +import org.jetbrains.plugins.gradle.tooling.internal.DynamicSettings import org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder System.setProperty("idea.active", "true") @@ -57,3 +57,11 @@ class JetGradlePlugin implements Plugin { } } } + +allprojects { + apply plugin: 'idea' + if (parent == null) { + idea.project.extensions.create('settings', DynamicSettings) + } + idea.module.extensions.create('settings', DynamicSettings) +}