jvm 生成时在资源中创建文件

5lwkijsr  于 2023-03-23  发布在  其他
关注(0)|答案(1)|浏览(164)

这是一个JVM应用程序。
我正在创建一个属性文件,其中包含要在应用运行时使用的版本。我在基础项目build.gradle.kts中编写了以下任务:

allprojects {
    //...

    tasks.register("createVersionPropertiesFile") {
        group = "build"
        doLast {
            val resourcesDir = File("$buildDir/resources/main").apply { mkdirs() }
            val resource = File(resourcesDir, "version.properties")
            resource.writeText("version=${project.version}")
        }
    }
}

如果我独立运行这个任务,它会按照我的预期创建文件。
为了在构建过程中运行任务,我将以下内容放在app模块的build.gradle.kts中:

tasks.processResources {
    dependsOn += tasks["createVersionPropertiesFile"]
}

然而,在构建过程中,resources目录实际上被删除而不是创建(或者如果是,它在构建完成之前再次被删除)。因此,在应用运行时找不到资源。
如何将此任务设置为在生成过程的适当阶段运行?
我知道另一种策略是在源代码的实际资源目录中创建资源文件,但我不想用这个自动生成的文件污染签入的源代码,我希望它只是构建的一部分。

x6h2sr28

x6h2sr281#

build/resources/目录是processResources任务的 output 目录,因此(as per the Javadoc)任何其他文件将被自动删除。
有一种更好的方法可以挂接一个任务,这样Gradle就会将其识别为源文件,并使Gradle自动运行该文件。
为了简单起见,我将演示如何在单个build.gradle.kts中设置createVersionPropertiesFile,而不使用allprojects {}配置。

注册任务输入输出

首先,确保在生成任务中注册了输出目录和输入目录,这可以使用运行时API来完成。任务有一个默认的tempDirectory,非常适合用作输出目录,但如果愿意,也可以使用自定义目录。
项目版本应设置为任务的输入属性,以便Gradle知道如果值更改,需要重新运行任务。为了与Gradle的配置缓存兼容(在任务执行期间不得使用project),应在任务配置块中将其定义为val

val createVersionPropertiesFileTask = tasks.register("createVersionPropertiesFile") {
  group = "build"

  // define a local val, to be compatible with Configuration Cache
  val projectVersion = project.version
  inputs.property("projectVersion", projectVersion)

  // tell Gradle about the output directory
  outputs.dir(temporaryDir)

  doLast {
    val resource = File(temporaryDir, "version.properties")
    resource.writeText("version=${projectVersion}")
  }
}

文档详细解释了为什么注册输入和输出是有益的。这里重要的是,由于Gradle的Provider API,任务可以转换为文件提供程序。

作为文件提供者的任务

Java项目是使用SourceSets组织的。这些项目预先配置了我们熟悉和喜爱的src/main/javasrc/test/resources,并且可以添加更多:

sourceSets {
  main {
    java {
      // add an additional source directory
      srcDir("$buildDir/some-extra-sources/java")
    }
    resources {
      // add an additional resource directory
      srcDir("$buildDir/some-extra-sources/resources")
    }
  }
}

虽然你可以将createVersionPropertiesFile的输出目录硬编码为一个额外的资源目录,但Gradle不会知道它需要运行任务来生成文件。我们可以有一个买一送一的交易:添加一个新的资源目录,* 并 * 告诉Gradle它需要运行任务。

sourceSets {
  main {
    resources {
      // add a new resource dir that is produced by the task
      srcDir(createVersionPropertiesFileTask.map { it.temporaryDir })
    }
  }
}

请注意,不需要添加任何dependsOn(createVersionPropertiesFileTask),因此您可以删除tasks.processResources的配置。
现在,当您运行./gradlew build时,您将看到您的自定义文件将由processResources任务生成到build/resources中。
如果您使用IntelliJ,还有一个小奖励:它将突出显示Projectexplorer侧边栏中的新资源目录。

为所有项目配置任务

您可能会发现使用allprojects {}为所有项目配置此任务存在问题。(还有一些其他原因不鼓励使用allprojects {}subprojects {}。)
相反,考虑创建一个约定插件,使用buildSrc通过几个简单的步骤创建和配置createVersionPropertiesFileTask
1.创建buildSrc/build.gradle.kts,由于buildSrc实际上是一个独立的项目,因此创建buildSrc/settings.gradle.kts
1.在buildSrc/build.gradle.kts中应用kotlin-dsl插件,这样我们就可以制作预编译的脚本插件。
1.使用上面提供的配置为您的约定创建一个文件。

// ./buildSrc/src/main/kotlin/my-java-conventions.gradle.kts

plugins {
  java
}

val createVersionPropertiesFileTask = tasks.register("createVersionPropertiesFile") {
  group = "build"

  val projectVersion = project.version
  inputs.property("projectVersion", projectVersion)

  outputs.dir(temporaryDir)

  doLast {
    val resource = File(temporaryDir, "version.properties")
    resource.writeText("version=${projectVersion}")
  }
}

sourceSets {
  main {
    resources {
      srcDir(createVersionPropertiesFileTask.map { it.temporaryDir })
    }
  }
}

1.现在,在所有子项目中,您可以在需要的地方专门应用约定插件-不再需要allprojects {}

// ./app-subproject/build.gradle.kts

plugins {
  `my-java-conventions`
}

写入属性任务

由于您正在生成.properties文件,因此您可能还需要考虑使用内置的任务类型来生成属性-WriteProperties。它具有一些实用程序,用于添加多个属性,并确保生成的文件可重复生成(以便Gradle的缓存工作良好)。

另请参见

相关问题