From 7a282a3229454806c2fe68e62ec0714ed14ece46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Tue, 9 Apr 2024 01:04:47 +0200 Subject: [PATCH] feat: allow to exclude ABI by source path --- .../autonomousapps/extension/AbiHandler.kt | 11 +++++--- .../analyzer/AndroidProjectAnalyzer.kt | 2 ++ .../internal/analyzer/JvmProjectAnalyzer.kt | 1 + .../internal/kotlin/PublicApiDump.kt | 27 ++++++++++++++++--- .../internal/kotlin/abiDependencies.kt | 3 ++- .../autonomousapps/tasks/AbiAnalysisTask.kt | 10 ++++++- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/autonomousapps/extension/AbiHandler.kt b/src/main/kotlin/com/autonomousapps/extension/AbiHandler.kt index 53784c891..06d586cca 100644 --- a/src/main/kotlin/com/autonomousapps/extension/AbiHandler.kt +++ b/src/main/kotlin/com/autonomousapps/extension/AbiHandler.kt @@ -115,8 +115,11 @@ public abstract class ExclusionsHandler @Inject constructor(objects: ObjectFacto annotationExclusions.addAll(*annotationRegexes) } - // TODO Excluded for now but left as a toe-hold for future use -// fun excludePaths(@Language("RegExp") vararg pathRegexes: String) { -// pathExclusions.addAll(*pathRegexes) -// } + /** + * Exclude any class from ABI analysis that has a source path that matches [pathRegexes], which means + * those classes will not be considered as part of the module's ABI. + */ + public fun excludePaths(@Language("RegExp") vararg pathRegexes: String) { + pathExclusions.addAll(*pathRegexes) + } } diff --git a/src/main/kotlin/com/autonomousapps/internal/analyzer/AndroidProjectAnalyzer.kt b/src/main/kotlin/com/autonomousapps/internal/analyzer/AndroidProjectAnalyzer.kt index 7910beee6..37c96647b 100644 --- a/src/main/kotlin/com/autonomousapps/internal/analyzer/AndroidProjectAnalyzer.kt +++ b/src/main/kotlin/com/autonomousapps/internal/analyzer/AndroidProjectAnalyzer.kt @@ -195,6 +195,8 @@ internal class AndroidLibAnalyzer( if (!hasAbi) return null return project.tasks.register("abiAnalysis$taskNameSuffix", AbiAnalysisTask::class.java) { + it.sourceFiles.setFrom(androidSources.sources.java?.all) + it.sourceFiles.setFrom(androidSources.sources.kotlin?.all) it.exclusions.set(abiExclusions) it.output.set(outputPaths.abiAnalysisPath) it.abiDump.set(outputPaths.abiDumpPath) diff --git a/src/main/kotlin/com/autonomousapps/internal/analyzer/JvmProjectAnalyzer.kt b/src/main/kotlin/com/autonomousapps/internal/analyzer/JvmProjectAnalyzer.kt index e49d08d68..1471b6a40 100644 --- a/src/main/kotlin/com/autonomousapps/internal/analyzer/JvmProjectAnalyzer.kt +++ b/src/main/kotlin/com/autonomousapps/internal/analyzer/JvmProjectAnalyzer.kt @@ -63,6 +63,7 @@ internal abstract class JvmAnalyzer( if (!hasAbi) return null return project.tasks.register("abiAnalysis$taskNameSuffix", AbiAnalysisTask::class.java) { + it.sourceFiles.setFrom(sourceSet.sourceCode) it.classes.setFrom(sourceSet.classesDirs) it.exclusions.set(abiExclusions) it.output.set(outputPaths.abiAnalysisPath) diff --git a/src/main/kotlin/com/autonomousapps/internal/kotlin/PublicApiDump.kt b/src/main/kotlin/com/autonomousapps/internal/kotlin/PublicApiDump.kt index 176c14232..650605eb9 100644 --- a/src/main/kotlin/com/autonomousapps/internal/kotlin/PublicApiDump.kt +++ b/src/main/kotlin/com/autonomousapps/internal/kotlin/PublicApiDump.kt @@ -35,16 +35,18 @@ internal fun JarFile.classEntries() = Sequence { entries().iterator() }.filter { } internal fun getBinaryAPI(jar: JarFile, visibilityFilter: (String) -> Boolean = { true }): List = - getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityFilter) + getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityFilter = visibilityFilter) internal fun getBinaryAPI( classes: Set, + sourceFiles: Set, visibilityFilter: (String) -> Boolean = { true } ): List = - getBinaryAPI(classes.asSequence().map { it.inputStream() }, visibilityFilter) + getBinaryAPI(classes.asSequence().map { it.inputStream() }, sourceFiles, visibilityFilter) internal fun getBinaryAPI( classStreams: Sequence, + sourceFiles: Set = emptySet(), visibilityFilter: (String) -> Boolean = { true } ): List { val classNodes = classStreams.map { @@ -60,6 +62,10 @@ internal fun getBinaryAPI( val visibilityMapNew = classNodes.readKotlinVisibilities().filterKeys(visibilityFilter) + val sourceFilePackageReversedBySourceFile = sourceFiles.associateWith { + it.parentFile.invariantSeparatorsPath.split('/').reversed() + } + return classNodes .filter { it != moduleInfo } .map { clazz -> @@ -136,6 +142,21 @@ internal fun getBinaryAPI( extractClassesAsDescriptors(kmFunction.returnType) }.toSet() + val sourceFileName = clazz.sourceFile ?: "${clazz.name.substringAfterLast('/')}." + val clazzPackageReversed = clazz.name.substringBeforeLast('/').split('/').reversed() + val sourceFile = sourceFilePackageReversedBySourceFile + .filterKeys { it.name.startsWith(sourceFileName) } + .maxByOrNull { (_, sourceFilePackageReversed) -> + sourceFilePackageReversed + .asSequence() + .zip(clazzPackageReversed.asSequence()) + .takeWhile { (sourceFilePart, clazzPart) -> sourceFilePart == clazzPart } + .count() + } + ?.key + ?.invariantSeparatorsPath + ?: sourceFileName + ClassBinarySignature( name = name, superName = superName, @@ -148,7 +169,7 @@ internal fun getBinaryAPI( isNotUsedWhenEmpty = metadata.isFileOrMultipartFacade() || isDefaultImpls(metadata), annotations = visibleAnnotations.annotationTypes(), invisibleAnnotations = invisibleAnnotations.annotationTypes(), - sourceFile = clazz.sourceFile + sourceFile = sourceFile ) } } diff --git a/src/main/kotlin/com/autonomousapps/internal/kotlin/abiDependencies.kt b/src/main/kotlin/com/autonomousapps/internal/kotlin/abiDependencies.kt index da8ed0cf4..451e6e954 100644 --- a/src/main/kotlin/com/autonomousapps/internal/kotlin/abiDependencies.kt +++ b/src/main/kotlin/com/autonomousapps/internal/kotlin/abiDependencies.kt @@ -12,9 +12,10 @@ import java.io.File internal fun computeAbi( classFiles: Set, + sourceFiles: Set, exclusions: AbiExclusions, abiDumpFile: File? = null -): Set = getBinaryAPI(classFiles).explodedAbi(exclusions, abiDumpFile) +): Set = getBinaryAPI(classFiles, sourceFiles).explodedAbi(exclusions, abiDumpFile) private fun List.explodedAbi( exclusions: AbiExclusions, diff --git a/src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt b/src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt index b971aad28..682098875 100644 --- a/src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt +++ b/src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt @@ -30,6 +30,11 @@ public abstract class AbiAnalysisTask @Inject constructor( description = "Produces a report of the ABI of this project" } + /** Source files from which the class files were generated. May be empty. */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + public abstract val sourceFiles: ConfigurableFileCollection + /** Class files generated by any JVM source (Java, Kotlin, Groovy, etc.). May be empty. */ @get:Classpath @get:InputFiles @@ -48,6 +53,7 @@ public abstract class AbiAnalysisTask @Inject constructor( @TaskAction public fun action() { workerExecutor.noIsolation().submit(AbiAnalysisWorkAction::class.java) { + it.sourceFiles.setFrom(this@AbiAnalysisTask.sourceFiles.asFileTree) // JVM projects it.classFiles.setFrom(classes.asFileTree.filterToClassFiles().files) // Android projects @@ -60,6 +66,7 @@ public abstract class AbiAnalysisTask @Inject constructor( } public interface AbiAnalysisParameters : WorkParameters { + public val sourceFiles: ConfigurableFileCollection public val classFiles: ConfigurableFileCollection public val exclusions: Property public val output: RegularFileProperty @@ -72,10 +79,11 @@ public abstract class AbiAnalysisTask @Inject constructor( val output = parameters.output.getAndDelete() val outputAbiDump = parameters.abiDump.getAndDelete() + val sourceFiles = parameters.sourceFiles.files val classFiles = parameters.classFiles.files val exclusions = parameters.exclusions.orNull?.fromJson() ?: AbiExclusions.NONE - val explodingAbi = computeAbi(classFiles, exclusions, outputAbiDump) + val explodingAbi = computeAbi(classFiles, sourceFiles, exclusions, outputAbiDump) output.bufferWriteJsonSet(explodingAbi) }