Gradle offers a variety of ways to organize your build logic. First of all you can put your build logic directly in the action closure of a task. If a couple of tasks share the same logic you can extract this logic into a method. If multiple projects of a multi-project build share some logic you can define this method in the parent project. If the build logic gets too complex for being properly modeled by methods then you likely should implement your logic with classes to encapsulate your logic. [40] Gradle makes this very easy. Just drop your classes in a certain directory and Gradle automatically compiles them and puts them in the classpath of your build script.
Gradleでは、様々な方法でビルドロジックを体系化できます。最初は、すべてのビルドロジックがタスクのクロージャに置かれているかもしれません。しかし、いくつかのタスクでロジックを共有したい場合はそのロジックをメソッドに切り出すことができます。マルチプロジェクトの場合、親プロジェクトでメソッドを定義すれば、サブプロジェクトでそのメソッドを共有できます。そうしてメソッドを切り出していけば、だんだんとビルドロジックが複雑になっていく場合もあるでしょう。そのときはオブジェクト指向のモデルを取り入れるのはどうでしょうか。 [41] Gradleでは、決まったディレクトリにクラスを放り込んでおけば自動的にコンパイルしてクラスパスに追加してくれるので、そういったモデルもとても簡単に実現できます。
Here is a summary of the ways you can organise your build logic:
ビルドロジックを体系化する方法について、いくつか例を挙げます。
POGOs. You can declare and use plain old Groovy objects (POGOs) directly in your build script. The build script is written in Groovy, after all, and Groovy provides you with lots of excellent ways to organize code.
POGOs。plain old Groovy Objects (POGOs) をビルドスクリプト内で直接宣言して使用できます。結局のところビルドスクリプトはGroovyで書くわけですから、Groovyで使えるいろんな方法でビルドロジックを体系化できるのです。
Inherited properties and methods. In a multi-project build, sub-projects inherit the properties and methods of their parent project.
プロパティとメソッドの継承。マルチプロジェクトでは、すべてのサブプロジェクトが親プロジェクトからプロパティとメソッドを継承します。
Configuration injection. In a multi-project build, a project (usually the root project) can inject properties and methods into another project.
設定のインジェクション。マルチプロジェクトでは、あるプロジェクト(普通ルートプロジェクトですが)が別のプロジェクトにプロパティとメソッドを注入できます。
buildSrc
project. Drop the source for
your build classes into a certain directory and Gradle automatically compiles them and includes them
in the classpath of your build script.
buildSrc
プロジェクト。ビルドスクリプトで使いたいクラスのソースを決まったディレクトリに入れておけば、Gradleは自動的にそのクラスをコンパイルしてクラスパスに追加します。
Shared scripts. Define common configuration in an external build, and apply the script to multiple projects, possibly across different builds.
共有スクリプト。外部のスクリプトファイルに共通で使う設定を定義して、それをいろいろなプロジェクトのビルドスクリプトでプロジェクトに適用できます。別々のビルドに含まれているプロジェクトの間でも共有可能です。
Custom tasks. Put your build logic into a custom task, and reuse that task in multiple places.
カスタムタスク。ビルドロジックをカスタムタスクにすれば、いろんな場所でそのロジックを再利用できます。
Custom plugins. Put your build logic into a custom plugin,
and apply that plugin to multiple projects. The plugin must be in the classpath of your build script.
You can achieve this either by using build sources
or
by adding an external library that contains the plugin.
カスタムプラグイン。ビルドロジックをカスタムプラグインにすれば、いろんなプロジェクトでそのプラグインを適用することができます。プラグインはビルドスクリプトのクラスパスに追加しなければなりませんが、buildSrcプロジェクトや外部ライブラリにそのプラグインを含めれば簡単に追加できます。
Execute an external build. Execute another Gradle build from the current build.
外部ビルドの実行。現在のビルドから、外部の別のビルドを呼び出すことができます。
External libraries. Use external libraries directly in your build file.
外部ライブラリ。外部のライブラリを直接ビルドで使うことができます。
Any method or property defined in a project build script is also visible to all the sub-projects. You can use this to define common configurations, and to extract build logic into methods which can be reused by the sub-projects.
プロジェクト内で定義されたプロパティとメソッドは、そのプロジェクトのサブプロジェクトでもすべて使用できます。これを使って共通設定を定義したり、よく使うビルドロジックを抽出して複数のサブプロジェクトで再利用したりできます。
例60.1 プロパティとメソッドの継承を使う
build.gradle
// Define an extra property ext.srcDirName = 'src/java' // Define a method def getSrcDir(project) { return project.file(srcDirName) }
child/build.gradle
task show << { // Use inherited property println 'srcDirName: ' + srcDirName // Use inherited method File srcDir = getSrcDir(project) println 'srcDir: ' + rootProject.relativePath(srcDir) }
gradle -q show
の出力
> gradle -q show srcDirName: src/java srcDir: child/src/java
You can use the configuration injection technique discussed in 「クロスプロジェクト設定Cross project configuration」 and 「サブプロジェクトの設定Subproject configuration」 to inject properties and methods into various projects. This is generally a better option than inheritance, for a number of reasons: The injection is explicit in the build script, You can inject different logic into different projects, And you can inject any kind of configuration such as repositories, plug-ins, tasks, and so on. The following sample shows how this works.
「クロスプロジェクト設定Cross project configuration」や「サブプロジェクトの設定Subproject configuration」で紹介した設定のインジェクションを使って、複数のプロジェクトにプロパティとメソッドを注入できます。設定のインジェクションは、一般的には継承よりいい選択肢になるはずです。たくさん理由はありますが、たとえばインジェクションはスクリプト内で明示的に使用していることが分かりますし、いくつかのプロジェクトだけに別のロジックを注入することもできます。また、リポジトリ設定やGradleプラグイン、タスクなどどんな設定でも注入できます。インジェクションの例は以下のサンプルをご覧ください。
例60.2 プロパティとメソッドのインジェクション
build.gradle
subprojects { // Define a new property ext.srcDirName = 'src/java' // Define a method using a closure as the method body ext.srcDir = { file(srcDirName) } // Define a task task show << { println 'project: ' + project.path println 'srcDirName: ' + srcDirName File srcDir = srcDir() println 'srcDir: ' + rootProject.relativePath(srcDir) } } // Inject special case configuration into a particular project project(':child2') { ext.srcDirName = "$srcDirName/legacy" }
child1/build.gradle
// Use injected property and method. Here, we override the injected value srcDirName = 'java' def dir = srcDir()
gradle -q show
の出力
> gradle -q show project: :child1 srcDirName: java srcDir: child1/java project: :child2 srcDirName: src/java/legacy srcDir: child2/src/java/legacy
When you run Gradle, it checks for the existence of a directory called buildSrc
.
Gradle then automatically compiles and tests this code and puts it in the classpath of your build script.
You don't need to provide any further instruction. This can be a good place to add your custom tasks and plugins.
Gradleは実行されたときにbuildSrc
というディレクトリがないか確認します。
そして、中のコードを自動的にコンパイル、テストして、ビルドスクリプトのクラスパスに追加してくれるのです。
何かを設定する必要は一切ありません。このディレクトリは、カスタムタスクやプラグインを追加するのに適しています。
For multi-project builds there can be only one buildSrc
directory, which has to be
in the root project directory.
マルチプロジェクトのビルドでは、ルートプロジェクトに一つだけbuildSrc
ディレクトリを作成できます。
Listed below is the default build script that Gradle applies to the buildSrc
project:
buildSrc
プロジェクトには、次のビルドスクリプトがデフォルトで適用されます。
図60.1 デフォルトのbuildSrcビルドスクリプトDefault buildSrc build script
apply plugin: 'groovy' dependencies { compile gradleApi() compile localGroovy() }
This means that you can just put your build source code in this directory and stick to the layout convention for a Java/Groovy project (see 表23.4「Javaプラグイン - デフォルトプロジェクトレイアウトJava plugin - default project layout」).
つまり、Java/Groovyプロジェクトのレイアウト規約(表23.4「Javaプラグイン - デフォルトプロジェクトレイアウトJava plugin - default project layout」参照)に従って、このディレクトリにソースコードを置くだけでいいということです。
If you need more flexibility, you can provide your own build.gradle
. Gradle applies the default build script
regardless of whether there is one specified. This means you only need to declare the extra things you need. Below is an example.
Notice that this example does not need to declare a dependency on the Gradle API, as this is done by the default build script:
さらに柔軟性が必要なときは、自分でbuild.gradle
を用意することもできます。
上記のデフォルトビルドスクリプトは、この場合でも適用されます。つまり、必要なものだけを追加で定義できるのです。次の例を見てください。Gradle APIへの依存関係は、デフォルトのビルドスクリプトで宣言されているため、ここでは宣言する必要がありません。
例60.3 カスタムbuildSrcビルドスクリプト
buildSrc/build.gradle
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.11'
}
The buildSrc
project can be a multi-project build, just like any other regular multi-project build. However,
all of the projects that should be on the classpath of the actual build must be runtime
dependencies of the root project in
buildSrc
. You can do this by adding this to the configuration of each project you wish to export:
buildSrc
プロジェクトは、マルチプロジェクトにすることができます。このプロジェクトは、普通のGradleマルチプロジェクトと同じように動作します。
ただし、実際のビルドのクラスパスに置きたい全てのプロジェクトを、buildSrcルートプロジェクトのruntime
依存関係にしなければなりません。これは、エクスポートしたい全てのプロジェクトの設定に、以下のコードを追加することで実現できます。
例60.4 buildSrcルートプロジェクトにサブプロジェクトを追加する
buildSrc/build.gradle
rootProject.dependencies { runtime project(path) }
ノート: 本例のソースコードは、Gradleのバイナリ配布物またはソース配布物に含まれています。以下の場所をご参照ください。samples/multiProjectBuildSrc
You can use the GradleBuild
task. You can use either of the
dir
or buildFile
properties to specify which build to execute,
and the tasks
property to specify which tasks to execute.
GradleBuild
で別のビルドを呼び出して実行することができます。そのとき、呼び出すビルドのディレクトリやビルドスクリプトに渡すプロパティを指定したり、実行するタスクのタスクプロパティを設定できます。
例60.5 別のビルドを呼び出す
build.gradle
task build(type: GradleBuild) { buildFile = 'other.gradle' tasks = ['hello'] }
other.gradle
task hello << {
println "hello from the other build."
}
gradle -q build
の出力
> gradle -q build hello from the other build.
If your build script needs to use external libraries, you can add them to the script's classpath in the
build script itself. You do this using the buildscript()
method, passing in a closure which
declares the build script classpath.
ビルドスクリプトを実行するときに外部のライブラリを使いたいなら、そのライブラリをビルドスクリプトのクラスパスに追加しなければなりません。buildscript()
メソッドにクロージャを渡し、その中でビルドスクリプトのクラスパスを設定できます。
例60.6 ビルドスクリプトのクラスパスを宣言する
build.gradle
buildscript { repositories { mavenCentral() } dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' } }
The closure passed to the buildscript()
method configures a
ScriptHandler
instance. You declare the build script
classpath by adding dependencies to the classpath
configuration. This is the same way
you declare, for example, the Java compilation classpath. You can use any of the dependency types described
in 「依存関係の定義方法 How to declare your dependencies」, except project dependencies.
buildscript()
メソッドに渡すクロージャは、ScriptHandler
のインスタンスにパラメータなどを設定するものです。ビルドスクリプトのクラスパスを追加するには、classpath
で依存関係を宣言しましょう。形式はJavaでコンパイル時のクラスパスを宣言するときなどと同じです。「依存関係の定義方法 How to declare your dependencies」に記載されているタイプの依存関係は、「プロジェクトへの依存」を除けばすべて使えます。
Having declared the build script classpath, you can use the classes in your build script as you would any other classes on the classpath. The following example adds to the previous example, and uses classes from the build script classpath.
外部ライブラリのクラスをビルドスクリプトのクラスパスを追加すれば、他のクラスと同じようにスクリプト内で使えるようになります。次の例では、先ほどの例に加え、クラスパスに追加したクラスを実際に使っています。
例60.7 外部ライブラリをビルドスクリプトで使用する
build.gradle
import org.apache.commons.codec.binary.Base64 buildscript { repositories { mavenCentral() } dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' } } task encode << { def byte[] encodedString = new Base64().encode('hello world\n'.getBytes()) println new String(encodedString) }
gradle -q encode
の出力
> gradle -q encode aGVsbG8gd29ybGQK
For multi-project builds, the dependencies declared in the a project's build script, are available to the build scripts of all sub-projects.
マルチプロジェクトの場合、プロジェクトのビルドスクリプトでクラスパスを宣言すれば、そのサブプロジェクトすべてで追加したライブラリを使用できるようになります。
For reasons we don't fully understand yet, external dependencies are not picked up by Ant's optional tasks. But you can easily do it in another way. [42]
何でかよく分からないのですが、Antのオプショナルタスクでは先ほどの方法で追加した外部ライブラリを拾ってくれないようです。しかし、次のようにすればこの問題は簡単に解決できます。
例60.8 Ant optional dependencies
build.gradle
configurations { ftpAntTask } dependencies { ftpAntTask("org.apache.ant:ant-commons-net:1.9.3") { module("commons-net:commons-net:1.4.1") { dependencies "oro:oro:2.0.8:jar" } } } task ftp << { ant { taskdef(name: 'ftp', classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP', classpath: configurations.ftpAntTask.asPath) ftp(server: "ftp.apache.org", userid: "anonymous", password: "me@myorg.com") { fileset(dir: "htdocs/manual") } } }
This is also a good example for the usage of client modules. The POM file in Maven Central for the ant-commons-net task does not provide the right information for this use case.
これはクライアントモジュールのいい使用例でもあります。mavenのセントラルリポジトリにあるant-commons-netタスクのpom.xml
は、このユースケースのための正しい情報を提供していません。
Gradle offers you a variety of ways of organizing your build logic. You can choose what is right for your domain and find the right balance between unnecessary indirections, and avoiding redundancy and a hard to maintain code base. It is our experience that even very complex custom build logic is rarely shared between different builds. Other build tools enforce a separation of this build logic into a separate project. Gradle spares you this unnecessary overhead and indirection.
Gradleは、ビルドロジックを体系化するための様々な方法を提供しています。自分の分野に合った方法を選択して、不必要に複雑にならないように、一方でコードベースの重複やメンテナンス性の低下を避けられるように、適切なバランスを探っていくことができます。これまでの経験では、非常に複雑なビルドロジックでさえ、異なるビルド間で共有することはあまりありませんでした。いくつかのビルドツールでは、このビルドロジックを、ひとつの別のプロジェクトにして分離します。Gradleでは、そのような無駄なオーバーヘッドや複雑さを省くことができます。
[40] Which might range from a single class to something very complex.
[41] 一つの単純なクラスから、複雑なクラスまで
[42] In fact, we think this is a better solution. Only if your buildscript and Ant's optional
task need the same library would you have to define it twice. In such a
case it would be nice if Ant's optional task would automatically pick up the classpath defined
in the “gradle.settings
” file.