mirror of
https://github.com/fankes/unmeta-gradle-plugin.git
synced 2025-09-05 18:45:17 +08:00
Support android builds via AGP
This commit is contained in:
@@ -4,15 +4,17 @@ kotlin = "1.8.21"
|
|||||||
ktlintGradle = "11.3.2"
|
ktlintGradle = "11.3.2"
|
||||||
pluginPublish = "1.2.0"
|
pluginPublish = "1.2.0"
|
||||||
versionCheck = "0.46.0"
|
versionCheck = "0.46.0"
|
||||||
|
androidGradlePlugin = "7.3.0"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt"}
|
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
|
||||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle"}
|
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle" }
|
||||||
pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish"}
|
pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" }
|
||||||
versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionCheck"}
|
versionCheck = { id = "com.github.ben-manes.versions", version.ref = "versionCheck" }
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
junit = "junit:junit:4.13.2"
|
junit = "junit:junit:4.13.2"
|
||||||
truth = "com.google.truth:truth:1.1.3"
|
truth = "com.google.truth:truth:1.1.3"
|
||||||
asm = "org.ow2.asm:asm:9.4"
|
asm = "org.ow2.asm:asm:9.4"
|
||||||
|
androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
|
||||||
|
@@ -10,6 +10,7 @@ dependencies {
|
|||||||
implementation(kotlin("stdlib"))
|
implementation(kotlin("stdlib"))
|
||||||
implementation(gradleApi())
|
implementation(gradleApi())
|
||||||
implementation(libs.asm)
|
implementation(libs.asm)
|
||||||
|
implementation(libs.androidGradlePlugin)
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
testImplementation(libs.truth)
|
testImplementation(libs.truth)
|
||||||
|
@@ -1,35 +1,25 @@
|
|||||||
package com.axzae.unmeta
|
package com.axzae.unmeta
|
||||||
|
|
||||||
import org.gradle.api.logging.Logger
|
|
||||||
import org.objectweb.asm.AnnotationVisitor
|
import org.objectweb.asm.AnnotationVisitor
|
||||||
import org.objectweb.asm.ClassVisitor
|
import org.objectweb.asm.ClassVisitor
|
||||||
import org.objectweb.asm.Opcodes
|
import org.objectweb.asm.Opcodes
|
||||||
|
|
||||||
class UnmetaClassVisitor(
|
class UnmetaClassVisitor(
|
||||||
private val path: String,
|
val path: String,
|
||||||
classVisitor: ClassVisitor,
|
classVisitor: ClassVisitor,
|
||||||
private val logger: Logger,
|
|
||||||
) : ClassVisitor(Opcodes.ASM7, classVisitor), Opcodes {
|
) : ClassVisitor(Opcodes.ASM7, classVisitor), Opcodes {
|
||||||
|
|
||||||
var modified = false
|
var isModified = false
|
||||||
|
private set
|
||||||
override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? {
|
|
||||||
return when (desc) {
|
|
||||||
"Lkotlin/Metadata;" -> {
|
|
||||||
logger.debug("Removed @Metadata annotation from $path")
|
|
||||||
modified = true
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
|
||||||
|
return when (descriptor) {
|
||||||
"Lkotlin/coroutines/jvm/internal/DebugMetadata;" -> {
|
"Lkotlin/coroutines/jvm/internal/DebugMetadata;" -> {
|
||||||
logger.debug("Removed @DebugMetadata annotation from $path")
|
isModified = true
|
||||||
modified = true
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> super.visitAnnotation(descriptor, visible)
|
||||||
super.visitAnnotation(desc, visible)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
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_IS_ENABLED = true
|
const val DEFAULT_IS_ENABLED = true
|
||||||
|
const val DEFAULT_VERBOSE = false
|
||||||
|
const val DEFAULT_OUTPUT_FILE = "outputs/logs/unmeta.txt"
|
||||||
|
|
||||||
@Suppress("UnnecessaryAbstractClass")
|
@Suppress("UnnecessaryAbstractClass")
|
||||||
abstract class UnmetaExtension @Inject constructor(project: Project) {
|
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)
|
val isEnabled: Property<Boolean> = objects.property(Boolean::class.java)
|
||||||
.convention(DEFAULT_IS_ENABLED)
|
.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))
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,25 @@
|
|||||||
package com.axzae.unmeta
|
package com.axzae.unmeta
|
||||||
|
|
||||||
|
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.configurationcache.extensions.capitalized
|
||||||
const val EXTENSION_NAME = "unmeta"
|
|
||||||
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) {
|
||||||
val extension = project.extensions.create(EXTENSION_NAME, UnmetaExtension::class.java, project)
|
val extension = project.extensions.create("unmeta", UnmetaExtension::class.java, project)
|
||||||
|
val androidComponents = project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
|
||||||
project.tasks.register(TASK_NAME, UnmetaTask::class.java) {
|
androidComponents.onVariants(androidComponents.selector().withBuildType("release")) { variant ->
|
||||||
it.isEnabled = extension.isEnabled.get()
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
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.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.ClassReader
|
||||||
import org.objectweb.asm.ClassWriter
|
import org.objectweb.asm.ClassWriter
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
abstract class UnmetaTask : DefaultTask() {
|
abstract class UnmetaTask : DefaultTask() {
|
||||||
|
|
||||||
@@ -14,27 +20,54 @@ abstract class UnmetaTask : DefaultTask() {
|
|||||||
group = BasePlugin.BUILD_GROUP
|
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
|
@TaskAction
|
||||||
fun unmetaAction() {
|
fun unmetaAction() {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
logger.warn("unmeta is disabled")
|
log("unmeta is disabled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log("Start dropping @DebugMetadata from kotlin classes")
|
||||||
logger.info("Start dropping @Metadata & @DebugMetadata from kotlin classes")
|
val executionMs = measureTimeMillis {
|
||||||
project.buildDir.listFiles()?.forEach { file -> if (file.isDirectory) dropMetadata(file) }
|
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()
|
directory.walk()
|
||||||
.filter { it.path.contains("classes") && it.path.endsWith(".class") && it.isFile }
|
.filter { it.path.contains("classes") && it.path.endsWith(".class") && it.isFile }
|
||||||
.forEach {
|
.forEach {
|
||||||
val sourceClassBytes = it.readBytes()
|
val sourceClassBytes = it.readBytes()
|
||||||
val classReader = ClassReader(sourceClassBytes)
|
val classReader = ClassReader(sourceClassBytes)
|
||||||
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
|
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)
|
classReader.accept(unmetaClassVisitor, ClassReader.SKIP_DEBUG)
|
||||||
if (unmetaClassVisitor.modified) {
|
if (unmetaClassVisitor.isModified) {
|
||||||
|
log("Removed @DebugMetadata annotation from ${unmetaClassVisitor.path}")
|
||||||
it.writeBytes(classWriter.toByteArray())
|
it.writeBytes(classWriter.toByteArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,4 @@ gradleEnterprise {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = ("com.axzae.unmeta")
|
|
||||||
|
|
||||||
include(":plugin")
|
include(":plugin")
|
||||||
|
@@ -2,6 +2,7 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user