Skip to content

Commit b8a2fc2

Browse files
shanshinSpace Team
authored andcommitted
[ABI Validation] Added support for dumping jar files in ABI tools
The ability to dump JVM classes from jar files has been added to the API of ABI tools. #KT-80313 Fixed Co-authored-by: Filipp Zhinkin <[email protected]> Merge-request: KT-MR-23137 Merged-by: Sergei Shanshin <[email protected]>
1 parent 69a11c8 commit b8a2fc2

File tree

11 files changed

+78
-25
lines changed

11 files changed

+78
-25
lines changed

libraries/tools/abi-validation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/AbiFilters.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ package org.jetbrains.kotlin.abi.tools.api
1717
*
1818
* A declaration passes the inclusion filters if there is no inclusion rules, or it matches any inclusion rule, or at least one of its members (actual for class declaration) matches any inclusion rule.
1919
*
20-
* @since 2.1.20
20+
* @since 2.2.0
2121
*/
2222
public class AbiFilters(
2323
/**

libraries/tools/abi-validation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/AbiToolsFactory.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.abi.tools.api
88
/**
99
* An abstract factory for obtaining an instance of [AbiToolsInterface] - main class for using the capabilities of ABI Validation tool.
1010
*
11-
* @since 2.1.20
11+
* @since 2.2.0
1212
*/
1313
public interface AbiToolsFactory {
1414
/**

libraries/tools/abi-validation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/AbiToolsInterface.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import java.io.File
1111
/**
1212
* All features of Kotlin ABI Validation tool.
1313
*
14-
* @since 2.1.20
14+
* @since 2.2.0
1515
*/
1616
public interface AbiToolsInterface {
1717
/**

libraries/tools/abi-validation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/v2/AbiToolsV2.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,25 @@ import java.io.File
1212
* A set of features for working with legacy format dumps,
1313
* used in previous [Binary Compatibility Validator plugin](https://github.com/Kotlin/binary-compatibility-validator).
1414
*
15-
* @since 2.1.20
15+
* @since 2.2.0
1616
*/
1717
public interface AbiToolsV2 {
1818
/**
19-
* Print ABI dump for JVM class-files into specified [appendable].
19+
* Print ABI dump for JVM from [inputFiles] into specified [appendable].
20+
* It is possible to pass class-files or jar files in [inputFiles].
21+
*
22+
* To control which declarations are passed to the dump, [filters] could be used. By default, no filters will be applied.
23+
*
24+
* A class declaration with internal visibility will be printed as public if [internalDeclarationsAsPublic] returns `true` for its name.
2025
*/
21-
public fun <T : Appendable> printJvmDump(appendable: T, classfiles: Iterable<File>, filters: AbiFilters)
26+
public fun <T : Appendable> printJvmDump(
27+
appendable: T,
28+
inputFiles: Iterable<File>,
29+
filters: AbiFilters
30+
)
2231

2332
/**
24-
* Create empty KLib dump without any declarations and targets.
33+
* Create an empty KLib dump without any declarations and targets.
2534
*/
2635
public fun createKlibDump(): KlibDump
2736

@@ -43,12 +52,14 @@ public interface AbiToolsV2 {
4352
public fun loadKlibDump(dump: CharSequence): KlibDump
4453

4554
/**
46-
* Get an ABI from a zipped or unpacked KLib specified in [klib] with specified [target].
55+
* Get an ABI from a zipped or unpacked KLib specified in [klib].
56+
*
57+
* Original target will be overridden by [target] if it's not `null`.
4758
*
4859
* To control which declarations are passed to the dump, [filters] could be used. By default, no filters will be applied.
4960
*
5061
* @throws IllegalStateException if a KLib could not be loaded from [klib].
5162
* @throws java.io.FileNotFoundException if file or directory [klib] does not exist.
5263
*/
53-
public fun extractKlibAbi(klib: File, target: KlibTarget, filters: AbiFilters = AbiFilters.Companion.EMPTY): KlibDump
64+
public fun extractKlibAbi(klib: File, target: KlibTarget? = null, filters: AbiFilters = AbiFilters.EMPTY): KlibDump
5465
}

libraries/tools/abi-validation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/v2/KlibDump.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import java.io.File
4141
* mergedDump.print(File("/path/to/merged.klib.api"))
4242
* ```
4343
*
44-
* @since 2.1.20
44+
* @since 2.2.0
4545
*/
4646
public interface KlibDump {
4747
/**

libraries/tools/abi-validation/abi-tools/src/main/kotlin/org/jetbrains/kotlin/abi/tools/v2/ToolsV2.kt

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@ import org.jetbrains.kotlin.abi.tools.api.v2.KlibTarget
1212
import org.jetbrains.kotlin.abi.tools.filtering.compileMatcher
1313
import org.jetbrains.kotlin.abi.tools.v2.klib.KlibDumpImpl
1414
import java.io.File
15+
import java.io.InputStream
16+
import java.util.jar.JarFile
1517

1618
internal object ToolsV2 : AbiToolsV2 {
19+
1720
override fun <T : Appendable> printJvmDump(
1821
appendable: T,
19-
classfiles: Iterable<File>,
20-
filters: AbiFilters,
22+
inputFiles: Iterable<File>,
23+
filters: AbiFilters
2124
) {
2225
val filtersMatcher = compileMatcher(filters)
2326

24-
val signatures = classfiles.asSequence()
25-
.map { classfile -> classfile.inputStream() }
27+
val signatures = streamsForInputFiles(inputFiles)
2628
.loadApiFromJvmClasses()
2729
.filterByMatcher(filtersMatcher)
2830

@@ -43,11 +45,34 @@ internal object ToolsV2 : AbiToolsV2 {
4345

4446
override fun extractKlibAbi(
4547
klib: File,
46-
target: KlibTarget,
48+
target: KlibTarget?,
4749
filters: AbiFilters,
4850
): KlibDump {
4951
val dump = KlibDumpImpl.fromKlib(klib, filters)
50-
dump.renameSingleTarget(target)
52+
if (target != null) {
53+
dump.renameSingleTarget(target)
54+
}
5155
return dump
5256
}
57+
58+
private fun streamsFromJar(jarFile: File): Sequence<InputStream> {
59+
val jar = JarFile(jarFile)
60+
return jar.entries().iterator().asSequence()
61+
.filter { file ->
62+
!file.isDirectory && file.name.endsWith(".class") && !file.name.startsWith("META-INF/")
63+
}.map { entry -> jar.getInputStream(entry) }
64+
}
65+
66+
private fun streamsForInputFiles(inputFiles: Iterable<File>): Sequence<InputStream> {
67+
return inputFiles.asSequence().flatMap { file ->
68+
if (!file.exists() || !file.isFile) {
69+
return@flatMap emptySequence()
70+
}
71+
when (file.extension) {
72+
"jar" -> streamsFromJar(file)
73+
"class" -> sequenceOf(file.inputStream())
74+
else -> emptySequence()
75+
}
76+
}
77+
}
5378
}

libraries/tools/abi-validation/abi-tools/src/test/kotlin/v2/tests/CasesPublicAPITest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class CasesPublicAPITest {
160160
includedClasses: Set<String> = emptySet(),
161161
excludedClasses: Set<String> = emptySet(),
162162
includedAnnotatedWith: Set<String> = emptySet(),
163-
excludedAnnotatedWith: Set<String> = emptySet(),
163+
excludedAnnotatedWith: Set<String> = emptySet()
164164
) {
165165
val filters = AbiFilters(includedClasses, excludedClasses, includedAnnotatedWith, excludedAnnotatedWith)
166166

@@ -172,17 +172,17 @@ class CasesPublicAPITest {
172172
}
173173

174174
internal fun doCheck(testClassPaths: List<File>, target: File, filters: AbiFilters) {
175-
val testClasses = testClassPaths.flatMap { it.walk() }.filter { it.name.endsWith(".class") }
176-
check(testClasses.isNotEmpty()) { "No class files are found in paths: $testClassPaths" }
175+
val inputFiles = testClassPaths.flatMap { it.walk() }.filter { it.extension == "class" || it.extension == "jar" }
176+
check(inputFiles.isNotEmpty()) { "No class or jar files are found in paths: $testClassPaths" }
177177

178178
if (!target.exists()) {
179179
target.bufferedWriter().use { writer ->
180-
ToolsV2.printJvmDump(writer, testClasses, filters)
180+
ToolsV2.printJvmDump(writer, inputFiles, filters)
181181
}
182182
fail("Expected data file did not exist. Generating: $target")
183183
} else {
184184
val stringBuffer = StringBuffer()
185-
ToolsV2.printJvmDump(stringBuffer, testClasses, filters)
185+
ToolsV2.printJvmDump(stringBuffer, inputFiles, filters)
186186
assertEqualsToFile(target, stringBuffer.toString())
187187
}
188188
}

libraries/tools/abi-validation/abi-tools/src/test/kotlin/v2/tests/PrecompiledCasesTest.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,25 @@ class PrecompiledCasesTest {
2525
@Test
2626
fun parcelable() = snapshotAPIAndCompare()
2727

28+
@Test
29+
fun jar() {
30+
val testDir = baseOutputPath.resolve(testName.methodName)
31+
val target = testDir.resolve(testName.methodName + ".txt")
32+
33+
doCheck(listOf(testDir), target, AbiFilters.EMPTY)
34+
}
35+
2836
@OptIn(ExperimentalPathApi::class)
2937
private fun snapshotAPIAndCompare(
3038
includedClasses: Set<String> = emptySet(),
3139
excludedClasses: Set<String> = emptySet(),
3240
includedAnnotatedWith: Set<String> = emptySet(),
3341
excludedAnnotatedWith: Set<String> = emptySet(),
3442
) {
35-
val testClassRelativePath = testName.methodName
43+
val testDir = baseOutputPath.resolve(testName.methodName)
3644
val filters = AbiFilters(includedClasses, excludedClasses, includedAnnotatedWith, excludedAnnotatedWith)
45+
val target = testDir.resolve(testName.methodName + ".txt")
3746

38-
val target = baseOutputPath.resolve(testClassRelativePath).resolve(testName.methodName + ".txt")
39-
40-
doCheck(listOf(baseOutputPath), target, filters)
47+
doCheck(listOf(testDir), target, filters)
4148
}
4249
}
583 Bytes
Binary file not shown.
1.16 KB
Binary file not shown.

0 commit comments

Comments
 (0)