From e4932d4ef9fefda48678c09f4749b5ca791efa21 Mon Sep 17 00:00:00 2001 From: You Qi Date: Thu, 27 Apr 2023 17:52:24 +0800 Subject: [PATCH] Implement unmeta codes - cited from io.github.izhangzhihao.unmeta --- example/build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 + plugin-build/plugin/build.gradle.kts | 2 + .../com/axzae/unmeta/UnmetaClassVisitor.kt | 35 ++++++++++++++ .../java/com/axzae/unmeta/UnmetaExtension.kt | 16 ++----- .../java/com/axzae/unmeta/UnmetaPlugin.kt | 6 +-- .../main/java/com/axzae/unmeta/UnmetaTask.kt | 46 +++++++++---------- .../java/com/axzae/unmeta/UnmetaPluginTest.kt | 18 ++------ 8 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaClassVisitor.kt diff --git a/example/build.gradle.kts b/example/build.gradle.kts index a9f49fc..7cf943c 100644 --- a/example/build.gradle.kts +++ b/example/build.gradle.kts @@ -4,5 +4,5 @@ plugins { } unmeta { - message.set("Just trying this gradle plugin...") + isEnabled.set(true) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 509dbbd..debbcff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,3 +14,5 @@ versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionChe [libraries] junit = "junit:junit:4.13.2" +truth = "com.google.truth:truth:1.1.3" +asm = "org.ow2.asm:asm:9.4" diff --git a/plugin-build/plugin/build.gradle.kts b/plugin-build/plugin/build.gradle.kts index 43600bd..8369929 100644 --- a/plugin-build/plugin/build.gradle.kts +++ b/plugin-build/plugin/build.gradle.kts @@ -9,8 +9,10 @@ plugins { dependencies { implementation(kotlin("stdlib")) implementation(gradleApi()) + implementation(libs.asm) testImplementation(libs.junit) + testImplementation(libs.truth) } java { diff --git a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaClassVisitor.kt b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaClassVisitor.kt new file mode 100644 index 0000000..6e4bc38 --- /dev/null +++ b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaClassVisitor.kt @@ -0,0 +1,35 @@ +package com.axzae.unmeta + +import org.gradle.api.logging.Logger +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.Opcodes + +class UnmetaClassVisitor( + private val path: String, + classVisitor: ClassVisitor, + private val logger: Logger, +) : ClassVisitor(Opcodes.ASM7, classVisitor), Opcodes { + + var modified = false + + override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? { + return when (desc) { + "Lkotlin/Metadata;" -> { + logger.debug("Removed @Metadata annotation from $path") + modified = true + null + } + + "Lkotlin/coroutines/jvm/internal/DebugMetadata;" -> { + logger.debug("Removed @DebugMetadata annotation from $path") + modified = true + null + } + + else -> { + super.visitAnnotation(desc, visible) + } + } + } +} diff --git a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaExtension.kt b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaExtension.kt index f8fb781..f8ba68d 100644 --- a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaExtension.kt +++ b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaExtension.kt @@ -1,26 +1,16 @@ package com.axzae.unmeta import org.gradle.api.Project -import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import javax.inject.Inject -const val DEFAULT_OUTPUT_FILE = "template-example.txt" +const val DEFAULT_IS_ENABLED = true @Suppress("UnnecessaryAbstractClass") abstract class UnmetaExtension @Inject constructor(project: Project) { private val objects = project.objects - // Example of a property that is mandatory. The task will - // fail if this property is not set as is annotated with @Optional. - val message: Property = objects.property(String::class.java) - - // Example of a property that is optional. - val tag: Property = objects.property(String::class.java) - - // Example of a property with a default set with .convention - val outputFile: RegularFileProperty = objects.fileProperty().convention( - project.layout.buildDirectory.file(DEFAULT_OUTPUT_FILE), - ) + val isEnabled: Property = objects.property(Boolean::class.java) + .convention(DEFAULT_IS_ENABLED) } diff --git a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaPlugin.kt b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaPlugin.kt index 992f159..308f521 100644 --- a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaPlugin.kt +++ b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaPlugin.kt @@ -8,14 +8,10 @@ const val TASK_NAME = "unmetaTask" abstract class UnmetaPlugin : Plugin { override fun apply(project: Project) { - // Add the 'template' extension object val extension = project.extensions.create(EXTENSION_NAME, UnmetaExtension::class.java, project) - // Add a task that uses configuration from the extension object project.tasks.register(TASK_NAME, UnmetaTask::class.java) { - it.tag.set(extension.tag) - it.message.set(extension.message) - it.outputFile.set(extension.outputFile) + it.isEnabled = extension.isEnabled.get() } } } diff --git a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaTask.kt b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaTask.kt index f0b01ca..7469459 100644 --- a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaTask.kt +++ b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaTask.kt @@ -1,14 +1,11 @@ package com.axzae.unmeta import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty import org.gradle.api.plugins.BasePlugin -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.options.Option +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import java.io.File abstract class UnmetaTask : DefaultTask() { @@ -17,26 +14,29 @@ abstract class UnmetaTask : DefaultTask() { group = BasePlugin.BUILD_GROUP } - @get:Input - @get:Option(option = "message", description = "A message to be printed in the output file") - abstract val message: Property - - @get:Input - @get:Option(option = "tag", description = "A Tag to be used for debug and in the output file") - @get:Optional - abstract val tag: Property - - @get:OutputFile - abstract val outputFile: RegularFileProperty - @TaskAction fun unmetaAction() { - val prettyTag = tag.orNull?.let { "[$it]" } ?: "" + if (!isEnabled) { + logger.warn("unmeta is disabled") + return + } - logger.lifecycle("$prettyTag message is: ${message.orNull}") - logger.lifecycle("$prettyTag tag is: ${tag.orNull}") - logger.lifecycle("$prettyTag outputFile is: ${outputFile.orNull}") + logger.info("Start dropping @Metadata & @DebugMetadata from kotlin classes") + project.buildDir.listFiles()?.forEach { file -> if (file.isDirectory) dropMetadata(file) } + } - outputFile.get().asFile.writeText("$prettyTag ${message.get()}") + private fun dropMetadata(directory: File) { + directory.walk() + .filter { it.path.contains("classes") && it.path.endsWith(".class") && it.isFile } + .forEach { + val sourceClassBytes = it.readBytes() + val classReader = ClassReader(sourceClassBytes) + val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) + val unmetaClassVisitor = UnmetaClassVisitor(it.absolutePath, classWriter, logger) + classReader.accept(unmetaClassVisitor, ClassReader.SKIP_DEBUG) + if (unmetaClassVisitor.modified) { + it.writeBytes(classWriter.toByteArray()) + } + } } } diff --git a/plugin-build/plugin/src/test/java/com/axzae/unmeta/UnmetaPluginTest.kt b/plugin-build/plugin/src/test/java/com/axzae/unmeta/UnmetaPluginTest.kt index 9370f28..40a8c5d 100644 --- a/plugin-build/plugin/src/test/java/com/axzae/unmeta/UnmetaPluginTest.kt +++ b/plugin-build/plugin/src/test/java/com/axzae/unmeta/UnmetaPluginTest.kt @@ -1,10 +1,8 @@ package com.axzae.unmeta +import com.google.common.truth.Truth.assertThat import org.gradle.testfixtures.ProjectBuilder -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull import org.junit.Test -import java.io.File class UnmetaPluginTest { @@ -13,7 +11,7 @@ class UnmetaPluginTest { val project = ProjectBuilder.builder().build() project.pluginManager.apply("com.axzae.unmeta") - assert(project.tasks.getByName("unmetaTask") is UnmetaTask) + assertThat(project.tasks.getByName("unmetaTask")).isInstanceOf(UnmetaTask::class.java) } @Test @@ -21,24 +19,18 @@ class UnmetaPluginTest { val project = ProjectBuilder.builder().build() project.pluginManager.apply("com.axzae.unmeta") - assertNotNull(project.extensions.getByName("unmeta")) + assertThat(project.extensions.getByName("unmeta")).isNotNull() } @Test fun `parameters are passed correctly from extension to task`() { val project = ProjectBuilder.builder().build() project.pluginManager.apply("com.axzae.unmeta") - val aFile = File(project.projectDir, ".tmp") (project.extensions.getByName("unmeta") as UnmetaExtension).apply { - tag.set("a-sample-tag") - message.set("just-a-message") - outputFile.set(aFile) + isEnabled.set(false) } val task = project.tasks.getByName("unmetaTask") as UnmetaTask - - assertEquals("a-sample-tag", task.tag.get()) - assertEquals("just-a-message", task.message.get()) - assertEquals(aFile, task.outputFile.get().asFile) + assertThat(task.isEnabled).isFalse() } }