はじめに
この記事は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
ファイルにプロキシ設定を記述します。
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
というファイルを作って、テキストエディタで以下の内容を書き込みます。
task hello << {
println 'Hello world!'
}
見て迷うことはないでしょうが、そのスクリプトにいま「hello」というタスクがあり、そのタスクは Hello world!
を出力します。
これをgradleコマンドで実行します。
build.gradleはGradleがデフォルトで読み込むビルドスクリプトですので、gradleコマンドにはスクリプトファイル名を指定する必要はありません。
また今回はログ出力を抑制して内容をすっきりさせるために -q
オプションを付けます。
実行するコマンドは gradle -q hello
です。
# gradle -q hello
Hello world!
意図した結果が出力されました。
タスクには、ある処理のまとまりを記述出来ます。
このタスクを使って、「コンパイルをする」や「テストを実行する」などの処理を行ないます。
タスクの依存関係
Gradleに限らず、ビルドシステムには「タスクの依存関係」は必要不可欠な概念です。
例えばテストを実行するためにはコンパイルが完了していなければなりません。
これは「テストを実行する」というタスクは「コンパイルをする」というタスクに依存している状態です。
この依存関係を、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
ファイルを見てみます。
/*
* 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'
}
コメントが多いので今回はコメントを削除します。
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など既存のセントラルリポジトリからライブラリをダウンロードします。
どのセントラルリポジトリを使用するかは、ビルドスクリプトの下記で指定します。
repositories {
jcenter()
}
これは「BintrayのJCenterにある依存関係を探すための定義済みリポジトリ」を意味します。
今回はMavenセントラルリポジトリを参照したいので、これを以下のように変更します。
repositories {
mavenCentral()
}
依存関係の定義
リポジトリからどのライブラリを使用するのかを定義するのが、ビルドスクリプトの下記の部分です。
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
を修正します。
修正前は下記のソースでした。
public class LibraryTest {
@Test public void testSomeLibraryMethod() {
Library classUnderTest = new Library();
assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
}
}
これにエラーとなる処理を入れます。
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徹底入門の著者の一人、綿引さんのスライド