如何在Kotlin DSL(KTS)中正确配置带指定输入输出的自定义Gradle任务
解决方案
方法一:扩展Copy任务实现轻量内容转换
Copy任务本身支持内容过滤,适合简单文本替换场景,且自动集成增量构建能力,无需手动声明输入输出追踪逻辑。
代码示例
tasks.register<Copy>("processTextFiles") { from("src/main/text") into("build/processed-text") include("**/*.txt") // 方式1:用ReplaceTokens批量替换占位符(适合@oldString@格式的标记) filter<ReplaceTokens>("tokens" to mapOf("oldString" to "newString")) // 方式2:自定义FilterReader实现精准字符串替换 /* filter { reader -> object : FilterReader(reader) { override fun read(cbuf: CharArray, off: Int, len: Int): Int { val tempBuf = CharArray(len) val readCount = super.read(tempBuf, off, len) if (readCount == -1) return -1 val processedContent = String(tempBuf, off, readCount).replace("oldString", "newString") processedContent.toCharArray().copyInto(cbuf, off) return processedContent.length } } } */ }
关键说明
- 复用Copy任务的输入输出追踪机制,Gradle会自动检测输入文件的内容/路径变化,仅在需要时执行任务。
- 两种过滤方式按需选择:
ReplaceTokens适合批量替换占位符,自定义FilterReader更灵活,支持任意字符串处理逻辑。
方法二:自定义任务类型(适配复杂处理逻辑)
如果需要多步骤文本处理、正则匹配等复杂操作,可创建自定义任务类,通过注解明确声明输入输出,同时支持增量构建。
步骤1:编写自定义任务类
可将类放在项目的buildSrc/src/main/kotlin目录(全局复用)或模块的src/main/kotlin目录(模块内复用):
import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.work.Incremental import org.gradle.work.InputChanges import java.io.File abstract class ProcessTextTask : DefaultTask() { // 输入目录,@Incremental标记支持增量处理 @get:Incremental @get:InputDirectory abstract val inputDir: DirectoryProperty // 输出目录 @get:OutputDirectory abstract val outputDir: DirectoryProperty // 可配置的替换参数 @get:Input abstract val oldString: Property<String> @get:Input abstract val newString: Property<String> @TaskAction fun process(inputChanges: InputChanges) { // 仅处理变化的文件 inputChanges.getFileChanges(inputDir).forEach { change -> val inputFile = change.file val outputFile = outputDir.file(inputFile.name).get().asFile if (change.fileType == FileType.DIRECTORY) return@forEach when (change.changeType) { ChangeType.ADDED, ChangeType.MODIFIED -> { val content = inputFile.readText() val processedContent = content.replace(oldString.get(), newString.get()) outputFile.writeText(processedContent) } ChangeType.REMOVED -> outputFile.delete() } } } }
步骤2:在build.gradle.kts中注册任务
tasks.register<ProcessTextTask>("processTextFiles") { inputDir.set(file("src/main/text")) outputDir.set(file("build/processed-text")) oldString.set("oldString") newString.set("newString") }
关键说明
@Incremental:告诉Gradle可以增量处理输入,仅处理新增/修改/删除的文件。@InputDirectory/@OutputDirectory:让Gradle正确追踪输入输出的状态变化,避免无意义的重复执行。- 若不需要增量处理(比如必须全量生成),可移除
@Incremental和inputChanges相关逻辑,保留输入输出注解即可。
解决你之前的增量构建失效问题
你之前的自定义任务每次都执行,核心原因有两个:
- 仅声明了输入输出目录,但任务动作中没有利用Gradle的增量输入API,Gradle无法判断哪些文件需要处理,只能全量执行。
- 手动遍历文件时未保留输入文件的相对路径(比如子目录文件会被覆盖到输出根目录),导致Gradle无法建立输入输出的关联关系。
验证增量构建
执行任务后,再次运行./gradlew processTextFiles,若输入文件无变化,Gradle会输出UP-TO-DATE,表示任务无需执行。
内容的提问来源于stack exchange,提问作者wnetsimon
相关产品推荐
相关产品推荐

