From 2f2f008f5ae3b52e9ff44eb0e914516b3afc1843 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 7 Nov 2013 17:52:13 +0400 Subject: [PATCH] Gradle: add Tooling API extra models support. Related task: IDEA-79466 gradle support should generate web module configuration --- .../externalSystem/model/DataNode.java | 5 + .../src/META-INF/gradle-javaee-plugin.xml | 8 + ...s.plugins.gradle.model.ModelBuilderService | 16 ++ .../JavaEEGradleProjectResolverExtension.java | 61 +++++++ .../gradle/model/ModelBuilderService.java | 28 ++++ .../plugins/gradle/model/WarModel.java | 35 ++++ .../gradle/model/data/WarModelData.java | 116 +++++++++++++ .../model/impl/WarModelBuilderImpl.java | 115 +++++++++++++ .../gradle/model/impl/WarModelImpl.java | 66 ++++++++ .../model/internal/ExtraModelBuilder.java | 50 ++++++ .../plugins/gradle/model/internal/init.gradle | 42 +++++ .../AbstractProjectResolverExtension.java | 155 ++++++++++++++++++ .../BaseGradleProjectResolverExtension.java | 16 +- .../project/GradleExecutionHelper.java | 56 +++++-- .../project/GradleProjectResolver.java | 10 +- .../GradleProjectResolverExtension.java | 7 + .../resolve/GradleCommonClassNames.java | 1 + .../resolve/GradleImplicitContributor.java | 1 + .../gradle/util/GradleEnvironment.java | 1 + 19 files changed, 764 insertions(+), 25 deletions(-) create mode 100644 plugins/gradle/src/META-INF/gradle-javaee-plugin.xml create mode 100644 plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/ModelBuilderService.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/WarModel.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/WarModelData.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelBuilderImpl.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelImpl.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ExtraModelBuilder.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java index d55acc41b5ed..4efac184c2ee 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java @@ -56,6 +56,11 @@ public class DataNode implements Serializable { myParent = parent; } + @Nullable + public DataNode getParent() { + return myParent; + } + @NotNull public DataNode createChild(@NotNull Key key, @NotNull T data) { DataNode result = new DataNode(key, data, this); diff --git a/plugins/gradle/src/META-INF/gradle-javaee-plugin.xml b/plugins/gradle/src/META-INF/gradle-javaee-plugin.xml new file mode 100644 index 000000000000..2a8b4d5051fc --- /dev/null +++ b/plugins/gradle/src/META-INF/gradle-javaee-plugin.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService b/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService new file mode 100644 index 000000000000..9d8e690005f8 --- /dev/null +++ b/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService @@ -0,0 +1,16 @@ +# +# Copyright 2000-2013 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. +# +org.jetbrains.plugins.gradle.model.impl.WarModelBuilderImpl diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java new file mode 100644 index 000000000000..07621c62d7af --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2013 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.integrations.javaee; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.Order; +import com.intellij.util.containers.ContainerUtil; +import org.gradle.tooling.model.idea.IdeaModule; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.WarModel; +import org.jetbrains.plugins.gradle.model.data.WarModelData; +import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import java.util.Set; + +/** + * {@link JavaEEGradleProjectResolverExtension} provides JavaEE project info based on gradle tooling API models. + * + * @author Vladislav.Soroka + * @since 10/14/13 + */ +@Order(ExternalSystemConstants.UNORDERED) +public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolverExtension { + private static final Logger LOG = Logger.getInstance(JavaEEGradleProjectResolverExtension.class); + + @Override + public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode ideModule) { + WarModel warModel = resolverCtx.getExtraProject(gradleModule, WarModel.class); + if (warModel != null) { + WarModelData warModelData = new WarModelData(GradleConstants.SYSTEM_ID, warModel.getWebAppDirName(), warModel.getWebAppDir()); + warModelData.setWebXml(warModel.getWebXml()); + warModelData.setWebRoots(warModel.getWebRoots()); + ideModule.createChild(WarModelData.KEY, warModelData); + } + + nextResolver.populateModuleExtraModels(gradleModule, ideModule); + } + + @NotNull + @Override + public Set getExtraProjectModelClasses() { + return ContainerUtil.set(WarModel.class); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/ModelBuilderService.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/ModelBuilderService.java new file mode 100644 index 000000000000..3a76dbee7c60 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/ModelBuilderService.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2013 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 org.gradle.api.Project; + +/** + * @author Vladislav.Soroka + * @since 11/5/13 + */ +public interface ModelBuilderService { + boolean canBuild(String modelName); + + Object buildAll(String modelName, Project project); +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/WarModel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/WarModel.java new file mode 100644 index 000000000000..29864949abfb --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/WarModel.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2013 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.File; +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 11/5/13 + */ +public interface WarModel extends Serializable { + String getWebAppDirName(); + + File getWebAppDir(); + + File getWebXml(); + + Map> getWebRoots(); +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/WarModelData.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/WarModelData.java new file mode 100644 index 000000000000..80eef6ee3766 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/WarModelData.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2013 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.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 11/6/13 + */ +public class WarModelData extends AbstractExternalEntityData { + private static final long serialVersionUID = 1L; + + @NotNull + public static final Key KEY = Key.create(WarModelData.class, ProjectKeys.MODULE.getProcessingWeight() + 1); + @NotNull + private final String myWebAppDirName; + @Nullable + private final File myWebAppDir; + @Nullable + private File myWebXml; + @NotNull + private Map> myWebRoots; + + + public WarModelData(@NotNull ProjectSystemId owner, @NotNull String webAppDirName, @Nullable File webAppDir) { + super(owner); + myWebAppDirName = webAppDirName; + myWebAppDir = webAppDir; + myWebRoots = Collections.emptyMap(); + } + + @NotNull + public String getWebAppDirName() { + return myWebAppDirName; + } + + @Nullable + public File getWebAppDir() { + return myWebAppDir; + } + + public void setWebXml(@Nullable File webXml) { + myWebXml = webXml; + } + + @Nullable + public File getWebXml() { + return myWebXml; + } + + public void setWebRoots(@Nullable Map> webRoots) { + myWebRoots = webRoots == null ? Collections.>emptyMap() : webRoots; + } + + @NotNull + public Map> getWebRoots() { + return myWebRoots; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + WarModelData that = (WarModelData)o; + + if (myWebAppDir != null ? !myWebAppDir.equals(that.myWebAppDir) : that.myWebAppDir != null) return false; + if (!myWebAppDirName.equals(that.myWebAppDirName)) return false; + if (!myWebRoots.equals(that.myWebRoots)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myWebAppDirName.hashCode(); + result = 31 * result + myWebRoots.hashCode(); + return result; + } + + @Override + public String toString() { + return "WarModelData{" + + "myWebAppDirName='" + myWebAppDirName + '\'' + + ", myWebAppDir=" + myWebAppDir + + ", myWebXml=" + myWebXml + + ", myWebRoots=" + myWebRoots + + '}'; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelBuilderImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelBuilderImpl.java new file mode 100644 index 000000000000..0e55d6b2998b --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelBuilderImpl.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2013 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.impl; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.file.FileVisitor; +import org.gradle.api.internal.file.copy.CopySpecInternal; +import org.gradle.api.plugins.WarPlugin; +import org.gradle.api.tasks.bundling.War; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.ModelBuilderService; +import org.jetbrains.plugins.gradle.model.WarModel; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 11/5/13 + */ +public class WarModelBuilderImpl implements ModelBuilderService { + + private static final String WEB_APP_DIR_PROPERTY = "webAppDir"; + private static final String WEB_APP_DIR_NAME_PROPERTY = "webAppDirName"; + + @Override + public boolean canBuild(String modelName) { + return WarModel.class.getName().equals(modelName); + } + + @Nullable + @Override + public Object buildAll(String modelName, Project project) { + final WarPlugin warPlugin = project.getPlugins().findPlugin(WarPlugin.class); + if (warPlugin == null) return null; + + final String webAppDirName = !project.hasProperty(WEB_APP_DIR_NAME_PROPERTY) ? + "src/main/webapp" : String.valueOf(project.property(WEB_APP_DIR_NAME_PROPERTY)); + + final File webAppDir = !project.hasProperty(WEB_APP_DIR_PROPERTY) ? null : (File)project.property(WEB_APP_DIR_PROPERTY); + + WarModelImpl warModel = new WarModelImpl(webAppDirName, webAppDir); + + for (Task task : project.getTasks()) { + if (task instanceof War) { + final War warTask = (War)task; + warModel.setWebXml(warTask.getWebXml()); + + final Map> webRoots = new HashMap>(); + warTask.getRootSpec().walk(new Action() { + @Override + public void execute(CopySpecInternal internal) { + final String relativePath = internal.getDestPath().getPathString(); + internal.getSource().visit(new FileVisitor() { + @Override + public void visitDir(FileVisitDetails dirDetails) { + try { + addPath(webRoots, relativePath, dirDetails.getFile().getPath()); + } + catch (Exception ignore) { + } + } + + @Override + public void visitFile(FileVisitDetails fileDetails) { + try { + if (warTask.getWebXml() == null || + !fileDetails.getFile().getCanonicalPath().equals(warTask.getWebXml().getCanonicalPath())) { + addPath(webRoots, relativePath, fileDetails.getFile().getParent()); + } + } + catch (Exception ignore) { + } + } + }); + } + }); + + warModel.setWebRoots(webRoots); + break; + } + } + + return warModel; + } + + private static void addPath(Map> webRoots, String relativePath, String path) { + relativePath = relativePath == null ? "" : relativePath; + Set paths = webRoots.get(relativePath); + if (paths == null) { + paths = new LinkedHashSet(); + webRoots.put(relativePath, paths); + } + paths.add(path); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelImpl.java new file mode 100644 index 000000000000..47aa662be085 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/impl/WarModelImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2013 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.impl; + +import org.jetbrains.plugins.gradle.model.WarModel; + +import java.io.File; +import java.util.Map; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 11/5/13 + */ +public class WarModelImpl implements WarModel { + private final String myWebAppDirName; + private final File myWebAppDir; + private File myWebXml; + private Map> myWebRoots; + + public WarModelImpl(String webAppDirName, File webAppDir) { + myWebAppDirName = webAppDirName; + myWebAppDir = webAppDir; + } + + @Override + public String getWebAppDirName() { + return myWebAppDirName; + } + + @Override + public File getWebAppDir() { + return myWebAppDir; + } + + public void setWebXml(File webXml) { + myWebXml = webXml; + } + + @Override + public File getWebXml() { + return myWebXml; + } + + @Override + public Map> getWebRoots() { + return myWebRoots; + } + + public void setWebRoots(Map> webRoots) { + myWebRoots = webRoots; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ExtraModelBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ExtraModelBuilder.java new file mode 100644 index 000000000000..2619557bd761 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ExtraModelBuilder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2013 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.internal; + +import org.gradle.api.Project; +import org.gradle.tooling.provider.model.ToolingModelBuilder; +import org.jetbrains.plugins.gradle.model.ModelBuilderService; + +import java.util.ServiceLoader; + +/** + * @author Vladislav.Soroka + * @since 11/5/13 + */ +@SuppressWarnings("UnusedDeclaration") +public class ExtraModelBuilder implements ToolingModelBuilder { + private static ServiceLoader buildersLoader = + ServiceLoader.load(ModelBuilderService.class, ExtraModelBuilder.class.getClassLoader()); + + @Override + public boolean canBuild(String modelName) { + for (ModelBuilderService service : buildersLoader) { + if (service.canBuild(modelName)) return true; + } + return false; + } + + @Override + public Object buildAll(String modelName, Project project) { + for (ModelBuilderService service : buildersLoader) { + if (service.canBuild(modelName)) { + return service.buildAll(modelName, project); + } + } + throw new IllegalArgumentException("Unsupported model: " + modelName); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle new file mode 100644 index 000000000000..a788cb85d83d --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2013 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. + */ +import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry + +@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.jetExtraModelBuilderClass.newInstance()) + } +} + +try { + URL[] urls = [new File("${JAR_PATH}").toURI().toURL()] + URLClassLoader classLoader = new URLClassLoader(urls, getClass().classLoader) + Class modelClass = classLoader.loadClass('org.jetbrains.plugins.gradle.model.internal.ExtraModelBuilder') + allprojects { + ext.jetExtraModelBuilderClass = modelClass + apply plugin: JetGradlePlugin + } +} +catch (all) { +} \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java new file mode 100644 index 000000000000..cde22784ba0a --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2013 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; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.configurations.SimpleJavaParameters; +import com.intellij.externalSystem.JavaProjectData; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.ExternalSystemException; +import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.model.task.TaskData; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.Order; +import com.intellij.openapi.util.KeyValue; +import org.gradle.tooling.model.idea.IdeaModule; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * {@link org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension} provides dummy implementation of Gradle project resolver. + * + * @author Vladislav.Soroka + * @since 10/14/13 + */ +@Order(ExternalSystemConstants.UNORDERED) +public abstract class AbstractProjectResolverExtension implements GradleProjectResolverExtension { + + @NotNull protected ProjectResolverContext resolverCtx; + @NotNull protected GradleProjectResolverExtension nextResolver; + + @Override + public void setProjectResolverContext(@NotNull ProjectResolverContext projectResolverContext) { + resolverCtx = projectResolverContext; + } + + @Override + public void setNext(@Nullable GradleProjectResolverExtension next) { + // there always should be at least gradle basic resolver further in the chain + //noinspection ConstantConditions + assert next != null; + nextResolver = next; + } + + @Nullable + @Override + public GradleProjectResolverExtension getNext() { + return nextResolver; + } + + @NotNull + @Override + public ProjectData createProject() { + return nextResolver.createProject(); + } + + @NotNull + @Override + public JavaProjectData createJavaProjectData() { + return nextResolver.createJavaProjectData(); + } + + @NotNull + @Override + public ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData) { + return nextResolver.createModule(gradleModule, projectData); + } + + @Override + public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode ideModule) { + nextResolver.populateModuleExtraModels(gradleModule, ideModule); + } + + @Override + public void populateModuleContentRoots(@NotNull IdeaModule gradleModule, + @NotNull DataNode ideModule) { + nextResolver.populateModuleContentRoots(gradleModule, ideModule); + } + + + @Override + public void populateModuleCompileOutputSettings(@NotNull IdeaModule gradleModule, + @NotNull DataNode ideModule) { + nextResolver.populateModuleCompileOutputSettings(gradleModule, ideModule); + } + + @Override + public void populateModuleDependencies(@NotNull IdeaModule gradleModule, + @NotNull DataNode ideModule, + @NotNull DataNode ideProject) { + nextResolver.populateModuleDependencies(gradleModule, ideModule, ideProject); + } + + @NotNull + @Override + public Collection populateModuleTasks(@NotNull IdeaModule gradleModule, + @NotNull DataNode ideModule, + @NotNull DataNode ideProject) + throws IllegalArgumentException, IllegalStateException { + return nextResolver.populateModuleTasks(gradleModule, ideModule, ideProject); + } + + @NotNull + @Override + public Collection filterRootProjectTasks(@NotNull List allTasks) { + return nextResolver.filterRootProjectTasks(allTasks); + } + + @NotNull + @Override + public Set getExtraProjectModelClasses() { + return Collections.emptySet(); + } + + @NotNull + @Override + public List> getExtraJvmArgs() { + return Collections.emptyList(); + } + + @NotNull + @Override + public ExternalSystemException getUserFriendlyError(@NotNull Throwable error, + @NotNull String projectPath, + @Nullable String buildFilePath) { + return nextResolver.getUserFriendlyError(error, projectPath, buildFilePath); + } + + @Override + public void enhanceRemoteProcessing(@NotNull SimpleJavaParameters parameters) throws ExecutionException { + } + + @Override + public void enhanceLocalProcessing(@NotNull List urls) { + } +} 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 9ede22404ece..1738364d4e77 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 @@ -127,10 +127,14 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver return new ModuleData(GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName, - projectData.getIdeProjectFileDirectoryPath(), + moduleConfigPath, moduleConfigPath); } + @Override + public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode ideModule) { + } + @Override public void populateModuleContentRoots(@NotNull IdeaModule gradleModule, @NotNull DataNode ideModule) { @@ -139,13 +143,11 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver return; } for (IdeaContentRoot gradleContentRoot : contentRoots) { - if (gradleContentRoot == null) { - continue; - } + if (gradleContentRoot == null) continue; + File rootDirectory = gradleContentRoot.getRootDirectory(); - if (rootDirectory == null) { - continue; - } + if (rootDirectory == null) continue; + ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, rootDirectory.getAbsolutePath()); ideModule.getData().setModuleFileDirectoryPath(ideContentRoot.getRootPath()); populateContentRoot(ideContentRoot, ExternalSystemSourceType.SOURCE, gradleContentRoot.getSourceDirectories()); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java index 8c01c6282d13..0ff1ded48da7 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.gradle.service.project; import com.intellij.execution.configurations.CommandLineTokenizer; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.ExternalSystemException; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent; @@ -24,6 +25,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtilRt; import com.intellij.util.Function; +import com.intellij.util.PathUtil; import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; @@ -42,6 +44,7 @@ import org.jetbrains.plugins.gradle.util.GradleUtil; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.util.List; @@ -53,6 +56,7 @@ import java.util.concurrent.TimeUnit; */ public class GradleExecutionHelper { + private static final Logger LOG = Logger.getInstance(GradleExecutionHelper.class); @SuppressWarnings("MethodMayBeStatic") @NotNull @@ -61,8 +65,7 @@ public class GradleExecutionHelper { @Nullable GradleExecutionSettings settings, @NotNull ProjectConnection connection, @NotNull ExternalSystemTaskNotificationListener listener, - @NotNull List extraJvmArgs) - { + @NotNull List extraJvmArgs) { ModelBuilder result = connection.model(modelType); prepare(result, id, settings, listener, extraJvmArgs, connection); return result; @@ -74,8 +77,7 @@ public class GradleExecutionHelper { @NotNull ProjectConnection connection, @Nullable GradleExecutionSettings settings, @NotNull ExternalSystemTaskNotificationListener listener, - @Nullable final String vmOptions) - { + @Nullable final String vmOptions) { BuildLauncher result = connection.newBuild(); List extraJvmArgs = vmOptions == null ? ContainerUtil.emptyList() : ContainerUtil.newArrayList(vmOptions.trim()); prepare(result, id, settings, listener, extraJvmArgs, connection); @@ -90,8 +92,7 @@ public class GradleExecutionHelper { @NotNull ExternalSystemTaskNotificationListener listener, @Nullable final String vmOptions, @NotNull final OutputStream standardOutput, - @NotNull final OutputStream standardError) - { + @NotNull final OutputStream standardError) { BuildLauncher result = connection.newBuild(); List extraJvmArgs = vmOptions == null ? ContainerUtil.emptyList() : ContainerUtil.newArrayList(vmOptions.trim()); prepare(result, id, settings, listener, extraJvmArgs, connection, standardOutput, standardError); @@ -118,8 +119,7 @@ public class GradleExecutionHelper { @NotNull List extraJvmArgs, @NotNull ProjectConnection connection, @NotNull final OutputStream standardOutput, - @NotNull final OutputStream standardError) - { + @NotNull final OutputStream standardError) { if (settings == null) { return; } @@ -129,7 +129,7 @@ public class GradleExecutionHelper { String vmOptions = settings.getDaemonVmOptions(); if (!StringUtil.isEmpty(vmOptions)) { CommandLineTokenizer tokenizer = new CommandLineTokenizer(vmOptions); - while(tokenizer.hasMoreTokens()) { + while (tokenizer.hasMoreTokens()) { String vmOption = tokenizer.nextToken(); if (!StringUtil.isEmpty(vmOption)) { jvmArgs.add(vmOption); @@ -167,14 +167,15 @@ public class GradleExecutionHelper { if (projectPathFile.isFile() && projectPath.endsWith(GradleConstants.EXTENSION) && projectPathFile.getParent() != null) { projectDir = projectPathFile.getParent(); - } else { + } + else { projectDir = projectPath; } // This is a workaround to get right base dir in case of 'PROJECT' setting used in case custom wrapper property file location // see org.gradle.wrapper.PathAssembler#getBaseDir for details String userDir = null; - if(settings != null && settings.getDistributionType() == DistributionType.WRAPPED) { + if (settings != null && settings.getDistributionType() == DistributionType.WRAPPED) { try { userDir = System.getProperty("user.dir"); System.setProperty("user.dir", projectDir); @@ -227,7 +228,9 @@ public class GradleExecutionHelper { "gradle.taskGraph.afterTask { Task task ->", " if (task instanceof Wrapper) {", " def wrapperPropertyFileLocation = task.jarFile.getCanonicalPath() - '.jar' + '.properties'", - " new File('" + StringUtil.escapeBackSlashes(wrapperPropertyFileLocation.getCanonicalPath()) + "').write wrapperPropertyFileLocation", + " new File('" + + StringUtil.escapeBackSlashes(wrapperPropertyFileLocation.getCanonicalPath()) + + "').write wrapperPropertyFileLocation", "}}", }; FileUtil.writeToFile(tempFile, StringUtil.join(lines, SystemProperties.getLineSeparator())); @@ -263,21 +266,20 @@ public class GradleExecutionHelper { /** * Allows to retrieve gradle api connection to use for the given project. * - * @param projectPath target project path - * @param settings execution settings to use + * @param projectPath target project path + * @param settings execution settings to use * @return connection to use * @throws IllegalStateException if it's not possible to create the connection */ @NotNull private static ProjectConnection getConnection(@NotNull String projectPath, @Nullable GradleExecutionSettings settings) - throws IllegalStateException - { + throws IllegalStateException { File projectDir = new File(projectPath); GradleConnector connector = GradleConnector.newConnector(); int ttl = -1; - if(settings != null) { + if (settings != null) { //noinspection EnumSwitchStatementWhichMissesCases switch (settings.getDistributionType()) { case LOCAL: @@ -357,4 +359,24 @@ public class GradleExecutionHelper { field.set(obj, fieldValue); field.setAccessible(isAccessible); } + + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + public static void setInitScript(LongRunningOperation longRunningOperation) { + try { + InputStream stream = GradleProjectResolver.class.getResourceAsStream("/org/jetbrains/plugins/gradle/model/internal/init.gradle"); + if (stream == null) return; + + String jarPath = PathUtil.getCanonicalPath(PathUtil.getJarPathForClass(GradleProjectResolver.class)); + String s = FileUtil.loadTextAndClose(stream).replace("${JAR_PATH}", jarPath); + + final File tempFile = FileUtil.createTempFile("ijinit", '.' + GradleConstants.EXTENSION, true); + FileUtil.writeToFile(tempFile, s); + + String[] buildExecutorArgs = new String[]{"--init-script", tempFile.getAbsolutePath()}; + longRunningOperation.withArguments(buildExecutorArgs); + } + catch (Exception e) { + LOG.warn("Can't use IJ gradle init script", e); + } + } } 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 34f044ca3591..4da7f14e7a1d 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 @@ -49,6 +49,7 @@ import org.jetbrains.plugins.gradle.remote.impl.GradleLibraryNamesMixer; import org.jetbrains.plugins.gradle.settings.ClassHolder; import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings; import org.jetbrains.plugins.gradle.util.GradleConstants; +import org.jetbrains.plugins.gradle.util.GradleEnvironment; import java.util.*; @@ -152,6 +153,11 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver, IdeaModule> pair : moduleMap.values()) { final DataNode moduleDataNode = pair.first; final IdeaModule ideaModule = pair.second; + projectResolverChain.populateModuleExtraModels(ideaModule, moduleDataNode); projectResolverChain.populateModuleContentRoots(ideaModule, moduleDataNode); projectResolverChain.populateModuleCompileOutputSettings(ideaModule, moduleDataNode); projectResolverChain.populateModuleDependencies(ideaModule, moduleDataNode, projectDataNode); @@ -251,7 +258,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver ideModule); + /** * Populates {@link com.intellij.openapi.externalSystem.model.ProjectKeys#CONTENT_ROOT) content roots} of the given ide module on the basis of the information * contained at the given gradle module. diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java index 1279557c87b3..ef446f1545b8 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleCommonClassNames.java @@ -40,6 +40,7 @@ public final class GradleCommonClassNames { @NonNls public static final String GRADLE_API_DEFAULT_TASK = "org.gradle.api.DefaultTask"; @NonNls public static final String GRADLE_API_TASKS_DELETE = "org.gradle.api.tasks.Delete"; @NonNls public static final String GRADLE_API_TASKS_BUNDLING_JAR = "org.gradle.api.tasks.bundling.Jar"; + @NonNls public static final String GRADLE_API_TASKS_BUNDLING_WAR = "org.gradle.api.tasks.bundling.War"; @NonNls public static final String GRADLE_API_TASKS_COMPILE_JAVA_COMPILE = "org.gradle.api.tasks.compile.JavaCompile"; @NonNls public static final String GRADLE_API_TASKS_WRAPPER_WRAPPER = "org.gradle.api.tasks.wrapper.Wrapper"; @NonNls public static final String GRADLE_API_TASKS_JAVADOC_JAVADOC = "org.gradle.api.tasks.javadoc.Javadoc"; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleImplicitContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleImplicitContributor.java index c64b19e6cb90..84ab010aa46c 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleImplicitContributor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleImplicitContributor.java @@ -56,6 +56,7 @@ public class GradleImplicitContributor implements GradleMethodContextContributor new Pair("buildNeeded", GRADLE_API_DEFAULT_TASK), new Pair("clean", GRADLE_API_TASKS_DELETE), new Pair("jar", GRADLE_API_TASKS_BUNDLING_JAR), + new Pair("war", GRADLE_API_TASKS_BUNDLING_WAR), new Pair("classes", GRADLE_API_DEFAULT_TASK), new Pair("compileJava", GRADLE_API_TASKS_COMPILE_JAVA_COMPILE), new Pair("compileTestJava", GRADLE_API_DEFAULT_TASK), diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java index e51148504da7..0a91825aabd5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NonNls; public class GradleEnvironment { @NonNls public static final boolean DEBUG_GRADLE_HOME_PROCESSING = Boolean.getBoolean("gradle.debug.home.processing"); + @NonNls public static final boolean USE_ENHANCED_TOOLING_API = Boolean.getBoolean("gradle.use.enhanced.tooling.api"); private GradleEnvironment() { }