Gradleタスク 外部コマンドの出力結果を利用する方法

この記事では、Gradleのタスクで外部コマンドを使う方法について解説します。
ビルドスクリプトは、Kotlin DSLを使用。

実装例として、gitのリビジョンと、ビルド時刻をリソースとして埋め込む方法を紹介します。

タスクで外部コマンドを実行する。

Gradleのタスクで外部コマンドを実行したい場合は、Project#exec を利用することができます。
標準出力の結果をByteArrayOutputStreamに流す事で、タスク内で文字列として取得する事も可能です。

Project#exec

gitコマンドを使って、現在のリビジョンを取得して、ターミナルに出力するタスクを書いてみましょう。

外部コマンドを実行する際のタスク例 (build.gradle.kts)

// 外部コマンド実行サンプルのタスク
val externalCommand by tasks.registering {

    // タスクの実行内容を書く
    doLast {

        // 出力結果を取得したいので、バッファストリームを用意しておく。
        val output: String = ByteArrayOutputStream().use { outputStream ->

            // Project#exec を使って、外部コマンド実行命令が書ける。
            exec {
                // ここは、ExecSpecというスコープ、実行するコマンドを指定できる。
                commandLine("git", "version")

                // 出力先をバッファストリームにしておく。(指定しない場合、ターミナルに出力されます)
                standardOutput = outputStream
            }
            outputStream.toString().trim()
        }

        // コマンドの出力結果 (git version の内容が出力される)
        println(output)
        // ==> git version 2.33.0.windows.2
    }
}

ビルド時の情報をリソースに含めるタスク

外部コマンドの出力結果を利用して、
ここからは、ビルド時の情報をsrc/main/resources 配下にjsonファイルで埋め込む実装例を記載します。

buildInfoResourceタスクを作る

build.gradle.kts は下記のとおり。

// タスク内でつかうクラスのインポート
import java.io.ByteArrayOutputStream
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

plugins {
    kotlin("jvm") version "1.6.10"
    kotlin("plugin.serialization") version "1.6.10"
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
}

// ビルド時の情報をリソースに書き込むタスク
val buildInfoResource by tasks.registering {
    // グループ設定しない場合、作ったタスクはデフォルトでotherに属します。
    group = "build"

    doLast {
        // ビルド時の時刻
        val now = ZonedDateTime.now()
        val builtAt = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z"))

        // git rev-parse HEAD コマンドでビルド時のリビジョンを取得する。
        val rev: String = ByteArrayOutputStream().use { outputStream ->
            exec {
                commandLine("git", "rev-parse", "HEAD")
                standardOutput = outputStream
            }
            outputStream.toString().trim()
        }

        // JSONとして、ビルド情報を作成。
        val json = """
            {
              "built_at": "$builtAt",
              "revision": "$rev"
            }
            """.trimIndent()

        // リソース出力場所、なければディレクトリ作成する。
        val dir = File(projectDir, "src/main/resources")
        dir.mkdirs()

        // build_info.json としてファイルに書き出し。
        val outputFile = File(dir, "build_info.json")
        outputFile.writeText(json)
    }
}

// processResourcesタスクの前に、buildInfoResourceタスクが実行されるようにする。
tasks.processResources {
    dependsOn(buildInfoResource)
}

src/main配下のコードは、
今回はKotlinを使うのでkotlin-jvmと、Jsonパース用にserializationを設定しています。

Project#exec で外部コマンドを実行して得られたリビジョン文字列を使って、JSONに成形して、
ファイル出力しています。

最後のdependsOn設定を入れることで、processResources タスク(元からあるjavaコンパイル用のタスク)が実行される前に、必ずこのbuildInfoResource タスクが実行されるようになります。
コンパイル時に自動でビルド情報をリソースに含める事ができます。

保存されたファイルは下記のようになります。
src/main/resources/build_info.json

{
  "built_at": "2022-01-23 17:27:36 +0900",
  "revision": "97005a30d85f11d9f76e12c2075e321a8ee00b1b"
}

ビルド情報を取得する処理

ビルド時に自動でbuild_info.json が出力されるようになったので、
アプリケーション側から参照するコードを書いてみましょう。

JSONのパースには、serializationを使います。

package gradle.external.command

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

// build_info.json 用の構造体
@Serializable
data class BuildInfoData(
    @SerialName("built_at")
    val builtAt: String,

    val revision: String,
)

fun main() {
    // リソースファイルの読み込み (Class#getResource)
    val json = object {}.javaClass.getResource("/build_info.json")!!.readText()

    // デコード (json -> BuildInfoData)
    val info = Json.decodeFromString(BuildInfoData.serializer(), json)

    println(info.builtAt)
    // ==> 2022-01-23 17:27:36 +0900

    println(info.revision)
    // ==> 97005a30d85f11d9f76e12c2075e321a8ee00b1b
}

@SerialName は、キー名とメンバー変数名が一致しない場合に指定します。

まとめ

  • Gradleのタスク内で、Project#exec を使うと外部コマンドを実行できる。

  • ByteArrayOutputStreamを使って出力結果を文字列として取得することが可能。

    タスク処理に外部コマンドの実行結果を組み込んで使う事ができる。

  • タスクの依存関係(dependsOn)を使って、ビルド時に自動実行させる事ができる。

    ビルド時にリソース埋め込みなど。

タイトルとURLをコピーしました