Skip to content

Commit 7a282a3

Browse files
committed
feat: allow to exclude ABI by source path
1 parent 04a70b0 commit 7a282a3

6 files changed

Lines changed: 45 additions & 9 deletions

File tree

src/main/kotlin/com/autonomousapps/extension/AbiHandler.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@ public abstract class ExclusionsHandler @Inject constructor(objects: ObjectFacto
115115
annotationExclusions.addAll(*annotationRegexes)
116116
}
117117

118-
// TODO Excluded for now but left as a toe-hold for future use
119-
// fun excludePaths(@Language("RegExp") vararg pathRegexes: String) {
120-
// pathExclusions.addAll(*pathRegexes)
121-
// }
118+
/**
119+
* Exclude any class from ABI analysis that has a source path that matches [pathRegexes], which means
120+
* those classes will not be considered as part of the module's ABI.
121+
*/
122+
public fun excludePaths(@Language("RegExp") vararg pathRegexes: String) {
123+
pathExclusions.addAll(*pathRegexes)
124+
}
122125
}

src/main/kotlin/com/autonomousapps/internal/analyzer/AndroidProjectAnalyzer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ internal class AndroidLibAnalyzer(
195195
if (!hasAbi) return null
196196

197197
return project.tasks.register("abiAnalysis$taskNameSuffix", AbiAnalysisTask::class.java) {
198+
it.sourceFiles.setFrom(androidSources.sources.java?.all)
199+
it.sourceFiles.setFrom(androidSources.sources.kotlin?.all)
198200
it.exclusions.set(abiExclusions)
199201
it.output.set(outputPaths.abiAnalysisPath)
200202
it.abiDump.set(outputPaths.abiDumpPath)

src/main/kotlin/com/autonomousapps/internal/analyzer/JvmProjectAnalyzer.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ internal abstract class JvmAnalyzer(
6363
if (!hasAbi) return null
6464

6565
return project.tasks.register("abiAnalysis$taskNameSuffix", AbiAnalysisTask::class.java) {
66+
it.sourceFiles.setFrom(sourceSet.sourceCode)
6667
it.classes.setFrom(sourceSet.classesDirs)
6768
it.exclusions.set(abiExclusions)
6869
it.output.set(outputPaths.abiAnalysisPath)

src/main/kotlin/com/autonomousapps/internal/kotlin/PublicApiDump.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@ internal fun JarFile.classEntries() = Sequence { entries().iterator() }.filter {
3535
}
3636

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

4040
internal fun getBinaryAPI(
4141
classes: Set<File>,
42+
sourceFiles: Set<File>,
4243
visibilityFilter: (String) -> Boolean = { true }
4344
): List<ClassBinarySignature> =
44-
getBinaryAPI(classes.asSequence().map { it.inputStream() }, visibilityFilter)
45+
getBinaryAPI(classes.asSequence().map { it.inputStream() }, sourceFiles, visibilityFilter)
4546

4647
internal fun getBinaryAPI(
4748
classStreams: Sequence<InputStream>,
49+
sourceFiles: Set<File> = emptySet(),
4850
visibilityFilter: (String) -> Boolean = { true }
4951
): List<ClassBinarySignature> {
5052
val classNodes = classStreams.map {
@@ -60,6 +62,10 @@ internal fun getBinaryAPI(
6062

6163
val visibilityMapNew = classNodes.readKotlinVisibilities().filterKeys(visibilityFilter)
6264

65+
val sourceFilePackageReversedBySourceFile = sourceFiles.associateWith {
66+
it.parentFile.invariantSeparatorsPath.split('/').reversed()
67+
}
68+
6369
return classNodes
6470
.filter { it != moduleInfo }
6571
.map { clazz ->
@@ -136,6 +142,21 @@ internal fun getBinaryAPI(
136142
extractClassesAsDescriptors(kmFunction.returnType)
137143
}.toSet()
138144

145+
val sourceFileName = clazz.sourceFile ?: "${clazz.name.substringAfterLast('/')}."
146+
val clazzPackageReversed = clazz.name.substringBeforeLast('/').split('/').reversed()
147+
val sourceFile = sourceFilePackageReversedBySourceFile
148+
.filterKeys { it.name.startsWith(sourceFileName) }
149+
.maxByOrNull { (_, sourceFilePackageReversed) ->
150+
sourceFilePackageReversed
151+
.asSequence()
152+
.zip(clazzPackageReversed.asSequence())
153+
.takeWhile { (sourceFilePart, clazzPart) -> sourceFilePart == clazzPart }
154+
.count()
155+
}
156+
?.key
157+
?.invariantSeparatorsPath
158+
?: sourceFileName
159+
139160
ClassBinarySignature(
140161
name = name,
141162
superName = superName,
@@ -148,7 +169,7 @@ internal fun getBinaryAPI(
148169
isNotUsedWhenEmpty = metadata.isFileOrMultipartFacade() || isDefaultImpls(metadata),
149170
annotations = visibleAnnotations.annotationTypes(),
150171
invisibleAnnotations = invisibleAnnotations.annotationTypes(),
151-
sourceFile = clazz.sourceFile
172+
sourceFile = sourceFile
152173
)
153174
}
154175
}

src/main/kotlin/com/autonomousapps/internal/kotlin/abiDependencies.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import java.io.File
1212

1313
internal fun computeAbi(
1414
classFiles: Set<File>,
15+
sourceFiles: Set<File>,
1516
exclusions: AbiExclusions,
1617
abiDumpFile: File? = null
17-
): Set<ExplodingAbi> = getBinaryAPI(classFiles).explodedAbi(exclusions, abiDumpFile)
18+
): Set<ExplodingAbi> = getBinaryAPI(classFiles, sourceFiles).explodedAbi(exclusions, abiDumpFile)
1819

1920
private fun List<ClassBinarySignature>.explodedAbi(
2021
exclusions: AbiExclusions,

src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public abstract class AbiAnalysisTask @Inject constructor(
3030
description = "Produces a report of the ABI of this project"
3131
}
3232

33+
/** Source files from which the class files were generated. May be empty. */
34+
@get:InputFiles
35+
@get:PathSensitive(PathSensitivity.RELATIVE)
36+
public abstract val sourceFiles: ConfigurableFileCollection
37+
3338
/** Class files generated by any JVM source (Java, Kotlin, Groovy, etc.). May be empty. */
3439
@get:Classpath
3540
@get:InputFiles
@@ -48,6 +53,7 @@ public abstract class AbiAnalysisTask @Inject constructor(
4853
@TaskAction
4954
public fun action() {
5055
workerExecutor.noIsolation().submit(AbiAnalysisWorkAction::class.java) {
56+
it.sourceFiles.setFrom(this@AbiAnalysisTask.sourceFiles.asFileTree)
5157
// JVM projects
5258
it.classFiles.setFrom(classes.asFileTree.filterToClassFiles().files)
5359
// Android projects
@@ -60,6 +66,7 @@ public abstract class AbiAnalysisTask @Inject constructor(
6066
}
6167

6268
public interface AbiAnalysisParameters : WorkParameters {
69+
public val sourceFiles: ConfigurableFileCollection
6370
public val classFiles: ConfigurableFileCollection
6471
public val exclusions: Property<String>
6572
public val output: RegularFileProperty
@@ -72,10 +79,11 @@ public abstract class AbiAnalysisTask @Inject constructor(
7279
val output = parameters.output.getAndDelete()
7380
val outputAbiDump = parameters.abiDump.getAndDelete()
7481

82+
val sourceFiles = parameters.sourceFiles.files
7583
val classFiles = parameters.classFiles.files
7684
val exclusions = parameters.exclusions.orNull?.fromJson<AbiExclusions>() ?: AbiExclusions.NONE
7785

78-
val explodingAbi = computeAbi(classFiles, exclusions, outputAbiDump)
86+
val explodingAbi = computeAbi(classFiles, sourceFiles, exclusions, outputAbiDump)
7987

8088
output.bufferWriteJsonSet(explodingAbi)
8189
}

0 commit comments

Comments
 (0)