本稿の対象者
本稿は Gradle の入門記事です。
- そもそも Gradle って?
- apply や compile って実はメソッドなんです。
- {} は単なるスコープではなくクロージャなんです。
- x = '10' は書けないけど、def y = '10' は書ける。
- でも version = '1.0' などは書ける。
- これらは Groovy 言語の構文が基本となっている。
- そもそも Groovy って?
- 自身でタスクやプラグインを作ることもできる。
apply plugin: 'java'
apply plugin: 'application'
repositories {
jcenter()
}
dependencies {
compile 'com.google.guava:guava:22.0'
testCompile 'junit:junit:4.12'
}
mainClassName = 'App'
概要
Gradle (ぐれいどる) は作成したプログラムをビルド(コンパイル/テスト/実行/パッケージング/配布)するために利用します。有名なビルドツールに Maven がありますが、Gradle は Maven のように XML で設定定義するのではなく Java 言語によく似た構文である Groovy 言語を利用して、プログラマブルにビルド定義ができプログラマと親和性が高いです。
Android 開発では標準のビルドツールですし、私も新たに作るプログラムは全て Gradle で作るようにしています。
Gradle 自身も Java と Groovy で作成され Gradle によりビルドされています。OSS として公開( https://github.com/gradle/gradle )されています。
JVM(Java 仮想マシン)上で動作するため、基本的にどのような環境でも動作させることができます。
セットアップ
前提
- (2018/02/04 時点) Java SE 7 以上が導入されていること。
※ 前述のとおり、Gradle 自身が Java で作成されているため。
Gradle インストール
- https://gradle.org/ へアクセスする
- 「Install Manually」を押下する
- 「Install Gradle」を押下する
- 「Download」リンクを押下する
- 「binary-only」リンクを押下する
- Gradle を配置し、PATH を通す
# ダウンロードした zip ファイルを解凍し、任意の場所へ配置する。(私は /usr/local/ に配置してシンボリックを作成しています。)
$ cd ~/Downloads
$ unzip gradle-4.5-bin.zip
$ mv gradle-4.5 /usr/local/
$ ln -snf /usr/local/gradle-4.5 /usr/local/gradle
# ${GRADLE_HOME}/bin を PATH を通す
$ vi ~/.bash_profile
...
GRADLE_HOME=/usr/local/gradle
export PATH=${GRADLE_HOME}/bin:$PATH
...
# 設定した PATH を有効化
$ . ~/.bash_profile
$ echo ${PATH}
Gradle 動作確認
gradle -v コマンドを実行し、下記のようにバージョン情報が出力されればOKです。
$ gradle -v
------------------------------------------------------------
Gradle 4.5
------------------------------------------------------------
Build time: 2018-01-24 17:04:52 UTC
Revision: 77d0ec90636f43669dc794ca17ef80dd65457bec
Groovy: 2.4.12
Ant: Apache Ant(TM) version 1.9.9 compiled on February 2 2017
JVM: 9 (Oracle Corporation 9+181)
OS: Mac OS X 10.11.6 x86_64
Gradle Wrapper
Gradle のインストール手順を紹介しましたが、プロジェクトに参加する開発者全員がこの手順を行うなると結構な手間になります。
そこで、最初の一人が Gradle のセットアップを行い、Gradle Wrapper を準備しておきます。残りのメンバーはこの Gradle Wrapper を使うことで、Gradle が自動的にダウンロードされ、Gradle のコマンドを実行できるようになります。
先程のコマンドを Gradle Wrapper で行うと下記となります。
# Windows の場合は gradlew -v でOK
$ ./gradlew -v
初回実行時は、Gradle をダウンロードするため時間がかかりますが、2回目以降は素早く実行可能です。
※以降の手順は基本的に Gradle Wrapper での手順を記載します。
Gradle Wrapper は下記のコマンドで生成することができます。
$ gradle wrapper
$ ls
gradle gradlew gradlew.bat
この gradlew や gradlew.bat というファイルが Gradle Wrapper です。
本稿確認用リポジトリ
事前に Gradle Wrapper を設定してあるリポジトリを用意していますので
こちらを clone してください。
$ mkdir ~/java
$ cd ~/java
$ git clone https://github.com/hatimiti/GradleInit samplepj
$ cd samplepj
Hello Gradle
「gradle init」コマンドを実行すると、引数で指定したタイプのプロジェクトの雛形を作成することができます。
# gradle init --type <TYPE>
# <TYPE>
# * 'basic'
# * 'groovy-application'
# * 'groovy-library'
# * 'java-application'
# * 'java-library'
# * 'pom'
# * 'scala-library'
$ ./gradlew init --type java-application
$ tree
~/java/samplepj
|--.gradle
|--build.gradle
|--gradle
| |--wrapper
| | |--gradle-wrapper.jar
| | |--gradle-wrapper.properties
|--gradlew
|--gradlew.bat
|--settings.gradle
|--src
| |--main
| | |--java
| | | |--App.java
| |--test
| | |--java
| | | |--AppTest.java
「build.gradle」ファイルが Gradle によるビルド定義の中心となるファイルです。
このファイルに依存関係やタスクを定義します。
Maven でいうところの pom.xml にあたります。
plugins {
id 'java'
id 'application'
}
mainClassName = 'App'
dependencies {
compile 'com.google.guava:guava:23.0'
testCompile 'junit:junit:4.12'
}
repositories {
jcenter()
}
Gradle の実行は「gradle <タスク名1> <タスク名2> ...」のように、実行したいタスクを連ねます。
下記の例では clean タスク、build タスク、run タスクを実行します。
$ ./gradlew clean build run
> Task :run
Hello world.
Java と Groovy の比較
build.gradle ファイルは Groovy で記述します。Groovy の省略記法やクロージャについて知ることで、build.gradle の記法が理解しやすくなります。
Groovy は Java の構文を基本的に踏襲しています。
ここでは Java プログラマ目線で、Groovy と Java で異なる部分を一部紹介したいと思います。
Groovy は groovyc コマンドでコンパイルし、.class ファイルへ変換も可能ですが、スクリプト言語としてコンパイルせずに動的実行が可能です.また、動的型付言語のため、def キーワードにより型宣言の省略や、ダックタイピングも可能です。
Groovy 文法
下記のコードを build.gradle に記述してください。
実際の Groovy ファイルの拡張子は .groovy ですが、ここでは Gradle のビルドスクリプトとして実行してみます。
これにより、build.gradle が Groovy で記述できることが実感できると思います。
コード下部に Point クラスを定義しているので、そちらを先に確認すると良いと思います。
import groovy.transform.ToString
import groovy.transform.EqualsAndHashCode
/*: デフォルトで import 済のパッケージ
import java.io.*
import java.lang.*
import java.math.BigDecimal
import java.math.BigInteger
import java.net.*
import java.util.*
import groovy.lang.*
import groovy.util.*
*/
// 末尾のセミコロン省略可能
Point a = new Point(10, 20)
Point b = new Point(3, 4)
// 特定の名前のメソッドは演算子記号で呼び出し可能
assert a + b == new Point(13, 24) // a.plus(b);
assert a - b == new Point(7, 16) // a.minus(b);
assert a * b == new Point(30, 80) // a.multiply(b);
assert a / b == new Point(3, 5) ///a.divide(b);
assert a++ == new Point(11, 21) // a.next();
assert b-- == new Point(2, 3) // a.previous();
assert a << b == new Point(13, 24) // a.leftShift(b);
// コンストラクタ呼び出しの色々
def c = [1, 2] as Point
Point d = [3, 4]
Point e = new Point(x: 5, y: 6)
assert c == new Point(1, 2)
assert d == new Point(3, 4)
assert e == new Point(5, 6)
// メソッド呼び出し時の括弧省略可能
def f = a.plus b
def g = a.next()
//def g = a.next // 引数が無い場合は省略不可
// メソッドパラメータを Map にすることで、名前付き指定のように呼び出し可能
assert Point.of(x: 10, y: 20) == new Point(10, 20)
// 宣言時にアクセス修飾子やfinalをつけていないパラメータ以外は getter/setter は自動で生成される
a.setX(15)
a.setY(30)
assert a == new Point(15, 30)
// シングルクォートは Charcter or Stirng 型に変換される
char s1 = 'x'; assert s1 instanceof Character
def s2 = 'x'; assert s2 instanceof String
def s3 = 'xy'; assert s3 instanceof String
// ダブルクォートは String or GString 型
def d1 = "x"; assert d1 instanceof String
def d2 = "xy"; assert d2 instanceof String
def d3 = "${a}"; assert d3 instanceof GString
// GString は ${変数} で文字列に値を埋め込み可能。 == は equals で扱われる。
assert "a = ${a}" == "a = Point(15, 30)"
// """ でマルチライン文字列を生成可能
def d4 = """
一行目
二行目
""";
final def LF = System.lineSeparator()
assert d4 == "${LF}一行目${LF}二行目${LF}"
// マルチライン文字列は \ と | と stripMargin() を使うとインデントがキレイにできる。
def d5 = """\
|一行目
|二行目
|""".stripMargin()
assert d5 == "一行目${LF}二行目${LF}"
// Closure(クロージャ)
def cl1 = {
print "${x}, ${y}"
}
a.execute(cl1)
// 括弧は省略可能
// b.execute({
// print "${x}, ${y}"
// })
b.execute {
print "${x}, ${y}"
}
// 最後の引数が Closure の場合 メソッド名() { ... } の形式で呼び出し可能。
// Closureへの引数が1つの場合は it で呼び出し可能。
// 省略しない場合は メソッド名() { p -> ... } の形式となる。
a.execute(b) {
int _x = x + it.x
int _y = y + it.y
print "${_x}, ${_y}"
}
// 5678910
(5..10).each {
print it
}
println "";
// 0123456789
(10).times {
print it
}
println "";
// List や Map
def lm1 = [1, 2, 3]
assert lm1 instanceof ArrayList
assert lm1.sum() == 6
def lm2 = [x: 10, y: 11, z: 12]
assert lm2 instanceof HashMap
assert lm2['y'] == 11
// 展開ドット演算子
assert lm2*.value == [10, 11, 12]
assert lm2.collect { it.value } == [10, 11, 12]
/**
* The 2D point at x and y.
*/
@ToString
@EqualsAndHashCode
class Point {
int x;
int y;
Point(int x = 0, int y = 0) {
this.x = x
this.y = y
}
Point plus(Point p) {
if (p == null) return this
new Point(x + p.x, y + p.y) // return 省略可: ブロックの最後の式が return される
}
Point minus(Point p) {
if (p == null) return this
new Point(x - p.x, y - p.y)
}
Point multiply(Point p) {
if (p == null) return this
new Point(x * p.x, y * p.y)
}
Point div(Point p) throws Exception {
if (p == null) return this
try {
new Point(x / p.x as int, y / p.y as int)
} catch (Exception e) {
// 検査例外を throw しても呼び出し側の catch 不要
throw new Exception(e)
}
}
Point next() {
x++
y++
this
}
Point previous() {
x--
y--
this
}
Point leftShift(Point p) {
plus(p)
}
void execute(Closure c) {
println "---"
c.delegate = this
c()
println ""
println "---"
}
void execute(Point p, Closure c) {
println "---"
c.delegate = this
c(p)
println ""
println "---"
}
static Point of(Map m) {
new Point(m['x'], m['y'])
}
}
task hello {}
下記コマンドで実行可能です。
$ ./gradlew hello -q
---
15, 30
---
---
2, 3
---
---
17, 33
---
5678910
0123456789
ここで、最初の build.gradle を Java っぽく書いてみた場合の記述を紹介します。
※ Closure の部分は Groovy 構文を利用しています。
// apply plugin: 'java'
Map<String, String> pluginJava = new HashMap<>();
pluginJava.put("plugin", "java");
this.project.apply(pluginJava);
// apply plugin: 'application'
Map<String, String> pluginApplication = new HashMap<>();
pluginApplication.put("plugin", "application");
this.project.apply(pluginApplication);
// repositories {
// jcenter()
// }
this.project.repositories({ ->
jcenter();
});
// dependencies {
// compile 'com.google.guava:guava:22.0'
// testCompile 'junit:junit:4.12'
// }
this.project.dependencies({ ->
compile("com.google.guava:guava:22.0");
testCompile("junit:junit:4.12");
});
// mainClassName = 'App'
this.project.mainClassName = "App";
Closure - this/owner/delegate
Closure 内部の暗黙的な参照先として以下の3つが存在します。
- this …… 該当スコープ定義元の参照です。 Java の this と同様です。
- owner …… 該当 Closure 定義元の参照です。基本的に this と同様ですが、Closure の中に Closure を定義した場合に 外部 Closure が参照先となります。
- delegate …… 該当 Closure 内の処理の委譲先です。動的に変更することが可能です。
class Deleg1 {
String hello_A() {
return "Deleg1 Say hello_A() !!"
}
String hello_B() {
return "Deleg1 Say hello_B() !!"
}
}
class Deleg2 {
String hello_A() {
return "Deleg2 Say hello_A() !!"
}
String hello_B() {
return "Deleg2 Say hello_B() !!"
}
}
class Main {
void execute() {
Closure c = {
println "this: ${this.class}"
println "owner: ${owner.class}"
println "delegate: ${delegate.class}"
println hello_A()
println hello_B()
Closure cc = {
println "this: ${this.class}"
println "owner: ${owner.class}"
println "delegate: ${delegate.class}"
println hello_A()
println hello_B()
}
println "\n-- execute cc --"
cc()
println "\n-- execute cc with Deleg2 --"
cc.delegate = new Deleg2()
cc()
println "\n-- execute cc by Closure.DELEGATE_FIRST --"
cc.resolveStrategy = Closure.DELEGATE_FIRST
cc()
}
println "Start Closure"
println "\n-- execute c with Deleg1 --"
// c() <- ここで c() を呼び出すと hello_B() メソッド参照先が見つからずにエラー
c.delegate = new Deleg1()
c()
println "\nEnd Closure"
}
String hello_A() {
return "Main Say hello_A() !!"
}
}
def m = new Main()
m.execute()
$ ./gradlew -q
Start Closure
-- execute c with Deleg1 --
this: class Main
owner: class Main
delegate: class Deleg1
Main Say hello_A() !!
Deleg1 Say hello_B() !!
-- execute cc --
this: class Main
owner: class Main$_execute_closure1
delegate: class Main$_execute_closure1
Main Say hello_A() !!
Deleg1 Say hello_B() !!
-- execute cc with Deleg2 --
this: class Main
owner: class Main$_execute_closure1
delegate: class Deleg2
Main Say hello_A() !!
Deleg1 Say hello_B() !!
-- execute cc by Closure.DELEGATE_FIRST --
this: class Main
owner: class Main$_execute_closure1
delegate: class Deleg2
Deleg2 Say hello_A() !!
Deleg2 Say hello_B() !!
End Closure
Gradle を扱う上で最も重要なのは delegate です。この委譲先を知ることで Closure 内で呼ばれるメソッドが、どこで定義されているか分かるようになります。
コード中にあるように、これら参照先に対する探索優先度は resolveStrategy プロパティで指定することができます。
優先度については、下記記事が参考になります。
- はじめようApache Groovy #15 - クロージャ
-
https://github.com/groovy/groovy-core/blob/master/src/main/groovy/lang/Closure.java
- OWNER_FIRST
- DELEGATE_FIRST
- OWNER_ONLY
- DELEGATE_ONLY
- TO_SELF
Gradle 内で利用されているクラス(抜粋)
- Project (doc)
前述の Java ふうに記述した例では this.project というインスタンスを利用して各種プロパティへアクセスしていました。
この project インスタンスが、build.gradle ファイル中の定義とやり取りの基本となるオブジェクトです。
このインターフェースは、ビルドファイル内でGradleとやりとりする際、メインで使用するAPIです。
Projectから、Gradleの全ての機能にプログラム的にアクセスできます。
...
Projectと"build.gradle"ファイルは、1対1の関係性を持ちます。
- Task (doc)
Taskはクラスのコンパイルやjavadocの生成など、ビルドに対する単一のアトミックな断片を表します。
Gradle を構成するプロパティ
プロパティは変数のようなものです。
外部(ビルドスクリプト外)から指定するもの、ビルドスクリプト内で定義するもの、があります。
外部から与えられた値で動的にビルド動作を変更したい場合や、値の共通化などに利用します。
- システムプロパティ …… 外部から指定するプロパティです。
- 拡張プロパティ …… ビルドスクリプト内で定義するプロパティです。
- プロジェクトプロパティ …… 外部から指定するプロパティで、内部的には拡張プロパティと同様に扱われます。
システムプロパティ
task hello {
doLast {
println System.properties['hello.test']
}
}
実行時に指定する
$ ./gradlew hello -Dhello.test="Hello Gradle 1"
> Task :hello
Hello Gradle 1
$ ./gradlew hello --system-prop hello.test="Hello Gradle 2"
> Task :hello
Hello Gradle 2
gradle.properties に指定する
gradle.properties に定義して利用する場合は systemProp.<システムプロパティ名>=<値>と定義します。
gradle.properties が無い場合は、プロジェクトのルートディレクトリに作成してください。
systemProp.hello.test=Hello Gradle 3
$ ./gradlew hello
> Task :hello
Hello Gradle 3
同時に指定した場合の優先度
- gradle.properties より実行時の引数が優先される。
- 引数は後方に指定したものが優先される。
$ ./gradlew hello -Dhello.test="Hello Gradle 1" --system-prop hello.test="Hello Gradle 2"
> Task :hello
Hello Gradle 2
$ ./gradlew hello --system-prop hello.test="Hello Gradle 2" -Dhello.test="Hello Gradle 1"
> Task :hello
Hello Gradle 1
拡張プロパティ
ext プロパティのクロージャ内で定義します。
ext {
prop1 = "Hello Ext Prop 1"
prop2 = "Hello Ext Prop 2"
}
task hello {
doLast {
println prop1
println prop2
}
}
$ ./gradlew hello
> Task :hello
Hello Ext Prop 1
Hello Ext Prop 2
project インスタンス経由で直接上書きする
project.ext.(プロパティ名) で拡張プロパティを直接指定可能。
ext {
prop1 = "Hello Ext Prop 1"
}
task hello {
doLast {
println prop1
project.ext.prop1 = "Override prop1"
println prop1
}
}
$ ./gradlew hello
> Task :hello
Hello Ext Prop 1
Override prop1
プロジェクトプロパティ
内部的には拡張プロパティと同様に扱われる。
プロパティ名(下記の例では sample)を指定すればスクリプト内で参照可能。
task hello {
doLast {
println sample
}
}
gradle.properties に指定する
sample=Project Property 1
$ ./gradlew hello
> Task :hello
Project Property 1
実行時に指定する
$ ./gradlew hello -Psample="Project Property 2"
> Task :hello
Project Property 2
$ ./gradlew hello --project-prop sample="Project Property 3"
> Task :hello
Project Property 3
Java プロジェクト
ここでは Java を利用したプロジェクトを Gradle を使ってビルドする場合によく利用する事項を紹介します。
今までの項で build.gradle を編集した場合は下記の状態に戻してください。
apply plugin: 'java'
apply plugin: 'application'
repositories {
jcenter()
}
dependencies {
compile 'com.google.guava:guava:22.0'
testCompile 'junit:junit:4.12'
}
mainClassName = 'App'
よく利用するプラグイン
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'maven'
主なタスク
# | タスク名 | 依存タスク | 概要 |
---|---|---|---|
1 | clean | - | 成果物(build)を削除します。 |
2 | build | - | プロジェクトをビルドします。 |
3 | __ assemble | build | 成果物を生成します。 |
4 | ____ jar | assemble | jar ファイルを作成します。 |
5 | ______ classes | jar | main ソースセット |
6 | ________ compileJava | classes | main/java をコンパイルします。 |
7 | ________ processResources | classes | main/resources をプロダクト成果物としてコピーします。 |
8 | __ check | build | コードチェック系タスクを実行します。 |
9 | ____ test | check | Junit などのコードテストを実行します。 |
10 | ______ testClasses | test | test ソースセット |
11 | ________ compileTestJava | testClasses | test/java をコンパイルします。 |
12 | ________ processTestResources | testClasses | test/resources をテスト成果物としてコピーします。 |
compile や testCompile
コンパイル時に必要な依存ライブラリを設定します。
compile に指定した場合は、プロダクトコードのコンパイルや実行時の依存に含まれます。war ファイルなどを作成する場合は、war ファイル内にも含まれます。
testCompile はテストコードのコンパイルやテスト実行時の依存にのみ含まれ、war ファイルなどには含まれません。
maven Central
repositories {
mavenCentral();
}
-
https://mvnrepository.com/ へアクセスする
-
「Gradle」タブを参照することで、Gradle の依存関係への指定方法がわかる。「License」項には OSS ライセンス種別も記載されている。
-
build.gradle に依存関係を記述する。
dependencies {
...
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.0'
...
}
jar の作り方
$ ./gradlew jar
$ ls build/libs/
samplepj.jar
main メソッドを持つ クラスを実行したい
main メソッドを実行する場合は application プラグインが必要となります。
application プラグインで追加される mainClassName プロパティにクラス名を指定し、run タスクを実行することでアプリケーションを実行することができます。
...
mainClassName = 'sample.Main'
...
$ ./gradlew run
コードチェックをしたい
findbugs (doc)
静的コード解析用プラグインです。
apply plugin: 'findbugs'
$ ./gradlew findBugsMain -q
$ ls build/reports/findbugs/
main.xml
checkstyle (doc)
コードスタイルのチェック用です。
apply plugin: 'checkstyle'
$ mkdir -p config/checkstyle
<!-- sun_checks.xml の内容を貼り付ける -->
$ ./gradlew checkstyleMain -q
$ ls build/reports/checkstyle/
main.html main.xml
pmd (doc)
静的コードチェックのプラグインです。
apply plugin: 'pmd'
$ ./gradlew pmdMain -q
$ ls build/reports/pmd/
main.html main.xml
findbugs/checkstyle/pmd を一度に実行したい
それぞれのタスクが check タスクに依存しているため、check タスクを実行すれば良い。
$ ./gradlew check -q
jacoco (doc)
コードカバレッジ測定に利用します。
apply plugin: "jacoco"
$ ./gradlew test jacocoTestReport -q
$ ls build/reports/jacoco/test/html/
hello index.html jacoco-resources jacoco-sessions.html
ビルドエラーの調査
--debug や --stacktrace を利用しましょう。
$ ./gradlew clean build --debug
$ ./gradlew clean build --stacktrace
タスクの作り方
ここまで見てきたように、Gradle ではデフォルトで用意されているタスクや、プラグインによって追加されるタスクがあります。
それだけでなく、自身でタスクを追加することが可能です。
Hello Task
タスクを定義するには Project クラスの task メソッドを利用します。
task hello {
...ここにタスク定義
}
タスク定義の際に注意しなくてはいけないのは、タスクのクロージャスコープは初期化時に実行されます。
タスク実行時に実行するべき処理は doLast メソッドで指定します。
task hello {
doLast {
println "Hello Task!!"
}
}
以前のバージョンでは leftShift ( << ) を利用した定義が推奨されていましたが、現在のバージョンでは << は非推奨となり、次バージョン以降で定義できなくなる可能性があるため doLast で定義するようにしましょう。
task hello << {
println "Hello Task!!"
}
doLast を利用した例ではインデントが1つ深く少し見づらいため、初期化が不要な場合は下記のように記述可能です。
task hello doLast {
println "Hello Task!!"
}
buildscript プロパティ
プロダクトコードではなく、ビルドスクリプト( build.gradle )内で利用するプラグインや依存関係の定義については buildscript に定義します。
OSSなどを利用する場合は、プロダクトコード同様に repositories と dependencies に定義します。
下記は maven セントラルリポジトリから、JGit(GitクライアントのJava実装)を利用する場合の例です。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '4.8.0.201706111038-r'
}
}
task 作成ハンズオン
※解答は最下部にあります。
ハンズオン1
JGit を利用して、特定の Git リポジトリを clone する cloneGitRepo タスクを作成してください。
import が必要なクラスは下記2つです。
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.internal.storage.file.FileRepository
clone 先のディレクトリ名と、clone するリポジトリ名、リポジトリURIは、前述した拡張プロパティに定義してください。
cloneDirName = 'jgit'
repositoryName = 'trylibs'
repositoryURI = "https://github.com/hatimiti/${repositoryName}.git"
JGit で clone する方法は下記です。
def gitClient = Git.cloneRepository()
.setURI(repositoryURI)
.setDirectory(new File("./${cloneDirName}/${repositoryName}"))
gitClient.call()
$ ./gradlew cloneGitRepo
ハンズオン2
ハンズオン1のタスクでクローンした clone 先ディレクトリを削除する cleanGitRepo タスクを作成してください。
ディレクトリ削除系のタスクは Delete クラスを利用することで簡単に実装することが可能です。
$ ./gradlew cleanGitRepo
プラグインの作り方
Gradle のプラグインは自身で作成することも可能です。プロジェクトを跨いで共通化したいタスクなどをプラグイン化しておくと、毎回 build.gradle に複製する必要がなくなるため便利になります。
Hello Plugin
プラグイン実装
通常のプロジェクトのように、ディレクトリを作成し、gradle init で初期化します。
$ mkdir HelloGradlePlugin
$ cd HelloGradlePlugin
$ gradle init
プラグインのソースコードを配置するディレクトリと、プラグイン公開定義用ディレクトリを作成します。
$ mkdir -p src/main/groovy/sample
$ mkdir -p src/main/resources/META-INF/gradle-plugins
今回は Groovy 言語を利用してプラグインを作成するため、groovy プラグインを導入します。Groovy に限らず、Java や Scala などの JVM 言語であればプラグインを作成することが可能です。Gradle 標準のプラグインの多くは Java で作られているようです。
また、Gradle プラグインを作成するためのクラス群や、Groovy 言語への依存関係を dependencies へ指定します。
プラグインは最終的に jar ファイルへ変換するため、jar ファイルの名前も定義しておきます。
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
jar.baseName = 'hello-gradle-plugin'
プラグイン本体ソースを作成します。
プラグインを作成する際のポイントは下記です。
- org.gradle.api.Plugin インタフェースを実装する
- Plugin#apply() メソッドをオーバライド実装する
- apply() メソッドの引数である org.gradle.api.Project インスタンスに対して各種設定を追加する
package sample
import org.gradle.api.Plugin
import org.gradle.api.Project
class HelloPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('hello') doLast {
println "Hello Gradle Plugin!!"
}
}
}
プラグイン公開用定義を専用の properties ファイルに記述します。
implementation-class に先程作成したクラスを指定します。
implementation-class=sample.HelloPlugin
jar ファイルを作成します。
jar タスクが成功すると、build/libs 配下に jar ファイルが作成されます。
$ ./gradlew jar
$ ls build/libs/hello-gradle-plugin.jar
build/libs/hello-gradle-plugin.jar
~/java/HelloGradlePlugin
|--build
| |--classes
| |--libs
| | |--hello-gradle-plugin.jar
|--build.gradle
|--src
| |--main
| | |--groovy
| | | |--sample
| | | | |--HelloPlugin.groovy
| | |--resources
| | | |--META-INF
| | | | |--gradle-plugins
| | | | | |--com.github.hatimiti.hello.properties
作成したプラグインを利用してみる
作成した プラグインの jar ファイルを、利用したい側のプロジェクト配下へコピーする。
$ cd ..
$ cp HelloGradlePlugin/build/libs/hello-gradle-plugin.jar samplepj/
$ cd samplepj
配置した jar ファイルを buildscript の dependencies に指定します。
そして、先程 META-INF 配下に配置した properties ファイルの '.properties' を除いたファイル名を apply メソッドに指定します。
buildscript {
dependencies {
classpath files('./hello-gradle-plugin.jar')
}
}
apply plugin: 'com.github.hatimiti.hello'
$ ./gradlew hello
> Task :hello
Hello Gradle Plugin!!
プラグイン(Project)にプロパティやメソッドを追加する
先程作成したプラグインディレクトリに移動します。
$ pwd
~/java/HelloGradlePlugin
プロパティを追加する場合はExtensionやConfigurationクラスを追加します。
package sample
class HelloPluginExtension {
String value
void extension() {
println("Hello Extension!!")
}
}
package sample
import org.gradle.api.Plugin
import org.gradle.api.Project
class HelloPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
def extension = new HelloPluginExtension()
project.extensions.hello = extension
project.task('hello') doLast {
println "Hello Gradle Plugin and ${extension.value}!!"
}
}
}
$ ./gradlew jar
追加したプロパティを利用してみる
$ cd ..
$ cp HelloGradlePlugin/build/libs/hello-gradle-plugin.jar samplepj/
$ cd samplepj
buildscript {
dependencies {
classpath files('./hello-gradle-plugin.jar')
}
}
apply plugin: 'com.github.hatimiti.hello'
hello {
value = "Eeeeextension"
extension()
}
$ ./gradlew hello
> Configure project :
Hello Extension!!
> Task :hello
Hello Gradle Plugin and Eeeeextension!!
plugin 作成ハンズオン
「task 作成ハンズオン」で作成したタスクを持つ MyGitPlugin プラグインを作成してください。
clone 先ディレクトリや、clone するリポジトリ名は mygit { } スコープで定義できるようにしてください。
色々な Gradle プラグイン
- com.dorongold.task-tree…… task の依存関係をツリー表示する
$ gradle build taskTree
> Task :taskTree
------------------------------------------------------------
Root project
------------------------------------------------------------
:build
+--- :assemble
| +--- :distTar
| | +--- :jar
| | | \--- :classes
| | | +--- :compileJava
| | | \--- :processResources
| | \--- :startScripts
| +--- :distZip
| | +--- :jar
| | | \--- :classes
| | | +--- :compileJava
| | | \--- :processResources
| | \--- :startScripts
| \--- :jar
| \--- :classes
| +--- :compileJava
| \--- :processResources
\--- :check
\--- :test
+--- :classes
| +--- :compileJava
| \--- :processResources
\--- :testClasses
+--- :compileTestJava
| \--- :classes
| +--- :compileJava
| \--- :processResources
\--- :processTestResources
ドキュメント
ハンズオン解答
1. Git リポジトリから clone するタスクと、clean するタスク定義
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '4.8.0.201706111038-r'
}
}
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.internal.storage.file.FileRepository
ext {
cloneDirName = 'jgit'
repositoryName = 'trylibs'
repositoryURI = "https://github.com/hatimiti/${repositoryName}.git"
}
task cloneGitRepo {
def gitCloneClient = Git.cloneRepository()
.setURI(repositoryURI)
.setDirectory(new File("./${cloneDirName}/${repositoryName}"))
doLast {
gitCloneClient.call()
}
}
task cleanGitRepo(type: Delete) {
delete "./${cloneDirName}"
}
2. Git クローンプラグイン
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath files('./mygit-gradle-plugin.jar')
classpath group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '4.8.0.201706111038-r'
}
}
apply plugin: 'com.github.hatimiti.mygit'
mygit {
cloneDirName = "jgit"
repositoryName = "trylibs"
repositoryURI = "https://github.com/hatimiti/${repositoryName}.git"
}
参考書籍
Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築