第56章 ビルドのライフサイクル

We said earlier, that the core of Gradle is a language for dependency based programming. In Gradle terms this means that you can define tasks and dependencies between tasks. Gradle guarantees that these tasks are executed in the order of their dependencies, and that each task is executed only once. These tasks form a Directed Acyclic Graph. There are build tools that build up such a dependency graph as they execute their tasks. Gradle builds the complete dependency graph before any task is executed. This lies at the heart of Gradle and makes many things possible which would not be possible otherwise.

前述の通り、Gradleというのは言ってしまえば依存性を記述する言語です。Gradleの用語でいえば、タスクとタスク間の依存関係を定義できる言語だということです。Gradleは、定義したタスクが定義した依存性に従って順に、またそれぞれ一回のみ実行されることを保証します。Gradleのタスクは、お互いの依存性により無閉路有向グラフ(DAG)を構築するのです。タスクを実行するときにこのようなタスクグラフを組み立てるビルドツールもありますが、Gradleではまずタスクグラフを完全に構築してからタスクを実行していきます。このことはGradleのもっとも重要な概念であり、このメカニズムによりたくさんのことを可能にしているのです。

Your build scripts configure this dependency graph. Therefore they are strictly speaking build configuration scripts.

ビルドスクリプトは、このタスクグラフを定義します。したがって、ビルドスクリプトとは、厳密にはビルド設定スクリプトだと言えるでしょう。

56.1. ビルドフェーズBuild phases

A Gradle build has three distinct phases.

Gradleには、独立した三つのビルドフェーズがあります。

初期化Initialization

Gradle supports single and multi-project builds. During the initialization phase, Gradle determines which projects are going to take part in the build, and creates a Project instance for each of these projects.

Gradleはシングルプロジェクト、マルチプロジェクトの双方をサポートします。初期化フェーズでは、どのプロジェクトがビルドに参加しているのか決定し、Projectインスタンスを生成します。

設定Configuration

During this phase the project objects are configured. The build scripts of all projects which are part of the build are executed. Gradle 1.4 introduced an incubating opt-in feature called configuration on demand. In this mode, Gradle configures only relevant projects (see 「Configuration on demand」).

プロジェクトのオブジェクトが設定されるフェーズです。ビルドに含まれるすべてのプロジェクトのビルドスクリプトが実行されます。 Gradle1.4で、「オンデマンド設定」という機能が試験的に導入されました。 オンデマンド設定モードでは、Gradleは全てのプロジェクトではなく、実行時に必要なプロジェクトのみ設定します。

実行Execution

Gradle determines the subset of the tasks, created and configured during the configuration phase, to be executed. The subset is determined by the task name arguments passed to the gradle command and the current directory. Gradle then executes each of the selected tasks.

設定フェーズで初期化と設定が完了したタスクのうち、実行するべきタスクを抽出して実行します。実行するタスクは、gradleコマンドに引き渡されたタスク名と、コマンドの実行ディレクトリから決定されます。

56.2. 設定ファイルSettings file

Beside the build script files, Gradle defines a settings file. The settings file is determined by Gradle via a naming convention. The default name for this file is settings.gradle. Later in this chapter we explain how Gradle looks for a settings file.

ビルドスクリプトの他、Gradleではビルドの「設定ファイル」を作成することができます。設定ファイルの場所はGradleの規約により決定され、デフォルトではプロジェクトディレクトリのsettings.gradleです。この設定ファイルをGradleがどのように探すのかについては、後ほど解説します。

The settings file is executed during the initialization phase. A multiproject build must have a settings.gradle file in the root project of the multiproject hierarchy. It is required because the settings file defines which projects are taking part in the multi-project build (see 57章マルチプロジェクトのビルド). For a single-project build, a settings file is optional. Besides defining the included projects, you might need it to add libraries to your build script classpath (see 60章ビルドロジックの体系化). Let's first do some introspection with a single project build:

設定ファイルは、初期化フェーズで使用されます。マルチプロジェクトの場合、settings.gradleファイルは必須です。ルートとなるプロジェクトに必ず配置しなければなりません。この設定ファイルにより、どのプロジェクトがビルドに参加するか決定するからです(57章マルチプロジェクトのビルド参照)。シングルプロジェクトの場合は設定ファイルはなくてもかまいません。ビルドスクリプトのクラスパスにライブラリを追加するときなどはこの設定ファイルを使うこともあるでしょう(60章ビルドロジックの体系化参照)。設定ファイルの読み込み、フェーズ実行の内部動作を、シングルプロジェクトの例を使って見てみます。

例56.1 シングルプロジェクトのビルド

settings.gradle

println 'This is executed during the initialization phase.'

build.gradle

println 'This is executed during the configuration phase.'

task configured {
    println 'This is also executed during the configuration phase.'
}

task test << {
    println 'This is executed during the execution phase.'
}

task testBoth {
    doFirst {
      println 'This is executed first during the execution phase.'
    }
    doLast {
      println 'This is executed last during the execution phase.'
    }
    println 'This is executed during the configuration phase as well.'
}

gradle test testBoth の出力

> gradle test testBoth
This is executed during the initialization phase.
This is executed during the configuration phase.
This is also executed during the configuration phase.
This is executed during the configuration phase as well.
:test
This is executed during the execution phase.
:testBoth
This is executed first during the execution phase.
This is executed last during the execution phase.

BUILD SUCCESSFUL

Total time: 1 secs

For a build script, the property access and method calls are delegated to a project object. Similarly property access and method calls within the settings file is delegated to a settings object. Look at Settings class in the API documentation for more information.

ビルドスクリプト内でのプロパティアクセスやメソッド呼び出しは、Projectインスタンスに委譲されますが、それと同様に設定ファイル内でのプロパティアクセス、メソッド呼び出しはsettingsインスタンスに委譲されます。Settingsをご参照ください。

56.3. マルチプロジェクトのビルドMulti-project builds

A multi-project build is a build where you build more than one project during a single execution of Gradle. You have to declare the projects taking part in the multiproject build in the settings file. There is much more to say about multi-project builds in the chapter dedicated to this topic (see 57章マルチプロジェクトのビルド).

マルチプロジェクトとは、ある一つのプロジェクトをビルドしているときに、関連する別のプロジェクトも呼び出されてビルドされるよう構成されたプロジェクトの集まりです。マルチプロジェクトのビルドにプロジェクトを参加させるには、設定ファイルでそのことを宣言しなくてはなりません。マルチプロジェクトについては、専用の章をもうけて詳しく説明します(57章マルチプロジェクトのビルド参照)。

56.3.1. プロジェクトの配置Project locations

Multi-project builds are always represented by a tree with a single root. Each element in the tree represents a project. A project has a path which denotes the position of the project in the multi-project build tree. In most cases the project path is consistent with the physical location of the project in the file system. However, this behavior is configurable. The project tree is created in the settings.gradle file. By default it is assumed that the location of the settings file is also the location of the root project. But you can redefine the location of the root project in the settings file.

マルチプロジェクトは、常に、あるプロジェクトをルートにしたプロジェクトのツリー構造となります。 マルチプロジェクト・ツリーを構成するそれぞれのプロジェクトは、自分がツリー内のどこに位置しているかを示すパス情報を持っています。 ほとんどのケースで、そのパス情報は、ファイルシステム上でのプロジェクトの物理的な位置を反映したものになるはずです。 ただし、これは設定で変更することも可能です。 プロジェクト・ツリーは、settings.gradleファイルで作成します。 デフォルトでは、settings.gradleファイルがあるプロジェクトがルートプロジェクトになっていますが、ルートプロジェクトの位置もその設定ファイルで変更可能です。

56.3.2. プロジェクト・ツリーの構築Building the tree

In the settings file you can use a set of methods to build the project tree. Hierarchical and flat physical layouts get special support.

設定ファイル内では、プロジェクト・ツリーを構築するためのメソッドを使用できます。これらのメソッドでは、プロジェクトを実際に配置するレイアウトとして、階層構造とフラットな構造の二つをサポートしています。

56.3.2.1. 階層構造のレイアウトHierarchical layouts

例56.2 階層構造のレイアウト

settings.gradle

include 'project1', 'project2:child', 'project3:child1'

The include method takes project paths as arguments. The project path is assumed to be equal to the relative physical file system path. For example, a path 'services:api' is mapped by default to a folder 'services/api' (relative from the project root). You only need to specify the leaves of the tree. This means that the inclusion of the path 'services:hotels:api' will result in creating 3 projects: 'services', 'services:hotels' and 'services:hotels:api'.

includeメソッドは、プロジェクトのパスを引数にとります。 このパスは、ファイルシステム上の物理的な相対パスを表したものです。 例えば、'services:api'というパスは、デフォルトでは'services/api'フォルダ(プロジェクトルートからの相対)にマッピングされます。 指定するのは、ツリーの末端となるプロジェクトだけで構いません。 つまり、'services:hotels:api'を指定しただけで3つのプロジェクトが作成されるということです('services'、'services:hotels'、'services:hotels:api')。

56.3.2.2. フラットなレイアウトFlat layouts

例56.3 フラットなレイアウト

settings.gradle

includeFlat 'project3', 'project4'

The includeFlat method takes directory names as an argument. These directories need to exist as siblings of the root project directory. The location of these directories are considered as child projects of the root project in the multi-project tree.

includeFlatメソッドは、ディレクトリ名を引数にとります。これらのディレクトリは、ルートプロジェクトのディレクトリと同じ階層にある隣接ディレクトリでなければなりませんが、マルチプロジェクト・ツリーにおいてはルートプロジェクトの子プロジェクトに設定されます。

56.3.3. プロジェクト・ツリーの属性を変更するModifying elements of the project tree

The multi-project tree created in the settings file is made up of so called project descriptors. You can modify these descriptors in the settings file at any time. To access a descriptor you can do:

設定ファイルで定義したプロジェクト・ツリーは、プロジェクトディスクリプタと呼ばれる属性を持っています。これらの属性は設定ファイルの中でいつでも変更することができます。ディスクリプタには、以下のようにしてアクセスできます。

例56.4 プロジェクト・ツリーの属性を変更する

settings.gradle

println rootProject.name
println project(':projectA').name

Using this descriptor you can change the name, project directory and build file of a project.

ディスクリプタを使用して、プロジェクト名やプロジェクトディレクトリ、プロジェクトのビルドスクリプト名を変更できます。

例56.5 プロジェクトツリーの属性を変更する

settings.gradle

rootProject.name = 'main'
project(':projectA').projectDir = new File(settingsDir, '../my-project-a')
project(':projectA').buildFileName = 'projectA.gradle'

Look at the ProjectDescriptor class in the API documentation for more information.

詳細についてはProjectDescriptorをご参照ください。

56.4. 初期化Initialization

How does Gradle know whether to do a single or multiproject build? If you trigger a multiproject build from a directory with a settings file is, things are easy. But Gradle also allows you to execute the build from within any subproject taking part in the build. [30] If you execute Gradle from within a project with no settings.gradle file, Gradle looks for a settings.gradle file in the following way:

Gradleは、現在ビルドしているものがマルチプロジェクトかシングルプロジェクトか、どうやって知るのでしょう。マルチプロジェクトのビルドを、設定ファイルのあるディレクトリから起動しているなら話は簡単ですが、Gradleはサブプロジェクトのディレクトリからビルドを実行することもできるようになっています。 [31] Gradleは、settings.gradleのないディレクトリで実行された場合、以下のように動作します。

  • It looks in a directory called master which has the same nesting level as the current dir.

    カレントディレクトリと同階層のディレクトリからmasterという名前のディレクトリを探す。

  • If not found yet, it searches parent directories.

    見つからなければ、親階層のディレクトリをたどっていって探す。

  • If not found yet, the build is executed as a single project build.

    見つからなければ、シングルプロジェクトとしてビルドする。

  • If a settings.gradle file is found, Gradle checks if the current project is part of the multiproject hierarchy defined in the found settings.gradle file. If not, the build is executed as a single project build. Otherwise a multiproject build is executed.

    settings.gradleが見つかれば、Gradleはビルドを実行したプロジェクトがマルチプロジェクトに参加するよう宣言されているかどうか確認する。宣言が確認できればマルチプロジェクトとしてビルドするが、確認できなければ、現在のプロジェクトをシングルプロジェクトとしてビルドする。

What is the purpose of this behavior? Gradle needs to determine whether the project you are in is a subproject of a multiproject build or not. Of course, if it is a subproject, only the subproject and its dependent projects are built, but Gradle needs to create the build configuration for the whole multiproject build (see 57章マルチプロジェクトのビルド). You can use the -u command line option to tell Gradle not to look in the parent hierarchy for a settings.gradle file. The current project is then always built as a single project build. If the current project contains a settings.gradle file, the -u option has no meaning. Such a build is always executed as:

なんのためにこのような振る舞いをするのでしょう。今からビルドするプロジェクトは、マルチプロジェクトのうちの一サブプロジェクトでしょうか。それとも、単なるシングルプロジェクトなのでしょうか。Gradleは、それを何とか決定しなくてはなりません。サブプロジェクトだった場合、Gradleはプロジェクト・ツリー全体の設定情報を作成する必要があるからです。ビルド対象がそのサブプロジェクトのみだったとしてもです(57章マルチプロジェクトのビルド参照)。-uオプションをつけてGradleを実行すれば、親階層をたどってsettings.gradleを探しに行くことはありません。つまり、(settings.gradleのない)現在ビルドしているプロジェクトは、常にシングルプロジェクトだと認識されるわけです。現在のプロジェクトにsettings.gradleがある場合は、-uをつけても意味はありません。settings.gradleをもつプロジェクトは以下のように処理されます。

  • a single project build, if the settings.gradle file does not define a multiproject hierarchy

    settings.gradleがマルチプロジェクト・ツリーを定義していない場合はシングルプロジェクトとしてビルド

  • a multiproject build, if the settings.gradle file does define a multiproject hierarchy.

    settings.gradleがマルチプロジェクト・ツリーを定義している場合は、マルチプロジェクトとしてビルド

The automatic search for a settings.gradle file only works for multi-project builds with a physical hierarchical or flat layout. For a flat layout you must additionally follow the naming convention described above (“master”). Gradle supports arbitrary physical layouts for a multiproject build, but for such arbitrary layouts you need to execute the build from the directory where the settings file is located. For information on how to run partial builds from the root see 「絶対パスによるタスクの実行Running tasks by their absolute path.

この設定ファイルの探索処理は、プロジェクトのディレクトリ構造が前述した階層レイアウトとフラットレイアウトのどちらかであることを想定したものです。さらに、フラットなレイアウトを採用した場合は、前述したような命名規約(settings.gradleをmasterディレクトリに配置する)に従う必要があります。マルチプロジェクト・ツリーはどんなディレクトリ構造からでも構築できますが、その場合は設定ファイルがあるディレクトリでGradleを実行しなければなりません。ルートプロジェクトからでも、一部のプロジェクトのみビルドすることは可能です。「絶対パスによるタスクの実行Running tasks by their absolute pathをご参照ください。次のリリースでは、コマンドラインから設定ファイルの場所を指定してやることで、サブプロジェクトからの部分ビルドをサポートする予定です。

初期化フェーズでは、ビルドに参加しているすべてのプロジェクトについてProjectオブジェクトが作成されます。シングルプロジェクトの場合はひとつだけ作成され、マルチプロジェクトの場合はSettingsオブジェクトで指定されているすべてのプロジェクトと、ルートプロジェクトのProjectオブジェクトが作成されるのです。作成されたProjectオブジェクトはそれぞれ名前を持っています。デフォルトではプロジェクトのディレクトリ名です。また、ルートプロジェクトをのぞく全てのプロジェクトは親プロジェクトを持っていますし、場合によっては子プロジェクトも設定されているかもしれません。

Gradle creates a Project object for every project taking part in the build. For a multi-project build these are the projects specified in the Settings object (plus the root project). Each project object has by default a name equal to the name of its top level directory, and every project except the root project has a parent project. Any project may have child projects.

56.5. シングルプロジェクトの設定と実行Configuration and execution of a single project build

For a single project build, the workflow of the after initialization phases are pretty simple. The build script is executed against the project object that was created during the initialization phase. Then Gradle looks for tasks with names equal to those passed as command line arguments. If these task names exist, they are executed as a separate build in the order you have passed them. The configuration and execution for multi-project builds is discussed in 57章マルチプロジェクトのビルド.

シングルプロジェクトの場合、初期化フェーズ完了後の処理の流れは非常にシンプルです。まず、初期化フェーズで作成されたプロジェクトのビルドスクリプトが実行されます。それから、Gradleはコマンドライン引数に渡されたタスクを探し、見つかればコマンドライン引数の順に別々に実行されます。マルチプロジェクトにおける設定と実行については、57章マルチプロジェクトのビルドを参照してください。

56.6. ライフサイクルからの通知に応答するResponding to the lifecycle in the build script

Your build script can receive notifications as the build progresses through its lifecycle. These notifications generally take two forms: You can either implement a particular listener interface, or you can provide a closure to execute when the notification is fired. The examples below use closures. For details on how to use the listener interfaces, refer to the API documentation.

ビルドスクリプト内では、ビルドがそのライフサイクルに従って進行していくなかで、さまざまな通知を受けとって処理できます。この通知を受けとるには大きく分けて二つの方法があります。ひとつは、特定のリスナーインターフェースを実装する方法、もうひとつは通知を受け取ったときに実行されるクロージャを作成する方法です。ここでは、クロージャを使用する方法について例を見ていきたいと思います。リスナーインターフェースを実装する方法についてはAPIドキュメントをご参照ください。

56.6.1. プロジェクトの評価Project evaluation

You can receive a notification immediately before and after a project is evaluated. This can be used to do things like performing additional configuration once all the definitions in a build script have been applied, or for some custom logging or profiling.

Below is an example which adds a test task to each project with the hasTests property value of true.

プロジェクトが評価される前またはされた後すぐに通知を受け取ることができます。これは、ビルドスクリプトが一通り評価された後、さらに追加の設定処理を行ったり、ロギング処理のカスタマイズやプロファイリングなどで使用されます。

以下の例は、testタスクをhasTestsプロパティを持つすべてのプロジェクトに追加するものです。

例56.6 特定プロパティを持つプロジェクトにタスクを追加する

build.gradle

allprojects {
    afterEvaluate { project ->
        if (project.hasTests) {
            println "Adding test task to $project"
            project.task('test') << {
                println "Running tests for $project"
            }
        }
    }
}

projectA.gradle

hasTests = true

gradle -q test の出力

> gradle -q test
Adding test task to project ':projectA'
Running tests for project ':projectA'

This example uses method Project.afterEvaluate() to add a closure which is executed after the project is evaluated.

It is also possible to receive notifications when any project is evaluated. This example performs some custom logging of project evaluation. Notice that the afterProject notification is received regardless of whether the project evaluates successfully or fails with an exception.

この例では、Project.afterEvaluate()を使用して、プロジェクト評価後に実行されるクロージャを追加しています。

プロジェクト評価時の通知は、どんなプロジェクトに対しても受け取ることができます。次の例では、カスタムロギング処理をプロジェクト評価時に実行しています。afterProjectが、プロジェクトの評価に成功しても例外で失敗しても通知される点に注目してください。

例56.7 通知

build.gradle

gradle.afterProject {project, projectState ->
    if (projectState.failure) {
        println "Evaluation of $project FAILED"
    } else {
        println "Evaluation of $project succeeded"
    }
}

gradle -q test の出力

> gradle -q test
Evaluation of root project 'buildProjectEvaluateEvents' succeeded
Evaluation of project ':projectA' succeeded
Evaluation of project ':projectB' FAILED

You can also add a ProjectEvaluationListener to the Gradle to receive these events.

ProjectEvaluationListenerGradleに追加することでも同様の処理を実現できます。

56.6.2. タスク作成Task creation

You can receive a notification immediately after a task is added to a project. This can be used to set some default values or add behaviour before the task is made available in the build file.

The following example sets the srcDir property of each task as it is created.

プロジェクトにタスクが作成されたときに通知を受け取ることができます。この通知は、タスクが使用される前にデフォルト値を設定したり、振る舞いを追加したりするのに使用することができます。

次の例は、作成されたタスクに、随時srcDirプロパティを追加していくものです。

例56.8 すべてのタスクにプロパティ値を設定する

build.gradle

tasks.whenTaskAdded { task ->
    task.ext.srcDir = 'src/main/java'
}

task a

println "source dir is $a.srcDir"

gradle -q a の出力

> gradle -q a
source dir is src/main/java

You can also add an Action to a TaskContainer to receive these events.

ActionTaskContainerに追加することでも同様の処理を実現できます。

56.6.3. タスク実行グラフの準備Task execution graph ready

You can receive a notification immediately after the task execution graph has been populated. We have seen this already in 「DAGによる設定Configure by DAG.

タスクの実行グラフが用意できたときに通知を受け取ることができます。この例については、「DAGによる設定Configure by DAGで紹介しました。

You can also add a TaskExecutionGraphListener to the TaskExecutionGraph to receive these events.

TaskExecutionGraphListenerTaskExecutionGraphに追加することでも同様の処理を実現できます。

56.6.4. タスクの実行Task execution

You can receive a notification immediately before and after any task is executed.

タスクが実行される前または実行された後に通知を受け取ることができます。

The following example logs the start and end of each task execution. Notice that the afterTask notification is received regardless of whether the task completes successfully or fails with an exception.

次の例では、タスクの実行が開始されたときと終わったときにそれぞれロギングを追加しています。afterTaskが、タスクの実行に成功しても例外で失敗しても通知されていることに注目してください。

例56.9 タスク実行の開始時および終了時にロギングを行う

build.gradle

task ok

task broken(dependsOn: ok) << {
    throw new RuntimeException('broken')
}

gradle.taskGraph.beforeTask { Task task ->
    println "executing $task ..."
}

gradle.taskGraph.afterTask { Task task, TaskState state ->
    if (state.failure) {
        println "FAILED"
    }
    else {
        println "done"
    }
}

gradle -q broken の出力

> gradle -q broken
executing task ':ok' ...
done
executing task ':broken' ...
FAILED

You can also use a TaskExecutionListener to the TaskExecutionGraph to receive these events.

TaskExecutionListenerTaskExecutionGraphに追加することでも同様の処理を実現できます。



[30] Gradle supports partial multiproject builds (see 57章マルチプロジェクトのビルド).

[31] Gradleは、マルチプロジェクトに含まれる一部のプロジェクトのみ部分的にビルドできる(57章マルチプロジェクトのビルド参照)