একটি Asciidoctor RevealJS প্লাগইনকে buildSrc Kotlin-এ মাইগ্রেশন: একটি Groovy API-এর রিভার্স ইঞ্জিনিয়ারিং
Publié le 16 April 2026
প্রেক্ষাপট এবং উদ্দেশ্য
শুরুর বিন্দু
প্রকল্প`slider-gradle`Reveal.js প্রেজেন্টেশন AsciiDoc ফাইল থেকে Gradle প্লাগ এর মাধ্যমে তাইর করে`org.asciidoctor.jvm.revealjs`. প্রধান কাজের কনফিগারেশন`asciidoctorRevealJs`সরাসরি বিল্ডস্ক্রিপ্টের রুটে থাকতেছিল`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
}
}
লক্ষ্য
এই পুরো কনফিগারেশনকে`buildSrc/src/main/kotlin/slides/SlidesPlugin.kt`যাতে গ্রাহক বিল্ডসক্রিপ্টটি নিম্নে সীমিত হয়:
apply<slides.SlidesPlugin>()
প্লাগইনকে সম্পূর্ণরূপে স্বতন্ত্র হওয়া দরকার:它嫩え自己でそのプラグインの依存関係を適用し、リポジトリを設定し、Ruby の gems を管理します。
প্রথম বাধা: `ruby { gems() }
গ্রুভি সিনট্যাক্সিক চিনি যা গোপন করে
লাইন`repositories { ruby { gems() } }`একটি Groovy DSL এক্সটেনশন শুধুমাত্র buildscript-এর নিষ্পাদন Context-এ উপলব্ধ। একটি স্ট্যাটিক Kotlin API নয় যা buildSrc থেকে অ্যাক্সেসযোগ্য।
এটি ডাক করার চেষ্টায়`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() }`আসলে করত ছিলতিনটি আলাদা অপারেশন:
-
একটি আইভি রিপো সংরক্ষণ করুন যা নির্দেশ করে`https://rubygems.org/gems/`
-
গ্রুপটি বাদ দিন`rubygems`মাভেন রিপোজিটরি çatকত্যা এড়াতে
-
কনফিগারেশনে জেম সংরক্ষণ করুন`asciidoctorGems`python
print("��</tool_call> JRuby এটিকে লোড করে রানটাইমে")
এই তিনটি দায়িত্বকে Kotlin-এ পৃথকভাবে পুনরুত্পাদন করা উচিত।
তিন অংশের সমাধান
ভাগ ১ : 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`এটিকে hardcode করা উচিত।[ext]`ডিফল্টে সমাধান করে.jar`, যা একটি ত্রুটি তৈরি করে`Resource missing`. |
| Ivy রেপো সরাসরি ঘোষণা করা হয়েছে`project.repositories`এবং ব্লকে না`repositories { }`কারণ এই ব্লকের রিসিভারটি …`RepositoryHandler`Gradle-এর স্ট্যান্ডার্ড কিন্তু একটি grolifant API এক্সটেনশনের সাথে অসঙ্গত`ivy`কটলিন ডিএসএল। |
অংশ ২ : asciidoctorGems-এর নির্ভরতা
প্লাগইনটি`org.asciidoctor.jvm.gems`প্রথমে প্রয়োগ করা দরকার — এটি কনফিগারেশন তৈরি করে`asciidoctorGems`এবং কাজ`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 | প্রয়োগের ক্রম গুরুত্বপূর্ণ :`gems`আগে`revealjs`. <2> বিশেষণ`@gem`নির্ভরতায় সঠিক এক্সটেনশন প্রযোজ্য করুন। |
অংশ ৩: settings.gradle.kts
`dependencyResolutionManagement`মধ্যে`settings.gradle.kts`প্রকল্পগুলোকে নিজের নিজের সংগ্রহস্থল ঘোষণা করতে অনুমতি দিতে হবে :
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}
এই লাইন না থাকলে, Gradle প্লাগিনে ঘোষিত রেপোজিটারিগুলো উপেক্ষা করে এবং জেম্স সমাধান ব্যর্থ হয়।
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
পরীক্ষণ কর dabei`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`পরিবর্তন করা Jaya না). |
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")
}
}
জাভা টুলচেইন কনফিগারেশন
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
)
}
}
ডকারের স্বয়ংক্রিয় শনাক্ত
প্রেক্ষাপট
Asciidoctor/JRuby প্লাগইন Java 17 দরকার। buildSrc-এ Kotlin 2.0.x Java 25 সমর্থন করে না (ভার্সন পার্সার ক্র্যাশ হয় উপর`"25.0.2"`) অতএব, Gradle ডেমনকে Java 17-এ চালানো উচিত অথবা Docker ব্যবহার করা উচিত।
কৌশল
-
Docker disponible → exécution via container
eclipse-temurin:17(ডিফল্ট আচরণ) -
ডকার অনুপস্থিত + Java 17 → স্থানীয় নিষ্পাদন
-
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 এক্সটেনশন |
তিনটি আলাদা প্রণালী : repo Ivy + বর্জন Maven + |
Gradle একটি খোঁজ করছে`.jar`একের পরিবর্তে`.gem` |
`[ext]`এই সমাধান করে`jar`ডিফল্ট |
হার্ডকোডার`.gem`আইভি প্যাটার্নেতে + বিশেষণ`@gem` |
|
Receiver grolifant DSL Kotlin-এ অযোগ্য |
সরাসরি কল`project.repositories.ivy { }` |
`javaLauncher`অসমাধান |
সুইতে না থাকা সম্পত্তি`AsciidoctorJRevealJSTask` |
|
`executable = …`কম্পাইল হয় না |
সম্পত্তি`val`ভিতরে`JavaForkOptions`গ্রোলিফান্ত |
পদ্ধতি`executable(Object)`জায়গায় |
|
এটি একটি Gradle পরিষেবা, এক্সটেনশন নয়। |
|
`revealjs { }`কর্মে সমাধানকৃত নয় |
প্রকল্পের_বিস্তৃতি, কাজের পদ্ধতি না |
|
জাভা ২৫ দিয়ে বিল্ড crash হয় |
Kotlin 2.0.x দুই অঙ্কের Java সংস্করণ parse করে না |
স্বয়ংক্রিয় ডকার ডিটেকশন + ফallback জাভা ১৭ |
অনুসন্ধানের পদ্ধতি : জাভাপ দিয়ে অজানা একটি API পড়া
নীতি
যদি ডকুমেন্টেশন অনুপস্থিত বা অসম্পূর্ণ হয়, তবে বাইটকোডগুলি সত্যের উৎস। javap`এইটি JDK-এ স্ট্যান্ডার্ড টুল যা ফাইলগুলোকে ডিকম্পাইল করে.class`পড়া যায় এমন জাভা স্বাক্ষর, কোড সোর্স প্রয়োজন ছাড়াই.
ধাপ ১: গ্রেডল ক্যাশে মধ্যে jar ফাইলটি খুঁজুন
গ্রেডল তার সব নির্ভরতা ডাউনলোড করে মাঝে`~/.gradle/caches/modules-2/files-2.1/`. প্রথম ধাপ হলো যে জারটি পরিদর্শন করতে দরকারি ক্লাসটিকে ধারণ করেছে তা খুঁজে বের করা।
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 tf asciidoctor-gradle-jvm-slides-4.0.0-alpha.1.jar | grep -i "RevealJS\|revealjs"
ধাপ ৩ : একটি শ্রেণি পরীক্ষা করুন
AsciidoctorJRevealJSTask, RevealJSExtension, RevealJSOptions, এটসেটরা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
ধাপ ৪ : শ্রেণীবিন্যাসে উঠা
কাজটি প্রসারিত করে`AbstractAsciidoctorTask`. এটি অনুক্রমে পরীক্ষা করা হয় প্রথমে তার জারটি লোকালাইজ করলে :
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
তারপর এটিকে পরিদর্শন করা Hoy :
javap -p -classpath grolifant40-legacy-api-2.0.0-alpha.6.jar \
org.ysb33r.grolifant.api.v4.JavaForkOptions
এখানে পাওয়া যায়`executable(java.lang.Object)— আহ্বানের সঠিক পদ্ধতি, বিপরীতভাবে`executable = …`যা কম্পাইল হয় না কারণ এটি একটি প্রপার্টি`val.
ধাপ ৬: প্রকল্পের এক্সটেনশন যাচাই করুন
জন্য`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 |
|
2 |
|
3 |
|
4 |
শনাক্তকারী`extends`এবং শ্রেণি উঠা |
5 |
অজানা টাইপগুলোকে তাদের নিজস্ব জারে অনুসরণ করুন |
6 |
খুঁ�া করা`NAME`প্রকল্পের এক্সটেনশন চিহ্নিত করতে |
এই পদ্ধতি ডকুমেন্ট করা হয়নি এমন যেকোনো Gradle প্লাগিনের API-এর জন্য প্রযোজ্য। অথবা যেটির অ্যালফা সংস্করণ বর্তমান ডকুমেন্টেশনের সাথে মেলে না।
আগামী ধাপ
এই buildSrc প্লাগিনটি স্বাধীন প্রকল্পে নিষ্কাশন করা হবে যা Gradle Plugin Portal বা Maven Local-এ প্রকাশিত। উপভোক্তা বিল্ড স্ক্রিপ্ট তখন :
plugins { id("slides") version "1.0.0" }
Et settings.gradle.kts`তার সর্বনিম্নে হ্রাস করা হবে যাতে কোনো উল্লেখ চেয়ে`foojay-resolver-convention, JDK-এর প্রোভিশনিং প্লাগইন নিজেই পরিচালিত হয় বা পূর্বশর্ত হিসেবে নথিভুক্ত।