Skip to content

Commit 5776b71

Browse files
committed
feat: allow to exclude ABI by source path
1 parent 3f5b6bb commit 5776b71

File tree

6 files changed

+47
-9
lines changed

6 files changed

+47
-9
lines changed

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

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

100-
// TODO Excluded for now but left as a toe-hold for future use
101-
// fun excludePaths(@Language("RegExp") vararg pathRegexes: String) {
102-
// pathExclusions.addAll(*pathRegexes)
103-
// }
100+
/**
101+
* Exclude any class from ABI analysis that has a source path that matches [pathRegexes], which means
102+
* those classes will not be considered as part of the module's ABI.
103+
*/
104+
public fun excludePaths(@Language("RegExp") vararg pathRegexes: String) {
105+
pathExclusions.addAll(*pathRegexes)
106+
}
104107
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ internal class AndroidLibAnalyzer(
194194
if (!hasAbi) return null
195195

196196
return project.tasks.register<AbiAnalysisTask>("abiAnalysis$taskNameSuffix") {
197+
sourceFiles.setFrom(javaSourceFiles)
198+
sourceFiles.setFrom(kotlinSourceFiles)
199+
sourceFiles.setFrom(groovySourceFiles)
200+
sourceFiles.setFrom(scalaSourceFiles)
197201
exclusions.set(abiExclusions)
198202
output.set(outputPaths.abiAnalysisPath)
199203
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<AbiAnalysisTask>("abiAnalysis$taskNameSuffix") {
66+
sourceFiles.setFrom(sourceSet.sourceCode)
6667
classes.setFrom(sourceSet.classesDirs)
6768
exclusions.set(abiExclusions)
6869
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
@@ -34,16 +34,18 @@ internal fun JarFile.classEntries() = Sequence { entries().iterator() }.filter {
3434
}
3535

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

3939
internal fun getBinaryAPI(
4040
classes: Set<File>,
41+
sourceFiles: Set<File>,
4142
visibilityFilter: (String) -> Boolean = { true }
4243
): List<ClassBinarySignature> =
43-
getBinaryAPI(classes.asSequence().map { it.inputStream() }, visibilityFilter)
44+
getBinaryAPI(classes.asSequence().map { it.inputStream() }, sourceFiles, visibilityFilter)
4445

4546
internal fun getBinaryAPI(
4647
classStreams: Sequence<InputStream>,
48+
sourceFiles: Set<File> = emptySet(),
4749
visibilityFilter: (String) -> Boolean = { true }
4850
): List<ClassBinarySignature> {
4951
val classNodes = classStreams.map {
@@ -59,6 +61,10 @@ internal fun getBinaryAPI(
5961

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

64+
val sourceFilePackageReversedBySourceFile = sourceFiles.associateWith {
65+
it.parentFile.invariantSeparatorsPath.split('/').reversed()
66+
}
67+
6268
return classNodes
6369
.filter { it != moduleInfo }
6470
.map { clazz ->
@@ -118,6 +124,21 @@ internal fun getBinaryAPI(
118124
// Strip out JDK classes
119125
.filterNotToSet { it.startsWith("Ljava/lang") }
120126

127+
val sourceFileName = clazz.sourceFile ?: "${clazz.name.substringAfterLast('/')}."
128+
val clazzPackageReversed = clazz.name.substringBeforeLast('/').split('/').reversed()
129+
val sourceFile = sourceFilePackageReversedBySourceFile
130+
.filterKeys { it.name.startsWith(sourceFileName) }
131+
.maxByOrNull { (_, sourceFilePackageReversed) ->
132+
sourceFilePackageReversed
133+
.asSequence()
134+
.zip(clazzPackageReversed.asSequence())
135+
.takeWhile { (sourceFilePart, clazzPart) -> sourceFilePart == clazzPart }
136+
.count()
137+
}
138+
?.key
139+
?.invariantSeparatorsPath
140+
?: sourceFileName
141+
121142
ClassBinarySignature(
122143
name = name,
123144
superName = superName,
@@ -130,7 +151,7 @@ internal fun getBinaryAPI(
130151
isNotUsedWhenEmpty = metadata.isFileOrMultipartFacade() || isDefaultImpls(metadata),
131152
annotations = visibleAnnotations.annotationTypes(),
132153
invisibleAnnotations = invisibleAnnotations.annotationTypes(),
133-
sourceFile = clazz.sourceFile
154+
sourceFile = sourceFile
134155
)
135156
}
136157
}

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+
sourceFiles.setFrom(this@AbiAnalysisTask.sourceFiles.asFileTree)
5157
// JVM projects
5258
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)