Bağlam ve amaç

Başlangıç noktası

Proje`slider-gradle`Reveal.js sunumlarını AsciiDoc dosyalarından Gradle eklentisi aracılığıyla oluşturur`org.asciidoctor.jvm.revealjs`. Ana görevin yapılandırması`asciidoctorRevealJs`doğrudan kök buildscript içinde bulunuyordu`build.gradle.kts`:

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
    }
}

Amaç

Tüm bu yapılandırmayı içine taşı`buildSrc/src/main/kotlin/slides/SlidesPlugin.kt`tüketici buildscript’inin şuna indirgenmesi için:

apply<slides.SlidesPlugin>()

Eklenti tamamen özerk olmalıdır: eklenti bağımlılıklarını kendisi uygular, depolarını yapılandırır ve Ruby gem’lerini yönetir.

İlk engel: `ruby { gems() }

Groovy sözdizimsel şekerinin gizlediği şey

Çizgi`repositories { ruby { gems() } }`Yalnızca buildscript yürütme bağlamında kullanılabilen bir Groovy DSL uzantısıdır. buildSrc’den erişilebilen statik bir Kotlin API’si olarak mevcut değildir.

Onu aramaya çalışırken`SlidesPlugin.kt`, derleme hemen başarısız olur.

Mekanizmanın ayrıştırılması

Gradle önbelleği analiz edildikten sonra (~/.gradle/caches/modules-2/files-2.1/rubygems/), dosyada görülür`ivy-3.1.0.xml`:

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

`ruby { gems() }`aslında gerçekleştiriyorduüç ayrı işlem :

  1. Şuraya işaret eden bir Ivy deposunu kaydet`https://rubygems.org/gems/`

  2. Grubu hariç tut`rubygems`çakışmaları önlemek için Maven depoları

  3. gem’i yapılandırmaya kaydet`asciidoctorGems`JRuby’nin onu çalışma zamanında yüklemesi için

Bu üç sorumluluk Kotlin’de ayrı ayrı yeniden üretilmelidir.

Üç bölümlü çözüm

Bölüm 1: rubygems için Ivy deposu

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 Uzantı`.gem`hardcode edilmelidir.[ext]`varsayılan olarak şuna çözümler.jar`, bu da bir hataya neden olur`Resource missing`.

NOT: Ivy deposu doğrudan üzerinde bildirilir`project.repositories`ve bir blok içinde değil`repositories { }`çünkü bu bloğun alıcısı değil`RepositoryHandler`Gradle standardı ancak uzantıyla uyumsuz bir grolifant API’si`ivy`Kotlin DSL.

Bölüm 2: asciidoctorGems bağımlılığı

Eklenti`org.asciidoctor.jvm.gems`önce uygulanmalıdır — yapılandırmayı oluşturur`asciidoctorGems`ve görev`asciidoctorGemsPrepare` :

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)
}
1 Uygulama sırası önemlidir:`gems`önce`revealjs`. <2> Niteleyici`@gem`bağımlılık üzerinde doğru uzantıyı zorlar.

Bölüm 3 : settings.gradle.kts

`dependencyResolutionManagement`içinde`settings.gradle.kts`projelerin kendi depolarını bildirmesine izin vermelidir:

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

Bu satır olmadan Gradle, eklentide tanımlanan depoları yok sayar ve gem’lerin çözümlenmesi başarısız olur.

Bytecode’lar aracılığıyla API iç gözlemi

Neden javap?

Eklenti`asciidoctor-gradle-jvm-slides`sürümündedir`4.0.0-alpha.1`. Belgeleri mevcut değil veya eksik. Tek güvenilir kaynak, derlenmiş sınıfların doğrudan incelenmesidir.

Görev hiyerarşisi

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

Sonuç:

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

forkOptions` keşfi

İncelerken`AbstractAsciidoctorTask` :

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

Bulunur:

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 (grolifant) API’si

`javaLauncher`bu görevde mevcut değil. Gerçek API’si`org.ysb33r.grolifant.api.v4.JavaForkOptions`açığa çıkarır:

public void executable(java.lang.Object);  (1)
public void setExecutable(java.lang.Object);
1 Yöntem`executable(Object)`atamayı değiştir`executable = …​`derlenmeyen (`val`yeniden atanamaz).

RevealJSExtension

`revealjs { }`görevin bir yöntemi değil, ancak birproje uzantısı:

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

Şu yolla erişilir:

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

Java toolchain yapılandırması

JavaToolchainService sorunu

`JavaToolchainService`bir proje uzantısı değildir. Aşağıdaki çağrı başarısız oluyor:

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

Doğru API şudur`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 otomatik algılama

Bağlam

Asciidoctor/JRuby eklentisi Java 17 gerektirir. buildSrc içindeki Kotlin 2.0.x, Java 25’i desteklemez (sürüm ayrıştırıcısı şurada çöküyor:`"25.0.2"`). Bu nedenle Gradle daemon’u Java 17 üzerinde çalışmalı ya da Docker kullanılmalıdır.

Strateji

  1. Docker mevcut → konteyner aracılığıyla çalıştırma`eclipse-temurin:17`(varsayılan davranış)

  2. Docker yok + Java 17 → yerel çalıştırma

  3. Docker yok + Java > 17 → açık hata

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."
    )
}

Nihai sonuç

Tüketici derleme betiği

apply<slides.SlidesPlugin>()

Hepsi bu. Tüm sorumluluk eklentiye aittir.

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"

Tuzaklar ve çözümler özeti

Sorun Neden Çözüm

`ruby { gems() }`Kotlin’de mevcut değil

Yalnızca Groovy DSL uzantısı

Üç ayrı mekanizma: Ivy deposu + Maven hariç tutma +asciidoctorGems

Gradle bir`.jar`instead of a`.gem`

`[ext]`çözülür`jar`varsayılan olarak

Sabit kodlamak`.gem`Ivy + niteleyici deseninde`@gem`

ivy { }`içinde derlenmiyor`repositories { }

grolifant alıcısı Kotlin DSL ile uyumsuz

Doğrudan arama`project.repositories.ivy { }`

`javaLauncher`çözülmemiş

Üzerinde bulunmayan özellik`AsciidoctorJRevealJSTask`

setInProcess("JAVA_EXEC")+forkOptions { executable(…​) }

`executable = …​`derlenmiyor

Mülkiyet`val`içinde`JavaForkOptions`obur

Yöntem`executable(Object)`yerine

JavaToolchainService`via ile bulunamıyor`extensions

Bu bir Gradle servisi, uzantı değil.

project.serviceOf<JavaToolchainService>()

`revealjs { }`görevde çözülmemiş

Proje uzantısı, görev yöntemi değil

project.extensions.getByType<RevealJSExtension>()

Build, Java 25 ile çöküyor

Kotlin 2.0.x, iki basamaklı Java sürümlerini ayrıştırmıyor

Otomatik Docker algılama + Java 17’ye geri dönüş

İnceleme yöntemi: javap ile bilinmeyen bir API’yi okumak

İlke

Belge yoksa veya eksikse, bytecode’lar gerçeğin kaynağıdır. javap`JDK’nın dosyaları decompile eden standart aracıdır.class`okunabilir Java imzaları olarak, kaynak kodunu gerektirmeden.

Adım 1: jar dosyasını Gradle önbelleğinde bulun

Gradle tüm bağımlılıklarını içine indirir`~/.gradle/caches/modules-2/files-2.1/`. İlk adım, incelenecek sınıfı içeren jar dosyasını bulmaktır:

find ~/.gradle/caches -name "asciidoctor-gradle-jvm-slides*.jar" 2>/dev/null
Sonuç :
/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

Adım 2: jar dosyasındaki sınıfları listele

Bir sınıfı incelemeden önce, jar dosyasında gerçekten var olup olmadığını kontrol ederiz:

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

Adım 3: bir sınıfı inceleyin

Bu, mevcut tüm sınıfları ortaya çıkarır: AsciidoctorJRevealJSTask, RevealJSExtension, RevealJSOptions, vb.
javap -p -classpath asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar \
org.asciidoctor.gradle.jvm.slides.AsciidoctorJRevealJSTask

Seçenek`-p`özel olanlar da dahil olmak üzere tüm üyeleri görüntüle. Sonuç, kilit satırı hemen gösterir:

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

Adım 4: hiyerarşide yukarı çık

Görev uzatır`AbstractAsciidoctorTask`. O da sırası geldiğinde denetlenir. önce onun jar dosyasını bularak:

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"

İşte orada keşfediyoruz`forkOptions`, JAVA_EXEC, et javaForkOptions tipinde`org.ysb33r.grolifant.api.v4.JavaForkOptions`.

Adım 5: bilinmeyen türleri izle

`JavaForkOptions`bilinmeyen bir grolifant sınıfıdır. Jar’ının yerini tespit ediyoruz:

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

Sonra onu inceleriz:

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

Orada bulunur`executable(java.lang.Object)— çağrılacak doğru yöntem, karşıt olarak`executable = …​`bir özellik olduğu için derlenmeyen`val.

Adım 6: proje uzantılarını kontrol edin

için`revealjs { }`, soru şuydu: bu görevin bir yöntemi mi yoksa bir proje uzantısı mı? Denetimi`AsciidoctorJRevealJSTask` hiçbir yöntem göstermiyor`revealjs`. Ardından incelenir`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;

varlığı`NAME`bunun projede kayıtlı bir uzantı olduğunu onaylar, aracılığıyla erişilebilir`project.extensions.getByType<RevealJSExtension>()`.

Yöntemin özeti

Adım Eylem

1

find ~/.gradle/caches -name "*.jar"— jar dosyasını bulun

2

jar tf jar.jar | grep NomClasse— sınıfın var olduğunu kontrol et

3

javap -p -classpath jar.jar NomCompletClasse— sınıfı incelemek

4

Tanımlamak`extends`ve hiyerarşide yukarı çıkmak

5

Bilinmeyen türleri kendi jar’larında takip et

6

Aramak`NAME`bir proje uzantısını tanımlamak için

Bu yöntem, API’si belgelenmemiş herhangi bir Gradle eklentisi için geçerlidir. veya alfa sürümü artık mevcut belgelerle eşleşmeyen.

Sonraki adım

Bu buildSrc eklentisi, Gradle Plugin Portal veya Maven Local üzerinde yayımlanan bağımsız bir projeye çıkarılacak. Tüketici buildscript’i o zaman şöyle olacak:

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

Et settings.gradle.kts`en aza indirilecek, referans verilmeksizin`foojay-resolver-convention, JDK sağlaması eklentinin kendisi tarafından yönetildiğinden veya önkoşul olarak belgelendiğinden.

İlgili Makaleler