Support android builds via AGP

This commit is contained in:
You Qi
2023-04-27 21:39:08 +08:00
parent e4932d4ef9
commit c733e9af54
8 changed files with 80 additions and 38 deletions

View File

@@ -4,15 +4,17 @@ kotlin = "1.8.21"
ktlintGradle = "11.3.2"
pluginPublish = "1.2.0"
versionCheck = "0.46.0"
androidGradlePlugin = "7.3.0"
[plugins]
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt"}
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle"}
pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish"}
versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionCheck"}
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle" }
pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" }
versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionCheck" }
[libraries]
junit = "junit:junit:4.13.2"
truth = "com.google.truth:truth:1.1.3"
asm = "org.ow2.asm:asm:9.4"
androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }

View File

@@ -10,6 +10,7 @@ dependencies {
implementation(kotlin("stdlib"))
implementation(gradleApi())
implementation(libs.asm)
implementation(libs.androidGradlePlugin)
testImplementation(libs.junit)
testImplementation(libs.truth)

View File

@@ -1,35 +1,25 @@
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,
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
}
var isModified = false
private set
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
return when (descriptor) {
"Lkotlin/coroutines/jvm/internal/DebugMetadata;" -> {
logger.debug("Removed @DebugMetadata annotation from $path")
modified = true
isModified = true
null
}
else -> {
super.visitAnnotation(desc, visible)
}
else -> super.visitAnnotation(descriptor, visible)
}
}
}

View File

@@ -1,10 +1,13 @@
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_IS_ENABLED = true
const val DEFAULT_VERBOSE = false
const val DEFAULT_OUTPUT_FILE = "outputs/logs/unmeta.txt"
@Suppress("UnnecessaryAbstractClass")
abstract class UnmetaExtension @Inject constructor(project: Project) {
@@ -13,4 +16,10 @@ abstract class UnmetaExtension @Inject constructor(project: Project) {
val isEnabled: Property<Boolean> = objects.property(Boolean::class.java)
.convention(DEFAULT_IS_ENABLED)
val verbose: Property<Boolean> = objects.property(Boolean::class.java)
.convention(DEFAULT_VERBOSE)
val outputFile: RegularFileProperty = objects.fileProperty()
.convention(project.layout.buildDirectory.file(DEFAULT_OUTPUT_FILE))
}

View File

@@ -1,17 +1,25 @@
package com.axzae.unmeta
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
const val EXTENSION_NAME = "unmeta"
const val TASK_NAME = "unmetaTask"
import org.gradle.configurationcache.extensions.capitalized
abstract class UnmetaPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create(EXTENSION_NAME, UnmetaExtension::class.java, project)
project.tasks.register(TASK_NAME, UnmetaTask::class.java) {
it.isEnabled = extension.isEnabled.get()
val extension = project.extensions.create("unmeta", UnmetaExtension::class.java, project)
val androidComponents = project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
androidComponents.onVariants(androidComponents.selector().withBuildType("release")) { variant ->
val compileKotlinTaskName = "compile${variant.name.capitalized()}Kotlin"
val unmetaTask = project.tasks.create("unmeta${variant.name.capitalized()}", UnmetaTask::class.java).apply {
isEnabled = extension.isEnabled.get()
verbose.set(extension.verbose.get())
variantName.set(variant.name)
outputFile.set(extension.outputFile)
}
project.afterEvaluate {
project.tasks.findByName(compileKotlinTaskName)?.finalizedBy(unmetaTask)
}
}
}
}

View File

@@ -1,11 +1,17 @@
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.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
import kotlin.system.measureTimeMillis
abstract class UnmetaTask : DefaultTask() {
@@ -14,27 +20,54 @@ abstract class UnmetaTask : DefaultTask() {
group = BasePlugin.BUILD_GROUP
}
@get:Input
@get:Option(option = "variantName", description = "Android variant (flavor + buildType)")
abstract val variantName: Property<String>
@get:Input
@get:Option(option = "verbose", description = "Enable verbose logging")
abstract val verbose: Property<Boolean>
@get:OutputFile
abstract val outputFile: RegularFileProperty
private val fileLogger by lazy { outputFile.get().asFile }
@TaskAction
fun unmetaAction() {
if (!isEnabled) {
logger.warn("unmeta is disabled")
log("unmeta is disabled")
return
}
logger.info("Start dropping @Metadata & @DebugMetadata from kotlin classes")
project.buildDir.listFiles()?.forEach { file -> if (file.isDirectory) dropMetadata(file) }
log("Start dropping @DebugMetadata from kotlin classes")
val executionMs = measureTimeMillis {
val kotlinClassesPath = project.buildDir.absolutePath + "/tmp/kotlin-classes/${variantName.get()}"
File(kotlinClassesPath).listFiles()?.forEach { file ->
if (file.isDirectory) removeAnnotation(file)
}
}
log("Unmeta Total Time: ${executionMs}ms")
}
private fun dropMetadata(directory: File) {
private fun log(message: String) {
when (verbose.get()) {
true -> logger.lifecycle(message)
else -> logger.debug(message)
}
fileLogger.appendText(message + System.lineSeparator(), Charsets.UTF_8)
}
private fun removeAnnotation(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)
val unmetaClassVisitor = UnmetaClassVisitor(it.absolutePath, classWriter)
classReader.accept(unmetaClassVisitor, ClassReader.SKIP_DEBUG)
if (unmetaClassVisitor.modified) {
if (unmetaClassVisitor.isModified) {
log("Removed @DebugMetadata annotation from ${unmetaClassVisitor.path}")
it.writeBytes(classWriter.toByteArray())
}
}

View File

@@ -31,6 +31,4 @@ gradleEnterprise {
}
}
rootProject.name = ("com.axzae.unmeta")
include(":plugin")

View File

@@ -2,6 +2,7 @@ pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
google()
}
}