背景と目的

出発点

プロジェクト`slider-gradle`Gradleプラグインを介してAsciiDocファイルからReveal.jsプレゼンテーションを生成します`org.asciidoctor.jvm.revealjs`. 主要タスクの設定`asciidoctorRevealJs`ルートのビルドスクリプトに直接存在していた`build.gradle.kts`[space]

plugins { id("org.asciidoctor.jvm.revealjs") }

apply<slides.SlidesPlugin>()

project.tasks.getByName<AsciidoctorJRevealJSTask>(TASK_ASCIIDOCTOR_REVEALJS) {
    repositories { ruby { gems() } }
    revealjs {
        version = "3.1.0"
        templateGitHub {
            setOrganisation("hakimel")
            setRepository("reveal.js")
            setTag("3.9.1")
        }
    }
    revealjsOptions {
        // ... configuration complète
    }
}

目的

この構成すべてを移動する`buildSrc/src/main/kotlin/slides/SlidesPlugin.kt`コンシューマーのbuildscriptが次のように簡略化されるように:

apply<slides.SlidesPlugin>()

プラグインは完全に自立している必要があります: それは自身でプラグインの依存関係を適用し、リポジトリを設定し、Ruby の gems を管理します。

最初の障害:`ruby { gems() }

Groovyのシンタックスシュガーが隠しているもの

ライン`repositories { ruby { gems() } }`これはbuildscript実行コンテキストでのみ利用可能なGroovy DSL拡張です。buildSrcからアクセス可能な静的なKotlin APIとして存在しません。

それを呼び出そうとしてから`SlidesPlugin.kt`, コンパイルはすぐに失敗します。

メカニズムの分解

Gradleキャッシュの分析後 (~/.gradle/caches/modules-2/files-2.1/rubygems/), ファイルの中で見つかる`ivy-3.1.0.xml`:

<artifact type='gem' url='https://rubygems.org/gems/asciidoctor-revealjs-3.1.0.gem' />

`ruby { gems() }`実際に行っていた3つの異なる操作:

  1. 指しているIvyリポジトリを保存する`https://rubygems.org/gems/`

  2. グループを除外する`rubygems`Maven リポジトリで競合を避けるため

  3. ジェムを設定に保存`asciidoctorGems`JRubyがランタイムでそれをロードするように

これらの三つの責任は、Kotlinで個別に再実装しなければなりません。

3部構成の解決策

パート 1: rubygems用のIvyリポジトリ

project.repositories.mavenCentral() {
    content { excludeGroup("rubygems") }
}
project.repositories.ivy {
    url = project.uri("https://rubygems.org/gems/")
    patternLayout { artifact("[module]-[revision].gem") } (1)
    metadataSources { artifact() }
    content { includeGroup("rubygems") }
}
1 拡張`.gem`ハードコーディングする必要がある。[ext]`デフォルトで解決する.jar`, これがエラーを引き起こす`Resource missing`.
Ivy リポジトリは直接上に宣言されます`project.repositories`そしてブロックの中にない`repositories { }`このブロックのレシーバーではないから`RepositoryHandler`Gradle標準だが、grolifantというAPIは拡張機能と互換性がない`ivy`Kotlin DSL.

パート 2: asciidoctorGems の依存関係

プラグイン`org.asciidoctor.jvm.gems`最初に適用する必要があります—これにより設定が作成されます`asciidoctorGems`とタスク`asciidoctorGemsPrepare`</think>

project.plugins.apply("org.asciidoctor.jvm.gems")
project.plugins.apply("org.asciidoctor.jvm.revealjs") (1)

project.dependencies {
    add("asciidoctorGems", "rubygems:asciidoctor-revealjs:3.1.0@gem") (2)
}

適用順序は重要です:`gems`前に`revealjs`. <2> 修飾語`@gem`依存関係に正しい拡張子を強制する。

パート3: settings.gradle.kts

`dependencyResolutionManagement`中に`settings.gradle.kts`プロジェクトが自身のリポジトリを宣言できるようにする必要があります :

@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}

この行がなければ、Gradleはプラグインで宣言されたリポジトリを無視し、gemの解決が失敗します。

バイトコードによるAPIのイントロスペクション

なぜ javap ?

プラグイン`asciidoctor-gradle-jvm-slides`バージョンです`4.0.0-alpha.1`. そのドキュメントは存在しないか不完全です。 唯一の信頼できるソースは、コンパイル済みクラスの直接検査です。

タスクの階層

javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
  org.asciidoctor.gradle.jvm.slides.AsciidoctorJRevealJSTask

結果 :

public class AsciidoctorJRevealJSTask
  extends org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask
  implements org.asciidoctor.gradle.base.slides.SlidesToExportAware

forkOptions`の発見

�検査しながら`AbstractAsciidoctorTask`:

javap -p -classpath asciidoctor-gradle-jvm-4.0.0-alpha.1.jar \
  org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask | grep -i "fork\|exec\|jvm"

見つかります:

final org.ysb33r.grolifant.api.v4.JavaForkOptions javaForkOptions;
public void forkOptions(org.gradle.api.Action<org.ysb33r.grolifant.api.v4.JavaForkOptions>);
public static final org.asciidoctor.gradle.base.process.ProcessMode JAVA_EXEC;

JavaForkOptionsのAPI (grolifant)

`javaLauncher`このタスクには存在しません。 実際のAPIは`org.ysb33r.grolifant.api.v4.JavaForkOptions`公開 :

public void executable(java.lang.Object);  (1)
public void setExecutable(java.lang.Object);
1 方法`executable(Object)`代入を置き換える`executable = …​`コンパイルされない (`val`再割り当てすることはできません)。

RevealJS拡張

`revealjs { }`タスクの方法ではなく、プロジェクトの拡張 :

javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
  org.asciidoctor.gradle.jvm.slides.RevealJSExtension

アクセスするには:

project.extensions.getByType<RevealJSExtension>().apply {
    version = "3.1.0"
    templateGitHub {
        setOrganisation("hakimel")
        setRepository("reveal.js")
        setTag("3.9.1")
    }
}

Java ツールチェーンの設定

JavaToolchainServiceの問題

`JavaToolchainService`プロジェクトの拡張ではありません。次の呼び出しは失敗します :

// ERREUR : Extension of type 'JavaToolchainService' does not exist
project.extensions.getByType<JavaToolchainService>()

良いAPIは`serviceOf`:

import org.gradle.kotlin.dsl.support.serviceOf

project.tasks.getByName<AsciidoctorJRevealJSTask>(TASK_ASCIIDOCTOR_REVEALJS) {
    setInProcess("JAVA_EXEC")
    forkOptions {
        executable(
            project.serviceOf<JavaToolchainService>()
                .launcherFor {
                    languageVersion.set(JavaLanguageVersion.of(17))
                    vendor.set(JvmVendorSpec.ADOPTIUM)
                }
                .get()
                .executablePath
                .asFile
                .absolutePath
        )
    }
}

Docker自動検出

文脈

Asciidoctor/JRuby プラグインは Java 17 が必要です。buildSrc における Kotlin 2.0.x は Java 25 をサポートしていません。(バージョン パーサーがクラッシュします。"25.0.2")したがって、Gradle デーモンは Java 17 上で実行するか、または Docker を使用する必要があります。

戦略

  1. Docker が利用可能 → コンテナを介した実行`eclipse-temurin:17`(デフォルトの動作)

  2. Docker がない + Java 17 → ローカル実行

  3. Dockerが不在 + Java > 17 → 明示的なエラー

val isDockerAvailable = try {
    Runtime.getRuntime().exec(arrayOf("docker", "info")).waitFor() == 0
} catch (e: Exception) {
    false
}

val javaVersion = JavaVersion.current().majorVersion.toInt()

when {
    isDockerAvailable -> project.tasks.register<Exec>(TASK_ASCIIDOCTOR_REVEALJS) {
        group = GROUP_TASK_SLIDER
        description = "Slider settings and generation (via Docker)"
        dependsOn(TASK_CLEAN_SLIDES_BUILD)
        finalizedBy(TASK_DASHBOARD_SLIDES_BUILD)
        commandLine(
            "docker", "run", "--rm",
            "-v", "${project.rootDir.absolutePath}:/workspace",
            "-v", "${System.getProperty("user.home")}/.gradle:/root/.gradle",
            "-w", "/workspace",
            "eclipse-temurin:17",
            "./gradlew", TASK_ASCIIDOCTOR_REVEALJS
        )
        workingDir = project.rootDir
    }
    javaVersion == 17 -> {
        project.repositories.mavenCentral() {
            content { excludeGroup("rubygems") }
        }
        project.repositories.ivy {
            url = project.uri("https://rubygems.org/gems/")
            patternLayout { artifact("[module]-[revision].gem") }
            metadataSources { artifact() }
            content { includeGroup("rubygems") }
        }
        project.extensions.getByType<RevealJSExtension>().apply {
            version = "3.1.0"
            templateGitHub {
                setOrganisation("hakimel")
                setRepository("reveal.js")
                setTag("3.9.1")
            }
        }
        project.tasks.getByName<AsciidoctorJRevealJSTask>(TASK_ASCIIDOCTOR_REVEALJS) {
            setInProcess("JAVA_EXEC")
            forkOptions {
                executable(
                    project.serviceOf<JavaToolchainService>()
                        .launcherFor {
                            languageVersion.set(JavaLanguageVersion.of(17))
                            vendor.set(JvmVendorSpec.ADOPTIUM)
                        }
                        .get()
                        .executablePath
                        .asFile
                        .absolutePath
                )
            }
            // ... reste de la configuration
        }
    }
    else -> error(
        "Docker est requis pour exécuter $TASK_ASCIIDOCTOR_REVEALJS " +
        "avec Java $javaVersion. Installez Docker ou utilisez Java 17."
    )
}

最終結果

消費者のビルドスクリプト

apply<slides.SlidesPlugin>()

それだけです。 プラグインがすべての責任を負います。

settings.gradle.kts

pluginManagement {
    repositories {
        mavenLocal()
        gradlePluginPortal()
    }
}

plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}

@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}

rootProject.name = "slider-gradle"

�罠と解決策のまとめ

問題 原因 解決策

`ruby { gems() }`Kotlinでは利用できません

Groovy DSL専用の拡張

3つの別々のメカニズム : repo Ivy + exclusion Maven +asciidoctorGems

Gradleは`.jar`代わりの`.gem`

`[ext]`解決するで`jar`デフォルト

ハードコーダー`.gem`Ivy パターン + 修飾子`@gem`

ivy { }`でコンパイルされない`repositories { }

コトリンDSLと互換性のないレシーバーグルリファント

直接呼び出し`project.repositories.ivy { }`

`javaLauncher`未解決

存在しないプロパティに`AsciidoctorJRevealJSTask`

setInProcess("JAVA_EXEC")(Note: No output)forkOptions { executable(…​) }

`executable = …​`コンパイルされません

財産`val`中に`JavaForkOptions`グロリファント

方法`executable(Object)`代わりに

JavaToolchainService`経由で見つからない`extensions

これは Gradle のサービスであり、拡張機能ではありません。

project.serviceOf<JavaToolchainService>()

`revealjs { }`タスクでは未解決

プロジェクトの拡張、タスクの方法ではない

project.extensions.getByType<RevealJSExtension>()

Build は Java 25 でクラッシュします

Kotlin 2.0.xは2桁のJavaバージョンをパースしません

自動Docker検出 + Java 17 フォールバック

調査方法:javap を使って不明な API を読む

原則

ドキュメントが欠如しているか、または不完全な場合、バイトコードが真実のソースとなります。 javap`はJDKの標準ツールで、ファイルをデコンパイルします。.class`� 読みやすい Java のシグニチャー, ソースコードを必要としない。

ステップ1:Gradleキャッシュ内のJARファイルを特定する

グラドル ダウンロードする すべての依存関係 に`~/.gradle/caches/modules-2/files-2.1/`。 最初のステップは、検査したいクラスを含む jar ファイルを見つけることです:

find ~/.gradle/caches -name "asciidoctor-gradle-jvm-slides*.jar" 2>/dev/null
結果 :
/home/user/.gradle/caches/modules-2/files-2.1/org.asciidoctor/
asciidoctor-gradle-jvm-slides/4.0.0-alpha.1/.../
asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar

ステップ2: jarのクラスをリストする

クラスを検査する前に、それがjar内に確かに存在することを確認します :

jar tf asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar | grep -i "RevealJS\|revealjs"

これはすべての利用可能なクラスを示します: AsciidoctorJRevealJSTask, RevealJSExtension, RevealJSOptions, など

ステップ 3 : クラスを検査する

javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.slides.AsciidoctorJRevealJSTask

オプション`-p`すべてのメンバー、プライベートなものも含めて表示します。 結果はすぐにキー行を表示します:

public class AsciidoctorJRevealJSTask
extends org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask

ステップ4: 階層を上がる

タスクは伸ばす`AbstractAsciidoctorTask`その後、それを検査します まずその jar を特定して:

find ~/.gradle/caches -name "asciidoctor-gradle-jvm-[0-9]*.jar" 2>/dev/null

javap -p -classpath asciidoctor-gradle-jvm-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask | grep -i "fork\|exec\|jvm\|java"

そこで見つける`forkOptions`, JAVA_EXEC, et javaForkOptions タイプの`org.ysb33r.grolifant.api.v4.JavaForkOptions`。

ステップ5: 不明な型を追跡

`JavaForkOptions`これは不明なgrolifantクラスです。そのjarファイルを見つけます:

find ~/.gradle/caches -name "grolifant*.jar" 2>/dev/null

次に、それを検査します:

javap -p -classpath grolifant40-legacy-api-2.0.0-alpha.6.jar \
org.ysb33r.grolifant.api.v4.JavaForkOptions

そこにはある`executable(java.lang.Object)— 呼び出すべき正しいメソッド, 対照的に`executable = …​`これはプロパティだからコンパイルできない`val.

ステップ6:プロジェクトの拡張機能を確認する

ために`revealjs { }`, 質問は:これはタスクの方法ですか? またはプロジェクトの拡張ですか?検査の`AsciidoctorJRevealJSTask` 方法を示さない`revealjs`. その後、検査します`RevealJSExtension` :

javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.slides.RevealJSExtension | head -5
public class RevealJSExtension implements groovy.lang.GroovyObject {
public static final java.lang.String NAME;

の存在`NAME`それがプロジェクトに登録された拡張機能であることを確認します。 経由でアクセス可能`project.extensions.getByType<RevealJSExtension>()`.

方法のまとめ

ステップ アクション

1

find ~/.gradle/caches -name "*.jar"— jar を特定する

2

jar tf jar.jar | grep NomClasse— クラスが存在することを確認する

3

javap -p -classpath jar.jar NomCompletClasse— クラスを検査する

4

識別する`extends`そして階層を上がる

5

未知の型を自身のJAR内で追跡する

6

探す`NAME`プロジェクトの拡張を特定するために

このメソッドは、ドキュメント化されていない API を持つ任意の Gradle プラグインに適用されます。 または、アルファバージョンが既存のドキュメントに合わなくなった。

次のステップ

この buildSrc プラグインは、Gradle Plugin Portal または Maven Local に公開された独立したプロジェクトに抽出されます。消費者側の buildscript は次のようになります:

plugins { id("slides") version "1.0.0" }

Et settings.gradle.kts`最小限に抑えられ、参照なしで`foojay-resolver-convention, JDKのプロビジョニングはプラグイン自身によって管理され、または前提条件としてドキュメント化されている。

関連記事