可执行任务描述了 Gradle 中的工作。这些任务具有操作。在 Gradle 核心中,compileJava
任务用于编译 Java 源代码。Jar
和 Zip
任务则将文件打包成归档文件。

可以通过扩展 DefaultTask
类并定义输入、输出和操作来创建自定义可执行任务。
任务输入和输出
可执行任务具有输入和输出。输入和输出可以是文件、目录或变量。
在可执行任务中
-
输入包含文件、文件夹和/或配置数据的集合。
例如,javaCompile
任务接受 Java 源代码文件和构建脚本配置(如 Java 版本)等作为输入。 -
输出指一个或多个文件或文件夹。
例如,javaCompile
任务生成类文件作为输出。
然后,jar
任务将这些类文件作为输入并生成一个 JAR 归档文件。
明确定义任务的输入和输出有两个目的
-
它们告知 Gradle 任务之间的依赖关系。
例如,如果 Gradle 理解compileJava
任务的输出是jar
任务的输入,它会优先运行compileJava
。 -
它们有助于实现增量构建。
例如,假设 Gradle 识别出任务的输入和输出保持不变。在这种情况下,它可以利用之前构建运行的结果或构建缓存,完全避免重新运行任务操作。
当应用像 java-library
这样的插件时,Gradle 会自动注册一些任务并使用默认值进行配置。
在一个假想的示例项目中,让我们定义一个任务,将 JAR 和启动脚本打包成一个归档文件。
gradle-project
├── app
│ ├── build.gradle.kts // app build logic
│ ├── run.sh // script file
│ └── ... // some java code
├── settings.gradle.kts // includes app subproject
├── gradle
├── gradlew
└── gradlew.bat
gradle-project
├── app
│ ├── build.gradle // app build logic
│ ├── run.sh // script file
│ └── ... // some java code
├── settings.gradle // includes app subproject
├── gradle
├── gradlew
└── gradlew.bat
run.sh
脚本可以从构建中执行 Java 应用程序(一旦打包成 JAR)。
java -cp 'libs/*' gradle.project.app.App
让我们使用 task.register()
注册一个名为 packageApp
的新任务。
tasks.register<Zip>("packageApp") {
}
tasks.register(Zip, "packageApp") {
}
我们使用了 Gradle 核心中现有的实现,即 Zip
任务实现(也就是 DefaultTask
的子类)。由于这里我们注册的是一个新任务,它没有预先配置。我们需要配置其输入和输出。
定义输入和输出是使任务成为可执行任务的关键。
如果输入是我们直接创建或编辑的文件,例如运行文件或 Java 源代码,它通常位于项目目录中的某个位置。为了确保我们使用正确的位置,我们使用 layout.projectDirectory
并定义相对于项目根目录的相对路径。
我们将 jar
任务的输出以及所有依赖项的 JAR(使用 configurations
.runtimeClasspath
)作为附加输入提供。
对于输出,我们需要定义两个属性。
首先,目标目录应该位于构建文件夹内的一个目录。我们可以通过 layout
访问它。
其次,我们需要指定 zip 文件的名称,我们将其命名为 myApplication.zip
。
这是完整的任务定义的样子
val packageApp = tasks.register<Zip>("packageApp") {
from(layout.projectDirectory.file("run.sh")) // input - run.sh file
from(tasks.jar) { // input - jar task output
into("libs")
}
from(configurations.runtimeClasspath) { // input - jar of dependencies
into("libs")
}
destinationDirectory.set(layout.buildDirectory.dir("dist")) // output - location of the zip file
archiveFileName.set("myApplication.zip") // output - name of the zip file
}
def packageApp = tasks.register(Zip, 'packageApp') {
from layout.projectDirectory.file('run.sh') // input - run.sh file
from tasks.jar { // input - jar task output
into 'libs'
}
from configurations.runtimeClasspath { // input - jar of dependencies
into 'libs'
}
destinationDirectory.set(layout.buildDirectory.dir('dist')) // output - location of the zip file
archiveFileName.set('myApplication.zip') // output - name of the zip file
}
如果我们运行 packageApp
任务,就会生成 myApplication.zip
。
$./gradlew :app:packageApp
> Task :app:compileJava
> Task :app:processResources NO-SOURCE
> Task :app:classes
> Task :app:jar
> Task :app:packageApp
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 executed
Gradle 执行了构建 JAR 文件所需的许多任务,其中包括 app
项目代码的编译以及代码依赖项的编译。
查看新创建的 ZIP 文件,我们可以看到它包含了运行 Java 应用程序所需的一切。
> unzip -l ./app/build/dist/myApplication.zip
Archive: ./app/build/dist/myApplication.zip
Length Date Time Name
--------- ---------- ----- ----
42 01-31-2024 14:16 run.sh
0 01-31-2024 14:22 libs/
847 01-31-2024 14:22 libs/app.jar
3041591 01-29-2024 14:20 libs/guava-32.1.2-jre.jar
4617 01-29-2024 14:15 libs/failureaccess-1.0.1.jar
2199 01-29-2024 14:15 libs/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
19936 01-29-2024 14:15 libs/jsr305-3.0.2.jar
223979 01-31-2024 14:16 libs/checker-qual-3.33.0.jar
16017 01-31-2024 14:16 libs/error_prone_annotations-2.18.0.jar
--------- -------
3309228 9 files
可执行任务应与生命周期任务关联,以便开发人员只需运行生命周期任务。
到目前为止,我们直接调用了新任务。现在,让我们将其与生命周期任务关联起来。
以下内容被添加到构建脚本中,以便使用 dependsOn()
将 packageApp
可执行任务与 build
生命周期任务关联起来。
tasks.build {
dependsOn(packageApp)
}
tasks.build {
dependsOn(packageApp)
}
我们可以看到运行 :build
也会运行 :packageApp
。
$ ./gradlew :app:build
> Task :app:compileJava UP-TO-DATE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:jar UP-TO-DATE
> Task :app:startScripts
> Task :app:distTar
> Task :app:distZip
> Task :app:assemble
> Task :app:compileTestJava
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses
> Task :app:test
> Task :app:check
> Task :app:packageApp
> Task :app:build
BUILD SUCCESSFUL in 1s
8 actionable tasks: 6 executed, 2 up-to-date
如有需要,可以定义自己的生命周期任务。
通过继承 DefaultTask
实现任务
为了满足更多个性化需求,如果现有插件无法提供您所需的构建功能,您可以创建自己的任务实现。
实现一个类意味着创建一个自定义类(即类型),这通过继承 DefaultTask
来完成。
让我们从一个使用 Gradle init
构建的简单 Java 应用程序示例开始,其源代码位于 app
子项目中,通用构建逻辑位于 buildSrc
中。
gradle-project
├── app
│ ├── build.gradle.kts
│ └── src // some java code
│ └── ...
├── buildSrc
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src // common build logic
│ └── ...
├── settings.gradle.kts
├── gradle
├── gradlew
└── gradlew.bat
gradle-project
├── app
│ ├── build.gradle
│ └── src // some java code
│ └── ...
├── buildSrc
│ ├── build.gradle
│ ├── settings.gradle
│ └── src // common build logic
│ └── ...
├── settings.gradle
├── gradle
├── gradlew
└── gradlew.bat
我们在 ./buildSrc/src/main/kotlin/GenerateReportTask.kt
或 ./buildSrc/src/main/groovy/GenerateReportTask.groovy
中创建一个名为 GenerateReportTask
的类。
为了让 Gradle 知道我们正在实现一个任务,我们扩展了 Gradle 附带的 DefaultTask
类。将任务类声明为 abstract
也很有益处,因为 Gradle 会自动处理许多事情。
import org.gradle.api.DefaultTask
abstract class GenerateReportTask : DefaultTask() {
}
import org.gradle.api.DefaultTask
abstract class GenerateReportTask extends DefaultTask {
}
接下来,我们使用属性和注解定义输入和输出。在此上下文中,Gradle 中的属性充当其背后实际值的引用,允许 Gradle 跟踪任务之间的输入和输出。
对于我们任务的输入,我们使用 Gradle 的 DirectoryProperty
。我们使用 @InputDirectory
对其进行注解,以表明它是任务的输入。
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.InputDirectory
abstract class GenerateReportTask : DefaultTask() {
@get:InputDirectory
abstract val sourceDirectory: DirectoryProperty
}
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.InputDirectory
abstract class GenerateReportTask extends DefaultTask {
@InputDirectory
abstract DirectoryProperty getSourceDirectory()
}
类似地,对于输出,我们使用 RegularFileProperty
并使用 @OutputFile
对其进行注解。
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
abstract class GenerateReportTask : DefaultTask() {
@get:InputDirectory
abstract val sourceDirectory: DirectoryProperty
@get:OutputFile
abstract val reportFile: RegularFileProperty
}
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
abstract class GenerateReportTask extends DefaultTask {
@InputDirectory
abstract DirectoryProperty getSourceDirectory()
@OutputFile
abstract RegularFileProperty getReportFile()
}
定义了输入和输出后,剩下的唯一事情就是实际的任务操作,它在一个使用 @TaskAction
注解的方法中实现。在这个方法内部,我们编写代码,使用 Gradle 特定的 API 访问输入和输出。
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
abstract class GenerateReportTask : DefaultTask() {
@get:InputDirectory
abstract val sourceDirectory: DirectoryProperty
@get:OutputFile
abstract val reportFile: RegularFileProperty
@TaskAction
fun generateReport() {
val sourceDirectory = sourceDirectory.asFile.get()
val reportFile = reportFile.asFile.get()
val fileCount = sourceDirectory.listFiles().count { it.isFile }
val directoryCount = sourceDirectory.listFiles().count { it.isDirectory }
val reportContent = """
|Report for directory: ${sourceDirectory.absolutePath}
|------------------------------
|Number of files: $fileCount
|Number of subdirectories: $directoryCount
""".trimMargin()
reportFile.writeText(reportContent)
println("Report generated at: ${reportFile.absolutePath}")
}
}
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
abstract class GenerateReportTask extends DefaultTask {
@InputDirectory
abstract DirectoryProperty getSourceDirectory()
@OutputFile
abstract RegularFileProperty getReportFile()
@TaskAction
void generateReport() {
def sourceDirectory = sourceDirectory.asFile.get()
def reportFile = reportFile.asFile.get()
def fileCount = sourceDirectory.listFiles().count { it.isFile() }
def directoryCount = sourceDirectory.listFiles().count { it.isDirectory() }
def reportContent = """
Report for directory: ${sourceDirectory.absolutePath}
------------------------------
Number of files: $fileCount
Number of subdirectories: $directoryCount
""".trim()
reportFile.text = reportContent
println("Report generated at: ${reportFile.absolutePath}")
}
}
任务操作生成 sourceDirectory
中文件的报告。
在应用程序的构建文件中,我们使用 task.register()
注册一个类型为 GenerateReportTask
的任务,并将其命名为 generateReport
。同时,我们配置了该任务的输入和输出。
tasks.register<GenerateReportTask>("generateReport") {
sourceDirectory = layout.projectDirectory.dir("src/main")
reportFile = layout.buildDirectory.file("reports/directoryReport.txt")
}
tasks.build {
dependsOn("generateReport")
}
tasks.register("generateReport", GenerateReportTask) {
sourceDirectory = layout.projectDirectory.dir("src/main")
reportFile = layout.buildDirectory.file("reports/directoryReport.txt")
}
tasks.build.dependsOn("generateReport")
generateReport
任务与 build
任务关联。
通过运行构建,我们观察到我们的启动脚本生成任务被执行,并且在后续构建中它是 UP-TO-DATE
(最新)。Gradle 的增量构建和缓存机制与自定义任务无缝协作。
./gradlew :app:build
> Task :buildSrc:checkKotlinGradlePluginConfigurationErrors
> Task :buildSrc:compileKotlin UP-TO-DATE
> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources NO-SOURCE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :app:compileJava UP-TO-DATE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:jar UP-TO-DATE
> Task :app:startScripts UP-TO-DATE
> Task :app:distTar UP-TO-DATE
> Task :app:distZip UP-TO-DATE
> Task :app:assemble UP-TO-DATE
> Task :app:compileTestJava UP-TO-DATE
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses UP-TO-DATE
> Task :app:test UP-TO-DATE
> Task :app:check UP-TO-DATE
> Task :app:generateReport
Report generated at: ./app/build/reports/directoryReport.txt
> Task :app:packageApp
> Task :app:build
BUILD SUCCESSFUL in 1s
13 actionable tasks: 10 executed, 3 up-to-date
任务操作
任务操作是实现任务功能的代码,如上一节所示。例如,javaCompile
任务操作调用 Java 编译器将源代码转换为字节码。
可以动态修改已注册任务的任务操作。这对于测试、修补或修改核心构建逻辑很有帮助。
让我们看一个简单的 Gradle 构建示例,其中包含一个构成 Java 应用程序的 app
子项目——它包含一个 Java 类并使用 Gradle 的 application
插件。项目在 buildSrc
文件夹中包含通用构建逻辑,其中包含了 my-convention-plugin
。
plugins {
id("my-convention-plugin")
}
version = "1.0"
application {
mainClass = "org.example.app.App"
}
plugins {
id 'my-convention-plugin'
}
version = '1.0'
application {
mainClass = 'org.example.app.App'
}
我们在 app
的构建文件中定义了一个名为 printVersion
的任务。
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
abstract class PrintVersion : DefaultTask() {
// Configuration code
@get:Input
abstract val version: Property<String>
// Execution code
@TaskAction
fun print() {
println("Version: ${version.get()}")
}
}
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
abstract class PrintVersion extends DefaultTask {
// Configuration code
@Input
abstract Property<String> getVersion()
// Execution code
@TaskAction
void printVersion() {
println("Version: ${getVersion().get()}")
}
}
这个任务做一件简单的事情:它将项目的版本打印到命令行。
这个类扩展了 DefaultTask
,并且它有一个 @Input
,类型是 Property<String>
。它有一个用 @TaskAction
注解的方法,该方法打印出版本。
注意,任务实现清晰地区分了“配置代码”和“执行代码”。
配置代码在 Gradle 的配置阶段执行。它在内存中构建项目的模型,以便 Gradle 知道在某个构建调用中需要做什么。任务操作周围的所有内容,例如输入或输出属性,都属于配置代码。
任务操作方法内部的代码是执行代码,它完成实际工作。如果任务是任务图的一部分,并且如果它不是 UP-TO-DATE(最新)或 FROM-CACHE(来自缓存)而无法跳过,它就会访问输入和输出并执行某些工作。
任务实现完成后,可以在构建设置中使用它。在我们的约定插件 my-convention-plugin
中,我们可以注册一个使用新任务实现的新任务。
tasks.register<PrintVersion>("printVersion") {
// Configuration code
version = project.version as String
}
tasks.register(PrintVersion, "printVersion") {
// Configuration code
version = project.version.toString()
}
在任务的配置块内部,我们可以编写配置阶段代码,该代码修改任务的输入和输出属性的值。在此处以任何方式都没有引用任务操作。
可以用更紧凑的方式直接在构建脚本中编写像这样的简单任务,而无需为任务创建单独的类。
让我们注册另一个任务,并将其命名为 printVersionDynamic
。
这次,我们没有为任务定义类型,这意味着任务将是通用类型 DefaultTask
。这个通用类型不定义任何任务操作,也就是说它没有使用 @TaskAction
注解的方法。这种类型对于定义“生命周期任务”非常有用。
tasks.register("printVersionDynamic") {
}
tasks.register("printVersionDynamic") {
}
然而,默认任务类型也可以用来动态定义具有自定义操作的任务,而无需额外的类。这通过使用 doFirst{}
或 doLast{}
构造来完成。类似于定义一个方法并使用 @TaskAction
注解它一样,这会向任务添加一个操作。
这些方法称为 doFirst{}
和 doLast{}
是因为任务可以有多个操作。如果任务已经定义了一个操作,您可以使用此区分来决定您的附加操作应该在现有操作之前还是之后运行。
tasks.register("printVersionDynamic") {
doFirst {
// Task action = Execution code
// Run before exiting actions
}
doLast {
// Task action = Execution code
// Run after existing actions
}
}
tasks.register("printVersionDynamic") {
doFirst {
// Task action = Execution code
// Run before exiting actions
}
doLast {
// Task action = Execution code
// Run after existing actions
}
}
如果您只有一个操作(本例中就是这种情况,因为我们从一个空任务开始),我们通常使用 doLast{}
方法。
在任务中,我们首先动态声明要打印的版本作为输入。我们没有声明一个属性并用 @Input
注解它,而是使用了所有任务都具有的通用输入属性。然后,我们在 doLast{}
方法内部添加操作代码,一个 println()
语句。
tasks.register("printVersionDynamic") {
inputs.property("version", project.version.toString())
doLast {
println("Version: ${inputs.properties["version"]}")
}
}
tasks.register("printVersionDynamic") {
inputs.property("version", project.version)
doLast {
println("Version: ${inputs.properties["version"]}")
}
}
我们看到了在 Gradle 中实现自定义任务的两种替代方法。
动态设置使其更紧凑。但是,在编写动态任务时,很容易混淆配置时和执行时状态。您还可以看到动态任务中的“输入”是无类型的,这可能导致问题。当您将自定义任务实现为一个类时,您可以清晰地将输入定义为具有专门类型的属性。
动态修改任务操作可以为已注册但因某种原因需要修改的任务提供价值。
让我们以 compileJava
任务为例。
任务注册后,您无法将其移除。相反,您可以清除其操作。
tasks.compileJava {
// Clear existing actions
actions.clear()
// Add a new action
doLast {
println("Custom action: Compiling Java classes...")
}
}
tasks.compileJava {
// Clear existing actions
actions.clear()
// Add a new action
doLast {
println("Custom action: Compiling Java classes...")
}
}
要移除您使用的插件已经设置的某些任务依赖项也很困难,在某些情况下甚至是不可能的。相反,您可以修改其行为。
tasks.compileJava {
// Modify the task behavior
doLast {
val outputDir = File("$buildDir/compiledClasses")
outputDir.mkdirs()
val compiledFiles = sourceSets["main"].output.files
compiledFiles.forEach { compiledFile ->
val destinationFile = File(outputDir, compiledFile.name)
compiledFile.copyTo(destinationFile, true)
}
println("Java compilation completed. Compiled classes copied to: ${outputDir.absolutePath}")
}
}
tasks.compileJava {
// Modify the task behavior
doLast {
def outputDir = file("$buildDir/compiledClasses")
outputDir.mkdirs()
def compiledFiles = sourceSets["main"].output.files
compiledFiles.each { compiledFile ->
def destinationFile = new File(outputDir, compiledFile.name)
compiledFile.copyTo(destinationFile)
}
println("Java compilation completed. Compiled classes copied to: ${outputDir.absolutePath}")
}
}
理解输入和输出
任务输入和输出对于以下方面很重要:
-
最新检查 (Up-to-date checks) - Gradle 的增量构建特性通过查看任务输入和输出的变化,帮助您的构建避免重复工作。
-
链接任务输入和输出 (Linking task inputs and outputs) - 当一个任务的输出与另一个任务的输入关联时,Gradle 可以自动创建任务依赖关系。
-
使用依赖配置 (Using dependency configurations) - 任务输出可以用来告知 Gradle,某个任务生成的 artifact 应添加到特定的配置中。
声明输入和输出
您可以通过两种主要方式配置任务的输入和输出:
-
静态配置: 直接在任务类内部定义输入和输出。无论何时执行任务,这些输入和输出都将始终适用。
-
动态配置: 动态地向任务添加输入和输出,这意味着您可以根据特定条件或要求自定义任务每次执行时的输入和输出。这种方法提供了更大的灵活性和对任务行为的精细控制。
abstract class ConfigurableTask : DefaultTask() {
@get:Input
abstract val inputProperty: Property<String>
@get:OutputFile
abstract val outputFile: RegularFileProperty
// Static Configuration: Inputs and Outputs defined in the task class
init {
group = "custom"
description = "A configurable task example"
}
@TaskAction
fun executeTask() {
println("Executing task with input: ${inputProperty.get()} and output: ${outputFile.asFile.get()}")
}
}
// Dynamic Configuration: Adding inputs and outputs to a task instance
tasks.register("dynamicTask", ConfigurableTask::class) {
// Set the input property dynamically
inputProperty = "dynamic input value"
// Set the output file dynamically
outputFile = layout.buildDirectory.file("dynamicOutput.txt")
}
abstract class ConfigurableTask extends DefaultTask {
@Input
abstract Property<String> getInputProperty()
@OutputFile
abstract RegularFileProperty getOutputFile()
// Static Configuration: Inputs and Outputs defined in the task class
ConfigurableTask() {
group = 'custom'
description = 'A configurable task example'
}
@TaskAction
void executeTask() {
println "Executing task with input: ${inputProperty.get()} and output: ${outputFile.asFile.get()}"
}
}
// Dynamic Configuration: Adding inputs and outputs to a task instance
tasks.register('dynamicTask', ConfigurableTask) {
// Set the input property dynamically
inputProperty = 'dynamic input value'
// Set the output file dynamically
outputFile = layout.buildDirectory.file('dynamicOutput.txt')
}
创建惰性输入和输出
Gradle 有惰性配置的概念,它允许在实际设置任务输入和输出之前引用它们。这通过 Property
类类型来实现。
abstract class MyTask : DefaultTask() {
// Avoid Java Bean properties
@Input
var myEagerProperty: String = "default value"
// Use Gradle managed properties instead
@get:Input
abstract val myLazyProperty: Property<String>
@TaskAction
fun myAction() {
println("Use ${myLazyProperty.get()} and do NOT use $myEagerProperty")
}
}
abstract class MyTask extends DefaultTask {
// Avoid Java Bean properties
@Input
String myEagerProperty = "default value"
// Use Gradle managed properties instead
@Input
abstract Property<String> getMyLazyProperty()
@TaskAction
void myAction() {
println("Use ${myLazyProperty.get()} and do NOT use $myEagerProperty")
}
}
这种机制的一个优点是,您可以在文件名尚未确定之前,将一个任务的输出文件链接到另一个任务的输入文件。Property
类也知道它链接到哪个任务,因此以这种方式使用输入和输出使 Gradle 能够自动添加所需的任务依赖关系。

为确保正确的惰性配置,应避免使用 Java Bean 类型。让我们探讨 Gradle 惰性类型可声明为任务输入和输出的常见选项:
Gradle 惰性类型 | Java Bean 类型 | 输入 | 输出 |
---|---|---|---|
|
|
X |
|
|
|
X |
X |
文件的 Iterable ( |
文件的 Iterable ( |
X |
X |
文件的 Map ( |
文件的 Map ( |
X |
|
|
|
X |
X |
目录的 Iterable ( |
目录的 Iterable ( |
X |
|
目录的 Map ( |
目录的 Map ( |
X |
注意
-
字符串仅支持任务输入,不支持输出。它们通常用于配置选项,例如
compileJava
任务类型的sourceCompatibility
。 -
任务输出只能是文件或目录。
-
除了
String
,Boolean
,String
和其他标准类型外,使用Property<T>
。 -
除了
List<T>
外,使用ListProperty<T>
。 -
除了
Set<T>
外,使用SetProperty<T>
。
当顺序很重要时,避免使用 FileTree ——它没有保证稳定的文件顺序,可能导致不可预测的行为。 |
注解输入和输出
一个好的实践是为自定义任务创建一个任务类。该类封装了任务操作逻辑,同时也应该声明任务期望的任何输入和输出。为此,我们使用注解。
对于任务输入,我们可以使用 @Input
, @InputFile
, @InputDirectory
, @InputFiles
, @Classpath
和 @CompileClasspath
。对于任务输出,我们有 @OutputFile
, @OutputDirectory
, @OutputFiles
, @OutputDirectories
。
以下是所有可用注解的详细信息:
注解 | 用途 |
---|---|
|
属性是任务的输入值 |
|
属性是任务的输入文件 |
|
属性是任务的一个或多个输入文件 |
|
属性是任务的输入目录 |
|
属性是一个或多个表示 Java classpath 的输入文件或目录 |
|
属性是一个或多个表示 Java 编译 classpath 的输入文件或目录 |
|
属性是任务的输出文件[1] |
|
属性是任务的一个或多个输出文件 |
|
属性是任务的输出目录 |
|
属性是任务的一个或多个输出目录 |
|
属性是一个或多个任务销毁(删除/移除)的文件或目录(来自其他任务)[2] |
|
属性是任务的本地状态 |
|
属性是嵌套的 bean,应该检查其是否有其他注解 |
|
属性不是输入或输出,不应在最新检查中考虑 |
|
属性在内部使用,不应在最新检查中考虑 |
|
属性是文件或目录,当属性值为空时,任务应被跳过 |
|
属性是文件或目录,其更改可以使用 |
|
属性可以是任何类型,其值不必指定,并且禁用验证检查 |
|
属性是一个或多个文件,只有文件路径的给定部分是重要的 |
|
与 |
|
与 |
请注意,在 Kotlin 中,注解以 get:
为前缀,因此 @InputFile
变为 @get:InputFile
。
abstract class AllTypes : DefaultTask() {
//inputs
@get:Input
abstract val inputString: Property<String>
@get:InputFile
abstract val inputFile: RegularFileProperty
@get:InputDirectory
abstract val inputDirectory: DirectoryProperty
@get:InputFiles
abstract val inputFileCollection: ConfigurableFileCollection
@get:Classpath
abstract val inputClasspath: ConfigurableFileCollection
// outputs
@get:OutputFile
abstract val outputFile: RegularFileProperty
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty
@get:OutputFiles
abstract val outputFiles: ConfigurableFileCollection
@get:OutputDirectories
abstract val outputDirectories: ConfigurableFileCollection
}
abstract class AllTypes extends DefaultTask {
//inputs
@Input
abstract Property<String> getInputString()
@InputFile
abstract RegularFileProperty getInputFile()
@InputDirectory
abstract DirectoryProperty getInputDirectory()
@InputFiles
abstract ConfigurableFileCollection getInputFileCollection()
@Classpath
abstract ConfigurableFileCollection getInputClasspath()
// outputs
@OutputFile
abstract RegularFileProperty getOutputFile()
@OutputDirectory
abstract DirectoryProperty getOutputDirectory()
@OutputFiles
abstract ConfigurableFileCollection getOutputFiles()
@OutputDirectories
abstract ConfigurableFileCollection getOutputDirectories()
}
clean
任务)非常有用。