Android のモジュールのパスを簡潔にする

# はじめに
Android Studio[^1] 上で multi-module project[^2] を構成する際に、モジュール名とフォルダ構成に関して、以下のようなトレードオフを考えることがあります。

- フォルダ構成の混沌さを許容しつつ、モジュール名を簡潔する。
- 中間フォルダを設けてフォルダ構成をわかりやすくしつつ、モジュール名に中間フォルダ名を含める。

本稿では、これらのトレードオフの説明と、双方のメリットのみを享受する方法を示します。

# トレードオフ

## ◆ サブモジュール名を簡潔にする
サブモジュール名を `:android`, `:kotlin` のように簡潔に記述する。

### ☆ メリット
サブモジュール名が簡潔になります。

```groovy
include ':android'
include ':kotlin'
```

### ☆ デメリット
フォルダ構成がカオスになり、サブモジュールの名前空間も制限されます。[^3]

※ <font color="red">モジュール</font>と<font color="red">非モジュール</font>フォルダ<font color="red">数十個が混然一体</font>とした状況を想像してみてください。カオスです、、、。
![chaos.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219474/f8ded407-7290-a759-7376-ce248ea6895b.png)


## ◆ 中間フォルダを設ける

### ☆ メリット
フォルダ構成が簡潔になり、サブモジュールの名前空間も制限されません。[^4]
![simple.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219474/452cbca8-c3e0-9556-4410-55ba43e8c522.png)

### ☆ デメリット
サブモジュール名が無駄に長くなります。

```groovy
include ':modules:android'
include ':modules:kotlin'
```

# メリットのみを享受する方法

## ◆ settings.gradle

```groovy:settings.gradle
include(
        ':android',
        ':kotlin'
)
// ↑ は以下と等価です。どちらでもOKです。
// include ':android'
// include ':kotlin'

project(":android").projectDir = new File(rootDir, "/modules/android")
project(":kotlin").projectDir = new File(rootDir, "/modules/kotlin")
```

### ☆ 自動化版
modules フォルダ直下に全サブモジュールのみが配置されるのであれば、以下のようにモジュールの列挙から設定までを自動化することもできます。[^5]

```groovy:settings.gradle
new File("modules").listFiles().each { file ->
    def name = file.getName()
    def path = ":$name"
    include path
    project(path).projectDir = new File(rootDir, "/modules/$name")
}
```

## ◆ サブモジュールの参照例
```groovy
include ':android'
include ':kotlin'
```

## ◆ Android 用の表示例
![android.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219474/0e45b5fd-b3f4-abdc-f397-9159af1e76d7.png)

# おわりに
- Android Studio 上で multi-module project を構成する際に、Gradle project に任意の名称を設定することにより、モジュール名とフォルダ構成の両方を簡潔にする方法を説明しました。
- settings.gradle の記述を自動化する例を示しました。

それでは、また。

# 追記(2020-08-04)
## モジュールを動的に追加する Gradle Plugin を作ってみた
### 概要
- "build.gradle" もしくは "build.gradle.kts" の存在する箇所をすべて include します。ただし、root project, buildSrc は無視します。
- 例えば、"/modules/android/build.gradle.kts" が存在する場合は、":modules:android" が include されます。

### 使い方
`settings.gradle.kts` もしくは `settings.gradle` に以下のように設定します。 

注意： **build.**gradle ではなく **settings**.gradle なので注意！

```kotlin:settings.gradle.kts
buildscript {
    repositories {
        // mavenLocal() // for of-commons SNAPSHOT release
        maven { url = uri("https://beyondseeker.github.io/of-commons/mvn-repo") } // for of-commons
        google()
        jcenter()
    }
    dependencies {
        classpath("com.objectfanatics:gradle-plugin-dynamicmodulesplugin:0.0.1")
    }
}

apply(mapOf("plugin" to "com.objectfanatics.gradle.plugin.dynamicmodulesplugin"))
```

```groovy:settings.gradle
buildscript {
    repositories {
        // mavenLocal() // for of-commons SNAPSHOT release
        maven { url = uri("https://beyondseeker.github.io/of-commons/mvn-repo") } // for of-commons
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.objectfanatics:gradle-plugin-dynamicmodulesplugin:0.0.1'
    }
}

apply plugin: 'com.objectfanatics.gradle.plugin.dynamicmodulesplugin'
```


[^1]: InteliJ Idea でも同様だと思われます。
[^2]: Gradle 的には Multi project.
[^3]: ルートフォルダ直下のモジュールが増えるほど、ルートフォルダ直下の名前空間が圧迫されてしまう。
[^4]: 中間フォルダの直下にはモジュール用のフォルダしか配置されないので、名前空間を100%利用できます。
[^5]: モジュール名の増減や変更時に settings.gradle を変更する必要がないのでラクチンです。