MPのご利用は計画的に

だいたい自分用のメモ

ひとつのbuild.gradle.ktsファイルでマルチプロジェクトを構成したい

概要

複数ファイルで構成するサンプルはよく見かけるけど、1つのファイルで構成するサンプルを見かけなかったので作ってみました。
これが正解かはわかりません。

環境

------------------------------------------------------------
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:          1.8.0_242 (AdoptOpenJDK 25.242-b08)
OS:           Mac OS X 10.15.6 x86_64

TL;DR

  • 全体の設定はallProjects
  • サブプロジェクト全体の設定はsubprojects
  • 各プロジェクト固有の設定はproject("MODULE_NAME")

どうすればいいか

とりあえず親プロジェクトを用意する

ゼロから用意するのは面倒なので、SpringInitializerで生成しちゃいます。
今回は親プロジェクトはナシで、サブプロジェクトになるものを2つ用意しました。
スタートはこんな感じです。ここから少しずつ分解していきます。

rootProject.name = "one-file-gradle-multi-project"

include("sub-project-1")
include("sub-project-2")
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.3.3.RELEASE"
    id("io.spring.dependency-management") version "1.0.10.RELEASE"
    kotlin("jvm") version "1.3.72"
    kotlin("plugin.spring") version "1.3.72"
}

group = "com.example.footaku.scratches"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3")
    runtimeOnly("org.postgresql:postgresql")
    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}

全体に適用したいものを抜き出す

repositoriesは全体に関係するのでallProjectsに移動します。

allprojects {
    repositories {
        mavenCentral()
    }
}

サブプロジェクト全体に適用するものを分ける

すべてのサブプロジェクトで使うdependenciestaskssubprojectsへ移動します。
ついでにpluginsourceSetsの設定も足します。

subprojects {
    apply(plugin = "java")
    apply(plugin = "kotlin")
    apply(plugin = "kotlin-spring")
    apply(plugin = "org.springframework.boot")
    apply(plugin = "io.spring.dependency-management")

    group = "com.example.footaku.scratches"
    version = "0.0.1-SNAPSHOT"
    java.sourceCompatibility = JavaVersion.VERSION_1_8
    java.targetCompatibility = JavaVersion.VERSION_1_8

    sourceSets {
        main {
            java.srcDir("src/main/kotlin")
            resources.srcDir("src/main/resources")
        }

        test {
            java.srcDir("src/test/kotlin")
            resources.srcDir("src/test/resources")
        }
    }

  dependencies{...}

  tasks.withType<...> {...}
}

プロジェクト固有の設定を分ける

さらに、一部のプロジェクトだけに適用するdependenciesなどはprojectでプロジェクト名を指定します。
今回はDBを使う設定をsub-project-1にだけ適用してみます。
この場合sub-project-1はDataSourceの設定をapplication.yml(.properties)に書かないとSpringBootは起動できなくなりますが、sub-project-2ではその依存が入っていないので設定が無くても大丈夫です。

project(":sub-project-1") {
    dependencies {
        implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3")
        runtimeOnly("org.postgresql:postgresql")
    }
}

完成

出来上がりがこちらです。

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.3.3.RELEASE"
    id("io.spring.dependency-management") version "1.0.10.RELEASE"
    kotlin("jvm") version "1.3.72"
    kotlin("plugin.spring") version "1.3.72"
}

allprojects {
    repositories {
        mavenCentral()
    }
}

subprojects {
    apply(plugin = "java")
    apply(plugin = "kotlin")
    apply(plugin = "kotlin-spring")
    apply(plugin = "org.springframework.boot")
    apply(plugin = "io.spring.dependency-management")

    group = "com.example.footaku.scratches"
    version = "0.0.1-SNAPSHOT"
    java.sourceCompatibility = JavaVersion.VERSION_1_8
    java.targetCompatibility = JavaVersion.VERSION_1_8

    sourceSets {
        main {
            java.srcDir("src/main/kotlin")
            resources.srcDir("src/main/resources")
        }

        test {
            java.srcDir("src/test/kotlin")
            resources.srcDir("src/test/resources")
        }
    }

    dependencies {
        implementation("org.springframework.boot:spring-boot-starter-web")
        implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
        implementation("org.jetbrains.kotlin:kotlin-reflect")
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

        testImplementation("org.springframework.boot:spring-boot-starter-test") {
            exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
        }
    }

    tasks.withType<Test> {
        useJUnitPlatform()
    }

    tasks.withType<KotlinCompile> {
        kotlinOptions {
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = "1.8"
        }
    }
}

project(":sub-project-1") {
    dependencies {
        implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3")
        runtimeOnly("org.postgresql:postgresql")
    }
}

困っていること

バージョンの指定方法

Mavenではバージョンなどの値を<properties>で設定すればどこでも使えるけれど、 Gradleにはそういう機能がないっぽいのが困りどころだなーと感じてます。

ワークアラウンド?としてextを使う方法をよく見かけますが、こちらはpluginの指定では使えず、 一方でbuildSrcに定義すればどこでも使える様になる代わりに、定義する場所が分散してしまうのでうーん…
できれば「このファイルだけ見ればOK」という状態にしたいのですが、 自分なりに「これがベスト!」という方法が見つかってません。

あってるかがわからない

とりあえず動いてるけどこれが正しいのかがわかってません。

参考