-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ABI Validation] Introduced ABI Tools API for old dump format
Resolves #KT-71168
- Loading branch information
Showing
13 changed files
with
496 additions
and
14 deletions.
There are no files selected for viewing
17 changes: 17 additions & 0 deletions
17
libraries/tools/abi-validation/abi-tools-api/build.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
plugins { | ||
kotlin("jvm") | ||
} | ||
|
||
kotlin { | ||
explicitApi() | ||
} | ||
|
||
configureKotlinCompileTasksGradleCompatibility() | ||
|
||
dependencies { | ||
// remove stdlib dependency from api artifact in order not to affect the dependencies of the user project | ||
compileOnly(kotlinStdlib()) | ||
|
||
testImplementation(kotlinTest("junit")) | ||
testImplementation(libs.junit4) | ||
} |
103 changes: 103 additions & 0 deletions
103
...validation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/AbiFilters.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. | ||
*/ | ||
|
||
package org.jetbrains.kotlin.abi.tools.api | ||
|
||
/** | ||
* Set of filtering rules that restrict ABI declarations included into a dump. | ||
* | ||
* It consists of a combination of rules for including and excluding declarations. | ||
* Each filter can be written as a filter for the class name (see [includedClasses] or [excludedClasses]), or an annotation filter (see [includedAnnotatedWith] or [excludedAnnotatedWith]). | ||
* | ||
* In order for a declaration (class, field, property or function) to get into the dump, it must pass the inclusion **and** exclusion filters. | ||
* | ||
* Declaration passes the exclusion filter if it does not match any of class name (see [excludedClasses]) or annotation (see [excludedAnnotatedWith]) filter rule. | ||
* | ||
* 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 inclusion rule. | ||
* | ||
* @since 2.1.20 | ||
*/ | ||
public class AbiFilters( | ||
/** | ||
* Include class into dump by its name. | ||
* Classes that do not match the specified names, that do not have an annotation from [includedAnnotatedWith] | ||
* and do not have members marked with an annotation from [includedAnnotatedWith] are excluded from the dump. | ||
* | ||
* The name filter works by comparing qualified class name with the value in the filter. | ||
* | ||
* For Kotlin classes, fully qualified names are used. | ||
* It is important to keep in mind that dots are used everywhere as separators, even in the case of a nested class. | ||
* E.g. for qualified name `foo.bar.Container.Value`, here `Value` is a class nested in `Container`. | ||
* | ||
* For classes from Java sources, canonical names are used. | ||
* The main motivation is a similar approach to writing the class name - dots are used everywhere as delimiters. | ||
* | ||
* It is allowed to use name templates, for this purpose wildcards `**`, `*` and `?` are added. | ||
* - `**` - zero or any number of characters | ||
* - `*` - zero or any number of characters excluding dot. Using to specify simple class name. | ||
* - `?` - any single character. | ||
*/ | ||
public val includedClasses: Set<String>, | ||
|
||
/** | ||
* Excludes a class from a dump by its name. | ||
* | ||
* The name filter works by comparing qualified class name with the value in the filter. | ||
* | ||
* For Kotlin classes, fully qualified names are used. | ||
* It is important to keep in mind that dots are used everywhere as separators, even in the case of a nested class. | ||
* E.g. for qualified name `foo.bar.Container.Value`, here `Value` is a class nested in `Container`. | ||
* | ||
* For classes from Java sources, canonical names are used. | ||
* The main motivation is a similar approach to writing the class name - dots are used everywhere as delimiters. | ||
* | ||
* It is allowed to use name templates, for this purpose wildcards `**`, `*` and `?` are added. | ||
* - `**` - zero or any number of characters | ||
* - `*` - zero or any number of characters excluding dot. Using to specify simple class name. | ||
* - `?` - any single character. | ||
*/ | ||
public val excludedClasses: Set<String>, | ||
|
||
/** | ||
* Includes a declaration by annotations placed on it. | ||
* | ||
* Any declaration that is not marked with one of the these annotations and does not match the [includedClasses] is excluded from the dump. | ||
* | ||
* If a class, a top-level function, a top-level property or a class member is annotated with one of specified annotations - then this declaration matches the filter. | ||
* | ||
* For exclusion filter it means that a class will be excluded from a dump, for inclusion filter it will keep a class or a member in a dump. | ||
* | ||
* It is allowed to use name templates, for this purpose wildcards `**`, `*` and `?` are added. | ||
* - `**` - zero or any number of characters | ||
* - `*` - zero or any number of characters excluding dot. Using to specify simple class name. | ||
* - `?` - any single character. | ||
* | ||
* The annotation should not have [Retention] equal to [AnnotationRetention.SOURCE], otherwise, filtering by it will not work. | ||
*/ | ||
public val includedAnnotatedWith: Set<String>, | ||
|
||
/** | ||
* Excludes a declaration by annotations placed on it. | ||
* | ||
* If a class, a top-level function, a top-level property or a class member is annotated with one of specified annotations - then this declaration matches the filter. | ||
* | ||
* For exclusion filter it means that class will be excluded from dump, for inclusion filter it will keep class or member in dump. | ||
* | ||
* It is allowed to use name templates, for this purpose wildcards `**`, `*` and `?` are added. | ||
* - `**` - zero or any number of characters | ||
* - `*` - zero or any number of characters excluding dot. Using to specify simple class name. | ||
* - `?` - any single character. | ||
* | ||
* The annotation should not have [Retention] equal to [AnnotationRetention.SOURCE], otherwise, filtering by it will not work. | ||
*/ | ||
public val excludedAnnotatedWith: Set<String>, | ||
) { | ||
public companion object { | ||
public val EMPTY: AbiFilters = AbiFilters(emptySet(), emptySet(), emptySet(), emptySet()) | ||
} | ||
|
||
public val isEmpty: Boolean = | ||
includedClasses.isEmpty() && excludedClasses.isEmpty() && includedAnnotatedWith.isEmpty() && excludedAnnotatedWith.isEmpty() | ||
} |
20 changes: 20 additions & 0 deletions
20
...ation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/AbiToolsFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. | ||
*/ | ||
|
||
package org.jetbrains.kotlin.abi.tools.api | ||
|
||
/** | ||
* An abstract factory for obtaining an instance of [AbiToolsInterface] - main class for using the capabilities of ABI Validation tool. | ||
* | ||
* @since 2.1.20 | ||
*/ | ||
public interface AbiToolsFactory { | ||
/** | ||
* Gets an instance of [AbiToolsInterface]. | ||
* | ||
* Can be idempotent and return same instance on each call but this is not guaranteed. | ||
*/ | ||
public fun get(): AbiToolsInterface | ||
} |
30 changes: 30 additions & 0 deletions
30
...ion/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/AbiToolsInterface.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. | ||
*/ | ||
|
||
package org.jetbrains.kotlin.abi.tools.api | ||
|
||
import org.jetbrains.kotlin.abi.tools.api.v2.AbiToolsV2 | ||
import java.io.File | ||
|
||
/** | ||
* All features of Kotlin ABI Validation tool. | ||
* | ||
* @since 2.1.20 | ||
*/ | ||
public interface AbiToolsInterface { | ||
/** | ||
* A set of features for working with legacy format dumps, used in previous [Binary Compatibility Validator plugin](https://github.com/Kotlin/binary-compatibility-validator). | ||
*/ | ||
public val v2: AbiToolsV2 | ||
|
||
/** | ||
* Compare two files line-by-line. | ||
* | ||
* @return `null` if there are no differences, diff string otherwise. | ||
* | ||
* @throws java.io.FileNotFoundException if [expectedFile] and/or [actualFile] does not exist. | ||
*/ | ||
public fun filesDiff(expectedFile: File, actualFile: File): String? | ||
} |
54 changes: 54 additions & 0 deletions
54
...idation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/v2/AbiToolsV2.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. | ||
*/ | ||
|
||
package org.jetbrains.kotlin.abi.tools.api.v2 | ||
|
||
import org.jetbrains.kotlin.abi.tools.api.AbiFilters | ||
import java.io.File | ||
|
||
/** | ||
* A set of features for working with legacy format dumps, | ||
* used in previous [Binary Compatibility Validator plugin](https://github.com/Kotlin/binary-compatibility-validator). | ||
* | ||
* @since 2.1.20 | ||
*/ | ||
public interface AbiToolsV2 { | ||
/** | ||
* Print ABI dump for JVM class-files into specified [appendable]. | ||
*/ | ||
public fun <T : Appendable> printJvmDump(appendable: T, classfiles: Iterable<File>, filters: AbiFilters) | ||
|
||
/** | ||
* Create empty KLib dump without any declarations and targets. | ||
*/ | ||
public fun createKlibDump(): KlibDump | ||
|
||
/** | ||
* Loads a KLib dump from a dump file. | ||
* | ||
* @throws IllegalArgumentException if [dumpFile] is empty. | ||
* @throws IllegalArgumentException if [dumpFile] is not a file. | ||
* @throws java.io.FileNotFoundException if [dumpFile] does not exist. | ||
*/ | ||
public fun loadKlibDump(dumpFile: File): KlibDump | ||
|
||
/** | ||
* Reads a KLib dump from a textual form. | ||
* | ||
* @throws IllegalArgumentException if this dump and the provided [dump] shares same targets. | ||
* @throws IllegalArgumentException if the provided [dump] is empty. | ||
*/ | ||
public fun loadKlibDump(dump: CharSequence): KlibDump | ||
|
||
/** | ||
* Get an ABI from a KLib file with specified [target]. | ||
* | ||
* To control which declarations are passed to the dump, [filters] could be used. By default, no filters will be applied. | ||
* | ||
* @throws IllegalStateException if a KLib could not be loaded from [klibFile]. | ||
* @throws java.io.FileNotFoundException if [klibFile] does not exist. | ||
*/ | ||
public fun extractKlibAbi(klibFile: File, target: KlibTarget, filters: AbiFilters = AbiFilters.Companion.EMPTY): KlibDump | ||
} |
148 changes: 148 additions & 0 deletions
148
...alidation/abi-tools-api/src/main/kotlin/org/jetbrains/kotlin/abi/tools/api/v2/KlibDump.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* | ||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. | ||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. | ||
*/ | ||
|
||
package org.jetbrains.kotlin.abi.tools.api.v2 | ||
|
||
import java.io.File | ||
|
||
/** | ||
* Represents a KLib ABI dump and allows manipulating it. | ||
* | ||
* This type is mutable. | ||
* | ||
* Usual [KlibDump] workflows consist of loading, updating and writing a dump back. | ||
* | ||
* **Creating a textual dump from a klib** | ||
* ```kotlin | ||
* val dump = abiTools.v2.extractKlibAbi(File("/path/to/library.klib"), KlibTarget("linuxX64")) | ||
* dump.print(File("/path/to/dump.klib.api")) | ||
* ``` | ||
* | ||
* **Loading a dump** | ||
* ```kotlin | ||
* val dump = abiTools.v2.loadKlibDump(File("/path/to/dump.klib.api")) | ||
* ``` | ||
* | ||
* **Merging multiple dumps into a new merged dump** | ||
* ```kotlin | ||
* val klibs = listOf(File("/path/to/library-linuxX64.klib") to KlibTarget("linuxX64"), File("/path/to/library-linuxArm64.klib") to KlibTarget("linuxArm64"), ...) | ||
* val mergedDump = abiTools.v2.createKlibDump() | ||
* klibs.forEach { mergedDump.merge(abiTools.v2.parseKlib(it.first, it.second)) } | ||
* mergedDump.print(File("/path/to/merged.klib.api")) | ||
* ``` | ||
* | ||
* **Updating an existing merged dump** | ||
* ```kotlin | ||
* val mergedDump = abiTools.v2.loadKlibDump(File("/path/to/merged.klib.api")) | ||
* val newTargetDump = abiTools.v2.extractKlibAbi(File("/path/to/library-linuxX64.klib"), KlibTarget("linuxX64")) | ||
* mergedDump.replace(newTargetDump) | ||
* mergedDump.print(File("/path/to/merged.klib.api")) | ||
* ``` | ||
* | ||
* @since 2.1.20 | ||
*/ | ||
public interface KlibDump { | ||
/** | ||
* Set of all targets for which this dump contains declarations. | ||
*/ | ||
public val targets: Set<KlibTarget> | ||
|
||
/** | ||
* Loads a textual KLib dump and merges it into this dump. | ||
* | ||
* It's an error to merge dumps having some targets in common. | ||
* | ||
* @throws IllegalArgumentException if this dump and [dumpFile] shares same targets. | ||
* @throws IllegalArgumentException if [dumpFile] is not a file or is empty. | ||
* @throws java.io.FileNotFoundException if [dumpFile] does not exist. | ||
*/ | ||
public fun merge(dumpFile: File) | ||
|
||
/** | ||
* Reads a textual KLib dump from the [dump] char sequence and merges it into this dump. | ||
* | ||
* It's also an error to merge dumps having some targets in common. | ||
* | ||
* @throws IllegalArgumentException if this dump and the provided [dump] shares same targets. | ||
* @throws IllegalArgumentException if the provided [dump] is empty. | ||
*/ | ||
public fun merge(dump: CharSequence) | ||
|
||
/** | ||
* Merges [other] dump with this one. | ||
* | ||
* It's also an error to merge dumps having some targets in common. | ||
* | ||
* The operation does not modify [other]. | ||
* | ||
* @throws IllegalArgumentException if this dump and [other] shares same targets. | ||
*/ | ||
public fun merge(other: KlibDump) | ||
|
||
/** | ||
* Removes the targets from this dump that are contained in the [other] targets set and all their declarations. | ||
* Then merges the [other] dump with this one. | ||
* | ||
* The operation does not modify [other]. | ||
*/ | ||
public fun replace(other: KlibDump) | ||
|
||
/** | ||
* Removes all declarations that do not belong to specified targets and removes these targets from the dump. | ||
* | ||
* All targets in the [targets] collection not contained within this dump will be ignored. | ||
*/ | ||
public fun retain(targets: Iterable<KlibTarget>) | ||
|
||
/** | ||
* Remove all declarations that do belong to specified targets and remove these targets from the dump. | ||
* | ||
* All targets in the [targets] collection not contained within this dump will be ignored. | ||
*/ | ||
public fun remove(targets: Iterable<KlibTarget>) | ||
|
||
/** | ||
* Change target identifier in this dump if it contains only single target. | ||
* | ||
* @throws IllegalStateException if dump contains multiple targets | ||
*/ | ||
public fun renameSingleTarget(target: KlibTarget) | ||
|
||
/** | ||
* Creates a copy of this dump. | ||
*/ | ||
public fun copy(): KlibDump | ||
|
||
/** | ||
* Serializes the dump and prints it to [to]. | ||
* | ||
* @return the target [to] where the dump was written. | ||
*/ | ||
public fun <A : Appendable> print(to: A): A | ||
|
||
/** | ||
* Serializes the dump and prints it to [file]. | ||
* | ||
* @return the target [file]. | ||
*/ | ||
public fun print(file: File): File | ||
|
||
/** | ||
* Merges [targetsFromOther] targets from [other] dump into this one. | ||
* | ||
* There are several rules that work when merging: | ||
* - If target in [targetsFromOther] exists in [other] dump but does not exist in this dump - declaration all declarations copied to this dump | ||
* - If this dump is empty and target in [targetsFromOther] is present in it [other] dump - it is added to this dump as is. | ||
* - If this dump is empty and target in [targetsFromOther] is not present in it [other] dump - nothing happen. | ||
* - If this dump and [other] dump are empty - [IllegalStateException] is thrown. | ||
* | ||
* Additional merging rule: | ||
* - If the targets from this dump have common declarations on some level ('all', 'native', 'linux', etc.) and target | ||
* in [targetsFromOther] also belongs to this group - these common declarations also added to this target in this dump. | ||
* | ||
* @throws IllegalStateException if this dump and [other] have no targets | ||
*/ | ||
public fun partialMerge(other: KlibDump, targetsFromOther: List<KlibTarget>) | ||
} |
Oops, something went wrong.