Implement unmeta codes

- cited from io.github.izhangzhihao.unmeta
This commit is contained in:
You Qi
2023-04-27 17:52:24 +08:00
parent 5dcfc59b37
commit e4932d4ef9
8 changed files with 72 additions and 55 deletions

View File

@@ -4,5 +4,5 @@ plugins {
} }
unmeta { unmeta {
message.set("Just trying this gradle plugin...") isEnabled.set(true)
} }

View File

@@ -14,3 +14,5 @@ versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionChe
[libraries] [libraries]
junit = "junit:junit:4.13.2" junit = "junit:junit:4.13.2"
truth = "com.google.truth:truth:1.1.3"
asm = "org.ow2.asm:asm:9.4"

View File

@@ -9,8 +9,10 @@ plugins {
dependencies { dependencies {
implementation(kotlin("stdlib")) implementation(kotlin("stdlib"))
implementation(gradleApi()) implementation(gradleApi())
implementation(libs.asm)
testImplementation(libs.junit) testImplementation(libs.junit)
testImplementation(libs.truth)
} }
java { java {

View File

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

View File

@@ -1,26 +1,16 @@
package com.axzae.unmeta package com.axzae.unmeta
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import javax.inject.Inject import javax.inject.Inject
const val DEFAULT_OUTPUT_FILE = "template-example.txt" const val DEFAULT_IS_ENABLED = true
@Suppress("UnnecessaryAbstractClass") @Suppress("UnnecessaryAbstractClass")
abstract class UnmetaExtension @Inject constructor(project: Project) { abstract class UnmetaExtension @Inject constructor(project: Project) {
private val objects = project.objects private val objects = project.objects
// Example of a property that is mandatory. The task will val isEnabled: Property<Boolean> = objects.property(Boolean::class.java)
// fail if this property is not set as is annotated with @Optional. .convention(DEFAULT_IS_ENABLED)
val message: Property<String> = objects.property(String::class.java)
// Example of a property that is optional.
val tag: Property<String> = 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),
)
} }

View File

@@ -8,14 +8,10 @@ const val TASK_NAME = "unmetaTask"
abstract class UnmetaPlugin : Plugin<Project> { abstract class UnmetaPlugin : Plugin<Project> {
override fun apply(project: Project) { override fun apply(project: Project) {
// Add the 'template' extension object
val extension = project.extensions.create(EXTENSION_NAME, UnmetaExtension::class.java, project) 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) { project.tasks.register(TASK_NAME, UnmetaTask::class.java) {
it.tag.set(extension.tag) it.isEnabled = extension.isEnabled.get()
it.message.set(extension.message)
it.outputFile.set(extension.outputFile)
} }
} }
} }

View File

@@ -1,14 +1,11 @@
package com.axzae.unmeta package com.axzae.unmeta
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.plugins.BasePlugin 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.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() { abstract class UnmetaTask : DefaultTask() {
@@ -17,26 +14,29 @@ abstract class UnmetaTask : DefaultTask() {
group = BasePlugin.BUILD_GROUP group = BasePlugin.BUILD_GROUP
} }
@get:Input
@get:Option(option = "message", description = "A message to be printed in the output file")
abstract val message: Property<String>
@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<String>
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction @TaskAction
fun unmetaAction() { fun unmetaAction() {
val prettyTag = tag.orNull?.let { "[$it]" } ?: "" if (!isEnabled) {
logger.warn("unmeta is disabled")
return
}
logger.lifecycle("$prettyTag message is: ${message.orNull}") logger.info("Start dropping @Metadata & @DebugMetadata from kotlin classes")
logger.lifecycle("$prettyTag tag is: ${tag.orNull}") project.buildDir.listFiles()?.forEach { file -> if (file.isDirectory) dropMetadata(file) }
logger.lifecycle("$prettyTag outputFile is: ${outputFile.orNull}") }
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())
}
}
} }
} }

View File

@@ -1,10 +1,8 @@
package com.axzae.unmeta package com.axzae.unmeta
import com.google.common.truth.Truth.assertThat
import org.gradle.testfixtures.ProjectBuilder import org.gradle.testfixtures.ProjectBuilder
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Test import org.junit.Test
import java.io.File
class UnmetaPluginTest { class UnmetaPluginTest {
@@ -13,7 +11,7 @@ class UnmetaPluginTest {
val project = ProjectBuilder.builder().build() val project = ProjectBuilder.builder().build()
project.pluginManager.apply("com.axzae.unmeta") project.pluginManager.apply("com.axzae.unmeta")
assert(project.tasks.getByName("unmetaTask") is UnmetaTask) assertThat(project.tasks.getByName("unmetaTask")).isInstanceOf(UnmetaTask::class.java)
} }
@Test @Test
@@ -21,24 +19,18 @@ class UnmetaPluginTest {
val project = ProjectBuilder.builder().build() val project = ProjectBuilder.builder().build()
project.pluginManager.apply("com.axzae.unmeta") project.pluginManager.apply("com.axzae.unmeta")
assertNotNull(project.extensions.getByName("unmeta")) assertThat(project.extensions.getByName("unmeta")).isNotNull()
} }
@Test @Test
fun `parameters are passed correctly from extension to task`() { fun `parameters are passed correctly from extension to task`() {
val project = ProjectBuilder.builder().build() val project = ProjectBuilder.builder().build()
project.pluginManager.apply("com.axzae.unmeta") project.pluginManager.apply("com.axzae.unmeta")
val aFile = File(project.projectDir, ".tmp")
(project.extensions.getByName("unmeta") as UnmetaExtension).apply { (project.extensions.getByName("unmeta") as UnmetaExtension).apply {
tag.set("a-sample-tag") isEnabled.set(false)
message.set("just-a-message")
outputFile.set(aFile)
} }
val task = project.tasks.getByName("unmetaTask") as UnmetaTask val task = project.tasks.getByName("unmetaTask") as UnmetaTask
assertThat(task.isEnabled).isFalse()
assertEquals("a-sample-tag", task.tag.get())
assertEquals("just-a-message", task.message.get())
assertEquals(aFile, task.outputFile.get().asFile)
} }
} }