IDEA 插件与 配置缓存 不兼容。

IDEA 插件生成 IntelliJ IDEA 使用的文件,从而可以从 IDEA 打开项目 (File - Open Project)。外部依赖(包括相关的源代码和 Javadoc 文件)和项目依赖都会被考虑在内。

如果您只想将 Gradle 项目加载到 IntelliJ IDEA 中,请使用 IDE 的 导入功能。您无需应用此插件即可将项目导入 IDEA,但如果您这样做,导入将考虑您拥有的任何额外的 IDEA 配置,这些配置不会直接修改生成的文件 — 有关更多详细信息,请参阅 配置 部分。

IDEA 插件具体生成什么文件取决于使用的其他插件

始终

生成 IDEA 模块文件。如果项目是根项目,还会生成 IDEA 项目和工作区文件。

Java 插件

此外,还会将 Java 配置添加到 IDEA 模块和项目文件中。

IDEA 插件的一个重点是保持自定义的开放性。该插件提供了一组标准化的钩子,用于从生成的文件中添加和删除内容。

用法

要使用 IDEA 插件,请在您的构建脚本中包含以下内容

build.gradle.kts
plugins {
    idea
}
build.gradle
plugins {
    id 'idea'
}

IDEA 插件为您的项目添加了许多 tasks。idea task 为项目生成 IDEA 模块文件。当项目是根项目时,idea task 还会生成 IDEA 项目和工作区。IDEA 项目包含 Gradle 构建中每个项目的模块。

当项目是根项目时,IDEA 插件还会添加一个 openIdea task。此 task 生成 IDEA 配置文件并在 IDEA 中打开结果。这意味着您只需从根项目运行 ./gradlew openIdea 即可在一个便捷的步骤中生成并打开 IDEA 项目。

IDEA 插件还向项目添加了一个 cleanIdea task。此 task 删除生成的文件(如果存在)。

Tasks

IDEA 插件将下面显示的 tasks 添加到项目中。请注意,clean task 不依赖于 cleanIdeaWorkspace task。这是因为工作区通常包含大量用户特定的临时数据,不希望在 IDEA 之外对其进行操作。

idea

依赖于: ideaProject, ideaModule, ideaWorkspace

生成所有 IDEA 配置文件

openIdea

依赖于: idea

生成所有 IDEA 配置文件并在 IDEA 中打开项目

cleanIdeaDelete

依赖于: cleanIdeaProject, cleanIdeaModule

移除所有 IDEA 配置文件

cleanIdeaProjectDelete

移除 IDEA 项目文件

cleanIdeaModuleDelete

移除 IDEA 模块文件

cleanIdeaWorkspaceDelete

移除 IDEA 工作区文件

ideaProjectGenerateIdeaProject

生成 .ipr 文件。此 task 仅添加到根项目。

ideaModuleGenerateIdeaModule

生成 .iml 文件

ideaWorkspaceGenerateIdeaWorkspace

生成 .iws 文件。此 task 仅添加到根项目。

配置

该插件添加了一些配置选项,允许自定义其生成的 IDEA 项目和模块文件。这些选项以模型属性和直接修改生成文件的底层机制的形式存在。例如,您可以添加源代码和资源目录,以及注入您自己的 XML 片段。前一种配置类型受到 IDEA 导入功能的尊重,而后一种则不然。

以下是可以使用的配置属性

ideaIdeaModel

顶级元素,以 DSL 友好的方式启用 idea 插件的配置

idea.project IdeaProject

允许配置项目信息

idea.module IdeaModule

允许配置模块信息

idea.workspace IdeaWorkspace

允许配置工作区 XML

点击链接查看这些配置属性的使用示例。

自定义生成的文件

IDEA 插件提供了用于以更受控和更详细的方式自定义生成内容的钩子和行为。此外,withXml 钩子是修改工作区文件的唯一实用方法,因为其对应的域对象本质上是空的。

我们在本节中讨论的技术不适用于 IDEA 的导入功能

Tasks 识别现有的 IDEA 文件,并将其与生成的内容合并。

合并

现有 IDEA 文件中也是生成内容目标的 sections 将根据具体 section 进行修改或覆盖。其余 sections 将保持原样。

禁用合并并完全覆盖

要完全重写现有的 IDEA 文件,请执行 clean task 及其对应的生成 task,例如 “gradle cleanIdea idea”(按此顺序)。如果您想将其设为默认行为,请将 “tasks.idea.dependsOn(cleanIdea)” 添加到您的构建脚本中。这样就不必显式执行 clean task。

此策略也适用于插件将生成的单个文件。例如,可以使用 “gradle cleanIdeaModule ideaModule” 对 “.iml” 文件执行此操作。

Hooking 到生成生命周期

该插件提供了对 Gradle 生成的元数据文件 sections 进行建模的对象。生成生命周期如下

  1. 读取文件;或者,如果文件不存在,则使用 Gradle 提供的默认版本

  2. 使用表示现有文件的域对象执行 beforeMerged 钩子

  3. 现有内容与从 Gradle 构建推断或在 eclipse DSL 中显式定义的配置合并

  4. 使用表示要持久化的文件内容的域对象执行 whenMerged 钩子

  5. 使用将要持久化的 XML 的原始表示执行 withXml 钩子

  6. 最终的 XML 被持久化

以下是用于每种模型类型的域对象

IdeaProject
IdeaModule
IdeaWorkspace

部分重写现有内容

“完全重写”会导致所有现有内容被丢弃,从而丢失直接在 IDE 中所做的任何更改。beforeMerged 钩子使仅覆盖现有内容的某些部分成为可能。以下示例从 Module 域对象中删除所有现有依赖项

build.gradle.kts
import org.gradle.plugins.ide.idea.model.Module

idea.module.iml {
    beforeMerged(Action<Module> {
        dependencies.clear()
    })
}
build.gradle
idea.module.iml {
    beforeMerged { module ->
        module.dependencies.clear()
    }
}

生成的模块文件将仅包含 Gradle 生成的依赖项条目,而不包含原始文件中可能存在的任何其他依赖项条目。(在依赖项条目的情况下,这也是默认行为。)模块文件的其他 sections 将保持原样或合并。对项目文件中的模块路径也可以执行相同的操作

build.gradle.kts
import org.gradle.plugins.ide.idea.model.Project

idea.project.ipr {
    beforeMerged(Action<Project> {
        modulePaths.clear()
    })
}
build.gradle
idea.project.ipr {
    beforeMerged { project ->
        project.modulePaths.clear()
    }
}

修改完全填充的域对象

whenMerged 钩子允许您操作完全填充的域对象。通常,这是自定义 IDEA 文件的首选方法。以下是如何导出 IDEA 模块的所有依赖项

示例 4. 导出依赖项
build.gradle.kts
import org.gradle.plugins.ide.idea.model.Module
import org.gradle.plugins.ide.idea.model.ModuleDependency

idea.module.iml {
    whenMerged(Action<Module> {
        dependencies.forEach {
            (it as ModuleDependency).isExported = true
        }
    })
}
build.gradle
idea.module.iml {
    whenMerged { module ->
        module.dependencies*.exported = true
    }
}

修改 XML 表示

withXml 钩子允许您在文件写入磁盘之前操作内存中的 XML 表示。尽管 Groovy 的 XML 支持和 Kotlin 的扩展函数弥补了很多不足,但这种方法不如操作域对象方便。作为回报,您可以完全控制生成的文件,包括域对象未建模的 sections。

示例 5. 自定义 XML
build.gradle.kts
import org.w3c.dom.Element

idea.project.ipr {
    withXml(Action<XmlProvider> {
        fun Element.firstElement(predicate: (Element.() -> Boolean)) =
            childNodes
                .run { (0 until length).map(::item) }
                .filterIsInstance<Element>()
                .first { it.predicate() }

        asElement()
            .firstElement { tagName == "component" && getAttribute("name") == "VcsDirectoryMappings" }
            .firstElement { tagName == "mapping" }
            .setAttribute("vcs", "Git")
    })
}
build.gradle
idea.project.ipr {
    withXml { provider ->
        provider.node.component
                .find { it.@name == 'VcsDirectoryMappings' }
                .mapping.@vcs = 'Git'
    }
}

识别额外的测试目录

当将此插件与 Java 插件 一起使用时,在添加额外的源代码集后,您可能希望通知 IDEA 它们何时包含测试源代码而不是生产源代码,以便 IDE 可以适当地处理这些目录。这可以通过使用此插件的 Module 块来实现。

build.gradle.kts
sourceSets {
    create("intTest") {
        java {
            setSrcDirs(listOf("src/integration"))
        }
    }
}

idea {
    module {
        testSources.from(sourceSets["intTest"].java.srcDirs)
    }
}
build.gradle
sourceSets {
    intTest {
        java {
            srcDirs = ['src/integration']
        }
    }
}

idea {
    module {
        testSources.from(sourceSets.intTest.java.srcDirs)
    }
}
当使用 JVM Test Suite 插件 时,测试源代码将自动正确识别。

进一步需要考虑的事项

生成的 IDEA 文件中依赖项的路径是绝对路径。如果您手动定义一个指向 Gradle 依赖缓存的路径变量,IDEA 将自动将绝对依赖项路径替换为此路径变量。您可以通过 “idea.pathVariables” 属性配置此路径变量,以便它可以进行适当的合并而不会创建重复项。