【入門編】Gradleのタスク実行をカスタマイズ!doFirstとdoLastを駆使したビルド前後の処理注入 – ビルド・パッケージ管理ツール生産性向上バイブル

こんにちは!日々の開発、本当にお疲れ様です。

JavaやKotlinを使ったバックエンド開発の世界へようこそ。これからモダンな開発環境に触れていくあなたへ、ぜひ知ってほしい強力な相棒がいます。それがビルドツールの「Gradle(グレードル)」です。

「ビルドツールって言われても、なんだか難しそう……」
「Mavenから名前は聞いたことあるけど、何が違うの?」

そんな不安を感じている方もご安心ください。今日は、Gradleの心臓部とも言える「タスクの仕組み」を紐解きながら、あなたのビルドを自由自在に操る魔法のようなテクニックを一緒に学んでいきましょう。

これをマスターすれば、毎日のコーディングやデプロイ作業が劇的に楽になり、「おっ、やるな!」と周囲を唸らせるエンジニアに一歩近づけますよ。

—

1. そもそもGradleとは?なぜ今、私たちが使うべきなのか

私たちが書いたJavaのコード(人間が読めるテキスト)は、そのままではコンピュータは理解できません。
コンピュータが理解できる「バイトコード」にコンパイルし、必要なライブラリ(外部の便利な部品)を集めて、1つの実行可能なファイル(JARやWAR)にまとめる一連の作業。これを行うのがビルドツールです。

その中でもGradleは、以下の圧倒的なアドバンテージを持っています。

  • 圧倒的なビルドスピード: 前回のビルドから変更されていない部分を賢く判断し、やり直さない(インクリメンタルビルド)。
  • GroovyやKotlinによる柔軟な記述: 設定ファイルをただの「データ(XMLなど)」ではなく「プログラム」として書けるため、複雑なカスタマイズも思いのまま。
  • 依存関係管理のスマートさ: 世界中のライブラリ倉庫から、必要なバージョンを自動で調和して取ってきてくれる。

Mavenという先輩ツールもありますが、現代の高速で複雑なアジャイル開発において、Gradleの柔軟性とスピードはもはや必須のインフラとなっています。

—

2. 最小限かつ最強の環境セットアップ

難解なインストール手順で挫折するのはもう終わりです。現代のGradle開発では、プロジェクトごとにバージョンを完全同期できる Gradle Wrapper(gradlew) という仕組みを使います。これを使えば、あなたのPCに専用のGradleをインストールしなくても、プロジェクトが勝手に最適な環境を用意してくれます。

最小限のプロジェクト構造を作る

まずは、適当な作業ディレクトリを作り、以下のファイル構成を整えてみてください。

my-gradle-app/
┣ build.gradle.kts # Gradleの設定ファイル(Kotlin DSLを使用)
┗ settings.gradle # プロジェクト名などを定義するファイル

各ファイルの中身を覗いてみましょう。初心者の方でも直感的に理解できるよう、丁寧にコメントを入れています。

`settings.gradle`

// プロジェクト全体の名前を定義します
rootProject.name = “my-gradle-app”

`build.gradle.kts`

// Javaの開発に必要な機能をプラグインとしてインポートします
plugins {
java
}

// 使用するライブラリをダウンロードする倉庫(リポジトリ)を指定します
repositories {
mavenCentral()
}

// アプリケーションで使う依存関係(今回はテスト用にJUnitを設定)
dependencies {
testImplementation(“org.junit.jupiter:junit-jupiter:5.9.3”)
}

—

3. 動作確認:世界で一番シンプルなHelloWorldタスク

設定ができたら、Gradleの動作確認をしてみましょう。
ターミナル(コマンドプロンプトやiTermなど)を開き、プロジェクトのルートディレクトリで以下のコマンドを実行してください。

(※Mac/Linuxなら `./gradlew`、Windowsなら `gradlew.bat` を使います)

Gradle Wrapperの初期セットアップと、簡単なタスクを実行
./gradlew tasks

実行すると、何やらたくさんのタスク(Gradleが実行できる命令のリスト)が表示されたはずです。これがGradleの全貌です。

それでは、自分でオリジナルの「HelloWorldタスク」を作ってみましょう。
`build.gradle.kts` の末尾に、以下のコードを追加してください。

// “hello”という名前のカスタムタスクを定義します
tasks.register(“hello”) {
// タスクの説明(./gradlew tasks で表示されます)
group = “custom”
description = “あいさつを出力するタスクです”

// タスクが実行されたときの処理
doLast {
println(“こんにちは、Gradleの世界へようこそ!”)
}
}

保存したら、先ほどのターミナルからこのタスクを呼び出してみます。

./gradlew hello

【実行結果のイメージ】

> Task :hello
こんにちは、Gradleの世界へようこそ!

BUILD SUCCESSFUL in 1s

おめでとうございます!これであなたも立派なGradle使いの第一歩を踏み出しました。

—

4. 本丸:doFirstとdoLastでビルドの前後を完全に支配する

ここからが本記事のハイライトです。
Gradleのタスクには、「ライフサイクル」という概念があります。ひとつのタスクが実行されるとき、実は内部でいくつかのフェーズを通過しています。

ここで登場するのが、`doFirst` と `doLast` です。

  • `doFirst`: タスクが実行される「直前」に割り込む(フックする)
  • `doLast`: タスクが実行された「直後」に割り込む(フックする)

「なぜこれが実務で必要なのか?」
例えば、以下のようなシチュエーションを想像してください。

  • 「重いビルドが始まる前に、Slackへ『ビルド開始します』と通知したい」
  • 「ファイルをコンパイルする前に、古い一時ファイルを綺麗に掃除しておきたい」
  • 「テストが終わった直後に、生成されたレポートを指定のサーバーにアップロードしたい」

これらはすべて、既存のタスクのソースコードを書き換えることなく、`doFirst` と `doLast` を使うだけで外部から安全に差し込むことができます。

実践:ビルド前後の処理をカスタマイズする

それでは、実際に既存のJavaビルドタスク(`compileJava`)に対して、`doFirst` と `doLast` を組み込んでみましょう。

`build.gradle.kts` に以下のコードを追加してください。

// Javaのソースコードをコンパイルする標準タスク(compileJava)をカスタマイズします
tasks.named(“compileJava”) {

// コンパイルが始まる「直前」に実行される処理
doFirst {
println(“—————————————-“)
println(“【お知らせ】Javaのコンパイルを開始します…”)
println(“使用しているJavaのバージョン: ${System.getProperty(“java.version”)}”)
println(“—————————————-“)
}

// コンパイルが完了した「直後」に実行される処理
doLast {
println(“—————————————-“)
println(“【成功】コンパイルが無事に完了しました!”)
println(“次のステップ(テスト実行)へ進みます。”)
println(“—————————————-“)
}
}

実行してみる

ターミナルからコンパイルタスクを叩いてみましょう。

./gradlew compileJava

【実行結果】

> Task :compileJava
—————————————-
【お知らせ】Javaのコンパイルを開始します…
使用しているJavaのバージョン: 17.0.8
—————————————-
(ここで実際のJavaファイルのコンパイルが走る)
—————————————-
【成功】コンパイルが無事に完了しました!
次のステップ(テスト実行)へ進みます。
—————————————-

BUILD SUCCESSFUL in 2s

どうでしょうか? 標準のコンパイル処理の「前」と「後」に、私たちが意図したカスタム処理が見事に割り込んでいるのがわかりますよね。これがタスクフックの威力です。

—

5. 現場で役立つ実践知:ファイル操作やAPI連携への応用

`doFirst` と `doLast` の中で何ができるか? その答えは「GroovyやKotlinで書けることなら、何でもできる」です。

実務で即座に使える、ちょっと実用的なサンプルを最後にご紹介します。

例:ビルドの直前に古い成果物を確実に削除する

Gradleには標準でクリーン機能がありますが、特定のカスタムログやキャッシュファイルを確実に吹き飛ばしたいときに便利です。

tasks.register(“cleanTempFiles”) {
doFirst {
val targetDir = file(“${project.projectDir}/temp”)
if (targetDir.exists()) {
targetDir.deleteRecursively()
println(“古い一時ファイルを削除しました: ${targetDir.absolutePath}”)
} else {
println(“削除対象の一時ファイルはありませんでした。”)
}
}
}

// buildタスクの実行前に、必ずこの一時ファイル削除タスクを動かす依存関係を設定
tasks.named(“build”) {
dependsOn(“cleanTempFiles”)
}

このように、タスク同士の前後関係や、タスク内の `doFirst` / `doLast` を組み合わせることで、あなただけの強固で自動化されたビルドパイプラインが完成します。

—

まとめ

今回は、Gradleのタスクライフサイクルと、`doFirst` / `doLast` を使った処理の注入方法について解説しました。

  • Gradleを使えば、柔軟かつ高速なビルド管理ができる。
  • `doFirst` を使えば、タスクの「直前」にログ出力や前処理を差し込める。
  • `doLast` を使えば、タスクの「直後」に通知や後片付けを差し込める。

ビルドツールを「言われた通りに動かす黒箱」から「自分の意のままにカスタマイズできる相棒」に変えるだけで、日々の開発スピードとコードへの愛着は劇的に変わります。

ぜひ、あなたのプロジェクトでも試してみてくださいね。それでは、快適なGradleライフを!

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