Skip to content
Merged
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
9 changes: 7 additions & 2 deletions api/binary-compatibility-validator.api
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,21 @@ public final class kotlinx/validation/api/klib/KlibDump {
public final fun copy ()Lkotlinx/validation/api/klib/KlibDump;
public final fun getTargets ()Ljava/util/Set;
public final fun merge (Ljava/io/File;Ljava/lang/String;)V
public final fun merge (Ljava/lang/CharSequence;Ljava/lang/String;)V
public final fun merge (Lkotlinx/validation/api/klib/KlibDump;)V
public static synthetic fun merge$default (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;Ljava/lang/String;ILjava/lang/Object;)V
public static synthetic fun merge$default (Lkotlinx/validation/api/klib/KlibDump;Ljava/lang/CharSequence;Ljava/lang/String;ILjava/lang/Object;)V
public final fun remove (Ljava/lang/Iterable;)V
public final fun replace (Lkotlinx/validation/api/klib/KlibDump;)V
public final fun retain (Ljava/lang/Iterable;)V
public final fun saveTo (Ljava/lang/Appendable;)V
public final fun saveTo (Ljava/lang/Appendable;)Ljava/lang/Appendable;
}

public final class kotlinx/validation/api/klib/KlibDump$Companion {
public final fun from (Ljava/io/File;Ljava/lang/String;)Lkotlinx/validation/api/klib/KlibDump;
public final fun from (Ljava/lang/CharSequence;Ljava/lang/String;)Lkotlinx/validation/api/klib/KlibDump;
public static synthetic fun from$default (Lkotlinx/validation/api/klib/KlibDump$Companion;Ljava/io/File;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
public static synthetic fun from$default (Lkotlinx/validation/api/klib/KlibDump$Companion;Ljava/lang/CharSequence;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
public final fun fromKlib (Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;)Lkotlinx/validation/api/klib/KlibDump;
public static synthetic fun fromKlib$default (Lkotlinx/validation/api/klib/KlibDump$Companion;Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
}
Expand Down Expand Up @@ -186,7 +191,7 @@ public final class kotlinx/validation/api/klib/KlibDumpKt {
public static synthetic fun inferAbi$default (Lkotlinx/validation/api/klib/KlibTarget;Ljava/lang/Iterable;Lkotlinx/validation/api/klib/KlibDump;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
public static final fun mergeFromKlib (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;)V
public static synthetic fun mergeFromKlib$default (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;ILjava/lang/Object;)V
public static final fun saveTo (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;)V
public static final fun saveTo (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;)Ljava/io/File;
}

public final class kotlinx/validation/api/klib/KlibSignatureVersion : java/io/Serializable {
Expand Down
72 changes: 67 additions & 5 deletions src/main/kotlin/api/klib/KlibDump.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package kotlinx.validation.api.klib

import kotlinx.validation.ExperimentalBCVApi
import org.jetbrains.kotlin.ir.backend.js.MainModule
import java.io.File
import java.io.FileNotFoundException

Expand Down Expand Up @@ -38,8 +37,7 @@ import java.io.FileNotFoundException
* ```kotlin
* val mergedDump = KlibDump.from(File("/path/to/merged.klib.api"))
* val newTargetDump = KlibDump.fromKlib(File("/path/to/library-linuxX64.klib"))
* mergedDump.remove(newTargetDump.targets)
* mergedDump.merge(newTargetDump)
* mergedDump.replace(newTargetDump)
* mergedDump.saveTo(File("/path/to/merged.klib.api"))
* ```
*/
Expand Down Expand Up @@ -72,6 +70,7 @@ public class KlibDump {
* @throws IllegalArgumentException if [dumpFile] contains multiple targets
* and [configurableTargetName] is not null.
* @throws IllegalArgumentException if [dumpFile] is not a file.
* @throws IllegalArgumentException if [dumpFile] is empty.
* @throws FileNotFoundException if [dumpFile] does not exist.
*
* @sample samples.KlibDumpSamples.mergeDumps
Expand All @@ -82,6 +81,29 @@ public class KlibDump {
merger.merge(dumpFile, configurableTargetName)
}

/**
* Reads a textual KLib dump from the [dump] char sequence and merges it into this dump.
*
* If a dump contains only a single target, it's possible to specify a custom configurable target name.
* Please refer to [KlibTarget.configurableName] for more details on the meaning of that name.
*
* By default, [configurableTargetName] is null and information about a target will be taken directly from
* the loaded dump.
*
* It's an error to specify non-null [configurableTargetName] for a dump containing multiple targets.
* 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] contains multiple targets
* and [configurableTargetName] is not null.
* @throws IllegalArgumentException if the provided [dump] is empty.
*
* @sample samples.KlibDumpSamples.updateMergedDump
*/
public fun merge(dump: CharSequence, configurableTargetName: String? = null) {
merger.merge(dump.lineSequence().iterator(), configurableTargetName)
}

/**
* Merges [other] dump with this one.
*
Expand All @@ -101,6 +123,19 @@ public class KlibDump {
merger.merge(other.merger)
}

/**
* 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].
*
* @sample samples.KlibDumpSamples.updateMergedDump
*/
public fun replace(other: KlibDump) {
remove(other.targets)
merge(other)
}

/**
* Removes all declarations that do not belong to specified targets and removes these targets from the dump.
*
Expand Down Expand Up @@ -134,10 +169,13 @@ public class KlibDump {
/**
* Serializes the dump and writes it to [to].
*
* @return the target [to] where the dump was written.
*
* @sample samples.KlibDumpSamples.mergeDumps
*/
public fun saveTo(to: Appendable) {
public fun <A : Appendable> saveTo(to: A): A {
merger.dump(to)
return to
}

public companion object {
Expand Down Expand Up @@ -166,6 +204,28 @@ public class KlibDump {
return KlibDump().apply { merge(dumpFile, configurableTargetName) }
}

/**
* Reads a dump from a textual form.
*
* If a dump contains only a single target, it's possible to specify a custom configurable target name.
* Please refer to [KlibTarget.configurableName] for more details on the meaning of that name.
*
* By default, [configurableTargetName] is null and information about a target will be taken directly from
* the loaded dump.
*
* It's an error to specify non-null [configurableTargetName] for a dump containing multiple targets.
*
* @throws IllegalArgumentException if this dump and the provided [dump] shares same targets.
* @throws IllegalArgumentException if the provided [dump] contains multiple targets
* and [configurableTargetName] is not null.
* @throws IllegalArgumentException if the provided [dump] is empty.
*
* @sample samples.KlibDumpSamples.updateMergedDump
*/
public fun from(dump: CharSequence, configurableTargetName: String? = null): KlibDump {
return KlibDump().apply { merge(dump, configurableTargetName) }
}

/**
* Dumps a public ABI of a klib represented by [klibFile] using [filters]
* and returns a [KlibDump] representing it.
Expand Down Expand Up @@ -278,6 +338,8 @@ public fun KlibDump.mergeFromKlib(

/**
* Serializes the dump and writes it to [file].
*
* @return the target [file].
*/
@ExperimentalBCVApi
public fun KlibDump.saveTo(file: File): Unit = file.bufferedWriter().use { saveTo(it) }
public fun KlibDump.saveTo(file: File): File = file.apply { bufferedWriter().use { saveTo(it) } }
113 changes: 105 additions & 8 deletions src/test/kotlin/samples/KlibDumpSamples.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class KlibDumpSamples {
@Rule
var tempFolder = TemporaryFolder()

fun createDumpWithContent(content: String): File {
fun createDumpFileWithContent(content: String): File {
val file = tempFolder.newFile()
file.writer().use {
it.write(content)
Expand All @@ -31,7 +31,7 @@ class KlibDumpSamples {
@OptIn(ExperimentalBCVApi::class)
@Test
fun mergeDumps() {
val linuxX64Dump = createDumpWithContent("""
val linuxX64Dump = createDumpFileWithContent("""
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
Expand All @@ -51,7 +51,7 @@ class KlibDumpSamples {
final fun org.example/ShardedClass(kotlin/Int, kotlin/Float, kotlin/Long): org.example/ShardedClass // org.example/ShardedClass|ShardedClass(kotlin.Int;kotlin.Float;kotlin.Long){}[0]
""".trimIndent())

val linuxArm64Dump = createDumpWithContent("""
val linuxArm64Dump = createDumpFileWithContent("""
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
Expand Down Expand Up @@ -103,7 +103,7 @@ class KlibDumpSamples {
@OptIn(ExperimentalBCVApi::class)
@Test
fun mergeDumpObjects() {
val linuxX64Dump = createDumpWithContent("""
val linuxX64Dump = createDumpFileWithContent("""
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
Expand All @@ -123,7 +123,7 @@ class KlibDumpSamples {
final fun org.example/ShardedClass(kotlin/Int, kotlin/Float, kotlin/Long): org.example/ShardedClass // org.example/ShardedClass|ShardedClass(kotlin.Int;kotlin.Float;kotlin.Long){}[0]
""".trimIndent())

val linuxArm64Dump = createDumpWithContent("""
val linuxArm64Dump = createDumpFileWithContent("""
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
Expand Down Expand Up @@ -193,11 +193,108 @@ class KlibDumpSamples {
""".trimIndent(), filteredDumpContent)
}

@OptIn(ExperimentalBCVApi::class)
@Test
fun updateMergedDump() {
val linuxX64Dump = """
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
// - Show declarations: true

// Library unique name: <org.example:bcv-klib-test>
// Platform: NATIVE
// Native targets: linux_x64
// Compiler version: 1.9.22
// ABI version: 1.8.0
final class org.example/ShardedClass { // org.example/ShardedClass|null[0]
final val value // org.example/ShardedClass.value|{}value[0]
final fun <get-value>(): kotlin/Int // org.example/ShardedClass.value.<get-value>|<get-value>(){}[0]
constructor <init>(kotlin/Int) // org.example/ShardedClass.<init>|<init>(kotlin.Int){}[0]
final fun add(kotlin/Int): kotlin/Int // org.example/ShardedClass.add|add(kotlin.Int){}[0]
}
final fun org.example/ShardedClass(kotlin/Int, kotlin/Float, kotlin/Long): org.example/ShardedClass // org.example/ShardedClass|ShardedClass(kotlin.Int;kotlin.Float;kotlin.Long){}[0]
""".trimIndent()

val linuxArm64Dump = """
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
// - Show declarations: true

// Library unique name: <org.example:bcv-klib-test>
// Platform: NATIVE
// Native targets: linux_arm64
// Compiler version: 1.9.22
// ABI version: 1.8.0
final class org.example/ShardedClass { // org.example/ShardedClass|null[0]
final val value // org.example/ShardedClass.value|{}value[0]
final fun <get-value>(): kotlin/Int // org.example/ShardedClass.value.<get-value>|<get-value>(){}[0]
constructor <init>(kotlin/Int) // org.example/ShardedClass.<init>|<init>(kotlin.Int){}[0]
final fun add(kotlin/Int): kotlin/Int // org.example/ShardedClass.add|add(kotlin.Int){}[0]
}
""".trimIndent()

val mergedDump = KlibDump()
mergedDump.merge(KlibDump.from(linuxArm64Dump))
mergedDump.merge(KlibDump.from(linuxX64Dump, configurableTargetName = "linuxX86_64"))

val newLinuxX64Dump = """
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
// - Show declarations: true

// Library unique name: <org.example:bcv-klib-test>
// Platform: NATIVE
// Native targets: linux_x64
// Compiler version: 1.9.22
// ABI version: 1.8.0
final class org.example/ShardedClass { // org.example/ShardedClass|null[0]
final val value // org.example/ShardedClass.value|{}value[0]
final fun <get-value>(): kotlin/Int // org.example/ShardedClass.value.<get-value>|<get-value>(){}[0]
constructor <init>(kotlin/Int) // org.example/ShardedClass.<init>|<init>(kotlin.Int){}[0]
final fun store(kotlin/Int): kotlin/Int // org.example/ShardedClass.store|store(kotlin.Long){}[0]
}
final fun org.example/ShardedClass(kotlin/Int, kotlin/Float, kotlin/Long): org.example/ShardedClass // org.example/ShardedClass|ShardedClass(kotlin.Int;kotlin.Float;kotlin.Long){}[0]
""".trimIndent()

mergedDump.replace(KlibDump.from(newLinuxX64Dump, configurableTargetName = "linuxX86_64"))

val mergedDumpContent = mergedDump.saveTo(StringBuilder()).toString()
assertEquals("""
// Klib ABI Dump
// Targets: [linuxArm64, linuxX64.linuxX86_64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: false
// - Show declarations: true

// Library unique name: <org.example:bcv-klib-test>
final class org.example/ShardedClass { // org.example/ShardedClass|null[0]
constructor <init>(kotlin/Int) // org.example/ShardedClass.<init>|<init>(kotlin.Int){}[0]

final val value // org.example/ShardedClass.value|{}value[0]
final fun <get-value>(): kotlin/Int // org.example/ShardedClass.value.<get-value>|<get-value>(){}[0]

// Targets: [linuxArm64]
final fun add(kotlin/Int): kotlin/Int // org.example/ShardedClass.add|add(kotlin.Int){}[0]

// Targets: [linuxX64.linuxX86_64]
final fun store(kotlin/Int): kotlin/Int // org.example/ShardedClass.store|store(kotlin.Long){}[0]
}

// Targets: [linuxX64.linuxX86_64]
final fun org.example/ShardedClass(kotlin/Int, kotlin/Float, kotlin/Long): org.example/ShardedClass // org.example/ShardedClass|ShardedClass(kotlin.Int;kotlin.Float;kotlin.Long){}[0]

""".trimIndent(), mergedDumpContent)
}

@OptIn(ExperimentalBCVApi::class)
@Test
fun extractTargets() {
// Oh no, we're running on Windows and Apple targets are unsupported, let's filter it out!
val mergedDump = createDumpWithContent("""
val mergedDump = createDumpFileWithContent("""
// Klib ABI Dump
// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64]
// Rendering settings:
Expand Down Expand Up @@ -243,7 +340,7 @@ class KlibDumpSamples {
// We want to get a dump for iosArm64, but our host compiler doesn't support it.
val unsupportedTarget = KlibTarget.parse("iosArm64")
// Thankfully, we have an old merged dump ...
val oldMergedDump = createDumpWithContent("""
val oldMergedDump = createDumpFileWithContent("""
// Klib ABI Dump
// Targets: [iosArm64, linuxArm64]
// Rendering settings:
Expand All @@ -260,7 +357,7 @@ class KlibDumpSamples {
""".trimIndent())

// ... and a new dump for linuxArm64
val linuxDump = createDumpWithContent("""
val linuxDump = createDumpFileWithContent("""
// Klib ABI Dump
// Targets: [linuxArm64]
// Rendering settings:
Expand Down