対象者
- Gradle って何?
- Groovy DSL の書き方
- Gradle のサンプルコードが欲しい
- Gradle の公式ドキュメントを読む前に概要を知りたい
わかりやすさ重視で記載しているので、厳密な定義は公式ドキュメントを読んでください。
なお、本記事は以下の Java 入門用の記事の初回です。他の記事は以下をご覧ください。
- 【Java 入門1】オブジェクト指向プログラミングの基本用語
- 【Java 入門2】ガベージコレクションの仕組みとは
- 【Java 入門3】JDBC (Java DataBase Connectivity)
- 【Java 入門4】Gradle (ビルド自動化システム)
Gradle とは?代表的な2つの使い方
Gradle とは Java のビルドツールです。Gradle では、主に以下の2つのビルド作業を自動化します。
また、Gradle の設定ファイルを配布するだけで、他のコンピュータでも同じビルド環境を作成できます。
使い方1:任意のビルド作業を自動実行
Gradle では Task と呼ばれる単位で任意のビルド作業を実行できます。 Task の一例は以下のとおりです。
- Plugin で事前に用意された Task
- ビルドの実施(例:ビルドのコード)
- テストコードの実行
- 自分で作成した任意の Task(例: Hello Worldのコード)
何が嬉しいか
大量のオプションがついたビルドコマンドやテストコードを毎回、手動で入力する作業から開放されます。
使い方2:モジュールの依存関係を自動解決
モジュールの依存関係とは、「モジュール A を実行するために他のモジュールが必要な状態」のことを表します。
Gradle では、モジュール A をインストールする際に、依存関係にある他のモジュールも自動でインストールし、依存関係を解決してくれます。(例:依存関係のあるモジュールを取得)
何が嬉しいか
依存関係にあるモジュールを1つ1つ調べて、手動でインストールする作業から開放されます。
Gradle のインストール方法
SDKMAN!(パッケージ管理ソフト)を使用して Gradle 6.6.1 をインストールします。
- SDKMAN! をインストール(既にインストールされている場合はスキップ) curl -s "https://get.sdkman.io" | bashsource "$HOME/.sdkman/bin/sdkman-init.sh"
以下のコマンドでバージョンが表示されるとインストール成功
sdk version$ sdk version ==== BROADCAST ================================================================= * 2020-09-21: micronaut 2.0.3 now available for download. * 2020-09-21: jbang 0.47.1 now available for download. * 2020-09-20: jbang 0.46.1 @jbangdev https://git.io/JUEb5 ================================================================================ SDKMAN 5.9.0+555
- JDK バージョン 8 以上をインストール(既にインストールされている場合はスキップ) sdk install java
以下のコマンドで JDK バージョン 8 以上が表示されるとインストール成功
java -version$ java -version
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.8+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.8+10, mixed mode) - Gradle をインストール sdk install gradle
以下のコマンドでバージョンが表示されるとインストール成功
gradle -version$ gradle -version
------------------------------------------------------------ Gradle 6.6.1 ------------------------------------------------------------ Build time: 2020-08-25 16:29:12 UTC Revision: f2d1fb54a951d8b11d25748e4711bec8d128d7e3 Kotlin: 1.3.72 Groovy: 2.5.12 Ant: Apache Ant(TM) version 1.10.8 compiled on May 10 2020 JVM: 11.0.8 (AdoptOpenJDK 11.0.8+10) OS: Linux 4.14.193-149.317.amzn2.x86_64 amd64
補足
SDKMAN! で利用可能なソフトウェアは以下のドキュメントに記載されたソフトウェアだけです。Maven セントラルリポジトリなどから、依存関係を解決しながらモジュールをインストールする場合は、Gradle を利用する必要があります。
Gradle の6つの書き方
Gradle では、Groovy DSL もしくは Kotlin DSL を利用して、ビルドを自動化するビルドスクリプト(build.gradle)を記載します。
今回紹介する Groovy DSL では、主に以下の6つの書式を持ちます。
書き方1: Properties(変数)
Properties は、オブジェクトの持つ変数のことです。
Gradle がデフォルトで利用可能な Project オブジェクトの Properties 一覧は以下のとおりです。
書き方:Properties
Properties の書き方は以下のとおりです。
例:Properties を操作する
ビルドスクリプト(build.gradle)で、ビルドディレクトリを格納する Property である "buildDir" を操作してみます。
println project.buildDir // property の値を取得 project.buildDir = "test" // property に値をセット println project.buildDir println "buildDirの値は $buildDir です。" //文字列に property の値を埋め込みます println "buildDirの値は ${project.buildDir} です。" //文字列に property の値を埋め込みます
補足
デフォルトのオブジェクトである Project オブジェクトは省略可能です。そのため以下のような記載方法が可能です。
println buildDir // property の値を取得
Property の操作結果
gradle --configure-on-demand コマンドで結果を確認してみましょう。
$ gradle --configure-on-demand Configuration on demand is an incubating feature. Configure project : /home/ec2-user/build /home/ec2-user/test buildDirの値は /home/ec2-user/test です。 buildDirの値は /home/ec2-user/test です。
結果は Configure project : に表示されます。
書き方2: Methods(メソッド)
Methods は、オブジェクトが所有する関数のことです。
Gradle がデフォルトで利用可能な Project オブジェクトの Methods 一覧は以下のとおりです。
書き方: Methods
Methods の書式は以下のとおりです。
例:Methods を呼び出す
ディレクトリを作成する "mkdir()" Method を使用してみます。
mkdir("src/main/java") //ディレクトリ src/main/java を作成します。
mkdir() Method の実行結果
gradle --configure-on-demand コマンドでメソッドを実行してみましょう。
build.gradle
build.gradle src
書き方3: Script blocks (スクリプト)
Script blocks はメソッドとほぼ同じ機能です。メソッドとの違いは公式ドキュメントに記載があります。
Blocks are also methods, just with specific types for the last argument.
There are two important aspects of blocks that you should understand:
1. They are implemented as methods with specific signatures.
https://docs.gradle.org/current/userguide/groovy_build_script_primer.html#groovy:blocks
2. They can change the target ("delegate") of unqualified methods and properties.
Gradle がデフォルトで利用可能な Project オブジェクトの Script blocks 一覧は以下のとおりです。
なお、上記の公式ドキュメントには Script blocks と Blocks の用語が混在しています。同じ意味で使っていると思うのですが。。。
書き方: Script blocks
Script blocks を利用するための書式は以下のとおりです。
<obj>.<name> { ... }
<obj>.<name>(<arg>,<arg>){ ... }
具体的なビルドスクリプトの書き方は後述する Tasks, Plugins, Dependencies で記載します。現時点では "{ }"で囲む書式があるんだな。ぐらいの認識でOKです。
書き方4: Tasks (任意の Task)
Tasks は、ビルドの作業内容のことです。
Tasks オブジェクト が持つ Properties と Methods は以下のとおりです。
書き方: Tasks
Tasks の書き方は以下のとおりです。
例:Hello World! の Task を作成する
Gradle では任意の Task を作成可能です。ここでは例として Hello World! Task を作成します。
task task1 { println "Hello world!" }
gradle <Task名> コマンドで Task を実行してみましょう。
$ gradle task1 Configure project : Hello world! BUILD SUCCESSFUL in 1s
Configure project : に Hello world! が表示されることが確認できます。
例:Task 一覧を確認
Task 一覧を表示するためには gradle task コマンドを利用します。
Build Setup tasks ----------------- init - Initializes a new Gradle build. wrapper - Generates Gradle wrapper files. Help tasks ---------- (中略) properties - Displays the properties of root project 'ec2-user'. tasks - Displays the tasks runnable from root project 'ec2-user'.
先程作成した "task1" が Task 一覧に無いので、登録しましょう
例:独自のTask を Task 一覧に追加
Task 一覧に独自の Task を追加するには2つの Properties ["group
", "description
"]を使用します。
task task1 { group = "Custom" //group property を設定します。task のオブジェクト名は省略します。 description = "Hello world を表示します。" //task の説明を記載します。 println "Hello world!" } task task2(group: "Custom2", description: "Hello world を表示します。") { //引数で指定することも可能です。 println "Hello world!" }
もう一度 Task 一覧を確認してみましょう。
Build Setup tasks ----------------- init - Initializes a new Gradle build. wrapper - Generates Gradle wrapper files. Custom tasks ------------ task1 - Hello world を表示します。 Custom2 tasks ------------- task2 - Hello world を表示します。 Help tasks ---------- (中略) properties - Displays the properties of root project 'ec2-user'. tasks - Displays the tasks runnable from root project 'ec2-user'.
無事、作成した Task が Task 一覧に表示されることを確認できました。
例:Task を指定した時だけ実行
現在、gradle tasks コマンドを実行した際に task1, task2 が実行されています。
> Configure project : Hello world! Hello world!
そこで、Task オブジェクトの "doLast" Method を利用することで、Task 名を指定された時だけ、Task を実行するようにします。
task task1 { group = "Custom" //group property を設定します。task のオブジェクト名は省略します。 description = "Hello world を表示します。" //task の説明を記載します。 doLast{ println "Hello world!" } } task task2(group: "Custom2", description: "Hello world を表示します。") { //引数で指定することも可能です。 doLast{ println "Hello world!" } }
doLast() Method の結果を確認
Task 名を指定すると、指定した Task だけが動作します。
$gradle task1 > Task :task1 Hello world! BUILD SUCCESSFUL in 690ms 1 actionable task: 1 executed
書き方5: Plugins(既製の Task を追加)
Plugins は、誰かが作った project を追加する機能です。
Core Plugins 一覧は以下のとおりです。
Non-core Plugins 一覧は以下のページから検索可能です。
書き方: Plugins
Plugins の書式は以下の2種類があります。
apply plugin: '<プラグイン名>'
plugins{ id "<プラグイン名1>" id "<プラグイン名2>" }
例:ビルドを実行する(java Plugin)
"Java" Plugin の "build" Task を追加し、ビルドを実行します。なお、ソースコードは src/main/java 配下に置きます。
事前準備(ビルド用のソースコード)
- mkdir -p src/main/java
- vim src/main/java/HelloWorld.java
public class HelloWorld { public static void main(String[] args){ System.out.println("Hello world."); } }
java Plugin でビルド用の Task を追加、およびビルドを実行
- vim build.gradle
apply plugin: 'java'
- gradle tasks
> Task :tasks ------------------------------------------------------------ Tasks runnable from root project ------------------------------------------------------------ Build tasks ----------- assemble - Assembles the outputs of this project. build - Assembles and tests this project. buildDependents - Assembles and tests this project and all projects that depend on it. buildNeeded - Assembles and tests this project and all projects it depends on. classes - Assembles main classes. clean - Deletes the build directory. jar - Assembles a jar archive containing the main classes. testClasses - Assembles test classes.
- gradle build
$gradle build BUILD SUCCESSFUL in 1s 2 actionable tasks: 2 executed
- ls build/classes/java/main/HelloWorld.class
$ ls build/classes/java/main/HelloWorld.class build/classes/java/main/HelloWorld.class
例:プログラムを実行する(application Plugin)
事前の準備としてこちらのソースコードを用意してください
- vim build.gradle
plugins { id 'application' } mainClassName = 'HelloWorld'
- gradle run
$ gradle run > Task :run Hello world. BUILD SUCCESSFUL in 839ms 2 actionable tasks: 2 executed
書き方6: Dependencies(依存関係の解決)
Dependencies は、モジュールの依存関係を解決する機能です。
Dependencies オブジェクトの持つ Properties, Methods は以下のとおりです。
書き方: Dependencies
Dependencies の書式は以下のとおりです。
dependencies { <configurationName> '<グループID>:<アーティファクトID>:<バージョン>' }
dependencies { <configurationName> group:'<グループID>',name:'<アーティファクトID>version':<バージョン>' }
<configurationName> の例
https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management
例:Maven リポジトリからモジュールを取得
Gradle を用いて org.apache.commons.lang3.StringUtils モジュールを取得するデモを実施します。まずはデモ用のプログラム src/main/java/Dependencies.java を作成します。
事前準備
import org.apache.commons.lang3.StringUtils;
public class Dependencies {
public static void main(String[] args) {
String str="Hello";
System.out.println("変数 str に文字が入っている場合 false -->:" + StringUtils.isEmpty(str));
}
}
Dependencies を利用しない場合(失敗例)
plugins { id 'application' } mainClassName = 'Dependencies'
Dependencies を使用しない状態でプログラムを実行してみます。
> Task :compileJava FAILED /Users/hogetech/work/test/src/main/java/Dependencies.java:1: エラー: パッケージorg.apache.commons.lang3は存在しません
org.apache.commons.lang3.StringUtils モジュールが無いと怒られます。
Dependencies を利用する場合(成功例)
次に Dependencies を利用して、Maven リポジトリから org.apache.commons.lang3 モジュールを取得します。
plugins { id 'application' } mainClassName = 'Dependencies' repositories { mavenCentral() } dependencies { compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.11' }
なお、Dependencies で指定するモジュールの GroupID, Name, Version は Maven リポジトリから検索可能です。
Dependencies を指定したビルドスクリプトの実行結果
> Task :run 変数 str に文字が入っている場合 false -->:false
モジュールが正しく import され、実行に成功したことがわかります。
例: 依存関係を解決していることを確認
複雑な依存関係を持つ kinesis モジュールを利用して、依存関係を解決していることを確認してみます。まずは、モジュールの GroupID, Name, Version は Maven リポジトリから検索します。
以下のどちらのサイトで検索してもOKです。
検索の結果、取得するモジュールは以下のとおりであることがわかりました。
- <グループID> = software.amazon.awssdk
- <アーティファクトID> = kinesis
- <バージョン> = 2.15.2
モジュールを取得する Gradle Task を作成
依存関係を可視化するために、依存関係を解決するために利用するモジュールをローカルにダウンロードするタスクを作成します。
apply plugin: 'java' //compileOnly を利用するため //リポジトリの設定 repositories { mavenCentral() //https://repo.maven.apache.org/maven2/ から取得 } //リポジトリから取得するモジュールの設定 dependencies { compileOnly "software.amazon.awssdk:kinesis:2.15.2" } //jar ディレクトリを削除 task delDependJar { delete 'jar' } // 依存するモジュールを jar ディレクトリにコピーする task getModule(dependsOn: delDependJar) { group = "Hoge" description = "依存関係の解決" doLast { configurations.compileOnly.each { def fromJarFile = it.absolutePath copy { from fromJarFile into 'jar' } } } }
annotations-2.15.2.jar netty-codec-4.1.46.Final.jar apache-client-2.15.2.jar netty-codec-http-4.1.46.Final.jar auth-2.15.2.jar netty-codec-http2-4.1.46.Final.jar aws-cbor-protocol-2.15.2.jar netty-common-4.1.46.Final.jar aws-core-2.15.2.jar netty-handler-4.1.46.Final.jar aws-json-protocol-2.15.2.jar netty-nio-client-2.15.2.jar commons-codec-1.11.jar netty-reactive-streams-2.0.4.jar commons-logging-1.2.jar netty-reactive-streams-http-2.0.4.jar eventstream-1.0.1.jar netty-resolver-4.1.46.Final.jar http-client-spi-2.15.2.jar netty-transport-4.1.46.Final.jar httpclient-4.5.9.jar netty-transport-native-epoll-4.1.46.Final-linux-x86_64.jar httpcore-4.4.11.jar netty-transport-native-unix-common-4.1.46.Final.jar jackson-annotations-2.10.4.jar profiles-2.15.2.jar jackson-core-2.10.4.jar protocol-core-2.15.2.jar jackson-databind-2.10.4.jar reactive-streams-1.0.3.jar jackson-dataformat-cbor-2.10.4.jar regions-2.15.2.jar kinesis-2.15.2.jar sdk-core-2.15.2.jar metrics-spi-2.15.2.jar slf4j-api-1.7.28.jar netty-buffer-4.1.46.Final.jar utils-2.15.2.jar
kinesis-2.15.2.jar を利用するために、依存関係にあるモジュールを取得していることがわかります。
例: 独自の Maven リポジトリの指定
repositories { maven { url 'http://redshift-maven-repository.s3-website-us-east-1.amazonaws.com/release' } }
MavenArtifactRepository maven(Closure closure) で url Properties を指定します。
例: BOM の利用
BOM ファイルは複数のモジュールのバージョン一覧を記載したファイルです。
複数のモジュールをインストールする場合、依存関係がバッティングする場合があります。
この場合、モジュール C はどのバージョンを入れればいいの?という話になります。
これを解決するのが BOM ファイルと呼ばれるものです。以下のサイトにわかりやすい説明があります。
BOM を利用する場合のビルドスクリプトの書き方の例を掲載します。
apply plugin: 'java' repositories { mavenCentral() } dependencies { compileOnly 'software.amazon.awssdk:kinesis:2.1.4' compileOnly 'software.amazon.awssdk:s3:2.1.4' }
apply plugin: 'java' repositories { mavenCentral() } dependencies { compileOnly platform('software.amazon.awssdk:bom:2.1.4') compileOnly 'software.amazon.awssdk:kinesis' compileOnly 'software.amazon.awssdk:s3' }
BOM を利用した場合、BOM にモジュールのバージョンが記載されているため、モジュールにバージョンを記載する必要が無いです。BOM ファイル software.amazon.awssdk:bom:2.1.4 の内容は下記のページを参照してください。
個人的によく使う設定
気が向いたら追記していきます。
plugins { id 'application' } mainClassName = '<パッケージ名>.<main クラス名>' run { if (project.hasProperty('args')) { // "args" というプロパティが渡されていたら args project.args.split('\\s+') // 空白文字で split して、 run タスクの args オプションにセットする } standardInput = System.in } repositories { mavenCentral() } dependencies { implementation platform('<bom>') implementation '<モジュール>' implementation 'org.slf4j:slf4j-log4j12:1.7.21' //log4j.properties の設定も忘れずに }
plugins { id 'application' } mainClassName = 'KinesisClient.Library' run { if (project.hasProperty('args')) { // "args" というプロパティが渡されていたら args project.args.split('\\s+') // 空白文字で split して、 run タスクの args オプションにセットする } }
plugins { id 'application' } mainClassName = 'KinesisClient.Library' run { standardInput = System.in //標準入力(端末)から入力を受け付けます }
参考文献
# 公式ドキュメント ユーザーガイド
#公式ドキュメント DSL Reference
#公式ドキュメントの日本語訳