aboutsummaryrefslogtreecommitdiff
path: root/gradle-plugin
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-10-30 00:00:54 +0100
committerLinnea Gräf <nea@nea.moe>2024-10-30 00:00:54 +0100
commit410f6a0dd1e5288df7c3fc90bd3937a97b2e6385 (patch)
treecc3db28a82d28dd59528aa3580878cf4fff8980a /gradle-plugin
downloadmcautotranslations-410f6a0dd1e5288df7c3fc90bd3937a97b2e6385.tar.gz
mcautotranslations-410f6a0dd1e5288df7c3fc90bd3937a97b2e6385.tar.bz2
mcautotranslations-410f6a0dd1e5288df7c3fc90bd3937a97b2e6385.zip
Init
Diffstat (limited to 'gradle-plugin')
-rw-r--r--gradle-plugin/build.gradle.kts23
-rw-r--r--gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsExtension.kt7
-rw-r--r--gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsGradlePlugin.kt37
-rw-r--r--gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MergeTranslations.kt181
4 files changed, 248 insertions, 0 deletions
diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts
new file mode 100644
index 0000000..caef680
--- /dev/null
+++ b/gradle-plugin/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ kotlin("jvm")
+ id("java-gradle-plugin")
+}
+
+dependencies {
+ implementation(kotlin("gradle-plugin-api"))
+ implementation(kotlin("stdlib"))
+ implementation("com.google.code.gson:gson:2.11.0")
+ implementation("org.ow2.asm:asm:9.7.1")
+ implementation(project(":annotations"))
+}
+
+gradlePlugin {
+ plugins {
+ create("mcAutoTranslations") {
+ id = "moe.nea.mc-auto-translations"
+ displayName = "MC Auto Translation File Generation"
+ implementationClass = "moe.nea.mcautotranslations.gradle.MCAutoTranslationsGradlePlugin"
+ }
+ }
+
+}
diff --git a/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsExtension.kt b/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsExtension.kt
new file mode 100644
index 0000000..7367056
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsExtension.kt
@@ -0,0 +1,7 @@
+package moe.nea.mcautotranslations.gradle
+
+abstract class MCAutoTranslationsExtension {
+
+ fun translationFunction(name: String) {} // TODO: actual config
+
+}
diff --git a/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsGradlePlugin.kt b/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsGradlePlugin.kt
new file mode 100644
index 0000000..d954e64
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MCAutoTranslationsGradlePlugin.kt
@@ -0,0 +1,37 @@
+package moe.nea.mcautotranslations.gradle
+
+import moe.nea.mcautotranslation.`gradle-plugin`.BuildConfig
+import org.gradle.api.Project
+import org.gradle.api.provider.Provider
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin
+import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
+import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
+
+class MCAutoTranslationsGradlePlugin : KotlinCompilerPluginSupportPlugin {
+ override fun apply(target: Project) {
+ target.extensions.create("mcAutoTranslations", MCAutoTranslationsExtension::class.java)
+ }
+
+ override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
+ val project = kotlinCompilation.target.project
+ val extension = project.extensions.getByType(MCAutoTranslationsExtension::class.java)
+ return project.provider {
+ listOf() // TODO: add plugin options from extension in here
+ }
+ }
+
+ override fun getCompilerPluginId(): String {
+ return BuildConfig.KOTLIN_PLUGIN_ID
+ }
+
+ override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact(
+ groupId = BuildConfig.KOTLIN_PLUGIN_GROUP,
+ artifactId = BuildConfig.KOTLIN_PLUGIN_ARTIFACT,
+ version = BuildConfig.KOTLIN_PLUGIN_VERSION,
+ )
+
+ override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean {
+ return true
+ }
+} \ No newline at end of file
diff --git a/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MergeTranslations.kt b/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MergeTranslations.kt
new file mode 100644
index 0000000..44cea35
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/moe/nea/mcautotranslations/gradle/MergeTranslations.kt
@@ -0,0 +1,181 @@
+package moe.nea.mcautotranslations.gradle
+
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import moe.nea.mcautotranslations.annotations.GatheredTranslation
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.Incremental
+import org.gradle.work.InputChanges
+import org.objectweb.asm.AnnotationVisitor
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.Type
+import java.io.File
+
+abstract class CollectTranslations : DefaultTask() {
+ @get:InputFiles
+ @get:Incremental
+ abstract val baseTranslations: ConfigurableFileCollection
+
+ @get:InputFiles
+ @get:Incremental
+ @get:PathSensitive(PathSensitivity.RELATIVE)
+ abstract val classes: ConfigurableFileCollection
+
+ @get:OutputFile
+ abstract val outputFile: RegularFileProperty
+
+ class Translations {
+ var baseTranslation: HashMap<String, HashMap<String, String>> = HashMap()
+ var inlineTranslations: HashMap<String, HashMap<String, String>> = HashMap()
+ }
+
+ companion object {
+ val gson = Gson()
+ val mapType: TypeToken<HashMap<String, String>> = object : TypeToken<HashMap<String, String>>() {}
+ }
+
+ @TaskAction
+ fun execute(inputs: InputChanges) {
+ val baseTranslationsDirty = inputs.getFileChanges(baseTranslations).any()
+ val outFile = outputFile.get().asFile
+ val outputExists = outFile.exists()
+ val canBeIncremental = outputExists && !baseTranslationsDirty
+ val baseTranslations: Translations = if (canBeIncremental) {
+ gson.fromJson(outFile.readText(), Translations::class.java)
+ } else {
+ val t = Translations()
+ baseTranslations.associateTo(t.baseTranslation) {
+ it.toString() to gson.fromJson(outFile.readText(), mapType)
+ }
+ t
+ }
+ val files: List<FileChange> = if (canBeIncremental) {
+ inputs.getFileChanges(classes).map { FileChange(it.file, it.normalizedPath) }
+ } else {
+ buildList {
+ classes.asFileTree.visit {
+ add(FileChange(it.file, it.path))
+ }
+ }
+ }
+ files
+ .asSequence()
+ .filter { checkFile(it.file) }
+ .forEach {
+ val className = getClassName(it.relativePath)
+ if (it.file.exists()) {
+ parseClassAnnotations(it.file)
+ } else {
+ baseTranslations.inlineTranslations.remove(className)
+ }
+ }
+ outFile.writeText(gson.toJson(baseTranslations))
+ }
+
+
+ private class KVVisitor(val map: MutableMap<String, String>) : AnnotationVisitor(Opcodes.ASM9) {
+ var value: String? = null
+ var key: String? = null
+ override fun visit(name: String, value: Any) {
+ when (name) {
+ "key" -> this.key = value as String
+ "value" -> this.value = value as String
+ else -> error("Unknown annotation element $name")
+ }
+ }
+
+ override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor {
+ error("Unknown annotation element $name")
+ }
+
+ override fun visitArray(name: String?): AnnotationVisitor {
+ error("Unknown annotation element $name")
+ }
+
+ override fun visitEnum(name: String?, descriptor: String?, value: String?) {
+ error("Unknown annotation element $name")
+ }
+
+ override fun visitEnd() {
+ map[key ?: error("Missing key")] = value ?: error("Missing value")
+ }
+ }
+
+ private class RepeatableVisitor(val map: MutableMap<String, String>) : AnnotationVisitor(Opcodes.ASM9) {
+ override fun visitArray(name: String?): AnnotationVisitor {
+ require(name == "value") { "Unknown annotation element $name" }
+ foundArray = true
+ return KVVisitor(map)
+ }
+
+ var foundArray = false
+
+ override fun visitEnd() {
+ require(foundArray) { "Missing array" }
+ }
+
+ override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor {
+ error("Unknown annotation element $name")
+ }
+
+ override fun visit(name: String?, value: Any?) {
+ error("Unknown annotation element $name")
+ }
+
+ override fun visitEnum(name: String?, descriptor: String?, value: String?) {
+ error("Unknown annotation element $name")
+ }
+ }
+
+ private class AnnotationCollector(val map: MutableMap<String, String>) : ClassVisitor(Opcodes.ASM9) {
+ override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? {
+ // TODO: inject our own annotations into the classpath
+ if (Type.getType(GatheredTranslation::class.java).descriptor.equals(descriptor)) {
+ return KVVisitor(map)
+ }
+ if (Type.getType(GatheredTranslation.Repeatable::class.java).descriptor.equals(descriptor)) {
+ return RepeatableVisitor(map)
+ }
+ // TODO: remove print log
+ println("Ignoring descriptor $descriptor")
+ return null
+ }
+ }
+
+ private fun parseClassAnnotations(file: File): HashMap<String, String> {
+ val map = HashMap<String, String>()
+ kotlin.runCatching {
+ ClassReader(file.readBytes())
+ .accept(AnnotationCollector(map),
+ ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
+ }.onFailure {
+ throw RuntimeException("Could not parse annotations in $file", it)
+ }
+ return map
+ }
+
+ private fun getClassName(relativePath: String): String {
+ return relativePath.replace("/", ".").removeSuffix(".class")
+ }
+
+ data class FileChange(val file: File, val relativePath: String)
+
+ private fun checkFile(file: File): Boolean {
+ if (file.isDirectory) return false
+ val extension = file.extension
+ if (extension == "kt" || extension == "java")
+ error("Cannot collect translations from source files. Please attach the CollectTranslations task to a compile output")
+ if (extension != "class") return false
+ return true
+ }
+
+} \ No newline at end of file