Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/main/kotlin/com/autonomousapps/extension/AbiHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ internal class AndroidLibAnalyzer(
if (!hasAbi) return null

return project.tasks.register<AbiAnalysisTask>("abiAnalysis$taskNameSuffix") {
sourceFiles.setFrom(javaSourceFiles)
sourceFiles.setFrom(kotlinSourceFiles)
sourceFiles.setFrom(groovySourceFiles)
sourceFiles.setFrom(scalaSourceFiles)
exclusions.set(abiExclusions)
output.set(outputPaths.abiAnalysisPath)
abiDump.set(outputPaths.abiDumpPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ internal abstract class JvmAnalyzer(
if (!hasAbi) return null

return project.tasks.register<AbiAnalysisTask>("abiAnalysis$taskNameSuffix") {
sourceFiles.setFrom(sourceSet.sourceCode)
classes.setFrom(sourceSet.classesDirs)
exclusions.set(abiExclusions)
output.set(outputPaths.abiAnalysisPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ internal fun JarFile.classEntries() = Sequence { entries().iterator() }.filter {
}

internal fun getBinaryAPI(jar: JarFile, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature> =
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityFilter)
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityFilter = visibilityFilter)

internal fun getBinaryAPI(
classes: Set<File>,
sourceFiles: Set<File>,
visibilityFilter: (String) -> Boolean = { true }
): List<ClassBinarySignature> =
getBinaryAPI(classes.asSequence().map { it.inputStream() }, visibilityFilter)
getBinaryAPI(classes.asSequence().map { it.inputStream() }, sourceFiles, visibilityFilter)

internal fun getBinaryAPI(
classStreams: Sequence<InputStream>,
sourceFiles: Set<File> = emptySet(),
visibilityFilter: (String) -> Boolean = { true }
): List<ClassBinarySignature> {
val classNodes = classStreams.map {
Expand All @@ -59,6 +61,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 ->
Expand Down Expand Up @@ -118,6 +124,21 @@ internal fun getBinaryAPI(
// Strip out JDK classes
.filterNotToSet { it.startsWith("Ljava/lang") }

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,
Expand All @@ -130,7 +151,7 @@ internal fun getBinaryAPI(
isNotUsedWhenEmpty = metadata.isFileOrMultipartFacade() || isDefaultImpls(metadata),
annotations = visibleAnnotations.annotationTypes(),
invisibleAnnotations = invisibleAnnotations.annotationTypes(),
sourceFile = clazz.sourceFile
sourceFile = sourceFile
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import java.io.File

internal fun computeAbi(
classFiles: Set<File>,
sourceFiles: Set<File>,
exclusions: AbiExclusions,
abiDumpFile: File? = null
): Set<ExplodingAbi> = getBinaryAPI(classFiles).explodedAbi(exclusions, abiDumpFile)
): Set<ExplodingAbi> = getBinaryAPI(classFiles, sourceFiles).explodedAbi(exclusions, abiDumpFile)

private fun List<ClassBinarySignature>.explodedAbi(
exclusions: AbiExclusions,
Expand Down
10 changes: 9 additions & 1 deletion src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,6 +53,7 @@ public abstract class AbiAnalysisTask @Inject constructor(
@TaskAction
public fun action() {
workerExecutor.noIsolation().submit(AbiAnalysisWorkAction::class.java) {
sourceFiles.setFrom([email protected])
// JVM projects
classFiles.setFrom(classes.asFileTree.filterToClassFiles().files)
// Android projects
Expand All @@ -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<String>
public val output: RegularFileProperty
Expand All @@ -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>() ?: AbiExclusions.NONE

val explodingAbi = computeAbi(classFiles, exclusions, outputAbiDump)
val explodingAbi = computeAbi(classFiles, sourceFiles, exclusions, outputAbiDump)

output.bufferWriteJsonSet(explodingAbi)
}
Expand Down