Gradle 6.7 で変更になった gradle init タスクのプロジェクトレイアウトとその使い方

f:id:Naotsugu:20200726205049p:plain


はじめに

組み込みのタスク gradle init を使うことで新しい Gradle プロジェクトを簡単に作成できます。

Gradle 6.7 では、新しい推奨構成でプロジェクトが作成されるようになりました。


旧来のプロジェクト構成

旧来は gradle init により application プロジェクトを作成した場合は以下のようなプロジェクト構成となっていました。

.
├── build.gradle
├── settings.gradle
└── src
    └── main
        └── java
            └── App.java


settings.gradle は以下のようになります。

rootProject.name = 'example'


build.gradle は以下のようになります。

plugins {
    id 'java'
    id 'application'
}

repositories {
    jcenter()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
    implementation 'com.google.guava:guava:29.0-jre'
}

application {
    mainClassName = 'example.App'
}

test {
    useJUnitPlatform()
}


新しいプロジェクト構成

Gradle 6.7 で JVM言語用の application プロジェクトを作成すると以下のようになります。

$ gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4]  2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 1
...

Split functionality across multiple subprojects?: という選択肢が追加されています。

1 を選択した場合は以下のようなプロジェクトが作成されます。

.
├── settings.gradle
└── app
    ├── build.gradle
    └── src
        └── main
            └── java
                └── App.java

ルートにあった build.gradleapp の中に配備されるようになりました。


settings.gradle は以下のようになり、app をインクルードしています。

rootProject.name = 'example'
include('app')


build.gradle は以下のようになります。

plugins {
    id 'application'
}

repositories {
    jcenter()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
    implementation 'com.google.guava:guava:29.0-jre'
}

application {
    mainClass = 'example.App'
}

tasks.named('test') {
    useJUnitPlatform()
}


もし、モジュールlib を追加する場合は以下のように追加し、

.
├── app
│   ...
│   └── build.gradle
├── lib
│   ...
│   └── build.gradle
└── settings.gradle

settings.gradle でインクルードすればOKです。

rootProject.name = 'example'
include 'app'
include 'lib'


application and library projects

先程の選択肢で application and library projects を選択します。

...
Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 1

プロジェクト構成は以下のようになります。

.
├── settings.gradle
├── buildSrc
│   ├── build.gradle
│   └── src
│       └── main
│           └── groovy
│               ├── example.java-application-conventions.gradle
│               ├── example.java-common-conventions.gradle
│               └── example.java-library-conventions.gradle
├── app
│   ├── build.gradle
│   └── src
│       └── main
│           └── java
│               └── example
│                   └── app
│                       ├── App.java
│                       └── MessageUtils.java
├── list
│   ├── build.gradle
│   └── src
│       └── main
│           └── java
│               └── example
│                   └── list
│                       └── LinkedList.java
└── utilities
    ├── build.gradle
    └── src
        └── main
            └── java
                └── example
                    └── utilities


settings.gradle では app, list, utilities をインクルードする構成になっています。

rootProject.name = 'example'
include('app', 'list', 'utilities')


app/build.gradle は以下のようになります。

plugins {
    id 'example.java-application-conventions'
}

dependencies {
    implementation project(':utilities')
}

application {
    mainClass = 'example.app.App'
}


utilities/build.gradle は以下のようになります。

plugins {
    id 'example.java-library-conventions'
}

dependencies {
    api project(':list')
}


list/build.gradle は以下のようになります。

plugins {
    id 'example.java-library-conventions'
}

dependencies にて、app -(implementation)-> utilities -(api)-> list のように依存が定義されています。

そしてそれぞれの pluginsexample.java-application-conventions のような convention が設定されています。

この convention プラグインは buildSrc の中に配備された以下のファイルで定義されたものです。

│   └── src
│       └── main
│           └── groovy
│               ├── example.java-application-conventions.gradle
│               ├── example.java-common-conventions.gradle
│               └── example.java-library-conventions.gradle


buildSrc/build.gradle は以下のような定義になっています。

plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    gradlePluginPortal()
}

'groovy-gradle-plugin' によりbuildSrc 内の Groovy で書かれた convention プラグインが、メインビルドのプラグインとして利用できるようになります。

では convention プラグインの中身はどのようになっているでしょう。


app/build.gradle にプラグインとして定義されている example.java-application-conventions.gradle は以下のようになっています。

plugins {
    id 'example.java-common-conventions'
    id 'application'
}

application プラグインに加え、example.java-common-conventions プラグインを利用しています。


utilities/build.gradleutilities/build.gradle から利用されている example.java-library-conventions.gradle は以下のようになっています。

plugins {
    id 'example.java-common-conventions'
    id 'java-library'
}

java-library プラグインに加え、こちらも example.java-common-conventions プラグインを利用しています。


そして example.java-common-conventions.gradle でプロジェクト共通の定義がされています。

plugins {
    id 'java'
}

repositories {
    jcenter()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

tasks.named('test') {
    useJUnitPlatform()
}


buildSrc

Gradle は実行されたときに buildSrc ディレクトリを確認し、そしの中のコードを自動的にコンパイル・テストを行い、ビルドスクリプトのクラスパスに追加します。

この buildSrc ビルドを使用して、必須ビルドロジックと共通ビルドロジックなどを整理できます。

通常、マルチプロジェクト内のサブプロジェクトは、ある特徴別で構成されます。 この特徴は、パブリック・ライブラリ であったり、内部ライブラリであったり、ウェブサービスであったり、ドキュメント生成コードであったり様々です。

これらの特徴は、サブプロジェクトのタイプを示しており、言い換えれば、サブプロジェクトのタイプがそのサブプロジェクトの特徴を示しているということになります。

Gradle ではプロジェクトの特徴を整理する方法としてプラグインシステムを使っています。一般的な Java プロジェクトであれば java プラグイン、アプリケーションであれば application プラグインを適用してプロジェクトを構成します。

buildSrc ディレクトリに convention プラグインとして各種の特徴を定義し、各サブプロジェクトでそのプラグインを適用することでビルドロジックを整理できます。つまりプラグインを適用することで、それぞれのサブプロジェクトを特徴付けることができます。

先の例では example.java-common-conventions という共通の convention プラグインと、example.java-library-conventions というライブラリ用の共通の convention プラグイン、そして example.java-application-conventions というアプリケーション用のconvention プラグインを定義し、それぞれのサブプロジェクトに適用することで、そのサププロジェクトのタイプを形付けることができるようになっています。

なお、convention プラグインにはケバブ・ケース(全て小文字で - で連結)で名前を付けることが推奨されています。



Gradle in Action

Gradle in Action