Gradle
ビルドツール
Gradle
概要
Gradleは、Javaエコシステムを中心とした強力なビルド自動化ツールです。2008年にHans Dockter氏とAdam Murdoch氏によって開発され、Apache AntとApache Mavenの制約を解決するために設計されました。GradleはGroovy DSLやKotlin DSLを使用してビルドスクリプトを記述し、宣言的な構成と命令的なロジックの両方をサポートします。増分ビルド、ビルドキャッシュ、並列実行などの高度な最適化機能により、大規模プロジェクトでも高速なビルドを実現します。Android開発の標準ビルドツールとしても採用され、現在はJava、Kotlin、Scala、C++、Swift等の多言語開発に対応しています。
詳細
主要機能
- DSLベース設定: GroovyまたはKotlin DSLによる柔軟なビルドスクリプト
- 高性能ビルド: 増分ビルド、ビルドキャッシュ、並列実行による最適化
- 依存関係管理: 強力な依存関係解決とコンフリクト処理
- マルチプロジェクト: 大規模なマルチモジュールプロジェクトサポート
- プラグインエコシステム: 豊富なコミュニティプラグインと公式プラグイン
- IDE統合: IntelliJ IDEA、Eclipse、Android Studioでの優れたサポート
- 多言語対応: Java、Kotlin、Scala、C++、Swift、JavaScript等
アーキテクチャ
タスクベースの実行エンジン、DAG(有向非環グラフ)による依存関係管理、Gradleデーモンによる高速化、設定フェーズと実行フェーズの分離。
エコシステム
Gradle Plugin Portal、Android Gradle Plugin、Spring Boot Gradle Plugin、Kotlin Gradle Plugin、豊富なサードパーティプラグイン群。
メリット・デメリット
メリット
- 高いパフォーマンス: 増分ビルドとキャッシュによる高速化
- 柔軟性: 命令的ロジックと宣言的設定の両方をサポート
- Kotlin DSL: 型安全なビルドスクリプト作成
- Android標準: Android開発のデファクトスタンダード
- 拡張性: プラグインによる高い拡張性
- 多言語サポート: Java以外の言語もサポート
- モダンなアーキテクチャ: 最新のビルドシステム設計
デメリット
- 学習曲線: DSLとGradleの概念理解が必要
- 複雑性: 大規模プロジェクトでは設定が複雑化
- メモリ使用量: Mavenより多くのメモリを消費
- ビルドスクリプトデバッグ: DSLのデバッグが困難な場合
- バージョン互換性: Gradleバージョン間の互換性問題
- 初期化時間: 初回起動時のコールドスタート時間
参考ページ
- Gradle 公式サイト
- Gradle ユーザーガイド
- Gradle DSL リファレンス
- Gradle Plugin Portal
- Gradle GitHub リポジトリ
- Gradle スタートガイド
書き方の例
インストールとプロジェクトセットアップ
# Gradle インストール確認
gradle --version
# 新しいGradleプロジェクト作成
gradle init
# プロジェクトタイプ選択(対話式)
# 1: basic
# 2: application
# 3: library
# 4: Gradle plugin
# 手動プロジェクト作成
mkdir my-gradle-project
cd my-gradle-project
# Gradle Wrapper 生成
gradle wrapper
# Wrapperを使用したビルド(推奨)
./gradlew build
./gradlew test
./gradlew clean
./gradlew tasks --all # 利用可能なタスク一覧
# ビルド情報表示
./gradlew projects
./gradlew dependencies
./gradlew properties
基本的なbuild.gradle(Groovy DSL)
plugins {
id 'java'
id 'application'
}
// プロジェクト情報
group = 'com.example'
version = '1.0.0'
// Java設定
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
// アプリケーション設定
application {
mainClass = 'com.example.Main'
}
// リポジトリ設定
repositories {
mavenCentral()
mavenLocal()
google()
gradlePluginPortal()
}
// 依存関係
dependencies {
// コンパイル時依存関係
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.slf4j:slf4j-api:2.0.6'
// ランタイム依存関係
runtimeOnly 'ch.qos.logback:logback-classic:1.4.6'
// テスト依存関係
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'org.mockito:mockito-core:5.1.1'
testImplementation 'org.assertj:assertj-core:3.24.2'
// Annotation processor
annotationProcessor 'org.projectlombok:lombok:1.18.26'
compileOnly 'org.projectlombok:lombok:1.18.26'
}
// テスト設定
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
exceptionFormat "full"
}
// テストカバレッジ
finalizedBy jacocoTestReport
}
// JaCoCo設定
jacoco {
toolVersion = "0.8.8"
}
jacocoTestReport {
dependsOn test
reports {
xml.required = true
html.required = true
}
}
// JAR設定
jar {
manifest {
attributes(
'Implementation-Title': project.name,
'Implementation-Version': project.version,
'Main-Class': application.mainClass
)
}
// Fat JAR作成
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
// カスタムタスク
task copyDocs(type: Copy) {
description = 'ドキュメントファイルをコピー'
group = 'documentation'
from 'src/docs'
into 'build/docs'
include '**/*.md'
}
// プロパティファイル処理
processResources {
expand(project.properties)
}
build.gradle.kts(Kotlin DSL)
plugins {
java
application
id("org.springframework.boot") version "3.1.0"
id("io.spring.dependency-management") version "1.1.0"
id("org.jetbrains.kotlin.jvm") version "1.8.21"
id("org.jetbrains.kotlin.plugin.spring") version "1.8.21"
}
group = "com.example"
version = "1.0.0"
java {
sourceCompatibility = JavaVersion.VERSION_17
}
application {
mainClass.set("com.example.ApplicationKt")
}
repositories {
mavenCentral()
}
dependencies {
// Spring Boot
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
// データベース
runtimeOnly("com.h2database:h2")
runtimeOnly("org.postgresql:postgresql")
// テスト
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
testImplementation("org.springframework.security:spring-security-test")
testImplementation("io.kotest:kotest-runner-junit5:5.5.5")
testImplementation("io.kotest:kotest-assertions-core:5.5.5")
testImplementation("io.mockk:mockk:1.13.4")
}
tasks {
test {
useJUnitPlatform()
}
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}
bootJar {
archiveFileName.set("${project.name}-${project.version}.jar")
}
register<Copy>("generateConfig") {
description = "設定ファイル生成"
group = "build setup"
from("src/main/templates")
into("src/main/resources")
expand(project.properties)
filteringCharset = "UTF-8"
}
}
// 開発用タスク
tasks.register("dev") {
description = "開発サーバー起動"
group = "application"
dependsOn("bootRun")
doFirst {
println("開発サーバーを起動しています...")
}
}
マルチプロジェクト設定
// settings.gradle
rootProject.name = 'multi-project-example'
include 'core'
include 'web'
include 'data'
include 'common'
// プロジェクト構造を定義
project(':core').projectDir = file('modules/core')
project(':web').projectDir = file('modules/web')
project(':data').projectDir = file('modules/data')
project(':common').projectDir = file('modules/common')
// ルートプロジェクトのbuild.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0' apply false
id 'io.spring.dependency-management' version '1.1.0' apply false
}
// 全サブプロジェクトに適用
allprojects {
group = 'com.example.multiproject'
version = '1.0.0'
repositories {
mavenCentral()
}
}
// サブプロジェクトのみに適用
subprojects {
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
java {
sourceCompatibility = JavaVersion.VERSION_17
}
dependencies {
// 共通依存関係
implementation 'org.slf4j:slf4j-api'
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
}
// プロジェクト間依存関係
project(':web') {
apply plugin: 'org.springframework.boot'
dependencies {
implementation project(':core')
implementation project(':data')
implementation project(':common')
implementation 'org.springframework.boot:spring-boot-starter-web'
}
}
project(':core') {
dependencies {
implementation project(':common')
implementation project(':data')
}
}
project(':data') {
dependencies {
implementation project(':common')
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
}
project(':common') {
dependencies {
implementation 'org.apache.commons:commons-lang3'
}
}
カスタムプラグインとタスク
// カスタムタスククラス
class CustomBuildTask extends DefaultTask {
@Input
String inputMessage = "Default message"
@OutputFile
File outputFile
@TaskAction
void execute() {
println "Executing custom build task: ${inputMessage}"
if (!outputFile.parentFile.exists()) {
outputFile.parentFile.mkdirs()
}
outputFile.text = """
Build executed at: ${new Date()}
Message: ${inputMessage}
Project: ${project.name}
Version: ${project.version}
"""
logger.lifecycle("Custom build completed: ${outputFile.absolutePath}")
}
}
// カスタムプラグイン
class CustomBuildPlugin implements Plugin<Project> {
void apply(Project project) {
// 拡張機能追加
project.extensions.create('customBuild', CustomBuildExtension)
// カスタムタスク追加
project.tasks.register('customBuild', CustomBuildTask) {
description = 'カスタムビルドタスクの実行'
group = 'custom'
inputMessage = project.customBuild.message
outputFile = new File(project.buildDir, 'custom/build-info.txt')
}
// 既存タスクへの依存関係設定
project.tasks.named('build') {
dependsOn 'customBuild'
}
}
}
// 拡張機能クラス
class CustomBuildExtension {
String message = "Hello from custom plugin"
boolean enabled = true
List<String> environments = ['dev', 'test', 'prod']
}
// プラグイン適用
apply plugin: CustomBuildPlugin
// プラグイン設定
customBuild {
message = "Custom build for ${project.name}"
enabled = true
environments = ['development', 'staging', 'production']
}
// 複数環境向けタスク生成
customBuild.environments.each { env ->
tasks.register("deploy${env.capitalize()}", Copy) {
description = "${env}環境へのデプロイ"
group = 'deployment'
from 'build/libs'
into "deploy/${env}"
include '*.jar'
doLast {
println "Deployed to ${env} environment"
}
}
}
高度な設定とビルド最適化
// gradle.properties
# ビルド最適化
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
# JVM設定
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC
# Kotlin設定
kotlin.code.style=official
kotlin.incremental=true
kotlin.daemon.jvm.options=-Xmx2g
// 高度なbuild.gradle設定
plugins {
id 'java'
id 'jacoco'
id 'checkstyle'
id 'com.github.spotbugs' version '5.0.13'
id 'org.sonarqube' version '4.0.0.2929'
}
// ビルドキャッシュ設定
buildCache {
local {
enabled = true
}
remote(HttpBuildCache) {
url = 'https://example.com/build-cache/'
enabled = true
push = true
credentials {
username = findProperty('buildCacheUsername')
password = findProperty('buildCachePassword')
}
}
}
// コード品質設定
checkstyle {
toolVersion = '10.7.0'
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
ignoreFailures = false
}
spotbugs {
toolVersion = '4.7.3'
effort = 'max'
reportLevel = 'low'
excludeFilter = file("${rootDir}/config/spotbugs/exclude.xml")
}
jacoco {
toolVersion = '0.8.8'
}
jacocoTestReport {
reports {
xml.required = true
html.required = true
csv.required = false
}
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
'**/Application.class',
'**/config/**',
'**/dto/**',
'**/entity/**'
])
}))
}
}
// SonarQube設定
sonarqube {
properties {
property 'sonar.projectKey', 'my-project'
property 'sonar.projectName', 'My Project'
property 'sonar.host.url', 'https://sonarcloud.io'
property 'sonar.organization', 'my-org'
property 'sonar.java.coveragePlugin', 'jacoco'
property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/test/jacocoTestReport.xml'
property 'sonar.junit.reportPaths', 'build/test-results/test'
}
}
// 環境別プロファイル
gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(':bootRun')) {
// 開発環境設定
bootRun {
environment 'SPRING_PROFILES_ACTIVE', 'development'
environment 'DEBUG', 'true'
}
}
}
// 条件付きタスク実行
if (project.hasProperty('env') && project.env == 'production') {
jar {
exclude '**/application-dev.yml'
exclude '**/logback-spring.xml'
}
}
// Gradle バージョンカタログ使用(Gradle 7.0+)
dependencyResolutionManagement {
versionCatalogs {
libs {
library('spring-boot-starter-web', 'org.springframework.boot', 'spring-boot-starter-web').version('3.1.0')
library('junit-jupiter', 'org.junit.jupiter', 'junit-jupiter').version('5.9.2')
bundle('testing', ['junit-jupiter', 'mockito-core', 'assertj-core'])
}
}
}
dependencies {
implementation libs.spring.boot.starter.web
testImplementation libs.bundles.testing
}
プロファイルとタスク制御
# 環境別実行
./gradlew build -Penv=production
./gradlew bootRun -Pspring.profiles.active=dev
# タスク並列実行
./gradlew test jacocoTestReport --parallel
# キャッシュ制御
./gradlew build --build-cache
./gradlew clean --build-cache
# 詳細ログ
./gradlew build --info
./gradlew build --debug
# 特定タスクのみ実行
./gradlew :web:test
./gradlew compileJava compileTestJava
# 品質チェック実行
./gradlew check
./gradlew sonarqube
# プロジェクト情報
./gradlew dependencies --configuration compileClasspath
./gradlew dependencyInsight --dependency org.springframework.boot
./gradlew projects
./gradlew tasks --group build
# パフォーマンス解析
./gradlew build --profile
./gradlew build --scan
# ビルド継続実行
./gradlew build --continue
# オフライン実行
./gradlew build --offline