From 01c32094b8180a8aec66e032a9a19fef98a22669 Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Sat, 31 Aug 2024 02:30:36 -0400 Subject: [PATCH] Add `klib` api validation (#62) --- .github/workflows/CI.yml | 41 ++++++++++++------- build.gradle.kts | 5 +++ gradle/libs.versions.toml | 2 +- library/common/api/common.klib.api | 33 ++++++++++++++++ library/digest/api/digest.klib.api | 33 ++++++++++++++++ library/mac/api/mac.klib.api | 35 +++++++++++++++++ library/xof/api/xof.klib.api | 54 ++++++++++++++++++++++++++ test-android/api/test-android.klib.api | 0 8 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 library/common/api/common.klib.api create mode 100644 library/digest/api/digest.klib.api create mode 100644 library/mac/api/mac.klib.api create mode 100644 library/xof/api/xof.klib.api create mode 100644 test-android/api/test-android.klib.api diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d9ac4d0..e5dba4b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,6 +11,7 @@ jobs: fail-fast: false matrix: os: [ macos-latest, ubuntu-latest, windows-latest ] + java-version: [ 11, 19 ] runs-on: ${{ matrix.os }} @@ -42,33 +43,43 @@ jobs: key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('gradle/libs.versions.toml') }} restore-keys: ${{ runner.os }}-gradle-caches- - # Windows does not build ANDROID, but needs Java 11 for JPMS Multi-Release Jar build - - name: Setup JDK 11 - if: matrix.os == 'windows-latest' + - name: Setup JDK uses: actions/setup-java@v3.4.0 with: distribution: 'zulu' - java-version: 11 + java-version: ${{ matrix.java-version }} - - name: Setup JDK 19 - if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' - uses: actions/setup-java@v3.4.0 - with: - distribution: 'zulu' - java-version: 19 + - name: Check API Compatibility + if: matrix.os == 'macos-latest' && matrix.java-version == 19 + run: > + ./gradlew apiCheck --stacktrace - name: Run macOS Tests - if: matrix.os == 'macos-latest' + if: matrix.os == 'macos-latest' && matrix.java-version == 19 run: > ./gradlew check --stacktrace - -PKMP_TARGETS="JVM,JS,IOS_ARM64,IOS_X64,IOS_SIMULATOR_ARM64,MACOS_ARM64,MACOS_X64,TVOS_ARM64,TVOS_X64,TVOS_SIMULATOR_ARM64,WATCHOS_ARM32,WATCHOS_ARM64,WATCHOS_DEVICE_ARM64,WATCHOS_X64,WATCHOS_SIMULATOR_ARM64,WASM_JS,WASM_WASI" + -PKMP_TARGETS="IOS_ARM64,IOS_X64,IOS_SIMULATOR_ARM64,JVM,MACOS_ARM64,MACOS_X64,TVOS_ARM64,TVOS_X64,TVOS_SIMULATOR_ARM64,WATCHOS_ARM32,WATCHOS_ARM64,WATCHOS_DEVICE_ARM64,WATCHOS_X64,WATCHOS_SIMULATOR_ARM64" + + - name: Run macOS Java11 Tests + if: matrix.os == 'macos-latest' && matrix.java-version == 11 + run: > + ./gradlew check --stacktrace + -PKMP_TARGETS="JS,JVM,WASM_JS,WASM_WASI" + - name: Run Linux Tests - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-latest' && matrix.java-version == 19 + run: > + ./gradlew check --stacktrace + -PKMP_TARGETS="ANDROID,ANDROID_ARM32,ANDROID_ARM64,ANDROID_X64,ANDROID_X86,JVM,LINUX_ARM64,LINUX_X64" + + - name: Run Linux Java11 Tests + if: matrix.os == 'ubuntu-latest' && matrix.java-version == 11 run: > ./gradlew check --stacktrace - -PKMP_TARGETS="JVM,JS,ANDROID,ANDROID_ARM32,ANDROID_ARM64,ANDROID_X64,ANDROID_X86,LINUX_ARM64,LINUX_X64,WASM_JS,WASM_WASI" + -PKMP_TARGETS="JS,JVM,WASM_JS,WASM_WASI" + - name: Run Windows Tests - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows-latest' && matrix.java-version == 11 run: > ./gradlew check --stacktrace -PKMP_TARGETS="JVM,JS,MINGW_X64,WASM_JS,WASM_WASI" diff --git a/build.gradle.kts b/build.gradle.kts index 862544f..be69dd4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,11 @@ plugins.withType { } apiValidation { + // Only enable when selectively enabled targets are not being passed via cli. + // See https://github.com/Kotlin/binary-compatibility-validator/issues/269 + @OptIn(kotlinx.validation.ExperimentalBCVApi::class) + klib.enabled = findProperty("KMP_TARGETS") == null + if (findProperty("CHECK_PUBLICATION") != null) { ignoredProjects.add("check-publication") } else { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e493a68..2c256a8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] gradle-android = "8.2.2" -gradle-binary-compat = "0.14.0" +gradle-binary-compat = "0.16.3" gradle-kmp-configuration = "0.3.2" gradle-kotlin = "1.9.24" gradle-publish-maven = "0.29.0" diff --git a/library/common/api/common.klib.api b/library/common/api/common.klib.api new file mode 100644 index 0000000..f27aba0 --- /dev/null +++ b/library/common/api/common.klib.api @@ -0,0 +1,33 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +open annotation class org.kotlincrypto.core/ExperimentalKotlinCryptoApi : kotlin/Annotation { // org.kotlincrypto.core/ExperimentalKotlinCryptoApi|null[0] + constructor () // org.kotlincrypto.core/ExperimentalKotlinCryptoApi.|(){}[0] +} + +open annotation class org.kotlincrypto.core/InternalKotlinCryptoApi : kotlin/Annotation { // org.kotlincrypto.core/InternalKotlinCryptoApi|null[0] + constructor () // org.kotlincrypto.core/InternalKotlinCryptoApi.|(){}[0] +} + +abstract interface <#A: out kotlin/Any> org.kotlincrypto.core/Copyable { // org.kotlincrypto.core/Copyable|null[0] + abstract fun copy(): #A // org.kotlincrypto.core/Copyable.copy|copy(){}[0] +} + +abstract interface org.kotlincrypto.core/Algorithm { // org.kotlincrypto.core/Algorithm|null[0] + abstract fun algorithm(): kotlin/String // org.kotlincrypto.core/Algorithm.algorithm|algorithm(){}[0] +} + +abstract interface org.kotlincrypto.core/Resettable { // org.kotlincrypto.core/Resettable|null[0] + abstract fun reset() // org.kotlincrypto.core/Resettable.reset|reset(){}[0] +} + +abstract interface org.kotlincrypto.core/Updatable { // org.kotlincrypto.core/Updatable|null[0] + abstract fun update(kotlin/Byte) // org.kotlincrypto.core/Updatable.update|update(kotlin.Byte){}[0] + abstract fun update(kotlin/ByteArray) // org.kotlincrypto.core/Updatable.update|update(kotlin.ByteArray){}[0] + abstract fun update(kotlin/ByteArray, kotlin/Int, kotlin/Int) // org.kotlincrypto.core/Updatable.update|update(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] +} diff --git a/library/digest/api/digest.klib.api b/library/digest/api/digest.klib.api new file mode 100644 index 0000000..c5efb82 --- /dev/null +++ b/library/digest/api/digest.klib.api @@ -0,0 +1,33 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class org.kotlincrypto.core.digest/Digest : org.kotlincrypto.core/Algorithm, org.kotlincrypto.core/Copyable, org.kotlincrypto.core/Resettable, org.kotlincrypto.core/Updatable { // org.kotlincrypto.core.digest/Digest|null[0] + abstract fun compress(kotlin/ByteArray, kotlin/Int) // org.kotlincrypto.core.digest/Digest.compress|compress(kotlin.ByteArray;kotlin.Int){}[0] + abstract fun copy(org.kotlincrypto.core.digest.internal/DigestState): org.kotlincrypto.core.digest/Digest // org.kotlincrypto.core.digest/Digest.copy|copy(org.kotlincrypto.core.digest.internal.DigestState){}[0] + abstract fun digest(kotlin/Long, kotlin/Int, kotlin/ByteArray): kotlin/ByteArray // org.kotlincrypto.core.digest/Digest.digest|digest(kotlin.Long;kotlin.Int;kotlin.ByteArray){}[0] + abstract fun resetDigest() // org.kotlincrypto.core.digest/Digest.resetDigest|resetDigest(){}[0] + final fun algorithm(): kotlin/String // org.kotlincrypto.core.digest/Digest.algorithm|algorithm(){}[0] + final fun blockSize(): kotlin/Int // org.kotlincrypto.core.digest/Digest.blockSize|blockSize(){}[0] + final fun copy(): org.kotlincrypto.core.digest/Digest // org.kotlincrypto.core.digest/Digest.copy|copy(){}[0] + final fun digest(): kotlin/ByteArray // org.kotlincrypto.core.digest/Digest.digest|digest(){}[0] + final fun digest(kotlin/ByteArray): kotlin/ByteArray // org.kotlincrypto.core.digest/Digest.digest|digest(kotlin.ByteArray){}[0] + final fun digestLength(): kotlin/Int // org.kotlincrypto.core.digest/Digest.digestLength|digestLength(){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core.digest/Digest.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // org.kotlincrypto.core.digest/Digest.hashCode|hashCode(){}[0] + final fun reset() // org.kotlincrypto.core.digest/Digest.reset|reset(){}[0] + final fun toString(): kotlin/String // org.kotlincrypto.core.digest/Digest.toString|toString(){}[0] + final fun update(kotlin/ByteArray) // org.kotlincrypto.core.digest/Digest.update|update(kotlin.ByteArray){}[0] + final fun update(kotlin/ByteArray, kotlin/Int, kotlin/Int) // org.kotlincrypto.core.digest/Digest.update|update(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + open fun update(kotlin/Byte) // org.kotlincrypto.core.digest/Digest.update|update(kotlin.Byte){}[0] + open fun updateDigest(kotlin/Byte) // org.kotlincrypto.core.digest/Digest.updateDigest|updateDigest(kotlin.Byte){}[0] + open fun updateDigest(kotlin/ByteArray, kotlin/Int, kotlin/Int) // org.kotlincrypto.core.digest/Digest.updateDigest|updateDigest(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] +} + +sealed class org.kotlincrypto.core.digest.internal/DigestState { // org.kotlincrypto.core.digest.internal/DigestState|null[0] + constructor (kotlin/String, kotlin/Int, kotlin/Int) // org.kotlincrypto.core.digest.internal/DigestState.|(kotlin.String;kotlin.Int;kotlin.Int){}[0] +} diff --git a/library/mac/api/mac.klib.api b/library/mac/api/mac.klib.api new file mode 100644 index 0000000..040442d --- /dev/null +++ b/library/mac/api/mac.klib.api @@ -0,0 +1,35 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class org.kotlincrypto.core.mac/Mac : org.kotlincrypto.core/Algorithm, org.kotlincrypto.core/Copyable, org.kotlincrypto.core/Resettable, org.kotlincrypto.core/Updatable { // org.kotlincrypto.core.mac/Mac|null[0] + abstract fun copy(org.kotlincrypto.core.mac/Mac.Engine): org.kotlincrypto.core.mac/Mac // org.kotlincrypto.core.mac/Mac.copy|copy(org.kotlincrypto.core.mac.Mac.Engine){}[0] + final fun algorithm(): kotlin/String // org.kotlincrypto.core.mac/Mac.algorithm|algorithm(){}[0] + final fun copy(): org.kotlincrypto.core.mac/Mac // org.kotlincrypto.core.mac/Mac.copy|copy(){}[0] + final fun doFinal(): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.doFinal|doFinal(){}[0] + final fun doFinal(kotlin/ByteArray): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.doFinal|doFinal(kotlin.ByteArray){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core.mac/Mac.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // org.kotlincrypto.core.mac/Mac.hashCode|hashCode(){}[0] + final fun macLength(): kotlin/Int // org.kotlincrypto.core.mac/Mac.macLength|macLength(){}[0] + final fun reset() // org.kotlincrypto.core.mac/Mac.reset|reset(){}[0] + final fun toString(): kotlin/String // org.kotlincrypto.core.mac/Mac.toString|toString(){}[0] + final fun update(kotlin/Byte) // org.kotlincrypto.core.mac/Mac.update|update(kotlin.Byte){}[0] + final fun update(kotlin/ByteArray) // org.kotlincrypto.core.mac/Mac.update|update(kotlin.ByteArray){}[0] + final fun update(kotlin/ByteArray, kotlin/Int, kotlin/Int) // org.kotlincrypto.core.mac/Mac.update|update(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + + abstract class Engine : org.kotlincrypto.core/Copyable, org.kotlincrypto.core/Resettable, org.kotlincrypto.core/Updatable { // org.kotlincrypto.core.mac/Mac.Engine|null[0] + abstract fun doFinal(): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.Engine.doFinal|doFinal(){}[0] + abstract fun macLength(): kotlin/Int // org.kotlincrypto.core.mac/Mac.Engine.macLength|macLength(){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core.mac/Mac.Engine.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // org.kotlincrypto.core.mac/Mac.Engine.hashCode|hashCode(){}[0] + open fun update(kotlin/ByteArray) // org.kotlincrypto.core.mac/Mac.Engine.update|update(kotlin.ByteArray){}[0] + + abstract inner class State { // org.kotlincrypto.core.mac/Mac.Engine.State|null[0] + constructor () // org.kotlincrypto.core.mac/Mac.Engine.State.|(){}[0] + } + } +} diff --git a/library/xof/api/xof.klib.api b/library/xof/api/xof.klib.api new file mode 100644 index 0000000..3635e88 --- /dev/null +++ b/library/xof/api/xof.klib.api @@ -0,0 +1,54 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract interface org.kotlincrypto.core.xof/XofAlgorithm : org.kotlincrypto.core/Algorithm // org.kotlincrypto.core.xof/XofAlgorithm|null[0] + +abstract class <#A: org.kotlincrypto.core.xof/XofAlgorithm> org.kotlincrypto.core.xof/XofFactory { // org.kotlincrypto.core.xof/XofFactory|null[0] + abstract inner class XofDelegate : org.kotlincrypto.core.xof/Xof<#A>, org.kotlincrypto.core/Algorithm, org.kotlincrypto.core/Resettable, org.kotlincrypto.core/Updatable { // org.kotlincrypto.core.xof/XofFactory.XofDelegate|null[0] + constructor (#A) // org.kotlincrypto.core.xof/XofFactory.XofDelegate.|(2:0){}[0] + + final val delegate // org.kotlincrypto.core.xof/XofFactory.XofDelegate.delegate|{}delegate[0] + final fun (): #A // org.kotlincrypto.core.xof/XofFactory.XofDelegate.delegate.|(){}[0] + + abstract fun newReader(#A): org.kotlincrypto.core.xof/Xof.Reader<#A> // org.kotlincrypto.core.xof/XofFactory.XofDelegate.newReader|newReader(2:0){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core.xof/XofFactory.XofDelegate.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // org.kotlincrypto.core.xof/XofFactory.XofDelegate.hashCode|hashCode(){}[0] + final fun newReader(): org.kotlincrypto.core.xof/Xof.Reader<#A> // org.kotlincrypto.core.xof/XofFactory.XofDelegate.newReader|newReader(){}[0] + open fun algorithm(): kotlin/String // org.kotlincrypto.core.xof/XofFactory.XofDelegate.algorithm|algorithm(){}[0] + open fun reset() // org.kotlincrypto.core.xof/XofFactory.XofDelegate.reset|reset(){}[0] + open fun update(kotlin/Byte) // org.kotlincrypto.core.xof/XofFactory.XofDelegate.update|update(kotlin.Byte){}[0] + open fun update(kotlin/ByteArray) // org.kotlincrypto.core.xof/XofFactory.XofDelegate.update|update(kotlin.ByteArray){}[0] + open fun update(kotlin/ByteArray, kotlin/Int, kotlin/Int) // org.kotlincrypto.core.xof/XofFactory.XofDelegate.update|update(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + } +} + +sealed class <#A: org.kotlincrypto.core.xof/XofAlgorithm> org.kotlincrypto.core.xof/Xof : org.kotlincrypto.core/Algorithm, org.kotlincrypto.core/Copyable>, org.kotlincrypto.core/Resettable, org.kotlincrypto.core/Updatable { // org.kotlincrypto.core.xof/Xof|null[0] + constructor () // org.kotlincrypto.core.xof/Xof.|(){}[0] + + abstract fun newReader(): org.kotlincrypto.core.xof/Xof.Reader<#A> // org.kotlincrypto.core.xof/Xof.newReader|newReader(){}[0] + final fun <#A1: kotlin/Any?> use(kotlin/Boolean =..., kotlin/Function1, #A1>): #A1 // org.kotlincrypto.core.xof/Xof.use|use(kotlin.Boolean;kotlin.Function1,0:0>){0§}[0] + final fun reader(kotlin/Boolean =...): org.kotlincrypto.core.xof/Xof.Reader<#A> // org.kotlincrypto.core.xof/Xof.reader|reader(kotlin.Boolean){}[0] + final fun toString(): kotlin/String // org.kotlincrypto.core.xof/Xof.toString|toString(){}[0] + + abstract inner class Reader { // org.kotlincrypto.core.xof/Xof.Reader|null[0] + constructor () // org.kotlincrypto.core.xof/Xof.Reader.|(){}[0] + + final var bytesRead // org.kotlincrypto.core.xof/Xof.Reader.bytesRead|{}bytesRead[0] + final fun (): kotlin/Long // org.kotlincrypto.core.xof/Xof.Reader.bytesRead.|(){}[0] + final var isClosed // org.kotlincrypto.core.xof/Xof.Reader.isClosed|{}isClosed[0] + final fun (): kotlin/Boolean // org.kotlincrypto.core.xof/Xof.Reader.isClosed.|(){}[0] + + abstract fun closeProtected() // org.kotlincrypto.core.xof/Xof.Reader.closeProtected|closeProtected(){}[0] + abstract fun readProtected(kotlin/ByteArray, kotlin/Int, kotlin/Int, kotlin/Long) // org.kotlincrypto.core.xof/Xof.Reader.readProtected|readProtected(kotlin.ByteArray;kotlin.Int;kotlin.Int;kotlin.Long){}[0] + final fun <#A2: kotlin/Any?> use(kotlin/Function1, #A2>): #A2 // org.kotlincrypto.core.xof/Xof.Reader.use|use(kotlin.Function1,0:0>){0§}[0] + final fun close() // org.kotlincrypto.core.xof/Xof.Reader.close|close(){}[0] + final fun read(kotlin/ByteArray): kotlin/Int // org.kotlincrypto.core.xof/Xof.Reader.read|read(kotlin.ByteArray){}[0] + final fun read(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/Int // org.kotlincrypto.core.xof/Xof.Reader.read|read(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + final fun toString(): kotlin/String // org.kotlincrypto.core.xof/Xof.Reader.toString|toString(){}[0] + } +} diff --git a/test-android/api/test-android.klib.api b/test-android/api/test-android.klib.api new file mode 100644 index 0000000..e69de29