第57章 マルチプロジェクトのビルド

The powerful support for multi-project builds is one of Gradle's unique selling points. This topic is also the most intellectually challenging.

マルチプロジェクトはビルドツールの知恵の絞りどころであり、とてもやりがいのあるテーマです。Gradleはマルチプロジェクトを強力にサポートしていて、ユニークなセールスポイントの一つとなっています。

A multi-project build in gradle consists of one root project, and one or more subprojects that may also have subprojects.

57.1. クロスプロジェクト設定Cross project configuration

While each subproject could configure itself in complete isolation of the other subprojects, it is common that subprojects share common traits. It is then usually preferable to share configurations among projects, so the same configuration affects several subprojects.

Let's start with a very simple multi-project build. Gradle is a general purpose build tool at its core, so the projects don't have to be Java projects. Our first examples are about marine life.

まず、ごく簡単な例から始めましょう。なんと言ってもGradleは汎用のビルドツールですので、扱うプロジェクトがJavaプロジェクトである必要はありません。 これから例として使用するプロジェクトは、海洋生物に関するものです。

57.1.1. Configuration and execution

「ビルドフェーズBuild phases describes the phases of every Gradle build. Let's zoom into the configuration and execution phases of a multi-project build. Configuration here means executing the build.gradle file of a project, which implies e.g. downloading all plugins that were declared using 'apply plugin'. By default, the configuration of all projects happens before any task is executed. This means that when a single task, from a single project is requested, all projects of multi-project build are configured first. The reason every project needs to be configured is to support the flexibility of accessing and changing any part of the Gradle project model.

57.1.1.1. Configuration on demand

The Configuration injection feature and access to the complete project model are possible because every project is configured before the execution phase. Yet, this approach may not be the most efficient in a very large multi-project build. There are Gradle builds with a hierarchy of hundreds of subprojects. The configuration time of huge multi-project builds may become noticeable. Scalability is an important requirement for Gradle. Hence, starting from version 1.4 a new incubating 'configuration on demand' mode is introduced.

Configuration on demand mode attempts to configure only projects that are relevant for requested tasks, i.e. it only executes the build.gradle file of projects that are participating in the build. This way, the configuration time of a large multi-project build can be reduced. In the long term, this mode will become the default mode, possibly the only mode for Gradle build execution. The configuration on demand feature is incubating so not every build is guaranteed to work correctly. The feature should work very well for multi-project builds that have decoupled projects (「分離されたプロジェクト Decoupled Projects). In “configuration on demand” mode, projects are configured as follows:

  • The root project is always configured. This way the typical common configuration is supported (allprojects or subprojects script blocks).
  • The project in the directory where the build is executed is also configured, but only when Gradle is executed without any tasks. This way the default tasks behave correctly when projects are configured on demand.
  • The standard project dependencies are supported and makes relevant projects configured. If project A has a compile dependency on project B then building A causes configuration of both projects.
  • The task dependencies declared via task path are supported and cause relevant projects to be configured. Example: someTask.dependsOn(":someOtherProject:someOtherTask")
  • A task requested via task path from the command line (or Tooling API) causes the relevant project to be configured. For example, building 'projectA:projectB:someTask' causes configuration of projectB.

Eager to try out this new feature? To configure on demand with every build run see 「gradle.propertiesを使用したビルド環境の構築Configuring the build environment via gradle.properties. To configure on demand just for a given build please see 付録D Gradle コマンドラインGradle Command Line.

57.1.2. 共通の振る舞いを定義するDefining common behavior

Let's look at some examples with the following project tree. This is a multi-project build with a root project named water and a subproject named bluewhale.

次のプロジェクトツリーにそってサンプルを見てみましょう。 これはルートプロジェクト「water」と、サブプロジェクトの「bluewhale(シロナガスクジラ)」からなるマルチプロジェクトです。

例57.1 マルチプロジェクト・ツリー - warter & bluewhale プロジェクト

Build layout

water/
  build.gradle
  settings.gradle
  bluewhale/

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/firstExample/water

settings.gradle

include 'bluewhale'

And where is the build script for the bluewhale project? In Gradle build scripts are optional. Obviously for a single project build, a project without a build script doesn't make much sense. For multiproject builds the situation is different. Let's look at the build script for the water project and execute it:

で、bluewhaleプロジェクトのビルドスクリプトは何処にあるのでしょう。Gradleでは、ビルドスクリプトは必須ではありません。シングルプロジェクトのビルドではビルドスクリプトのないプロジェクトなどたいした意味はありませんが、マルチプロジェクトの場合そうとは限りません。waterのビルドスクリプトを見てみましょう。

例57.2 water(親プロジェクト)のビルドスクリプト

build.gradle

Closure cl = { task -> println "I'm $task.project.name" }
task hello << cl
project(':bluewhale') {
    task hello << cl
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale

Gradle allows you to access any project of the multi-project build from any build script. The Project API provides a method called project(), which takes a path as an argument and returns the Project object for this path. The capability to configure a project build from any build script we call cross project configuration. Gradle implements this via configuration injection.

Gradleでは、マルチプロジェクトを構成するすべてのプロジェクトに、どのビルドスクリプトからでもアクセスすることができます。ビルドスクリプトではproject()というメソッドを使用でき、引数にプロジェクトへのパスを渡せばProjectオブジェクトを取得できます。つまり、あるプロジェクトを設定しようとしたとき、マルチプロジェクト内どのビルドスクリプトからでもプロジェクトを取得して設定できるのです。このことを、私たちはクロスプロジェクト設定と呼んでいます。Gradleは、この機能を設定のインジェクションを利用して実現します。

We are not that happy with the build script of the water project. It is inconvenient to add the task explicitly for every project. We can do better. Let's first add another project called krill to our multi-project build.

waterプロジェクトのビルドスクリプトは、まだ満足できるものではありません。すべてのプロジェクトに手でこのタスクを追加していくのは面倒ですよね。もっといい書き方ができます。krill(オキアミ)プロジェクトをマルチプロジェクトに追加してみましょう。

例57.3 マルチプロジェクトツリー - water, bluewhaleそしてkrill

Build layout

water/
  build.gradle
  settings.gradle
  bluewhale/
  krill/

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/addKrill/water

settings.gradle

include 'bluewhale', 'krill'

Now we rewrite the water build script and boil it down to a single line.

waterビルドスクリプトを書き直し、一行で記述するようにします。

例57.4 Waterプロジェクトのビルドスクリプト

build.gradle

allprojects {
    task hello << { task -> println "I'm $task.project.name" }
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale
I'm krill

Is this cool or is this cool? And how does this work? The Project API provides a property allprojects which returns a list with the current project and all its subprojects underneath it. If you call allprojects with a closure, the statements of the closure are delegated to the projects associated with allprojects. You could also do an iteration via allprojects.each, but that would be more verbose.

素晴らしい。これはどういう原理で動いているのでしょう。プロジェクトAPIはallprojectsというプロパティを提供しており、そのプロパティには現在のプロジェクトと、そのサブプロジェクトのリストが格納されています。 allprojectsをクロージャとともに呼び出せば、クロージャの実行がallprojectsに格納されたすべてのプロジェクトに委譲されます。もちろんallprojectsをeachでループすることもできますが、記述が冗長になってしまいます。

Other build systems use inheritance as the primary means for defining common behavior. We also offer inheritance for projects as you will see later. But Gradle uses configuration injection as the usual way of defining common behavior. We think it provides a very powerful and flexible way of configuring multiproject builds.

他のビルドシステムでは、プロジェクト間の共通設定を行う方法として主に継承を使用します。後ほど紹介するように、Gradleでも同様にプロジェクト間の継承を使用することが出来ますが、Gradleではもっと使いやすい共通設定の定義方法としてこの設定のインジェクションを使用します。設定の継承に比べて、設定を注入する方が柔軟性のある強力な方法だと考えているからです。

Another possibilty for sharing configuration is to use a common external script. See 「外部のビルドスクリプトをプロジェクトに取り込むConfiguring the project using an external build script for more information.

57.2. サブプロジェクトの設定Subproject configuration

The Project API also provides a property for accessing the subprojects only.

プロジェクトAPIはサブプロジェクトのみにアクセスする方法も提供します。

57.2.1. 共通の振る舞いを定義するDefining common behavior

例57.5 サブプロジェクト共通の振る舞いとすべてのプロジェクト共通の振る舞いをそれぞれ定義する

build.gradle

allprojects {
    task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
    hello << {println "- I depend on water"}
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale
- I depend on water
I'm krill
- I depend on water

You may notice that there are two code snippets referencing the “hello” task. The first one, which uses the “task” keyword, constructs the task and provides it's base configuration. The second piece doesn't use the “task” keyword, as it is further configuring the existing “hello” task. You may only construct a task once in a project, but you may any number of code blocks providing additional configuration.

57.2.2. 個別の振る舞いを追加するAdding specific behavior

You can add specific behavior on top of the common behavior. Usually we put the project specific behavior in the build script of the project where we want to apply this specific behavior. But as we have already seen, we don't have to do it this way. We could add project specific behavior for the bluewhale project like this:

共通の振る舞いの上に、それぞれのプロジェクト個別の振る舞いを追加することができます。普通は、プロジェクト個別の振る舞いはそのプロジェクトのビルドスクリプトに記述しますが、いままで見てきたようにかならずそうしなければいけないという訳ではありません。次の例に示すような方法で、サブプロジェクトbluewhaleに個別の振る舞いを追加することもできます。

例57.6 プロジェクト個別の振る舞いを定義する

build.gradle

allprojects {
    task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
    hello << {println "- I depend on water"}
}
project(':bluewhale').hello << {
    println "- I'm the largest animal that has ever lived on this planet."
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale
- I depend on water
- I'm the largest animal that has ever lived on this planet.
I'm krill
- I depend on water

As we have said, we usually prefer to put project specific behavior into the build script of this project. Let's refactor and also add some project specific behavior to the krill project.

前述のとおり、プロジェクト個別の振る舞いは、普通そのプロジェクトのビルドスクリプトに記述します。リファクタリングし、さらにkrillプロジェクトにも振る舞いを追加してみましょう。

例57.7 krillプロジェクトに個別の振る舞いを定義する

Build layout

water/
  build.gradle
  settings.gradle
  bluewhale/
    build.gradle
  krill/
    build.gradle

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/spreadSpecifics/water

settings.gradle

include 'bluewhale', 'krill'

bluewhale/build.gradle

hello.doLast {
  println "- I'm the largest animal that has ever lived on this planet."
}

krill/build.gradle

hello.doLast {
  println "- The weight of my species in summer is twice as heavy as all human beings."
}

build.gradle

allprojects {
    task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
    hello << {println "- I depend on water"}
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale
- I depend on water
- I'm the largest animal that has ever lived on this planet.
I'm krill
- I depend on water
- The weight of my species in summer is twice as heavy as all human beings.

57.2.3. プロジェクトのフィルタリングProject filtering

To show more of the power of configuration injection, let's add another project called tropicalFish and add more behavior to the build via the build script of the water project.

設定のインジェクションが持つもっと大きな力をお見せするために、tropicalFish(熱帯魚)プロジェクトを追加します。このプロジェクトに、waterプロジェクトのビルドスクリプトから振る舞いを定義していきます。

57.2.3.1. 名前によるフィルタリングFiltering by name

例57.8 プロジェクトに振る舞いを追加する(プロジェクト名によるフィルタリング)

Build layout

water/
  build.gradle
  settings.gradle
  bluewhale/
    build.gradle
  krill/
    build.gradle
  tropicalFish/

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/addTropical/water

settings.gradle

include 'bluewhale', 'krill', 'tropicalFish'

build.gradle

allprojects {
    task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
    hello << {println "- I depend on water"}
}
configure(subprojects.findAll {it.name != 'tropicalFish'}) {
    hello << {println '- I love to spend time in the arctic waters.'}
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale
- I depend on water
- I love to spend time in the arctic waters.
- I'm the largest animal that has ever lived on this planet.
I'm krill
- I depend on water
- I love to spend time in the arctic waters.
- The weight of my species in summer is twice as heavy as all human beings.
I'm tropicalFish
- I depend on water

The configure() method takes a list as an argument and applies the configuration to the projects in this list.

configure()は引数にリストを取るメソッドで、そのリストに格納されたプロジェクトに設定を適用します。

57.2.3.2. プロパティによるフィルタリングFiltering by properties

Using the project name for filtering is one option. Using extra project properties is another. (See 「拡張プロパティ Extra properties for more information on extra properties.)

フィルタリングには、プロジェクト名だけでなく拡張プロパティを使うこともできます。(拡張プロパティの詳細については、「拡張プロパティ Extra propertiesを参照してください。)

例57.9 プロジェクトに振る舞いを追加する(プロパティによるフィルタリング)

Build layout

water/
  build.gradle
  settings.gradle
  bluewhale/
    build.gradle
  krill/
    build.gradle
  tropicalFish/
    build.gradle

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/tropicalWithProperties/water

settings.gradle

include 'bluewhale', 'krill', 'tropicalFish'

bluewhale/build.gradle

ext.arctic = true
hello.doLast {
  println "- I'm the largest animal that has ever lived on this planet."
}

krill/build.gradle

ext.arctic = true
hello.doLast {
    println "- The weight of my species in summer is twice as heavy as all human beings."
}

tropicalFish/build.gradle

ext.arctic = false

build.gradle

allprojects {
    task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
    hello {
        doLast {println "- I depend on water"}
        afterEvaluate { Project project ->
            if (project.arctic) { doLast {
                println '- I love to spend time in the arctic waters.' }
            }
        }
    }
}

gradle -q hello の出力

> gradle -q hello
I'm water
I'm bluewhale
- I depend on water
- I'm the largest animal that has ever lived on this planet.
- I love to spend time in the arctic waters.
I'm krill
- I depend on water
- The weight of my species in summer is twice as heavy as all human beings.
- I love to spend time in the arctic waters.
I'm tropicalFish
- I depend on water

In the build file of the water project we use an afterEvaluate notification. This means that the closure we are passing gets evaluated after the build scripts of the subproject are evaluated. As the property arctic is set in those build scripts, we have to do it this way. You will find more on this topic in 「依存関係 - なんの依存関係?Dependencies - Which dependencies?

waterプロジェクトのビルドスクリプトでは、afterEvaluateという宣言文が使用されています。afterEvaluateに引き渡されたクロージャは、そのプロジェクトのビルドスクリプトが評価された後に実行されます。サブプロジェクトのarcticプロパティは、それぞれのプロジェクトのビルドスクリプト内でセットされているので、この方法でarcticプロパティにアクセスする必要があるのです。このトピックは「依存関係 - なんの依存関係?Dependencies - Which dependencies?で詳しく説明します。

57.3. マルチプロジェクトのビルド実行ルールExecution rules for multi-project builds

When we executed the hello task from the root project dir, things behaved in an intuitive way. All the hello tasks of the different projects were executed. Let's switch to the bluewhale dir and see what happens if we execute Gradle from there.

helloタスクをルートプロジェクトのディレクトリで実行したとき、ビルドは直感に従う、自然な形で実行されました。つまり、サブプロジェクトに定義されたhelloタスクも含め、すべてのhelloタスクが実行されたのです。では、bluewhaleディレクトリでGradleを実行した場合、どのようにビルドが実行されるのでしょうか。

例57.10 サブプロジェクトからビルドを実行する

gradle -q hello の出力

> gradle -q hello
I'm bluewhale
- I depend on water
- I'm the largest animal that has ever lived on this planet.
- I love to spend time in the arctic waters.

The basic rule behind Gradle's behavior is simple. Gradle looks down the hierarchy, starting with the current dir, for tasks with the name hello and executes them. One thing is very important to note. Gradle always evaluates every project of the multi-project build and creates all existing task objects. Then, according to the task name arguments and the current dir, Gradle filters the tasks which should be executed. Because of Gradle's cross project configuration every project has to be evaluated before any task gets executed. We will have a closer look at this in the next section. Let's now have our last marine example. Let's add a task to bluewhale and krill.

Gradleがビルドを実行するときの基本的なルールは単純なものです。この例では、Gradleはhelloタスクを探して、カレントディレクトリからプロジェクト階層を降りていき、タスクを見つけるとそれを実行するだけです。ただし、非常に重要な注意点が一つあります。Gradleはマルチプロジェクトに参加しているすべてのプロジェクトを常に評価します。ビルドスクリプトに定義されているタスクオブジェクトは、すべて作成されるのです。そのあと、Gradleは実行時にコマンドに引き渡したタスク名と実行ディレクトリによって、実際に実行するタスクをフィルタリングします。Gradleのクロスプロジェクト設定を実現するため、すべてのプロジェクトはタスク実行前に評価されてなければなりません。次の節では、このことについてさらに詳しく見ていきます。さて、これまで作成してきたmarineプロジェクトのbluewhalekrillに新しいタスクを追加しましょう。

例57.11 プロジェクトの評価と実行

bluewhale/build.gradle

ext.arctic = true
hello << { println "- I'm the largest animal that has ever lived on this planet." }

task distanceToIceberg << {
    println '20 nautical miles'
}

krill/build.gradle

ext.arctic = true
hello << {
    println "- The weight of my species in summer is twice as heavy as all human beings."
}

task distanceToIceberg << {
    println '5 nautical miles'
}

gradle -q distanceToIceberg の出力

> gradle -q distanceToIceberg
20 nautical miles
5 nautical miles

Here's the output without the -q option:

-qオプションを外すと、出力結果は以下のようになります。:

例57.12 プロジェクトの評価と実行

gradle distanceToIceberg の出力

> gradle distanceToIceberg
:bluewhale:distanceToIceberg
20 nautical miles
:krill:distanceToIceberg
5 nautical miles

BUILD SUCCESSFUL

Total time: 1 secs

The build is executed from the water project. Neither water nor tropicalFish have a task with the name distanceToIceberg. Gradle does not care. The simple rule mentioned already above is: Execute all tasks down the hierarchy which have this name. Only complain if there is no such task!

このビルドはwaterプロジェクトのディレクトリで実行しています。waterプロジェクトもtropicalFishプロジェクトもdistanceToIcebergという名前のタスクは持っていませんが、Gradleはそんなこと気にしません。前述の実行ルールに従って、プロジェクト階層にあるdistanceToIcebergという名前のタスクをすべて実行するだけです。distanceToIcebergタスクが一つもないときだけ警告を出してきます。

57.4. 絶対パスによるタスクの実行Running tasks by their absolute path

As we have seen, you can run a multi-project build by entering any subproject dir and execute the build from there. All matching task names of the project hierarchy starting with the current dir are executed. But Gradle also offers to execute tasks by their absolute path (see also 「プロジェクトとタスクのパスProject and task paths):

いままで見てきたように、マルチプロジェクトのサブプロジェクトは、プロジェクトのディレクトリに移動して個別にビルドできます。その場合、実行ディレクトリ以下の、指定したタスク名のタスクがすべて実行されます。また、ビルド時にタスクの絶対パスを指定することもできます。ディレクトリを移動しなくても、パスで指定したプロジェクトのタスクのみ実行できるのです。(「プロジェクトとタスクのパスProject and task pathsもご参照ください)

例57.13 絶対パスによるタスクの実行

gradle -q :hello :krill:hello hello の出力

> gradle -q :hello :krill:hello hello
I'm water
I'm krill
- I depend on water
- The weight of my species in summer is twice as heavy as all human beings.
- I love to spend time in the arctic waters.
I'm tropicalFish
- I depend on water

The build is executed from the tropicalFish project. We execute the hello tasks of the water, the krill and the tropicalFish project. The first two tasks are specified by their absolute path, the last task is executed using the name matching mechanism described above.

ビルドはtropicalFishプロジェクトのディレクトリで実行されています。上記の例では、ルートプロジェクトwatarkrillプロジェクト、そしてカレントのサブプロジェクトtropicalFishhelloがそれぞれ実行されます。

57.5. プロジェクトとタスクのパスProject and task paths

A project path has the following pattern: It starts with an optional colon, which denotes the root project. The root project is the only project in a path that is not specified by its name. The rest of a project path is a colon-separated sequence of project names, where the next project is a subproject of the previous project.

The path of a task is simply its project path plus the task name, like “:bluewhale:hello”. Within a project you can address a task of the same project just by its name. This is interpreted as a relative path.

タスクのパスは、例えば :bluewhale:hello のように、単純にプロジェクトパスにタスク名を加えるだけです。なお、カレントのプロジェクトにあるタスクは、タスク名だけで指定できます。

Originally Gradle has used the '/' character as a natural path separator. With the introduction of directory tasks (see 「ディレクトリの作成 Directory creation) this was no longer possible, as the name of the directory task contains the '/' character.

以前Gradleでは、プロジェクトのパスセパレータとして「/」を使用していました。しかし、このセパレータ文字はdirectoryタスク(「ディレクトリの作成 Directory creation参照)が導入されたときに廃止されています。directoryタスクのタスク名に、「/」が含まれるためです。

57.6. 依存関係 - なんの依存関係?Dependencies - Which dependencies?

The examples from the last section were special, as the projects had no Execution Dependencies. They had only Configuration Dependencies. The following sections illustrate the differences between these two types of dependencies.

先ほどまでの例は特殊なもので、プロジェクトの間で設定情報は依存関係がありましたが、実行に関しては依存関係がありませんでした。次のセクションでは、これら2つのタイプの依存関係の違いについて説明します。

57.6.1. 実行に関する依存関係Execution dependencies

57.6.1.1. 依存関係と実行順序Dependencies and execution order

例57.14 依存関係とビルド実行順序

Build layout

messages/
  settings.gradle
  consumer/
    build.gradle
  producer/
    build.gradle

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/dependencies/firstMessages/messages

settings.gradle

include 'consumer', 'producer'

consumer/build.gradle

task action << {
    println("Consuming message: ${rootProject.producerMessage}")
}

producer/build.gradle

task action << {
    println "Producing message:"
    rootProject.producerMessage = 'Watch the order of execution.'
}

gradle -q action の出力

> gradle -q action
Consuming message: null
Producing message:

This didn't quite do what we want. If nothing else is defined, Gradle executes the task in alphanumeric order. Therefore, Gradle will execute “:consumer:action” before “:producer:action”. Let's try to solve this with a hack and rename the producer project to “aProducer”.

この例はうまく動作しません。依存関係が定義されていないと、Gradleはタスクをアルファベット順に実行します。 したがって、:consumer(消費者):action:producer(供給者):actionより先に実行されてしまうのです。 producerプロジェクトの名前をaProducerに修正すればこの問題を解決することはできます。

例57.15 依存関係とビルド実行順序

Build layout

messages/
  settings.gradle
  aProducer/
    build.gradle
  consumer/
    build.gradle

settings.gradle

include 'consumer', 'aProducer'

aProducer/build.gradle

task action << {
    println "Producing message:"
    rootProject.producerMessage = 'Watch the order of execution.'
}

consumer/build.gradle

task action << {
    println("Consuming message: ${rootProject.producerMessage}")
}

gradle -q action の出力

> gradle -q action
Producing message:
Consuming message: Watch the order of execution.

We can show where this hack doesn't work if we now switch to the consumer dir and execute the build.

ただ、このハックは簡単に破綻してしまいます。consumerディレクトリに移動して、ビルドしてみましょう。

例57.16 依存関係とビルド実行順序

gradle -q action の出力

> gradle -q action
Consuming message: null

The problem is that the two “action” tasks are unrelated. If you execute the build from the “messages” project Gradle executes them both because they have the same name and they are down the hierarchy. In the last example only one “action” task was down the hierarchy and therefore it was the only task that was executed. We need something better than this hack.

問題はこの二つの“action”タスクが無関係であることです。 確かに、messagesプロジェクトをビルドすればGradleはこの二つのタスクを両方とも実行します。 しかし、それは二つのタスクが同じ名前のタスクであり、両方とも実行ディレクトリ以下にあるからです。 最後の例では一方のタスクが実行ディレクトリから外れたので、片方の“action”タスクしか実行されませんでした。 このハックよりも良い方法が必要です。

57.6.1.2. 依存関係を宣言するDeclaring dependencies

例57.17 依存関係を宣言する

Build layout

messages/
  settings.gradle
  consumer/
    build.gradle
  producer/
    build.gradle

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/dependencies/messagesWithDependencies/messages

settings.gradle

include 'consumer', 'producer'

consumer/build.gradle

task action(dependsOn: ":producer:action") << {
    println("Consuming message: ${rootProject.producerMessage}")
}

producer/build.gradle

task action << {
    println "Producing message:"
    rootProject.producerMessage = 'Watch the order of execution.'
}

gradle -q action の出力

> gradle -q action
Producing message:
Consuming message: Watch the order of execution.

Running this from the consumer directory gives:

consumerディレクトリでビルドすると、以下のようになります。

例57.18 依存関係を宣言する

gradle -q action の出力

> gradle -q action
Producing message:
Consuming message: Watch the order of execution.

This is now working better because we have declared that the “action” task in the “consumer” project has an execution dependency on the “action” task in the “producer” project.

consumerプロジェクトの“action”タスクは producerプロジェクトの“action”タスクに 実行時依存関係を持つことを宣言したことで、事態は改善しました。

57.6.1.3. プロジェクトをまたぐタスク間依存関係の性質 The nature of cross project task dependencies

Of course, task dependencies across different projects are not limited to tasks with the same name. Let's change the naming of our tasks and execute the build.

もちろん、同じ名前のタスク間でしかプロジェクトをまたぐ依存関係は結べない、ということはありません。 タスクの名前を変えて実行してみたのが、以下の例です。

例57.19 プロジェクトにまたがるタスク間の依存関係

consumer/build.gradle

task consume(dependsOn: ':producer:produce') << {
    println("Consuming message: ${rootProject.producerMessage}")
}

producer/build.gradle

task produce << {
    println "Producing message:"
    rootProject.producerMessage = 'Watch the order of execution.'
}

gradle -q consume の出力

> gradle -q consume
Producing message:
Consuming message: Watch the order of execution.

57.6.2. 評価順序の依存関係Configuration time dependencies

Let's see one more example with our producer-consumer build before we enter Java land. We add a property to the “producer” project and create a configuration time dependency from “consumer” to “producer”.

そろそろ実際のJavaでの例をみていきたいと思いますが、その前に、producer-consumerプロジェクトについてもう一つ例を見てみます。ひとつのプロパティをproducerプロジェクトに加えて、consumerプロジェクトから評価順序の依存関係をproducerプロジェクトに作成します。

例57.20 評価順序の依存関係

consumer/build.gradle

def message = rootProject.producerMessage

task consume << {
    println("Consuming message: " + message)
}

producer/build.gradle

rootProject.producerMessage = 'Watch the order of evaluation.'

gradle -q consume の出力

> gradle -q consume
Consuming message: null

The default evaluation order of projects is alphanumeric (for the same nesting level). Therefore the “consumer” project is evaluated before the “producer” project and the “producerMessage” value is set after it is read by the “consumer” project. Gradle offers a solution for this.

デフォルトでは、プロジェクトはプロジェクト名のアルファベット順で評価されます。したがって、consumerプロジェクトはproducerプロジェクトの前に評価され、producerプロジェクトのプロパティはconsumerプロジェクトが読みにいった後にセットされてしまうのです。Gradleでは、このような問題に対する解決策を提供しています。

例57.21 評価順序の依存関係 - evaluationDependsOn

consumer/build.gradle

evaluationDependsOn(':producer')

def message = rootProject.producerMessage

task consume << {
    println("Consuming message: " + message)
}

gradle -q consume の出力

> gradle -q consume
Consuming message: Watch the order of evaluation.

The use of the “evaluationDependsOn” command results in the evaluation of the “producer” project before the “consumer” project is evaluated. This example is a bit contrived to show the mechanism. In this case there would be an easier solution by reading the key property at execution time.

このevaluationDependsOnコマンドにより、producerプロジェクトがconsumerの前に評価されるようになります。 なお、この例はメカニズムを説明するためのやや不自然なものです。 この場合は実行時にタスク内でプロパティを読みにいった方が早いでしょう。

例57.22 評価順序の依存関係

consumer/build.gradle

task consume << {
    println("Consuming message: ${rootProject.producerMessage}")
}

gradle -q consume の出力

> gradle -q consume
Consuming message: Watch the order of evaluation.

Configuration dependencies are very different from execution dependencies. Configuration dependencies are between projects whereas execution dependencies are always resolved to task dependencies. Also note that all projects are always configured, even when you start the build from a subproject. The default configuration order is top down, which is usually what is needed.

評価順序の依存関係と実行時の依存関係では、大きく異なる点があります。 まず、実行の依存関係は最終的には常にタスク間の依存関係になりますが、評価順序の依存関係はプロジェクト間の依存関係です。 さらに、サブプロジェクトを始点にビルドを開始した場合でも、そのマルチプロジェクトに含まれる全てのプロジェクトが評価されるという違いもあります。 デフォルトでは、ルートプロジェクトからトップダウンでサブプロジェクトに向かって評価されていきます。普通はその順序で評価する必要があるからです。

To change the default configuration order to “bottom up”, use the “evaluationDependsOnChildren()” method instead.

このデフォルトを変更して"ボトムアップ"で評価されるようにするには、かわりに"evaluationDependsOnChildren()"メソッドを使ってください。

On the same nesting level the configuration order depends on the alphanumeric position. The most common use case is to have multi-project builds that share a common lifecycle (e.g. all projects use the Java plugin). If you declare with dependsOn a execution dependency between different projects, the default behavior of this method is to also create a configuration dependency between the two projects. Therefore it is likely that you don't have to define configuration dependencies explicitly.

同じ階層にあるプロジェクトは、プロジェクト名のアルファベット順で評価されます。よく使われるのは、マルチプロジェクトに参加しているプロジェクトの間で、同じビルドサイクルを共有させるときです(すべてのプロジェクトがJavaプラグインを使うなど)。プロジェクト間に実行の依存関係を定義すると、評価順序の依存関係も同様に定義されます。したがって、評価順序の依存関係を明示的に指定することはあまりないかもしれません。

57.6.3. 実生活での例Real life examples

Gradle's multi-project features are driven by real life use cases. One good example consists of two web application projects and a parent project that creates a distribution including the two web applications. [32] For the example we use only one build script and do cross project configuration.

Gradleのマルチプロジェクト機能は、開発現場でのユースケースに基づいて作成されています。ここではそのようなユースケースの例として、二つのWebアプリケーションとそれらの配布物を作成する親プロジェクトを紹介します。 [33] この例では、クロスプロジェクト設定を使って、一つのビルドスクリプトですべてのプロジェクトの設定を行っています。

例57.23 依存関係 - 実生活の例 - クロスプロジェクト設定

Build layout

webDist/
  settings.gradle
  build.gradle
  date/
    src/main/java/
      org/gradle/sample/
        DateServlet.java
  hello/
    src/main/java/
      org/gradle/sample/
        HelloServlet.java

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/dependencies/webDist

settings.gradle

include 'date', 'hello'

build.gradle

allprojects {
    apply plugin: 'java'
    group = 'org.gradle.sample'
    version = '1.0'
}

subprojects {
    apply plugin: 'war'
    repositories {
        mavenCentral()
    }
    dependencies {
        compile "javax.servlet:servlet-api:2.5"
    }
}

task explodedDist(dependsOn: assemble) << {
    File explodedDist = mkdir("$buildDir/explodedDist")
    subprojects.each {project ->
        project.tasks.withType(Jar).each {archiveTask ->
            copy {
                from archiveTask.archivePath
                into explodedDist
            }
        }
    }
}

We have an interesting set of dependencies. Obviously the date and hello projects have a configuration dependency on webDist, as all the build logic for the webapp projects is injected by webDist. The execution dependency is in the other direction, as webDist depends on the build artifacts of date and hello. There is even a third dependency. webDist has a configuration dependency on date and hello because it needs to know the archivePath. But it asks for this information at execution time. Therefore we have no circular dependency.

この例は、面白い依存関係を持っています。dateプロジェクトとhelloプロジェクトで定義されているタスクの評価は、明らかにwebDist評価に依存しています。Webアプリのビルドロジックは、すべてwebDistによって注入されているからです。しかし、実行の依存関係はそれとは逆です。webDisthellodateの成果物を使ってビルドされるからです。さらに、もう一つの依存関係があります。webDistプロジェクトの評価は、dateとhelloの評価に依存しています。なぜなら、それらのプロジェクトのarchivePathプロパティの値を知る必要があるからです。ただ、この問い合わせは実行時に行われていて、依存関係が循環しているわけではありません。

Such and other dependency patterns are daily bread in the problem space of multi-project builds. If a build system does not support these patterns, you either can't solve your problem or you need to do ugly hacks which are hard to maintain and massively impair your productivity as a build master.

この手の依存関係は、マルチプロジェクトのビルドではよく発生します。ビルドシステムがこのような依存関係に対応できなければ、解決できない問題が発生したり、苦し紛れのハックを行うハメになるでしょう。ビルドのメンテナンスが難しくなり、ビルドの管理者として生産性を大きく落としてしまうことになりかねません。

57.7. プロジェクト依存関係Project lib dependencies

What if one project needs the jar produced by another project in its compile path, and not just the jar but also the transitive dependencies of this jar? Obviously this is a very common use case for Java multi-project builds. As already mentioned in 「プロジェクト依存関係 Project dependencies, Gradle offers project lib dependencies for this.

あるプロジェクトが、別のプロジェクトの生成するjarをコンパイルするときのクラスパスに必要としていたらどうすればいいのでしょう。さらに、単にjarだけでなく、そのjarが依存する他のライブラリも必要だとしたら? これは、Javaのマルチプロジェクトではいかにもよくありそうなパターンです。「プロジェクト依存関係 Project dependenciesですでに言及していますが、プロジェクト依存関係はこのようなケースに対応するものです。

例57.24 プロジェクト依存関係

Build layout

java/
  settings.gradle
  build.gradle
  api/
    src/main/java/
      org/gradle/sample/
        api/
          Person.java
        apiImpl/
          PersonImpl.java
  services/personService/
    src/
      main/java/
        org/gradle/sample/services/
          PersonService.java
      test/java/
        org/gradle/sample/services/
          PersonServiceTest.java
  shared/
    src/main/java/
      org/gradle/sample/shared/
        Helper.java

ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/userguide/multiproject/dependencies/java


We have the projects “shared”, “api” and “personService”. The “personService” project has a lib dependency on the other two projects. The “api” project has a lib dependency on the “shared” project. [34]

sharedapipersonServiceの三つのプロジェクトがあります。personServiceプロジェクトは他の二つのプロジェクトに依存しています。さらに、apiプロジェクトはsharedプロジェクトに依存しています。 [35]

例57.25 プロジェクト依存関係

settings.gradle

include 'api', 'shared', 'services:personService'

build.gradle

subprojects {
    apply plugin: 'java'
    group = 'org.gradle.sample'
    version = '1.0'
    repositories {
        mavenCentral()
    }
    dependencies {
        testCompile "junit:junit:4.11"
    }
}

project(':api') {
    dependencies {
        compile project(':shared')
    }
}

project(':services:personService') {
    dependencies {
        compile project(':shared'), project(':api')
    }
}

All the build logic is in the “build.gradle” file of the root project. [36] A “lib” dependency is a special form of an execution dependency. It causes the other project to be built first and adds the jar with the classes of the other project to the classpath. It also adds the dependencies of the other project to the classpath. So you can enter the “api” directory and trigger a “gradle compile”. First the “shared” project is built and then the “api” project is built. Project dependencies enable partial multi-project builds.

ビルドロジックはすべてルートプロジェクトのbuild.gradleに記述されています。 [37] プロジェクト依存関係は、実行依存の別形態といえます。まずあるプロジェクトのビルドを実行し、生成されたクラスをjarに格納して、そのプロジェクトに依存しているプロジェクトのクラスパスに追加するのです。さらに、そのプロジェクトの依存関係も同時にクラスパスに追加します。apiディレクトリに移動して、gradle compileと入力してみましょう。まずsharedがビルドされ、次にapiがビルドされるでしょう。プロジェクト依存はマルチプロジェクトの部分ビルドを可能にします。

If you come from Maven land you might be perfectly happy with this. If you come from Ivy land, you might expect some more fine grained control. Gradle offers this to you:

Mavenを使用されてきた人なら、このことですごく満足できるでしょう。Ivyの経験をお持ちなら、もっと細かい制御を期待するかもしれません。Gradleでは、次のようにします。

例57.26 細かい依存関係の制御

build.gradle

subprojects {
    apply plugin: 'java'
    group = 'org.gradle.sample'
    version = '1.0'
}

project(':api') {
    configurations {
        spi
    }
    dependencies {
        compile project(':shared')
    }
    task spiJar(type: Jar) {
        baseName = 'api-spi'
        dependsOn classes
        from sourceSets.main.output
        include('org/gradle/sample/api/**')
    }
    artifacts {
        spi spiJar
    }
}

project(':services:personService') {
    dependencies {
        compile project(':shared')
        compile project(path: ':api', configuration: 'spi')
        testCompile "junit:junit:4.11", project(':api')
    }
}

The Java plugin adds per default a jar to your project libraries which contains all the classes. In this example we create an additional library containing only the interfaces of the “api” project. We assign this library to a new dependency configuration. For the person service we declare that the project should be compiled only against the “api” interfaces but tested with all classes from “api”.

Javaプラグインは、デフォルトではすべてのクラスが含まれたjarをプロジェクトのライブラリとして追加しますが、ここではそれに加えて、apiプロジェクトのインターフェースのみが含まれたライブラリを作成しています。そしてこのライブラリを、新規に作成した、依存関係のコンフィギュレーション(spi)に割り当てています。person serviceをコンパイルするときはインターフェースしか要りませんが、テストをするにはapiからすべてのクラスを持ってくる必要があるのです。

57.7.1. 依存プロジェクトをビルドしないようにするDisabling the build of dependency projects

Sometimes you don't want depended on projects to be built when doing a partial build. To disable the build of the depended on projects you can run Gradle with the -a option.

部分ビルドを実行する際、依存しているプロジェクトはビルドしたくない、という場合もあるかもしれません。依存しているプロジェクトのビルドを無効化するには、-aオプションをつけてプロジェクトをビルドしてください。

57.8. Parallel project execution

With more and more CPU cores available on developer desktops and CI servers, it is important that Gradle is able to fully utilise these processing resources. More specifically, the parallel execution attempts to:

  • Reduce total build time for a multi-project build where execution is IO bound or otherwise does not consume all available CPU resources.
  • Provide faster feedback for execution of small projects without awaiting completion of other projects.

Although Gradle already offers parallel test execution via Test.setMaxParallelForks() the feature described in this section is parallel execution at a project level. Parallel execution is an incubating feature. Please use it and let us know how it works for you.

Parallel project execution allows the separate projects in a decoupled multi-project build to be executed in parallel (see also: 「分離されたプロジェクト Decoupled Projects). While parallel execution does not strictly require decoupling at configuration time, the long-term goal is to provide a powerful set of features that will be available for fully decoupled projects. Such features include:

  • 「Configuration on demand」.
  • Configuration of projects in parallel.
  • Re-use of configuration for unchanged projects.
  • Project-level up-to-date checks.
  • Using pre-built artifacts in the place of building dependent projects.

How does parallel execution work? First, you need to tell Gradle to use the parallel mode. You can use the command line argument (付録D Gradle コマンドラインGradle Command Line) or configure your build environment (「gradle.propertiesを使用したビルド環境の構築Configuring the build environment via gradle.properties). Unless you provide a specific number of parallel threads Gradle attempts to choose the right number based on available CPU cores. Every parallel worker exclusively owns a given project while executing a task. This means that 2 tasks from the same project are never executed in parallel. Therefore only multi-project builds can take advantage of parallel execution. Task dependencies are fully supported and parallel workers will start executing upstream tasks first. Bear in mind that the alphabetical scheduling of decoupled tasks, known from the sequential execution, does not really work in parallel mode. You need to make sure the task dependencies are declared correctly to avoid ordering issues.

57.9. 分離されたプロジェクト Decoupled Projects

Gradle allows any project to access any other project during both the configuration and execution phases. While this provides a great deal of power and flexibility to the build author, it also limits the flexibility that Gradle has when building those projects. For instance, this effectively prevents Gradle from correctly building multiple projects in parallel, configuring only a subset of projects, or from substituting a pre-built artifact in place of a project dependency.

Gradleでは、評価フェーズでも実行フェーズでも、あらゆるプロジェクト間でお互いにアクセスすることができるようになっています。 これはビルドの作成者にとっては非常に強力で柔軟性のある仕様ですが、ビルド時にはGradleの方の柔軟性が制限されることになります。 例を挙げると、強固に結合されたプロジェクトは、Gradleに平行ビルドさせることができませんし、依存プロジェクトの代わりにビルド済みのアーティファクトを使用することもできません。

Two projects are said to be decoupled if they do not directly access each other's project model. Decoupled projects may only interact in terms of declared dependencies: project dependencies (「プロジェクト依存関係 Project dependencies) and/or task dependencies (「タスクの依存関係Task dependencies). Any other form of project interaction (i.e. by modifying another project object or by reading a value from another project object) causes the projects to be coupled. The consequence of coupling during the configuration phase is that if gradle is invoked with the 'configuration on demand' option, the result of the build can be flawed in several ways. The consequence of coupling during execution phase is that if gradle is invoked with the parallel option, one project task runs too late to influence a task of a project building in parallel. Gradle does not attempt to detect coupling and warn the user, as there are too many possibilities to introduce coupling.

お互いのプロジェクト・モデルにアクセスしない二つのプロジェクトは、分離されたプロジェクトと呼ばれます。 分離されたプロジェクトでは、お互いのやりとりに依存関係宣言のみを使用します。つまり、プロジェクト依存(「プロジェクト依存関係 Project dependencies)やタスク依存(「タスクの依存関係Task dependencies)です。 その他のあらゆるアクセス、例えば他プロジェクトのオブジェクトを変更したり、設定値を読み込んだりといった処理は、全てプロジェクトを結合させます。

A very common way for projects to be coupled is by using configuration injection (「クロスプロジェクト設定Cross project configuration). It may not be immediately apparent, but using key Gradle features like the allprojects and subprojects keywords automatically cause your projects to be coupled. This is because these keywords are used in a build.gradle file, which defines a project. Often this is a “root project” that does nothing more than define common configuration, but as far as Gradle is concerned this root project is still a fully-fledged project, and by using allprojects that project is effectively coupled to all other projects. Coupling of the root project to subprojects does not impact 'configuration on demand', but using the allprojects and subprojects in any subproject's build.gradle file will have an impact.

プロジェクトが結合されるもっとも一般的な状況は、設定のインジェクション(「クロスプロジェクト設定Cross project configuration)です。 これは直ぐには分かりにくいかもしれませんが、allprojectssubprojectsキーワードのような、Gradleの特定の機能を使うと、プロジェクトは自動的に結合されます。 これらのキーワードは、build.gradleファイルで別のプロジェクトを定義するのに使われるからです。 共通設定の定義しかしていないルートプロジェクトもよくありますが、Gradleがルートプロジェクトを、完全な普通のプロジェクトと判断し、さらにallprojectsを使用している限り、プロジェクトは他の全てのプロジェクトと結合されます。

This means that using any form of shared build script logic or configuration injection (allprojects, subprojects, etc.) will cause your projects to be coupled. As we extend the concept of project decoupling and provide features that take advantage of decoupled projects, we will also introduce new features to help you to solve common use cases (like configuration injection) without causing your projects to be coupled.

つまり、どんな形であれ、ビルドスクリプトのロジック共有、および設定のインジェクション(allprojectssubprojectsなど)は、プロジェクトを結合させるということです。 私たちは、この分離されたプロジェクトという概念を拡張し、有効活用するような機能を追加する予定です。それにより、設定インジェクションなどの機能を、プロジェクトを結合することなく実現できます。

In order to make good use of cross project configuration without running into issues for parallel and 'configuration on demand' options, follow these recommendations:

  • Avoid a subproject's build.gradle referencing other subprojects; prefering cross configuration from the root project.
  • Avoid changing the configuration of other projects at execution time.

57.10. マルチプロジェクトのビルドとテスティングMulti-Project Building and Testing

The build task of the Java plugin is typically used to compile, test, and perform code style checks (if the CodeQuality plugin is used) of a single project. In multi-project builds you may often want to do all of these tasks across a range of projects. The buildNeeded and buildDependents tasks can help with this.

Javaプラグインのbuildタスクは、シングルプロジェクトをコンパイル、テスト、さらに(CodeQualityプラグインを使っていれば)コードスタイルのチェックも行うという、典型的なビルド処理を実行します。さらに、マルチプロジェクトを構成している場合は、これらのタスクをプロジェクトを横断して実行したい場面もあるでしょう。buildNeededbuildDependentsはこの用途を助けてくれるものです。

Let's use the project structure shown in 例57.25「プロジェクト依存関係」. In this example, the “:services:personservice” project depends on both the “:api” and “:shared” projects. The “:api” project also depends on the “:shared” project.

例57.25「プロジェクト依存関係」にあるプロジェクトの構造を使いましょう。この例では:service:personserviceが:apiと:sharedの双方に依存しています。また、:apiは:sharedに依存しています。

Assume you are working on a single project, the “:api” project. You have been making changes, but have not built the entire project since performing a clean. You want to build any necessary supporting jars, but only perform code quality and unit tests on the project you have changed. The build task does this.

シングルプロジェクトの:apiプロジェクトで作業しているとします。:apiに変更を加えましたが、クリーン以降、まだプロジェクト全体のビルドはしていません。jarが必要になってビルドするというとき、コード品質のチェックやユニットテストは自分が変更したプロジェクトでのみ実行したいところです。buildタスクは、まさにこの処理を行います。

例57.27 シングルプロジェクトのビルドとテスト

gradle :api:build の出力

> gradle :api:build
:shared:compileJava
:shared:processResources
:shared:classes
:shared:jar
:api:compileJava
:api:processResources
:api:classes
:api:jar
:api:assemble
:api:compileTestJava
:api:processTestResources
:api:testClasses
:api:test
:api:check
:api:build

BUILD SUCCESSFUL

Total time: 1 secs

While you are working in a typical development cycle repeatedly building and testing changes to the “:api” project (knowing that you are only changing files in this one project), you may not want to even suffer the expense of building “:shared:compile” to see what has changed in the “:shared” project. Adding the “-a” option will cause Gradle to use cached jars to resolve any project lib dependencies and not try to re-build the depended on projects.

典型的な開発サイクルでは、:apiプロジェクトに何か変更を加えるたびビルドとテストを繰り返すことになります。このとき、:shared:compileタスクは、ビルドのたび:sharedプロジェクトが変更されていないか確認しに行きます。:apiプロジェクトしか変更していないことが分かっている場合、このコストさえ苦痛かもしれません。-aオプションをつけてビルドすると、依存プロジェクトのライブラリを解決する際、キャッシュされたjarが使用されるので、依存プロジェクトが再ビルドされることありません。

例57.28 シングルプロジェクトの部分ビルドとテスト

gradle -a :api:build の出力

> gradle -a :api:build
:api:compileJava
:api:processResources
:api:classes
:api:jar
:api:assemble
:api:compileTestJava
:api:processTestResources
:api:testClasses
:api:test
:api:check
:api:build

BUILD SUCCESSFUL

Total time: 1 secs

If you have just gotten the latest version of source from your version control system which included changes in other projects that “:api” depends on, you might want to not only build all the projects you depend on, but test them as well. The buildNeeded task also tests all the projects from the project lib dependencies of the testRuntime configuration.

今、ちょうど最新のソースをバージョン管理システムから持ってきたとしましょう。:apiが依存しているプロジェクトが変更されていました。このとき、依存先の全プロジェクトについて、ビルドだけでなくテストもしたいと考えるかもしれません。buildNeededタスクは、指定したプロジェクトだけでなく、そのプロジェクトのtestRuntime依存関係に設定されているプロジェクトをすべてテストします。

例57.29 依存プロジェクトのビルドとテスト

gradle :api:buildNeeded の出力

> gradle :api:buildNeeded
:shared:compileJava
:shared:processResources
:shared:classes
:shared:jar
:api:compileJava
:api:processResources
:api:classes
:api:jar
:api:assemble
:api:compileTestJava
:api:processTestResources
:api:testClasses
:api:test
:api:check
:api:build
:shared:assemble
:shared:compileTestJava
:shared:processTestResources
:shared:testClasses
:shared:test
:shared:check
:shared:build
:shared:buildNeeded
:api:buildNeeded

BUILD SUCCESSFUL

Total time: 1 secs

You also might want to refactor some part of the “:api” project that is used in other projects. If you make these types of changes, it is not sufficient to test just the “:api” project, you also need to test all projects that depend on the “:api” project. The buildDependents task also tests all the projects that have a project lib dependency (in the testRuntime configuration) on the specified project.

さらに、:apiプロジェクトをリファクタリングする場合のことを考えてみます。:apiプロジェクトは他のプロジェクトから使用されているので、このプロジェクトだけテストしても十分とは言えません。:apiプロジェクトに依存しているすべてのプロジェクトをテストする必要があります。buildDependentsタスクは、指定したプロジェクトだけでなく、そのプロジェクトにtestRuntimeのプロジェクト依存関係を持っているすべてのプロジェクトをテストします。

例57.30 依存されているプロジェクトのビルドとテスト

gradle :api:buildDependents の出力

> gradle :api:buildDependents
:shared:compileJava
:shared:processResources
:shared:classes
:shared:jar
:api:compileJava
:api:processResources
:api:classes
:api:jar
:api:assemble
:api:compileTestJava
:api:processTestResources
:api:testClasses
:api:test
:api:check
:api:build
:services:personService:compileJava
:services:personService:processResources
:services:personService:classes
:services:personService:jar
:services:personService:assemble
:services:personService:compileTestJava
:services:personService:processTestResources
:services:personService:testClasses
:services:personService:test
:services:personService:check
:services:personService:build
:services:personService:buildDependents
:api:buildDependents

BUILD SUCCESSFUL

Total time: 1 secs

Finally, you may want to build and test everything in all projects. Any task you run in the root project folder will cause that same named task to be run on all the children. So you can just run “gradle build” to build and test all projects.

最後に、すべてのプロジェクトをビルド、テストしたいと思うことがあるかもしれません。ルートプロジェクトのフォルダーでタスクを実行すれば、全ての子プロジェクトでそのタスクと同名のタスクが実行されます。なので、ただgradle buildと実行すれば大丈夫です。すべてのプロジェクトがビルドされテストされます。

57.11. Multi Project and buildSrc

buildSrcプロジェクトのソースをビルドするBuild sources in the buildSrc project tells us that we can place build logic to be compiled and tested in the special buildSrc directory. In a multi project build, there can only be one buildSrc directory which must be located in the root directory.

57.12. プロパティとメソッドの継承Property and method inheritance

Properties and methods declared in a project are inherited to all its subprojects. This is an alternative to configuration injection. But we think that the model of inheritance does not reflect the problem space of multi-project builds very well. In a future edition of this user guide we might write more about this.

プロジェクトで宣言されたプロパティとメソッドは、すべての子プロジェクトに継承されます。これは設定のインジェクションの代替手段となるでしょう。ただ、私たちは継承というモデルはマルチプロジェクトの様々な特質を十分に反映できるものではないと考えています。将来バージョンのユーザーガイドでこのことについて詳しく書くつもりです。

Method inheritance might be interesting to use as Gradle's Configuration Injection does not support methods yet (but will in a future release).

なお、Gradleの設定のインジェクションではまだメソッドを注入することができません(将来のリリースで対応する予定です)。なので、メソッドについては継承を使う価値があるかもしれません。

You might be wondering why we have implemented a feature we obviously don't like that much. One reason is that it is offered by other tools and we want to have the check mark in a feature comparison :). And we like to offer our users a choice.

どうみても継承機能を敬遠しているのに、なんでそんなのを実装したのか不思議に思われるかもしれません。一つの理由は、継承機能が別のビルドツールで提供されているからです。比較表にチェックマークを付けたかったのです(^^)。また、ユーザーには選択肢を用意したかったというのもあります。

57.13. まとめSummary

Writing this chapter was pretty exhausting and reading it might have a similar effect. Our final message for this chapter is that multi-project builds with Gradle are usually not difficult. There are five elements you need to remember: allprojects, subprojects, evaluationDependsOn, evaluationDependsOnChildren and project lib dependencies. [38] With those elements, and keeping in mind that Gradle has a distinct configuration and execution phase, you already have a lot of flexibility. But when you enter steep territory Gradle does not become an obstacle and usually accompanies and carries you to the top of the mountain.

この章を書くのは非常に疲れました。読むのも疲れるでしょう。最後にひとつだけ、Gradleでマルチプロジェクトを扱うのは決して難しいことではありません。覚えておく必要があるのは次の5つだけです。allprojectsubprojectsevaluationDependsOnevaluationDependsOnChildren、そしてプロジェクト依存関係です。 [39] この5つ、そしてGradleが設定フェーズと実行フェーズを別個に取り扱うのだということを覚えておけば、あとは多くの場合融通がききます。もし何らかの困難な問題に突き当たったとしても、Gradleが障害になることもなく、逆にあなたを見晴らしのいい山の上に連れて行ってくれるはずです。



[32] The real use case we had, was using http://lucene.apache.org/solr, where you need a separate war for each index you are accessing. That was one reason why we have created a distribution of webapps. The Resin servlet container allows us, to let such a distribution point to a base installation of the servlet container.

[33] 実際のケースではhttp://lucene.apache.org/solrを使っており、warファイルをアクセスするインデックスごとに分割する必要がありました。分散型のウェブアプリケーションを作成したひとつの理由はそれです。サーブレットコンテナResinはそのような分散させたWebアプリケーションを、ベースとなるサーブレットコンテナをポイントするよう設定することができます。

[34] services” is also a project, but we use it just as a container. It has no build script and gets nothing injected by another build script.

[35] servicesもまたプロジェクトですが、これはただの入れ物です。このプロジェクトにはビルドスクリプトも別のプロジェクトから注入される設定もありません。

[36] We do this here, as it makes the layout a bit easier. We usually put the project specific stuff into the build script of the respective projects.

[37] この例では、ビルドスクリプトの配置を少し簡易化しています。普通は、プロジェクトが指定するものはそのプロジェクトのビルドスクリプトに記述します。

[38] So we are well in the range of the 7 plus 2 Rule :)

[39] マジカルナンバー7±2に収まるナイスな量です