diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index debbcff..320bce5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/plugin-build/plugin/build.gradle.kts b/plugin-build/plugin/build.gradle.kts index 8369929..6d35f07 100644 --- a/plugin-build/plugin/build.gradle.kts +++ b/plugin-build/plugin/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(kotlin("stdlib")) implementation(gradleApi()) implementation(libs.asm) + implementation(libs.androidGradlePlugin) testImplementation(libs.junit) testImplementation(libs.truth) 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 index 6e4bc38..beeed6b 100644 --- a/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaClassVisitor.kt +++ b/plugin-build/plugin/src/main/java/com/axzae/unmeta/UnmetaClassVisitor.kt @@ -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) } } } 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 f8ba68d..fc5d2ae 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,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 = objects.property(Boolean::class.java) .convention(DEFAULT_IS_ENABLED) + + val verbose: Property = objects.property(Boolean::class.java) + .convention(DEFAULT_VERBOSE) + + val outputFile: RegularFileProperty = objects.fileProperty() + .convention(project.layout.buildDirectory.file(DEFAULT_OUTPUT_FILE)) } 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 308f521..e058158 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 @@ -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 { 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) + } } } } 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 7469459..0bfea51 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,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 + + @get:Input + @get:Option(option = "verbose", description = "Enable verbose logging") + abstract val verbose: Property + + @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()) } } diff --git a/plugin-build/settings.gradle.kts b/plugin-build/settings.gradle.kts index b145c2d..2613f17 100644 --- a/plugin-build/settings.gradle.kts +++ b/plugin-build/settings.gradle.kts @@ -31,6 +31,4 @@ gradleEnterprise { } } -rootProject.name = ("com.axzae.unmeta") - include(":plugin") diff --git a/settings.gradle.kts b/settings.gradle.kts index 0b764f1..fecad8a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ pluginManagement { repositories { gradlePluginPortal() mavenCentral() + google() } }