com.autonomousapps.tasks.FindUnusedProcsTask.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dependency-analysis-gradle-plugin Show documentation
Show all versions of dependency-analysis-gradle-plugin Show documentation
Analyzes dependency usage in Android and JVM projects
package com.autonomousapps.tasks
import com.autonomousapps.TASK_GROUP_DEP
import com.autonomousapps.internal.AnnotationProcessor
import com.autonomousapps.internal.Imports
import com.autonomousapps.internal.ProcClassVisitor
import com.autonomousapps.internal.asm.ClassReader
import com.autonomousapps.internal.utils.*
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import java.io.File
import java.util.zip.ZipFile
import kotlin.text.RegexOption.IGNORE_CASE
@CacheableTask
abstract class FindUnusedProcsTask : DefaultTask() {
init {
group = TASK_GROUP_DEP
description = "Produces a report of unused annotation processors"
}
/**
* This project's compiled source.
*/
@get:Optional
@get:Classpath
abstract val jar: RegularFileProperty
/**
* This project's compiled source. Class files generated by Kotlin source. May be empty.
*/
@get:Classpath
@get:InputFiles
abstract val kotlinClasses: ConfigurableFileCollection
/**
* This project's compiled source. Class files generated by Java source. May be empty.
*/
@get:Classpath
@get:InputFiles
abstract val javaClasses: ConfigurableFileCollection
/**
* All the imports in the Java and Kotlin source in this project. Needed for annotation processors
* that support annotation types that have
* [RetentionPolicy.SOURCE][java.lang.annotation.RetentionPolicy.SOURCE] or
* [AnnotationRetention.SOURCE] retention.
*/
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:InputFile
abstract val imports: RegularFileProperty
/**
* Annotation processors that are present in the project (on `kapt` or `annotationProcessor`
* configurations).
*/
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val annotationProcessorsProperty: RegularFileProperty
@get:OutputFile
abstract val output: RegularFileProperty
private val annotationProcessors by lazy {
annotationProcessorsProperty.fromJsonSet()
}
@TaskAction fun action() {
// Output
val outputFile = output.getAndDelete()
// Inputs
val classFiles = javaClasses.plus(kotlinClasses)
val jarFile = jar.orNull?.asFile
val a = findUsedProcsInClassFiles(classFiles)
val b = jarFile?.let { findUsedProcsInJar(it) } ?: emptySet()
val c = findUsedProcsInImports()
val usedProcs = a + b + c
val unusedProcs = annotationProcessors - usedProcs
outputFile.writeText(unusedProcs.toJson())
}
private fun findUsedProcsInClassFiles(classFiles: FileCollection): Set {
return classFiles
.filterToClassFiles()
.flatMapToSet { classFile ->
val visitor = ProcClassVisitor(logger, annotationProcessors)
val reader = classFile.inputStream().use { ClassReader(it.readBytes()) }
reader.accept(visitor, 0)
visitor.usedProcs()
}
}
private fun findUsedProcsInJar(jarFile: File): Set {
val zip = ZipFile(jarFile)
return zip.asClassFiles()
.flatMapToSet { classEntry ->
val visitor = ProcClassVisitor(logger, annotationProcessors)
val reader = zip.getInputStream(classEntry).use { ClassReader(it.readBytes()) }
reader.accept(visitor, 0)
visitor.usedProcs()
}
}
private fun findUsedProcsInImports(): Set {
val imports = imports.fromJsonList().flatten()
val usedProcs = mutableSetOf()
for (proc in annotationProcessors) {
if (imports.containsSupportedType(proc.supportedAnnotationTypes)) {
usedProcs.add(proc)
}
}
return usedProcs
}
private fun Set.containsSupportedType(supportedTypes: Set): Boolean {
// convert ["lombok.*"] to [lombok.(package) regex]
val stars = supportedTypes
.filter { it.endsWith("*") }
.map { it.replace(".", "\\.") }
.map { it.replace("*", JAVA_SUB_PACKAGE) }
.map { it.toRegex(setOf(IGNORE_CASE)) }
for (import in this) {
if (supportedTypes.contains(import) || stars.any { it.matches(import) }) {
return true
}
}
return false
}
private fun List.flatten(): Set {
val destination = mutableSetOf()
for (i in this) {
destination.addAll(i.imports)
}
return destination
}
}