Uzzu::Blog

Software Design, and my life.

GradleのPluginManagementの仕組み

https://docs.gradle.org/5.2/userguide/plugins.html#sec:plugin_management

2.1ぐらいからincubatingな New Gradle Plugin APIのPluginManagementのResolutionStrategyをそろそろ理解する。

Plugin作成

とりあえずPluginを実装します。はい。実装しました。

ではPlugin配布の為のビルドスクリプトを書いていきます。 java-gradle-plugin のplugin applyをお忘れなく。まずはGradlePluginの設定です。 Kotlin DSLですが、だいたいGroovyDSLなので良いでしょう。

gradlePlugin {
    plugins {
        register("greatPlugin") {
            id = "com.github.uzzu.gradle.great"
            implementationClass = "com.github.uzzu.gradle.GreatPlugin"
        }
        register("superPlugin") {
            id = "super-uzzu"
            implementationClass = "com.github.uzzu.gradle.SuperPlugin"
        }
}

デプロイスクリプトを書きます。GradlePluginPortalやその他maven repositoryに配布する場合はその記述が必要ですが今回は省略します。 maven-publish のplugin applyをお忘れなく。

group = "com.github.uzzu.gradle.awesome"
version = "1.0"
publishing {
    repositories {
        mavenLocal()
    }

    (publications) {
        register("pluginMaven", MavenPublication::class) {
            artifactId = "awesome-plugin"
        }
    }
}

やりました。では ./gradlew publish します。はい。しました。

Plugin読込(解決)

ではデプロイしたPluginを読み込んでみます。

settings.gradle(.kts)にPluginManagementの記述をしていきます。 enableFeaturePreview を使用している場合はその下に書きます。

pluginManagement {
    repositories {
        mavenLocal()
    }

    resolutionStrategy {
    }
}

書きました。では実際にprojectから読み込んでみます。

plugins {
    id("com.github.uzzu.gradle.great") version "1.0"
    id("super-uzzu") version "1.0"
}

読み込めました。

以下の形でも大丈夫です。

// settings.gradle.kts
pluginManagement {
    repositories {
        mavenLocal()
    }

    resolutionStrategy {
        eachPlugin {
            when (requested.id.id) {
                "com.github.uzzu.gradle.great",
                "super-uzzu" -> {
                    useVersion("1.0")
                }
            }
        }
    }
}
// build.gradle.kts
plugins {
    id("com.github.uzzu.gradle.great")
    id("super-uzzu")
}

従来のように buildscriptclasspath を記述せずともPluginを読み込めました。そしてデプロイしたはずの com.github.uzzu.gradle.awesome-plugin の記述はありません。どういう理屈なのでしょうか。

色々覗いてみる

まずはmaven repositoryを覗きます。

やたら名前が長い感じのrepositoryになってます。POMも見てみます。

GreatPlugin

<?xml version="1.0" encoding="UTF-8"?>    
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>    
  <groupId>com.github.uzzu.gradle.great</groupId>    
  <artifactId>com.github.uzzu.gradle.great.gradle.plugin</artifactId>    
  <version>1.0</version>    
  <packaging>pom</packaging>    
  <dependencies>    
    <dependency>    
      <groupId>com.github.uzzu.gradle</groupId>    
      <artifactId>awesome-plugin</artifactId>    
      <version>1.0</version>    
    </dependency>    
  </dependencies>    
</project> 

SuperPlugin

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>super-uzzu</groupId>
  <artifactId>super-uzzu.gradle.plugin</artifactId>
  <version>1.0</version>
  <packaging>pom</packaging>
  <dependencies>
    <dependency>
      <groupId>com.github.uzzu.gradle</groupId>
      <artifactId>awesome-plugin</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>
</project>

AwesomePlugin

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.github.uzzu.gradle</groupId>
  <artifactId>awesome-plugin</artifactId>
  <version>1.0</version>
  <dependencies>
    <!-- (omitted) -->
  </dependencies>
</project>

はーなるほど、POMのみのartifactを作って、そこでPlugin本体である所のAwesomeの依存を解決して読み込んでいる。AwesomePluginのjarの中を見るとこんな感じ

├── META-INF
│   ├── MANIFEST.MF
│   └── gradle-plugins
│       ├── com.github.uzzu.gradle.great.properties
│       └── super-uzzu.properties
└── com
    └── github
        └── uzzu
            └── gradle
                ├── GreatPlugin.class
                └── SuperPlugin.class
$ cat META-INF/gradle-plugins/com.github.uzzu.gradle.great.properties
implementation-class=com.github.uzzu.gradle.GreatPlugin
$ cat META-INF/gradle-plugins/super-uzzu.properties
implementation-class=com.github.uzzu.gradle.SuperPlugin

なるほどですね。

実際にPOMを作っている部分のソースを見てみます。

https://github.com/gradle/gradle/blob/v5.2.0/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/MavenPluginPublishPlugin.java#L84-L107

おおなるほど、確かに POMに書かれている通りgroupIdとartifactIdを生成している。

読込側も見てみます。

https://github.com/gradle/gradle/blob/v5.2.0/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/ArtifactRepositoriesPluginResolver.java#L118-L127

余談

Android Gradle PluginやKotlin Gradle PluginはNew Gradle Plugin APIには対応してなさそう。 useModule で単体では読み込めても、 Kotlin MPP + multimoduleをやっていると同2つのpluginの適用ががうまくいかない。根気が尽きた。Kotlinにおいては中の人的にもincubating featureだから使ってない旨のissue commentをどっかでみたけど忘れた。忘れてください。Gradle難しい。最悪、今まで通りbuildscript使えばいいのでまあいいでしょう。

結論

個人的には「これいるのかな…」という感じで、ずっとincubating なのも頷けます。pluginManagementにおけるresolutionStrategyはいまいち目的が見えない。誰か教えてください。ライブラリ管理におけるresolutionStrategyはexclude地獄を排除できるのでぼちぼち便利なんですけどね。うーん。

comments powered by Disqus