Статьи

Ад IntelliJ и Gradle интеграции

Я люблю использовать Gradle в настоящее время. Я люблю IntelliJ еще больше. Тем не менее, интеграция этих двух оказывается настоящей болью в заднице!

Для простых проектов Java все отлично работает. Хотя я должен признать, что держусь подальше от плагина JetGradle, который в лучшем случае можно использовать. Но большую часть времени я все еще использую плагин Gradle IDEA. Это просто работает лучше, и я привык всегда открывать консоль: мне нужно столько же печатать,  gradle idea сколько синхронизировать проект Gradle в IDEA.

Но…

Интеграция WAR просто ужасна, нет, даже ужасна. И плагин Gradle IDEA, и JetGradle не имеют функциональности для добавления веб-фасета в мой модуль. Парни из Gradle на самом деле не склонны добавлять функциональность в свой плагин, и парни из Jetbrains, кажется, блаженно не знают, что проблема была подана более года назад (я сам создал другую, чтобы разбудить их).

Отсутствие автоматического способа синхронизации моего файла сборки Gradle с моей IDE для веб-проектов вынуждает меня использовать Maven для этих проектов или использовать Eclipse в качестве моей IDEA (работает плагин eclipse-wtp). Я не доволен ни одним из этих вариантов.

Предлагаемые решения тоже не очень хороши. Вы можете вручную добавить веб-фасет в свой проект IDEA. Это подвержено ошибкам и не то, что я с нетерпением жду, чтобы делать каждый раз, когда я меняю зависимость. Другое решение еще хуже и включает манипулирование файлами проекта IntelliJ XML из скрипта сборки Gradle. Сколько времени может занять первое решение, это просто чокнутый.

Я сильно опечален. Gradle стал моим главным выбором для создания программного обеспечения. Но если они не могут обеспечить достойную интеграцию с веб-проектами, я вынужден использовать Maven для любых проектов, требующих войны. Gradle, не заставляй меня сожалеть о переходе к вам.

Обновление : Слава Богу, Gradle использует Groovy и имеет отличные возможности редактирования XML. Поэтому я выбрал решение, которое казалось безумным: манипулирование файлами iml и ipr. Потребовалось немного поработать, но следующий фрагмент добавит веб-аспект в ваш проект и создаст в IntelliJ развертываемый артефакт (взорванный WAR). Это не красиво, но работает (по крайней мере, для меня есть несколько вариантов использования, которые не будут работать). Я просто хотел, чтобы ребята из Gradle сделали это в первую очередь, но, возможно, мой код попадет в Gradle. Можно только надеяться.

Во-первых, в вашей buildSrc\src\main\groovy папке есть класс,  который обладает всеми функциями для процесса обогащения (как я его называю).

import org.gradle.api.Project;

public class IdeaEnricher {

    def static updateWebArtifacts(Project gradleProject, Node project) {
        def artifactManager = project.component.find { it.@name == 'ArtifactManager' } as Node
        if (artifactManager) {
            Node artifact = artifactManager.artifact.find { it.@type == 'exploded-war' }
            if (artifact) {
                artifactManager.remove(artifact)
            }
        } else {
            artifactManager = project.appendNode('component', [name: 'ArtifactManager']);
        }
        def builder = new NodeBuilder();
        def artifact = builder.artifact(type: 'exploded-war', name: "${gradleProject.name}/Exploded war") {
            'output-path'("\$PROJECT_DIR\$/build/libs/${gradleProject.name}_exploded.war")
            root(id: 'root') {
                element(id: 'javaee-facet-resources', facet: "${gradleProject.name}/web/Web");
                element(id: 'directory', name: 'WEB-INF') {
                    element(id: 'directory', name: 'classes') {
                        element(id: 'module-output', name: "${gradleProject.name}")
                    }
                    element(id: 'directory', name: 'lib') {
                        gradleProject.configurations.runtime.each {
                            element(id: 'file-copy', path: it)
                        }
                    }
                }
            }
        }
        artifactManager.append artifact

    }

    def static updateWebFacet(Project gradleProject, Node module) {
        def facetManager = module.component.find { it.@name == 'FacetManager' } as Node
        if (facetManager) {
            Node webFacet = facetManager.facet.find { it.@type == 'web' }
            if (webFacet) {
                facetManager.remove(webFacet)
            }
        } else {
            facetManager = module.appendNode('component', [name: 'FacetManager']);
        }
        def builder = new NodeBuilder();
        def webFacet = builder.facet(type: "web", name: 'Web') {
            configuration {
                descriptors {
                    deploymentDescriptor(name: 'web.xml', url: 'file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml')
                }
                webroots {
                    root(url: 'file://$MODULE_DIR$/src/main/webapp', relative: '/')
                }
                sourceRoots {
                    root(url: 'file://$MODULE_DIR$/src/main/java')
                    root(url: 'file://$MODULE_DIR$/src/main/resources')
                }
            }
        }
        facetManager.append webFacet
    }

    def static updateBuildOutputFolderForGradle(Project gradleProject, Node project) {
        def projectRootManager = project.component.find { it.@name == 'ProjectRootManager' } as Node
        Node output = projectRootManager.output.find{it.@url == 'file://$PROJECT_DIR$/out'}
        if(output) {
            output.@url = 'file://$PROJECT_DIR$/build/ide'
        }
    }
}

Включение его в ваш скрипт сборки gradle довольно тривиально:

idea {
    module {
        iml {
            withXml {
                IdeaEnricher.updateWebFacet(this.project, it.asNode())
            }
        }
    }
    project {
        ipr {
            withXml {
                IdeaEnricher.updateWebArtifacts(this.project, it.asNode())
                IdeaEnricher.updateBuildOutputFolderForGradle(this.project, it.asNode())
            }
        }
    }
}

Я склонен немного поработать над файлом ipr, чтобы выходная папка соответствовала папке сборки gradle. Теперь IntelliJ создает свою собственную  out папку. Это небольшое раздражение, но так как я все равно меняю конфигурацию, я мог бы пройти весь путь.

Это еще раз убедило меня, что Gradle — правильный выбор. Конечно, он немного грубоват, и еще не все функции, которые есть у вас в Maven, есть. Однажды я сказал, что Грэдл предоставляет вам инструменты, чтобы оторвать всю вашу ногу, но на этот раз я был рад, что в моем распоряжении все оружие Groovy. Тот факт, что вы можете добавить дополнительную логику в свою сборку, является чем-то чрезвычайно мощным, и я вижу, что буду делать в будущем гораздо больше.