Gradle插件中需通过Extension注册DirectoryProperty/RegularFileProperty声明可配置路径,必须用ObjectFactory创建并设convention默认值;须加@PathSensitive注解确保增量构建感知;跨模块路径须用afterEvaluate安全获取。

Gradle 插件里怎么声明文件路径和目录属性
插件本身不“声明”路径或目录属性,而是通过 Project 对象暴露的 API(如 project.file()、project.layout.projectDirectory)来访问或构造路径;真正需要你主动定义的,是**可配置的路径参数**——比如让用户能传入一个输出目录、一个资源根路径。这类参数必须注册为插件扩展(Extension),且类型需明确支持 RegularFileProperty 或 DirectoryProperty,否则 Gradle 无法做增量构建感知和路径标准化。
-
别直接用
String或File类型存路径:会导致路径未标准化(Windows 反斜杠、相对路径未解析)、无法参与增量构建、IDE 不识别语义 -
必须用
ObjectFactory创建属性:在插件apply()中通过project.objects获取,再调用fileProperty()或directoryProperty() -
注册到 Extension 时要设默认值:比如
outputDir.convention(project.layout.buildDirectory.dir("outputs/myplugin")),避免用户不配置就空指针
示例(Kotlin DSL 插件):
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val objects = project.objects
val extension = project.extensions.create("myplugin", MyExtension::class.java, objects)
project.tasks.register("myTask", MyTask::class.java) {
it.outputDir.set(extension.outputDir)
}
}
}
<p>abstract class MyExtension @Inject constructor(objects: ObjectFactory) {
@get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val outputDir: DirectoryProperty</p><pre class="brush:php;toolbar:false;">init {
outputDir.convention(project.layout.buildDirectory.dir("outputs/myplugin"))
}}
为什么 DirectoryProperty 要加 @PathSensitive
Gradle 默认把 DirectoryProperty 当作“整体路径”,不关心里面文件内容变化。如果你的任务实际依赖目录下某个配置文件(比如 config.json),但没声明敏感性,那改了 config.json 后任务会跳过执行——因为 Gradle 认为“目录路径没变,就不重跑”。
-
PathSensitivity.NONE:只比对路径字符串是否相同(极少用) -
PathSensitivity.NAME_ONLY:文件名列表变了才触发重执行(适合扫描类路径) -
PathSensitivity.RELATIVE:文件内容或元信息(如修改时间)变化即重执行(最常用)
漏掉这个注解是本地调试时“改了配置却没生效”的高频原因。
project.file() 和 project.layout.projectDirectory.file() 有啥区别
两者都返回 RegularFile,但语义和生命周期不同:
-
project.file("src/main/resources"):立即解析成绝对路径,结果是普通File对象,**不能用于 Task 输入输出声明**,也不参与构建缓存 -
project.layout.projectDirectory.file("src/main/resources"):返回RegularFile,属于 Gradle 的“惰性文件系统 API”,可直接赋给RegularFileProperty,支持延迟求值、路径标准化、构建缓存
简单记:只要后续要进 @InputFile / @OutputDirectory,就必须用 layout.* 系列 API 构造路径对象,别碰裸 File。
多模块项目里怎么让插件读取其他模块的目录
插件运行时只能访问当前 Project 实例的上下文。想跨模块访问(比如主模块插件要读取 :lib 模块的 build/libs),不能硬编码路径,得靠 Project.afterEvaluate + project.rootProject.findProject() 安全获取:
- 先检查目标模块是否存在:
val libProj = project.rootProject.findProject(":lib") - 再确保它已配置完成:
libProj?.afterEvaluate { /* 此时 libProj.layout.buildDirectory 已可用 */ } - 禁止在
apply()阶段直接调findProject(":lib").layout.buildDirectory—— 此时模块可能还没被加载,会 NPE
跨模块路径引用是 Gradle 插件最容易出竞态的地方,务必用 afterEvaluate 套一层,别图省事写死相对路径。

















