From 20c7891c2fbc73031f60cf8d58d6fad009f75e2a Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Jul 2025 22:54:35 +0200 Subject: [PATCH] add api generation task --- build.gradle.kts | 1 + buildSrc/build.gradle.kts | 11 +++ buildSrc/src/main/kotlin/ApiGenerator.kt | 101 +++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/ApiGenerator.kt diff --git a/build.gradle.kts b/build.gradle.kts index 5681a8cf4..733f6134e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ plugins { id("xyz.jpenilla.run-paper") version "2.3.1" id("io.sentry.jvm.gradle") version "5.7.0" } +apply() version = "3.6.11-1.20.1-1.21.5" diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..c94238170 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + kotlin("jvm") version "2.0.20" +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.ow2.asm:asm:9.8") +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt new file mode 100644 index 000000000..f1bd334d5 --- /dev/null +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -0,0 +1,101 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.jvm.tasks.Jar +import org.objectweb.asm.* +import java.io.File +import java.util.jar.JarFile +import java.util.jar.JarOutputStream + +class ApiGenerator : Plugin { + override fun apply(target: Project) { + target.tasks.register("generateApi", GenerateApiTask::class.java) + } +} + +abstract class GenerateApiTask : DefaultTask() { + init { + group = "iris" + dependsOn("jar") + } + + @InputFile + val inputFile: Provider = project.tasks + .named("jar", Jar::class.java) + .flatMap { it.archiveFile } + .map { it.asFile } + + @OutputFile + val outputFile: Provider = inputFile.map { targetDirectory().resolve(it.name) } + + @TaskAction + fun generate() { + JarFile(inputFile.get()).use { jar -> + JarOutputStream(outputFile.get().outputStream()).use { out -> + jar.stream() + .parallel() + .filter { !it.isDirectory } + .filter { it.name.endsWith(".class") } + .forEach { + val bytes = jar.getInputStream(it).use { input -> + val writer = ClassWriter(ClassWriter.COMPUTE_MAXS) + val visitor = MethodClearingVisitor(writer) + ClassReader(input).accept(visitor, 0) + writer.toByteArray() + } + + synchronized(out) { + out.putNextEntry(it) + out.write(bytes) + out.closeEntry() + } + } + } + } + } + + fun targetDirectory(): File { + val dir = System.getenv("DEPLOY_DIR") ?: return project.layout.buildDirectory.dir("api").get().asFile + return File(dir) + } +} + +private class MethodClearingVisitor( + cv: ClassVisitor +) : ClassVisitor(Opcodes.ASM9, cv) { + + override fun visitMethod( + access: Int, + name: String?, + descriptor: String?, + signature: String?, + exceptions: Array? + ) = ExceptionThrowingMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions)) +} + +private class ExceptionThrowingMethodVisitor( + mv: MethodVisitor +) : MethodVisitor(Opcodes.ASM9, mv) { + + override fun visitCode() { + if (mv == null) return + mv.visitCode() + + mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException") + mv.visitInsn(Opcodes.DUP) + mv.visitLdcInsn("Only API") + mv.visitMethodInsn( + Opcodes.INVOKESPECIAL, + "java/lang/IllegalStateException", + "", "(Ljava/lang/String;)V", false + ) + mv.visitInsn(Opcodes.ATHROW) + + mv.visitMaxs(0, 0) + mv.visitEnd() + } +} \ No newline at end of file