動機
つい1週間くらい前にはじめてbuild.gradle触ってみて「はにゃ?」てなったので、どこかのbuild.gradleを読んでざっくり理解できるようになれたらいいな、のスタンスです
Gradle
GradleはGroovyを使ってスクリプトを記述するビルドツール
入門するなら
ありがたい先人の知恵(ほんとにありがたい......)
- Gradle入門
- Gradle の使い方
- 初めてのgradle build(Java)
- Gradle にしっかり入門する
- Gradle初心者によるGradle事始め
- Gradle (build.gradle) 読み書き入門
- Groovyを知らない人のためのbuild.gradle読み書き入門
そもそもbuild.gradleとは
- build.gradleは、Gradleというビルドツールの設定ファイルのこと(入門したらほぼ確実に触るやつ)
- ※ そもそものビルドとは(参考:ビルドとはなにか?~コンパイル・リンクとの違いを解説~)
- build.gradleを使ってプロジェクトのビルド方法や依存関係などを指定できる
- 具体的には、プロジェクトの構造や使用する外部ライブラリ、ビルド時のオプションなどの設定が可能
- これにより、Gradleが自動的に必要なファイルや依存関係をダウンロードし、プロジェクトをビルドする際に必要なタスクが実行可能になる
- 例えばプロジェクトがJavaで開発されている場合、build.gradleにはコンパイル対象のソースコードや依存するライブラリの指定を含むことができ、また、ビルド時に実行したいテストやデプロイなどのタスクも定義できる
- 要するに、build.gradleはプロジェクトのビルドに関する設定を記述するファイルであり、Gradleがこれを読み込んでビルド作業を進める役割を果たしている
ビルドスクリプト内のセクションやブロックについて
基本的(だと思っている)なものを載せています
- import
- buildscript
- plugins
- java
- IDE
- configurations / configurations.all
- repositories
- dependencies
- sourceSets
- task
- ext
※ サンプルコードはそれぞれ独立しているし中身もちゃんとしてないので、書いても機能しません
import
正確にはセクションやブロックではないけども
/*
* This file was generated by the Gradle 'init' task.
*
* This is a general purpose Gradle build.
* To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.5/samples
* This project uses @Incubating APIs which are subject to change.
*/
// 上記はgradle initコマンドで勝手に生成される
/*
* 先頭に追加されるimport文はGradleプラグインが提供する機能によって自動的に追加される。
* たとえばJavaプラグインを適用する場合、import文が自動的に生成されてJava関連のクラスやタスクをビルドスクリプト内で利用できるようになる、など。
*/
import org.gradle.api.tasks... // Gradleのタスク関連のクラスを使用する場合など
import org.gradle.api... // Gradleの基本的な機能やAPIを使用する場合など
import org.gradle.api.artifacts... // Gradleの依存関係管理に関連するクラスなど
import org.gradle.api.plugins... // Gradleのプラグイン関連のクラスを使用する場合など
buildscript
ビルドスクリプト自体の設定やクラスパスの管理
/*
* Gradleビルドスクリプト内で「ビルドスクリプト自体」の設定や
* 依存関係を管理するために使用されるブロック。
* 主に、ビルドスクリプト内で使用する外部プラグインや依存関係を指定するために使用。
*/
buildscript {
repositories {
// 必要なリポジトリをここに指定
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1' // Gradleプラグインの依存関係の指定
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10' // Kotlinプラグインの依存関係の指定
}
}
buildscriptブロックは通常、ビルドスクリプトの先頭に配置される。
- buildscriptブロックの主な要素とその目的:
-
repositories
:- ビルドスクリプトが使用するリポジトリ(依存関係の解決に使用されるライブラリやプラグインが存在する場所)を定義する
- Maven Central Repository や JCenter Repository などのリポジトリを指定することが多い
-
dependencies
:- ビルドスクリプト自体が依存する外部のライブラリやプラグインを指定する
- ビルドスクリプト自体が利用する機能やタスクに必要な依存関係を追加する
-
参考:
plugins
プラグインの適用や構成
/*
* Gradleビルドスクリプトでプラグインを宣言し、
* プロジェクトに適用するために使用されるブロック
*/
// Gradle 2.1以降で導入された新しい書き方
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '7.1.0'
}
// 古いバージョンから使用されている書き方
apply plugin: 'application'
apply plugin: 'com.google.cloud.tools.jib' version: '3.2.0'
pluginの設定で、ビルドプロセスに追加の機能やタスクを追加することができる(めっちゃ便利)
pluginをいれるとざっくり以下のようなことができるようになる
- 追加のタスクの実行:
- プラグインによって提供されるタスクをビルドプロセスに追加することができる
- これにより、目的に応じて自動化された作業を簡単に行えるようになる
- 依存関係の管理:
- プラグインを使用することで、依存関係の管理を楽にすることができる
- 例えば、JavaプロジェクトにMavenプラグインを設定すればMavenリポジトリから依存するライブラリを自動的にダウンロードしてビルドに組み込める、など
- ビルド環境のカスタマイズ:
- プラグインを使用することで、ビルドプロセスや環境のカスタマイズができる
- 例えば、AndroidプロジェクトにAndroidプラグインを設定すればアプリケーションのビルドタイプや署名設定、リソースの最適化などの定義もできるようになる
- 拡張機能の利用:
- プラグインは、Gradleの機能やタスクを拡張するためのものも存在している
- 例えば、静的解析ツールやコード品質の改善、デプロイの自動化など、プラグインを使用して開発プロセスを向上させることができる
参考:
プラグインの設定
pluginの補足:設定例
// SonarQubeとJaCoCoのプラグインをビルドスクリプトに追加
plugins {
id 'org.sonarqube' version '3.1.1' // SonarQubeプラグイン
id 'jacoco' version '0.8.7' // JaCoCoプラグイン
}
sonarqube {
properties {
property 'sonar.host.url', 'https://sonarqube.example.com' // SonarQubeサーバのURL
property 'sonar.login', 'your-sonarqube-token' // SonarQubeサーバへのアクセスに使用する認証トークン
}
}
jacoco {
toolVersion = '0.8.7' // JaCoCoのバージョン
reportsDir = file("$buildDir/reports/jacoco") // JaCoCoレポートの出力ディレクトリ
}
上記のように設定を組んで、明示的にタスクを呼び出せば走ってくれる
- 補足:CI/CDパイプライン内でよく使われてそうなpluginたち
-
Lint
:コードの品質やスタイルに関する静的解析 -
SonarQube
:静的解析およびバグ検出 -
Jacoco
:コードのカバレッジ情報の提供 -
SpotBugs
/FindBugs
:バグ検出 -
Checkstyle
:コードスタイルチェッカー
-
参考:
java
サンプルはjavaですが、他のプログラミング言語のブロックも定義可能
- Kotlin
- Groovy
- Scala
- Android
など...
/*
* Javaプラグインが提供する機能を設定するために使用される
* このブロック内で、プロジェクトのJavaソースコードに関連する設定や依存関係を定義できる
*/
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
- javaブロックはプロジェクト自体に対する設定(Javaのコンパイル設定やソースセットの管理、依存関係など)を定義する
-
sourceCompatibility
とtargetCompatibility
について-
sourceCompatibility
:- プロジェクトのソースコード(Javaファイル)が指定したバージョンのJavaと互換性があることを示す
- コード内で使用される言語機能やAPIのバージョンに合わせて設定する
-
targetCompatibility
:- プロジェクトのターゲットとするJavaバージョンのコンパイル互換性を指定する
- ソースコードはこのバージョンに従ってコンパイルされ、バイトコードやクラスファイルが生成される
- 実行またはデプロイ時に、ターゲットJavaバージョンと互換性があることが保証される
-
- javaブロック内に
sourceSets
ブロックやdependencies
ブロックをまとめてしまう書き方もできる
参考:
こういうのもあるみたいね
IDE
※ サンプルとしてeclipseを定義
/*
* IntelliJ IDEAやVisual Studio Codeの定義もできるみたい
*/
eclipse {
// Java Development Tools(JDT)の設定
jdt {
sourceCompatibility = 1.8 // javaブロックに設定があればこっちで設定しなくてもOK
targetCompatibility = 1.8 // javaブロックに設定があればこっちで設定しなくてもOK
// ソースファイルのエンコーディングを設定する例
encoding = 'UTF-8'
// コンパイラオプションを設定する例
compilerOptions = [
'compliance': '1.8',
'source': '1.8',
'target': '1.8',
'unchecked': 'true',
'deprecation': 'true'
]
// コードフォーマッタの設定を設定する例
formatter {
lineSplit = 100
// その他のフォーマッタ設定...
}
}
// Web Tools Platform(WTP)の設定
wtp {
component {
contextPath = 'yourApplicationName'
}
}
// プロジェクトにカスタムファセットを追加するための設定
facet {
myFacet {
facetName = 'myFacet'
facetVersion = '1.0'
}
}
// こういう書き方もできる
facet(name: 'myFacet2', version: '2.0')
facet(name: 'myFacet3', version: '3.0')
// ビルドパスの設定
classpath {
file {
def node = it.asNode().appendNode('classpathentry', [ kind: 'src', path: 'src/main/com/example' ])
node.appendNode('attributes').appendNode('attribute', [name: 'optional', value: 'true'])
}
}
}
-
eclipse
ブロック:- GradleプロジェクトをEclipse IDEで使用するための定義
-
jdt
ブロック:- Eclipseプラグインを使用して生成されるEclipseプロジェクトのJDT(Java Development Tools)の設定をカスタマイズする
- 例えば以下のような設定が可能
encoding
compilerArgs
compilerOptions
formatter
-
wtp
ブロック:- Eclipseプラグインを使用して生成されるEclipseプロジェクトのWTP(Web Tools Platform)の設定をカスタマイズする
-
component
ブロック内では、アプリケーションのコンテキストパス(WebアプリケーションがWebコンテナにデプロイされた際のアクセスURLの一部)を指定する - デプロイ時にアプリケーションがWebコンテナに展開される際のパス
- 例えばサンプルの場合、WebアプリケーションへのアクセスURLは以下のようになる
http://localhost:8080/yourApplicationName
-
- Eclipseプラグインを使用して生成されるEclipseプロジェクトのWTP(Web Tools Platform)の設定をカスタマイズする
-
facet
ブロック:- カスタムファセットとは、Eclipseプロジェクトに追加できる特定の機能や設定のセット
- サンプルでは、myFacetという名前のファセットを定義し、バージョンは1.0に設定されている
- サンプルのように2通りの書き方があるみたい
- カスタムファセットとは、Eclipseプロジェクトに追加できる特定の機能や設定のセット
-
classpath
ブロック:- ビルドパスの設定を詳細に行うために使用される
- このブロック内で
file
ブロックを使用して、ビルドに必要なクラスパスの設定を行う- サンプルでは、
src/main/com/example
ディレクトリをビルドパスに追加し、それをオプションのソースディレクトリとしてマークしている - これにより、
src/main/com/example
ディレクトリが存在しなくてもビルドが成功するように設定される
- サンプルでは、
参考:
- GradleでEclipseとIntellij IDEAの開発環境を作る(※ 古めの記事ですが)
- JDTについて:Java 開発ツール (JDT)
- WTPについて:EclipseでのWeb開発を支援するWeb Tools Platform
- build.gradleでEclipseWTPプロジェクトを作る
- Spring Bootで作るWebアプリケーション② - Gradleプロジェクトの編集
configurations / configurations.all
依存関係のグループやスコープを管理
/*
* プロジェクトの依存関係を管理するための設定を行うブロック
* Gradleでは、ビルド時に使用する外部ライブラリやモジュールを依存関係として宣言する
* -> この依存関係はconfigurationsと呼ばれる「グループ」に分類される
* 例えば以下はJavaプロジェクトでよくある設定例
*/
configurations {
compileOnly // コンパイル時にのみ必要な依存関係
implementation // コンパイルから実行までの全体のプロセスで必要な依存関係
testImplementation // 実行時に必要な依存関係
runtimeOnly // コンパイル時には必要ないが、実行時にのみ必要な依存関係
}
「configurations と dependencies はどちらも依存関係を管理してるな?」と、けっこう理解に苦しんだのですが、以下のような感じです。
-
configurations
ブロック:- プロジェクト内の依存関係を「グループに分類するため」に使用される
- ブロック内で定義したグループは、依存関係のスコープやカテゴリに基づいて命名される
- これにより、ビルド時に必要な依存関係を特定のスコープに紐づけることができる
-
dependencies
ブロック:- 実際の依存関係をconfiguration(グループ)に対して「依存関係を追加する」ために使用される
- 依存関係は、外部のライブラリやモジュールの指定、プロジェクト内の他のモジュールとの依存関係、またはオプションの条件(例:テスト実行時のみの依存関係)として指定可能
つまり、configurationsは依存関係のグループ(またはスコープ)を作成し、dependenciesはそのグループに対して実際の依存関係を指定するために使用されるという違いがある
例えば以下のサンプルのように定義されていた場合、
configurations {
compileOnly
implementation
testImplementation
}
dependencies {
compileOnly 'com.example:library:1.0'
implementation 'com.example:library:2.0'
testImplementation 'com.example:library:3.0'
}
-
configurations
:設定した依存関係が適用されるビルドプロセスの「グループを定義」している -
dependencies
:指定したライブラリなどが、上記ビルドプロセスの「どこで依存関係を発生させるか」を定義している
つまりサンプルのスクリプトで説明すると、
-
com.example:library:1.0
はコンパイル時のみ依存関係が指定され、それ以外のプロセスでは指定なし -
com.example:library:2.0
はビルドプロセス全体で依存関係が指定されている -
com.example:library:3.0
はテストのビルドプロセス全体で依存関係が指定される
ざっくり言ってしまえば、configurationsブロックでビルドプロセスの段階をグループとして宣言しておいて、dependenciesブロックでライブラリなどを宣言したグループにあてはめることで依存関係を設定している、といった感じかなと。
ちなみに cofigurations.all
は、というと
/*
* build.gradle内のすべてのconfigurationsに共通の設定を適用するために使用される
* configurationsブロック内で個別に設定を行う代わりに、
* すべてのconfigurationsに一括で設定を適用する場合に便利
* たとえば、以下のようにconfigurations.allブロックを使用して、
* すべてのconfigurationsに対して依存関係解決戦略を適用することができる
*/
configurations.all {
resolutionStrategy {
// 依存関係の解決戦略を設定
}
}
-
configurations.all
ブロック内で依存関係の解決戦略や他の設定を指定することにより、プロジェクト全体に適用されるグローバルな設定ができる- たとえば以下
- 依存関係の変更通知を有効にする
- 必要な依存関係を一括で除外する
- 特定の依存関係をすべてのconfigurationsに適用させる
- たとえば以下
- ただし、configurations.allを使用するとすべてのconfigurationsに対して一括で設定が適用されるため、予期しない影響が発生する場合があるので注意が必要!
repositories
ビルドプロセスで使用される外部リポジトリの設定
/*
* Gradleビルドスクリプト内でプロジェクトの依存関係を解決するための
* リポジトリなどの設定を行うために使用される
*/
repositories {
// Maven Centralリポジトリ
mavenCentral()
// JCenterリポジトリ
jcenter()
// カスタムのMavenリポジトリ
maven {
url "https://example.repo.com/maven-repo"
}
}
- repositoriesブロックをbuildscriptブロック内に指定する場合:
- リポジトリは、Gradle「ビルドスクリプト自体」を実行するために必要なライブラリやプラグインの解決にのみ使用される。
- repositoriesブロックをbuildscriptブロックの外で指定する場合:
- リポジトリは、プロジェクトの通常の依存関係の解決およびビルドスクリプトの実行に使用される。
参考:
dependencies
ビルドプロセスでプロジェクトが依存する外部ライブラリやプラグインの定義
/*
* Gradleのビルドスクリプト内で依存関係の管理を行う
* プロジェクトに必要な外部ライブラリやモジュールの依存関係を宣言できる
*/
dependencies {
// 通常の依存関係の宣言
implementation 'com.example:library:1.0.0'
// group、name、versionの各プロパティを明示的に指定することもできる
implementation group: 'com.example', name: 'library', version: '1.0.0'
// APIとして公開される依存関係を宣言するとき
api 'com.example:api:2.0.0'
// コンパイル時のみに依存する依存関係を宣言するとき
compileOnly 'com.example:compileOnly:1.0.0'
// 実行時のみに依存する依存関係を宣言するとき
runtimeOnly 'com.example:runtimeOnly:1.5.0'
// 依存ライブラリを除外するとき
implementation ('com.example:exclude:1.0.0') {
exclude group: 'org.unwanted', module: 'unwanted-lib'
}
// 外部リポジトリからの依存関係の追加するとき必要
// build.gradleファイル内のどこかで既にrepositoriesブロックが定義されている場合は不要
repositories {
mavenCentral()
jcenter()
}
}
Gradleは、あなたのプロジェクトをビルドしたり実行したりするのに必要なファイルが何かを判定し、それらを見つけてこなければなりません。これらの入力ファイルを、プロジェクトの依存関係と呼びます。
(引用:Gradle公式ドキュメント > 第8章 依存関係管理の基本)
参考:
- Declaring dependencies
- Gradle の compile, api, implementation とかについて
- Java: Mavenプロジェクトの依存ライブラリを確認する(dependency:tree)
推移的依存について:
sourceSets
ソースセットやリソースのディレクトリ構造を定義
/*
* Gradleビルドスクリプト内でソースセットの構成を定義するために使用される。
* デフォルトではmainとtestという2つのソースセットが提供されるが、
* それ以外のソースセットを追加したり既存のソースセットの構成を変更したりする場合は
* sourceSetsブロックを使用する必要がある。
* ただしデフォルトのディレクトリ構造に従っている場合、
* 特にカスタムのソースセットを追加しない場合は、
* sourceSetsブロックを明示的に定義する必要はない。
*/
sourceSets {
main {
java.srcDirs = ['src/main/java']
resources.srcDirs = ['src/main/resources']
}
test {
java.srcDirs = ['src/test/java']
resources.srcDirs = ['src/test/resources']
}
integrationTest {
java.srcDirs = ['src/integrationTest/java'] // 統合テストコードのディレクトリを指定する
resources.srcDirs = ['src/integrationTest/resources'] // 統合テストリソースのディレクトリを指定する
}
}
ちなみに補足
- ソースセット:
- 全体的なプロジェクトのソースコードのセットを表し、通常は「main」と「test」などのサブセットを持っている
- リソースのディレクトリ構造:
- リソースのディレクトリ構造は、プロジェクト内の特定のリソースファイル(設定ファイル、画像、テンプレートなど)のディレクトリや構造のこと
参考:
- 第23章 Javaプラグイン 23.2. ソースセット
- 【Gradle】規約と異なる構成のJavaプロジェクトをビルドする
- Gradle で Java プロジェクトをビルドするときのディレクトリ構成を変更する (sourceSets, buildDir)
task
ビルドスクリプト内で追加のカスタムタスクを定義
/*
* タスクは、Gradleビルドスクリプト内で定義される動作の単位のこと
* ビルドプロセスの中で実行する特定の操作や処理を定義し、
* ビルドのカスタマイズやビルドフローの制御を行うために使用される
*/
// すでにビルドスクリプトに組み込まれているタスクはtaskキーワード不要
clean {
delete 'build'
}
// 自分で作成する場合はtaskキーワードを使用してタスクを定義し、
// その中で具体的な動作や処理を設定することができる
task myTask {
doLast {
println "Custom task executed"
}
}
// defキーワードを使用した変数の定義とタスクの組み合わせは、
// ビルドスクリプト内の他の場所でもタスクを参照するために便利
def myTask = task('myTask') {
// タスクの設定や動作を追加する
doLast {
println "Hello, World!"
}
}
// あらかじめ用意されたタスクタイプを使用してタスクの宣言もできる
task hoge(type: Jar) {
archiveFileName = 'fuga.jar'
}
タスクについては先人がいろいろな記事を残してくれているので参考にするとマジで参考になります。
参考:
- Gradle のタスク定義のあれこれ
- 本記事 > 入門するなら
タスクの依存関係や実行順の制御
taskの補足
task task1 {
// 処理
}
task task2 {
dependsOn task1 // task2はtask1に依存しているため、task2が実行される前にtask1が先に実行される
// 処理
}
task task3 {
// 処理
}
task2.mustRunAfter task1 // task2はtask1の後に実行される
task3.mustRunAfter task2 // task3はtask2の後に実行される
task compileJava {
// 処理
}
task runTests {
// 処理
}
task build(dependsOn: [compileJava, runTests]) {
// buildタスクは、compileJavaタスクとrunTestsタスクの両方が実行される前に実行される
}
タスク関連のメソッドはたくさんある
参考:
ext
追加(拡張)プロパティ
/*
* extブロック内で定義されたプロパティやメソッドは、
* 裏側ではExtraPropertiesExtensionオブジェクトのプロパティとして扱われる
* グローバルスコープでビルドスクリプト全体で使用されることが想定されているらしい
*/
ext {
group = 'com.example' // プロジェクトのグループID
version = '1.0.0' // プロジェクトのバージョン
libraries = [
junit: 'junit:junit:4.13.2', // 依存ライブラリの定義
guava: 'com.google.guava:guava:30.1-jre'
]
getVersionAndDate = {
def dateFormat = new SimpleDateFormat('yyyy-MM-dd')
return "Version: ${version}, Build Date: ${dateFormat.format(new Date())}"
}
}
println "Group ID: ${project.ext.group}"
println project.ext.getVersionAndDate()
extブロックについての理解は以下のような認識です。
- build.gradleファイル内で定義される特別なブロック
- プロジェクトの追加プロパティや変数を定義するために使用される
- プロジェクトのビルドスクリプト内で再利用可能な設定や値を定義するのに便利
- extブロック内で定義した変数には、後続のスクリプトで
project.ext
を使用してアクセスできる -
project.ext
は、ビルドスクリプト内でプロジェクト全体を表すオブジェクトであり、extオブジェクトのプロパティにアクセスするためのエイリアス
参考:
- Gradle公式ドキュメント > ExtraPropertiesExtension
- Gradle (build.gradle) 読み書き入門 > 拡張プロパティ
- Gradleには設定ファイルがいっぱい
その他Gradleに関する情報など
- Gradle徹底入門 の著者の1人である 綿引さんスライド
おわりに
せっかくある程度理解深まったし、オープンソースのbuild.gradle見るか~てspring-bootのbuild.gradle 見にいったらこの記事で書いてることほぼないやんけ、となって泣いた。