mike-neckのブログ

Java or Groovy or Swift or Golang

今更だけど、Gradleをちゃんと勉強しよう - Gradleのタスク実行フェーズ - その1

f:id:mike_neck:20150818084108g:plain

Gradle芸人業を毎日やってるけど、まだまだ全然Gradleのことわかってないなと思う日々なので、ここは基本に帰って、1つずつドキュメントのメソッド、プロパティをためしていくことにします。

第一回はproject.gradle

gradleプロパティ

初回からマニアックなテーマです。

普通にビルドスクリプトを書く分にはほとんど使うことのないプロパティです。

ただし、これを抑えることで、タスク実行のライフサイクルなどの理解がはかどります。

Gradleのライフサイクル

『Gradle徹底入門』からの引用ですが、Gradleのタスク実行には、次の3つのフェーズがあります。

  1. 初期化フェーズ
  2. 設定フェーズ
  3. 実行フェーズ

初期化フェーズ

ここでは設定の読み込み、プロジェクト構造の解釈を行います。

設定フェーズ

ここではタスクの依存関係の解析、タスクの実行内容の解析などを行います。

実行フェーズ

ここではタスクを実際に実行していきます。

hooks

project.gradleプロパティのDSLはこちらにあります。

Gradle - Gradle DSL Version 2.6

これを読み解いていくと、各フェーズのHookを次のメソッドで付与することができます。

初期化フェーズ

フェーズ開始前
  • settingsEvaludated
    • 引数 - Settings -document
    • 設定をロードして、ビルドの準備のための準備ができた状態
    • 記述可能なビルドファイル - settings.gradle
フェーズ終了
  • projectLoaded
    • 引数 - gradle
    • 設定からプロジェクトが構築された状態であり、まだプロジェクトの評価は完了していない
    • 記述可能なビルドファイル - settings.gradle

設定フェーズ

フェーズ開始前
  • beforeProject
    • 引数 - project
    • プロジェクトの評価開始前
    • 記述可能なビルドファイル - settings.gradle, build.gradle(build.gradleに記述した場合は、ルートプロジェクトに対しては呼び出されない)
フェーズ終了後
  • afterProject
    • 引数1 - project
    • 引数2 - failure - もし評価が失敗した場合
    • プロジェクトの評価が完了した後
    • 記述可能なビルドファイル - settings.gradle, build.gradle

なお、beforeProjectafterProjectrootProject -> subProjectの順番で行われる

実行フェーズ

フェーズ開始前
  • projectsEvaluated
    • 引数 - gradle
    • すべてのプロジェクトオブジェクトの評価が完了し、プロジェクトが全て設定された状態で、タスクグラフが利用可能な状態
    • 記述可能なビルドファイル - settings.gradle, build.gradle
フェーズ終了後
  • buildFinished
    • 引数1 - BuildResult - document
    • すべてのタスクの実行が終了した場合
    • 記述可能なビルドファイル - settings.gradle, build.gradle

サンプル

ディレクトリー構造

とりあえず、次のようなディレクトリー構造を作ります。

project-root
+- src
|  +- main
|  |  +- java
|  +- test
|     +- java
+- build.gradle
+- gradle.properties
+- settings.gradle
+- sub
|  +- src
|     +- main
|     |  +- java
|     +- test
|        +- java
+- dub
   +- src
      +- main
      |  +- java
      +- test
         +- java

スクリプト

settings.gradleは次のような感じにします。

// 初期化フェーズ終了後
gradle.projectsLoaded {grdl ->
    def prj = grdl.rootProject
    logger.lifecycle "====projects loaded(${prj.name})===="
    logger.lifecycle " after projects created from settings"
    logger.lifecycle " evaluated[${prj.state.executed}]"
}

// 初期化フェーズ開始前
gradle.settingsEvaluated {sts ->
    def prj = sts.rootProject
    logger.lifecycle "====settings evaluated(${prj.name})===="
    logger.lifecycle " settingsDir[${sts.settingsDir}]"
    logger.lifecycle " applied plugins[${sts.plugins.size()}]"
    logger.lifecycle "  **tasks**"
    logger.lifecycle sts.startParameter.taskNames.join(' -> ')
    logger.lifecycle "  **task-requests**"
    logger.lifecycle sts.startParameter.taskRequests.collect{"<${it.args.join('|')}>"}.join('-')
}

// プロジェクト評価前(設定フェーズ前)
gradle.beforeProject {prj ->
    logger.lifecycle "====before project(${prj.name})===="
    logger.lifecycle " before project evaluation"
    logger.lifecycle " evaluated[${prj.state.executed}]"
}

include 'sub', 'dub'
rootProject.name = 'project-root'

build.gradleには通常のビルドスクリプトに加え、次のスクリプトを追加します。

// プロジェクト評価後(設定フェーズ後)
gradle.afterProject {prj, fail ->
    logger.lifecycle "====after project(${prj.name})===="
    logger.lifecycle " after project evaluation"
    logger.lifecycle " evaluated[${prj.state.executed}]"
}

// 実行フェーズ終了後
gradle.buildFinished {result ->
    def failed = result.failure != null
    logger.lifecycle "====build finished===="
    if (failed) {
        logger.lifecycle " **FAILED**"
        logger.lifecycle "failure[${result.failure}]"
    } else {
        logger.lifecycle " **SUCCEEDED**"
        logger.lifecycle "failure[none]"
    }
}

// 実行フェーズ開始前
gradle.projectsEvaluated {grdl ->
    def prj = grdl.rootProject
    logger.lifecycle "====projects evaluated(${prj.name})===="
    logger.lifecycle " after all project's evaluation"
    logger.lifecycle " task graph is available"
    logger.lifecycle " evaluated[${prj.state.executed}]"
}

// 単なるプリントするだけのタスクを3つ
(1..3).each {num ->
    task "hello${num}" {
        doLast {
            println "hello ${num}"
        }
    }
}

実行

hello1hello3タスクを実行してみます。

$ gradle hello1 hello2 hello3
====settings evaluated(project-root)====
 settingsDir[/Users/my/projects/project-root]
 applied plugins[0]
  **tasks**
hello1 -> hello2 -> hello3
  **task-requests**
<hello1|hello2|hello3>

====projects loaded(project-root)====
 after projects created from settings
 evaluated[false]

====before project(project-root)====
 before project evaluation
 evaluated[false]

====after project(project-root)====
 after project evaluation
 evaluated[true]

====before project(dub)====
 before project evaluation
 evaluated[false]

====after project(dub)====
 after project evaluation
 evaluated[true]

====before project(sub)====
 before project evaluation
 evaluated[false]

====after project(sub)====
 after project evaluation
 evaluated[true]

====projects evaluated(project-root)====
 after all project's evaluation
 task graph is available
 evaluated[true]

:hello1
hello 1
:hello2
hello 2
:hello3
hello 3

BUILD SUCCESSFUL

Total time: 1.254 secs
====build finished====
 **SUCCEEDED**
failure[none]

hooksで紹介した通りの順番で実行されているのがわかるかと思います。

またプロジェクトのstate(評価された状態)が、beforeProjectではfalseからafterProjecttrueに変わっているのもわかるかと思います。


さて、今回フェーズについて紹介しました。その2ではタスクグラフと、それを使ってどういうことができるのかを紹介したいと思います。