gradle

Gradle初心者によるGradle事始め

More than 1 year has passed since last update.

はじめに

この記事はG*Advent Calendar(Groovy,Grails,Gradle,Spock...) Advent Calendar 2016 の22日目の記事です。

今回は、Gradleを使ってJavaのソースコードをコンパイルしてJARファイルにまとめてテストを実行するまでを書こうと思います。
(IDEに依存しないためにコンソールベースで記述しますのであしからず)

Gradleとは?

Gradleとは、Groovyで記述するビルド自動化システムです。
Javaで古くから使われていたAntやMavenはXMLで記述するのに対し、GradleはGroovyで記述できるため柔軟で強力なビルド自動化システムと言えます。

「規約によるビルド」

MavenやGradleは「規約によるビルド」システムと言われています。
一方、古くからあるmakeやAntは「手続き的なビルド」システムと言われています。

「手続き的なビルド」はソースファイルがあるディレクトリや生成ファイルの出力先のディレクトリを逐次指定する必要がありましたが、「規約によるビルド」はシステム側に一定のルール(規約)があり、ビルドシステムを利用する我々はそのルールに則ってソースファイルなどを配置します。
ルールに則っていればスクリプトが簡潔だというのが、「規約によるビルド」を採用しているGradleの強みです。Groovyが使えるという点と併せれば、もう最強と言えるでしょう。

Gradleのインストール

下記の手順でGradleのインストールと確認を行います。

必要な環境

現在(2016/12/22)のGradle最新版は3.2.1です。
GradleはJVM言語なのでJavaの環境が必要で、3.2.1はJava 7以上のJDKもしくはJREが必要です。
手元のPCは「java version "1.8.0_111"」なので、以下ではこの環境で試します。

ダウンロード

Gradleのダウンロードサイト から Complete distribution もしくは Binary only distributionをダウンロードします。
Complete distributionにはドキュメント類も含まれているので、今回は Complete distribution をダウンロードします。

解凍と環境変数設定

ダウンロードしたZIPファイルを適当なディレクトリに解凍して、解凍したディレクトリまでを GRADLE_HOME という環境変数に設定します。
併せてGRADLE_HOMEのbinディレクトリまでを PATH 環境変数に追加します。

インストール出来たかテストする

コンソールを立ち上げて gradle -version を実行し、Gradleがインストール出来たかテストします。

# gradle -version

------------------------------------------------------------
Gradle 3.2.1
------------------------------------------------------------

Build time:   2016-11-22 15:19:54 UTC
Revision:     83b485b914fd4f335ad0e66af9d14aad458d2cc5

Groovy:       2.4.7
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_111 (Oracle Corporation 25.111-b14)
OS:           Windows 7 6.1 x86

このようにバージョン情報が表示されればOKです。
Gradleを入れると、一緒にGroovyとAntが入るようですね。

プロキシ設定

仕事場でGradleを使う場合、プロキシサーバを設定しないと後々困るのでここで設定します。
今回はユーザごとにGradleの設定を施す ~/.gradle/gradle.properties ファイルにプロキシ設定を記述します。

gradle.properties
systemProp.http.proxyHost=xxx.xxx.xxx.xxx
systemProp.http.proxyPort=9999
systemProp.https.proxyHost=xxx.xxx.xxx.xxx
systemProp.https.proxyPort=9999

Gradleのキホン

設定が完了したので、本題に入ります。
以下では適当なところに GradleSample というディレクトリを作成し、その中で各種Gradleのコマンドを実行します。

Gradleの入り口

GradleはPATH環境変数に設定したディレクトリにある gradle コマンドからすべての操作を実行します。
(すでに gradle -version は実行しましたね)

手始めに引数なしの gradle コマンドを実行してみましょう。

# gradle
Starting a Gradle Daemon (subsequent builds will be faster)
:help

Welcome to Gradle 3.2.1.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 14.365 secs

引数なしのgradleコマンドは何もしないので、gradleコマンドの簡単なヘルプが出力されます。

ビルドをしたければ gradle <task> を実行し、
利用可能なタスクを表示したければ gradle tasks を実行し、
コマンドラインオプションを表示したければ gradle --help を実行し、
それぞれのタスクについて詳細が知りたければ gradle help --task <task> を実行しろと言っています。

<task>の部分には具体的なタスク名を記述します。
この「タスク」がGradleのキモとなる部分です。
次にこのタスクについて見てみます。

タスク

いまいるGradleSampleディレクトリに build.gradle というファイルを作って、テキストエディタで以下の内容を書き込みます。

build.gradle
task hello << {
    println 'Hello world!'
}

見て迷うことはないでしょうが、そのスクリプトにいま「hello」というタスクがあり、そのタスクは Hello world! を出力します。
これをgradleコマンドで実行します。
build.gradleはGradleがデフォルトで読み込むビルドスクリプトですので、gradleコマンドにはスクリプトファイル名を指定する必要はありません。
また今回はログ出力を抑制して内容をすっきりさせるために -q オプションを付けます。
実行するコマンドは gradle -q hello です。

# gradle -q hello
Hello world!

意図した結果が出力されました。

タスクには、ある処理のまとまりを記述出来ます。
このタスクを使って、「コンパイルをする」や「テストを実行する」などの処理を行ないます。

タスクの依存関係

Gradleに限らず、ビルドシステムには「タスクの依存関係」は必要不可欠な概念です。
例えばテストを実行するためにはコンパイルが完了していなければなりません。
これは「テストを実行する」というタスクは「コンパイルをする」というタスクに依存している状態です。
この依存関係を、Gradleでは以下のように記述します。

build.gradle
task runCompile << {
    println 'Run compile.'
}

task executeTest(dependsOn: runCompile) << {
    println 'Execute test.'
}

タスク名の後ろに (dependsOn: xxx) を記述し、xxx の部分に依存するタスク名を指定します。

このビルドスクリプトを実行します。
指定するタスクは「テストを実行する」ため gradle -q executeTest です。

# gradle -q executeTest
Run compile.
Execute test.

executeTestを実行する前に、依存しているrunCompileを実行しているのが分かると思います。

タスクの一覧

Gradleはタスクを指定して実行することは分かっていただけたでしょうが、そのタスクはビルドスクリプトを開かなくても gradle -q tasks を実行すれば分かります。

# gradle -q tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
components - Displays the components produced by root project 'GradleSample'. [incubating]
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'GradleSample'. [incubating]
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample'.

Other tasks
-----------
executeTest

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

Other tasks のところに今回作成したビルドスクリプトの内容があります。
おや?でも executeTest しかありません。 runCompile はどこにあるのでしょう?
今度は gradle -q tasks --all を実行し、すべての情報を出力します。

# gradle -q tasks --all

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
components - Displays the components produced by root project 'GradleSample'. [incubating]
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'GradleSample'. [incubating]
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample'.

Other tasks
-----------
executeTest
    runCompile

executeTest の次にインデントされて runCompile が出力されました。
これが「タスクの依存関係」を表しています。
ほかの依存関係の表現として行の最後の [xxxx] があるのですが、これは後ほど説明します。

Gradleを使ったJavaのビルド

以下ではGradleを使ったJavaソースコードのビルド方法を記します。
先程まで使っていた build.gradle は不要なので削除しましょう。

サンプルのプロジェクトを作成

Gradleは init タスクでJava/Groovy/Scalaの雛形プロジェクトを作成することが出来ます。

# gradle help --task init
:help
Detailed task information for init

Path
     :init

Type
     InitBuild (org.gradle.buildinit.tasks.InitBuild)

Options
     --type     Set type of build to create.
                Available values are:
                     basic
                     groovy-library
                     java-library
                     pom
                     scala-library

     --test-framework     Set alternative test framework to be used.
                          Available values are:
                               spock
                               testng

Description
     Initializes a new Gradle build. [incubating]

Group
     Build Setup

BUILD SUCCESSFUL

Total time: 1.498 secs

今回はJavaのプロジェクトを作成したいので、gradle init --type java-library を実行します。

# gradle init --type java-library
:wrapper
:init

BUILD SUCCESSFUL

Total time: 2.481 secs

実行すると、各種ファイルが生成されます。

│  build.gradle
│  gradlew
│  gradlew.bat
│  settings.gradle
│
├─.gradle
│  └─3.2.1
│      └─taskArtifacts
│              fileHashes.bin
│              fileSnapshots.bin
│              taskArtifacts.bin
│              taskArtifacts.lock
│
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│
└─src
    ├─main
    │  └─java
    │          Library.java
    │
    └─test
        └─java
                LibraryTest.java

色々なディレクトリやファイルが作成されますが、重要なのは build.gradle ファイルと、src ディレクトリです。
(他のディレクトリとファイルは今回説明を省略します)

ビルドスクリプトの確認

作成された build.gradle ファイルを見てみます。

build.gradle
/*
 * This build file was auto generated by running the Gradle 'init' task
 * by '???' at '16/12/21 23:19' with Gradle 3.2.1
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * user guide available at https://docs.gradle.org/3.2.1/userguide/tutorial_java_projects.html
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.21'

    // Declare the dependency for your favourite test framework you want to use in your tests.
    // TestNG is also supported by the Gradle Test task. Just change the
    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
    // 'test.useTestNG()' to your build script.
    testCompile 'junit:junit:4.12'
}

コメントが多いので今回はコメントを削除します。

build.gradle
apply plugin: 'java'

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    testCompile 'junit:junit:4.12'
}

すっきりしました。

Javaプラグイン

Javaのビルドスクリプトで重要なのが、先頭の apply plugin: 'java' で、これを記述するとJavaのビルドに必要な各種タスクが読み込まれます。

他にも、.projectのようなEclipse固有の設定ファイルを生成する apply plugin: 'eclipse' や、Groovyの apply plugin: 'groovy' や、 Scalaの apply plugin: 'scala' などがありますが、今回は省略します。

リポジトリの指定

Javaのプログラムを書く上で、Spring BootやJUnitやSLF4Jなどのライブラリを使用しないことはないでしょう。
Antの時代は各ライブラリの提供元のサイトから必要なJARファイルをダウンロードしてコンパイル時にローカルのパスを指定していましたが、Mavenの時代からそれはセントラルリポジトリと呼ばれるリモートに置かれ、必要に応じてビルドシステムが自動的にダウンロードするようになりました。
MavenであればMaven Central Repositoryからダウンロードします。

GradleはGradle固有のセントラルリポジトリがあるわけではなく、Mavenなど既存のセントラルリポジトリからライブラリをダウンロードします。
どのセントラルリポジトリを使用するかは、ビルドスクリプトの下記で指定します。

build.gradle
repositories {
    jcenter()
}

これは「BintrayのJCenterにある依存関係を探すための定義済みリポジトリ」を意味します。
今回はMavenセントラルリポジトリを参照したいので、これを以下のように変更します。

build.gradle
repositories {
    mavenCentral()
}

依存関係の定義

リポジトリからどのライブラリを使用するのかを定義するのが、ビルドスクリプトの下記の部分です。

build.gradle
dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    testCompile 'junit:junit:4.12'
}

ここで言う「依存関係」はライブラリの「依存関係」です。
タスクの「依存関係」ではありません。

compile で指定するのはテスト時とコンパイル時に使用するライブラリ、
testCompile で指定するのはテスト時に使用するライブラリです。

ライブラリの指定は、コロン区切りで 'GroupId : ArtifactId : Version' の3つが必要です。
この3つはMavenセントラルリポジトリにあるものを指定します。

タスクの確認

この状態でタスクの一覧を確認します。

# gradle tasks --all
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project. [jar]
build - Assembles and tests this project. [assemble, check]
buildDependents - Assembles and tests this project and all projects that depend on it. [build]
buildNeeded - Assembles and tests this project and all projects it depends on. [build]
classes - Assembles main classes.
    compileJava - Compiles main Java source.
    processResources - Processes main resources.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes. [classes]
testClasses - Assembles test classes. [classes]
    compileTestJava - Compiles test Java source.
    processTestResources - Processes test resources.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code. [classes]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
components - Displays the components produced by root project 'GradleSample'. [incubating]
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'GradleSample'. [incubating]
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample'.

Verification tasks
------------------
check - Runs all checks. [test]
test - Runs the unit tests. [classes, testClasses]

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

BUILD SUCCESSFUL

Total time: 1.529 secs

Java用のプラグインを指定しているので、Build tasks などが追加されています。
ビルドする場合、build タスクを指定します。

ここで先程保留した依存するタスクの話をします。
build タスクの右にある [assemble, check] となっている部分が依存しているタスクです。
build タスクの前に assemble タスク(プロジェクトのすべてのアーカイブを構築)と check タスク(プロジェクトのすべての検証タスクを実行)が実行されます。
assemble タスクの前には jar タスク(当該プロジェクトのJARファイルを構築)が実行され、
jar タスクの前には classes タスク(製品クラスディレクトリを構築)が実行され、
classesタスクの前には compileJava タスク(javacを使って製品Javaソースファイルをコンパイル)と processResources タスク(製品リソースを製品クラスディレクトリにコピー)が実行されます。

規約

Gradleは「規約によるビルド」をするビルドシステムだと言いましたが、Javaプラグインを使用する場合は下記のようなディレクトリの規約があります。

ディレクトリ 意味
src/main/java 製品のJavaソース
src/main/resources 製品のリソース
src/test/java テストのJavaソース
src/test/resources テストのリソース

ここでもう一度 init タスクで作成したディレクトリを見てみます。

└─src
    ├─main
    │  └─java
    │          Library.java
    │
    └─test
        └─java
                LibraryTest.java

init タスクで作成された「製品のJavaソース」ディレクトリと「テストのJavaソース」ディレクトリが、規約に則ったディレクトリだということがわかってもらえるかと思います。

ビルドの実行

gradle build でビルドを実行しましょう。

# gradle build
:compileJava
Download https://jcenter.bintray.com/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.pom
Download https://jcenter.bintray.com/org/slf4j/slf4j-parent/1.7.21/slf4j-parent-1.7.21.pom
Download https://jcenter.bintray.com/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 10.611 secs

実行したタスクが列挙されています。
compileJava から始まり、build で終わりました。

最初は依存ライブラリのダウンロードが発生するかもしれません。
もしプロキシを使用する環境で実行した場合、設定に不備があるとダウンロードに失敗するはずなので、設定を見直してください。

ビルド時に生成されるファイルは build ディレクトリにあります。
今回のテンプレートソースのJARファイルは build/libs/GradleSample.jar です。

実行したタスクに test があるのでテストは実行されたはずですが、念のためにテストクラスでエラーを起こしてみます。
test/java/LibraryTest.java を修正します。
修正前は下記のソースでした。

LibraryTest.java
public class LibraryTest {
    @Test public void testSomeLibraryMethod() {
        Library classUnderTest = new Library();
        assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
    }
}

これにエラーとなる処理を入れます。

LibraryTest.java
public class LibraryTest {
    @Test public void testSomeLibraryMethod() {
        Library classUnderTest = new Library();
        assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());

        assertTrue(false);
    }
}

assertTrue(false); でテストは失敗するはずです。
gradle build を実行します。

# gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test

LibraryTest > testSomeLibraryMethod FAILED
    java.lang.AssertionError at LibraryTest.java:14

1 test completed, 1 failed
:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:xxx/GradleSample/build/reports/tests/test/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 2.343 secs

テストが失敗しました。

なのでここまでで最初に言った「JavaのソースコードをコンパイルしてJARファイルにまとめてテストを実行するまで」が達成しました。

駆け足ですが、Gradle事始めは以上です。

参考にすべきサイト・書籍・スライド

最後に、自分はGradleを使う上で下記を参考にしています。

参考にすべきサイト

今回の記事のほとんどは日本語訳を基に書きました。

参考にすべき書籍

参考にすべきスライド

  • Gradle徹底入門の著者の一人、綿引さんのスライド