目的
共通部品に依存するAPIやバッチを一つのプロジェクトにまとめるマルチプロジェクト化をしたい人のためにKotlin + Gradleでの方法を書きたいと思います
(せっかくなのでJarファイル作成の設定も記載しておきます)
環境
- MacBookPro M1(zsh)
- IntelliJ IDEA Community版
- Gradle 7.0.2
成果物
GitHub: https://github.com/seki-shinnosuke/kotlin-gradle-multi-project
内容
今回作るマルチプロジェクト構成はこんな感じです
kotlin-gradle-multi-project
├── common
│ └── // 共通処理(UtilやComponent)を配置するためのサブプロジェクト
├── api
│ └── // SpringBootで作成したRestAPI
└── batch
└── // SpringBootで作成したCommandLine
まずは「settings.gradle.kts」
kotlin-gradle-multi-projectをrootプロジェクトとし配下の各プロジェクトはサブプロジェクトとしてincludeします
rootProject.name = "kotlin-gradle-multi-project"
include("common", "api", "batch")
そして今回の肝となる「build.gradle.kts」
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.springframework.boot.gradle.tasks.bundling.BootJar
object Versions {
const val jdk = "17"
}
plugins {
application
idea
kotlin("jvm") version "1.6.10"
kotlin("plugin.spring") version "1.6.10"
id("org.springframework.boot") version "2.6.6"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
allprojects {
group = "com.example"
repositories {
mavenCentral()
maven("https://plugins.gradle.org/m2/")
}
tasks {
// JSR 305チェックを明示的に有効にする
withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-java-parameters")
kotlinOptions.jvmTarget = Versions.jdk
}
withType<Test>().configureEach {
useJUnitPlatform()
val javaToolchains = project.extensions.getByType<JavaToolchainService>()
javaLauncher.set(
javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(Versions.jdk.toInt()))
}
)
}
// 共通部品を入れるプロジェクト以外はBootJarを生成可能にする
// Jarファイル名: kotlin-gradle-multi-project-[api|batch].jar
// 実行クラス名: com.example.[api|batch].[Api|Batch]Application
withType<BootJar>().configureEach {
if (this.project == rootProject || this.project.name == "common") {
enabled = false
} else {
mainClass.set("${rootProject.group}.${this.project.name}.${this.project.name.capitalize()}Application")
}
}
withType<Jar>().configureEach {
if (this.project == rootProject) {
enabled = false
} else {
enabled = true
archiveBaseName.set("${rootProject.name}-${this.project.name}")
}
}
}
}
subprojects {
apply {
plugin("kotlin")
plugin("kotlin-spring")
plugin("org.springframework.boot")
plugin("io.spring.dependency-management")
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
implementation("org.springframework.boot:spring-boot-gradle-plugin:2.6.6")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
}
configure<DependencyManagementExtension> {
imports {
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
}
}
project(":common") {
dependencies {
implementation("org.springframework:spring-web")
}
tasks.bootJar {
enabled = false
}
tasks.bootRun {
enabled = false
}
tasks.jar {
enabled = true
}
// commonに作成したテストクラスを共通部品として他のプロジェクトでも取り込みたい場合
val testCompile by configurations.creating
configurations.create("testArtifacts") {
extendsFrom(testCompile)
}
tasks.register("testJar", Jar::class.java) {
archiveClassifier.set("test")
from(sourceSets["test"].output)
}
artifacts {
add("testArtifacts", tasks.named<Jar>("testJar"))
}
}
project(":api") {
dependencies {
// 共通プロジェクトへの依存を追加
implementation(project(":common"))
implementation("org.springframework.boot:spring-boot-starter-web")
}
springBoot {
buildInfo()
}
}
project(":batch") {
dependencies {
// 共通プロジェクトへの依存を追加
implementation(project(":common"))
implementation("org.springframework.boot:spring-boot-starter-web")
}
springBoot {
buildInfo()
}
}
ポイントは以下
allprojects {
// ルートを含めた全プロジェクトに対しての設定
}
subprojects {
// 全サブプロジェクトに対しての設定
}
project(":common") {
// 共通プロジェクト
}
project(":xxxxxx") {
dependencies {
// 共通プロジェクトへの依存を追加
implementation(project(":common"))
}
}
マルチプロジェクトで共通化した際にプロダクトコードは依存関係を追加できるがテストコードでも共通のテストクラスを利用したいということも多々あると思います(DBの接続設定やテスト用のアノテーションクラスなど)
そういった時はテストクラスをJar生成時に含める形で共通プロジェクトに以下のような設定を加えると読み込んでくれます
val testCompile by configurations.creating
configurations.create("testArtifacts") {
extendsFrom(testCompile)
}
tasks.register("testJar", Jar::class.java) {
archiveClassifier.set("test")
from(sourceSets["test"].output)
}
artifacts {
add("testArtifacts", tasks.named<Jar>("testJar"))
}
まとめ
Java + Gradle(Groovy)でのマルチプロジェクト化は結構記事がありますがKotlinでの記事があまり見られなかったので書いてみました
プロジェクト内で共通化するならMavenリポジトリを立ててそこにパッケージとしてPublishした方がいいのではと思いますが利用規模やGit管理時に1リポジトリで完結するプロジェクトにしたい場合は参考にしていただければと思います