From 5fa768d5f95a1c355bc9ffdeb8693f1a9eb21a55 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:32:56 +0100 Subject: [PATCH 01/37] update bcv to 0.14.0 --- README.md | 4 +-- gradle/libs.versions.toml | 2 +- modules/bcv-gradle-plugin/build.gradle.kts | 32 +++++++++++++++++++ .../src/main/kotlin/BCVProjectPlugin.kt | 7 ++-- .../kotlin/workers/BCVSignaturesWorker.kt | 3 ++ 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 434be9f..57727dc 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ or (**experimentally**) [as a Settings plugin](#settings-plugin) in `settings.gr The minimal supported Gradle version is 7.6. -By default, BCV-MU uses BCV version `0.13.2`, which can be overridden, but may introduce runtime +By default, BCV-MU uses BCV version `0.14.0`, which can be overridden, but may introduce runtime errors. ### Build plugin @@ -100,7 +100,7 @@ binaryCompatibilityValidator { bcvEnabled.set(true) // Override the default BCV version - kotlinxBinaryCompatibilityValidatorVersion.set("0.13.2") + kotlinxBinaryCompatibilityValidatorVersion.set("0.14.0") } ``` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b353dcc..eb58f62 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ kotlinGradle = "1.9.22" javaDiffUtils = "4.12" junit = "5.10.1" kotest = "5.8.0" -kotlinx-bcv = "0.13.2" +kotlinx-bcv = "0.14.0" gradlePluginPublishPlugin = "1.2.1" shadowPlugin = "8.1.1" diff --git a/modules/bcv-gradle-plugin/build.gradle.kts b/modules/bcv-gradle-plugin/build.gradle.kts index 557ff59..88e5e76 100644 --- a/modules/bcv-gradle-plugin/build.gradle.kts +++ b/modules/bcv-gradle-plugin/build.gradle.kts @@ -130,3 +130,35 @@ publishing { } } } + +val createBCVProperties by tasks.registering { + val bcvVersion = libs.versions.kotlinx.bcv + inputs.property("bcvVersion", bcvVersion) + + val generatedSource = layout.buildDirectory.dir("generated-src/main/kotlin/") + outputs.dir(generatedSource) + .withPropertyName("generatedSource") + + doLast { + val bcvMuBuildPropertiesFile = generatedSource.get() + .file("dev/adamko/kotlin/binary_compatibility_validator/internal/BCVProperties.kt") + + bcvMuBuildPropertiesFile.asFile.apply { + parentFile.mkdirs() + writeText( + """ + |package dev.adamko.kotlin.binary_compatibility_validator.internal + | + |internal object BCVProperties { + | const val bcvVersion: String = "${bcvVersion.get()}" + |} + | + """.trimMargin() + ) + } + } +} + +kotlin.sourceSets.main { + kotlin.srcDir(createBCVProperties) +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 080fd12..4b414e0 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -7,10 +7,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.API_ import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.EXTENSION_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.RUNTIME_CLASSPATH_CONFIGURATION_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.RUNTIME_CLASSPATH_RESOLVER_CONFIGURATION_NAME -import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi -import dev.adamko.kotlin.binary_compatibility_validator.internal.declarable -import dev.adamko.kotlin.binary_compatibility_validator.internal.resolvable -import dev.adamko.kotlin.binary_compatibility_validator.internal.sourceSets +import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiCheckTask import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiDumpTask import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiGenerateTask @@ -97,7 +94,7 @@ constructor( enabled.convention(true) outputApiDir.convention(layout.projectDirectory.dir(API_DIR)) projectName.convention(providers.provider { project.name }) - kotlinxBinaryCompatibilityValidatorVersion.convention("0.13.1") + kotlinxBinaryCompatibilityValidatorVersion.convention(BCVProperties.bcvVersion) } extension.targets.configureEach { diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt index 45da136..8f95750 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt @@ -105,6 +105,9 @@ abstract class BCVSignaturesWorker : WorkAction .filterOutAnnotated(ignoredMarkers.map(::replaceDots).toSet()) } + // Hack to access internal properties :( + // See https://github.com/Kotlin/binary-compatibility-validator/issues/178 + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") private fun writeSignatures( outputApiDir: File, projectName: String?, From 4d4ba27eaa69b07ded71986360e7d712f9a54578 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Thu, 15 Feb 2024 22:32:07 +0100 Subject: [PATCH 02/37] - update tests - implement BCV features --- gradle.properties | 5 +- .../validation/test/AndroidLibraryTest.kt | 4 +- .../validation/test/IgnoredClassesTests.kt | 30 +++- .../validation/test/NonPublicMarkersTest.kt | 38 ++++ .../validation/test/OutputDirectoryTests.kt | 167 ++++++++++++++++++ .../validation/test/PublicMarkersTest.kt | 42 ++++- .../examples/classes/AnnotatedPackage.dump | 7 + .../resources/examples/classes/BuildCon.dump | 6 + .../resources/examples/classes/BuildCon.kt | 7 + .../classes/ClassFromAnnotatedPackage.kt | 4 + .../examples/classes/PackageAnnotation.java | 11 ++ .../examples/classes/package-info.java | 2 + .../gradle/base/jdkCompatibility.gradle.kts | 28 +++ ...dkCompatibilityWithExactVersion.gradle.kts | 23 +++ .../gradle/base/withPlugin.gradle.kts | 2 +- .../nonPublicMarkers/packages.gradle.kts | 3 + .../outputDirectory/different.gradle.kts | 3 + .../outputDirectory/outer.gradle.kts | 3 + .../outputDirectory/subdirectory.gradle.kts | 3 + .../publicMarkers/packages.gradle.kts | 3 + .../settings/jdk-provisioning.gradle.kts | 3 + .../testFixtures/kotlin/GradleTestKitUtils.kt | 4 +- .../src/testFixtures/kotlin/api/TestDsl.kt | 4 +- .../src/main/kotlin/BCVProjectPlugin.kt | 21 ++- .../src/main/kotlin/tasks/BCVApiCheckTask.kt | 24 ++- .../src/main/kotlin/tasks/BCVApiDumpTask.kt | 36 +++- .../main/kotlin/tasks/BCVApiGenerateTask.kt | 1 + .../kotlin/workers/BCVSignaturesWorker.kt | 70 +++++--- 28 files changed, 503 insertions(+), 51 deletions(-) create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnnotatedPackage.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassFromAnnotatedPackage.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/PackageAnnotation.java create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/package-info.java create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts diff --git a/gradle.properties b/gradle.properties index 92c473a..f672d9c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError org.gradle.caching=true @@ -7,6 +7,3 @@ org.gradle.unsafe.configuration-cache-problems=warn org.gradle.parallel=true org.gradle.welcome=never - -# https://github.com/gradle/gradle/issues/9268#issuecomment-1397116301 -systemProp.org.gradle.unsafe.kotlin.assignment=true diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt index f168e08..81bd85e 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt @@ -1,8 +1,8 @@ package kotlinx.validation.test -import dev.adamko.kotlin.binary_compatibility_validator.test.* -import dev.adamko.kotlin.binary_compatibility_validator.test.utils.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveRunTask import java.io.File import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Disabled diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt index 0c59131..9403873 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt @@ -84,7 +84,35 @@ internal class IgnoredClassesTests : BaseKotlinGradleTest() { val expected = readResourceFile("/examples/classes/AnotherBuildConfig.dump") rootProjectApiDump.readText().shouldBeEqualComparingTo(expected) -// Assertions.assertThat(rootProjectApiDump.readText()).isEqualToIgnoringNewLines(expected) + } + } + + @Test + fun `apiDump should dump class whose name is a subsset of another class that is excluded via ignoredClasses`() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts") + } + kotlin("BuildConfig.kt") { + resolve("/examples/classes/BuildConfig.kt") + } + kotlin("BuildCon.kt") { + resolve("/examples/classes/BuildCon.kt") + } + + runner { + arguments.add(":apiDump") + } + } + + runner.build().apply { + shouldHaveRunTask(":apiDump", SUCCESS) + + assertTrue(rootProjectApiDump.exists(), "api dump file should exist") + + val expected = readResourceFile("/examples/classes/BuildCon.dump") + rootProjectApiDump.readText().shouldBeEqualComparingTo(expected) } } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt index 9f39c8e..94f13e4 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt @@ -3,6 +3,8 @@ package kotlinx.validation.test import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.shouldBe import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Test @@ -33,4 +35,40 @@ class NonPublicMarkersTest : BaseKotlinGradleTest() { shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) } } + + @Test + fun testFiltrationByPackageLevelAnnotations() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts") + } + java("annotated/PackageAnnotation.java") { + resolve("/examples/classes/PackageAnnotation.java") + } + java("annotated/package-info.java") { + resolve("/examples/classes/package-info.java") + } + kotlin("ClassFromAnnotatedPackage.kt") { + resolve("/examples/classes/ClassFromAnnotatedPackage.kt") + } + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + runner { + arguments.add(":apiDump") + } + } + + runner + .forwardOutput() + .build { + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + + rootProjectApiDump.shouldExist() + + val dumpFile = readResourceFile("/examples/classes/AnotherBuildConfig.dump") + rootProjectApiDump.readText() shouldBe dumpFile + } + } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt new file mode 100644 index 0000000..59fde83 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt @@ -0,0 +1,167 @@ +package kotlinx.validation.test + +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.buildAndFail +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome +import io.kotest.assertions.withClue +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import org.gradle.testkit.runner.TaskOutcome.FAILED +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.junit.jupiter.api.Test + +class OutputDirectoryTests : BaseKotlinGradleTest() { + + @Test + fun dumpIntoCustomDirectory() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/outputDirectory/different.gradle.kts") + } + + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + dir("api") { + file("letMeBe.txt") + } + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + + val dumpFile = rootProjectDir.resolve("custom/${rootProjectDir.name}.api") + dumpFile.shouldExist() + + val expected = readResourceFile("/examples/classes/AnotherBuildConfig.dump") + dumpFile.readText() shouldBe expected + + val fileInsideDir = rootProjectDir.resolve("api").resolve("letMeBe.txt") + withClue("existing api directory should not be overwritten") { + fileInsideDir.shouldExist() + } + } + } + + @Test + fun validateDumpFromACustomDirectory() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/outputDirectory/different.gradle.kts") + } + + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + dir("custom") { + file("${rootProjectDir.name}.api") { + resolve("/examples/classes/AnotherBuildConfig.dump") + } + } + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) + } + } + + @Test + fun dumpIntoSubdirectory() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts") + } + + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + + val dumpFile = rootProjectDir.resolve("validation/api/${rootProjectDir.name}.api") + dumpFile.shouldExist() + + val expected = readResourceFile("/examples/classes/AnotherBuildConfig.dump") + dumpFile.readText() shouldBe expected + } + } + + @Test + fun validateDumpFromASubdirectory() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts") + } + + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + dir("validation") { + dir("api") { + file("${rootProjectDir.name}.api") { + resolve("/examples/classes/AnotherBuildConfig.dump") + } + } + } + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) + } + } + + @Test + fun dumpIntoParentDirectory() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/outputDirectory/outer.gradle.kts") + } + + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + + runner { + arguments.add(":apiDump") + } + } + + runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiDump", FAILED) + + output shouldContain /* language=text */ """ + |> Error: Invalid output apiDirectory + | + | apiDirectory is set to a custom directory, outside of the current project directory. + | This is not permitted. apiDirectory must be a subdirectory of project ':' (the root project) directory. + | + | Remove the custom apiDirectory, or update apiDirectory to be a project subdirectory. + """.trimMargin() + } + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt index dea8f24..b4580d6 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt @@ -2,7 +2,11 @@ package kotlinx.validation.test import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.invariantNewlines import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.shouldBe import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Test @@ -15,19 +19,15 @@ class PublicMarkersTest : BaseKotlinGradleTest() { resolve("/examples/gradle/base/withPlugin.gradle.kts") resolve("/examples/gradle/configuration/publicMarkers/markers.gradle.kts") } - kotlin("ClassWithPublicMarkers.kt") { resolve("/examples/classes/ClassWithPublicMarkers.kt") } - kotlin("ClassInPublicPackage.kt") { resolve("/examples/classes/ClassInPublicPackage.kt") } - apiFile(projectName = rootProjectDir.name) { resolve("/examples/classes/ClassWithPublicMarkers.dump") } - runner { arguments.add(":apiCheck") } @@ -37,4 +37,38 @@ class PublicMarkersTest : BaseKotlinGradleTest() { shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) } } + + @Test + fun testFiltrationByPackageLevelAnnotations() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/publicMarkers/packages.gradle.kts") + } + java("annotated/PackageAnnotation.java") { + resolve("/examples/classes/PackageAnnotation.java") + } + java("annotated/package-info.java") { + resolve("/examples/classes/package-info.java") + } + kotlin("ClassFromAnnotatedPackage.kt") { + resolve("/examples/classes/ClassFromAnnotatedPackage.kt") + } + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + runner { + arguments.add(":apiDump") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + + rootProjectApiDump.shouldExist() + rootProjectApiDump.shouldBeAFile() + val expected = readResourceFile("/examples/classes/AnnotatedPackage.dump") + rootProjectApiDump.readText().invariantNewlines() shouldBe expected + } + } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnnotatedPackage.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnnotatedPackage.dump new file mode 100644 index 0000000..0dcd804 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnnotatedPackage.dump @@ -0,0 +1,7 @@ +public final class annotated/ClassFromAnnotatedPackage { + public fun ()V +} + +public abstract interface annotation class annotated/PackageAnnotation : java/lang/annotation/Annotation { +} + diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.dump new file mode 100644 index 0000000..b21a6b7 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.dump @@ -0,0 +1,6 @@ +public final class com/company/BuildCon { + public fun ()V + public final fun f1 ()I + public final fun getP1 ()I +} + diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.kt new file mode 100644 index 0000000..f9c476e --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/BuildCon.kt @@ -0,0 +1,7 @@ +package com.company + +public class BuildCon { + public val p1 = 1 + + public fun f1() = p1 +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassFromAnnotatedPackage.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassFromAnnotatedPackage.kt new file mode 100644 index 0000000..ebd1bef --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassFromAnnotatedPackage.kt @@ -0,0 +1,4 @@ +package annotated + +class ClassFromAnnotatedPackage { +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/PackageAnnotation.java b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/PackageAnnotation.java new file mode 100644 index 0000000..6e41985 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/PackageAnnotation.java @@ -0,0 +1,11 @@ +package annotated; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PACKAGE) +public @interface PackageAnnotation { +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/package-info.java b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/package-info.java new file mode 100644 index 0000000..b15e17d --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/package-info.java @@ -0,0 +1,2 @@ +@PackageAnnotation +package annotated; diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts new file mode 100644 index 0000000..3ebdb4d --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts @@ -0,0 +1,28 @@ +import org.jetbrains.kotlin.config.JvmTarget + +plugins { + kotlin("jvm") version "1.9.22" + id("org.jetbrains.kotlinx.binary-compatibility-validator") +} + +repositories { + mavenCentral() +} + +val minTarget = JvmTarget.supportedValues().minBy { it.majorVersion } +val maxTarget = JvmTarget.supportedValues().maxBy { it.majorVersion } + +val useMax = (project.properties["useMaxVersion"]?.toString() ?: "false").toBoolean() +val target = (if (useMax) maxTarget else minTarget).toString() + +val toolchainVersion = target.split('.').last().toInt() + +kotlin { + jvmToolchain(toolchainVersion) +} + +tasks.compileKotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(target)) + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts new file mode 100644 index 0000000..da90886 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts @@ -0,0 +1,23 @@ +import org.jetbrains.kotlin.config.JvmTarget + +plugins { + kotlin("jvm") version "1.9.22" + id("org.jetbrains.kotlinx.binary-compatibility-validator") +} + +repositories { + mavenCentral() +} + +val target = project.properties["jdkVersion"]!!.toString() +val toolchainVersion = target.split('.').last().toInt() + +kotlin { + jvmToolchain(toolchainVersion) +} + +tasks.compileKotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(target)) + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts index 07272f7..69984e0 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts @@ -1,4 +1,4 @@ plugins { - kotlin("jvm") version "1.7.20" + kotlin("jvm") version "1.8.0" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts new file mode 100644 index 0000000..b50120b --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts @@ -0,0 +1,3 @@ +configure { + ignoredMarkers.add("annotated.PackageAnnotation") +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts new file mode 100644 index 0000000..11b5528 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts @@ -0,0 +1,3 @@ +configure { + outputApiDir.set(layout.projectDirectory.dir("custom")) +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts new file mode 100644 index 0000000..20d781c --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts @@ -0,0 +1,3 @@ +configure { + outputApiDir.set(layout.projectDirectory.dir("../api")) +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts new file mode 100644 index 0000000..d408e15 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts @@ -0,0 +1,3 @@ +configure { + outputApiDir.set(layout.projectDirectory.dir("validation/api")) +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts new file mode 100644 index 0000000..b9edfa6 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts @@ -0,0 +1,3 @@ +configure { + publicMarkers.add("annotated.PackageAnnotation") +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts new file mode 100644 index 0000000..04abb29 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.7.0") +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt index 1085709..d0aeebb 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt @@ -176,7 +176,7 @@ internal fun devMavenRepoKotlinDsl(): String { |exclusiveContent { | forRepository { | maven(file("$devMavenRepoPathString")) { - | name = "Dev Maven Repo" + | name = "DevMavenRepo" | } | } | filter { @@ -196,7 +196,7 @@ private fun devMavenRepoGroovyDsl(): String { | forRepository { | maven { | url = file("$devMavenRepoPathString") - | name = "Dev Maven Repo" + | name = "DevMavenRepo" | } | } | filter { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt index d7a5aff..3cbe7e4 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt @@ -157,7 +157,7 @@ fun AppendableScope.addText(text: String) { } interface FileContainer { - fun file(fileName: String, fn: AppendableScope.() -> Unit) + fun file(fileName: String, fn: AppendableScope.() -> Unit = {}) } class BaseKotlinScope : FileContainer { @@ -194,7 +194,7 @@ class AppendableScope(val filePath: String) { class Runner { val arguments: MutableList = mutableListOf( "--configuration-cache", - "--info", + //"--info", "--stacktrace", ) } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 4b414e0..1da795d 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -12,6 +12,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiCheckTask import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiDumpTask import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiGenerateTask import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVDefaultTask +import java.io.File import javax.inject.Inject import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin @@ -48,21 +49,25 @@ constructor( project.tasks.withType().configureEach { bcvEnabled.convention(extension.enabled) + onlyIf("BCV is disabled") { bcvEnabled.get() } } project.tasks.withType().configureEach { runtimeClasspath.from(bcvGenerateClasspath) targets.addAllLater(providers.provider { extension.targets }) - onlyIf("Must have at least one target") { targets.isNotEmpty() } outputApiBuildDir.convention(layout.buildDirectory.dir("bcv-api")) projectName.convention(extension.projectName) + + onlyIf("Must have at least one target") { targets.isNotEmpty() } } project.tasks.withType().configureEach { outputs.dir(temporaryDir) // dummy output, so up-to-date checks work expectedProjectName.convention(extension.projectName) - expectedApiDirPath.convention(extension.outputApiDir.map { it.asFile.canonicalFile.absolutePath }) + expectedApiDirPath.convention( + extension.outputApiDir.map { it.asFile.canonicalFile.invariantSeparatorsPath } + ) } project.tasks.withType().configureEach { @@ -95,11 +100,23 @@ constructor( outputApiDir.convention(layout.projectDirectory.dir(API_DIR)) projectName.convention(providers.provider { project.name }) kotlinxBinaryCompatibilityValidatorVersion.convention(BCVProperties.bcvVersion) + + // have to set conventions because otherwise .add("...") doesn't work + ignoredMarkers.convention(emptyList()) + publicPackages.convention(emptyList()) + publicClasses.convention(emptyList()) + publicMarkers.convention(emptyList()) + ignoredClasses.convention(emptyList()) + @Suppress("DEPRECATION") + nonPublicMarkers.convention(null) } extension.targets.configureEach { enabled.convention(true) + inputClasses.setFrom(emptyList()) + inputJar.convention(null) + publicMarkers.convention(extension.publicMarkers) publicPackages.convention(extension.publicPackages) publicClasses.convention(extension.publicClasses) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index 539595f..eab8151 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -66,7 +66,7 @@ constructor( val checkApiDeclarationPaths = projectApiDir.relativePathsOfContent { !isDirectory } val builtApiDeclarationPaths = apiBuildDir.relativePathsOfContent { !isDirectory } - logger.info("checkApiDeclarationPaths: $checkApiDeclarationPaths") + logger.info("[$path] checkApiDeclarationPaths: $checkApiDeclarationPaths") checkApiDeclarationPaths.forEach { checkApiDeclarationPath -> logger.info("---------------------------") @@ -83,22 +83,22 @@ constructor( checkApiDeclaration: File, builtApiDeclaration: File?, ) { - logger.info("checkApiDeclaration: $checkApiDeclaration") - logger.info("builtApiDeclaration: $builtApiDeclaration") + logger.info("[$path] checkApiDeclaration: $checkApiDeclaration") + logger.info("[$path] builtApiDeclaration: $builtApiDeclaration") val allBuiltFilePaths = builtApiDeclaration?.parentFile.relativePathsOfContent() val allCheckFilePaths = checkApiDeclaration.parentFile.relativePathsOfContent() - logger.info("allBuiltPaths: $allBuiltFilePaths") - logger.info("allCheckFiles: $allCheckFilePaths") + logger.info("[$path] allBuiltPaths: $allBuiltFilePaths") + logger.info("[$path] allCheckFiles: $allCheckFilePaths") val builtFilePath = allBuiltFilePaths.singleOrNull() - ?: error("Expected a single file ${expectedProjectName.get()}.api, but found ${allBuiltFilePaths.size}: $allBuiltFilePaths") + ?: error("[$path] Expected a single file ${expectedProjectName.get()}.api, but found ${allBuiltFilePaths.size}: $allBuiltFilePaths") if (builtApiDeclaration == null || builtFilePath !in allCheckFilePaths) { val relativeDirPath = projectApiDir.get().toRelativeString(rootDir) + File.separator error( - "File ${builtFilePath.lastName} is missing from ${relativeDirPath}, please run '$apiDumpTaskPath' task to generate one" + "[$path] File ${builtFilePath.lastName} is missing from ${relativeDirPath}, please run '$apiDumpTaskPath' task to generate one" ) } @@ -155,7 +155,7 @@ constructor( } } -/* +/** * We use case-insensitive comparison to workaround issues with case-insensitive OSes and Gradle * behaving slightly different on different platforms. We neither know original sensitivity of * existing .api files, not build ones, because projectName that is part of the path can have any @@ -172,6 +172,14 @@ private class RelativePaths( operator fun get(path: RelativePath): RelativePath? = map[path] + override fun toString(): String = + map.keys.joinToString( + prefix = "RelativePaths(", + separator = "/", + postfix = ")", + transform = RelativePath::getPathString, + ) + companion object { private fun caseInsensitiveMap() = TreeMap { path1, path2 -> diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt index ee8f7eb..3ef6ce1 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt @@ -1,10 +1,13 @@ package dev.adamko.kotlin.binary_compatibility_validator.tasks import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.isRootProject import javax.inject.Inject +import kotlin.io.path.invariantSeparatorsPathString import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.ProjectLayout import org.gradle.api.tasks.* @CacheableTask @@ -12,7 +15,8 @@ abstract class BCVApiDumpTask @BCVInternalApi @Inject constructor( - private val fs: FileSystemOperations + private val fs: FileSystemOperations, + private val layout: ProjectLayout, ) : BCVDefaultTask() { @get:InputFiles @@ -22,8 +26,38 @@ constructor( @get:OutputDirectory abstract val apiDirectory: DirectoryProperty + private val projectGradlePath: String = + if (project.isRootProject) { + "project ':' (the root project)" + } else { + "subproject '${project.path}'" + } + @TaskAction fun action() { + validateApiDir() + updateDumpDir() + } + + private fun validateApiDir() { + val projectDir = layout.projectDirectory.asFile.toPath().toAbsolutePath().normalize() + val apiDir = projectDir.resolve(apiDirectory.get().asFile.toPath()).normalize() + require(apiDir.startsWith(projectDir)) { + /* language=text */ """ + |Error: Invalid output apiDirectory + | + |apiDirectory is set to a custom directory, outside of the current project directory. + |This is not permitted. apiDirectory must be a subdirectory of $projectGradlePath directory. + | + |Remove the custom apiDirectory, or update apiDirectory to be a project subdirectory. + | + |Project directory: ${projectDir.invariantSeparatorsPathString} + |apiDirectory: ${apiDir.invariantSeparatorsPathString} + """.trimMargin() + } + } + + private fun updateDumpDir() { fs.sync { from(apiDumpFiles) { include("**/*.api") diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index b77f0cb..bb2df11 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -91,6 +91,7 @@ constructor( @OptIn(BCVInternalApi::class) submit(BCVSignaturesWorker::class) worker@{ this@worker.projectName.set(task.projectName) + this@worker.taskPath.set(task.path) this@worker.outputApiDir.set(outputDir) this@worker.inputClasses.from(target.inputClasses) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt index 8f95750..bb6f507 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt @@ -34,8 +34,15 @@ abstract class BCVSignaturesWorker : WorkAction val ignoredClasses: SetProperty val projectName: Property + /** + * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, + * for log messages + */ + val taskPath: Property } + private val logTag: String by lazy { "[${parameters.taskPath.get()}:BCVSignaturesWorker]" } + override fun execute() { val projectName = parameters.projectName.get() @@ -56,13 +63,13 @@ abstract class BCVSignaturesWorker : WorkAction writeSignatures( outputApiDir = parameters.outputApiDir.get().asFile, projectName = parameters.projectName.get(), - signatures = signatures + signatures = signatures, ) signatures.count() } - logger.info("BCVSignaturesWorker generated $signaturesCount signatures for $projectName in $duration") + logger.info("$logTag generated $signaturesCount signatures for $projectName in $duration") } private fun generateSignatures( @@ -75,39 +82,62 @@ abstract class BCVSignaturesWorker : WorkAction ignoredMarkers: Set, ignoredPackages: Set, ): List { + + logger.info( + """ + $logTag inputJar : $inputJar + $logTag publicMarkers : $publicMarkers + $logTag publicPackages : $publicPackages + $logTag publicClasses : $publicClasses + $logTag ignoredClasses : $ignoredClasses + $logTag ignoredMarkers : $ignoredMarkers + $logTag ignoredPackages : $ignoredPackages + """.trimIndent() + ) + val signatures = when { // inputJar takes precedence if specified inputJar != null -> JarFile(inputJar.asFile).use { it.loadApiFromJvmClasses() } !inputClasses.isEmpty -> { - logger.info("inputClasses: ${inputClasses.files}") + logger.info("$logTag inputClasses: ${inputClasses.files}") val filteredInputClasses = inputClasses.asFileTree.matching { exclude("META-INF/**") include("**/*.class") } - logger.info("filteredInputClasses: ${filteredInputClasses.files}") + logger.info("$logTag filteredInputClasses: ${filteredInputClasses.files}") - filteredInputClasses.asSequence() + filteredInputClasses + .asSequence() .map(File::inputStream) .loadApiFromJvmClasses() } else -> - error("BCVSignaturesWorker should have either inputClassesDirs, or inputJar property set") + error("$logTag should have either inputClassesDirs, or inputJar property set") } + val publicPackagesNames = + signatures.extractAnnotatedPackages(publicMarkers.map(::replaceDots).toSet()) + val ignoredPackagesNames = + signatures.extractAnnotatedPackages(ignoredMarkers.map(::replaceDots).toSet()) + return signatures - .retainExplicitlyIncludedIfDeclared(publicPackages, publicClasses, publicMarkers) - .filterOutNonPublic(ignoredPackages, ignoredClasses) + .retainExplicitlyIncludedIfDeclared( + publicPackages = publicPackages + publicPackagesNames, + publicClasses = publicClasses, + publicMarkerAnnotations = publicMarkers, + ) + .filterOutNonPublic( + nonPublicPackages = ignoredPackages + ignoredPackagesNames, + nonPublicClasses = ignoredClasses, + ) .filterOutAnnotated(ignoredMarkers.map(::replaceDots).toSet()) } - // Hack to access internal properties :( - // See https://github.com/Kotlin/binary-compatibility-validator/issues/178 - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") private fun writeSignatures( outputApiDir: File, projectName: String?, @@ -115,19 +145,11 @@ abstract class BCVSignaturesWorker : WorkAction ) { outputApiDir.mkdirs() - outputApiDir - .resolve("$projectName.api") - .bufferedWriter().use { writer -> - signatures - .sortedBy { it.name } - .forEach { api -> - writer.append(api.signature).appendLine(" {") - api.memberSignatures - .sortedWith(MEMBER_SORT_ORDER) - .forEach { writer.append("\t").appendLine(it.signature) } - writer.appendLine("}\n") - } - } + val apiFile = outputApiDir.resolve("$projectName.api") + + apiFile.bufferedWriter().use { writer -> + signatures.dump(writer) + } } companion object { From c68833a1bfc0160ae1cf60e7e94f4e4e32d24116 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 19 Feb 2024 14:03:30 +0100 Subject: [PATCH 03/37] minor format log message --- .../bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index eab8151..073ad43 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -58,7 +58,7 @@ constructor( ?: error( """ Expected folder with API declarations '${expectedApiDirPath.get()}' does not exist. - Please ensure that task '$apiDumpTaskPath' was executed in order to get API dump to compare the build against + Please ensure that task '$apiDumpTaskPath' was executed in order to get API dump to compare the build against. """.trimIndent() ) From 832ad110947e697525d99308dcf5fc0426ac2274 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:29:22 +0200 Subject: [PATCH 04/37] wip implementation of bcv 0.15, supporting klibs --- .github/workflows/run_publish_maven.yml | 2 +- README.md | 14 +- build.gradle.kts | 28 +- .../gradle-plugin-variants.gradle.kts | 57 -- .../conventions/maven-publishing.gradle.kts | 29 +- .../src/main/kotlin/buildsrc/utils/gradle.kt | 18 - .../main/kotlin/buildsrc/utils/intellij.kt | 70 ++ .../kotlin/buildsrc/utils/kotlinStdlib.kt | 3 + gradle.properties | 6 +- gradle/libs.versions.toml | 10 +- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 20 +- .../build.gradle.kts | 2 + .../validation/test/AndroidLibraryTest.kt | 2 - .../validation/test/DefaultConfigTests.kt | 1 - .../test/JavaVersionsCompatibilityTest.kt | 81 ++ .../validation/test/JvmProjectTests.kt | 54 ++ .../validation/test/KlibVerificationTests.kt | 807 ++++++++++++++++++ .../test/MultiPlatformSingleJvmTargetTest.kt | 47 +- .../validation/test/MultipleJvmTargetsTest.kt | 2 - .../validation/test/NonPublicMarkersTest.kt | 46 + .../validation/test/PublicMarkersTest.kt | 36 + .../validation/test/SettingsPluginDslTest.kt | 73 +- .../AnotherBuildConfig.klib.clash.dump | 17 + .../AnotherBuildConfig.klib.custom.dump | 14 + .../classes/AnotherBuildConfig.klib.dump | 14 + .../AnotherBuildConfig.klib.linuxX64Only.dump | 14 + ...AnotherBuildConfig.klib.renamedTarget.dump | 14 + .../classes/AnotherBuildConfig.klib.web.dump | 14 + ...AnotherBuildConfigLinux.klib.grouping.dump | 17 + .../classes/AnotherBuildConfigLinuxArm64.kt | 8 + ...notherBuildConfigLinuxArm64Extra.klib.dump | 16 + .../AnotherBuildConfigModified.klib.dump | 15 + .../classes/AnotherBuildConfigModified.kt | 14 + .../classes/ClassWithPublicMarkers.klib.dump | 45 + .../examples/classes/Empty.klib.dump | 6 + .../examples/classes/GeneratedSources.dump | 5 + .../classes/GeneratedSources.klib.dump | 12 + .../classes/HiddenDeclarations.klib.dump | 11 + .../examples/classes/HiddenDeclarations.kt | 43 + .../examples/classes/NonPublicMarkers.kt | 34 + .../examples/classes/Properties.klib.dump | 17 + .../resources/examples/classes/SubPackage.kt | 10 + .../examples/classes/Subclasses.dump | 7 + .../examples/classes/Subclasses.klib.dump | 14 + .../resources/examples/classes/Subclasses.kt | 16 + .../TopLevelDeclarations.klib.all.dump | 67 ++ .../classes/TopLevelDeclarations.klib.dump | 67 ++ .../TopLevelDeclarations.klib.unsup.dump | 67 ++ .../classes/TopLevelDeclarations.klib.v1.dump | 67 ++ ...lDeclarations.klib.with.guessed.linux.dump | 67 ++ .../TopLevelDeclarations.klib.with.linux.dump | 67 ++ .../examples/classes/TopLevelDeclarations.kt | 35 + .../enableJvmInWithNativePlugin.gradle.kts | 3 + .../gradle/base/jdkCompatibility.gradle.kts | 30 +- ...dkCompatibilityWithExactVersion.gradle.kts | 18 +- .../multiplatformWithJvmTargets.gradle.kts | 12 +- ...ultiplatformWithSingleJvmTarget.gradle.kts | 4 +- .../gradle/base/withNativePlugin.gradle.kts | 27 + .../withNativePluginAndNoTargets.gradle.kts | 19 + ...withNativePluginAndSingleTarget.gradle.kts | 20 + .../gradle/base/withPlugin.gradle.kts | 2 +- .../appleTargets/targets.gradle.kts | 15 + .../generatedJvmSources.gradle.kts | 23 + .../generatedSources.gradle.kts | 23 + .../grouping/clashingTargetNames.gradle.kts | 9 + .../grouping/customTargetNames.gradle.kts | 12 + .../ignoreSubclasses/ignore.gradle.kts | 9 + .../jarAsInput/inputJar.gradle.kts | 2 +- .../nonNativeKlibTargets/targets.gradle.kts | 5 + .../nonPublicMarkers/klib.gradle.kts | 13 + .../signatures/invalid.gradle.kts | 10 + .../configuration/signatures/v1.gradle.kts | 8 + .../unsupported/enforce.gradle.kts | 8 + .../settings/jdk-provisioning.gradle.kts | 2 +- .../settings-android-project.gradle.kts | 4 +- .../settings-name-testproject.gradle.kts | 2 +- .../settings-with-hierarchy.gradle.kts | 8 +- .../kotlin/api/BaseKotlinGradleTest.kt | 6 +- .../src/testFixtures/kotlin/api/TestDsl.kt | 68 +- .../testFixtures/kotlin/gradleRunnerUtils.kt | 6 +- .../api/bcv-gradle-plugin.api | 80 +- modules/bcv-gradle-plugin/build.gradle.kts | 19 +- .../src/main/kotlin/BCVPlugin.kt | 1 + .../src/main/kotlin/BCVProjectExtension.kt | 24 +- .../src/main/kotlin/BCVProjectPlugin.kt | 224 +++-- .../src/main/kotlin/BCVSettingsPlugin.kt | 29 +- .../main/kotlin/adapters/AndroidAdapter.kt | 22 + .../main/kotlin/adapters/JavaTestFixtures.kt | 27 + .../main/kotlin/adapters/KotlinJvmAdapter.kt | 25 + .../adapters/KotlinMultiplatformAdapter.kt | 130 +++ .../kotlin/internal/BCVExperimentalApi.kt | 19 + .../src/main/kotlin/internal/GradlePath.kt | 3 +- .../src/main/kotlin/internal/gradleUtils.kt | 49 +- .../src/main/kotlin/targets/BCVJvmTarget.kt | 46 + .../src/main/kotlin/targets/BCVKLibTarget.kt | 44 + .../src/main/kotlin/targets/BCVTarget.kt | 40 +- ...{BCVTargetSpec.kt => BCVTargetBaseSpec.kt} | 18 +- .../main/kotlin/targets/BCVTargetDefaults.kt | 50 ++ .../kotlin/targets/KLibSignatureVersion.kt | 36 + .../main/kotlin/targets/KLibValidationSpec.kt | 47 + .../src/main/kotlin/tasks/BCVApiCheckTask.kt | 12 +- .../src/main/kotlin/tasks/BCVApiDumpTask.kt | 3 +- .../tasks/BCVApiGeneratePreparationTask.kt | 37 + .../main/kotlin/tasks/BCVApiGenerateTask.kt | 144 +++- ...aturesWorker.kt => JvmSignaturesWorker.kt} | 3 +- .../kotlin/workers/KLibSignaturesWorker.kt | 170 ++++ .../kotlin/kotestGradleAssertions.kt | 11 +- settings.gradle.kts | 46 +- 110 files changed, 3457 insertions(+), 494 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JavaVersionsCompatibilityTest.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.clash.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.custom.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.linuxX64Only.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.renamedTarget.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.web.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassWithPublicMarkers.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Empty.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Properties.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/enableJvmInWithNativePlugin.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/appleTargets/targets.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedJvmSources.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedSources.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/customTargetNames.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonNativeKlibTargets/targets.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/adapters/AndroidAdapter.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/adapters/JavaTestFixtures.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinJvmAdapter.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/internal/BCVExperimentalApi.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt rename modules/bcv-gradle-plugin/src/main/kotlin/targets/{BCVTargetSpec.kt => BCVTargetBaseSpec.kt} (79%) create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibSignatureVersion.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt rename modules/bcv-gradle-plugin/src/main/kotlin/workers/{BCVSignaturesWorker.kt => JvmSignaturesWorker.kt} (98%) create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt diff --git a/.github/workflows/run_publish_maven.yml b/.github/workflows/run_publish_maven.yml index 3d0cf4a..c48da70 100644 --- a/.github/workflows/run_publish_maven.yml +++ b/.github/workflows/run_publish_maven.yml @@ -40,5 +40,5 @@ jobs: with: runs-on: macos-latest # only macOS supports building all Kotlin targets gradle-task: >- - publishAllPublicationsToSonatypeReleaseRepository --stacktrace --no-configuration-cache --no-parallel + publishAllPublicationsToSonatypeReleaseRepository --stacktrace --no-parallel checkout-ref: ${{ inputs.checkout-ref }} diff --git a/README.md b/README.md index 57727dc..1c44472 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ or (**experimentally**) [as a Settings plugin](#settings-plugin) in `settings.gr #### Requirements -The minimal supported Gradle version is 7.6. +The minimal supported Gradle version is 7.6.4. -By default, BCV-MU uses BCV version `0.14.0`, which can be overridden, but may introduce runtime +By default, BCV-MU uses BCV version `0.15.0-Beta.2`, which can be overridden, but may introduce runtime errors. ### Build plugin @@ -49,7 +49,7 @@ plugins { } ``` -To initialise the API declarations, run the Gradle task +To initialize the API declarations, run the Gradle task ```shell ./gradlew apiDump @@ -74,7 +74,7 @@ BCV-MU can be configured in a similar manner to BCV: // build.gradle.kts plugins { - kotlin("jvm") version "1.8.10" + kotlin("jvm") version "1.9.22" id("dev.adamko.kotlin.binary-compatibility-validator") version "$bcvMuVersion" } @@ -100,7 +100,7 @@ binaryCompatibilityValidator { bcvEnabled.set(true) // Override the default BCV version - kotlinxBinaryCompatibilityValidatorVersion.set("0.14.0") + kotlinxBinaryCompatibilityValidatorVersion.set("0.15.0-Beta.2") } ``` @@ -113,7 +113,7 @@ these `BCVTarget`s can be specifically modified, or manually defined, for fine-g // build.gradle.kts plugins { - kotlin("jvm") version "1.8.10" + kotlin("jvm") version "1.9.22" id("dev.adamko.kotlin.binary-compatibility-validator") version "$bcvMuVersion" `java-test-fixtures` } @@ -214,7 +214,7 @@ All subprojects are included by default, and can be excluded using BCV-MU config buildscript { dependencies { // BCV-MU requires the Kotlin Gradle Plugin classes are present - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.8.10") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.22") } } diff --git a/build.gradle.kts b/build.gradle.kts index 6e678c1..5a486a7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ -import buildsrc.utils.generatedKotlinDslAccessorDirs +import buildsrc.utils.excludeProjectConfigurationDirs +import buildsrc.utils.filterContains plugins { buildsrc.conventions.base @@ -13,20 +14,17 @@ project.version = object { idea { module { - excludeDirs = excludeDirs + - layout.generatedKotlinDslAccessorDirs() + - layout.files( - ".idea", - "gradle/wrapper", - ) + excludeProjectConfigurationDirs(layout, providers) } } + val readmeCheck by tasks.registering { group = LifecycleBasePlugin.VERIFICATION_GROUP val readme = providers.fileContents(layout.projectDirectory.file("README.md")).asText val supportedGradleVersion = libs.versions.supportedGradleVersion val kotlinBcvVersion = libs.versions.kotlinx.bcv + val kgpVersion = embeddedKotlinVersion doLast { readme.get().let { readme -> @@ -39,6 +37,20 @@ val readmeCheck by tasks.registering { require("The minimal supported Gradle version is ${supportedGradleVersion.get()}" in readme) { "Incorrect Gradle version in README" } + readme.lines() + .filterContains("kotlin(\"jvm\") version ") + .forEach { line -> + require("kotlin(\"jvm\") version \"$kgpVersion\"" in line) { + "Incorrect Kotlin JVM plugin (expected $kgpVersion) version in README\n $line" + } + } + readme.lines() + .filterContains("""org.jetbrains.kotlin:kotlin-gradle-plugin-api:""") + .forEach { line -> + require("""classpath("org.jetbrains.kotlin:kotlin-gradle-plugin-api:$kgpVersion")""" in line) { + "Incorrect kotlin-gradle-plugin-api version (expected $kgpVersion) version in README\n $line" + } + } } } } @@ -50,7 +62,7 @@ tasks.check { val projectVersion by tasks.registering { description = "prints the project version" group = "help" - val version = providers.provider { project.version } + val version = providers.provider { project.version.toString() } inputs.property("version", version) outputs.cacheIf("logging task, it should always run") { false } doLast { diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts deleted file mode 100644 index 334374f..0000000 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts +++ /dev/null @@ -1,57 +0,0 @@ -package buildsrc.conventions - -import org.gradle.api.attributes.plugin.GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE -import buildsrc.utils.configurationNames - -plugins { - id("buildsrc.conventions.base") - `java-gradle-plugin` -} - -configurations - .matching { it.isCanBeConsumed && it.name in sourceSets.main.get().configurationNames() } - .configureEach { - attributes { - attribute(GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("7.6")) - } - } - -fun createGradleVariantSourceSet(name: String, gradleVersion: String) { - val variantSources = sourceSets.create(name) - - java { - registerFeature(variantSources.name) { - usingSourceSet(variantSources) - capability("${project.group}", "${project.name}", "${project.version}") - - withJavadocJar() - withSourcesJar() - } - } - - configurations - .matching { it.isCanBeConsumed && it.name in variantSources.configurationNames() } - .configureEach { - attributes { - attribute(GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named(gradleVersion)) - } - } - - tasks.named(variantSources.processResourcesTaskName) { - val copyPluginDescriptors = rootSpec.addChild() -// copyPluginDescriptors.into("META-INF/gradle-plugins") - copyPluginDescriptors.into(tasks.pluginDescriptors.map { it.outputDirectory.asFile.get().invariantSeparatorsPath }) - copyPluginDescriptors.from(tasks.pluginDescriptors) - } - - dependencies { - variantSources.compileOnlyConfigurationName(gradleApi()) - } - - project.tasks.named(variantSources.compileJavaTaskName).configure { - classpath += sourceSets.main.get().compileClasspath - } -} - -//val mainGradle = registerGradleVariant("mainGradle", "7.6") -createGradleVariantSourceSet("mainGradle8", "8.1") diff --git a/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts index 4dcac80..5381a17 100644 --- a/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts @@ -2,6 +2,7 @@ package buildsrc.conventions import buildsrc.settings.MavenPublishingSettings + plugins { `maven-publish` signing @@ -16,7 +17,7 @@ publishing { publications.withType().configureEach { pom { name.convention("Binary Compatibility Validator MU") - description.convention("BCV-MU is a Gradle Plugin that validates the public JVM binary API of libraries, to make sure that breaking changes are tracked.") + description.convention("BCV-MU is a Gradle Plugin that validates the public API of libraries, to make sure that breaking changes are tracked.") url.convention("https://github.com/adamko-dev/kotlin-binary-compatibility-validator-mu") scm { @@ -89,23 +90,35 @@ signing { //endregion -//region Fix Gradle warning about signing tasks using publishing task outputs without explicit dependencies -// https://youtrack.jetbrains.com/issue/KT-46466 https://github.com/gradle/gradle/issues/26091 tasks.withType().configureEach { + + //region Fix Gradle warning about signing tasks using publishing task outputs without explicit dependencies + // https://youtrack.jetbrains.com/issue/KT-46466 https://github.com/gradle/gradle/issues/26091 val signingTasks = tasks.withType() mustRunAfter(signingTasks) -} -//endregion - + //endregion -//region publishing logging -tasks.withType().configureEach { + //region publishing logging val publicationGAV = provider { publication?.run { "$group:$artifactId:$version" } } doLast("log publication GAV") { if (publicationGAV.isPresent) { logger.info("[task: ${path}] ${publicationGAV.get()}") } } + //endregion +} + + +//region Maven Central can't handle parallel uploads, so limit parallel uploads with a service. +abstract class MavenPublishLimiter : BuildService + +val mavenPublishLimiter = + gradle.sharedServices.registerIfAbsent("mavenPublishLimiter", MavenPublishLimiter::class) { + maxParallelUsages = 1 + } + +tasks.withType().configureEach { + usesService(mavenPublishLimiter) } //endregion diff --git a/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt b/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt index a789d79..4b22e5e 100644 --- a/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt +++ b/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt @@ -1,10 +1,8 @@ package buildsrc.utils -import java.io.File import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.component.AdhocComponentWithVariants -import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RelativePath import org.gradle.api.tasks.SourceSet import org.gradle.kotlin.dsl.* @@ -142,19 +140,3 @@ fun SourceSet.configurationNames() = javadocElementsConfigurationName, sourcesElementsConfigurationName, ) - -/** exclude generated Gradle code, so it doesn't clog up search results */ -fun ProjectLayout.generatedKotlinDslAccessorDirs(): Set { - - val generatedSrcDirs = listOf( - "kotlin-dsl-accessors", - "kotlin-dsl-external-plugin-spec-builders", - "kotlin-dsl-plugins", - ) - - return projectDirectory.asFile.walk() - .filter { it.isDirectory && it.parentFile.name in generatedSrcDirs } - .flatMap { file -> - file.walk().maxDepth(1).filter { it.isDirectory }.toList() - }.toSet() -} diff --git a/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt b/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt index 467efeb..837b358 100644 --- a/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt +++ b/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt @@ -1,4 +1,74 @@ package buildsrc.utils import java.io.File +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.ProjectLayout +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.provider.ValueSource +import org.gradle.api.provider.ValueSourceParameters +import org.gradle.kotlin.dsl.* +import org.gradle.plugins.ide.idea.model.IdeaModule + +/** + * Exclude directories containing + * + * - generated Gradle code, + * - IDE files, + * - Gradle config, + * + * so they don't clog up search results. + */ +fun IdeaModule.excludeProjectConfigurationDirs( + layout: ProjectLayout, + providers: ProviderFactory, +) { + val excludedDirs = providers.of(IdeaExcludedDirectoriesSource::class) { + parameters.projectDir.set(layout.projectDirectory) + } + + excludeDirs.addAll(excludedDirs.get()) +} + +// We have to use a ValueSource to find the files, otherwise Gradle +// considers _all files_ an input for configuration cache 🙄 +internal abstract class IdeaExcludedDirectoriesSource + : ValueSource, IdeaExcludedDirectoriesSource.Parameters> { + + interface Parameters : ValueSourceParameters { + val projectDir: DirectoryProperty + } + + override fun obtain(): Set { + val projectDir = parameters.projectDir.get().asFile + + val excludedDirs = setOf( + ".git", + ".gradle", + ".idea", + ".kotlin", + ) + + val generatedSrcDirs = listOf( + "kotlin-dsl-accessors", + "kotlin-dsl-external-plugin-spec-builders", + "kotlin-dsl-plugins", + ) + + val generatedDirs = projectDir + .walk() + .onEnter { it.name !in excludedDirs && it.parentFile.name !in generatedSrcDirs } + .filter { it.isDirectory } + .filter { it.parentFile.name in generatedSrcDirs } + .flatMap { file -> + file.walk().maxDepth(1).filter { it.isDirectory }.toList() + } + .toSet() + + // can't use buildSet {} https://github.com/gradle/gradle/issues/28325 + return mutableSetOf().apply { + addAll(generatedDirs) + add(projectDir.resolve(".idea")) + add(projectDir.resolve("gradle/wrapper")) + } + } +} diff --git a/buildSrc/src/main/kotlin/buildsrc/utils/kotlinStdlib.kt b/buildSrc/src/main/kotlin/buildsrc/utils/kotlinStdlib.kt index 8c9b795..8557fca 100644 --- a/buildSrc/src/main/kotlin/buildsrc/utils/kotlinStdlib.kt +++ b/buildSrc/src/main/kotlin/buildsrc/utils/kotlinStdlib.kt @@ -7,3 +7,6 @@ fun String.titlecaseFirstChar(): String = else -> it.toString() } } + +fun List.filterContains(substring: String): List = + filter { substring in it } diff --git a/gradle.properties b/gradle.properties index f672d9c..41186c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -Xmx2g -XX:MaxMetaspaceSize=512m org.gradle.caching=true -org.gradle.unsafe.configuration-cache=true -org.gradle.unsafe.configuration-cache-problems=warn +org.gradle.configuration-cache=true +org.gradle.configuration-cache-problems=warn org.gradle.parallel=true org.gradle.welcome=never diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eb58f62..251691a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,18 @@ [versions] -kotlinGradle = "1.9.22" +kotlinGradle = "1.9.23" javaDiffUtils = "4.12" -junit = "5.10.1" -kotest = "5.8.0" -kotlinx-bcv = "0.14.0" +junit = "5.10.2" +kotest = "5.8.1" +kotlinx-bcv = "0.15.0-Beta.2" gradlePluginPublishPlugin = "1.2.1" shadowPlugin = "8.1.1" devPublish = "0.2.0" bcvMu = "main-SNAPSHOT" -supportedGradleVersion = "7.6" # the minimal supported Gradle plugin version, used in functional tests +supportedGradleVersion = "7.6.4" # the minimal supported Gradle plugin version, used in functional tests [libraries] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch delta 34118 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 delta 34176 zcmX7vV`H6d(}mmEwr$(CZQE$vU^m*aZQE(=WXEZ2+l}qF_w)XN>&rEBu9;)4>7EB0 zo(HR^Mh47P)@z^^pH!4#b(O8!;$>N+S+v5K5f8RrQ+Qv0_oH#e!pI2>yt4ij>fI9l zW&-hsVAQg%dpn3NRy$kb_vbM2sr`>bZ48b35m{D=OqX;p8A${^Dp|W&J5mXvUl#_I zN!~GCBUzj~C%K?<7+UZ_q|L)EGG#_*2Zzko-&Kck)Qd2%CpS3{P1co1?$|Sj1?E;PO z7alI9$X(MDly9AIEZ-vDLhpAKd1x4U#w$OvBtaA{fW9)iD#|AkMrsSaNz(69;h1iM1#_ z?u?O_aKa>vk=j;AR&*V-p3SY`CI}Uo%eRO(Dr-Te<99WQhi>y&l%UiS%W2m(d#woD zW?alFl75!1NiUzVqgqY98fSQNjhX3uZ&orB08Y*DFD;sjIddWoJF;S_@{Lx#SQk+9 zvSQ-620z0D7cy8-u_7u?PqYt?R0m2k%PWj%V(L|MCO(@3%l&pzEy7ijNv(VXU9byn z@6=4zL|qk*7!@QWd9imT9i%y}1#6+%w=s%WmsHbw@{UVc^?nL*GsnACaLnTbr9A>B zK)H-$tB`>jt9LSwaY+4!F1q(YO!E7@?SX3X-Ug4r($QrmJnM8m#;#LN`kE>?<{vbCZbhKOrMpux zTU=02hy${;n&ikcP8PqufhT9nJU>s;dyl;&~|Cs+o{9pCu{cRF+0{iyuH~6=tIZXVd zR~pJBC3Hf-g%Y|bhTuGyd~3-sm}kaX5=T?p$V?48h4{h2;_u{b}8s~Jar{39PnL7DsXpxcX#3zx@f9K zkkrw9s2*>)&=fLY{=xeIYVICff2Id5cc*~l7ztSsU@xuXYdV1(lLGZ5)?mXyIDf1- zA7j3P{C5s?$Y-kg60&XML*y93zrir8CNq*EMx)Kw)XA(N({9t-XAdX;rjxk`OF%4-0x?ne@LlBQMJe5+$Ir{Oj`@#qe+_-z!g5qQ2SxKQy1ex_x^Huj%u+S@EfEPP-70KeL@7@PBfadCUBt%`huTknOCj{ z;v?wZ2&wsL@-iBa(iFd)7duJTY8z-q5^HR-R9d*ex2m^A-~uCvz9B-1C$2xXL#>ow z!O<5&jhbM&@m=l_aW3F>vjJyy27gY}!9PSU3kITbrbs#Gm0gD?~Tub8ZFFK$X?pdv-%EeopaGB#$rDQHELW!8bVt`%?&>0 zrZUQ0!yP(uzVK?jWJ8^n915hO$v1SLV_&$-2y(iDIg}GDFRo!JzQF#gJoWu^UW0#? z*OC-SPMEY!LYY*OO95!sv{#-t!3Z!CfomqgzFJld>~CTFKGcr^sUai5s-y^vI5K={ z)cmQthQuKS07e8nLfaIYQ5f}PJQqcmokx?%yzFH*`%k}RyXCt1Chfv5KAeMWbq^2MNft;@`hMyhWg50(!jdAn;Jyx4Yt)^^DVCSu?xRu^$*&&=O6#JVShU_N3?D)|$5pyP8A!f)`| z>t0k&S66T*es5(_cs>0F=twYJUrQMqYa2HQvy)d+XW&rai?m;8nW9tL9Ivp9qi2-` zOQM<}D*g`28wJ54H~1U!+)vQh)(cpuf^&8uteU$G{9BUhOL| zBX{5E1**;hlc0ZAi(r@)IK{Y*ro_UL8Ztf8n{Xnwn=s=qH;fxkK+uL zY)0pvf6-iHfX+{F8&6LzG;&d%^5g`_&GEEx0GU=cJM*}RecV-AqHSK@{TMir1jaFf&R{@?|ieOUnmb?lQxCN!GnAqcii9$ z{a!Y{Vfz)xD!m2VfPH=`bk5m6dG{LfgtA4ITT?Sckn<92rt@pG+sk>3UhTQx9ywF3 z=$|RgTN<=6-B4+UbYWxfQUOe8cmEDY3QL$;mOw&X2;q9x9qNz3J97)3^jb zdlzkDYLKm^5?3IV>t3fdWwNpq3qY;hsj=pk9;P!wVmjP|6Dw^ez7_&DH9X33$T=Q{>Nl zv*a*QMM1-2XQ)O=3n@X+RO~S`N13QM81^ZzljPJIFBh%x<~No?@z_&LAl)ap!AflS zb{yFXU(Uw(dw%NR_l7%eN2VVX;^Ln{I1G+yPQr1AY+0MapBnJ3k1>Zdrw^3aUig*! z?xQe8C0LW;EDY(qe_P!Z#Q^jP3u$Z3hQpy^w7?jI;~XTz0ju$DQNc4LUyX}+S5zh> zGkB%~XU+L?3pw&j!i|x6C+RyP+_XYNm9`rtHpqxvoCdV_MXg847oHhYJqO+{t!xxdbsw4Ugn($Cwkm^+36&goy$vkaFs zrH6F29eMPXyoBha7X^b+N*a!>VZ<&Gf3eeE+Bgz7PB-6X7 z_%2M~{sTwC^iQVjH9#fVa3IO6E4b*S%M;#WhHa^L+=DP%arD_`eW5G0<9Tk=Ci?P@ z6tJXhej{ZWF=idj32x7dp{zmQY;;D2*11&-(~wifGXLmD6C-XR=K3c>S^_+x!3OuB z%D&!EOk;V4Sq6eQcE{UEDsPMtED*;qgcJU^UwLwjE-Ww54d73fQ`9Sv%^H>juEKmxN+*aD=0Q+ZFH1_J(*$~9&JyUJ6!>(Nj zi3Z6zWC%Yz0ZjX>thi~rH+lqv<9nkI3?Ghn7@!u3Ef){G(0Pvwnxc&(YeC=Kg2-7z zr>a^@b_QClXs?Obplq@Lq-l5>W);Y^JbCYk^n8G`8PzCH^rnY5Zk-AN6|7Pn=oF(H zxE#8LkI;;}K7I^UK55Z)c=zn7OX_XVgFlEGSO}~H^y|wd7piw*b1$kA!0*X*DQ~O` z*vFvc5Jy7(fFMRq>XA8Tq`E>EF35{?(_;yAdbO8rrmrlb&LceV%;U3haVV}Koh9C| zTZnR0a(*yN^Hp9u*h+eAdn)d}vPCo3k?GCz1w>OOeme(Mbo*A7)*nEmmUt?eN_vA; z=~2}K_}BtDXJM-y5fn^v>QQo+%*FdZQFNz^j&rYhmZHgDA-TH47#Wjn_@iH4?6R{J z%+C8LYIy>{3~A@|y4kN8YZZp72F8F@dOZWp>N0-DyVb4UQd_t^`P)zsCoygL_>>x| z2Hyu7;n(4G&?wCB4YVUIVg0K!CALjRsb}&4aLS|}0t`C}orYqhFe7N~h9XQ_bIW*f zGlDCIE`&wwyFX1U>}g#P0xRRn2q9%FPRfm{-M7;}6cS(V6;kn@6!$y06lO>8AE_!O z{|W{HEAbI0eD$z9tQvWth7y>qpTKQ0$EDsJkQxAaV2+gE28Al8W%t`Pbh zPl#%_S@a^6Y;lH6BfUfZNRKwS#x_keQ`;Rjg@qj zZRwQXZd-rWngbYC}r6X)VCJ-=D54A+81%(L*8?+&r7(wOxDSNn!t(U}!;5|sjq zc5yF5$V!;%C#T+T3*AD+A({T)#p$H_<$nDd#M)KOLbd*KoW~9E19BBd-UwBX1<0h9 z8lNI&7Z_r4bx;`%5&;ky+y7PD9F^;Qk{`J@z!jJKyJ|s@lY^y!r9p^75D)_TJ6S*T zLA7AA*m}Y|5~)-`cyB+lUE9CS_`iB;MM&0fX**f;$n($fQ1_Zo=u>|n~r$HvkOUK(gv_L&@DE0b4#ya{HN)8bNQMl9hCva zi~j0v&plRsp?_zR zA}uI4n;^_Ko5`N-HCw_1BMLd#OAmmIY#ol4M^UjLL-UAat+xA+zxrFqKc@V5Zqan_ z+LoVX-Ub2mT7Dk_ z<+_3?XWBEM84@J_F}FDe-hl@}x@v-s1AR{_YD!_fMgagH6s9uyi6pW3gdhauG>+H? zi<5^{dp*5-9v`|m*ceT&`Hqv77oBQ+Da!=?dDO&9jo;=JkzrQKx^o$RqAgzL{ zjK@n)JW~lzxB>(o(21ibI}i|r3e;17zTjdEl5c`Cn-KAlR7EPp84M@!8~CywES-`mxKJ@Dsf6B18_!XMIq$Q3rTDeIgJ3X zB1)voa#V{iY^ju>*Cdg&UCbx?d3UMArPRHZauE}c@Fdk;z85OcA&Th>ZN%}=VU%3b9={Q(@M4QaeuGE(BbZ{U z?WPDG+sjJSz1OYFpdImKYHUa@ELn%n&PR9&I7B$<-c3e|{tPH*u@hs)Ci>Z@5$M?lP(#d#QIz}~()P7mt`<2PT4oHH}R&#dIx4uq943D8gVbaa2&FygrSk3*whGr~Jn zR4QnS@83UZ_BUGw;?@T zo5jA#potERcBv+dd8V$xTh)COur`TQ^^Yb&cdBcesjHlA3O8SBeKrVj!-D3+_p6%P zP@e{|^-G-C(}g+=bAuAy8)wcS{$XB?I=|r=&=TvbqeyXiuG43RR>R72Ry7d6RS;n^ zO5J-QIc@)sz_l6%Lg5zA8cgNK^GK_b-Z+M{RLYk5=O|6c%!1u6YMm3jJg{TfS*L%2 zA<*7$@wgJ(M*gyTzz8+7{iRP_e~(CCbGB}FN-#`&1ntct@`5gB-u6oUp3#QDxyF8v zOjxr}pS{5RpK1l7+l(bC)0>M;%7L?@6t}S&a zx0gP8^sXi(g2_g8+8-1~hKO;9Nn%_S%9djd*;nCLadHpVx(S0tixw2{Q}vOPCWvZg zjYc6LQ~nIZ*b0m_uN~l{&2df2*ZmBU8dv`#o+^5p>D5l%9@(Y-g%`|$%nQ|SSRm0c zLZV)45DS8d#v(z6gj&6|ay@MP23leodS8-GWIMH8_YCScX#Xr)mbuvXqSHo*)cY9g z#Ea+NvHIA)@`L+)T|f$Etx;-vrE3;Gk^O@IN@1{lpg&XzU5Eh3!w;6l=Q$k|%7nj^ z|HGu}c59-Ilzu^w<93il$cRf@C(4Cr2S!!E&7#)GgUH@py?O;Vl&joXrep=2A|3Vn zH+e$Ctmdy3B^fh%12D$nQk^j|v=>_3JAdKPt2YVusbNW&CL?M*?`K1mK*!&-9Ecp~>V1w{EK(429OT>DJAV21fG z=XP=%m+0vV4LdIi#(~XpaUY$~fQ=xA#5?V%xGRr_|5WWV=uoG_Z&{fae)`2~u{6-p zG>E>8j({w7njU-5Lai|2HhDPntQ(X@yB z9l?NGoKB5N98fWrkdN3g8ox7Vic|gfTF~jIfXkm|9Yuu-p>v3d{5&hC+ZD%mh|_=* zD5v*u(SuLxzX~owH!mJQi%Z=ALvdjyt9U6baVY<88B>{HApAJ~>`buHVGQd%KUu(d z5#{NEKk6Vy08_8*E(?hqZe2L?P2$>!0~26N(rVzB9KbF&JQOIaU{SumX!TsYzR%wB z<5EgJXDJ=1L_SNCNZcBWBNeN+Y`)B%R(wEA?}Wi@mp(jcw9&^1EMSM58?68gwnXF` zzT0_7>)ep%6hid-*DZ42eU)tFcFz7@bo=<~CrLXpNDM}tv*-B(ZF`(9^RiM9W4xC%@ZHv=>w(&~$Wta%)Z;d!{J;e@z zX1Gkw^XrHOfYHR#hAU=G`v43E$Iq}*gwqm@-mPac0HOZ0 zVtfu7>CQYS_F@n6n#CGcC5R%4{+P4m7uVlg3axX}B(_kf((>W?EhIO&rQ{iUO$16X zv{Abj3ZApUrcar7Ck}B1%RvnR%uocMlKsRxV9Qqe^Y_5C$xQW@9QdCcF%W#!zj;!xWc+0#VQ*}u&rJ7)zc+{vpw+nV?{tdd&Xs`NV zKUp|dV98WbWl*_MoyzM0xv8tTNJChwifP!9WM^GD|Mkc75$F;j$K%Y8K@7?uJjq-w zz*|>EH5jH&oTKlIzueAN2926Uo1OryC|CmkyoQZABt#FtHz)QmQvSX35o`f z<^*5XXxexj+Q-a#2h4(?_*|!5Pjph@?Na8Z>K%AAjNr3T!7RN;7c)1SqAJfHY|xAV z1f;p%lSdE8I}E4~tRH(l*rK?OZ>mB4C{3e%E-bUng2ymerg8?M$rXC!D?3O}_mka? zm*Y~JMu+_F7O4T;#nFv)?Ru6 z92r|old*4ZB$*6M40B;V&2w->#>4DEu0;#vHSgXdEzm{+VS48 z7U1tVn#AnQ3z#gP26$!dmS5&JsXsrR>~rWA}%qd{92+j zu+wYAqrJYOA%WC9nZ>BKH&;9vMSW_59z5LtzS4Q@o5vcrWjg+28#&$*8SMYP z!l5=|p@x6YnmNq>23sQ(^du5K)TB&K8t{P`@T4J5cEFL@qwtsCmn~p>>*b=37y!kB zn6x{#KjM{S9O_otGQub*K)iIjtE2NfiV~zD2x{4r)IUD(Y8%r`n;#)ujIrl8Sa+L{ z>ixGoZJ1K@;wTUbRRFgnltN_U*^EOJS zRo4Y+S`cP}e-zNtdl^S5#%oN#HLjmq$W^(Y6=5tM#RBK-M14RO7X(8Gliy3+&9fO; zXn{60%0sWh1_g1Z2r0MuGwSGUE;l4TI*M!$5dm&v9pO7@KlW@j_QboeDd1k9!7S)jIwBza-V#1)(7ht|sjY}a19sO!T z2VEW7nB0!zP=Sx17-6S$r=A)MZikCjlQHE)%_Ka|OY4+jgGOw=I3CM`3ui^=o0p7u z?xujpg#dRVZCg|{%!^DvoR*~;QBH8ia6%4pOh<#t+e_u!8gjuk_Aic=|*H24Yq~Wup1dTRQs0nlZOy+30f16;f7EYh*^*i9hTZ`h`015%{i|4 z?$7qC3&kt#(jI#<76Biz=bl=k=&qyaH>foM#zA7}N`Ji~)-f-t&tR4^do)-5t?Hz_Q+X~S2bZx{t+MEjwy3kGfbv(ij^@;=?H_^FIIu*HP_7mpV)NS{MY-Rr7&rvWo@Wd~{Lt!8|66rq`GdGu% z@<(<7bYcZKCt%_RmTpAjx=TNvdh+ZiLkMN+hT;=tC?%vQQGc7WrCPIYZwYTW`;x|N zrlEz1yf95FiloUU^(onr3A3>+96;;6aL?($@!JwiQ2hO|^i)b4pCJ7-y&a~B#J`#FO!3uBp{5GBvM2U@K85&o0q~6#LtppE&cVY z3Bv{xQ-;i}LN-60B2*1suMd=Fi%Y|7@52axZ|b=Wiwk^5eg{9X4}(q%4D5N5_Gm)` zg~VyFCwfkIKW(@@ZGAlTra6CO$RA_b*yz#){B82N7AYpQ9)sLQfhOAOMUV7$0|d$=_y&jl>va$3u-H z_+H*|UXBPLe%N2Ukwu1*)kt!$Y>(IH3`YbEt; znb1uB*{UgwG{pQnh>h@vyCE!6B~!k}NxEai#iY{$!_w54s5!6jG9%pr=S~3Km^EEA z)sCnnau+ZY)(}IK#(3jGGADw8V7#v~<&y5cF=5_Ypkrs3&7{}%(4KM7) zuSHVqo~g#1kzNwXc39%hL8atpa1Wd#V^uL=W^&E)fvGivt)B!M)?)Y#Ze&zU6O_I?1wj)*M;b*dE zqlcwgX#eVuZj2GKgBu@QB(#LHMd`qk<08i$hG1@g1;zD*#(9PHjVWl*5!;ER{Q#A9 zyQ%fu<$U?dOW=&_#~{nrq{RRyD8upRi}c-m!n)DZw9P>WGs>o1vefI}ujt_`O@l#Z z%xnOt4&e}LlM1-0*dd?|EvrAO-$fX8i{aTP^2wsmSDd!Xc9DxJB=x1}6|yM~QQPbl z0xrJcQNtWHgt*MdGmtj%x6SWYd?uGnrx4{m{6A9bYx`m z$*UAs@9?3s;@Jl19%$!3TxPlCkawEk12FADYJClt0N@O@Pxxhj+Kk(1jK~laR0*KGAc7%C4nI^v2NShTc4#?!p{0@p0T#HSIRndH;#Ts0YECtlSR}~{Uck+keoJq6iH)(Zc~C!fBe2~4(Wd> zR<4I1zMeW$<0xww(@09!l?;oDiq zk8qjS9Lxv$<5m#j(?4VLDgLz;8b$B%XO|9i7^1M;V{aGC#JT)c+L=BgCfO5k>CTlI zOlf~DzcopV29Dajzt*OcYvaUH{UJPaD$;spv%>{y8goE+bDD$~HQbON>W*~JD`;`- zZEcCPSdlCvANe z=?|+e{6AW$f(H;BND>uy1MvQ`pri>SafK5bK!YAE>0URAW9RS8#LWUHBOc&BNQ9T+ zJpg~Eky!u!9WBk)!$Z?!^3M~o_VPERYnk1NmzVYaGH;1h+;st==-;jzF~2LTn+x*k zvywHZg7~=aiJe=OhS@U>1fYGvT1+jsAaiaM;) zay2xsMKhO+FIeK?|K{G4SJOEt*eX?!>K8jpsZWW8c!X|JR#v(1+Ey5NM^TB1n|_40 z@Db2gH}PNT+3YEyqXP8U@)`E|Xat<{K5K;eK7O0yV72m|b!o43!e-!P>iW>7-9HN7 zmmc7)JX0^lPzF#>$#D~nU^3f!~Q zQWly&oZEb1847&czU;dg?=dS>z3lJkADL1innNtE(f?~OxM`%A_PBp?Lj;zDDomf$ z;|P=FTmqX|!sHO6uIfCmh4Fbgw@`DOn#`qAPEsYUiBvUlw zevH{)YWQu>FPXU$%1!h*2rtk_J}qNkkq+StX8Wc*KgG$yH#p-kcD&)%>)Yctb^JDB zJe>=!)5nc~?6hrE_3n^_BE<^;2{}&Z>Dr)bX>H{?kK{@R)`R5lnlO6yU&UmWy=d03 z*(jJIwU3l0HRW1PvReOb|MyZT^700rg8eFp#p<3Et%9msiCxR+jefK%x81+iN0=hG z;<`^RUVU+S)Iv-*5y^MqD@=cp{_cP4`s=z)Ti3!Bf@zCmfpZTwf|>|0t^E8R^s`ad z5~tA?0x7OM{*D;zb6bvPu|F5XpF11`U5;b*$p zNAq7E6c=aUnq>}$JAYsO&=L^`M|DdSSp5O4LA{|tO5^8%Hf1lqqo)sj=!aLNKn9(3 zvKk($N`p`f&u+8e^Z-?uc2GZ_6-HDQs@l%+pWh!|S9+y3!jrr3V%cr{FNe&U6(tYs zLto$0D+2}K_9kuxgFSeQ!EOXjJtZ$Pyl_|$mPQ9#fES=Sw8L% zO7Jij9cscU)@W+$jeGpx&vWP9ZN3fLDTp zaYM$gJD8ccf&g>n?a56X=y zec%nLN`(dVCpSl9&pJLf2BN;cR5F0Nn{(LjGe7RjFe7efp3R_2JmHOY#nWEc2TMhMSj5tBf-L zlxP3sV`!?@!mRnDTac{35I7h@WTfRjRiFw*Q*aD8)n)jdkJC@)jD-&mzAdK6Kqdct8P}~dqixq;n zjnX!pb^;5*Rr?5ycT7>AB9)RED^x+DVDmIbHKjcDv2lHK;apZOc=O@`4nJ;k|iikKk66v4{zN#lmSn$lh z_-Y3FC)iV$rFJH!#mNqWHF-DtSNbI)84+VLDWg$ph_tkKn_6+M1RZ!)EKaRhY={el zG-i@H!fvpH&4~$5Q+zHU(Ub=;Lzcrc3;4Cqqbr$O`c5M#UMtslK$3r+Cuz>xKl+xW?`t2o=q`1djXC=Q6`3C${*>dm~I{ z(aQH&Qd{{X+&+-4{epSL;q%n$)NOQ7kM}ea9bA++*F+t$2$%F!U!U}(&y7Sd0jQMV zkOhuJ$+g7^kb<`jqFiq(y1-~JjP13J&uB=hfjH5yAArMZx?VzW1~>tln~d5pt$uWR~TM!lIg+D)prR zocU0N2}_WTYpU`@Bsi1z{$le`dO{-pHFQr{M}%iEkX@0fv!AGCTcB90@e|slf#unz z*w4Cf>(^XI64l|MmWih1g!kwMJiifdt4C<5BHtaS%Ra>~3IFwjdu;_v*7BL|fPu+c zNp687`{}e@|%)5g4U*i=0zlSWXzz=YcZ*&Bg zr$r(SH0V5a%oHh*t&0y%R8&jDI=6VTWS_kJ!^WN!ET@XfEHYG-T1jJsDd`yEgh!^* z+!P62=v`R2=TBVjt=h}|JIg7N^RevZuyxyS+jsk>=iLA52Ak+7L?2$ZDUaWdi1PgB z_;*Uae_n&7o27ewV*y(wwK~8~tU<#Np6UUIx}zW6fR&dKiPq|$A{BwG_-wVfkm+EP zxHU@m`im3cD#fH63>_X`Il-HjZN_hqOVMG;(#7RmI13D-s_>41l|vDH1BglPsNJ+p zTniY{Hwoief+h%C^|@Syep#722=wmcTR7awIzimAcye?@F~f|n<$%=rM+Jkz9m>PF70$)AK@|h_^(zn?!;={;9Zo7{ zBI7O?6!J2Ixxk;XzS~ScO9{K1U9swGvR_d+SkromF040|Slk%$)M;9O_8h0@WPe4= z%iWM^ust8w$(NhO)7*8uq+9CycO$3m-l}O70sBi<4=j0CeE_&3iRUWJkDM$FIfrkR zHG2|hVh3?Nt$fdI$W?<|Qq@#hjDijk@7eUr1&JHYI>(_Q4^3$+Zz&R)Z`WqhBIvjo zX#EbA8P0Qla-yACvt)%oAVHa#kZi3Y8|(IOp_Z6J-t{)98*OXQ#8^>vTENsV@(M}^ z(>8BXw`{+)BfyZB!&85hT0!$>7$uLgp9hP9M7v=5@H`atsri1^{1VDxDqizj46-2^ z?&eA9udH#BD|QY2B7Zr$l;NJ-$L!u8G{MZoX)~bua5J=0p_JnM`$(D4S!uF}4smWq zVo%kQ~C~X?cWCH zo4s#FqJ)k|D{c_ok+sZ8`m2#-Uk8*o)io`B+WTD0PDA!G`DjtibftJXhPVjLZj~g& z=MM9nF$7}xvILx}BhM;J-Xnz0=^m1N2`Mhn6@ct+-!ijIcgi6FZ*oIPH(tGYJ2EQ0 z{;cjcc>_GkAlWEZ2zZLA_oa-(vYBp7XLPbHCBcGH$K9AK6nx}}ya%QB2=r$A;11*~ z_wfru1SkIQ0&QUqd)%eAY^FL!G;t@7-prQ|drDn#yDf%Uz8&kGtrPxKv?*TqkC(}g zUx10<;3Vhnx{gpWXM8H zKc0kkM~gIAts$E!X-?3DWG&^knj4h(q5(L;V81VWyC@_71oIpXfsb0S(^Js#N_0E} zJ%|XX&EeVPyu}? zz~(%slTw+tcY3ZMG$+diC8zed=CTN}1fB`RXD_v2;{evY z@MCG$l9Az+F()8*SqFyrg3jrN7k^x3?;A?L&>y{ZUi$T8!F7Dv8s}}4r9+Wo0h^m= zAob@CnJ;IR-{|_D;_w)? zcH@~&V^(}Ag}%A90);X2AhDj(-YB>$>GrW1F4C*1S5`u@N{T|;pYX1;E?gtBbPvS* zlv3r#rw2KCmLqX0kGT8&%#A6Sc(S>apOHtfn+UdYiN4qPawcL{Sb$>&I)Ie>Xs~ej z7)a=-92!sv-A{-7sqiG-ysG0k&beq6^nX1L!Fs$JU#fsV*CbsZqBQ|y z{)}zvtEwO%(&mIG|L?qs2Ou1rqTZHV@H+sm8Nth(+#dp0DW4VXG;;tCh`{BpY)THY z_10NNWpJuzCG%Q@#Aj>!v7Eq8eI6_JK3g2CsB2jz)2^bWiM{&U8clnV7<2?Qx5*k_ zl9B$P@LV7Sani>Xum{^yJ6uYxM4UHnw4zbPdM|PeppudXe}+OcX z!nr!xaUA|xYtA~jE|436iL&L={H3e}H`M1;2|pLG)Z~~Ug9X%_#D!DW>w}Es!D{=4 zxRPBf5UWm2{}D>Em;v43miQ~2{>%>O*`wA{7j;yh;*DV=C-bs;3p{AD;>VPcn>E;V zLgtw|Y{|Beo+_ABz`lofH+cdf33LjIf!RdcW~wWgmsE%2yCQGbst4TS_t%6nS8a+m zFEr<|9TQzQC@<(yNN9GR4S$H-SA?xiLIK2O2>*w-?cdzNPsG4D3&%$QOK{w)@Dk}W z|3_Z>U`XBu7j6Vc=es(tz}c7k4al1$cqDW4a~|xgE9zPX(C`IsN(QwNomzsBOHqjd zi{D|jYSv5 zC>6#uB~%#!!*?zXW`!yHWjbjwm!#eo3hm;>nJ!<`ZkJamE6i>>WqkoTpbm(~b%G_v z`t3Z#ERips;EoA_0c?r@WjEP|ulD+hue5r8946Sd0kuBD$A!=dxigTZn)u3>U;Y8l zX9j(R*(;;i&HrB&M|Xnitzf@><3#)aKy=bFCf5Hz@_);{nlL?J!U>%fL$Fk~Ocs3& zB@-Ek%W>h9#$QIYg07&lS_CG3d~LrygXclO!Ws-|PxMsn@n{?77wCaq?uj`dd7lllDCGd?ed&%5k{RqUhiN1u&?uz@Fq zNkv_4xmFcl?vs>;emR1R<$tg;*Ayp@rl=ik z=x2Hk zJqsM%++e|*+#camAiem6f;3-khtIgjYmNL0x|Mz|y{r{6<@_&a7^1XDyE>v*uo!qF zBq^I8PiF#w<-lFvFx9xKoi&0j)4LX~rWsK$%3hr@ebDv^($$T^4m4h#Q-(u*Mbt6F zE%y0Fvozv=WAaTj6EWZ)cX{|9=AZDvPQuq>2fUkU(!j1GmdgeYLX`B0BbGK(331ME zu3yZ3jQ@2)WW5!C#~y}=q5Av=_;+hNi!%gmY;}~~e!S&&^{4eJuNQ2kud%Olf8TRI zW-Dze987Il<^!hCO{AR5tLW{F1WLuZ>nhPjke@CSnN zzoW{m!+PSCb7byUf-1b;`{0GU^zg7b9c!7ueJF`>L;|akVzb&IzoLNNEfxp7b7xMN zKs9QG6v@t7X)yYN9}3d4>*ROMiK-Ig8(Do$3UI&E}z!vcH2t(VIk-cLyC-Y%`)~>Ce23A=dQsc<( ziy;8MmHki+5-(CR8$=lRt{(9B9W59Pz|z0^;`C!q<^PyE$KXt!KibFH*xcB9V%xTD zn;YlZ*tTukwr$(mWMka@|8CW-J8!zCXI{P1-&=wSvZf&%9SZ7m`1&2^nV#D z6T*)`Mz3wGUC69Fg0Xk!hwY}ykk!TE%mr57TLX*U4ygwvM^!#G`HYKLIN>gT;?mo% zAxGgzSnm{}vRG}K)8n(XjG#d+IyAFnozhk|uwiey(p@ zu>j#n4C|Mhtd=0G?Qn5OGh{{^MWR)V*geNY8d)py)@5a85G&_&OSCx4ASW8g&AEXa zC}^ET`eORgG*$$Q1L=9_8MCUO4Mr^1IA{^nsB$>#Bi(vN$l8+p(U^0dvN_{Cu-UUm zQyJc!8>RWp;C3*2dGp49QVW`CRR@no(t+D|@nl138lu@%c1VCy3|v4VoKZ4AwnnjF z__8f$usTzF)TQ$sQ^|#(M}-#0^3Ag%A0%5vA=KK$37I`RY({kF-z$(P50pf3_20YTr%G@w+bxE_V+Tt^YHgrlu$#wjp7igF!=o8e2rqCs|>XM9+M7~TqI&fcx z=pcX6_MQQ{TIR6a0*~xdgFvs<2!yaA1F*4IZgI!)xnzJCwsG&EElg_IpFbrT}nr)UQy}GiK;( zDlG$cksync34R3J^FqJ=={_y9x_pcd%$B*u&vr7^ItxqWFIAkJgaAQiA)pioK1JQ| zYB_6IUKc$UM*~f9{Xzw*tY$pUglV*?BDQuhsca*Fx!sm`9y`V&?lVTH%%1eJ74#D_ z7W+@8@7LAu{aq)sPys{MM~;`k>T%-wPA)E2QH7(Z4XEUrQ5YstG`Uf@w{n_Oc!wem z7=8z;k$N{T74B*zVyJI~4d60M09FYG`33;Wxh=^Ixhs69U_SG_deO~_OUO1s9K-8p z5{HmcXAaKqHrQ@(t?d@;63;Pnj2Kk<;Hx=kr>*Ko`F*l){%GVDj5nkohSU)B&5Vrc zo0u%|b%|VITSB)BXTRPQC=Bv=qplloSI#iKV#~z#t#q*jcS`3s&w-z^m--CYDI7n2 z%{LHFZ*(1u4DvhES|Dc*n%JL8%8?h7boNf|qxl8D)np@5t~VORwQn)TuSI07b-T=_ zo8qh+0yf|-6=x;Ra$w&WeVZhUO%3v6Ni*}i&sby3s_(?l5Er{K9%0_dE<`7^>8mLr zZ|~l#Bi@5}8{iZ$(d9)!`}@2~#sA~?uH|EbrJQcTw|ssG)MSJJIF96-_gf&* zy~I&$m6e0nnLz^M2;G|IeUk?s+afSZ){10*P~9W%RtYeSg{Nv5FG<2QaWpj?d`;}<4( z>V1i|wNTpH`jJtvTD0C3CTws410U9HS_%Ti2HaB~%^h6{+$@5`K9}T=eQL;dMZ?=Y zX^z?B3ZU_!E^OW%Z*-+t&B-(kLmDwikb9+F9bj;NFq-XHRB=+L)Rew{w|7p~7ph{#fRT}}K zWA)F7;kJBCk^aFILnkV^EMs=B~#qh*RG2&@F|x2$?7QTX_T6qL?i$c6J*-cNQC~E6dro zR)CGIoz;~V?=>;(NF4dihkz~Koqu}VNPE9^R{L@e6WkL{fK84H?C*uvKkO(!H-&y( zq|@B~juu*x#J_i3gBrS0*5U*%NDg+Ur9euL*5QaF^?-pxxieMM6k_xAP;S}sfKmIa zj(T6o{4RfARHz25YWzv=QaJ4P!O$LHE(L~6fB89$`6+olZR!#%y?_v+Cf+g)5#!ZM zkabT-y%v|ihYuV}Y%-B%pxL264?K%CXlbd_s<GY5BG*`kYQjao$QHiC_qPk5uE~AO+F=eOtTWJ1vm*cU(D5kvs3kity z$IYG{$L<8|&I>|WwpCWo5K3!On`)9PIx(uWAq>bSQTvSW`NqgprBIuV^V>C~?+d(w$ZXb39Vs`R=BX;4HISfN^qW!{4 z^amy@Nqw6oqqobiNlxzxU*z2>2Q;9$Cr{K;*&l!;Y??vi^)G|tefJG9utf|~4xh=r3UjmRlADyLC*i`r+m;$7?7*bL!oR4=yU<8<-3XVA z%sAb`xe&4RV(2vj+1*ktLs<&m~mGJ@RuJ)1c zLxZyjg~*PfOeAm8R>7e&#FXBsfU_?azU=uxBm=E6z7FSr7J>{XY z1qUT>dh`X(zHRML_H-7He^P_?148AkDqrb>;~1M-k+xHVy>;D7p!z=XBgxMGQX2{* z-xMCOwS33&K^~3%#k`eIjKWvNe1f3y#}U4;J+#-{;=Xne^6+eH@eGJK#i|`~dgV5S zdn%`RHBsC!=9Q=&=wNbV#pDv6rgl?k1wM03*mN`dQBT4K%uRoyoH{e=ZL5E*`~X|T zbKG9aWI}7NGTQtjc3BYDTY3LbkgBNSHG$5xVx8gc@dEuJqT~QPBD=Scf53#kZzZ6W zM^$vkvMx+-0$6R^{{hZ2qLju~e85Em>1nDcRN3-Mm7x;87W#@RSIW9G>TT6Q{4e~b z8DN%n83FvXWdpr|I_8TaMv~MCqq0TA{AXYO-(~l=ug42gpMUvOjG_pWSEdDJ2Bxqz z!em;9=7y3HW*XUtK+M^)fycd8A6Q@B<4biGAR)r%gQf>lWI%WmMbij;un)qhk$bff zQxb{&L;`-1uvaCE7Fm*83^0;!QA5-zeSvKY}WjbwE68)jqnOmj^CTBHaD zvK6}Mc$a39b~Y(AoS|$%ePoHgMjIIux?;*;=Y|3zyfo)^fM=1GBbn7NCuKSxp1J|z zC>n4!X_w*R8es1ofcPrD>%e=E*@^)7gc?+JC@mJAYsXP;10~gZv0!Egi~){3mjVzs z^PrgddFewu>Ax_G&tj-!L=TuRl0FAh#X0gtQE#~}(dSyPO=@7yd zNC6l_?zs_u5&x8O zQ|_JvKf!WHf43F0R%NQwGQi-Dy7~PGZ@KRKMp?kxlaLAV=X{UkKgaTu2!qzPi8aJ z-;n$}unR?%uzCkMHwb56T%IUV)h>qS(XiuRLh3fdlr!Cri|{fZf0x9GVYUOlsKgxLA7vHrkpQddcSsg4JfibzpB zwR!vYiL)7%u8JG7^x@^px(t-c_Xt|9Dm)C@_zGeW_3nMLZBA*9*!fLTV$Uf1a0rDt zJI@Z6pdB9J(a|&T_&AocM2WLNB;fpLnlOFtC9yE6cb39?*1@wy8UgruTtX?@=<6YW zF%82|(F7ANWQ`#HPyPqG6~ggFlhJW#R>%p@fzrpL^K)Kbwj(@#7s97r`)iJ{&-ToR z$7(mQI@~;lwY+8dSKP~0G|#sjL2lS0LQP3Oe=>#NZ|JKKYd6s6qwe#_6Xz_^L4PJ5TM_|#&~zy= zabr|kkr3Osj;bPz`B0s;c&kzzQ2C8|tC9tz;es~zr{hom8bT?t$c|t;M0t2F{xI;G z`0`ADc_nJSdT`#PYCWu4R0Rmbk#PARx(NBfdU>8wxzE(`jA}atMEsaG6zy8^^nCu| z9_tLj90r-&Xc~+p%1vyt>=q_hQsDYB&-hPj(-OGxFpesWm;A(Lh>UWy4SH9&+mB(A z2jkTQ2C&o(Q4wC_>|c()M8_kF?qKhNB+PW6__;U+?ZUoDp2GNr<|*j(CC*#v0{L2E zgVBw6|3c(~V4N*WgJsO(I3o>8)EO5;p7Xg8yU&%rZ3QSRB6Ig6MK7Wn5r+xo2V}fM z0QpfDB9^xJEi}W*Fv6>=p4%@eP`K5k%kCE0YF2Eu5L!DM1ZY7wh`kghC^NwxrL}90dRXjQx=H>8 zOWP@<+C!tcw8EL8aCt9{|4aT+x|70i6m*LP*lhp;kGr5f#OwRy`(60LK@rd=to5yk^%N z6MTSk)7)#!cGDV@pbQ>$N8i2rAD$f{8T{QM+|gaj^sBt%24UJGF4ufrG1_Ag$Rn?c zzICg9`ICT>9N_2vqvVG#_lf9IEd%G5gJ_!j)1X#d^KUJBkE9?|K03AEe zo>5Rql|WuUU=LhLRkd&0rH4#!!>sMg@4Wr=z2|}dpOa`4c;_DqN{3Pj`AgSnc;h%# z{ny1lK%7?@rwZO(ZACq#8mL)|vy8tO0d1^4l;^e?hU+zuH%-8Y^5YqM9}sRzr-XC0 zPzY1l($LC-yyy*1@eoEANoTLQAZ2lVto2r7$|?;PPQX`}rbxPDH-a$8ez@J#v0R5n z7P*qT3aHj02*cK)WzZmoXkw?e3XNu&DkElGZ0Nk~wBti%yLh+l2DYx&U1lD_NW_Yt zGN>yOF?u%ksMW?^+~2&p@NoPzk`T)8qifG_owD>@iwI3@u^Y;Mqaa!2DGUKi{?U3d z|Efe=CBc!_ZDoa~LzZr}%;J|I$dntN24m4|1(#&Tw0R}lP`a`?uT;>szf^0mDJx3u z6IJvpeOpS$OV!Xw21p>Xu~MZ(Nas5Iim-#QSLIYSNhYgx1V!AR>b zf5b7O`ITTvW5z%X8|7>&BeEs8~J1i47l;`7Y#MUMReQ4z!IL1rh8UauKNPG?7rV_;#Y zG*6Vrt^SsTMOpV7mkui}l_S8UNOBcYi+DzcMF>YKrs3*(q5fwVCr;_zO?gpGx*@%O zl`KOwYMSUs4e&}eM#FhB3(RIDJ9ZRn6NN{2Nf+ z2jcz%-u6IPq{n7N3wLH{9c+}4G(NyZa`UmDr5c-SPgj0Sy$VN#Vxxr;kF>-P;5k!w zuAdrP(H+v{Dybn78xM6^*Ym@UGxx?L)m}WY#R>6M2zXnPL_M9#h($ECz^+(4HmKN7 zA>E;`AEqouHJd7pegrq4zkk>kHh`TEb`^(_ea;v{?MW3Sr^FXegkqAQPM-h^)$#Jn z?bKbnXR@k~%*?q`TPL=sD8C+n^I#08(}d$H(@Y;3*{~nv4RLZLw`v=1M0-%j>CtT( zTp#U03GAv{RFAtj4vln4#E4eLOvt zs;=`m&{S@AJbcl1q^39VOtmN^Zm(*x(`(SUgF(=6#&^7oA8T_ojX>V5sJx@*cV|29 z)6_%P6}e}`58Sd;LY2cWv~w}fer&_c1&mlY0`YNNk9q=TRg@Khc5E$N`aYng=!afD z@ewAv^jl$`U5;q4OxFM4ab%X_Jv>V!98w$8ZN*`D-)0S7Y^6xW$pQ%g3_lEmW9Ef^ zGmFsQw`E!ATjDvy@%mdcqrD-uiKB}!)ZRwpZRmyu+x|RUXS+oQ*_jIZKAD~U=3B|t zz>9QQr91qJihg9j9rWHww{v@+SYBzCfc0kI=4Gr{ZLcC~mft^EkJ`CMl?8fZ z3G4ix71=2dQ`5QuTOYA0(}f`@`@U<#K?1TI(XO9c*()q!Hf}JUCaUmg#y?ffT9w1g zc)e=JcF-9J`hK{0##K#A>m^@ZFx!$g09WSBdc8O^IdP&JE@O{i0&G!Ztvt{L4q%x& zGE2s!RVi6ZN9)E*(c33HuMf7#X2*VPVThdmrVz-Fyqxcs&aI4DvP#bfW={h$9>K0HsBTUf z2&!G;( z^oOVIYJv~OM=-i`6=r4Z1*hC8Fcf3rI9?;a_rL*nr@zxwKNlxf(-#Kgn@C~4?BdKk zYvL?QcQeDwwR5_S(`sn&{PL6FYxwb-qSh_rUUo{Yi-GZz5rZotG4R<+!PfsGg`MVtomw z5kzOZJrh(#rMR_87KeP0Q=#^5~r_?y1*kN?3Fq% zvnzHw$r!w|Soxz8Nbx2d&{!#w$^Hua%fx!xUbc2SI-<{h>e2I;$rJL)4)hnT5cx^* zIq#+{3;Leun3Xo=C(XVjt_z)F#PIoAw%SqJ=~DMQeB zNWQ={d|1qtlDS3xFik}#j*8%DG0<^6fW~|NGL#P_weHnJ(cYEdJtI9#1-Pa8M}(r{ zwnPJB_qB?IqZw5h!hRwW2WIEb?&F<52Ruxpr77O2K>=t*3&Z@=5(c^Uy&JSph}{Q^ z0Tl|}gt=&vK;Rb9Tx{{jUvhtmF>;~k$8T7kp;EV`C!~FKW|r$n^d6=thh`)^uYgBd zydgnY9&mm$?B@pKK+_QreOm?wnl5l}-wA$RZCZukfC$slxbqv9uKq0o^QeSID96{Rm^084kZ)*`P zk))V~+<4-_7d6<~)PL%!+%JP`Dn23vUpH47h~xnA=B_a}rLy|7U-f0W+fH`{wnyh2 zD$JYdXuygeP5&OAqpl2)BZ|X){~G;E|7{liYf%AZFmXXyA@32qLA)tuuQz`n^iH1Y z=)pAzxK$jw0Xq?7`M`=kN2WeQFhz)p;QhjbKg#SB zP~_Vqo0SGbc5Q;v4Q7vm6_#iT+p9B>%{s`8H}r|hAL5I8Q|ceJAL*eruzD8~_m>fg26HvLpik&#{3Zd#|1C_>l&-RW2nBBzSO zQ3%G{nI*T}jBjr%3fjG*&G#ruH^ioDM>0 zb0vSM8ML?tPU*y%aoCq;V%x%~!W*HaebuDn9qeT*vk0%X>fq-4zrrQf{Uq5zI1rEy zjQ@V|Cp~$AoBu=VgnVl@Yiro>ZF{uB=5)~i1rZzmDTIzLBy`8Too!#Z4nE$Z{~uB( z_=o=gKuhVpy&`}-c&f%**M&(|;2iy+nZy2Su}GOAH_GT9z`!ogwn$+Bi&1ZhtPF zVS&LO5#Bq}cew$kvE7*t8W^{{7&7WaF{upy0mj*K&xbnXvSP9V$6m6cesHGC!&Us36ld9f*Pn8gbJb3`PPT|ZG zri2?uIu09i>6Y-0-8sREOU?WaGke0+rHPb^sp;*E{Z5P7kFJ@RiLZTO`cN2mRR#Nz zxjJ##Nk+Uy-2N-8K_@576L(kJ>$UhP+)|w!SQHkkz+e62*hpzyfmY4eQLZtZUhEdG zIZluDOoPDlt5#iw+2epC3vEATfok^?SDT`TzBwtgKjY z>ZImbO)i~T=IYAfw$3j2mF1Cj*_yqK(qw(U^r-!gcUKvWQrDG@E{lEyWDWOPtA9v{ z5($&mxw{nZWo_Ov??S#Bo1;+YwVfx%M23|o$24Hdf^&4hQeV=Cffa5MMYOu2NZLSC zQ4UxWvn+8%YVGDg(Y*1iHbUyT^=gP*COcE~QkU|&6_3h z-GOS6-@o9+Vd(D7x#NYt{Bvx2`P&ZuCx#^l0bR89Hr6Vm<||c3Waq(KO0eZ zH(|B;X}{FaZ8_4yyWLdK!G_q9AYZcoOY}Jlf3R;%oR5dwR(rk7NqyF%{r>F4s^>li z`R~-fh>YIAC1?%!O?mxLx!dq*=%IRCj;vXX628aZ;+^M0CDFUY0Rc<1P5e(OVX8n- z*1UOrX{J}b2N)6m5&_xw^WSN=Lp$I$T>f8K6|J_bj%ZsIYKNs1$TFt!RuCWF48;98`7D(XPVnk+~~i=U$} zR#;!ZRo4eVqlDxjDeE^3+8)bzG_o~VRwdxqvD^HNh#@o>1My$0*Y_`wfQ$y}az|Uz zM47oEaYNTH?J^w9EVNnvfmmbV+GHDe)Kf;$^@6?9DrSHnk@*{PuJ>ra|9KO!qQ-Fp zNNcZB4ZdAI>jEh@3Mt(E1Fy!^gH-Zx6&lr8%=duIgI^~gC{Q;4yoe;#F7B`w9daIe z{(I;y)=)anc;C;)#P`8H6~iAG_q-4rPJb(6rn4pjclGi6$_L79sFAj#CTv;t@94S6 zz`Id7?k!#3JItckcwOf?sj=Xr6oKvAyt1=jiWN@XBFoW6dw_+c9O9x2i4or?*~8f& zm<>yzc6Aw_E-gsGAa`6`cjK~k^TJt(^`E1^_h)5(8)1kzAsBxjd4+!hJ&&T!qklDN z`?j#za=(^wRCvEI75uE^K#IBe5!5g2XW}|lUqAmdmIQb7xJtP}G9^(=!V`ZS_7#RZ zjXq#Cekw>fE*YS-?Qea|7~H?)bbLK;G&(~%!B@H`o#LYAuu6;-c~jFfjY7GKZ|9~{ zE!`!d@@rhY_@5fDbuQ8gRI~R_vs4%fR5$?yot4hDPJ28k_Wzmc^0yzwMr#*(OXq@g zRUgQmJA?E>3GO=5N8iWIfBP{&QM%!Oa*iwTlbd0Fbm*QCX>oRb*2XfG-=Bz1Qz0$v zn#X!2C!LqE601LEMq;X7`P*5nurdKZAmmsI-zZ|rTH;AFxNDyZ_#hN2m4W(|YB64E z470#yh$;8QzsdA;6vbNvc95HLvZvyT4{C>F(fwy&izvNDuvfO1Z;`Ss#4a_c6pm*{0t|_i9z{@84^lffQa5zG4<{(+p5-S z^>lG-^GJR#V>;5f3~y%n=`U_jBp~WgB0cp;Lx5VZYPYCH&(evw#}AYRlGJ>vcoeVr z3%#-QUBgeH!GB>XLw;rT&oMI9ynP;leDwh4O2uM!oIWo&Qxk{^9#nX&^3GJ z(U~5{S9aw@yHH^yuQGso=~*JOC9Zdi6(TFP+IddkfK5Eu9q;+F9?PPNAe-O;;P_Aa zPJ{Dqa1gQb%dZ|0I{#B0(z|r(qq!A4CxlW92-LwXFjYfOzAT1DDK`9rm4AB~l&oVv zi6_{)M9L1%JP}i52y@`!T9RB~!CRel53wl?amNHqcuElq%hn)|#BPvW5_m51RVb|? zXQ&B*eAD}}QamG>o{?i~usG5X6IDa3+Xkb8w%7;C8|Cln70biA+ZH}fxkH^Wei$vZPnuqIT!Mmy26;mLfU z3Bbv4M^vvMlz-I+46=g>0^wWkmA!hlYj*I!%it^x9Kx(d{L|+L{rW?Y#hLHWJfd5X z>B=Swk8=;mRtIz}Hr3NE_garb5W*!7fnNM{+m2_>!cHZZlNEeof~7M#FBEQ+f&gJ3 z^zv*t?XV)jQi%0-Ra|ISiW-fx)DsK-> zI}Fv%uee$#-1PKJwr=lU89eh=M{>Nk7IlJ)U33U)lLW+OOU%A|9-Lf;`@c*+vX{W2 z{{?0QoP!#?8=5%yL=fP%iF+?n$0#iHz`P;1{Ra6iwr=V7v^8;NoLJ5)QxIyIx>ur?lMwV=mBo0BA?28kMow8SX=Ax5L%S~x4+EQi#Ig`(ht%)D(F#Pa!)SiHy&PvUp32=VtAsR|6|NZR@jkad zX^aEgojf9(-)rNOZ=NVA&a;6Cljkb=H-bY9m^_I)`pBHB16QW)sU27zF13ypefeATJc1Wzy39GrKF{UntHsIU59AdXp?j{eh2R)IbU&omd zk6(qzvE@hve1yM6dgkbz>5HDR&MD~yi$yymQ}?b;RfL$N-#l7(u?T^Wlu+Q;fo|jd zBe^jzGMHY(2=5l?bEIh+zgE$1TEQ&!p3fH;AW`P?W5Hkj3eJnT>dqg! zf~}A*SZU5HHDCbdywQ^l_PqssHRlrySYN=`hAv2sVrtcF!`kyEu%XeeRUTJU7vB%h zY0*)N$mLo6d=tJfe}IPIeiH~>AKwCpkn&WEfYgl?3anq5#-F$6$v-(G_j0*S9mdsn zg@ek_ut4(?+JP_9-n`YqoD(gAz+Ttm1#t za96D}oQR(o=e8wwes19_(p4g(A1vSGwPAp~Hh3hh!fc>u{1E^+^}AzwilFVf6^vbL zc&NnRs`u)N-P|Cu4()yTiuE{j_V&=K?iP!IUBf~ei2}~_KBvUAlXa;R#Wl`gOBtJ$Y5(L))@`riLB)v*r>9*8VfmQt<72?+fdwP{BA@?_qo>mN7yzICUCaeG(+>Rb~8wg~6U(P)NlDLuhQgjbC}=)HuZgC}0Z-qLX4lJ7^)8~!!*qP0=~`Y_(A z{@15*ZevZSI^s|OnpCeCwLXf#tgbq8y~R*GB5anmZ;_N!+-3>!wu@NBFCNJ$#y?{? zMI!?s*=_xA;V&aX)ROxzVW8*de+&P#2zucA|8mksdgCXBsZ*TM=%{L1Tk5LB_*^@&S?O=ot{h)1xRVSn27&Tk8>rF|6ruzYb;Nq) z;qvlmrP^SL$mhe4Ai)xpl6Wx&y;z8o!7-+6$qj;ZLXvfR71I@w(R|6lyuP6v-lP&r z@KK-TEmGQfMmk1c0^fd7!^si}T%b5a2%>T-Drh|^Cf z$}qxIv@zxbmJ#qjK6Q_aGDe{ciVT20V1lW52Xs!}x(4_j)sUXYdm4 zwYC9FOa;X*c*LxL;xE5ov?|?^7gWXyALy_D2GvDo-8%0-Y%9TkkO_Tcr2qIUg3(OC z%3wt?hyn*+e^z%(~2#!2dvMFa$mzgwk1I1X;naFMjXSbnmZ!zd%7u)=cgi z*0&@Scrl&BDfU(9Pks8#;!~v~r7~DN{G6WE&_;7i{{a*?oiCao(l%2ruxX0fAt69e2vLgL%Mf_)!*(Tz zNKW>sW@YB2vBfP>C&L|-pq)Uq^PsG_THu;8iEcqafO?0k$IQp1KyWyOoTxwmKvlc^ zO9$%Tt8;%qQxwy5;CsJ)V}a7I6}SvQ%0_H53Kcqx=m83fIzpLSGgfVe^SPdc*xPdciI5dg}#{Etv$e<)gGD=qm0v=!aN@*?$s zLhzD%4w{vf-g6FHQjG9XyC+4=bewb?Mz%!u8%oP{G9{UJFTLTcCi3R(=Nm&t&Sl(? zr>pj?=ECdDVa}-g%`LF^1EY@>7d}%VhYpKFSDPH)D(zB+gPe1m7E}W>TiW=8L0&(D&YG=0<&7G4Bu{;-#Ud;-1%Ta9V}U6fyK1YX z`Rq|i-X(loPZ)M$H%m@j7bGx>uj~y=0)!t#dc|c}+hT%~Sq>fefez0Ul|jOJHta~u zx7*mV6~Jpt(FkY(pQN91>aFk7VS%Sa^oLaq$*)W?fy`xuFJgH<2s=!Rz}_(qdmdF~ zlr2f=)q_vpi8X;Jq>5^$GweJ{iS`Khw2f)fsvKpgh;U~13a+9 zfaw}UuGiBy;q10pI^Avb#X3D=k_r(T{N;-xA)OM}2Py5L##<96NU*Sr7GQqhfrPej z?;B$Bt_sTxuSAPXfTSC{zr?@$$0iHxC@z*5F52j*PG87hh`0w3At8jPf*rjNE~_Gj z2)fjeUFJ(#l9uWuw&5#@13|AQ1;pdA?EL4YKq0JDR5T8I?aWGxI=J9}vdyH;gQ@iE z>+UnC2iwT0f80-VuE^bY!N@(}9?bOXyy%rTqSNDN4rO4Zt#(kZwcGgTp&3((F+nsd ze~B)%K6oP4WX_w1>|QImC;9q zy}4p+s%^Too2(gE>yo%+yY#F{)phtmNqsJPVQQ0lGR|H9q>aA&AtU4M+EZ%`xvQLb zbigBOc`dL}&j3er?EOI`!W)N#>+uwp_!h^5FspaEylq!e(FPY-6T3~WeNmZ<$?Y6y z-!bM1kD7ZF8xl+Pi6fiv1?)q%`aNxn#pK%)ct||L&Xnf8Gu&3g;Of{B8Pt=u`e+Mn zA(DmU#3cF#Nr7W;X0V4ksFHMcNDAf4G&D8VjLeZ^|5-f$>_|71>P3xuu)?4NJed*w z6GR_RB5HQLzT(h+`Y?-3esxeue{-Q%b+!&o>IJ!#=}#_&q+hwJga>fkt(*(WdoN5vSta z#$mMN6}YzYRpaBZ)j)EL91-oL1(|d(>%UclsTUOyXyWM&(hNqLwqtn`!E>HJM{ zh>M~xa1@*U^cwx-k5QjePr5=B6u*jpJ)C0{C?f7Yga+I^4$TleyX$x&jm9z@c!?cC z<2kY7)p^+W{AXd@l1C09_yB*TG|yzb96BYk z8Wpj81vB>zcR+qM4m~A44w1n7$fxB$-?MV}S?Fh}c_|2FXg`cZ?750i;Cdl-_nGK# zta)h)6!*AsQ-z8caSh)%5JY>_yCeJs~FpAzdY8 zF@SU_hN#~ip5I;UACFzx1v0yf{j97l&)e-=`d#1Kp6A(Kj&HC!%vK!wEdK3HFJ?|6 za;WwUczZ+&<$g!Td^48@lJtfW@doXL#jY6)dK_RDCQAZ}l&OdD+?Yl5-bqpsHZR^( zF{u_cR(x>u(c4i5f(^8!h6CV0#ZxRFhLlunWiGDLO6yoRb(wV<(P^8=fOU7Hp{AHE z;Yg%kg@6&tL3Z*IrbkDeQ$%rbalVP39D@LVrC2xSavnTp%PorXPf1DVzHyqjDsDnS zL=mv0a2s60bHKGQM)ue>npH0SCp;XtZFUzm?R-x7D*(PxMmuJ4J*K2eY&ebe0yQHe zVG&*qe{pot{PM^xQv`H_rn2FcYOrEN+I#uX^1`Id%J$;Hi2cNCU!0Hlc0TjxLzkss zHxmC;hQBu5U4J0XflWM;{uH`_47Sg)QyZ{8D&T0;bdc3{^^<=q7P?C_2E-}PQn>*= z2T5q^J|Q_2+x%Qt`i3m6=6V$)BxIx{2KAFkMb#q`iMCD|L>+}_dYVA$wBr1Zr}YOF z^MMGO@PHGGh>g|^yF`PvvtDwN@kxt?ClLcG<+murHMz1Asj!$l=b)4{d}SqOJ}>Y< zSeAyP@ZEcpx`ayIdp>{--UVLYC_cZZURh_!4u2(*#x@Tk(QJa}4BqqZ$6%LhF-HB~ zAcc?$I6KP}IxANcAteEBX$Ys?T=JB|Fnd3*UAO0mYAXCgWf~?7Z_G7G5`H4;S^QKK zG*2l75vI@DHQC*es>6&|r^#RHKRQ5rwv_l4`!(!I3%)Z$P1fnZ8N@27zyg}54ElO%SjQ_4uujX)4ta@Gz2)_>4b~vX|rhRIH-eqdD zL)xaEpW3K|a>daQRRR*_$W>rWOsW-IE4VQl3L$3}=-PFU)s@XG&9+DFivH-;2&w~$ES_nJZJH!?1mO!CnP)Jb{mW9=f`bDpo^PI6i4|YurK)Q1 z^Ys1oHRdr!$X4RuyR%kgp!a*Lz*_AAoJ$EVAdsNCoPA^VZE1pGO@D3UStACE+%vs6 z$io@E>DmB|3VV~GbOt2oc+K;t zdn3gaFvYz;vRN-+2+Qk{8|O}e86nVck)fZn3sg$j#dLVham{yGkc$I#!HF7mRS%f* z!+NdzG49K(qaO^SBlp@K@D?|^rAq;8{*@kRc4sYSNQmoy7@_RS_ksWl2T_38h2A)# ziU2WXWD03(NqS&Mu*?0-iK8X_Z3w`}c7MPv0qZ7iM|L3xdTnR{y!7{#82$}uJCiGT zqa=8<9L05hu6 z1N+2n7OzT{NEf?gS@eq7@buCDFe9mAxY%THo^b@BHckKK>jg6{@)>n z43cPs%$Qi0iwyZ+{C491>FRu5+6baJ{&XXXC@Sp+b!QE|{7_d?lm5K=B z)myKEcxjFm74+drF|JCYcxdY%ASig#YoRBRUV7An7f-%rqj%PHECbxh#5476cEq@NQL?dI6gUqvS@w zq!WmD(aR0{NxItAZCKDCVw=Zu{9WGDu^i?2g zLerPiOU*HSaXg^3CdOX^F6c9MiHINP339N%)a96`^Z-c#&EogcxMSYo0Cb4{-}q1( zRrJine`P|6WRkm8u4Ja1QRYq$AR>b7tugd#EsT-VmXN-t!TYjZy}i!uKi6$u>EJ?w zvdHZg+hp+5ree?>fdJAX)5#Wtm#2M-{~2jfX2{G`)?D6UD1MevdeeU;;HCi}AtJr( SGW6ptSs!X7{rG*o_g?|vpSEZK diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e09..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..25da30d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts index 432191e..16dd7d7 100644 --- a/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts @@ -20,6 +20,8 @@ dependencies { testFixturesApi(libs.kotest.assertionsCore) testFixturesApi(libs.kotest.property) + testFixturesApi("org.jetbrains.kotlin:kotlin-gradle-plugin:$embeddedKotlinVersion") + testFixturesApi(testFixtures(projects.modules.bcvGradlePlugin)) } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt index 81bd85e..208616c 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/AndroidLibraryTest.kt @@ -20,7 +20,6 @@ internal class AndroidLibraryTest : BaseKotlinGradleTest() { createProjectWithSubModules() runner { arguments.add(":kotlin-library:apiDump") - arguments.add("--full-stacktrace") } } @@ -53,7 +52,6 @@ internal class AndroidLibraryTest : BaseKotlinGradleTest() { createProjectWithSubModules() runner { arguments.add(":java-library:apiDump") - arguments.add("--full-stacktrace") } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt index 57306d7..abc63fc 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt @@ -223,7 +223,6 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() { val expected = readResourceFile("/examples/classes/AnotherBuildConfig.dump") rootProjectApiDump.readText().invariantNewlines().shouldBeEqualComparingTo(expected) - //Assertions.assertThat(rootProjectApiDump.readText()).isEqualToIgnoringNewLines(expected) } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JavaVersionsCompatibilityTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JavaVersionsCompatibilityTest.kt new file mode 100644 index 0000000..3f91338 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JavaVersionsCompatibilityTest.kt @@ -0,0 +1,81 @@ +package kotlinx.validation.test + +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome +import org.gradle.internal.impldep.org.junit.Assume.assumeFalse +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.junit.jupiter.api.Test + +class JavaVersionsCompatibilityTest : BaseKotlinGradleTest() { + private fun skipInDebug(runner: GradleRunner) { + assumeFalse( + "The test requires a separate Gradle process as it uses a different JVM version, so it cannot be executed with debug turned on.", + runner.isDebug, + ) + } + + private fun checkCompatibility(useMaxSupportedJdk: Boolean): Unit = checkCompatibility { + buildGradleKts { + resolve("/examples/gradle/base/jdkCompatibility.gradle.kts") + } + runner { + arguments.add("-PuseMaxSupportedJdk=$useMaxSupportedJdk") + } + } + + private fun checkCompatibility(jvmTarget: String): Unit = checkCompatibility { + buildGradleKts { + resolve("/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts") + } + runner { + arguments.add("-PjvmTarget=$jvmTarget") + } + } + + private fun checkCompatibility(configure: BaseKotlinScope.() -> Unit = {}) { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/jdk-provisioning.gradle.kts") + } + kotlin("AnotherBuildConfig.kt") { + resolve("/examples/classes/AnotherBuildConfig.kt") + } + apiFile(projectName = rootProjectDir.name) { + resolve("/examples/classes/AnotherBuildConfig.dump") + } + + runner { + gradleVersion = "8.5" + arguments.add(":apiCheck") + } + + configure() + } + + skipInDebug(runner) + + runner.build { + shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) + } + } + + @Test + fun testMaxSupportedJvmVersion(): Unit = checkCompatibility(true) + + @Test + fun testMinSupportedJvmVersion(): Unit = checkCompatibility(false) + + @Test + fun testJvm8(): Unit = checkCompatibility("1.8") + + @Test + fun testJvm11(): Unit = checkCompatibility("11") + + @Test + fun testJvm17(): Unit = checkCompatibility("17") + + @Test + fun testJvm21(): Unit = checkCompatibility("21") +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt new file mode 100644 index 0000000..e5a9f1a --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt @@ -0,0 +1,54 @@ +package kotlinx.validation.test + +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.invariantNewlines +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome +import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.junit.jupiter.api.Test + +class JvmProjectTests : BaseKotlinGradleTest() { + + @Test + fun `apiDump for a project with generated sources only`() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/generatedSources/generatedJvmSources.gradle.kts") + } + runner { + // TODO: enable configuration cache back when we start skipping tasks correctly + //configurationCache = false + arguments.add(":apiDump") + } + } + runner.build { + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + + val expected = readResourceFile("/examples/classes/GeneratedSources.dump") + rootProjectApiDump.readText().invariantNewlines() shouldBe expected + } + } + + @Test + fun `apiCheck for a project with generated sources only`() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/withPlugin.gradle.kts") + resolve("/examples/gradle/configuration/generatedSources/generatedJvmSources.gradle.kts") + } + apiFile(projectName = rootProjectDir.name) { + resolve("/examples/classes/GeneratedSources.dump") + } + runner { + // TODO: enable configuration cache back when we start skipping tasks correctly + //configurationCache = false + arguments.add(":apiCheck") + } + } + runner.build { + shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) + } + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt new file mode 100644 index 0000000..7da6dc3 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -0,0 +1,807 @@ +package kotlinx.validation.test + +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.* +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.comparables.shouldBeEqualComparingTo +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.file.shouldNotExist +import io.kotest.matchers.string.shouldContain +import java.io.File +import java.nio.file.Files +import kotlin.io.path.Path +import kotlin.io.path.name +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome.* +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.junit.jupiter.api.Assumptions.assumeTrue +import org.junit.jupiter.api.Test + + +internal class KlibVerificationTests : BaseKotlinGradleTest() { + + private fun BaseKotlinScope.baseProjectSetting() { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + gradleProperties { + addLine("kotlin.mpp.stability.nowarn=true\n") + } + buildGradleKts { + resolve("/examples/gradle/base/withNativePlugin.gradle.kts") + } + } + + private fun BaseKotlinScope.additionalBuildConfig(config: String) { + buildGradleKts { + resolve(config) + } + } + + private fun BaseKotlinScope.addToSrcSet(pathTestFile: String, sourceSet: String = "commonMain") { + val fileName = Path(pathTestFile).name + kotlin(fileName, sourceSet) { + resolve(pathTestFile) + } + } + + @Test + fun `apiDump for native targets`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + + runner { + buildCache = false + configurationCache = false + arguments.add(":apiDump") + } + } + runner.build { + checkKLibDump("/examples/classes/TopLevelDeclarations.klib.with.linux.dump") + } + } + + @Test + fun `apiCheck for native targets`() { + val runner = test { + baseProjectSetting() + + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + + abiFile(projectName = "testproject") { + resolve("/examples/classes/TopLevelDeclarations.klib.dump") + } + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `apiCheck for native targets should fail when a class is not in a dump`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/BuildConfig.kt") + abiFile(projectName = "testproject") { + resolve("/examples/classes/Empty.klib.dump") + } + runner { + arguments.add(":apiCheck") + } + } + + runner.buildAndFail { + output shouldContain "+final class com.company/BuildConfig { // com.company/BuildConfig|null[0]" + tasks.filter { it.path.endsWith("ApiCheck") }.shouldForAll { + it.shouldHaveOutcome(FAILED) + } + } + } + + @Test + fun `apiDump should include target-specific sources`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump") + } + } + + @Test + fun `apiDump with native targets along with JVM target`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/base/enableJvmInWithNativePlugin.gradle.kts") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") + + val jvmApiDump = rootProjectDir.resolve("$API_DIR/testproject.api") + jvmApiDump.shouldExist() + + val expected = readResourceFile("/examples/classes/AnotherBuildConfig.dump") + jvmApiDump.readText().invariantNewlines().shouldBeEqualComparingTo(expected) + } + } + + @Test + fun `apiDump should ignore a class listed in ignoredClasses`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts") + addToSrcSet("/examples/classes/BuildConfig.kt") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") + } + } + + @Test + fun `apiDump should succeed if a class listed in ignoredClasses is not found`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + + runner { + arguments.add(":apiDump") + } + } + + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") + } + } + + @Test + fun `apiDump should ignore all entities from a package listed in ingoredPackages`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/ignoredPackages/oneValidPackage.gradle.kts") + addToSrcSet("/examples/classes/BuildConfig.kt") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + addToSrcSet("/examples/classes/SubPackage.kt") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") + } + } + + @Test + fun `apiDump should ignore all entities annotated with non-public markers`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts") + addToSrcSet("/examples/classes/HiddenDeclarations.kt") + addToSrcSet("/examples/classes/NonPublicMarkers.kt") + + runner { + arguments.add(":apiDump") + } + } + + + runner.build { + checkKLibDump("/examples/classes/HiddenDeclarations.klib.dump") + } + } + + @Test + fun `apiDump should not dump subclasses excluded via ignoredClasses`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts") + addToSrcSet("/examples/classes/Subclasses.kt") + + runner { + arguments.add(":apiDump") + } + } + + + runner.build { + checkKLibDump("/examples/classes/Subclasses.klib.dump") + } + } + + @Test + fun `apiCheck for native targets using v1 signatures`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/signatures/v1.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + + abiFile(projectName = "testproject") { + resolve("/examples/classes/TopLevelDeclarations.klib.v1.dump") + } + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `apiDump for native targets should fail when using invalid signature version`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/signatures/invalid.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + + runner { + arguments.add(":apiDump") + } + } + + runner.buildAndFail { + output shouldContain "Unsupported KLib signature version '100500'" + } + } + + @Test + fun `apiDump should work for Apple-targets`() { + assumeTrue(HostManager().isEnabled(KonanTarget.MACOS_ARM64)) + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/appleTargets/targets.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + checkKLibDump("/examples/classes/TopLevelDeclarations.klib.all.dump") + } + } + + @Test + fun `apiCheck should work for Apple-targets`() { + assumeTrue(HostManager().isEnabled(KonanTarget.MACOS_ARM64)) + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/appleTargets/targets.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + abiFile(projectName = "testproject") { + resolve("/examples/classes/TopLevelDeclarations.klib.all.dump") + } + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `apiCheck should not fail if a target is not supported`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + abiFile(projectName = "testproject") { + resolve("/examples/classes/TopLevelDeclarations.klib.dump") + } + runner { + arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `apiCheck should ignore unsupported targets by default`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + abiFile(projectName = "testproject") { + // note that the regular dump is used, where linuxArm64 is presented + resolve("/examples/classes/TopLevelDeclarations.klib.dump") + } + runner { + arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `apiCheck should fail for unsupported targets with strict mode turned on`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/unsupported/enforce.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + abiFile(projectName = "testproject") { + // note that the regular dump is used, where linuxArm64 is presented + resolve("/examples/classes/TopLevelDeclarations.klib.dump") + } + runner { + arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") + arguments.add(":apiCheck") + } + } + + runner.buildAndFail { + shouldHaveTaskWithOutcome(":klibApiExtractForValidation", FAILED) + } + } + + @Test + fun `klibDump should infer a dump for unsupported target from similar enough target`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + runner { + arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") + arguments.add(":klibApiDump") + } + } + runner.build { + checkKLibDump( + "/examples/classes/TopLevelDeclarations.klib.with.linux.dump", + dumpTask = ":klibApiDump" + ) + } + } + + @Test + fun `infer a dump for a target with custom name`() { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + buildGradleKts { + resolve("/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts") + } + additionalBuildConfig("/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxMain") + runner { + arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linux") + arguments.add(":klibApiDump") + } + } + runner.build { + checkKLibDump( + "/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump", + dumpTask = ":klibApiDump" + ) + } + } + + @Test + fun `klibDump should fail when the only target in the project is disabled`() { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + buildGradleKts { + resolve("/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts") + } + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + runner { + arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") + arguments.add(":klibApiDump") + } + } + + runner.buildAndFail { + shouldHaveTaskWithOutcome(":linuxArm64ApiInfer", FAILED) + output shouldContain "The target linuxArm64 is not supported by the host compiler and there are no targets similar to linuxArm64 to infer a dump from it." + } + } + + @Test + fun `klibDump if all klib-targets are unavailable`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + runner { + arguments.add( + "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + + "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" + ) + arguments.add(":klibApiDump") + } + } + + runner.buildAndFail { + output shouldContain "is not supported by the host compiler and there are no targets similar to" + } + } + + @Test + fun `klibCheck if all klib-targets are unavailable`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + abiFile(projectName = "testproject") { + // note that the regular dump is used, where linuxArm64 is presented + resolve("/examples/classes/TopLevelDeclarations.klib.dump") + } + runner { + arguments.add( + "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + + "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" + ) + arguments.add(":klibApiCheck") + } + } + + runner.buildAndFail { + output shouldContain "KLib ABI dump/validation requires at least one enabled klib target, but none were found." + } + } + + @Test + fun `target name clashing with a group name`() { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + buildGradleKts { + resolve("/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts") + resolve("/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts") + } + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + kotlin("AnotherBuildConfigLinuxX64.kt", "linuxMain") { + resolve("/examples/classes/AnotherBuildConfigLinuxArm64.kt") + } + runner { + arguments.add(":klibApiDump") + } + } + + runner.build { + checkKLibDump( + "/examples/classes/AnotherBuildConfig.klib.clash.dump", + dumpTask = ":klibApiDump" + ) + } + } + + @Test + fun `target name grouping with custom target names`() { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + buildGradleKts { + resolve("/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts") + resolve("/examples/gradle/configuration/grouping/customTargetNames.gradle.kts") + } + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + runner { + arguments.add(":klibApiDump") + } + } + + runner.build { + checkKLibDump( + "/examples/classes/AnotherBuildConfig.klib.custom.dump", + dumpTask = ":klibApiDump" + ) + } + } + + @Test + fun `target name grouping`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + kotlin("AnotherBuildConfigLinuxX64.kt", "linuxX64Main") { + resolve("/examples/classes/AnotherBuildConfigLinuxArm64.kt") + } + runner { + arguments.add(":klibApiDump") + } + } + runner.build { + checkKLibDump( + "/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump", + dumpTask = ":klibApiDump" + ) + } + } + + @Test + fun `apiDump should work with web targets`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/nonNativeKlibTargets/targets.gradle.kts") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.web.dump") + } + } + + @Test + fun `apiCheck should work with web targets`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/nonNativeKlibTargets/targets.gradle.kts") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + abiFile(projectName = "testproject") { + resolve("/examples/classes/AnotherBuildConfig.klib.web.dump") + } + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `check dump is updated on added declaration`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + + runner { + arguments.add(":apiDump") + } + } + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") + } + + // Update the source file by adding a declaration + val updatedSourceFile = File( + this::class.java.getResource( + "/examples/classes/AnotherBuildConfigModified.kt" + )!!.toURI() + ) + val existingSource = runner.projectDir.resolve( + "src/commonMain/kotlin/AnotherBuildConfig.kt" + ) + Files.write(existingSource.toPath(), updatedSourceFile.readBytes()) + + + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfigModified.klib.dump") + } + } + + @Test + fun `check dump is updated on a declaration added to some source sets`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + + runner { + arguments.add(":apiDump") + } + } + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") + // Update the source file by adding a declaration + val updatedSourceFile = File( + this::class.java.getResource( + "/examples/classes/AnotherBuildConfigLinuxArm64.kt" + )!!.toURI() + ) + val existingSource = runner.projectDir.resolve( + "src/linuxArm64Main/kotlin/AnotherBuildConfigLinuxArm64.kt" + ) + existingSource.parentFile.mkdirs() + Files.write(existingSource.toPath(), updatedSourceFile.readBytes()) + + + runner.build { + + checkKLibDump("/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump") + } + } + } + + @Test + fun `re-validate dump after sources updated`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + abiFile(projectName = "testproject") { + resolve("/examples/classes/AnotherBuildConfig.klib.dump") + } + runner { + arguments.add(":apiCheck") + } + } + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + + // Update the source file by adding a declaration + val updatedSourceFile = File( + this::class.java.getResource( + "/examples/classes/AnotherBuildConfigModified.kt" + )!!.toURI() + ) + val existingSource = runner.projectDir.resolve( + "src/commonMain/kotlin/AnotherBuildConfig.kt" + ) + Files.write(existingSource.toPath(), updatedSourceFile.readBytes()) + + runner.buildAndFail { + shouldHaveTaskWithOutcome(":klibApiCheck", FAILED) + } + } + + @Test + fun `validation should fail on target rename`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt") + abiFile(projectName = "testproject") { + resolve("/examples/classes/AnotherBuildConfig.klib.renamedTarget.dump") + } + runner { + arguments.add(":apiCheck") + } + } + runner.buildAndFail { + output shouldContain " -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64.linux, linuxX64, mingwX64]" + } + } + + @Test + fun `apiDump should not fail for empty project`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", sourceSet = "commonTest") + + runner { + arguments.add(":apiDump") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":klibApiCheck", SKIPPED) + rootProjectApiDump.parentFile.shouldNotExist() + } + } + + @Test + fun `apiDump should not fail if there is only one target`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", sourceSet = "commonTest") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", sourceSet = "linuxX64Main") + + runner { + arguments.add(":apiDump") + } + } + runner.build { + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.linuxX64Only.dump") + } + } + + @Test + fun `apiCheck should not fail for empty project`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", sourceSet = "commonTest") + runner { + arguments.add(":apiCheck") + } + } + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + @Test + fun `apiDump for a project with generated sources only`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/generatedSources/generatedSources.gradle.kts") + runner { + // TODO: enable configuration cache back when we start skipping tasks correctly +// configurationCache = false + arguments.add(":apiDump") + } + } + runner.build { + checkKLibDump("/examples/classes/GeneratedSources.klib.dump") + } + } + + @Test + fun `apiCheck for a project with generated sources only`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/generatedSources/generatedSources.gradle.kts") + abiFile(projectName = "testproject") { + resolve("/examples/classes/GeneratedSources.klib.dump") + } + runner { + // TODO: enable configuration cache back when we start skipping tasks correctly + //configurationCache = false + arguments.add(":apiCheck") + } + } + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + + private fun BuildResult.checkKLibDump( + expectedDumpFileName: String, + projectName: String = "testproject", + dumpTask: String = ":apiDump", + ) { + shouldHaveRunTask(dumpTask, SUCCESS) + + val generatedDump = rootProjectAbiDump(projectName) + + generatedDump.shouldExist() + + val expected = readResourceFile(expectedDumpFileName) + generatedDump.readText().invariantNewlines().shouldBeEqualComparingTo(expected) + } + + companion object { + private const val BANNED_TARGETS_PROPERTY_NAME = + "binary.compatibility.validator.klib.targets.disabled.for.testing" + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt index 3f9a931..e9c80a0 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt @@ -7,8 +7,7 @@ import io.kotest.matchers.file.shouldBeAFile import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import java.io.File -import org.gradle.testkit.runner.TaskOutcome.FAILED -import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.* import org.junit.jupiter.api.Test internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { @@ -27,7 +26,6 @@ internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { createProjectHierarchyWithPluginOnRoot() runner { arguments.add(":apiCheck") - arguments.add("--stacktrace") } dir("api/") { @@ -56,9 +54,7 @@ internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { val runner = test { createProjectHierarchyWithPluginOnRoot() runner { - arguments.add("--continue") arguments.add(":check") - arguments.add("--stacktrace") } dir("api/") { @@ -107,7 +103,6 @@ internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { runner { arguments.add(":apiDump") - arguments.add("--stacktrace") } dir("src/jvmMain/kotlin") {} @@ -135,5 +130,45 @@ internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { } } + @Test + fun testApiDumpPassesForEmptyProject() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts") + } + + runner { + arguments.add(":apiDump") + } + } + + runner.build { +// shouldHaveTaskWithOutcome(":jvmApiDump", SKIPPED) +// shouldHaveTaskWithOutcome(":apiDump", UP_TO_DATE) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + } + } + + @Test + fun testApiCheckPassesForEmptyProject() { + val runner = test { + buildGradleKts { + resolve("/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts") + } + + emptyApiFile(projectName = rootProjectDir.name) + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { +// shouldHaveTaskWithOutcome(":jvmApiCheck", SKIPPED) +// shouldHaveTaskWithOutcome(":apiCheck", UP_TO_DATE) + shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) + } + } + private val jvmApiDump: File get() = rootProjectDir.resolve("api/testproject.api") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt index 6cac93c..548f13e 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt @@ -62,9 +62,7 @@ internal class MultipleJvmTargetsTest : BaseKotlinGradleTest() { val runner = test { createProjectHierarchyWithPluginOnRoot() runner { - arguments.add("--continue") arguments.add(":check") - arguments.add("--stacktrace") } dir("api/jvm/") { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt index 94f13e4..f021aea 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt @@ -6,6 +6,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTas import io.kotest.matchers.file.shouldExist import io.kotest.matchers.shouldBe import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class NonPublicMarkersTest : BaseKotlinGradleTest() { @@ -36,6 +37,39 @@ class NonPublicMarkersTest : BaseKotlinGradleTest() { } } + @Test + @Disabled("https://youtrack.jetbrains.com/issue/KT-62259") + fun testIgnoredMarkersOnPropertiesForNativeTargets() { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + + buildGradleKts { + resolve("/examples/gradle/base/withNativePlugin.gradle.kts") + resolve("/examples/gradle/configuration/nonPublicMarkers/markers.gradle.kts") + } + + kotlin("Properties.kt", sourceSet = "commonMain") { + resolve("/examples/classes/Properties.kt") + } + + commonNativeTargets.forEach { + abiFile(projectName = "testproject", target = it) { + resolve("/examples/classes/Properties.klib.dump") + } + } + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveTaskWithOutcome(":apiCheck", SUCCESS) + } + } + @Test fun testFiltrationByPackageLevelAnnotations() { val runner = test { @@ -71,4 +105,16 @@ class NonPublicMarkersTest : BaseKotlinGradleTest() { rootProjectApiDump.readText() shouldBe dumpFile } } + + companion object { + private val commonNativeTargets: Set = setOf( + "linuxX64", + "linuxArm64", + "mingwX64", + "androidNativeArm32", + "androidNativeArm64", + "androidNativeX64", + "androidNativeX86" + ) + } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt index b4580d6..8ec9d81 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt @@ -3,6 +3,7 @@ package kotlinx.validation.test import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build import dev.adamko.kotlin.binary_compatibility_validator.test.utils.invariantNewlines +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveRunTask import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome import io.kotest.matchers.file.shouldBeAFile import io.kotest.matchers.file.shouldExist @@ -38,6 +39,41 @@ class PublicMarkersTest : BaseKotlinGradleTest() { } } + /** ⚠️ Public markers are not supported in KLib ABI dumps */ + @Test + fun testPublicMarkersForNativeTargets() { + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + + buildGradleKts { + resolve("/examples/gradle/base/withNativePlugin.gradle.kts") + resolve("/examples/gradle/configuration/publicMarkers/markers.gradle.kts") + } + + kotlin("ClassWithPublicMarkers.kt", sourceSet = "commonMain") { + resolve("/examples/classes/ClassWithPublicMarkers.kt") + } + + kotlin("ClassInPublicPackage.kt", sourceSet = "commonMain") { + resolve("/examples/classes/ClassInPublicPackage.kt") + } + + abiFile(projectName = "testproject") { + resolve("/examples/classes/ClassWithPublicMarkers.klib.dump") + } + + runner { + arguments.add(":apiCheck") + } + } + + runner.build { + shouldHaveRunTask(":apiCheck", SUCCESS) + } + } + @Test fun testFiltrationByPackageLevelAnnotations() { val runner = test { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/SettingsPluginDslTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/SettingsPluginDslTest.kt index bdc6029..f6fc871 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/SettingsPluginDslTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/SettingsPluginDslTest.kt @@ -20,16 +20,19 @@ internal class SettingsPluginDslTest : FunSpec({ projectType = "Kotlin/JVM", project = kotlinJvmProjectWithBcvSettingsPlugin(), expectedPrintedBCVTargets = """ + |type: class dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget_Decorated |name: kotlinJvm - |platformType: kotlinJvm |enabled: true |ignoredClasses: [com.package.MyIgnoredClass] |ignoredMarkers: [com.package.MyInternalApiAnnotationMarker] |ignoredPackages: [com.package.my_ignored_package] - |inputClasses: [main, main] + |publicMarkers: [com.package.MyIgnoredClass] + |publicPackages: [com.package.MyInternalApiAnnotationMarker] + |publicClasses: [com.package.my_ignored_package] + |platformType: kotlinJvm |inputJar: null + |inputClasses: [main, main] |------------------------------ - | """.trimMargin() ) @@ -37,16 +40,19 @@ internal class SettingsPluginDslTest : FunSpec({ projectType = "Kotlin/Multiplatform", project = kotlinMultiplatformProjectWithBcvSettingsPlugin(), expectedPrintedBCVTargets = """ + |type: class dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget_Decorated |name: jvm - |platformType: jvm |enabled: true |ignoredClasses: [com.package.MyIgnoredClass] |ignoredMarkers: [com.package.MyInternalApiAnnotationMarker] |ignoredPackages: [com.package.my_ignored_package] - |inputClasses: [main] + |publicMarkers: [com.package.MyIgnoredClass] + |publicPackages: [com.package.MyInternalApiAnnotationMarker] + |publicClasses: [com.package.my_ignored_package] + |platformType: jvm |inputJar: null + |inputClasses: [main] |------------------------------ - | """.trimMargin() ) @@ -79,22 +85,22 @@ internal class SettingsPluginDslTest : FunSpec({ apiDump.shouldExist() apiDump.shouldBeAFile() apiDump.readText().invariantNewlines() shouldBe /* language=TEXT */ """ - | - """.trimMargin() + | + """.trimMargin() } testCase.project.projectDir.resolve("sub2/api/sub2.api").asClue { apiDump -> apiDump.shouldExist() apiDump.shouldBeAFile() apiDump.readText().invariantNewlines() shouldBe /* language=TEXT */ """ - | - """.trimMargin() + | + """.trimMargin() } } test("expect the conventions set in the settings plugin are used in the subprojects") { testCase.project.runner.withArguments("printBCVTargets", "-q", "--stacktrace").build { - output.invariantNewlines() shouldBe testCase.expectedPrintedBCVTargets + output.invariantNewlines().trim() shouldBe testCase.expectedPrintedBCVTargets.trim() } } } @@ -148,7 +154,7 @@ private fun kotlinMultiplatformProjectWithBcvSettingsPlugin() = private val settingsGradleKtsWithBcvPlugin = """ buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.20") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.23") } } @@ -176,7 +182,7 @@ binaryCompatibilityValidator { @Language("kts") private val buildGradleKtsWithKotlinJvmAndBcvConfig = """ plugins { - kotlin("jvm") version "1.7.20" + kotlin("jvm") version "1.9.23" } // check that the DSL is available: @@ -190,7 +196,7 @@ binaryCompatibilityValidator { } @Language("kts") private val buildGradleKtsWithKotlinMultiplatformJvmAndBcvConfig = """ plugins { - kotlin("multiplatform") version "1.7.20" + kotlin("multiplatform") version "1.9.23" } kotlin { @@ -204,23 +210,40 @@ binaryCompatibilityValidator { } @Language("kts") private val printBcvTargetsTask = """ + val printBCVTargets by tasks.registering { - + val bcvTargets = binaryCompatibilityValidator.targets doLast { - bcvTargets.forEach { - println("name: " + it.name) - println("platformType: " + it.platformType) - println("enabled: " + it.enabled.get()) - println("ignoredClasses: " + it.ignoredClasses.get()) - println("ignoredMarkers: " + it.ignoredMarkers.get()) - println("ignoredPackages: " + it.ignoredPackages.get()) - println("inputClasses: " + it.inputClasses.files.map { f -> f.name }) - println("inputJar: " + it.inputJar.orNull) + bcvTargets.forEach { t -> + println("type: " + t::class.toString()) + println("name: " + t.name) + println("enabled: " + t.enabled.get()) + println("ignoredClasses: " + t.ignoredClasses.get()) + println("ignoredMarkers: " + t.ignoredMarkers.get()) + println("ignoredPackages: " + t.ignoredPackages.get()) + println("publicMarkers: " + t.ignoredClasses.get()) + println("publicPackages: " + t.ignoredMarkers.get()) + println("publicClasses: " + t.ignoredPackages.get()) + when (t) { + + is dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget -> { + println("platformType: " + t.platformType) + println("inputJar: " + t.inputJar.orNull) + println("inputClasses: " + t.inputClasses.files.map { f -> f.name }) + } + + is dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget -> { + println("klibFile: " + t.klibFile.files.map { f -> f.name }) + println("compilationDependencies: " + t.compilationDependencies.files.map { f -> f.name }) + println("currentPlatform: " + t.currentPlatform.orNull) + println("supportedByCurrentHost: " + t.supportedByCurrentHost.orNull) + println("hasKotlinSources: " + t.hasKotlinSources.orNull) + } + } println("------------------------------") } } } - """ diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.clash.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.clash.dump new file mode 100644 index 0000000..78f09ce --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.clash.dump @@ -0,0 +1,17 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64.linux, mingwX64] +// Alias: linux => [linuxArm64, linuxX64.linux] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} +// Targets: [linux] +final fun (org.different.pack/BuildConfig).org.different.pack/linuxArm64Specific(): kotlin/Int // org.different.pack/linuxArm64Specific|linuxArm64Specific@org.different.pack.BuildConfig(){}[0] diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.custom.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.custom.dump new file mode 100644 index 0000000..1adbdac --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.custom.dump @@ -0,0 +1,14 @@ +// Klib ABI Dump +// Targets: [linuxX64.linuxA, linuxX64.linuxB] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.dump new file mode 100644 index 0000000..9511bef --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.dump @@ -0,0 +1,14 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.linuxX64Only.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.linuxX64Only.dump new file mode 100644 index 0000000..7c6117d --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.linuxX64Only.dump @@ -0,0 +1,14 @@ +// Klib ABI Dump +// Targets: [linuxX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.renamedTarget.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.renamedTarget.dump new file mode 100644 index 0000000..8b84f00 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.renamedTarget.dump @@ -0,0 +1,14 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64.linux, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.web.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.web.dump new file mode 100644 index 0000000..bebc349 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfig.klib.web.dump @@ -0,0 +1,14 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, js, linuxArm64, linuxX64, mingwX64, wasmJs, wasmWasi] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump new file mode 100644 index 0000000..f67ac44 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump @@ -0,0 +1,17 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Alias: linux => [linuxArm64, linuxX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} +// Targets: [linux] +final fun (org.different.pack/BuildConfig).org.different.pack/linuxArm64Specific(): kotlin/Int // org.different.pack/linuxArm64Specific|linuxArm64Specific@org.different.pack.BuildConfig(){}[0] diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt new file mode 100644 index 0000000..f5352c0 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package org.different.pack + +fun BuildConfig.linuxArm64Specific(): Int = 42 diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump new file mode 100644 index 0000000..20292d7 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump @@ -0,0 +1,16 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} +// Targets: [linuxArm64] +final fun (org.different.pack/BuildConfig).org.different.pack/linuxArm64Specific(): kotlin/Int // org.different.pack/linuxArm64Specific|linuxArm64Specific@org.different.pack.BuildConfig(){}[0] diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.klib.dump new file mode 100644 index 0000000..75eb66b --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.klib.dump @@ -0,0 +1,15 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class org.different.pack/BuildConfig { // org.different.pack/BuildConfig|null[0] + constructor () // org.different.pack/BuildConfig.|(){}[0] + final fun f1(): kotlin/Int // org.different.pack/BuildConfig.f1|f1(){}[0] + final fun f2(): kotlin/Int // org.different.pack/BuildConfig.f2|f2(){}[0] + final val p1 // org.different.pack/BuildConfig.p1|{}p1[0] + final fun (): kotlin/Int // org.different.pack/BuildConfig.p1.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt new file mode 100644 index 0000000..8165117 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package org.different.pack + +public class BuildConfig { + public val p1 = 1 + + public fun f1() = p1 + + public fun f2() = p1 +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassWithPublicMarkers.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassWithPublicMarkers.klib.dump new file mode 100644 index 0000000..b1e8f29 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/ClassWithPublicMarkers.klib.dump @@ -0,0 +1,45 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Alias: androidNative => [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86] +// Alias: linux => [linuxArm64, linuxX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class foo.api/ClassInPublicPackage { // foo.api/ClassInPublicPackage|null[0] + constructor () // foo.api/ClassInPublicPackage.|(){}[0] + final class Inner { // foo.api/ClassInPublicPackage.Inner|null[0] + constructor () // foo.api/ClassInPublicPackage.Inner.|(){}[0] + } +} +final class foo/ClassWithPublicMarkers { // foo/ClassWithPublicMarkers|null[0] + constructor () // foo/ClassWithPublicMarkers.|(){}[0] + final class MarkedClass { // foo/ClassWithPublicMarkers.MarkedClass|null[0] + constructor () // foo/ClassWithPublicMarkers.MarkedClass.|(){}[0] + final val bar1 // foo/ClassWithPublicMarkers.MarkedClass.bar1|{}bar1[0] + final fun (): kotlin/Int // foo/ClassWithPublicMarkers.MarkedClass.bar1.|(){}[0] + } + final class NotMarkedClass { // foo/ClassWithPublicMarkers.NotMarkedClass|null[0] + constructor () // foo/ClassWithPublicMarkers.NotMarkedClass.|(){}[0] + } + final var bar1 // foo/ClassWithPublicMarkers.bar1|{}bar1[0] + final fun (): kotlin/Int // foo/ClassWithPublicMarkers.bar1.|(){}[0] + final fun (kotlin/Int) // foo/ClassWithPublicMarkers.bar1.|(kotlin.Int){}[0] + final var bar2 // foo/ClassWithPublicMarkers.bar2|{}bar2[0] + final fun (): kotlin/Int // foo/ClassWithPublicMarkers.bar2.|(){}[0] + final fun (kotlin/Int) // foo/ClassWithPublicMarkers.bar2.|(kotlin.Int){}[0] + final var notMarkedPublic // foo/ClassWithPublicMarkers.notMarkedPublic|{}notMarkedPublic[0] + final fun (): kotlin/Int // foo/ClassWithPublicMarkers.notMarkedPublic.|(){}[0] + final fun (kotlin/Int) // foo/ClassWithPublicMarkers.notMarkedPublic.|(kotlin.Int){}[0] +} +open annotation class foo/PublicClass : kotlin/Annotation { // foo/PublicClass|null[0] + constructor () // foo/PublicClass.|(){}[0] +} +open annotation class foo/PublicField : kotlin/Annotation { // foo/PublicField|null[0] + constructor () // foo/PublicField.|(){}[0] +} +open annotation class foo/PublicProperty : kotlin/Annotation { // foo/PublicProperty|null[0] + constructor () // foo/PublicProperty.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Empty.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Empty.klib.dump new file mode 100644 index 0000000..40583d9 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Empty.klib.dump @@ -0,0 +1,6 @@ +// Klib ABI Dump +// Targets: [mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.dump new file mode 100644 index 0000000..f69e8d2 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.dump @@ -0,0 +1,5 @@ +public final class Generated { + public fun ()V + public final fun helloCreator ()I +} + diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.klib.dump new file mode 100644 index 0000000..97ba68f --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/GeneratedSources.klib.dump @@ -0,0 +1,12 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class /Generated { // /Generated|null[0] + constructor () // /Generated.|(){}[0] + final fun helloCreator(): kotlin/Int // /Generated.helloCreator|helloCreator(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.klib.dump new file mode 100644 index 0000000..bed1c06 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.klib.dump @@ -0,0 +1,11 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class examples.classes/VC { // examples.classes/VC|null[0] + final var prop // examples.classes/VC.prop|{}prop[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt new file mode 100644 index 0000000..702ed05 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package examples.classes + +import annotations.* + +@HiddenFunction +public fun hidden() = Unit + +@HiddenProperty +public val v: Int = 42 + +@HiddenClass +public class HC + +public class VC @HiddenCtor constructor() { + @HiddenProperty + public val v: Int = 42 + + public var prop: Int = 0 + @HiddenGetter + get() = field + @HiddenSetter + set(value) { + field = value + } + + @HiddenProperty + public var fullyHiddenProp: Int = 0 + + @HiddenFunction + public fun m() = Unit +} + +@HiddenClass +public class HiddenOuterClass { + public class HiddenInnerClass { + + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt new file mode 100644 index 0000000..fdf8288 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package annotations + +@HiddenClass +@Target(AnnotationTarget.CLASS) +annotation class HiddenClass + +@HiddenClass +@Target(AnnotationTarget.FUNCTION) +annotation class HiddenFunction + +@HiddenClass +@Target(AnnotationTarget.CONSTRUCTOR) +annotation class HiddenCtor + +@HiddenClass +@Target(AnnotationTarget.PROPERTY) +annotation class HiddenProperty + +@HiddenClass +@Target(AnnotationTarget.FIELD) +annotation class HiddenField + +@HiddenClass +@Target(AnnotationTarget.PROPERTY_GETTER) +annotation class HiddenGetter + +@HiddenClass +@Target(AnnotationTarget.PROPERTY_SETTER) +annotation class HiddenSetter diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Properties.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Properties.klib.dump new file mode 100644 index 0000000..6359372 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Properties.klib.dump @@ -0,0 +1,17 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class foo/ClassWithProperties { // foo/ClassWithProperties|null[0] + constructor () // foo/ClassWithProperties.|(){}[0] +} +open annotation class foo/HiddenField : kotlin/Annotation { // foo/HiddenField|null[0] + constructor () // foo/HiddenField.|(){}[0] +} +open annotation class foo/HiddenProperty : kotlin/Annotation { // foo/HiddenProperty|null[0] + constructor () // foo/HiddenProperty.|(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt new file mode 100644 index 0000000..c5a298b --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package com.company.division + +public class ClassWithinSubPackage { +} + diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.dump new file mode 100644 index 0000000..04cb152 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.dump @@ -0,0 +1,7 @@ +public final class subclasses/A { + public fun ()V +} + +public final class subclasses/A$D { + public fun ()V +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.klib.dump new file mode 100644 index 0000000..e13fa3f --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.klib.dump @@ -0,0 +1,14 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final class subclasses/A { // subclasses/A|null[0] + constructor () // subclasses/A.|(){}[0] + final class D { // subclasses/A.D|null[0] + constructor () // subclasses/A.D.|(){}[0] + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt new file mode 100644 index 0000000..7f3f639 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package subclasses + +public class A { + public class B { + public class C + } + + public class D { + + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump new file mode 100644 index 0000000..aa08591 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump @@ -0,0 +1,67 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class examples.classes/AC { // examples.classes/AC|null[0] + abstract fun a() // examples.classes/AC.a|a(){}[0] + constructor () // examples.classes/AC.|(){}[0] + final fun b() // examples.classes/AC.b|b(){}[0] +} +abstract interface examples.classes/I // examples.classes/I|null[0] +final class examples.classes/C { // examples.classes/C|null[0] + constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] + final fun m() // examples.classes/C.m|m(){}[0] + final val v // examples.classes/C.v|{}v[0] + final fun (): kotlin/Any // examples.classes/C.v.|(){}[0] +} +final class examples.classes/D { // examples.classes/D|null[0] + constructor (kotlin/Int) // examples.classes/D.|(kotlin.Int){}[0] + final fun component1(): kotlin/Int // examples.classes/D.component1|component1(){}[0] + final fun copy(kotlin/Int =...): examples.classes/D // examples.classes/D.copy|copy(kotlin.Int){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // examples.classes/D.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // examples.classes/D.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // examples.classes/D.toString|toString(){}[0] + final val x // examples.classes/D.x|{}x[0] + final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] +} +final class examples.classes/Outer { // examples.classes/Outer|null[0] + constructor () // examples.classes/Outer.|(){}[0] + final class Nested { // examples.classes/Outer.Nested|null[0] + constructor () // examples.classes/Outer.Nested.|(){}[0] + final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] + constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] + } + } +} +final const val examples.classes/con // examples.classes/con|{}con[0] + final fun (): kotlin/String // examples.classes/con.|(){}[0] +final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] + enum entry A // examples.classes/E.A|null[0] + enum entry B // examples.classes/E.B|null[0] + enum entry C // examples.classes/E.C|null[0] + final fun valueOf(kotlin/String): examples.classes/E // examples.classes/E.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/E.values|values#static(){}[0] + final val entries // examples.classes/E.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] +} +final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] +final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] +final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] +final object examples.classes/O // examples.classes/O|null[0] +final val examples.classes/l // examples.classes/l|{}l[0] + final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/r // examples.classes/r|{}r[0] + final fun (): kotlin/Float // examples.classes/r.|(){}[0] + final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] +open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] + constructor () // examples.classes/A.|(){}[0] +} +open class examples.classes/OC { // examples.classes/OC|null[0] + constructor () // examples.classes/OC.|(){}[0] + final fun c() // examples.classes/OC.c|c(){}[0] + open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump new file mode 100644 index 0000000..c7bb38f --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump @@ -0,0 +1,67 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class examples.classes/AC { // examples.classes/AC|null[0] + abstract fun a() // examples.classes/AC.a|a(){}[0] + constructor () // examples.classes/AC.|(){}[0] + final fun b() // examples.classes/AC.b|b(){}[0] +} +abstract interface examples.classes/I // examples.classes/I|null[0] +final class examples.classes/C { // examples.classes/C|null[0] + constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] + final fun m() // examples.classes/C.m|m(){}[0] + final val v // examples.classes/C.v|{}v[0] + final fun (): kotlin/Any // examples.classes/C.v.|(){}[0] +} +final class examples.classes/D { // examples.classes/D|null[0] + constructor (kotlin/Int) // examples.classes/D.|(kotlin.Int){}[0] + final fun component1(): kotlin/Int // examples.classes/D.component1|component1(){}[0] + final fun copy(kotlin/Int =...): examples.classes/D // examples.classes/D.copy|copy(kotlin.Int){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // examples.classes/D.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // examples.classes/D.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // examples.classes/D.toString|toString(){}[0] + final val x // examples.classes/D.x|{}x[0] + final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] +} +final class examples.classes/Outer { // examples.classes/Outer|null[0] + constructor () // examples.classes/Outer.|(){}[0] + final class Nested { // examples.classes/Outer.Nested|null[0] + constructor () // examples.classes/Outer.Nested.|(){}[0] + final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] + constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] + } + } +} +final const val examples.classes/con // examples.classes/con|{}con[0] + final fun (): kotlin/String // examples.classes/con.|(){}[0] +final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] + enum entry A // examples.classes/E.A|null[0] + enum entry B // examples.classes/E.B|null[0] + enum entry C // examples.classes/E.C|null[0] + final fun valueOf(kotlin/String): examples.classes/E // examples.classes/E.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/E.values|values#static(){}[0] + final val entries // examples.classes/E.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] +} +final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] +final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] +final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] +final object examples.classes/O // examples.classes/O|null[0] +final val examples.classes/l // examples.classes/l|{}l[0] + final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/r // examples.classes/r|{}r[0] + final fun (): kotlin/Float // examples.classes/r.|(){}[0] + final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] +open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] + constructor () // examples.classes/A.|(){}[0] +} +open class examples.classes/OC { // examples.classes/OC|null[0] + constructor () // examples.classes/OC.|(){}[0] + final fun c() // examples.classes/OC.c|c(){}[0] + open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump new file mode 100644 index 0000000..9764aec --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump @@ -0,0 +1,67 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class examples.classes/AC { // examples.classes/AC|null[0] + abstract fun a() // examples.classes/AC.a|a(){}[0] + constructor () // examples.classes/AC.|(){}[0] + final fun b() // examples.classes/AC.b|b(){}[0] +} +abstract interface examples.classes/I // examples.classes/I|null[0] +final class examples.classes/C { // examples.classes/C|null[0] + constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] + final fun m() // examples.classes/C.m|m(){}[0] + final val v // examples.classes/C.v|{}v[0] + final fun (): kotlin/Any // examples.classes/C.v.|(){}[0] +} +final class examples.classes/D { // examples.classes/D|null[0] + constructor (kotlin/Int) // examples.classes/D.|(kotlin.Int){}[0] + final fun component1(): kotlin/Int // examples.classes/D.component1|component1(){}[0] + final fun copy(kotlin/Int =...): examples.classes/D // examples.classes/D.copy|copy(kotlin.Int){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // examples.classes/D.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // examples.classes/D.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // examples.classes/D.toString|toString(){}[0] + final val x // examples.classes/D.x|{}x[0] + final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] +} +final class examples.classes/Outer { // examples.classes/Outer|null[0] + constructor () // examples.classes/Outer.|(){}[0] + final class Nested { // examples.classes/Outer.Nested|null[0] + constructor () // examples.classes/Outer.Nested.|(){}[0] + final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] + constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] + } + } +} +final const val examples.classes/con // examples.classes/con|{}con[0] + final fun (): kotlin/String // examples.classes/con.|(){}[0] +final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] + enum entry A // examples.classes/E.A|null[0] + enum entry B // examples.classes/E.B|null[0] + enum entry C // examples.classes/E.C|null[0] + final fun valueOf(kotlin/String): examples.classes/E // examples.classes/E.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/E.values|values#static(){}[0] + final val entries // examples.classes/E.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] +} +final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] +final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] +final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] +final object examples.classes/O // examples.classes/O|null[0] +final val examples.classes/l // examples.classes/l|{}l[0] + final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/r // examples.classes/r|{}r[0] + final fun (): kotlin/Float // examples.classes/r.|(){}[0] + final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] +open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] + constructor () // examples.classes/A.|(){}[0] +} +open class examples.classes/OC { // examples.classes/OC|null[0] + constructor () // examples.classes/OC.|(){}[0] + final fun c() // examples.classes/OC.c|c(){}[0] + open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump new file mode 100644 index 0000000..9442fd6 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump @@ -0,0 +1,67 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 1 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class examples.classes/AC { // examples.classes/AC|null[0] + abstract fun a() // examples.classes/AC.a|-4432112437378250461[0] + constructor () // examples.classes/AC.|-5645683436151566731[0] + final fun b() // examples.classes/AC.b|4789657038926421504[0] +} +abstract interface examples.classes/I // examples.classes/I|null[0] +final class examples.classes/C { // examples.classes/C|null[0] + constructor (kotlin/Any) // examples.classes/C.|4518179880532599055[0] + final fun m() // examples.classes/C.m|-1029306787563722981[0] + final val v // examples.classes/C.v|138869847852828796[0] + final fun (): kotlin/Any // examples.classes/C.v.|4964732996156868941[0] +} +final class examples.classes/D { // examples.classes/D|null[0] + constructor (kotlin/Int) // examples.classes/D.|-5182794243525578284[0] + final fun component1(): kotlin/Int // examples.classes/D.component1|162597135895221648[0] + final fun copy(kotlin/Int =...): examples.classes/D // examples.classes/D.copy|-6971662324481626298[0] + final fun equals(kotlin/Any?): kotlin/Boolean // examples.classes/D.equals|4638265728071529943[0] + final fun hashCode(): kotlin/Int // examples.classes/D.hashCode|3409210261493131192[0] + final fun toString(): kotlin/String // examples.classes/D.toString|-1522858123163872138[0] + final val x // examples.classes/D.x|-8060530855978347579[0] + final fun (): kotlin/Int // examples.classes/D.x.|1482705010654679335[0] +} +final class examples.classes/Outer { // examples.classes/Outer|null[0] + constructor () // examples.classes/Outer.|-5645683436151566731[0] + final class Nested { // examples.classes/Outer.Nested|null[0] + constructor () // examples.classes/Outer.Nested.|-5645683436151566731[0] + final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] + constructor () // examples.classes/Outer.Nested.Inner.|-5645683436151566731[0] + } + } +} +final const val examples.classes/con // examples.classes/con|-2899158152154217071[0] + final fun (): kotlin/String // examples.classes/con.|-2604863570302238407[0] +final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] + enum entry A // examples.classes/E.A|null[0] + enum entry B // examples.classes/E.B|null[0] + enum entry C // examples.classes/E.C|null[0] + final fun valueOf(kotlin/String): examples.classes/E // examples.classes/E.valueOf|-4683474617854611729[0] + final fun values(): kotlin/Array // examples.classes/E.values|-8715569000920726747[0] + final val entries // examples.classes/E.entries|-5134227801081826149[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|-6068527377476727729[0] +} +final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|8042761629495509481[0] +final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|6322333980269160703[0] +final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|-9193388292326484960[0] +final object examples.classes/O // examples.classes/O|null[0] +final val examples.classes/l // examples.classes/l|3307215303229595169[0] + final fun (): kotlin/Long // examples.classes/l.|3795442967620585[0] +final var examples.classes/r // examples.classes/r|-8117627916896159533[0] + final fun (): kotlin/Float // examples.classes/r.|-7424184448774736572[0] + final fun (kotlin/Float) // examples.classes/r.|9171637170963327464[0] +open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] + constructor () // examples.classes/A.|-5645683436151566731[0] +} +open class examples.classes/OC { // examples.classes/OC|null[0] + constructor () // examples.classes/OC.|-5645683436151566731[0] + final fun c() // examples.classes/OC.c|-2724918380551733646[0] + open fun o(): kotlin/Int // examples.classes/OC.o|-3264635847192431671[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump new file mode 100644 index 0000000..5b397f2 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump @@ -0,0 +1,67 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64.linux, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class examples.classes/AC { // examples.classes/AC|null[0] + abstract fun a() // examples.classes/AC.a|a(){}[0] + constructor () // examples.classes/AC.|(){}[0] + final fun b() // examples.classes/AC.b|b(){}[0] +} +abstract interface examples.classes/I // examples.classes/I|null[0] +final class examples.classes/C { // examples.classes/C|null[0] + constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] + final fun m() // examples.classes/C.m|m(){}[0] + final val v // examples.classes/C.v|{}v[0] + final fun (): kotlin/Any // examples.classes/C.v.|(){}[0] +} +final class examples.classes/D { // examples.classes/D|null[0] + constructor (kotlin/Int) // examples.classes/D.|(kotlin.Int){}[0] + final fun component1(): kotlin/Int // examples.classes/D.component1|component1(){}[0] + final fun copy(kotlin/Int =...): examples.classes/D // examples.classes/D.copy|copy(kotlin.Int){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // examples.classes/D.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // examples.classes/D.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // examples.classes/D.toString|toString(){}[0] + final val x // examples.classes/D.x|{}x[0] + final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] +} +final class examples.classes/Outer { // examples.classes/Outer|null[0] + constructor () // examples.classes/Outer.|(){}[0] + final class Nested { // examples.classes/Outer.Nested|null[0] + constructor () // examples.classes/Outer.Nested.|(){}[0] + final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] + constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] + } + } +} +final const val examples.classes/con // examples.classes/con|{}con[0] + final fun (): kotlin/String // examples.classes/con.|(){}[0] +final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] + enum entry A // examples.classes/E.A|null[0] + enum entry B // examples.classes/E.B|null[0] + enum entry C // examples.classes/E.C|null[0] + final fun valueOf(kotlin/String): examples.classes/E // examples.classes/E.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/E.values|values#static(){}[0] + final val entries // examples.classes/E.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] +} +final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] +final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] +final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] +final object examples.classes/O // examples.classes/O|null[0] +final val examples.classes/l // examples.classes/l|{}l[0] + final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/r // examples.classes/r|{}r[0] + final fun (): kotlin/Float // examples.classes/r.|(){}[0] + final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] +open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] + constructor () // examples.classes/A.|(){}[0] +} +open class examples.classes/OC { // examples.classes/OC|null[0] + constructor () // examples.classes/OC.|(){}[0] + final fun c() // examples.classes/OC.c|c(){}[0] + open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump new file mode 100644 index 0000000..c7bb38f --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump @@ -0,0 +1,67 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64, linuxX64, mingwX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract class examples.classes/AC { // examples.classes/AC|null[0] + abstract fun a() // examples.classes/AC.a|a(){}[0] + constructor () // examples.classes/AC.|(){}[0] + final fun b() // examples.classes/AC.b|b(){}[0] +} +abstract interface examples.classes/I // examples.classes/I|null[0] +final class examples.classes/C { // examples.classes/C|null[0] + constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] + final fun m() // examples.classes/C.m|m(){}[0] + final val v // examples.classes/C.v|{}v[0] + final fun (): kotlin/Any // examples.classes/C.v.|(){}[0] +} +final class examples.classes/D { // examples.classes/D|null[0] + constructor (kotlin/Int) // examples.classes/D.|(kotlin.Int){}[0] + final fun component1(): kotlin/Int // examples.classes/D.component1|component1(){}[0] + final fun copy(kotlin/Int =...): examples.classes/D // examples.classes/D.copy|copy(kotlin.Int){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // examples.classes/D.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // examples.classes/D.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // examples.classes/D.toString|toString(){}[0] + final val x // examples.classes/D.x|{}x[0] + final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] +} +final class examples.classes/Outer { // examples.classes/Outer|null[0] + constructor () // examples.classes/Outer.|(){}[0] + final class Nested { // examples.classes/Outer.Nested|null[0] + constructor () // examples.classes/Outer.Nested.|(){}[0] + final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] + constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] + } + } +} +final const val examples.classes/con // examples.classes/con|{}con[0] + final fun (): kotlin/String // examples.classes/con.|(){}[0] +final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] + enum entry A // examples.classes/E.A|null[0] + enum entry B // examples.classes/E.B|null[0] + enum entry C // examples.classes/E.C|null[0] + final fun valueOf(kotlin/String): examples.classes/E // examples.classes/E.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/E.values|values#static(){}[0] + final val entries // examples.classes/E.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] +} +final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] +final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] +final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] +final object examples.classes/O // examples.classes/O|null[0] +final val examples.classes/l // examples.classes/l|{}l[0] + final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/r // examples.classes/r|{}r[0] + final fun (): kotlin/Float // examples.classes/r.|(){}[0] + final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] +open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] + constructor () // examples.classes/A.|(){}[0] +} +open class examples.classes/OC { // examples.classes/OC|null[0] + constructor () // examples.classes/OC.|(){}[0] + final fun c() // examples.classes/OC.c|c(){}[0] + open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt new file mode 100644 index 0000000..577e3f9 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt @@ -0,0 +1,35 @@ +@file:Suppress("unused", "UNUSED_PARAMETER", "RedundantVisibilityModifier", "MayBeConstant") + +package examples.classes + +public fun testFun(): Int = 42 +public fun consume(arg: T) = Unit +public inline fun testInlineFun() = Unit +public const val con: String = "I'm a constant!" +public val l: Long = 0xc001 +public var r: Float = 3.14f + +public annotation class A +public interface I +public data class D(val x: Int) +public class C(public val v: Any) { + public fun m() = Unit +} + +public object O +public enum class E { A, B, C } +public abstract class AC { + public abstract fun a() + public fun b() = Unit +} + +public open class OC { + public open fun o(): Int = 42 + public fun c() = Unit +} + +public class Outer { + public class Nested { + public inner class Inner + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/enableJvmInWithNativePlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/enableJvmInWithNativePlugin.gradle.kts new file mode 100644 index 0000000..993a178 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/enableJvmInWithNativePlugin.gradle.kts @@ -0,0 +1,3 @@ +kotlin { + jvm() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts index 3ebdb4d..a9921d7 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts @@ -1,28 +1,20 @@ -import org.jetbrains.kotlin.config.JvmTarget - plugins { - kotlin("jvm") version "1.9.22" - id("org.jetbrains.kotlinx.binary-compatibility-validator") -} - -repositories { - mavenCentral() + kotlin("jvm") version "1.9.22" + id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } -val minTarget = JvmTarget.supportedValues().minBy { it.majorVersion } -val maxTarget = JvmTarget.supportedValues().maxBy { it.majorVersion } +val minJvmTarget = org.jetbrains.kotlin.config.JvmTarget.supportedValues().minBy { it.majorVersion } +val maxJvmTarget = org.jetbrains.kotlin.config.JvmTarget.supportedValues().maxBy { it.majorVersion } -val useMax = (project.properties["useMaxVersion"]?.toString() ?: "false").toBoolean() -val target = (if (useMax) maxTarget else minTarget).toString() +val useMaxJdkVersion = providers.gradleProperty("useMaxJdkVersion").orNull.toBoolean() +val selectedJvmTarget = (if (useMaxJdkVersion) maxJvmTarget else minJvmTarget).toString() -val toolchainVersion = target.split('.').last().toInt() +val toolchainVersion = selectedJvmTarget.split('.').last().toInt() kotlin { - jvmToolchain(toolchainVersion) -} + jvmToolchain(toolchainVersion) -tasks.compileKotlin { - compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(target)) - } + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(selectedJvmTarget)) + } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts index da90886..b2b2cd1 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts @@ -1,23 +1,15 @@ -import org.jetbrains.kotlin.config.JvmTarget - plugins { - kotlin("jvm") version "1.9.22" - id("org.jetbrains.kotlinx.binary-compatibility-validator") -} - -repositories { - mavenCentral() + kotlin("jvm") version "1.9.22" + id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } -val target = project.properties["jdkVersion"]!!.toString() -val toolchainVersion = target.split('.').last().toInt() +val selectedJvmTarget = providers.gradleProperty("jvmTarget").get() +val toolchainVersion = selectedJvmTarget.split('.').last().toInt() kotlin { jvmToolchain(toolchainVersion) -} -tasks.compileKotlin { compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(target)) + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(selectedJvmTarget)) } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts index 895de7b..1d85c46 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version "1.5.20" + kotlin("multiplatform") version embeddedKotlinVersion id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } @@ -12,6 +12,9 @@ kotlin { testRuns["test"].executionTask.configure { useJUnit() } + attributes { + attribute(Attribute.of("variant", String::class.java), "a") + } } jvm("anotherJvm") { compilations.all { @@ -20,11 +23,14 @@ kotlin { testRuns["test"].executionTask.configure { useJUnit() } + attributes { + attribute(Attribute.of("variant", String::class.java), "b") + } } } sourceSets { - val commonMain by getting - val commonTest by getting { + commonMain + commonTest { dependencies { implementation(kotlin("stdlib")) implementation(kotlin("test-common")) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts index 8fcf6bf..b7471e6 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version "1.7.20" + kotlin("multiplatform") version embeddedKotlinVersion id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } @@ -18,7 +18,6 @@ kotlin { val commonMain by getting val commonTest by getting { dependencies { - implementation(kotlin("stdlib")) implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) } @@ -26,7 +25,6 @@ kotlin { val jvmMain by getting val jvmTest by getting { dependencies { - implementation(kotlin("stdlib")) implementation(kotlin("test-junit")) } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts new file mode 100644 index 0000000..0c52b6a --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts @@ -0,0 +1,27 @@ +plugins { + kotlin("multiplatform") version embeddedKotlinVersion + id("dev.adamko.kotlin.binary-compatibility-validator") version "+" +} + +kotlin { + linuxX64() + linuxArm64() + mingwX64() + androidNativeArm32() + androidNativeArm64() + androidNativeX64() + androidNativeX86() + + sourceSets { + commonMain {} + commonTest { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +binaryCompatibilityValidator { + klib.enable() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts new file mode 100644 index 0000000..e9012d5 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin("multiplatform") version embeddedKotlinVersion + id("dev.adamko.kotlin.binary-compatibility-validator") version "+" +} + +kotlin { + sourceSets { + commonMain {} + commonTest { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +binaryCompatibilityValidator { + klib.enable() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts new file mode 100644 index 0000000..14103c8 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts @@ -0,0 +1,20 @@ +plugins { + kotlin("multiplatform") version embeddedKotlinVersion + id("dev.adamko.kotlin.binary-compatibility-validator") version "+" +} + +kotlin { + linuxArm64() + + sourceSets { + commonTest { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +binaryCompatibilityValidator { + klib.enable() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts index 69984e0..b81ec3c 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts @@ -1,4 +1,4 @@ plugins { - kotlin("jvm") version "1.8.0" + kotlin("jvm") version embeddedKotlinVersion id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/appleTargets/targets.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/appleTargets/targets.gradle.kts new file mode 100644 index 0000000..0bb199c --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/appleTargets/targets.gradle.kts @@ -0,0 +1,15 @@ +kotlin { + macosX64() + macosArm64() + iosX64() + iosArm64() + iosSimulatorArm64() + tvosX64() + tvosArm64() + tvosSimulatorArm64() + watchosArm32() + watchosArm64() + watchosX64() + watchosSimulatorArm64() + watchosDeviceArm64() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedJvmSources.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedJvmSources.gradle.kts new file mode 100644 index 0000000..5ca5f72 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedJvmSources.gradle.kts @@ -0,0 +1,23 @@ +val generateSources by tasks.registering { + val outputDir = layout.buildDirectory.dir("generated/kotlin") + + outputs.dir(outputDir).withPropertyName("outputDir") + + doLast { + outputDir.get().asFile.apply { + mkdirs() + resolve("Generated.kt").writeText( + """ + |public class Generated { + | public fun helloCreator(): Int = 42 + |} + | + """.trimMargin() + ) + } + } +} + +kotlin.sourceSets.main { + kotlin.srcDir(generateSources) +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedSources.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedSources.gradle.kts new file mode 100644 index 0000000..e89a3b7 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/generatedSources/generatedSources.gradle.kts @@ -0,0 +1,23 @@ +val generateSources by tasks.registering { + val outputDir = layout.buildDirectory.dir("generated/kotlin") + + outputs.dir(outputDir).withPropertyName("outputDir") + + doLast { + outputDir.get().asFile.apply { + mkdirs() + resolve("Generated.kt").writeText( + """ + |public class Generated { + | public fun helloCreator(): Int = 42 + |} + | + """.trimMargin() + ) + } + } +} + +kotlin.sourceSets.getByName("commonMain") { + kotlin.srcDir(generateSources) +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts new file mode 100644 index 0000000..026df98 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts @@ -0,0 +1,9 @@ +kotlin { + linuxX64("linux") + linuxArm64() + mingwX64() + androidNativeArm32() + androidNativeArm64() + androidNativeX64() + androidNativeX86() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/customTargetNames.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/customTargetNames.gradle.kts new file mode 100644 index 0000000..ae59ace --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/grouping/customTargetNames.gradle.kts @@ -0,0 +1,12 @@ +kotlin { + linuxX64("linuxA") { + attributes { + attribute(Attribute.of("variant", String::class.java), "a") + } + } + linuxX64("linuxB") { + attributes { + attribute(Attribute.of("variant", String::class.java), "b") + } + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts new file mode 100644 index 0000000..776e96f --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts @@ -0,0 +1,9 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +configure { + // ignoredClasses.add("subclasses.A.B") + ignoredClasses.add("subclasses.A\$B") +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/jarAsInput/inputJar.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/jarAsInput/inputJar.gradle.kts index d894a22..a75c78d 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/jarAsInput/inputJar.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/jarAsInput/inputJar.gradle.kts @@ -1,6 +1,6 @@ tasks.jar { exclude("foo/HiddenField.class") - exclude("**/HiddenProperty.class") + exclude("foo/HiddenProperty.class") } binaryCompatibilityValidator { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonNativeKlibTargets/targets.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonNativeKlibTargets/targets.gradle.kts new file mode 100644 index 0000000..71931fa --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonNativeKlibTargets/targets.gradle.kts @@ -0,0 +1,5 @@ +kotlin { + wasmWasi() + wasmJs() + js() +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts new file mode 100644 index 0000000..e853381 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts @@ -0,0 +1,13 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +configure { + nonPublicMarkers.add("annotations.HiddenClass") + nonPublicMarkers.add("annotations.HiddenCtor") + nonPublicMarkers.add("annotations.HiddenProperty") + nonPublicMarkers.add("annotations.HiddenGetter") + nonPublicMarkers.add("annotations.HiddenSetter") + nonPublicMarkers.add("annotations.HiddenFunction") +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts new file mode 100644 index 0000000..ddb2f31 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts @@ -0,0 +1,10 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +configure { + klib { + signatureVersion = kotlinx.validation.api.klib.KlibSignatureVersion.of(100500) + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts new file mode 100644 index 0000000..28f0322 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts @@ -0,0 +1,8 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +configure { + klib.signatureVersion = kotlinx.validation.api.klib.KlibSignatureVersion.of(1) +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts new file mode 100644 index 0000000..99270cd --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts @@ -0,0 +1,8 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +configure { + klib.strictValidation = true +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts index 04abb29..1791349 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/jdk-provisioning.gradle.kts @@ -1,3 +1,3 @@ plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.7.0") + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-android-project.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-android-project.gradle.kts index 4b53276..72aad02 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-android-project.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-android-project.gradle.kts @@ -1,9 +1,8 @@ pluginManagement { repositories { - mavenLocal() + mavenCentral() gradlePluginPortal() google() - mavenCentral() } } @@ -16,5 +15,6 @@ dependencyResolutionManagement { } rootProject.name = "android-project" + include(":kotlin-library") include(":java-library") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-name-testproject.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-name-testproject.gradle.kts index aa60750..6b8818c 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-name-testproject.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-name-testproject.gradle.kts @@ -1 +1 @@ -rootProject.name = "testproject" \ No newline at end of file +rootProject.name = "testproject" diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-with-hierarchy.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-with-hierarchy.gradle.kts index b8c087f..d28e187 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-with-hierarchy.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/settings/settings-with-hierarchy.gradle.kts @@ -1,4 +1,4 @@ -include("sub1") -include("sub1:subsub1") -include("sub1:subsub2") -include("sub2") +include(":sub1") +include(":sub1:subsub1") +include(":sub1:subsub2") +include(":sub2") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt index d161d06..ec3ddf2 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt @@ -10,5 +10,9 @@ open class BaseKotlinGradleTest { val rootProjectDir: File get() = testProjectDir - val rootProjectApiDump: File get() = rootProjectDir.resolve("api/${rootProjectDir.name}.api") + val rootProjectApiDump: File get() = rootProjectDir.resolve("$API_DIR/${rootProjectDir.name}.api") + + fun rootProjectAbiDump( + project: String = rootProjectDir.name + ): File = rootProjectDir.resolve("$API_DIR/$project.klib.api") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt index 3cbe7e4..fcfe3f5 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt @@ -55,8 +55,8 @@ fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { return GradleRunner.create() .withProjectDir(rootProjectDir) - .withGradleVersion(minimumGradleTestVersion) - .withArguments(baseKotlinScope.runner.arguments) + .withGradleVersion(baseKotlinScope.runner.gradleVersion) + .withArguments(baseKotlinScope.runner.buildArgs()) .apply { GradleProjectTest.gradleTestKitDir?.let { println("Using Gradle TestKit dir $it") @@ -66,7 +66,7 @@ fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { } /** - * same as [file][FileContainer.file], but prepends "src/${sourceSet}/kotlin" before given `classFileName` + * same as [file][FileContainer.file], but prepends `src/${sourceSet}/kotlin` before given `classFileName` */ fun FileContainer.kotlin( classFileName: String, @@ -82,7 +82,7 @@ fun FileContainer.kotlin( } /** - * same as [file][FileContainer.file], but prepends "src/${sourceSet}/java" before given `classFileName` + * same as [file][FileContainer.file], but prepends `src/${sourceSet}/java` before given `classFileName` */ fun FileContainer.java( classFileName: String, @@ -113,6 +113,14 @@ fun FileContainer.settingsGradleKts(fn: AppendableScope.() -> Unit) { file(fileName, fn) } +/** + * Shortcut for creating a `settings.gradle.kts` by using [file][FileContainer.file] + */ +fun FileContainer.gradleProperties(fn: AppendableScope.() -> Unit) { + val fileName = "gradle.properties" + file(fileName, fn) +} + /** * Declares a directory with the given [dirName] inside the current container. * All calls creating files within this scope will create the files nested in this directory. @@ -133,6 +141,23 @@ fun FileContainer.apiFile(projectName: String, fn: AppendableScope.() -> Unit) { } } +/** + * Shortcut for creating a `api//.klib.api` descriptor using [file][FileContainer.file] + */ +fun FileContainer.abiFile(projectName: String, target: String, fn: AppendableScope.() -> Unit) { + dir(API_DIR) { + dir(target) { + file("$projectName.klib.api", fn) + } + } +} + +fun FileContainer.abiFile(projectName: String, fn: AppendableScope.() -> Unit) { + dir(API_DIR) { + file("$projectName.klib.api", fn) + } +} + // not using default argument in apiFile for clarity in tests (explicit "empty" in the name) /** * Shortcut for creating an empty `api/.api` descriptor by using [file][FileContainer.file] @@ -142,10 +167,7 @@ fun FileContainer.emptyApiFile(projectName: String) { } fun BaseKotlinScope.runner(fn: Runner.() -> Unit) { - val runner = Runner() fn(runner) - - this.runner = runner } fun AppendableScope.resolve(@Language("file-reference") path: String) { @@ -156,11 +178,14 @@ fun AppendableScope.addText(text: String) { this.content.add(AppendableScope.AppendText(text)) } +fun AppendableScope.addLine(text: String): Unit = addText("$text\n") + interface FileContainer { fun file(fileName: String, fn: AppendableScope.() -> Unit = {}) } class BaseKotlinScope : FileContainer { + var files: MutableList = mutableListOf() var runner: Runner = Runner() @@ -192,13 +217,28 @@ class AppendableScope(val filePath: String) { } class Runner { - val arguments: MutableList = mutableListOf( - "--configuration-cache", - //"--info", - "--stacktrace", - ) -} - + var gradleVersion: String = minimumGradleTestVersion + var configurationCache: Boolean = true + var rerunTasks: Boolean = false + var rerunTask: Boolean = false + var buildCache: Boolean = true + var stacktrace: Boolean = true + var continues: Boolean = true + + val arguments: MutableList = mutableListOf() + + internal fun buildArgs(): List = buildList { + addAll(arguments) + if (configurationCache) add("--configuration-cache") else add("--no-configuration-cache") + if (buildCache) add("--build-cache") else add("--no-build-cache") + if (rerunTasks) add("--rerun-tasks") + if (rerunTask) add("--rerun-task") + if (stacktrace) add("--stacktrace") + if (continues) add("--continue") + }.distinct() +} + +/** Read a resources file as a [String]. */ fun readResourceFile(@Language("file-reference") path: String): String { val resource = BaseKotlinGradleTest::class.java.getResource(path) ?: error("Could not find resource '$path'") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/gradleRunnerUtils.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/gradleRunnerUtils.kt index 59e57a2..ed574bb 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/gradleRunnerUtils.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/gradleRunnerUtils.kt @@ -8,7 +8,7 @@ fun GradleRunner.withEnvironment(vararg map: Pair): GradleRunner withEnvironment(map.toMap()) -inline fun GradleRunner.build(block: BuildResult.() -> Unit) = build().block() -inline fun GradleRunner.buildAndFail(block: BuildResult.() -> Unit) = buildAndFail().block() +inline fun GradleRunner.build(block: BuildResult.() -> Unit): Unit = build().block() +inline fun GradleRunner.buildAndFail(block: BuildResult.() -> Unit): Unit = buildAndFail().block() @Suppress("UnstableApiUsage") -inline fun GradleRunner.run(block: BuildResult.() -> Unit) = run().block() +inline fun GradleRunner.run(block: BuildResult.() -> Unit): Unit = run().block() diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index 8b660f6..3cc23cc 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -5,6 +5,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVPlugin public static final field API_GENERATE_TASK_NAME Ljava/lang/String; public static final field Companion Ldev/adamko/kotlin/binary_compatibility_validator/BCVPlugin$Companion; public static final field EXTENSION_NAME Ljava/lang/String; + public static final field PREPARE_API_GENERATE_TASK_NAME Ljava/lang/String; public static final field RUNTIME_CLASSPATH_CONFIGURATION_NAME Ljava/lang/String; public static final field RUNTIME_CLASSPATH_RESOLVER_CONFIGURATION_NAME Ljava/lang/String; public static final field TASK_GROUP Ljava/lang/String; @@ -15,7 +16,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVPlugin public final class dev/adamko/kotlin/binary_compatibility_validator/BCVPlugin$Companion { } -public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVProjectExtension : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetSpec, org/gradle/api/plugins/ExtensionAware { +public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVProjectExtension : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec, org/gradle/api/plugins/ExtensionAware { public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredMarkers ()Lorg/gradle/api/provider/SetProperty; @@ -27,7 +28,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVProjec public abstract fun getPublicClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getPublicMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getPublicPackages ()Lorg/gradle/api/provider/SetProperty; - public final fun getTargets ()Lorg/gradle/api/NamedDomainObjectContainer; + public final fun getTargets ()Lorg/gradle/api/ExtensiblePolymorphicDomainObjectContainer; } public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVProjectPlugin : org/gradle/api/Plugin { @@ -45,9 +46,8 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVSettin } public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVSettingsPlugin$Extension { - public fun (Ldev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetSpec;)V - public final fun defaultTargetValues (Lkotlin/jvm/functions/Function1;)V - public final fun getDefaultTargetValues ()Ldev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetSpec; + public final fun defaultTargetValues (Lorg/gradle/api/Action;)V + public final fun getDefaultTargetValues ()Ldev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetDefaults; public abstract fun getIgnoredProjects ()Lorg/gradle/api/provider/SetProperty; } @@ -56,28 +56,74 @@ public final class dev/adamko/kotlin/binary_compatibility_validator/BCVSettingsP public final synthetic fun execute (Ljava/lang/Object;)V } -public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetSpec, java/io/Serializable, org/gradle/api/Named { - public fun (Ljava/lang/String;)V +public final class dev/adamko/kotlin/binary_compatibility_validator/adapters/AndroidAdapterKt$createKotlinAndroidTargets$1$1$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public final class dev/adamko/kotlin/binary_compatibility_validator/adapters/JavaTestFixturesKt$createJavaTestFixtureTargets$1$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public final class dev/adamko/kotlin/binary_compatibility_validator/adapters/KotlinJvmAdapterKt$createKotlinJvmTargets$1$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public final class dev/adamko/kotlin/binary_compatibility_validator/adapters/KotlinMultiplatformAdapterKt$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public abstract interface annotation class dev/adamko/kotlin/binary_compatibility_validator/internal/BCVExperimentalApi : java/lang/annotation/Annotation { +} + +public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVJvmTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget, org/gradle/api/Named { + public abstract fun getInputClasses ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getInputJar ()Lorg/gradle/api/file/RegularFileProperty; + public final fun getObjects ()Lorg/gradle/api/model/ObjectFactory; + public final fun getPlatformType ()Ljava/lang/String; +} + +public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVKLibTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget, dev/adamko/kotlin/binary_compatibility_validator/targets/KLibValidationSpec, org/gradle/api/Named { + public abstract fun getCompilationDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getCurrentPlatform ()Lorg/gradle/api/provider/Property; + public abstract fun getHasKotlinSources ()Lorg/gradle/api/provider/Property; + public abstract fun getInputAbiFile ()Lorg/gradle/api/file/RegularFileProperty; + public abstract fun getKlibFile ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getOutputAbiFile ()Lorg/gradle/api/file/RegularFileProperty; + public abstract fun getSupportedByCurrentHost ()Lorg/gradle/api/provider/Property; + public final fun getTargetName ()Ljava/lang/String; +} + +public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec, java/io/Serializable, org/gradle/api/Named, org/gradle/api/plugins/ExtensionAware { + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredPackages ()Lorg/gradle/api/provider/SetProperty; - public abstract fun getInputClasses ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getInputJar ()Lorg/gradle/api/file/RegularFileProperty; public fun getName ()Ljava/lang/String; - public final fun getPlatformType ()Ljava/lang/String; public abstract fun getPublicClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getPublicMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getPublicPackages ()Lorg/gradle/api/provider/SetProperty; } -public abstract interface class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetSpec : java/io/Serializable { +public abstract interface class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec : java/io/Serializable { + public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; + public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getIgnoredMarkers ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getIgnoredPackages ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getPublicClasses ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getPublicMarkers ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getPublicPackages ()Lorg/gradle/api/provider/SetProperty; +} + +public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetDefaults : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec, java/io/Serializable, org/gradle/api/plugins/ExtensionAware { public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredPackages ()Lorg/gradle/api/provider/SetProperty; - public abstract fun getInputClasses ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getInputJar ()Lorg/gradle/api/file/RegularFileProperty; public abstract fun getPublicClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getPublicMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getPublicPackages ()Lorg/gradle/api/provider/SetProperty; @@ -96,6 +142,12 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract fun getApiDumpFiles ()Lorg/gradle/api/file/ConfigurableFileCollection; } +public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVApiGeneratePreparationTask : dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVDefaultTask { + public final fun action ()V + public abstract fun getApiDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getApiDumpFiles ()Lorg/gradle/api/file/ConfigurableFileCollection; +} + public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVApiGenerateTask : dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVDefaultTask { public final fun generate ()V public abstract fun getInputDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; @@ -109,7 +161,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract fun getBcvEnabled ()Lorg/gradle/api/provider/Property; } -public final class dev/adamko/kotlin/binary_compatibility_validator/workers/BCVSignaturesWorker$Companion { +public final class dev/adamko/kotlin/binary_compatibility_validator/workers/JvmSignaturesWorker$Companion { } public final class org/gradle/kotlin/dsl/BcvGradleDslAccessorsKt { diff --git a/modules/bcv-gradle-plugin/build.gradle.kts b/modules/bcv-gradle-plugin/build.gradle.kts index 88e5e76..3e09063 100644 --- a/modules/bcv-gradle-plugin/build.gradle.kts +++ b/modules/bcv-gradle-plugin/build.gradle.kts @@ -8,8 +8,6 @@ plugins { buildsrc.conventions.`maven-publishing` id("dev.adamko.dev-publish") `java-test-fixtures` - //com.github.johnrengelman.shadow - //buildsrc.conventions.`gradle-plugin-variants` dev.adamko.kotlin.`binary-compatibility-validator` } @@ -17,8 +15,9 @@ dependencies { implementation(libs.javaDiffUtils) compileOnly(libs.kotlinx.bcv) -// compileOnly(libs.kotlin.gradlePlugin) - compileOnly(libs.kotlin.gradlePluginApi) + + compileOnly(libs.kotlin.gradlePlugin) +// compileOnly(libs.kotlin.gradlePluginApi) testFixturesApi(gradleTestKit()) @@ -85,13 +84,6 @@ configurations skipTestFixturesPublications() -// Shadow plugin doesn't seem to help with https://github.com/adamko-dev/kotlin-binary-compatibility-validator-mu/issues/1 -//tasks.shadowJar { -// minimize() -// isEnableRelocation = false -// archiveClassifier.set("") -//} - tasks.withType>().configureEach { compilerOptions { freeCompilerArgs.addAll( @@ -101,7 +93,10 @@ tasks.withType>().configureEach { } binaryCompatibilityValidator { - ignoredMarkers.add("dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi") + ignoredMarkers.addAll( + "dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi", + "dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi", + ) } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVPlugin.kt index 3163de2..dd6bbcc 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVPlugin.kt @@ -29,5 +29,6 @@ constructor() : Plugin { const val API_CHECK_TASK_NAME = "apiCheck" const val API_DUMP_TASK_NAME = "apiDump" const val API_GENERATE_TASK_NAME = "apiGenerate" + const val PREPARE_API_GENERATE_TASK_NAME = "prepareApiGenerate" } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt index bbbe293..5907ab2 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt @@ -1,25 +1,24 @@ package dev.adamko.kotlin.binary_compatibility_validator -import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi -import dev.adamko.kotlin.binary_compatibility_validator.internal.adding -import dev.adamko.kotlin.binary_compatibility_validator.internal.domainObjectContainer +import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget -import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetSpec +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetBaseSpec +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibValidationSpec import javax.inject.Inject -import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.file.DirectoryProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.model.ReplacedBy import org.gradle.api.plugins.ExtensionAware import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.* abstract class BCVProjectExtension @BCVInternalApi @Inject constructor( private val objects: ObjectFactory -) : BCVTargetSpec, ExtensionAware { +) : BCVTargetBaseSpec, ExtensionAware { /** Sets the default [BCVTarget.enabled] value for all [targets]. */ abstract override val enabled: Property @@ -46,17 +45,24 @@ constructor( /** Sets the default [BCVTarget.ignoredClasses] value for all [targets]. */ abstract override val ignoredClasses: SetProperty + @BCVExperimentalApi + val klib: KLibValidationSpec = + extensions.adding("klib") { + objects.newInstance(KLibValidationSpec::class) + } + /** * The directory that contains the API declarations. * - * Defaults to [BCVPlugin.API_DIR]. + * Defaults to `$projectDir/`[BCVPlugin.API_DIR]. */ abstract val outputApiDir: DirectoryProperty abstract val projectName: Property abstract val kotlinxBinaryCompatibilityValidatorVersion: Property + abstract val kotlinCompilerEmbeddableVersion: Property - val targets: NamedDomainObjectContainer = - extensions.adding("targets") { objects.domainObjectContainer() } + val targets: BCVTargetsContainer = + extensions.adding("targets") { objects.bcvTargetsContainer() } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 1da795d..6c114d3 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -7,11 +7,15 @@ import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.API_ import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.EXTENSION_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.RUNTIME_CLASSPATH_CONFIGURATION_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.RUNTIME_CLASSPATH_RESOLVER_CONFIGURATION_NAME +import dev.adamko.kotlin.binary_compatibility_validator.adapters.createJavaTestFixtureTargets +import dev.adamko.kotlin.binary_compatibility_validator.adapters.createKotlinAndroidTargets +import dev.adamko.kotlin.binary_compatibility_validator.adapters.createKotlinJvmTargets +import dev.adamko.kotlin.binary_compatibility_validator.adapters.createKotlinMultiplatformTargets import dev.adamko.kotlin.binary_compatibility_validator.internal.* -import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiCheckTask -import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiDumpTask -import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVApiGenerateTask -import dev.adamko.kotlin.binary_compatibility_validator.tasks.BCVDefaultTask +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion +import dev.adamko.kotlin.binary_compatibility_validator.tasks.* import java.io.File import javax.inject.Inject import org.gradle.api.NamedDomainObjectProvider @@ -20,15 +24,11 @@ import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ProjectLayout import org.gradle.api.logging.Logging +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.SourceSet -import org.gradle.internal.component.external.model.TestFixturesSupport.TEST_FIXTURE_SOURCESET_NAME import org.gradle.kotlin.dsl.* import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin.CHECK_TASK_NAME -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainer abstract class BCVProjectPlugin @@ -37,6 +37,7 @@ abstract class BCVProjectPlugin constructor( private val providers: ProviderFactory, private val layout: ProjectLayout, + private val objects: ObjectFactory, ) : Plugin { override fun apply(project: Project) { @@ -47,46 +48,9 @@ constructor( val bcvGenerateClasspath = createBcvMuClasspath(project, extension) - project.tasks.withType().configureEach { - bcvEnabled.convention(extension.enabled) + configureBcvTaskConventions(project, extension, bcvGenerateClasspath) - onlyIf("BCV is disabled") { bcvEnabled.get() } - } - - project.tasks.withType().configureEach { - runtimeClasspath.from(bcvGenerateClasspath) - targets.addAllLater(providers.provider { extension.targets }) - outputApiBuildDir.convention(layout.buildDirectory.dir("bcv-api")) - projectName.convention(extension.projectName) - - onlyIf("Must have at least one target") { targets.isNotEmpty() } - } - - project.tasks.withType().configureEach { - outputs.dir(temporaryDir) // dummy output, so up-to-date checks work - expectedProjectName.convention(extension.projectName) - expectedApiDirPath.convention( - extension.outputApiDir.map { it.asFile.canonicalFile.invariantSeparatorsPath } - ) - } - - project.tasks.withType().configureEach { - apiDirectory.convention(extension.outputApiDir) - } - - val apiGenerateTask = project.tasks.register(API_GENERATE_TASK_NAME, BCVApiGenerateTask::class) - - project.tasks.register(API_DUMP_TASK_NAME, BCVApiDumpTask::class) { - apiDumpFiles.from(apiGenerateTask.map { it.outputApiBuildDir }) - } - - val apiCheckTask = project.tasks.register(API_CHECK_TASK_NAME, BCVApiCheckTask::class) { - apiBuildDir.convention(apiGenerateTask.flatMap { it.outputApiBuildDir }) - } - - project.tasks.named(CHECK_TASK_NAME).configure { - dependsOn(apiCheckTask) - } + registerBcvTasks(project) createKotlinJvmTargets(project, extension) createKotlinAndroidTargets(project, extension) @@ -94,12 +58,14 @@ constructor( createJavaTestFixtureTargets(project, extension) } + private fun createExtension(project: Project): BCVProjectExtension { val extension = project.extensions.create(EXTENSION_NAME, BCVProjectExtension::class).apply { enabled.convention(true) outputApiDir.convention(layout.projectDirectory.dir(API_DIR)) projectName.convention(providers.provider { project.name }) kotlinxBinaryCompatibilityValidatorVersion.convention(BCVProperties.bcvVersion) + kotlinCompilerEmbeddableVersion.convention("1.9.22") // have to set conventions because otherwise .add("...") doesn't work ignoredMarkers.convention(emptyList()) @@ -111,27 +77,43 @@ constructor( nonPublicMarkers.convention(null) } - extension.targets.configureEach { - enabled.convention(true) + extension.targets.apply { + registerBinding(BCVJvmTarget::class, BCVJvmTarget::class) + registerBinding(BCVKLibTarget::class, BCVKLibTarget::class) - inputClasses.setFrom(emptyList()) - inputJar.convention(null) + withType().configureEach { + enabled.convention(true) + inputClasses.setFrom(emptyList()) + inputJar.convention(null) + } - publicMarkers.convention(extension.publicMarkers) - publicPackages.convention(extension.publicPackages) - publicClasses.convention(extension.publicClasses) + @OptIn(BCVExperimentalApi::class) + withType().configureEach { + enabled.convention(extension.klib.enabled) - ignoredClasses.convention(extension.ignoredClasses) - ignoredMarkers.convention( - @Suppress("DEPRECATION") - extension.ignoredMarkers.orElse(extension.nonPublicMarkers) - ) - ignoredPackages.convention(extension.ignoredPackages) + signatureVersion.convention(KLibSignatureVersion.Latest) + strictValidation.convention(false) + supportedByCurrentHost.convention(false) + } + + configureEach { + publicMarkers.convention(extension.publicMarkers) + publicPackages.convention(extension.publicPackages) + publicClasses.convention(extension.publicClasses) + + ignoredClasses.convention(extension.ignoredClasses) + ignoredMarkers.convention( + @Suppress("DEPRECATION") + extension.ignoredMarkers.orElse(extension.nonPublicMarkers) + ) + ignoredPackages.convention(extension.ignoredPackages) + } } return extension } + private fun createBcvMuClasspath( project: Project, extension: BCVProjectExtension, @@ -149,92 +131,82 @@ constructor( ) } ) + addLater( + extension.kotlinCompilerEmbeddableVersion.map { version -> + project.dependencies.create( + "org.jetbrains.kotlin:kotlin-compiler-embeddable:$version" + ) + } + ) } } return project.configurations.register(RUNTIME_CLASSPATH_RESOLVER_CONFIGURATION_NAME) { description = "Resolve the runtime classpath for running binary-compatibility-validator." resolvable() - isVisible = false extendsFrom(bcvGenerateClasspath.get()) } } - private fun createKotlinJvmTargets( + + private fun configureBcvTaskConventions( project: Project, extension: BCVProjectExtension, + bcvGenerateClasspath: NamedDomainObjectProvider ) { - project.pluginManager.withPlugin("kotlin") { - extension.targets.create("kotlinJvm") { - project - .sourceSets - .matching { it.name == SourceSet.MAIN_SOURCE_SET_NAME } - .all { - inputClasses.from(output.classesDirs) - } - } + project.tasks.withType().configureEach { + bcvEnabled.convention(extension.enabled) + + onlyIf("BCV is disabled") { bcvEnabled.get() } } - } - private fun createKotlinAndroidTargets( - project: Project, - extension: BCVProjectExtension, - ) { - project.pluginManager.withPlugin("kotlin-android") { - val kotlinSourceSetsContainer = project.extensions.getByType() - kotlinSourceSetsContainer.sourceSets.all { - extension.targets.create(name) { - inputClasses.from(kotlin.classesDirectory) - } - } + project.tasks.withType().configureEach { + apiDumpFiles.from(extension.outputApiDir) + apiDirectory.convention(objects.directoryProperty().fileValue(temporaryDir)) } - } - private fun createKotlinMultiplatformTargets( - project: Project, - extension: BCVProjectExtension, - ) { - project.pluginManager.withPlugin("kotlin-multiplatform") { - val kotlinTargetsContainer = project.extensions.getByType() - - kotlinTargetsContainer.targets - .matching { - it.platformType in arrayOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm) - }.all { - val targetPlatformType = platformType - - extension.targets.register(targetName) { - enabled.convention(true) - compilations - .matching { - when (targetPlatformType) { - KotlinPlatformType.jvm -> it.name == "main" - KotlinPlatformType.androidJvm -> it.name == "release" - else -> false - } - }.all { - inputClasses.from(output.classesDirs) - } - } - } + project.tasks.withType().configureEach { + runtimeClasspath.from(bcvGenerateClasspath) + targets.addAllLater(providers.provider { extension.targets }) + outputApiBuildDir.convention(layout.buildDirectory.dir("bcv-api")) + projectName.convention(extension.projectName) + + onlyIf("Must have at least one target") { targets.isNotEmpty() } + } + + project.tasks.withType().configureEach { + outputs.dir(temporaryDir) // dummy output, so up-to-date checks work + expectedProjectName.convention(extension.projectName) + expectedApiDirPath.convention( + extension.outputApiDir.map { it.asFile.canonicalFile.invariantSeparatorsPath } + ) + } + + project.tasks.withType().configureEach { + apiDirectory.convention(extension.outputApiDir) + } + + project.tasks.named(CHECK_TASK_NAME).configure { + dependsOn(project.tasks.withType()) } } - private fun createJavaTestFixtureTargets( - project: Project, - extension: BCVProjectExtension, - ) { - project.pluginManager.withPlugin("java-test-fixtures") { - extension.targets.register(TEST_FIXTURE_SOURCESET_NAME) { - // don't enable by default - requiring an API spec for test-fixtures is pretty unusual - enabled.convention(false) - project - .sourceSets - .matching { it.name == TEST_FIXTURE_SOURCESET_NAME } - .all { - inputClasses.from(output.classesDirs) - } + + private fun registerBcvTasks(project: Project) { +// val prepareApiGenerateTask = +// project.tasks.register(PREPARE_API_GENERATE_TASK_NAME, BCVApiGeneratePreparationTask::class) +// + val apiGenerateTask = + project.tasks.register(API_GENERATE_TASK_NAME, BCVApiGenerateTask::class) { +// extantApiDumpDir.convention(prepareApiGenerateTask.flatMap { it.apiDirectory }) } + + project.tasks.register(API_DUMP_TASK_NAME, BCVApiDumpTask::class) { + apiDumpFiles.from(apiGenerateTask.map { it.outputApiBuildDir }) + } + + project.tasks.register(API_CHECK_TASK_NAME, BCVApiCheckTask::class) { + apiBuildDir.convention(apiGenerateTask.flatMap { it.outputApiBuildDir }) } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVSettingsPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVSettingsPlugin.kt index ed01a2e..fc735a4 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVSettingsPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVSettingsPlugin.kt @@ -1,9 +1,13 @@ package dev.adamko.kotlin.binary_compatibility_validator +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.globToRegex -import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetSpec +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetBaseSpec +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion import javax.inject.Inject +import org.gradle.api.Action import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.initialization.Settings @@ -21,7 +25,7 @@ constructor( override fun apply(settings: Settings) { val extension = settings.extensions.create( "bcvSettings", - objects.newInstance(), + objects.newInstance(), ).apply { ignoredProjects.convention(emptySet()) @@ -30,6 +34,13 @@ constructor( ignoredClasses.convention(emptySet()) ignoredMarkers.convention(emptySet()) ignoredPackages.convention(emptySet()) + + @OptIn(BCVExperimentalApi::class) + klib.apply { + enabled.convention(false) + signatureVersion.convention(KLibSignatureVersion.Latest) + strictValidation.convention(false) + } } } @@ -50,13 +61,16 @@ constructor( } } - abstract class Extension @Inject constructor( + abstract class Extension + @BCVInternalApi + @Inject + constructor( /** - * Set [BCVTargetSpec] values that will be used as defaults for all + * Set [BCVTargetBaseSpec] values that will be used as defaults for all * [BCVProjectExtension.targets] in subprojects. */ - val defaultTargetValues: BCVTargetSpec + val defaultTargetValues: BCVTargetDefaults ) { /** @@ -70,8 +84,7 @@ constructor( */ abstract val ignoredProjects: SetProperty - - fun defaultTargetValues(configure: BCVTargetSpec.() -> Unit) = - defaultTargetValues.configure() + fun defaultTargetValues(configure: Action): Unit = + configure.execute(defaultTargetValues) } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/adapters/AndroidAdapter.kt b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/AndroidAdapter.kt new file mode 100644 index 0000000..9fcd173 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/AndroidAdapter.kt @@ -0,0 +1,22 @@ +package dev.adamko.kotlin.binary_compatibility_validator.adapters + +import dev.adamko.kotlin.binary_compatibility_validator.BCVProjectExtension +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget +import org.gradle.api.Project +import org.gradle.kotlin.dsl.* +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer + + +internal fun createKotlinAndroidTargets( + project: Project, + extension: BCVProjectExtension, +) { + project.pluginManager.withPlugin("kotlin-android") { + val kotlinSourceSetsContainer = project.extensions.getByType() + kotlinSourceSetsContainer.sourceSets.all { + extension.targets.register(name) { + inputClasses.from(kotlin.classesDirectory) + } + } + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/adapters/JavaTestFixtures.kt b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/JavaTestFixtures.kt new file mode 100644 index 0000000..da70cba --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/JavaTestFixtures.kt @@ -0,0 +1,27 @@ +package dev.adamko.kotlin.binary_compatibility_validator.adapters + +import dev.adamko.kotlin.binary_compatibility_validator.BCVProjectExtension +import dev.adamko.kotlin.binary_compatibility_validator.internal.sourceSets +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget +import org.gradle.api.Project +import org.gradle.internal.component.external.model.TestFixturesSupport.TEST_FIXTURE_SOURCESET_NAME +import org.gradle.kotlin.dsl.* + + +internal fun createJavaTestFixtureTargets( + project: Project, + extension: BCVProjectExtension, +) { + project.pluginManager.withPlugin("java-test-fixtures") { + extension.targets.register(TEST_FIXTURE_SOURCESET_NAME) { + // don't enable by default - requiring an API spec for test-fixtures is a little unusual, and might be surprising + enabled.convention(false) + project + .sourceSets + .matching { it.name == TEST_FIXTURE_SOURCESET_NAME } + .all { + inputClasses.from(output.classesDirs) + } + } + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinJvmAdapter.kt b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinJvmAdapter.kt new file mode 100644 index 0000000..cbe14e8 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinJvmAdapter.kt @@ -0,0 +1,25 @@ +package dev.adamko.kotlin.binary_compatibility_validator.adapters + +import dev.adamko.kotlin.binary_compatibility_validator.BCVProjectExtension +import dev.adamko.kotlin.binary_compatibility_validator.internal.sourceSets +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget +import org.gradle.api.Project +import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.* + + +internal fun createKotlinJvmTargets( + project: Project, + extension: BCVProjectExtension, +) { + project.pluginManager.withPlugin("kotlin") { + extension.targets.register("kotlinJvm") { + project + .sourceSets + .matching { it.name == SourceSet.MAIN_SOURCE_SET_NAME } + .all { + inputClasses.from(output.classesDirs) + } + } + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt new file mode 100644 index 0000000..e311bf9 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt @@ -0,0 +1,130 @@ +package dev.adamko.kotlin.binary_compatibility_validator.adapters + +import dev.adamko.kotlin.binary_compatibility_validator.BCVProjectExtension +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget +import java.io.File +import org.gradle.api.Project +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.* +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainer +import org.jetbrains.kotlin.konan.target.HostManager + +private val logger: Logger = Logging.getLogger("dev.adamko.kotlin.binary_compatibility_validator.adapters.KotlinMultiplatformAdapter") + +internal fun createKotlinMultiplatformTargets( + project: Project, + extension: BCVProjectExtension, +) { + project.pluginManager.withPlugin("kotlin-multiplatform") { + val kotlinTargetsContainer = project.extensions.getByType() + + kotlinTargetsContainer.targets + .matching { it.platformType != common } + .configureEach target@{ + when (platformType) { + common, + -> { + // no-op + } + + js, + native, + wasm, + -> registerKotlinKLibCompilation(extension, this@target, project.providers) + + androidJvm, + jvm, + -> registerKotlinJvmCompilations(extension, this@target) + } + } + } +} + + +private fun registerKotlinJvmCompilations( + extension: BCVProjectExtension, + target: KotlinTarget, +) { + val targetPlatformType = target.platformType + + extension.targets.register(target.targetName) { + logger.lifecycle("registering JVM target ${target.targetName}") +// enabled.convention(true) + + target.compilations + .matching { + when (targetPlatformType) { + jvm -> it.name == KotlinCompilation.MAIN_COMPILATION_NAME + androidJvm -> it.name == "release" + else -> false + } + }.all { + inputClasses.from(output.classesDirs) + } + } +} + + +private fun registerKotlinKLibCompilation( + extension: BCVProjectExtension, + target: KotlinTarget, + providers: ProviderFactory, +) { + extension.targets.register(target.targetName) { + logger.lifecycle("registering KLib target ${target.targetName}") +// enabled.convention(false) + + target.compilations + .matching { it.name == KotlinCompilation.MAIN_COMPILATION_NAME } + .all { + klibFile.from(output.classesDirs) + compilationDependencies.from(providers.provider { compileDependencyFiles }) + currentPlatform.convention(HostManager.platformName()) + supportedByCurrentHost.set(target.isSupportedByCurrentHost()) + hasKotlinSources.convention( + providers.provider { + allKotlinSourceSets.any { it.kotlin.srcDirs.any(File::exists) } + } + ) + } + } +} + +private fun KotlinTarget.isSupportedByCurrentHost(): Boolean { + return when (this) { + is org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -> + HostManager().isEnabled(konanTarget) + + else -> true + } +} + +//private fun extractUnderlyingTarget(target: KotlinTarget): String { +// if (target is KotlinNativeTarget) { +// return konanTargetNameMapping[target.konanTarget.name]!! +// } +// return when (target.platformType) { +// KotlinPlatformType.js -> "js" +// KotlinPlatformType.wasm -> when ((target as KotlinJsIrTarget).wasmTargetType) { +// KotlinWasmTargetType.WASI -> "wasmWasi" +// KotlinWasmTargetType.JS -> "wasmJs" +// else -> throw IllegalStateException("Unreachable") +// } +// +// else -> throw IllegalArgumentException("Unsupported platform type: ${target.platformType}") +// } +//} +// +// +//internal fun KotlinCompilation.hasKotlinSources(): Provider = +// project.provider { +// allKotlinSourceSets.any { it.kotlin.srcDirs.any(File::exists) } +// } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/internal/BCVExperimentalApi.kt b/modules/bcv-gradle-plugin/src/main/kotlin/internal/BCVExperimentalApi.kt new file mode 100644 index 0000000..5b334f2 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/internal/BCVExperimentalApi.kt @@ -0,0 +1,19 @@ +package dev.adamko.kotlin.binary_compatibility_validator.internal + +import kotlin.annotation.AnnotationRetention.BINARY +import kotlin.annotation.AnnotationTarget.* + +/** + * Marks an API that is still experimental in BCV and may change in the future. + * There are no guarantees on preserving the behavior of the API until its stabilization. + */ +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Retention(BINARY) +@Target( + CLASS, + FUNCTION, + PROPERTY, + CONSTRUCTOR, +) +@MustBeDocumented +annotation class BCVExperimentalApi diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/internal/GradlePath.kt b/modules/bcv-gradle-plugin/src/main/kotlin/internal/GradlePath.kt index f633c57..3c8fcfd 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/internal/GradlePath.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/internal/GradlePath.kt @@ -6,7 +6,8 @@ internal typealias GradlePath = org.gradle.util.Path internal fun GradlePath(path: String): GradlePath = GradlePath.path(path) -internal val Project.isRootProject get() = this == rootProject +internal val Project.isRootProject: Boolean + get() = this == rootProject internal val Project.fullPath: String get() = when { diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/internal/gradleUtils.kt b/modules/bcv-gradle-plugin/src/main/kotlin/internal/gradleUtils.kt index fe36551..7bea8f8 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/internal/gradleUtils.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/internal/gradleUtils.kt @@ -1,10 +1,13 @@ package dev.adamko.kotlin.binary_compatibility_validator.internal +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget +import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectFactory import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware import org.gradle.api.plugins.ExtensionContainer import org.gradle.kotlin.dsl.* import org.gradle.util.GradleVersion @@ -37,7 +40,7 @@ internal fun Configuration.declarable( ) { isCanBeResolved = false isCanBeConsumed = false - canBeDeclared(true) + canBeDeclared = true isVisible = visible } @@ -52,11 +55,11 @@ internal fun Configuration.declarable( * ``` */ internal fun Configuration.consumable( - visible: Boolean = true, + visible: Boolean = false, ) { isCanBeResolved = false isCanBeConsumed = true - canBeDeclared(false) + canBeDeclared = false isVisible = visible } @@ -75,7 +78,7 @@ internal fun Configuration.resolvable( ) { isCanBeResolved = true isCanBeConsumed = false - canBeDeclared(false) + canBeDeclared = false isVisible = visible } @@ -87,12 +90,13 @@ internal fun Configuration.resolvable( * This function should be removed when the minimal supported Gradle version is 8.2. */ @Suppress("UnstableApiUsage") -private fun Configuration.canBeDeclared(value: Boolean) { - if (CurrentGradleVersion >= "8.2") { - isCanBeDeclared = value +private var Configuration.canBeDeclared: Boolean + get() = isCanBeDeclaredSupported && isCanBeDeclared + set(value) { + if (isCanBeDeclaredSupported) isCanBeDeclared = value } -} +private val isCanBeDeclaredSupported = CurrentGradleVersion >= "8.2" /** * Create a new [NamedDomainObjectContainer], using @@ -112,6 +116,35 @@ internal inline fun ObjectFactory.domainObjectContainer( } +/** + * Create a new [ExtensiblePolymorphicDomainObjectContainer], using + * [org.gradle.kotlin.dsl.polymorphicDomainObjectContainer] + * (but [T] is `reified`). + * + * @see org.gradle.kotlin.dsl.polymorphicDomainObjectContainer + */ +internal inline fun ObjectFactory.polymorphicDomainObjectContainer() + : ExtensiblePolymorphicDomainObjectContainer = + polymorphicDomainObjectContainer(T::class) + + + +/** Create a new [BCVTargetsContainer] instance. */ +internal fun ObjectFactory.bcvTargetsContainer(): BCVTargetsContainer { + val container = polymorphicDomainObjectContainer() + container.whenObjectAdded { + // workaround for https://github.com/gradle/gradle/issues/24972 + (container as ExtensionAware).extensions.add(name, this) + } + return container +} + + +typealias BCVTargetsContainer = + ExtensiblePolymorphicDomainObjectContainer + + + /** * [Add][ExtensionContainer.add] a value (from [valueProvider]) with [name], and return the value. * diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt new file mode 100644 index 0000000..129db08 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt @@ -0,0 +1,46 @@ +package dev.adamko.kotlin.binary_compatibility_validator.targets + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional + +abstract class BCVJvmTarget +@BCVInternalApi +@Inject +constructor( + /** + * The JVM platform being targeted. + * + * Targets with the same [platformType] will be grouped together into a single API declaration. + */ + @get:Internal + val platformType: String, + @get:Internal + val objects: ObjectFactory +) : BCVTarget(platformType), Named { + + @get:Classpath + @get:Optional +// @get:Internal + abstract val inputClasses: ConfigurableFileCollection + + @get:Classpath + @get:Optional +// @get:Internal + abstract val inputJar: RegularFileProperty + +// // create a new property to track the inputs, because Gradle sucks and doesn't allow for +// // @Optional & @Classpath on a property +// @get:Classpath +// protected val inputClasspath: FileCollection +// get() = objects.fileCollection() +// .from(inputClasses) +// .from(inputJar) +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt new file mode 100644 index 0000000..996c8fc --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt @@ -0,0 +1,44 @@ +package dev.adamko.kotlin.binary_compatibility_validator.targets + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import java.io.Serializable +import org.gradle.api.Named +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal + +@OptIn(BCVExperimentalApi::class) +abstract class BCVKLibTarget +@BCVInternalApi +constructor( + @get:Internal + val targetName: String, +) : BCVTarget(targetName), KLibValidationSpec, Named, Serializable { + + @get:Classpath + abstract val klibFile: ConfigurableFileCollection + + @get:Classpath + abstract val compilationDependencies: ConfigurableFileCollection + + @get:Input + abstract val currentPlatform: Property // for up-to-date checks? + + @get:Input + abstract val supportedByCurrentHost: Property + @get:Input + abstract val hasKotlinSources: Property + +// @get:Internal // TODO +// abstract val inputAbiFile: RegularFileProperty +// @get:Internal // TODO +// abstract val outputAbiFile: RegularFileProperty +// abstract val supportedTargets: SetProperty + +// @Internal +// override fun getName(): String = targetName +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt index 639a838..fe18e00 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt @@ -1,38 +1,34 @@ package dev.adamko.kotlin.binary_compatibility_validator.targets +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import java.io.Serializable import javax.inject.Inject import org.gradle.api.Named -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty +import org.gradle.api.plugins.ExtensionAware import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty -import org.gradle.api.tasks.* +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional -abstract class BCVTarget +sealed class BCVTarget +@BCVInternalApi @Inject constructor( - /** - * The JVM platform being targeted. - * - * Targets with the same [platformType] will be grouped together into a single API declaration. - */ - @get:Input - val platformType: String -) : BCVTargetSpec, Serializable, Named { + private val named: String, +) : BCVTargetBaseSpec, Serializable, Named, ExtensionAware { @get:Input @get:Optional abstract override val enabled: Property - @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract override val inputClasses: ConfigurableFileCollection - - @get:InputFile - @get:Optional - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract override val inputJar: RegularFileProperty +// @get:InputFiles +// @get:PathSensitive(RELATIVE) +// abstract override val inputClasses: ConfigurableFileCollection +// +// @get:InputFile +// @get:Optional +// @get:PathSensitive(RELATIVE) +// abstract override val inputJar: RegularFileProperty /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget.publicMarkers */ @get:Input @@ -64,6 +60,6 @@ constructor( @get:Optional abstract override val ignoredClasses: SetProperty - @Internal - override fun getName(): String = platformType + @Input + override fun getName(): String = named } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetSpec.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetBaseSpec.kt similarity index 79% rename from modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetSpec.kt rename to modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetBaseSpec.kt index 1d1fd5e..f90d330 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetSpec.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetBaseSpec.kt @@ -1,30 +1,14 @@ package dev.adamko.kotlin.binary_compatibility_validator.targets import java.io.Serializable -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty -interface BCVTargetSpec : Serializable { +interface BCVTargetBaseSpec : Serializable { /** Enables or disables API generation and validation for this target */ val enabled: Property - /** - * The classes to generate signatures for. - * - * Note that if [inputJar] has a value, the contents of [inputClasses] will be ignored - */ - val inputClasses: ConfigurableFileCollection - - /** - * A JAR that contains the classes to generate signatures for. - * - * Note that if [inputJar] has a value, the contents of [inputClasses] will be ignored - */ - val inputJar: RegularFileProperty - /** * Fully qualified names of annotations that can be used to explicitly mark public declarations. * diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt new file mode 100644 index 0000000..6e8dfbd --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt @@ -0,0 +1,50 @@ +package dev.adamko.kotlin.binary_compatibility_validator.targets + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.adding +import java.io.Serializable +import javax.inject.Inject +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.* + +abstract class BCVTargetDefaults +@BCVInternalApi +@Inject +constructor( + private val objects: ObjectFactory, +) : BCVTargetBaseSpec, Serializable, ExtensionAware { + + abstract override val enabled: Property + +// abstract override val inputClasses: ConfigurableFileCollection + +// abstract override val inputJar: RegularFileProperty + + /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults.publicMarkers */ + abstract override val publicMarkers: SetProperty + + /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults.publicPackages */ + abstract override val publicPackages: SetProperty + + /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults.publicClasses */ + abstract override val publicClasses: SetProperty + + /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults.ignoredMarkers */ + abstract override val ignoredMarkers: SetProperty + + /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults.ignoredPackages */ + abstract override val ignoredPackages: SetProperty + + /** @see dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetDefaults.ignoredClasses */ + abstract override val ignoredClasses: SetProperty + + @BCVExperimentalApi + val klib: KLibValidationSpec = + extensions.adding("klib") { + objects.newInstance(KLibValidationSpec::class) + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibSignatureVersion.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibSignatureVersion.kt new file mode 100644 index 0000000..07c201a --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibSignatureVersion.kt @@ -0,0 +1,36 @@ +package dev.adamko.kotlin.binary_compatibility_validator.targets + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import java.io.Serializable + +@BCVExperimentalApi +sealed interface KLibSignatureVersion { + + val version: Int + + fun isLatest(): Boolean = version == Latest.version + + @BCVExperimentalApi + companion object { + fun of(value: Int): KLibSignatureVersion { + require(value >= 1) { + "Invalid version value, expected positive value: $value" + } + return KLibSignatureVersionImpl(value) + } + + val Latest: KLibSignatureVersion = KLibSignatureVersionImpl(Int.MIN_VALUE) + } +} + + +@OptIn(BCVExperimentalApi::class) +private data class KLibSignatureVersionImpl( + override val version: Int, +) : Serializable, KLibSignatureVersion { + + override fun toString(): String { + val version = if (isLatest()) "Latest" else "$version" + return "KLibSignatureVersion($version)" + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt new file mode 100644 index 0000000..957e879 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt @@ -0,0 +1,47 @@ +package dev.adamko.kotlin.binary_compatibility_validator.targets + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * Settings affecting KLib ABI validation. + */ +@BCVExperimentalApi +interface KLibValidationSpec { + + /** Enables KLib ABI validation checks. */ + @get:Input + @get:Optional + val enabled: Property + + /** Enables KLib ABI validation checks. */ + fun enable(): Unit = enabled.set(true) + + /** + * Specifies which version of signature KLib ABI dump should contain. + * By default, or when explicitly set to null, the latest supported version will be used. + * + * This option covers some advanced scenarios and does not require any configuration by default. + * + * A linker uses signatures to look up symbols, thus signature changes brake binary compatibility and + * should be tracked. Signature format itself is not stabilized yet and may change in the future. In that case, + * a new version of a signature will be introduced. Change of a signature version will be reflected in a dump + * causing a validation failure even if declarations itself remained unchanged. + * However, if a KLib supports multiple signature versions simultaneously, one my explicitly specify the version + * that will be dumped to prevent changes in a dump file. + */ + @get:Input + @get:Optional + val signatureVersion: Property + + /** + * Fail validation if some build targets are not supported by the host compiler. + * By default, ABI dumped only for supported files will be validated. This option makes validation behavior + * stricter and treats having unsupported targets as an error. + */ + @get:Input + @get:Optional + val strictValidation: Property +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index 073ad43..30a0a67 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -47,10 +47,10 @@ constructor( internal abstract val expectedProjectName: Property // Project and tasks paths are used for creating better error messages - private val projectFullPath = project.fullPath - private val apiDumpTaskPath = GradlePath(project.path).child(API_DUMP_TASK_NAME) + private val projectFullPath: String = project.fullPath + private val apiDumpTaskPath: GradlePath = GradlePath(project.path).child(API_DUMP_TASK_NAME) - private val rootDir = project.rootProject.rootDir + private val rootDir: File = project.rootProject.rootDir @TaskAction fun verify() { @@ -69,13 +69,11 @@ constructor( logger.info("[$path] checkApiDeclarationPaths: $checkApiDeclarationPaths") checkApiDeclarationPaths.forEach { checkApiDeclarationPath -> - logger.info("---------------------------") checkTarget( checkApiDeclaration = checkApiDeclarationPath.getFile(projectApiDir), // fetch the builtFile, using the case-insensitive map builtApiDeclaration = builtApiDeclarationPaths[checkApiDeclarationPath]?.getFile(apiBuildDir) ) - logger.info("---------------------------") } } @@ -166,9 +164,7 @@ private class RelativePaths( private val map: TreeMap = caseInsensitiveMap() ) : Set by map.keys { - operator fun plusAssign(path: RelativePath) { - map[path] = path - } + operator fun plusAssign(path: RelativePath): Unit = map.set(path, path) operator fun get(path: RelativePath): RelativePath? = map[path] diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt index 3ef6ce1..e96e281 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt @@ -9,6 +9,7 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.ProjectLayout import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE @CacheableTask abstract class BCVApiDumpTask @@ -20,7 +21,7 @@ constructor( ) : BCVDefaultTask() { @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) + @get:PathSensitive(RELATIVE) abstract val apiDumpFiles: ConfigurableFileCollection @get:OutputDirectory diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt new file mode 100644 index 0000000..a9f8524 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt @@ -0,0 +1,37 @@ +package dev.adamko.kotlin.binary_compatibility_validator.tasks + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import javax.inject.Inject +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "Performs simple file operations, not worth caching") +abstract class BCVApiGeneratePreparationTask +@BCVInternalApi +@Inject +constructor( + private val fs: FileSystemOperations, +) : BCVDefaultTask() { + + @get:InputFiles + @get:PathSensitive(RELATIVE) + abstract val apiDumpFiles: ConfigurableFileCollection + + @get:OutputDirectory + abstract val apiDirectory: DirectoryProperty + + @TaskAction + fun action() { + fs.sync { + from(apiDumpFiles) + into(apiDirectory) + } + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index bb2df11..1811216 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -1,15 +1,21 @@ package dev.adamko.kotlin.binary_compatibility_validator.tasks +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.adding import dev.adamko.kotlin.binary_compatibility_validator.internal.domainObjectContainer +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget -import dev.adamko.kotlin.binary_compatibility_validator.workers.BCVSignaturesWorker -import java.io.* +import dev.adamko.kotlin.binary_compatibility_validator.workers.JvmSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibSignaturesWorker +import java.io.File import javax.inject.Inject -import kotlinx.validation.api.* -import org.gradle.api.* -import org.gradle.api.file.* +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.* @@ -41,6 +47,13 @@ constructor( @get:Input abstract val projectName: Property +// +// /** +// * A directory containing a copy of any currently existing API Dump files. +// * Provided by [BCVApiGeneratePreparationTask]. +// */ +// @get:InputFiles +// abstract val extantApiDumpDir: DirectoryProperty @get:OutputDirectory abstract val outputApiBuildDir: DirectoryProperty @@ -53,26 +66,30 @@ constructor( fs.delete { delete(outputApiBuildDir) } outputApiBuildDir.asFile.mkdirs() - val enabledTargets = targets.asMap.values.filter { it.enabled.getOrElse(true) } + logger.lifecycle("[$path] got ${targets.size} targets : ${targets.joinToString { it.name }}") - enabledTargets.forEach { target -> + val enabledTargets = targets.matching { it.enabled.getOrElse(true) } - val outputDir = if (enabledTargets.size == 1) { - outputApiBuildDir - } else { - outputApiBuildDir.dir(target.platformType) - } + logger.lifecycle("[$path] ${enabledTargets.size} enabledTargets : ${enabledTargets.joinToString { it.name }}") - workQueue.submit( - target = target, - outputDir = outputDir.asFile, - ) - } + generateJvmTargets( + workQueue = workQueue, + jvmTargets = enabledTargets.withType(), + enabledTargets = enabledTargets.size, + outputApiBuildDir = outputApiBuildDir, + ) + + generateKLibTargets( + workQueue = workQueue, + klibTargets = enabledTargets.withType(), + outputApiBuildDir = outputApiBuildDir, + ) // The worker queue is asynchronous, so any code here won't wait for the workers to finish. // Any follow-up work must be done in another task. } + private fun prepareWorkQueue(): WorkQueue { fs.delete { delete(temporaryDir) } temporaryDir.mkdirs() @@ -82,18 +99,44 @@ constructor( } } + //region JVM + + private fun generateJvmTargets( + workQueue: WorkQueue, + outputApiBuildDir: Directory, + jvmTargets: Collection, + enabledTargets: Int, + ) { + if (jvmTargets.isEmpty()) return + logger.lifecycle("[$path] generating ${jvmTargets.size} JVM targets : ${jvmTargets.joinToString { it.name }}") + + jvmTargets.forEach { target -> + val outputDir = if (enabledTargets == 1) { + outputApiBuildDir + } else { + outputApiBuildDir.dir(target.platformType) + } + + workQueue.submit( + target = target, + outputDir = outputDir.asFile, + ) + } + } + private fun WorkQueue.submit( - target: BCVTarget, + target: BCVJvmTarget, outputDir: File, ) { val task = this@BCVApiGenerateTask @OptIn(BCVInternalApi::class) - submit(BCVSignaturesWorker::class) worker@{ + submit(JvmSignaturesWorker::class) worker@{ this@worker.projectName.set(task.projectName) this@worker.taskPath.set(task.path) this@worker.outputApiDir.set(outputDir) + this@worker.inputClasses.from(target.inputClasses) this@worker.inputJar.set(target.inputJar) @@ -106,4 +149,67 @@ constructor( this@worker.ignoredClasses.set(target.ignoredClasses) } } + //endregion + + + //region KLib + + private fun generateKLibTargets( + workQueue: WorkQueue, + outputApiBuildDir: Directory, + klibTargets: Collection, + ) { + if (klibTargets.isEmpty()) return + logger.lifecycle("[$path] generating ${klibTargets.size} KLib targets : ${klibTargets.joinToString { it.name }}") + + val (supportedKLibTargets, unsupportedKLibTargets) = + klibTargets.partition { it.supportedByCurrentHost.get() } + logger.lifecycle("[$path] generating ${supportedKLibTargets.size} supported KLib targets : ${supportedKLibTargets.joinToString { it.name }}") + + supportedKLibTargets.forEach { target -> + workQueue.submit( + target = target, + outputDir = outputApiBuildDir.asFile, + ) + } + + workQueue.await() + logger.lifecycle("[$path] finished generating supported KLib targets.") + + logger.lifecycle("[$path] generating ${unsupportedKLibTargets.size} unsupported KLib targets : ${unsupportedKLibTargets.joinToString { it.name }}") + + supportedKLibTargets.forEach { target -> + workQueue.submit( + target = target, + outputDir = outputApiBuildDir.asFile, + ) + } + } + + @OptIn(BCVExperimentalApi::class) + private fun WorkQueue.submit( + target: BCVKLibTarget, + outputDir: File, + ) { + val task = this@BCVApiGenerateTask + + @OptIn(BCVInternalApi::class) + submit(KLibSignaturesWorker::class) worker@{ + this@worker.targetName.set(target.targetName) + this@worker.taskPath.set(task.path) + + this@worker.outputApiDir.set(outputDir) + + this@worker.klib.set(target.klibFile.singleFile) + this@worker.signatureVersion.set(target.signatureVersion) + this@worker.supportedByCurrentHost.set(target.supportedByCurrentHost) + +// this@worker.targets.addAll(klibTargets) + + this@worker.ignoredPackages.set(target.ignoredPackages) + this@worker.ignoredMarkers.set(target.ignoredMarkers) + this@worker.ignoredClasses.set(target.ignoredClasses) + } + } + //endregion } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/JvmSignaturesWorker.kt similarity index 98% rename from modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt rename to modules/bcv-gradle-plugin/src/main/kotlin/workers/JvmSignaturesWorker.kt index bb6f507..3ea992b 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/BCVSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/JvmSignaturesWorker.kt @@ -14,7 +14,7 @@ import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters @BCVInternalApi -abstract class BCVSignaturesWorker : WorkAction { +abstract class JvmSignaturesWorker : WorkAction { private val logger = Logging.getLogger(this::class.java) @@ -34,6 +34,7 @@ abstract class BCVSignaturesWorker : WorkAction val ignoredClasses: SetProperty val projectName: Property + /** * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, * for log messages diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt new file mode 100644 index 0000000..064e337 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt @@ -0,0 +1,170 @@ +package dev.adamko.kotlin.binary_compatibility_validator.workers + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion +import java.io.Serializable +import kotlinx.validation.ExperimentalBCVApi +import kotlinx.validation.api.klib.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters + +@BCVInternalApi +@BCVExperimentalApi +@OptIn(ExperimentalBCVApi::class) +abstract class KLibSignaturesWorker : WorkAction { + + @BCVInternalApi + interface Parameters : WorkParameters, Serializable { + val klib: RegularFileProperty + val targetName: Property + + val supportedByCurrentHost: Property + + val outputApiDir: DirectoryProperty + + val ignoredPackages: SetProperty + val ignoredMarkers: SetProperty + val ignoredClasses: SetProperty + + val signatureVersion: Property + +// val targets: ListProperty + + /** + * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, + * for log messages + */ + val taskPath: Property + } + + override fun execute() { +// val targets = parameters.targets.get() +// val supportedTargets = targets.filter { it.supportedByCurrentHost.get() } +// +// targets.forEach { target -> + if (parameters.supportedByCurrentHost.get()) { + dumpSupportedTarget() + } else { +// inferDumpForUnsupportedTarget(target, supportedTargets) +// } + } + } + + private fun dumpSupportedTarget() { + val outputFile = parameters.outputApiDir.asFile.get() + .resolve("${parameters.targetName.get()}.klib.api") + + val dump = KlibDump.fromKlib( + klibFile = parameters.klib.get().asFile, + configurableTargetName = parameters.targetName.get(), + filters = KLibDumpFilters { + ignoredClasses += parameters.ignoredClasses.get() + ignoredPackages += parameters.ignoredPackages.get() + nonPublicMarkers += parameters.ignoredMarkers.get() + signatureVersion = parameters.signatureVersion.get().convert() + } + ) + + dump.saveTo(outputFile) + } + + /** + * Filter out targets that are unsupported by the current machine from the golden image + */ + private fun filterSupportedTargets() { + + } + + private fun mergeTargetDumps() { + + } + + + private fun inferDumpForUnsupportedTarget( + target: BCVKLibTarget, +// supportedTargets: List, + ) { + val unsupportedTarget = KlibTarget(target.targetName, target.targetName) +// val supportedTargetNames = +// supportedTargets.map { KlibTarget(target.targetName, target.targetName) }.toSet() + + // Find a set of supported targets that are closer to unsupported target in the hierarchy. + // Note that dumps are stored using configurable name, but grouped by the canonical target name. +// val matchingTargets = findMatchingTargets(supportedTargetNames, unsupportedTarget) + // Load dumps that are a good fit for inference +// val supportedTargetDumps = matchingTargets.map { target -> +// val dumpFile = +// File(outputApiDir).parentFile.resolve(target.configurableName).resolve(dumpFileName) +// KlibDump.from(dumpFile, target.configurableName).also { +// check(it.targets.single() == target) +// } +// } + +// // Load an old dump, if any +// var image: KlibDump? = null +// if (inputImageFile.exists()) { +// if (inputImageFile.length() > 0L) { +// image = KlibDump.from(inputImageFile) +// } else { +// logger.warn( +// "Project's ABI file exists, but empty: $inputImageFile. " + +// "The file will be ignored during ABI dump inference for the unsupported target " + +// unsupportedTarget +// ) +// } +// } + +// inferAbi(unsupportedTarget, supportedTargetDumps, image).saveTo(outputFile) + +// logger.warn( +// "An ABI dump for target $unsupportedTarget was inferred from the ABI generated for the following targets " + +// "as the former target is not supported by the host compiler: " + +// "[${matchingTargets.joinToString(",")}]. " + +// "Inferred dump may not reflect an actual ABI for the target $unsupportedTarget. " + +// "It is recommended to regenerate the dump on the host supporting all required compilation target." +// ) + } + +// private fun findMatchingTargets( +// supportedTargets: Set, +// unsupportedTarget: KlibTarget, +// ): Collection { +// var currentGroup: String? = unsupportedTarget.targetName +// while (currentGroup != null) { +// // If a current group has some supported targets, use them. +// val groupTargets = TargetHierarchy.targets(currentGroup) +// val matchingTargets = supportedTargets.filter { groupTargets.contains(it.targetName) } +// if (matchingTargets.isNotEmpty()) { +// return matchingTargets +// } +// // Otherwise, walk up the target hierarchy. +// currentGroup = TargetHierarchy.parent(currentGroup) +// } +// throw IllegalStateException( +// "The target $unsupportedTarget is not supported by the host compiler " + +// "and there are no targets similar to $unsupportedTarget to infer a dump from it." +// ) +// } + + @BCVInternalApi + companion object { + private val logger: Logger = Logging.getLogger(KLibSignaturesWorker::class.java) + + private fun KLibSignatureVersion.convert(): KlibSignatureVersion = + when { + isLatest() -> KlibSignatureVersion.LATEST + else -> KlibSignatureVersion.of(version) + } + + private fun KlibTarget(configName: String, targetName: String): KlibTarget = + KlibTarget.parse("${configName}.${targetName}") + } +} diff --git a/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt b/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt index 76e10a9..ce2f392 100644 --- a/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt +++ b/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt @@ -58,11 +58,14 @@ private fun haveTask(taskPath: String): Matcher = neverNullMatcher { value -> MatcherResult( value.task(taskPath) != null, - { "BuildResult should have run task $taskPath. All tasks: ${value.tasks.joinToString { it.path }}" }, - { "BuildResult should not have run task $taskPath. All tasks: ${value.tasks.joinToString { it.path }}" }, + { "BuildResult should have run task $taskPath. All tasks: ${value.tasks.toPathAndOutcomeString()}" }, + { "BuildResult should not have run task $taskPath. All tasks: ${value.tasks.toPathAndOutcomeString()}" }, ) } +internal fun Collection.toPathAndOutcomeString(): String = + joinToString { "${it.path} (${it.outcome})" } + infix fun BuildTask?.shouldHaveOutcome(outcome: TaskOutcome) { this should haveOutcome(outcome) } @@ -75,8 +78,8 @@ private fun haveOutcome(outcome: TaskOutcome): Matcher = neverNullMatcher { value -> MatcherResult( value.outcome == outcome, - { "Task ${value.path} should have outcome $outcome" }, - { "Task ${value.path} should not have outcome $outcome" }, + { "Task ${value.path} should have outcome $outcome, but was ${value.outcome}" }, + { "Task ${value.path} should not have outcome $outcome, but was ${value.outcome}" }, ) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7da05be..c18b1bd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,32 +30,30 @@ enableFeaturePreview("STABLE_CONFIGURATION_CACHE") //region git versioning val gitDescribe: Provider = - providers - .exec { - workingDir(rootDir) - commandLine( - "git", - "describe", - "--always", - "--tags", - "--dirty=-DIRTY", - "--broken=-BROKEN", - "--match=v[0-9]*\\.[0-9]*\\.[0-9]*", - ) - isIgnoreExitValue = true - }.standardOutput.asText.map { it.trim() } + providers.exec { + workingDir(rootDir) + commandLine( + "git", + "describe", + "--always", + "--tags", + "--dirty=-DIRTY", + "--broken=-BROKEN", + "--match=v[0-9]*\\.[0-9]*\\.[0-9]*", + ) + isIgnoreExitValue = true + }.standardOutput.asText.map { it.trim() } val currentBranchName: Provider = - providers - .exec { - workingDir(rootDir) - commandLine( - "git", - "branch", - "--show-current", - ) - isIgnoreExitValue = true - }.standardOutput.asText.map { it.trim() } + providers.exec { + workingDir(rootDir) + commandLine( + "git", + "branch", + "--show-current", + ) + isIgnoreExitValue = true + }.standardOutput.asText.map { it.trim() } val currentCommitHash: Provider = providers.exec { From 4d21585c5c3d3f9306a687b82282e3a6cfe37015 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 15:18:14 +0200 Subject: [PATCH 05/37] wip --- .../build.gradle.kts | 8 ++ .../validation/test/KlibVerificationTests.kt | 14 +- .../kotlin/api/BaseKotlinGradleTest.kt | 4 +- .../src/testFixtures/kotlin/api/TestDsl.kt | 2 +- .../api/bcv-gradle-plugin.api | 8 +- .../src/main/kotlin/BCVProjectPlugin.kt | 9 +- .../src/main/kotlin/internal/kotlinTime.kt | 11 ++ .../main/kotlin/tasks/BCVApiGenerateTask.kt | 132 ++++++++++++++---- .../workers/KLibInferSignaturesWorker.kt | 108 ++++++++++++++ .../main/kotlin/workers/KLibMergeWorker.kt | 54 +++++++ .../kotlin/workers/KLibSignaturesWorker.kt | 95 +------------ 11 files changed, 311 insertions(+), 134 deletions(-) create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/internal/kotlinTime.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt diff --git a/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts index 16dd7d7..19788d8 100644 --- a/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts @@ -59,6 +59,14 @@ testing.suites { "devMavenRepoDir", devPublish.devMavenRepo.asFile.get().invariantSeparatorsPath, ) + val tmpDir = layout.buildDirectory.dir("test-temp").get().asFile + systemProperty( + "java.io.tmpdir", + tmpDir.absoluteFile.invariantSeparatorsPath + ) + doFirst { + tmpDir.mkdirs() + } } } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 7da6dc3..be517df 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -2,10 +2,12 @@ package kotlinx.validation.test import dev.adamko.kotlin.binary_compatibility_validator.test.utils.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* +import io.kotest.assertions.withClue import io.kotest.inspectors.shouldForAll import io.kotest.matchers.comparables.shouldBeEqualComparingTo import io.kotest.matchers.file.shouldExist import io.kotest.matchers.file.shouldNotExist +import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import java.io.File import java.nio.file.Files @@ -790,14 +792,16 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { projectName: String = "testproject", dumpTask: String = ":apiDump", ) { - shouldHaveRunTask(dumpTask, SUCCESS) + withClue(output) { + shouldHaveRunTask(dumpTask, SUCCESS) - val generatedDump = rootProjectAbiDump(projectName) + val generatedDump = rootProjectAbiDump(projectName) - generatedDump.shouldExist() + generatedDump.shouldExist() - val expected = readResourceFile(expectedDumpFileName) - generatedDump.readText().invariantNewlines().shouldBeEqualComparingTo(expected) + val expected = readResourceFile(expectedDumpFileName) + generatedDump.readText().invariantNewlines() shouldBe expected + } } companion object { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt index ec3ddf2..70c92f9 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt @@ -6,9 +6,9 @@ import org.junit.jupiter.api.io.TempDir open class BaseKotlinGradleTest { @TempDir(cleanup = ON_SUCCESS) - lateinit var testProjectDir: File + lateinit var testTempDir: File - val rootProjectDir: File get() = testProjectDir + val rootProjectDir: File get() = testTempDir.resolve("bcv-test-project") val rootProjectApiDump: File get() = rootProjectDir.resolve("$API_DIR/${rootProjectDir.name}.api") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt index fcfe3f5..01f64e1 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt @@ -221,7 +221,7 @@ class Runner { var configurationCache: Boolean = true var rerunTasks: Boolean = false var rerunTask: Boolean = false - var buildCache: Boolean = true + var buildCache: Boolean = false var stacktrace: Boolean = true var continues: Boolean = true diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index 3cc23cc..aaf8436 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -21,6 +21,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVProjec public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredPackages ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getKotlinCompilerEmbeddableVersion ()Lorg/gradle/api/provider/Property; public abstract fun getKotlinxBinaryCompatibilityValidatorVersion ()Lorg/gradle/api/provider/Property; public abstract fun getNonPublicMarkers ()Lorg/gradle/api/provider/SetProperty; public abstract fun getOutputApiDir ()Lorg/gradle/api/file/DirectoryProperty; @@ -86,13 +87,11 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/B public final fun getPlatformType ()Ljava/lang/String; } -public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVKLibTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget, dev/adamko/kotlin/binary_compatibility_validator/targets/KLibValidationSpec, org/gradle/api/Named { +public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVKLibTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget, dev/adamko/kotlin/binary_compatibility_validator/targets/KLibValidationSpec, java/io/Serializable, org/gradle/api/Named { public abstract fun getCompilationDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public abstract fun getCurrentPlatform ()Lorg/gradle/api/provider/Property; public abstract fun getHasKotlinSources ()Lorg/gradle/api/provider/Property; - public abstract fun getInputAbiFile ()Lorg/gradle/api/file/RegularFileProperty; public abstract fun getKlibFile ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getOutputAbiFile ()Lorg/gradle/api/file/RegularFileProperty; public abstract fun getSupportedByCurrentHost ()Lorg/gradle/api/provider/Property; public final fun getTargetName ()Ljava/lang/String; } @@ -150,11 +149,14 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVApiGenerateTask : dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVDefaultTask { public final fun generate ()V + public abstract fun getExtantApiDumpDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getInputDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public abstract fun getOutputApiBuildDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getProjectName ()Lorg/gradle/api/provider/Property; public abstract fun getRuntimeClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getSupportedTargetsDir ()Ljava/io/File; public final fun getTargets ()Lorg/gradle/api/NamedDomainObjectContainer; + public final fun getUnsupportedTargetsDir ()Ljava/io/File; } public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVDefaultTask : org/gradle/api/DefaultTask { diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 6c114d3..f86f45f 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -5,6 +5,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.API_ import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.API_DUMP_TASK_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.API_GENERATE_TASK_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.EXTENSION_NAME +import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.PREPARE_API_GENERATE_TASK_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.RUNTIME_CLASSPATH_CONFIGURATION_NAME import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.RUNTIME_CLASSPATH_RESOLVER_CONFIGURATION_NAME import dev.adamko.kotlin.binary_compatibility_validator.adapters.createJavaTestFixtureTargets @@ -193,12 +194,12 @@ constructor( private fun registerBcvTasks(project: Project) { -// val prepareApiGenerateTask = -// project.tasks.register(PREPARE_API_GENERATE_TASK_NAME, BCVApiGeneratePreparationTask::class) -// + val prepareApiGenerateTask = + project.tasks.register(PREPARE_API_GENERATE_TASK_NAME, BCVApiGeneratePreparationTask::class) + val apiGenerateTask = project.tasks.register(API_GENERATE_TASK_NAME, BCVApiGenerateTask::class) { -// extantApiDumpDir.convention(prepareApiGenerateTask.flatMap { it.apiDirectory }) + extantApiDumpDir.convention(prepareApiGenerateTask.flatMap { it.apiDirectory }) } project.tasks.register(API_DUMP_TASK_NAME, BCVApiDumpTask::class) { diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/internal/kotlinTime.kt b/modules/bcv-gradle-plugin/src/main/kotlin/internal/kotlinTime.kt new file mode 100644 index 0000000..649540c --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/internal/kotlinTime.kt @@ -0,0 +1,11 @@ +package dev.adamko.kotlin.binary_compatibility_validator.internal + +import kotlin.time.Duration +import kotlin.time.Duration.Companion.nanoseconds + +// can't use kotlin.time.measureTime {} because Gradle forces the language level to be low. +internal fun measureTime(block: () -> Unit): Duration = + System.nanoTime().let { startTime -> + block() + (System.nanoTime() - startTime).nanoseconds + } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 1811216..7886789 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -1,13 +1,12 @@ package dev.adamko.kotlin.binary_compatibility_validator.tasks -import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi -import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi -import dev.adamko.kotlin.binary_compatibility_validator.internal.adding -import dev.adamko.kotlin.binary_compatibility_validator.internal.domainObjectContainer +import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget import dev.adamko.kotlin.binary_compatibility_validator.workers.JvmSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibInferSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibMergeWorker import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibSignaturesWorker import java.io.File import javax.inject.Inject @@ -19,6 +18,7 @@ import org.gradle.api.file.FileSystemOperations import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE import org.gradle.kotlin.dsl.* import org.gradle.workers.WorkQueue import org.gradle.workers.WorkerExecutor @@ -47,17 +47,25 @@ constructor( @get:Input abstract val projectName: Property -// -// /** -// * A directory containing a copy of any currently existing API Dump files. -// * Provided by [BCVApiGeneratePreparationTask]. -// */ -// @get:InputFiles -// abstract val extantApiDumpDir: DirectoryProperty + + /** + * A directory containing a copy of any currently existing API Dump files. + * Provided by [BCVApiGeneratePreparationTask]. + */ + @get:InputDirectory + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val extantApiDumpDir: DirectoryProperty @get:OutputDirectory abstract val outputApiBuildDir: DirectoryProperty + @get:LocalState + val supportedTargetsDir: File get() = temporaryDir.resolve("klib-supported") + + @get:LocalState + val unsupportedTargetsDir: File get() = temporaryDir.resolve("klib-unsupported") + @TaskAction fun generate() { val workQueue = prepareWorkQueue() @@ -66,6 +74,12 @@ constructor( fs.delete { delete(outputApiBuildDir) } outputApiBuildDir.asFile.mkdirs() + fs.delete { delete(supportedTargetsDir) } + supportedTargetsDir.mkdirs() + + fs.delete { delete(unsupportedTargetsDir) } + unsupportedTargetsDir.mkdirs() + logger.lifecycle("[$path] got ${targets.size} targets : ${targets.joinToString { it.name }}") val enabledTargets = targets.matching { it.enabled.getOrElse(true) } @@ -166,24 +180,52 @@ constructor( klibTargets.partition { it.supportedByCurrentHost.get() } logger.lifecycle("[$path] generating ${supportedKLibTargets.size} supported KLib targets : ${supportedKLibTargets.joinToString { it.name }}") - supportedKLibTargets.forEach { target -> - workQueue.submit( - target = target, - outputDir = outputApiBuildDir.asFile, - ) + val supportedKLibGenDuration = measureTime { + supportedKLibTargets.forEach { target -> + workQueue.submit( + target = target, + outputDir = supportedTargetsDir, + ) + } + workQueue.await() } - - workQueue.await() - logger.lifecycle("[$path] finished generating supported KLib targets.") + logger.lifecycle("[$path] finished generating supported KLib targets in $supportedKLibGenDuration") logger.lifecycle("[$path] generating ${unsupportedKLibTargets.size} unsupported KLib targets : ${unsupportedKLibTargets.joinToString { it.name }}") - supportedKLibTargets.forEach { target -> - workQueue.submit( - target = target, - outputDir = outputApiBuildDir.asFile, - ) + val unsupportedKLibGenDuration = measureTime { + unsupportedKLibTargets.forEach { target -> + workQueue.inferKLib( + target = target, + supportedTargetDumpFiles = supportedTargetsDir.walk().filter { it.isFile }.toSet(), + extantApiDumpFile = extantApiDumpDir.asFile.orNull?.walk()?.filter { it.isFile } + ?.firstOrNull(), + outputDir = unsupportedTargetsDir, + ) + } } + logger.lifecycle("[$path] finished generating unsupported KLib targets in $unsupportedKLibGenDuration") + + workQueue.await() + + val allTargetDumpFiles = + supportedTargetsDir.walk().filter { it.isFile }.toSet() union + unsupportedTargetsDir.walk().filter { it.isFile }.toSet() + + logger.lifecycle("[$path] merging ${allTargetDumpFiles.size} dump files : ${allTargetDumpFiles.joinToString { it.name }}") + + workQueue.merge( + projectName.get(), + targetDumpFiles = allTargetDumpFiles, + outputDir = outputApiBuildDir.asFile, + ) + workQueue.await() + + logger.lifecycle( + "[$path] merged ${allTargetDumpFiles.size} dump files : ${ + outputApiBuildDir.asFile.walk().filter { it.isFile }.toList() + }" + ) } @OptIn(BCVExperimentalApi::class) @@ -202,7 +244,7 @@ constructor( this@worker.klib.set(target.klibFile.singleFile) this@worker.signatureVersion.set(target.signatureVersion) - this@worker.supportedByCurrentHost.set(target.supportedByCurrentHost) +// this@worker.supportedByCurrentHost.set(target.supportedByCurrentHost) // this@worker.targets.addAll(klibTargets) @@ -211,5 +253,45 @@ constructor( this@worker.ignoredClasses.set(target.ignoredClasses) } } + + @OptIn(BCVExperimentalApi::class) + private fun WorkQueue.inferKLib( + target: BCVKLibTarget, + supportedTargetDumpFiles: Set, + extantApiDumpFile: File?, + outputDir: File, + ) { + val task = this@BCVApiGenerateTask + + @OptIn(BCVInternalApi::class) + submit(KLibInferSignaturesWorker::class) worker@{ + this@worker.targetName.set(target.name) + this@worker.taskPath.set(task.path) + + this@worker.outputApiDir.set(outputDir) + + this@worker.supportedTargetDumpFiles.from(supportedTargetDumpFiles) + this@worker.extantApiDumpFile.set(extantApiDumpFile) + } + } + + @OptIn(BCVExperimentalApi::class) + private fun WorkQueue.merge( + projectName: String, + targetDumpFiles: Set, + outputDir: File, + ) { + val task = this@BCVApiGenerateTask + + @OptIn(BCVInternalApi::class) + submit(KLibMergeWorker::class) worker@{ + this@worker.projectName.set(projectName) + this@worker.taskPath.set(task.path) + + this@worker.outputApiDir.set(outputDir) + + this@worker.targetDumpFiles.from(targetDumpFiles) + } + } //endregion } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt new file mode 100644 index 0000000..ebc5af1 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt @@ -0,0 +1,108 @@ +package dev.adamko.kotlin.binary_compatibility_validator.workers + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import java.io.Serializable +import kotlinx.validation.ExperimentalBCVApi +import kotlinx.validation.api.klib.KlibDump +import kotlinx.validation.api.klib.KlibTarget +import kotlinx.validation.api.klib.inferAbi +import kotlinx.validation.api.klib.saveTo +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Property +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters + +@BCVInternalApi +@BCVExperimentalApi +@OptIn(ExperimentalBCVApi::class) +abstract class KLibInferSignaturesWorker : WorkAction { + + @BCVInternalApi + interface Parameters : WorkParameters, Serializable { + val targetName: Property + + val outputApiDir: DirectoryProperty + + val supportedTargetDumpFiles: ConfigurableFileCollection + val extantApiDumpFile: RegularFileProperty + + /** + * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, + * for log messages + */ + val taskPath: Property + } + + override fun execute() { + // Find a set of supported targets that are closer to unsupported target in the hierarchy. + // Note that dumps are stored using configurable name, but grouped by the canonical target name. +// val matchingTargets = findMatchingTargets(availableDumps.keys, target.get()) + // Load dumps that are a good fit for inference + val supportedTargetDumps = + parameters.supportedTargetDumpFiles.asFileTree.map { dumpFile -> + KlibDump.from(dumpFile, dumpFile.name.substringBefore(".klib.api")) +// .also { +// check(it.targets.single() == target) +// } + } + + val extantApiDumpFile = parameters.extantApiDumpFile.orNull?.asFile + + // Load an old dump, if any + val extantImage: KlibDump? = + extantApiDumpFile?.let { extantApiDump -> + KlibDump.from(extantApiDump) + } + if (extantImage == null) { + logger.warn( + "Project's ABI file exists, but empty: ${extantApiDumpFile}. " + + "The file will be ignored during ABI dump inference for the unsupported target " +// + target.get() + ) + } + + val target = KlibTarget.parse(parameters.targetName.get()) + inferAbi(target, supportedTargetDumps, extantImage) + .saveTo(parameters.outputApiDir.get().asFile.resolve(parameters.targetName.get())) + +// logger.warn( +// "An ABI dump for target ${target.get()} was inferred from the ABI generated for the following targets " + +// "as the former target is not supported by the host compiler: " + +// "[${matchingTargets.joinToString(",")}]. " + +// "Inferred dump may not reflect an actual ABI for the target ${target.get()}. " + +// "It is recommended to regenerate the dump on the host supporting all required compilation target." +// ) + } + + +// private fun findMatchingTargets( +// supportedTargets: Set, +// unsupportedTarget: KlibTarget +// ): Collection { +// var currentGroup: String? = unsupportedTarget.targetName +// while (currentGroup != null) { +// // If a current group has some supported targets, use them. +// val groupTargets = TargetHierarchy.targets(currentGroup) +// val matchingTargets = supportedTargets.filter { groupTargets.contains(it.targetName) } +// if (matchingTargets.isNotEmpty()) { +// return matchingTargets +// } +// // Otherwise, walk up the target hierarchy. +// currentGroup = TargetHierarchy.parent(currentGroup) +// } +// throw IllegalStateException( +// "The target $unsupportedTarget is not supported by the host compiler " + +// "and there are no targets similar to $unsupportedTarget to infer a dump from it." +// ) +// } + + @BCVInternalApi + companion object { + private val logger: Logger = Logging.getLogger(KLibInferSignaturesWorker::class.java) + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt new file mode 100644 index 0000000..d47a335 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt @@ -0,0 +1,54 @@ +package dev.adamko.kotlin.binary_compatibility_validator.workers + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import java.io.Serializable +import kotlinx.validation.ExperimentalBCVApi +import kotlinx.validation.api.klib.KlibDump +import kotlinx.validation.api.klib.saveTo +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Property +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters + +@BCVInternalApi +@BCVExperimentalApi +@OptIn(ExperimentalBCVApi::class) +abstract class KLibMergeWorker : WorkAction { + + @BCVInternalApi + interface Parameters : WorkParameters, Serializable { + val projectName: Property + + val outputApiDir: DirectoryProperty + + val targetDumpFiles: ConfigurableFileCollection + + /** + * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, + * for log messages + */ + val taskPath: Property + } + + override fun execute() { + val sourceFiles = parameters.targetDumpFiles.asFileTree + val outputFile = parameters.outputApiDir.get().asFile.resolve(parameters.projectName.get() + ".klib.api") + + val dump = KlibDump() + + sourceFiles.forEach { dumpFile -> + dump.merge(dumpFile, dumpFile.name.substringBefore(".klib.api")) + } + + dump.saveTo(outputFile) + } + + @BCVInternalApi + companion object { + private val logger: Logger = Logging.getLogger(KLibMergeWorker::class.java) + } +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt index 064e337..449602d 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt @@ -2,7 +2,6 @@ package dev.adamko.kotlin.binary_compatibility_validator.workers import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi -import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion import java.io.Serializable import kotlinx.validation.ExperimentalBCVApi @@ -26,8 +25,6 @@ abstract class KLibSignaturesWorker : WorkAction - val supportedByCurrentHost: Property - val outputApiDir: DirectoryProperty val ignoredPackages: SetProperty @@ -46,19 +43,6 @@ abstract class KLibSignaturesWorker : WorkAction - if (parameters.supportedByCurrentHost.get()) { - dumpSupportedTarget() - } else { -// inferDumpForUnsupportedTarget(target, supportedTargets) -// } - } - } - - private fun dumpSupportedTarget() { val outputFile = parameters.outputApiDir.asFile.get() .resolve("${parameters.targetName.get()}.klib.api") @@ -70,90 +54,13 @@ abstract class KLibSignaturesWorker : WorkAction, - ) { - val unsupportedTarget = KlibTarget(target.targetName, target.targetName) -// val supportedTargetNames = -// supportedTargets.map { KlibTarget(target.targetName, target.targetName) }.toSet() - - // Find a set of supported targets that are closer to unsupported target in the hierarchy. - // Note that dumps are stored using configurable name, but grouped by the canonical target name. -// val matchingTargets = findMatchingTargets(supportedTargetNames, unsupportedTarget) - // Load dumps that are a good fit for inference -// val supportedTargetDumps = matchingTargets.map { target -> -// val dumpFile = -// File(outputApiDir).parentFile.resolve(target.configurableName).resolve(dumpFileName) -// KlibDump.from(dumpFile, target.configurableName).also { -// check(it.targets.single() == target) -// } -// } - -// // Load an old dump, if any -// var image: KlibDump? = null -// if (inputImageFile.exists()) { -// if (inputImageFile.length() > 0L) { -// image = KlibDump.from(inputImageFile) -// } else { -// logger.warn( -// "Project's ABI file exists, but empty: $inputImageFile. " + -// "The file will be ignored during ABI dump inference for the unsupported target " + -// unsupportedTarget -// ) -// } -// } - -// inferAbi(unsupportedTarget, supportedTargetDumps, image).saveTo(outputFile) - -// logger.warn( -// "An ABI dump for target $unsupportedTarget was inferred from the ABI generated for the following targets " + -// "as the former target is not supported by the host compiler: " + -// "[${matchingTargets.joinToString(",")}]. " + -// "Inferred dump may not reflect an actual ABI for the target $unsupportedTarget. " + -// "It is recommended to regenerate the dump on the host supporting all required compilation target." -// ) - } - -// private fun findMatchingTargets( -// supportedTargets: Set, -// unsupportedTarget: KlibTarget, -// ): Collection { -// var currentGroup: String? = unsupportedTarget.targetName -// while (currentGroup != null) { -// // If a current group has some supported targets, use them. -// val groupTargets = TargetHierarchy.targets(currentGroup) -// val matchingTargets = supportedTargets.filter { groupTargets.contains(it.targetName) } -// if (matchingTargets.isNotEmpty()) { -// return matchingTargets -// } -// // Otherwise, walk up the target hierarchy. -// currentGroup = TargetHierarchy.parent(currentGroup) -// } -// throw IllegalStateException( -// "The target $unsupportedTarget is not supported by the host compiler " + -// "and there are no targets similar to $unsupportedTarget to infer a dump from it." -// ) -// } - @BCVInternalApi companion object { private val logger: Logger = Logging.getLogger(KLibSignaturesWorker::class.java) From 9b395b776f5ac7374a15264e3f22d31c2e4c60ea Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:05:01 +0200 Subject: [PATCH 06/37] add assign util fn for `Property` --- .../api/bcv-gradle-plugin.api | 4 ++++ .../src/main/kotlin/dsl/assign.kt | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/dsl/assign.kt diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index aaf8436..15cc2ac 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -166,6 +166,10 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public final class dev/adamko/kotlin/binary_compatibility_validator/workers/JvmSignaturesWorker$Companion { } +public final class org/gradle/kotlin/dsl/AssignKt { + public static final fun assign (Lorg/gradle/api/provider/Property;I)V +} + public final class org/gradle/kotlin/dsl/BcvGradleDslAccessorsKt { public static final fun binaryCompatibilityValidator (Lorg/gradle/api/initialization/Settings;Lkotlin/jvm/functions/Function1;)V public static final fun getBinaryCompatibilityValidator (Lorg/gradle/api/initialization/Settings;)Ldev/adamko/kotlin/binary_compatibility_validator/BCVSettingsPlugin$Extension; diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/dsl/assign.kt b/modules/bcv-gradle-plugin/src/main/kotlin/dsl/assign.kt new file mode 100644 index 0000000..fdc9a72 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/dsl/assign.kt @@ -0,0 +1,18 @@ +@file:Suppress("PackageDirectoryMismatch") + +package org.gradle.kotlin.dsl + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion +import org.gradle.api.provider.Property + + +@OptIn(BCVExperimentalApi::class) +// TODO test Property.assign +fun Property.assign(version: Int) { + this.set(KLibSignatureVersion.of(version)) +} +// +//@OptIn(BCVExperimentalApi::class) +//fun KLibSignatureVersion(version: Int): KLibSignatureVersion = +// KLibSignatureVersion.of(version) From d461fb7a16899fde3a67b3c280cf599bf3b37bf6 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:05:17 +0200 Subject: [PATCH 07/37] add util for assigning klib.signatureVersion --- .../src/main/kotlin/targets/KLibValidationSpec.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt index 957e879..b025dba 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt @@ -36,6 +36,13 @@ interface KLibValidationSpec { @get:Optional val signatureVersion: Property + /** + * Sets the value of [signatureVersion]. + * @see signatureVersion + */ + fun signatureVersion(version: Int): Unit = + signatureVersion.set(KLibSignatureVersion.of(version)) + /** * Fail validation if some build targets are not supported by the host compiler. * By default, ABI dumped only for supported files will be validated. This option makes validation behavior From c7dfe2db874b83ecd36adba3945f9804757c0cdf Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:05:32 +0200 Subject: [PATCH 08/37] fix signatureVersion convention for targets --- modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index f86f45f..6f48779 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -15,7 +15,6 @@ import dev.adamko.kotlin.binary_compatibility_validator.adapters.createKotlinMul import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget -import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion import dev.adamko.kotlin.binary_compatibility_validator.tasks.* import java.io.File import javax.inject.Inject @@ -92,7 +91,7 @@ constructor( withType().configureEach { enabled.convention(extension.klib.enabled) - signatureVersion.convention(KLibSignatureVersion.Latest) + signatureVersion.convention(extension.klib.signatureVersion) strictValidation.convention(false) supportedByCurrentHost.convention(false) } From 9393e69dd060c91704c7eadc320b209b6e8db228 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:05:49 +0200 Subject: [PATCH 09/37] update test util message when no tasks are found --- .../src/testFixtures/kotlin/kotestGradleAssertions.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt b/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt index ce2f392..3d13342 100644 --- a/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt +++ b/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt @@ -55,16 +55,17 @@ infix fun BuildResult?.shouldNotHaveRunTask(taskPath: String) { } private fun haveTask(taskPath: String): Matcher = - neverNullMatcher { value -> + neverNullMatcher { result -> MatcherResult( - value.task(taskPath) != null, - { "BuildResult should have run task $taskPath. All tasks: ${value.tasks.toPathAndOutcomeString()}" }, - { "BuildResult should not have run task $taskPath. All tasks: ${value.tasks.toPathAndOutcomeString()}" }, + result.task(taskPath) != null, + { "BuildResult should have run task $taskPath. All tasks: ${result.tasks.toPathAndOutcomeString()}" }, + { "BuildResult should not have run task $taskPath. All tasks: ${result.tasks.toPathAndOutcomeString()}" }, ) } internal fun Collection.toPathAndOutcomeString(): String = joinToString { "${it.path} (${it.outcome})" } + .ifEmpty { "" } infix fun BuildTask?.shouldHaveOutcome(outcome: TaskOutcome) { this should haveOutcome(outcome) From f076934fa77874208bb1b3825a63e9da97edd940 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:06:07 +0200 Subject: [PATCH 10/37] fixing integration tests... --- .../validation/test/KlibVerificationTests.kt | 19 ++++++++----------- .../gradle/base/androidProjectRoot.gradle.kts | 2 +- .../gradle/base/jdkCompatibility.gradle.kts | 2 +- ...dkCompatibilityWithExactVersion.gradle.kts | 2 +- .../multiplatformWithJvmTargets.gradle.kts | 2 +- ...ultiplatformWithSingleJvmTarget.gradle.kts | 2 +- .../gradle/base/withNativePlugin.gradle.kts | 2 +- .../withNativePluginAndNoTargets.gradle.kts | 2 +- ...withNativePluginAndSingleTarget.gradle.kts | 2 +- .../gradle/base/withPlugin.gradle.kts | 2 +- .../gradle/base/withoutPlugin.gradle.kts | 2 +- .../ignoreSubclasses/ignore.gradle.kts | 11 +++-------- .../oneValidFullyQualifiedClass.gradle.kts | 2 +- .../oneValidPackage.gradle.kts | 2 +- .../nonPublicMarkers/klib.gradle.kts | 19 +++++++------------ .../nonPublicMarkers/markers.gradle.kts | 2 +- .../nonPublicMarkers/packages.gradle.kts | 4 ++-- .../outputDirectory/different.gradle.kts | 4 ++-- .../outputDirectory/outer.gradle.kts | 4 ++-- .../outputDirectory/subdirectory.gradle.kts | 4 ++-- .../publicMarkers/markers.gradle.kts | 2 +- .../publicMarkers/mixedMarkers.gradle.kts | 2 +- .../publicMarkers/packages.gradle.kts | 4 ++-- .../signatures/invalid.gradle.kts | 13 ++++--------- .../configuration/signatures/v1.gradle.kts | 9 ++------- .../unsupported/enforce.gradle.kts | 9 ++------- 26 files changed, 51 insertions(+), 79 deletions(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index be517df..78f04be 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -28,7 +28,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") } gradleProperties { - addLine("kotlin.mpp.stability.nowarn=true\n") + addLine("kotlin.mpp.stability.nowarn=true") } buildGradleKts { resolve("/examples/gradle/base/withNativePlugin.gradle.kts") @@ -371,7 +371,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { - shouldHaveTaskWithOutcome(":klibApiExtractForValidation", FAILED) + shouldHaveTaskWithOutcome(":apiCheck", FAILED) } } @@ -383,13 +383,13 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") runner { arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } runner.build { checkKLibDump( "/examples/classes/TopLevelDeclarations.klib.with.linux.dump", - dumpTask = ":klibApiDump" + dumpTask = ":apiDump" ) } } @@ -500,15 +500,12 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { resolve("/examples/classes/AnotherBuildConfigLinuxArm64.kt") } runner { - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } runner.build { - checkKLibDump( - "/examples/classes/AnotherBuildConfig.klib.clash.dump", - dumpTask = ":klibApiDump" - ) + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.clash.dump") } } @@ -546,13 +543,13 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { resolve("/examples/classes/AnotherBuildConfigLinuxArm64.kt") } runner { - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } runner.build { checkKLibDump( "/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump", - dumpTask = ":klibApiDump" + dumpTask = ":apiDump" ) } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/androidProjectRoot.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/androidProjectRoot.gradle.kts index dca231e..85851e5 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/androidProjectRoot.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/androidProjectRoot.gradle.kts @@ -1,7 +1,7 @@ plugins { id("com.android.application").version("7.2.2").apply(false) id("com.android.library").version("7.2.2").apply(false) - id("org.jetbrains.kotlin.android").version("1.7.10").apply(false) + id("org.jetbrains.kotlin.android").version("1.9.24").apply(false) id("dev.adamko.kotlin.binary-compatibility-validator") version "+" apply false } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts index a9921d7..b672459 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibility.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") version "1.9.22" + kotlin("jvm") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts index b2b2cd1..a231434 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/jdkCompatibilityWithExactVersion.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") version "1.9.22" + kotlin("jvm") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts index 1d85c46..b60aa54 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithJvmTargets.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version embeddedKotlinVersion + kotlin("multiplatform") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts index b7471e6..7598d33 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/multiplatformWithSingleJvmTarget.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version embeddedKotlinVersion + kotlin("multiplatform") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts index 0c52b6a..6ec54c5 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePlugin.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version embeddedKotlinVersion + kotlin("multiplatform") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts index e9012d5..b236ce6 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndNoTargets.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version embeddedKotlinVersion + kotlin("multiplatform") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts index 14103c8..c1a77ab 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withNativePluginAndSingleTarget.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version embeddedKotlinVersion + kotlin("multiplatform") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts index b81ec3c..e3d7618 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withPlugin.gradle.kts @@ -1,4 +1,4 @@ plugins { - kotlin("jvm") version embeddedKotlinVersion + kotlin("jvm") version "1.9.24" id("dev.adamko.kotlin.binary-compatibility-validator") version "+" } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withoutPlugin.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withoutPlugin.gradle.kts index 1ac35f3..599617a 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withoutPlugin.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/base/withoutPlugin.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") version "1.3.70" + kotlin("jvm") version "1.9.24" } dependencies { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts index 776e96f..508e5b9 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoreSubclasses/ignore.gradle.kts @@ -1,9 +1,4 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -configure { - // ignoredClasses.add("subclasses.A.B") - ignoredClasses.add("subclasses.A\$B") +binaryCompatibilityValidator { + // ignoredClasses.add("subclasses.A.B") + ignoredClasses.add("subclasses.A\$B") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts index abda226..72d4c12 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredClasses/oneValidFullyQualifiedClass.gradle.kts @@ -1,3 +1,3 @@ -configure { +binaryCompatibilityValidator { ignoredClasses.add("com.company.BuildConfig") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredPackages/oneValidPackage.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredPackages/oneValidPackage.gradle.kts index 6292368..eae0cd1 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredPackages/oneValidPackage.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/ignoredPackages/oneValidPackage.gradle.kts @@ -1,3 +1,3 @@ -configure { +binaryCompatibilityValidator { ignoredPackages.add("com.company") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts index e853381..8db64dd 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/klib.gradle.kts @@ -1,13 +1,8 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -configure { - nonPublicMarkers.add("annotations.HiddenClass") - nonPublicMarkers.add("annotations.HiddenCtor") - nonPublicMarkers.add("annotations.HiddenProperty") - nonPublicMarkers.add("annotations.HiddenGetter") - nonPublicMarkers.add("annotations.HiddenSetter") - nonPublicMarkers.add("annotations.HiddenFunction") +binaryCompatibilityValidator { + nonPublicMarkers.add("annotations.HiddenClass") + nonPublicMarkers.add("annotations.HiddenCtor") + nonPublicMarkers.add("annotations.HiddenProperty") + nonPublicMarkers.add("annotations.HiddenGetter") + nonPublicMarkers.add("annotations.HiddenSetter") + nonPublicMarkers.add("annotations.HiddenFunction") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/markers.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/markers.gradle.kts index 77e468f..2368b7f 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/markers.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/markers.gradle.kts @@ -1,4 +1,4 @@ -configure { +binaryCompatibilityValidator { ignoredMarkers.add("foo.HiddenField") ignoredMarkers.add("foo.HiddenProperty") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts index b50120b..10461af 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/nonPublicMarkers/packages.gradle.kts @@ -1,3 +1,3 @@ -configure { - ignoredMarkers.add("annotated.PackageAnnotation") +binaryCompatibilityValidator { + ignoredMarkers.add("annotated.PackageAnnotation") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts index 11b5528..169a7ce 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/different.gradle.kts @@ -1,3 +1,3 @@ -configure { - outputApiDir.set(layout.projectDirectory.dir("custom")) +binaryCompatibilityValidator { + outputApiDir.set(layout.projectDirectory.dir("custom")) } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts index 20d781c..fdc31df 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/outer.gradle.kts @@ -1,3 +1,3 @@ -configure { - outputApiDir.set(layout.projectDirectory.dir("../api")) +binaryCompatibilityValidator { + outputApiDir.set(layout.projectDirectory.dir("../api")) } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts index d408e15..fd2d71a 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/outputDirectory/subdirectory.gradle.kts @@ -1,3 +1,3 @@ -configure { - outputApiDir.set(layout.projectDirectory.dir("validation/api")) +binaryCompatibilityValidator { + outputApiDir.set(layout.projectDirectory.dir("validation/api")) } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/markers.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/markers.gradle.kts index 58c67f2..d2ecd7a 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/markers.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/markers.gradle.kts @@ -1,4 +1,4 @@ -configure { +binaryCompatibilityValidator { publicMarkers.add("foo.PublicClass") publicMarkers.add("foo.PublicField") publicMarkers.add("foo.PublicProperty") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/mixedMarkers.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/mixedMarkers.gradle.kts index 61e6534..bd93777 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/mixedMarkers.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/mixedMarkers.gradle.kts @@ -1,4 +1,4 @@ -configure { +binaryCompatibilityValidator { ignoredMarkers.add("mixed.PrivateApi") publicMarkers.add("mixed.PublicApi") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts index b9edfa6..8142280 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/publicMarkers/packages.gradle.kts @@ -1,3 +1,3 @@ -configure { - publicMarkers.add("annotated.PackageAnnotation") +binaryCompatibilityValidator { + publicMarkers.add("annotated.PackageAnnotation") } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts index ddb2f31..1024b7d 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/invalid.gradle.kts @@ -1,10 +1,5 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -configure { - klib { - signatureVersion = kotlinx.validation.api.klib.KlibSignatureVersion.of(100500) - } +binaryCompatibilityValidator { + klib { + signatureVersion(100500) + } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts index 28f0322..e8c381a 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/signatures/v1.gradle.kts @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -configure { - klib.signatureVersion = kotlinx.validation.api.klib.KlibSignatureVersion.of(1) +binaryCompatibilityValidator { + klib.signatureVersion(1) } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts index 99270cd..38834ca 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/gradle/configuration/unsupported/enforce.gradle.kts @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -configure { - klib.strictValidation = true +binaryCompatibilityValidator { + klib.strictValidation.set(true) } From 50d9c5695c89243769ddbd9a611125fb4dc70e00 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:41:07 +0200 Subject: [PATCH 11/37] filter out KLib targets where the klib doesn't exist --- .../src/main/kotlin/tasks/BCVApiGenerateTask.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 7886789..e1b74fa 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -93,9 +93,14 @@ constructor( outputApiBuildDir = outputApiBuildDir, ) + // TODO log when klib file doesn't exist + // TODO log warning when klibFile has >1 file + val klibTargets = enabledTargets.withType() + .filter { it.klibFile.singleOrNull()?.exists() == true } + generateKLibTargets( workQueue = workQueue, - klibTargets = enabledTargets.withType(), + klibTargets = klibTargets, outputApiBuildDir = outputApiBuildDir, ) From 7dc0753af6aaa956469a44a66af14b869c1c5299 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:41:35 +0200 Subject: [PATCH 12/37] add BCVInternalApi to supportedByCurrentHost (should only be used in tests) --- modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api | 1 - .../bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index 15cc2ac..a49a87c 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -92,7 +92,6 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/B public abstract fun getCurrentPlatform ()Lorg/gradle/api/provider/Property; public abstract fun getHasKotlinSources ()Lorg/gradle/api/provider/Property; public abstract fun getKlibFile ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getSupportedByCurrentHost ()Lorg/gradle/api/provider/Property; public final fun getTargetName ()Ljava/lang/String; } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt index 996c8fc..dfc568e 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVKLibTarget.kt @@ -5,7 +5,6 @@ import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import java.io.Serializable import org.gradle.api.Named import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.Input @@ -28,8 +27,10 @@ constructor( @get:Input abstract val currentPlatform: Property // for up-to-date checks? + @BCVInternalApi // should only be used in tests @get:Input abstract val supportedByCurrentHost: Property + @get:Input abstract val hasKotlinSources: Property From 64846ec808b37851a97c877f94029c989af4c20e Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:42:47 +0200 Subject: [PATCH 13/37] add Kotlin version to generated BCVProperties --- modules/bcv-gradle-plugin/build.gradle.kts | 3 +++ modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/bcv-gradle-plugin/build.gradle.kts b/modules/bcv-gradle-plugin/build.gradle.kts index 3e09063..d008798 100644 --- a/modules/bcv-gradle-plugin/build.gradle.kts +++ b/modules/bcv-gradle-plugin/build.gradle.kts @@ -129,6 +129,8 @@ publishing { val createBCVProperties by tasks.registering { val bcvVersion = libs.versions.kotlinx.bcv inputs.property("bcvVersion", bcvVersion) + val kotlinVersion = libs.versions.kotlinGradle + inputs.property("kotlinVersion", kotlinVersion) val generatedSource = layout.buildDirectory.dir("generated-src/main/kotlin/") outputs.dir(generatedSource) @@ -146,6 +148,7 @@ val createBCVProperties by tasks.registering { | |internal object BCVProperties { | const val bcvVersion: String = "${bcvVersion.get()}" + | const val kotlinVersion: String = "${kotlinVersion.get()}" |} | """.trimMargin() diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 6f48779..78ef09c 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -65,7 +65,7 @@ constructor( outputApiDir.convention(layout.projectDirectory.dir(API_DIR)) projectName.convention(providers.provider { project.name }) kotlinxBinaryCompatibilityValidatorVersion.convention(BCVProperties.bcvVersion) - kotlinCompilerEmbeddableVersion.convention("1.9.22") + kotlinCompilerEmbeddableVersion.convention(BCVProperties.kotlinVersion) // have to set conventions because otherwise .add("...") doesn't work ignoredMarkers.convention(emptyList()) From a561c21445f440968d7067217d20a8e92ba832a9 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:45:18 +0200 Subject: [PATCH 14/37] configure klib defaults --- .../src/main/kotlin/BCVProjectPlugin.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 78ef09c..b3b8d46 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -15,6 +15,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.adapters.createKotlinMul import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion import dev.adamko.kotlin.binary_compatibility_validator.tasks.* import java.io.File import javax.inject.Inject @@ -75,6 +76,13 @@ constructor( ignoredClasses.convention(emptyList()) @Suppress("DEPRECATION") nonPublicMarkers.convention(null) + + @OptIn(BCVExperimentalApi::class) + klib.apply { + enabled.convention(false) + signatureVersion.convention(KLibSignatureVersion.Latest) + strictValidation.convention(false) + } } extension.targets.apply { @@ -92,7 +100,7 @@ constructor( enabled.convention(extension.klib.enabled) signatureVersion.convention(extension.klib.signatureVersion) - strictValidation.convention(false) + strictValidation.convention(extension.klib.strictValidation) supportedByCurrentHost.convention(false) } From 5b8234c83eb3d2bc7434021284e92a82e7393f52 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 17:46:34 +0200 Subject: [PATCH 15/37] update method of disabling KLib targets in tests --- .../validation/test/KlibVerificationTests.kt | 81 +++++++++++++++---- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 78f04be..5c817bc 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -215,7 +215,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } - runner.build { checkKLibDump("/examples/classes/HiddenDeclarations.klib.dump") } @@ -323,8 +322,9 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { abiFile(projectName = "testproject") { resolve("/examples/classes/TopLevelDeclarations.klib.dump") } + disableKLibTargets("linuxArm64") runner { - arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") +// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiCheck") } } @@ -343,8 +343,10 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { // note that the regular dump is used, where linuxArm64 is presented resolve("/examples/classes/TopLevelDeclarations.klib.dump") } + disableKLibTargets("linuxArm64") + runner { - arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") +// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiCheck") } } @@ -364,8 +366,11 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { // note that the regular dump is used, where linuxArm64 is presented resolve("/examples/classes/TopLevelDeclarations.klib.dump") } + + disableKLibTargets("linuxArm64") + runner { - arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") +// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiCheck") } } @@ -381,8 +386,11 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { baseProjectSetting() addToSrcSet("/examples/classes/TopLevelDeclarations.kt") addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + + disableKLibTargets("linuxArm64") + runner { - arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") +// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiDump") } } @@ -406,8 +414,11 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { additionalBuildConfig("/examples/gradle/configuration/grouping/clashingTargetNames.gradle.kts") addToSrcSet("/examples/classes/TopLevelDeclarations.kt") addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxMain") + + disableKLibTargets("linux") + runner { - arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linux") +// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linux") arguments.add(":klibApiDump") } } @@ -430,8 +441,11 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } addToSrcSet("/examples/classes/TopLevelDeclarations.kt") addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") + + disableKLibTargets("linuxArm64") + runner { - arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") +// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":klibApiDump") } } @@ -447,11 +461,20 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { val runner = test { baseProjectSetting() addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + disableKLibTargets( + "linuxArm64", + "linuxX64", + "mingwX64", + "androidNativeArm32", + "androidNativeArm64", + "androidNativeX64", + "androidNativeX86", + ) runner { - arguments.add( - "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + - "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" - ) +// arguments.add( +// "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + +// "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" +// ) arguments.add(":klibApiDump") } } @@ -470,11 +493,20 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { // note that the regular dump is used, where linuxArm64 is presented resolve("/examples/classes/TopLevelDeclarations.klib.dump") } + disableKLibTargets( + "linuxArm64", + "linuxX64", + "mingwX64", + "androidNativeArm32", + "androidNativeArm64", + "androidNativeX64", + "androidNativeX86", + ) runner { - arguments.add( - "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + - "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" - ) +// arguments.add( +// "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + +// "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" +// ) arguments.add(":klibApiCheck") } } @@ -802,7 +834,22 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } companion object { - private const val BANNED_TARGETS_PROPERTY_NAME = - "binary.compatibility.validator.klib.targets.disabled.for.testing" +// private const val BANNED_TARGETS_PROPERTY_NAME = +// "binary.compatibility.validator.klib.targets.disabled.for.testing" + + private fun BaseKotlinScope.disableKLibTargets(vararg targetNames: String) { + buildGradleKts { + val disabledTargets = + targetNames.joinToString(separator = ", ", prefix = "setOf(", postfix = ")") + addText( + """ + |binaryCompatibilityValidator.targets.withType() + | .matching { it.targetName in $disabledTargets } + | .configureEach { this.supportedByCurrentHost.set(false) } + | + """.trimMargin() + ) + } + } } } From 49c79ab6c0f8545cb298831947c63563b8fe2cf6 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 18:22:33 +0200 Subject: [PATCH 16/37] tidy log --- .../src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt index e311bf9..56c58a0 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/adapters/KotlinMultiplatformAdapter.kt @@ -7,17 +7,16 @@ import java.io.File import org.gradle.api.Project import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging -import org.gradle.api.provider.Provider import org.gradle.api.provider.ProviderFactory import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.* import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainer import org.jetbrains.kotlin.konan.target.HostManager -private val logger: Logger = Logging.getLogger("dev.adamko.kotlin.binary_compatibility_validator.adapters.KotlinMultiplatformAdapter") +private val logger: Logger = + Logging.getLogger("dev.adamko.kotlin.binary_compatibility_validator.adapters.KotlinMultiplatformAdapter") internal fun createKotlinMultiplatformTargets( project: Project, @@ -79,7 +78,7 @@ private fun registerKotlinKLibCompilation( providers: ProviderFactory, ) { extension.targets.register(target.targetName) { - logger.lifecycle("registering KLib target ${target.targetName}") + logger.info("BCV: registering KLib target ${target.targetName}") // enabled.convention(false) target.compilations From d2fdd97cd5660ac7e54b3e74ea99a46c2b36f0ab Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 31 May 2024 19:04:56 +0200 Subject: [PATCH 17/37] fixes --- gradle.properties | 2 +- .../validation/test/KlibVerificationTests.kt | 100 ++++++------------ .../examples/classes/HiddenDeclarations.kt | 5 - .../examples/classes/NonPublicMarkers.kt | 5 - .../src/main/kotlin/tasks/BCVApiCheckTask.kt | 16 ++- .../main/kotlin/tasks/BCVApiGenerateTask.kt | 14 +-- .../kotlin/workers/KLibSignaturesWorker.kt | 28 +++-- 7 files changed, 73 insertions(+), 97 deletions(-) diff --git a/gradle.properties b/gradle.properties index ec28775..6fb6d5f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -Xmx2g -XX:MaxMetaspaceSize=512m +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -Xmx4g -XX:MaxMetaspaceSize=1g org.gradle.caching=true diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 5c817bc..7dda2fc 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -4,13 +4,10 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import io.kotest.assertions.withClue import io.kotest.inspectors.shouldForAll -import io.kotest.matchers.comparables.shouldBeEqualComparingTo +import io.kotest.matchers.file.shouldBeEmptyDirectory import io.kotest.matchers.file.shouldExist -import io.kotest.matchers.file.shouldNotExist import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain -import java.io.File -import java.nio.file.Files import kotlin.io.path.Path import kotlin.io.path.name import org.gradle.testkit.runner.BuildResult @@ -143,7 +140,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { jvmApiDump.shouldExist() val expected = readResourceFile("/examples/classes/AnotherBuildConfig.dump") - jvmApiDump.readText().invariantNewlines().shouldBeEqualComparingTo(expected) + jvmApiDump.readText().invariantNewlines().shouldBe(expected.invariantNewlines()) } } @@ -386,19 +383,13 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { baseProjectSetting() addToSrcSet("/examples/classes/TopLevelDeclarations.kt") addToSrcSet("/examples/classes/AnotherBuildConfigLinuxArm64.kt", "linuxArm64Main") - disableKLibTargets("linuxArm64") - runner { -// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiDump") } } runner.build { - checkKLibDump( - "/examples/classes/TopLevelDeclarations.klib.with.linux.dump", - dumpTask = ":apiDump" - ) + checkKLibDump("/examples/classes/TopLevelDeclarations.klib.with.linux.dump") } } @@ -419,14 +410,11 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { runner { // arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linux") - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } runner.build { - checkKLibDump( - "/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump", - dumpTask = ":klibApiDump" - ) + checkKLibDump("/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump") } } @@ -446,7 +434,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { runner { // arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } @@ -475,7 +463,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { // "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + // "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" // ) - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } @@ -507,7 +495,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { // "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + // "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" // ) - arguments.add(":klibApiCheck") + arguments.add(":apiCheck") } } @@ -553,15 +541,12 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } addToSrcSet("/examples/classes/AnotherBuildConfig.kt") runner { - arguments.add(":klibApiDump") + arguments.add(":apiDump") } } runner.build { - checkKLibDump( - "/examples/classes/AnotherBuildConfig.klib.custom.dump", - dumpTask = ":klibApiDump" - ) + checkKLibDump("/examples/classes/AnotherBuildConfig.klib.custom.dump") } } @@ -579,10 +564,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } runner.build { - checkKLibDump( - "/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump", - dumpTask = ":apiDump" - ) + checkKLibDump("/examples/classes/AnotherBuildConfigLinux.klib.grouping.dump") } } @@ -637,16 +619,9 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } // Update the source file by adding a declaration - val updatedSourceFile = File( - this::class.java.getResource( - "/examples/classes/AnotherBuildConfigModified.kt" - )!!.toURI() - ) - val existingSource = runner.projectDir.resolve( - "src/commonMain/kotlin/AnotherBuildConfig.kt" - ) - Files.write(existingSource.toPath(), updatedSourceFile.readBytes()) - + val updatedSourceFile = readResourceFile("/examples/classes/AnotherBuildConfigModified.kt") + val existingSource = runner.projectDir.resolve("src/commonMain/kotlin/AnotherBuildConfig.kt") + existingSource.writeText(updatedSourceFile) runner.build { checkKLibDump("/examples/classes/AnotherBuildConfigModified.klib.dump") @@ -666,20 +641,13 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { runner.build { checkKLibDump("/examples/classes/AnotherBuildConfig.klib.dump") // Update the source file by adding a declaration - val updatedSourceFile = File( - this::class.java.getResource( - "/examples/classes/AnotherBuildConfigLinuxArm64.kt" - )!!.toURI() - ) - val existingSource = runner.projectDir.resolve( - "src/linuxArm64Main/kotlin/AnotherBuildConfigLinuxArm64.kt" - ) + val updatedSourceFile = readResourceFile("/examples/classes/AnotherBuildConfigLinuxArm64.kt") + val existingSource = + runner.projectDir.resolve("src/linuxArm64Main/kotlin/AnotherBuildConfigLinuxArm64.kt") existingSource.parentFile.mkdirs() - Files.write(existingSource.toPath(), updatedSourceFile.readBytes()) - + existingSource.writeText(updatedSourceFile) runner.build { - checkKLibDump("/examples/classes/AnotherBuildConfigLinuxArm64Extra.klib.dump") } } @@ -702,18 +670,12 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } // Update the source file by adding a declaration - val updatedSourceFile = File( - this::class.java.getResource( - "/examples/classes/AnotherBuildConfigModified.kt" - )!!.toURI() - ) - val existingSource = runner.projectDir.resolve( - "src/commonMain/kotlin/AnotherBuildConfig.kt" - ) - Files.write(existingSource.toPath(), updatedSourceFile.readBytes()) + val updatedSourceFile = readResourceFile("/examples/classes/AnotherBuildConfigModified.kt") + val existingSource = runner.projectDir.resolve("src/commonMain/kotlin/AnotherBuildConfig.kt") + existingSource.writeText(updatedSourceFile) runner.buildAndFail { - shouldHaveTaskWithOutcome(":klibApiCheck", FAILED) + shouldHaveTaskWithOutcome(":apiCheck", FAILED) } } @@ -746,8 +708,8 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveTaskWithOutcome(":klibApiCheck", SKIPPED) - rootProjectApiDump.parentFile.shouldNotExist() + shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + rootProjectApiDump.parentFile.shouldBeEmptyDirectory() } } @@ -777,7 +739,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } runner.build { - shouldHaveRunTask(":apiCheck", SUCCESS) + shouldHaveRunTask(":apiCheck", SKIPPED) } } @@ -819,10 +781,9 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { private fun BuildResult.checkKLibDump( expectedDumpFileName: String, projectName: String = "testproject", - dumpTask: String = ":apiDump", ) { withClue(output) { - shouldHaveRunTask(dumpTask, SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS) val generatedDump = rootProjectAbiDump(projectName) @@ -840,12 +801,13 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { private fun BaseKotlinScope.disableKLibTargets(vararg targetNames: String) { buildGradleKts { val disabledTargets = - targetNames.joinToString(separator = ", ", prefix = "setOf(", postfix = ")") + targetNames.joinToString(", ") { "\"$it\"" } addText( """ - |binaryCompatibilityValidator.targets.withType() - | .matching { it.targetName in $disabledTargets } - | .configureEach { this.supportedByCurrentHost.set(false) } + |binaryCompatibilityValidator.targets + | .withType() + | .matching { it.targetName in setOf($disabledTargets) } + | .configureEach { supportedByCurrentHost.set(false) } | """.trimMargin() ) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt index 702ed05..7565370 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/HiddenDeclarations.kt @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - package examples.classes import annotations.* diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt index fdf8288..e99b28a 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/NonPublicMarkers.kt @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - package annotations @HiddenClass diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index 30a0a67..1efb0d2 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -9,6 +9,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.internal.fullPath import java.io.File import java.util.TreeMap import javax.inject.Inject +import kotlin.io.path.listDirectoryEntries import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileVisitDetails import org.gradle.api.file.RelativePath @@ -52,6 +53,17 @@ constructor( private val rootDir: File = project.rootProject.rootDir + init { + super.onlyIf { task -> + require(task is BCVApiCheckTask) + task.apiBuildDir.orNull?.asFile + ?.takeIf(File::exists) + ?.toPath() + ?.listDirectoryEntries() + ?.isNotEmpty() == true + } + } + @TaskAction fun verify() { val projectApiDir = projectApiDir.orNull @@ -156,8 +168,8 @@ constructor( /** * We use case-insensitive comparison to workaround issues with case-insensitive OSes and Gradle * behaving slightly different on different platforms. We neither know original sensitivity of - * existing .api files, not build ones, because projectName that is part of the path can have any - * sensitivity. To work around that, we replace paths we are looking for the same paths that + * existing `.api` files, not build ones, because `projectName` that is part of the path can have + * any sensitivity. To work around that, we replace paths we are looking for the same paths that * actually exist on the FS. */ private class RelativePaths( diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index e1b74fa..60e6993 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -84,12 +84,9 @@ constructor( val enabledTargets = targets.matching { it.enabled.getOrElse(true) } - logger.lifecycle("[$path] ${enabledTargets.size} enabledTargets : ${enabledTargets.joinToString { it.name }}") - generateJvmTargets( workQueue = workQueue, jvmTargets = enabledTargets.withType(), - enabledTargets = enabledTargets.size, outputApiBuildDir = outputApiBuildDir, ) @@ -97,6 +94,7 @@ constructor( // TODO log warning when klibFile has >1 file val klibTargets = enabledTargets.withType() .filter { it.klibFile.singleOrNull()?.exists() == true } + .sortedBy { it.targetName } generateKLibTargets( workQueue = workQueue, @@ -124,13 +122,16 @@ constructor( workQueue: WorkQueue, outputApiBuildDir: Directory, jvmTargets: Collection, - enabledTargets: Int, ) { - if (jvmTargets.isEmpty()) return + if (jvmTargets.isEmpty()) { + logger.info("[$path] No enabled JVM targets") + return + } + logger.lifecycle("[$path] generating ${jvmTargets.size} JVM targets : ${jvmTargets.joinToString { it.name }}") jvmTargets.forEach { target -> - val outputDir = if (enabledTargets == 1) { + val outputDir = if (jvmTargets.size == 1) { outputApiBuildDir } else { outputApiBuildDir.dir(target.platformType) @@ -249,6 +250,7 @@ constructor( this@worker.klib.set(target.klibFile.singleFile) this@worker.signatureVersion.set(target.signatureVersion) + this@worker.strictValidation.set(target.strictValidation) // this@worker.supportedByCurrentHost.set(target.supportedByCurrentHost) // this@worker.targets.addAll(klibTargets) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt index 449602d..c567f43 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt @@ -32,8 +32,7 @@ abstract class KLibSignaturesWorker : WorkAction val signatureVersion: Property - -// val targets: ListProperty + val strictValidation: Property /** * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, @@ -46,16 +45,19 @@ abstract class KLibSignaturesWorker : WorkAction Date: Sat, 1 Jun 2024 17:18:35 +0200 Subject: [PATCH 18/37] tidy kdoc --- .../main/kotlin/targets/KLibValidationSpec.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt index b025dba..23db368 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt @@ -25,12 +25,12 @@ interface KLibValidationSpec { * * This option covers some advanced scenarios and does not require any configuration by default. * - * A linker uses signatures to look up symbols, thus signature changes brake binary compatibility and - * should be tracked. Signature format itself is not stabilized yet and may change in the future. In that case, - * a new version of a signature will be introduced. Change of a signature version will be reflected in a dump - * causing a validation failure even if declarations itself remained unchanged. - * However, if a KLib supports multiple signature versions simultaneously, one my explicitly specify the version - * that will be dumped to prevent changes in a dump file. + * A linker uses signatures to look up symbols, thus signature changes brake binary compatibility + * and should be tracked. Signature format itself is not stabilized yet and may change in the + * future. In that case, a new version of a signature will be introduced. Change of a signature + * version will be reflected in a dump causing a validation failure even if declarations itself + * remained unchanged. However, if a KLib supports multiple signature versions simultaneously, + * one may explicitly specify the version that will be dumped to prevent changes in a dump file. */ @get:Input @get:Optional @@ -45,8 +45,9 @@ interface KLibValidationSpec { /** * Fail validation if some build targets are not supported by the host compiler. - * By default, ABI dumped only for supported files will be validated. This option makes validation behavior - * stricter and treats having unsupported targets as an error. + * + * By default, ABI dumped only for supported files will be validated. This option makes + * validation behavior stricter and treats having unsupported targets as an error. */ @get:Input @get:Optional From 899de2eb804aaf1bcb74173b6ea9e3d4a7a82aa2 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:18:48 +0200 Subject: [PATCH 19/37] prep for klib extract --- .../validation/test/KlibVerificationTests.kt | 1 - .../main/kotlin/tasks/BCVApiGenerateTask.kt | 71 +++++++++++++++---- .../main/kotlin/workers/KLibExtractWorker.kt | 67 +++++++++++++++++ 3 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibExtractWorker.kt diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 7dda2fc..2d019e7 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -229,7 +229,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } - runner.build { checkKLibDump("/examples/classes/Subclasses.klib.dump") } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 60e6993..677db68 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -4,10 +4,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget -import dev.adamko.kotlin.binary_compatibility_validator.workers.JvmSignaturesWorker -import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibInferSignaturesWorker -import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibMergeWorker -import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.* import java.io.File import javax.inject.Inject import org.gradle.api.NamedDomainObjectContainer @@ -184,6 +181,23 @@ constructor( val (supportedKLibTargets, unsupportedKLibTargets) = klibTargets.partition { it.supportedByCurrentHost.get() } + + generateSupportedKLibTargets(workQueue, supportedKLibTargets) + generateUnsupportedKLibTargets(workQueue, unsupportedKLibTargets) + + workQueue.await() + + val allTargetDumpFiles = + supportedTargetsDir.walk().filter { it.isFile }.toSet() union + unsupportedTargetsDir.walk().filter { it.isFile }.toSet() + + mergeDumpFiles(workQueue, allTargetDumpFiles, outputApiBuildDir.asFile) + } + + private fun generateSupportedKLibTargets( + workQueue: WorkQueue, + supportedKLibTargets: List + ) { logger.lifecycle("[$path] generating ${supportedKLibTargets.size} supported KLib targets : ${supportedKLibTargets.joinToString { it.name }}") val supportedKLibGenDuration = measureTime { @@ -195,8 +209,14 @@ constructor( } workQueue.await() } + logger.lifecycle("[$path] finished generating supported KLib targets in $supportedKLibGenDuration") + } + private fun generateUnsupportedKLibTargets( + workQueue: WorkQueue, + unsupportedKLibTargets: List + ) { logger.lifecycle("[$path] generating ${unsupportedKLibTargets.size} unsupported KLib targets : ${unsupportedKLibTargets.joinToString { it.name }}") val unsupportedKLibGenDuration = measureTime { @@ -210,30 +230,32 @@ constructor( ) } } - logger.lifecycle("[$path] finished generating unsupported KLib targets in $unsupportedKLibGenDuration") - workQueue.await() + logger.lifecycle("[$path] finished generating unsupported KLib targets in $unsupportedKLibGenDuration") + } - val allTargetDumpFiles = - supportedTargetsDir.walk().filter { it.isFile }.toSet() union - unsupportedTargetsDir.walk().filter { it.isFile }.toSet() + private fun mergeDumpFiles( + workQueue: WorkQueue, + allTargetDumpFiles: Set, + outputApiBuildDir: File + ) { logger.lifecycle("[$path] merging ${allTargetDumpFiles.size} dump files : ${allTargetDumpFiles.joinToString { it.name }}") workQueue.merge( projectName.get(), targetDumpFiles = allTargetDumpFiles, - outputDir = outputApiBuildDir.asFile, + outputDir = outputApiBuildDir, ) workQueue.await() - logger.lifecycle( - "[$path] merged ${allTargetDumpFiles.size} dump files : ${ - outputApiBuildDir.asFile.walk().filter { it.isFile }.toList() - }" - ) + if (logger.isLifecycleEnabled) { + val fileNames = outputApiBuildDir.walk().filter { it.isFile }.toList() + logger.lifecycle("[$path] merged ${allTargetDumpFiles.size} dump files : $fileNames") + } } + @OptIn(BCVExperimentalApi::class) private fun WorkQueue.submit( target: BCVKLibTarget, @@ -300,5 +322,24 @@ constructor( this@worker.targetDumpFiles.from(targetDumpFiles) } } + + @OptIn(BCVExperimentalApi::class) + private fun WorkQueue.extract( + target: BCVKLibTarget, + targetDumpFiles: Set, + outputDir: File, + ) { + val task = this@BCVApiGenerateTask + + @OptIn(BCVInternalApi::class) + submit(KLibExtractWorker::class) worker@{ + this@worker.taskPath.set(task.path) + this@worker.strictValidation.set(target.strictValidation) + +// this@worker.inputAbiFile.set() +// this@worker.outputAbiFile.set() +// this@worker.supportedTargets.set() + } + } //endregion } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibExtractWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibExtractWorker.kt new file mode 100644 index 0000000..a5d4fda --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibExtractWorker.kt @@ -0,0 +1,67 @@ +package dev.adamko.kotlin.binary_compatibility_validator.workers + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi +import java.io.Serializable +import kotlinx.validation.ExperimentalBCVApi +import kotlinx.validation.KlibValidationSettings +import kotlinx.validation.api.klib.KlibDump +import kotlinx.validation.api.klib.KlibTarget +import kotlinx.validation.api.klib.saveTo +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters + +@BCVInternalApi +@BCVExperimentalApi +@OptIn(ExperimentalBCVApi::class) +abstract class KLibExtractWorker : WorkAction { + + @BCVInternalApi + interface Parameters : WorkParameters, Serializable { +// val projectName: Property + /** Merged KLib dump that should be filtered by this task. */ + val inputAbiFile: RegularFileProperty + /** A path to the resulting dump file. */ + val outputAbiFile: RegularFileProperty + /** Provider returning targets supported by the host compiler. */ + val supportedTargets: SetProperty + /** Refer to [KlibValidationSettings.strictValidation] for details. */ + val strictValidation: Property + /** + * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, + * for log messages + */ + val taskPath: Property + } + + override fun execute() { + val inputAbiFile = parameters.inputAbiFile.get().asFile + val supportedTargets = parameters.supportedTargets.get() + val strictValidation = parameters.strictValidation.getOrElse(false) + val outputAbiFile = parameters.outputAbiFile.get().asFile + + if (inputAbiFile.length() == 0L) { + error("Project ABI file $inputAbiFile is empty") + } + val dump = KlibDump.from(inputAbiFile) + val enabledTargets = supportedTargets.map { KlibTarget.parse(it).targetName } + // Filter out only unsupported files. + // That ensures that target renaming will be caught and reported as a change. + val targetsToRemove = dump.targets.filter { it.targetName !in enabledTargets } + if (targetsToRemove.isNotEmpty() && strictValidation) { + error("Validation could not be performed as some targets are not available and strictValidation mode is enabled") + } + dump.remove(targetsToRemove) + dump.saveTo(outputAbiFile) + } + + @BCVInternalApi + companion object { + private val logger: Logger = Logging.getLogger(KLibExtractWorker::class.java) + } +} From 4f2a73fdf73391176cecb232271cd7af92c8697e Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:37:50 +0200 Subject: [PATCH 20/37] NFC: extend top-level decls test case --- .../validation/test/KlibVerificationTests.kt | 18 ------ .../TopLevelDeclarations.klib.all.dump | 63 ++++++++++++++++++- .../classes/TopLevelDeclarations.klib.dump | 63 ++++++++++++++++++- .../TopLevelDeclarations.klib.unsup.dump | 63 ++++++++++++++++++- .../classes/TopLevelDeclarations.klib.v1.dump | 63 ++++++++++++++++++- ...lDeclarations.klib.with.guessed.linux.dump | 63 ++++++++++++++++++- .../TopLevelDeclarations.klib.with.linux.dump | 63 ++++++++++++++++++- .../examples/classes/TopLevelDeclarations.kt | 38 ++++++++++- 8 files changed, 409 insertions(+), 25 deletions(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 2d019e7..115e4d5 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -320,7 +320,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } disableKLibTargets("linuxArm64") runner { -// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiCheck") } } @@ -342,7 +341,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { disableKLibTargets("linuxArm64") runner { -// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiCheck") } } @@ -362,11 +360,8 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { // note that the regular dump is used, where linuxArm64 is presented resolve("/examples/classes/TopLevelDeclarations.klib.dump") } - disableKLibTargets("linuxArm64") - runner { -// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiCheck") } } @@ -408,7 +403,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { disableKLibTargets("linux") runner { -// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linux") arguments.add(":apiDump") } } @@ -432,7 +426,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { disableKLibTargets("linuxArm64") runner { -// arguments.add("-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64") arguments.add(":apiDump") } } @@ -458,10 +451,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { "androidNativeX86", ) runner { -// arguments.add( -// "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + -// "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" -// ) arguments.add(":apiDump") } } @@ -490,10 +479,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { "androidNativeX86", ) runner { -// arguments.add( -// "-P$BANNED_TARGETS_PROPERTY_NAME=linuxArm64,linuxX64,mingwX64," + -// "androidNativeArm32,androidNativeArm64,androidNativeX64,androidNativeX86" -// ) arguments.add(":apiCheck") } } @@ -794,9 +779,6 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } companion object { -// private const val BANNED_TARGETS_PROPERTY_NAME = -// "binary.compatibility.validator.klib.targets.disabled.for.testing" - private fun BaseKotlinScope.disableKLibTargets(vararg targetNames: String) { buildGradleKts { val disabledTargets = diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump index aa08591..05aa3cd 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.all.dump @@ -11,7 +11,11 @@ abstract class examples.classes/AC { // examples.classes/AC|null[0] constructor () // examples.classes/AC.|(){}[0] final fun b() // examples.classes/AC.b|b(){}[0] } +abstract fun interface examples.classes/FI { // examples.classes/FI|null[0] + abstract fun a() // examples.classes/FI.a|a(){}[0] +} abstract interface examples.classes/I // examples.classes/I|null[0] +abstract interface examples.classes/II // examples.classes/II|null[0] final class examples.classes/C { // examples.classes/C|null[0] constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] final fun m() // examples.classes/C.m|m(){}[0] @@ -28,17 +32,34 @@ final class examples.classes/D { // examples.classes/D|null[0] final val x // examples.classes/D.x|{}x[0] final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] } +final class examples.classes/IC : examples.classes/II { // examples.classes/IC|null[0] + constructor () // examples.classes/IC.|(){}[0] +} final class examples.classes/Outer { // examples.classes/Outer|null[0] constructor () // examples.classes/Outer.|(){}[0] final class Nested { // examples.classes/Outer.Nested|null[0] constructor () // examples.classes/Outer.Nested.|(){}[0] + final enum class NE : kotlin/Enum { // examples.classes/Outer.Nested.NE|null[0] + enum entry A // examples.classes/Outer.Nested.NE.A|null[0] + enum entry B // examples.classes/Outer.Nested.NE.B|null[0] + enum entry C // examples.classes/Outer.Nested.NE.C|null[0] + final fun valueOf(kotlin/String): examples.classes/Outer.Nested.NE // examples.classes/Outer.Nested.NE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/Outer.Nested.NE.values|values#static(){}[0] + final val entries // examples.classes/Outer.Nested.NE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/Outer.Nested.NE.entries.|#static(){}[0] + } final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] } + final inner class YetAnotherInner { // examples.classes/Outer.Nested.YetAnotherInner|null[0] + constructor () // examples.classes/Outer.Nested.YetAnotherInner.|(){}[0] + } } } final const val examples.classes/con // examples.classes/con|{}con[0] final fun (): kotlin/String // examples.classes/con.|(){}[0] +final const val examples.classes/intCon // examples.classes/intCon|{}intCon[0] + final fun (): kotlin/Int // examples.classes/intCon.|(){}[0] final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] enum entry A // examples.classes/E.A|null[0] enum entry B // examples.classes/E.B|null[0] @@ -48,20 +69,60 @@ final enum class examples.classes/E : kotlin/Enum { // examp final val entries // examples.classes/E.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] } +final enum class examples.classes/EE : kotlin/Enum { // examples.classes/EE|null[0] + enum entry AA // examples.classes/EE.AA|null[0] + enum entry BB // examples.classes/EE.BB|null[0] + enum entry CC // examples.classes/EE.CC|null[0] + final fun valueOf(kotlin/String): examples.classes/EE // examples.classes/EE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/EE.values|values#static(){}[0] + final val entries // examples.classes/EE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/EE.entries.|#static(){}[0] +} final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] final object examples.classes/O // examples.classes/O|null[0] +final object examples.classes/OO // examples.classes/OO|null[0] +final val examples.classes/a // examples.classes/a|{}a[0] + final fun (): kotlin/Any // examples.classes/a.|(){}[0] +final val examples.classes/i // examples.classes/i|{}i[0] + final fun (): kotlin/Int // examples.classes/i.|(){}[0] final val examples.classes/l // examples.classes/l|{}l[0] final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/d // examples.classes/d|{}d[0] + final fun (): kotlin/Double // examples.classes/d.|(){}[0] + final fun (kotlin/Double) // examples.classes/d.|(kotlin.Double){}[0] final var examples.classes/r // examples.classes/r|{}r[0] final fun (): kotlin/Float // examples.classes/r.|(){}[0] final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] constructor () // examples.classes/A.|(){}[0] } +open annotation class examples.classes/AA : kotlin/Annotation { // examples.classes/AA|null[0] + constructor () // examples.classes/AA.|(){}[0] +} +open annotation class examples.classes/AAA : kotlin/Annotation { // examples.classes/AAA|null[0] + constructor () // examples.classes/AAA.|(){}[0] +} open class examples.classes/OC { // examples.classes/OC|null[0] - constructor () // examples.classes/OC.|(){}[0] + constructor (kotlin/Int) // examples.classes/OC.|(kotlin.Int){}[0] + constructor (kotlin/Long) // examples.classes/OC.|(kotlin.Long){}[0] + constructor (kotlin/String) // examples.classes/OC.|(kotlin.String){}[0] final fun c() // examples.classes/OC.c|c(){}[0] + final val ix // examples.classes/OC.ix|{}ix[0] + final fun (): kotlin/Int // examples.classes/OC.ix.|(){}[0] + final val iy // examples.classes/OC.iy|{}iy[0] + final fun (): kotlin/Long // examples.classes/OC.iy.|(){}[0] + final val iz // examples.classes/OC.iz|{}iz[0] + final fun (): kotlin/String // examples.classes/OC.iz.|(){}[0] + final var x // examples.classes/OC.x|{}x[0] + final fun (): kotlin/Int // examples.classes/OC.x.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.x.|(kotlin.Int){}[0] + final var y // examples.classes/OC.y|{}y[0] + final fun (): kotlin/Int // examples.classes/OC.y.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.y.|(kotlin.Int){}[0] + final var z // examples.classes/OC.z|{}z[0] + final fun (): kotlin/Int // examples.classes/OC.z.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.z.|(kotlin.Int){}[0] open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump index c7bb38f..fdc5d28 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.dump @@ -11,7 +11,11 @@ abstract class examples.classes/AC { // examples.classes/AC|null[0] constructor () // examples.classes/AC.|(){}[0] final fun b() // examples.classes/AC.b|b(){}[0] } +abstract fun interface examples.classes/FI { // examples.classes/FI|null[0] + abstract fun a() // examples.classes/FI.a|a(){}[0] +} abstract interface examples.classes/I // examples.classes/I|null[0] +abstract interface examples.classes/II // examples.classes/II|null[0] final class examples.classes/C { // examples.classes/C|null[0] constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] final fun m() // examples.classes/C.m|m(){}[0] @@ -28,17 +32,34 @@ final class examples.classes/D { // examples.classes/D|null[0] final val x // examples.classes/D.x|{}x[0] final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] } +final class examples.classes/IC : examples.classes/II { // examples.classes/IC|null[0] + constructor () // examples.classes/IC.|(){}[0] +} final class examples.classes/Outer { // examples.classes/Outer|null[0] constructor () // examples.classes/Outer.|(){}[0] final class Nested { // examples.classes/Outer.Nested|null[0] constructor () // examples.classes/Outer.Nested.|(){}[0] + final enum class NE : kotlin/Enum { // examples.classes/Outer.Nested.NE|null[0] + enum entry A // examples.classes/Outer.Nested.NE.A|null[0] + enum entry B // examples.classes/Outer.Nested.NE.B|null[0] + enum entry C // examples.classes/Outer.Nested.NE.C|null[0] + final fun valueOf(kotlin/String): examples.classes/Outer.Nested.NE // examples.classes/Outer.Nested.NE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/Outer.Nested.NE.values|values#static(){}[0] + final val entries // examples.classes/Outer.Nested.NE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/Outer.Nested.NE.entries.|#static(){}[0] + } final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] } + final inner class YetAnotherInner { // examples.classes/Outer.Nested.YetAnotherInner|null[0] + constructor () // examples.classes/Outer.Nested.YetAnotherInner.|(){}[0] + } } } final const val examples.classes/con // examples.classes/con|{}con[0] final fun (): kotlin/String // examples.classes/con.|(){}[0] +final const val examples.classes/intCon // examples.classes/intCon|{}intCon[0] + final fun (): kotlin/Int // examples.classes/intCon.|(){}[0] final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] enum entry A // examples.classes/E.A|null[0] enum entry B // examples.classes/E.B|null[0] @@ -48,20 +69,60 @@ final enum class examples.classes/E : kotlin/Enum { // examp final val entries // examples.classes/E.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] } +final enum class examples.classes/EE : kotlin/Enum { // examples.classes/EE|null[0] + enum entry AA // examples.classes/EE.AA|null[0] + enum entry BB // examples.classes/EE.BB|null[0] + enum entry CC // examples.classes/EE.CC|null[0] + final fun valueOf(kotlin/String): examples.classes/EE // examples.classes/EE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/EE.values|values#static(){}[0] + final val entries // examples.classes/EE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/EE.entries.|#static(){}[0] +} final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] final object examples.classes/O // examples.classes/O|null[0] +final object examples.classes/OO // examples.classes/OO|null[0] +final val examples.classes/a // examples.classes/a|{}a[0] + final fun (): kotlin/Any // examples.classes/a.|(){}[0] +final val examples.classes/i // examples.classes/i|{}i[0] + final fun (): kotlin/Int // examples.classes/i.|(){}[0] final val examples.classes/l // examples.classes/l|{}l[0] final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/d // examples.classes/d|{}d[0] + final fun (): kotlin/Double // examples.classes/d.|(){}[0] + final fun (kotlin/Double) // examples.classes/d.|(kotlin.Double){}[0] final var examples.classes/r // examples.classes/r|{}r[0] final fun (): kotlin/Float // examples.classes/r.|(){}[0] final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] constructor () // examples.classes/A.|(){}[0] } +open annotation class examples.classes/AA : kotlin/Annotation { // examples.classes/AA|null[0] + constructor () // examples.classes/AA.|(){}[0] +} +open annotation class examples.classes/AAA : kotlin/Annotation { // examples.classes/AAA|null[0] + constructor () // examples.classes/AAA.|(){}[0] +} open class examples.classes/OC { // examples.classes/OC|null[0] - constructor () // examples.classes/OC.|(){}[0] + constructor (kotlin/Int) // examples.classes/OC.|(kotlin.Int){}[0] + constructor (kotlin/Long) // examples.classes/OC.|(kotlin.Long){}[0] + constructor (kotlin/String) // examples.classes/OC.|(kotlin.String){}[0] final fun c() // examples.classes/OC.c|c(){}[0] + final val ix // examples.classes/OC.ix|{}ix[0] + final fun (): kotlin/Int // examples.classes/OC.ix.|(){}[0] + final val iy // examples.classes/OC.iy|{}iy[0] + final fun (): kotlin/Long // examples.classes/OC.iy.|(){}[0] + final val iz // examples.classes/OC.iz|{}iz[0] + final fun (): kotlin/String // examples.classes/OC.iz.|(){}[0] + final var x // examples.classes/OC.x|{}x[0] + final fun (): kotlin/Int // examples.classes/OC.x.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.x.|(kotlin.Int){}[0] + final var y // examples.classes/OC.y|{}y[0] + final fun (): kotlin/Int // examples.classes/OC.y.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.y.|(kotlin.Int){}[0] + final var z // examples.classes/OC.z|{}z[0] + final fun (): kotlin/Int // examples.classes/OC.z.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.z.|(kotlin.Int){}[0] open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump index 9764aec..f40c1ef 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.unsup.dump @@ -11,7 +11,11 @@ abstract class examples.classes/AC { // examples.classes/AC|null[0] constructor () // examples.classes/AC.|(){}[0] final fun b() // examples.classes/AC.b|b(){}[0] } +abstract fun interface examples.classes/FI { // examples.classes/FI|null[0] + abstract fun a() // examples.classes/FI.a|a(){}[0] +} abstract interface examples.classes/I // examples.classes/I|null[0] +abstract interface examples.classes/II // examples.classes/II|null[0] final class examples.classes/C { // examples.classes/C|null[0] constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] final fun m() // examples.classes/C.m|m(){}[0] @@ -28,17 +32,34 @@ final class examples.classes/D { // examples.classes/D|null[0] final val x // examples.classes/D.x|{}x[0] final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] } +final class examples.classes/IC : examples.classes/II { // examples.classes/IC|null[0] + constructor () // examples.classes/IC.|(){}[0] +} final class examples.classes/Outer { // examples.classes/Outer|null[0] constructor () // examples.classes/Outer.|(){}[0] final class Nested { // examples.classes/Outer.Nested|null[0] constructor () // examples.classes/Outer.Nested.|(){}[0] + final enum class NE : kotlin/Enum { // examples.classes/Outer.Nested.NE|null[0] + enum entry A // examples.classes/Outer.Nested.NE.A|null[0] + enum entry B // examples.classes/Outer.Nested.NE.B|null[0] + enum entry C // examples.classes/Outer.Nested.NE.C|null[0] + final fun valueOf(kotlin/String): examples.classes/Outer.Nested.NE // examples.classes/Outer.Nested.NE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/Outer.Nested.NE.values|values#static(){}[0] + final val entries // examples.classes/Outer.Nested.NE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/Outer.Nested.NE.entries.|#static(){}[0] + } final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] } + final inner class YetAnotherInner { // examples.classes/Outer.Nested.YetAnotherInner|null[0] + constructor () // examples.classes/Outer.Nested.YetAnotherInner.|(){}[0] + } } } final const val examples.classes/con // examples.classes/con|{}con[0] final fun (): kotlin/String // examples.classes/con.|(){}[0] +final const val examples.classes/intCon // examples.classes/intCon|{}intCon[0] + final fun (): kotlin/Int // examples.classes/intCon.|(){}[0] final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] enum entry A // examples.classes/E.A|null[0] enum entry B // examples.classes/E.B|null[0] @@ -48,20 +69,60 @@ final enum class examples.classes/E : kotlin/Enum { // examp final val entries // examples.classes/E.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] } +final enum class examples.classes/EE : kotlin/Enum { // examples.classes/EE|null[0] + enum entry AA // examples.classes/EE.AA|null[0] + enum entry BB // examples.classes/EE.BB|null[0] + enum entry CC // examples.classes/EE.CC|null[0] + final fun valueOf(kotlin/String): examples.classes/EE // examples.classes/EE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/EE.values|values#static(){}[0] + final val entries // examples.classes/EE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/EE.entries.|#static(){}[0] +} final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] final object examples.classes/O // examples.classes/O|null[0] +final object examples.classes/OO // examples.classes/OO|null[0] +final val examples.classes/a // examples.classes/a|{}a[0] + final fun (): kotlin/Any // examples.classes/a.|(){}[0] +final val examples.classes/i // examples.classes/i|{}i[0] + final fun (): kotlin/Int // examples.classes/i.|(){}[0] final val examples.classes/l // examples.classes/l|{}l[0] final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/d // examples.classes/d|{}d[0] + final fun (): kotlin/Double // examples.classes/d.|(){}[0] + final fun (kotlin/Double) // examples.classes/d.|(kotlin.Double){}[0] final var examples.classes/r // examples.classes/r|{}r[0] final fun (): kotlin/Float // examples.classes/r.|(){}[0] final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] constructor () // examples.classes/A.|(){}[0] } +open annotation class examples.classes/AA : kotlin/Annotation { // examples.classes/AA|null[0] + constructor () // examples.classes/AA.|(){}[0] +} +open annotation class examples.classes/AAA : kotlin/Annotation { // examples.classes/AAA|null[0] + constructor () // examples.classes/AAA.|(){}[0] +} open class examples.classes/OC { // examples.classes/OC|null[0] - constructor () // examples.classes/OC.|(){}[0] + constructor (kotlin/Int) // examples.classes/OC.|(kotlin.Int){}[0] + constructor (kotlin/Long) // examples.classes/OC.|(kotlin.Long){}[0] + constructor (kotlin/String) // examples.classes/OC.|(kotlin.String){}[0] final fun c() // examples.classes/OC.c|c(){}[0] + final val ix // examples.classes/OC.ix|{}ix[0] + final fun (): kotlin/Int // examples.classes/OC.ix.|(){}[0] + final val iy // examples.classes/OC.iy|{}iy[0] + final fun (): kotlin/Long // examples.classes/OC.iy.|(){}[0] + final val iz // examples.classes/OC.iz|{}iz[0] + final fun (): kotlin/String // examples.classes/OC.iz.|(){}[0] + final var x // examples.classes/OC.x|{}x[0] + final fun (): kotlin/Int // examples.classes/OC.x.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.x.|(kotlin.Int){}[0] + final var y // examples.classes/OC.y|{}y[0] + final fun (): kotlin/Int // examples.classes/OC.y.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.y.|(kotlin.Int){}[0] + final var z // examples.classes/OC.z|{}z[0] + final fun (): kotlin/Int // examples.classes/OC.z.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.z.|(kotlin.Int){}[0] open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump index 9442fd6..661d87f 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.v1.dump @@ -11,7 +11,11 @@ abstract class examples.classes/AC { // examples.classes/AC|null[0] constructor () // examples.classes/AC.|-5645683436151566731[0] final fun b() // examples.classes/AC.b|4789657038926421504[0] } +abstract fun interface examples.classes/FI { // examples.classes/FI|null[0] + abstract fun a() // examples.classes/FI.a|-4432112437378250461[0] +} abstract interface examples.classes/I // examples.classes/I|null[0] +abstract interface examples.classes/II // examples.classes/II|null[0] final class examples.classes/C { // examples.classes/C|null[0] constructor (kotlin/Any) // examples.classes/C.|4518179880532599055[0] final fun m() // examples.classes/C.m|-1029306787563722981[0] @@ -28,17 +32,34 @@ final class examples.classes/D { // examples.classes/D|null[0] final val x // examples.classes/D.x|-8060530855978347579[0] final fun (): kotlin/Int // examples.classes/D.x.|1482705010654679335[0] } +final class examples.classes/IC : examples.classes/II { // examples.classes/IC|null[0] + constructor () // examples.classes/IC.|-5645683436151566731[0] +} final class examples.classes/Outer { // examples.classes/Outer|null[0] constructor () // examples.classes/Outer.|-5645683436151566731[0] final class Nested { // examples.classes/Outer.Nested|null[0] constructor () // examples.classes/Outer.Nested.|-5645683436151566731[0] + final enum class NE : kotlin/Enum { // examples.classes/Outer.Nested.NE|null[0] + enum entry A // examples.classes/Outer.Nested.NE.A|null[0] + enum entry B // examples.classes/Outer.Nested.NE.B|null[0] + enum entry C // examples.classes/Outer.Nested.NE.C|null[0] + final fun valueOf(kotlin/String): examples.classes/Outer.Nested.NE // examples.classes/Outer.Nested.NE.valueOf|-4683474617854611729[0] + final fun values(): kotlin/Array // examples.classes/Outer.Nested.NE.values|-8715569000920726747[0] + final val entries // examples.classes/Outer.Nested.NE.entries|-5134227801081826149[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/Outer.Nested.NE.entries.|-6068527377476727729[0] + } final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] constructor () // examples.classes/Outer.Nested.Inner.|-5645683436151566731[0] } + final inner class YetAnotherInner { // examples.classes/Outer.Nested.YetAnotherInner|null[0] + constructor () // examples.classes/Outer.Nested.YetAnotherInner.|-5645683436151566731[0] + } } } final const val examples.classes/con // examples.classes/con|-2899158152154217071[0] final fun (): kotlin/String // examples.classes/con.|-2604863570302238407[0] +final const val examples.classes/intCon // examples.classes/intCon|-4533540985615038728[0] + final fun (): kotlin/Int // examples.classes/intCon.|-7661624206875263703[0] final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] enum entry A // examples.classes/E.A|null[0] enum entry B // examples.classes/E.B|null[0] @@ -48,20 +69,60 @@ final enum class examples.classes/E : kotlin/Enum { // examp final val entries // examples.classes/E.entries|-5134227801081826149[0] final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|-6068527377476727729[0] } +final enum class examples.classes/EE : kotlin/Enum { // examples.classes/EE|null[0] + enum entry AA // examples.classes/EE.AA|null[0] + enum entry BB // examples.classes/EE.BB|null[0] + enum entry CC // examples.classes/EE.CC|null[0] + final fun valueOf(kotlin/String): examples.classes/EE // examples.classes/EE.valueOf|-4683474617854611729[0] + final fun values(): kotlin/Array // examples.classes/EE.values|-8715569000920726747[0] + final val entries // examples.classes/EE.entries|-5134227801081826149[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/EE.entries.|-6068527377476727729[0] +} final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|8042761629495509481[0] final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|6322333980269160703[0] final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|-9193388292326484960[0] final object examples.classes/O // examples.classes/O|null[0] +final object examples.classes/OO // examples.classes/OO|null[0] +final val examples.classes/a // examples.classes/a|-1200697420457237799[0] + final fun (): kotlin/Any // examples.classes/a.|6785176174175479410[0] +final val examples.classes/i // examples.classes/i|5014384761142332495[0] + final fun (): kotlin/Int // examples.classes/i.|6945482638966853621[0] final val examples.classes/l // examples.classes/l|3307215303229595169[0] final fun (): kotlin/Long // examples.classes/l.|3795442967620585[0] +final var examples.classes/d // examples.classes/d|5174763769109925331[0] + final fun (): kotlin/Double // examples.classes/d.|-6701718004621354461[0] + final fun (kotlin/Double) // examples.classes/d.|-6916287485929380915[0] final var examples.classes/r // examples.classes/r|-8117627916896159533[0] final fun (): kotlin/Float // examples.classes/r.|-7424184448774736572[0] final fun (kotlin/Float) // examples.classes/r.|9171637170963327464[0] open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] constructor () // examples.classes/A.|-5645683436151566731[0] } +open annotation class examples.classes/AA : kotlin/Annotation { // examples.classes/AA|null[0] + constructor () // examples.classes/AA.|-5645683436151566731[0] +} +open annotation class examples.classes/AAA : kotlin/Annotation { // examples.classes/AAA|null[0] + constructor () // examples.classes/AAA.|-5645683436151566731[0] +} open class examples.classes/OC { // examples.classes/OC|null[0] - constructor () // examples.classes/OC.|-5645683436151566731[0] + constructor (kotlin/Int) // examples.classes/OC.|-5182794243525578284[0] + constructor (kotlin/Long) // examples.classes/OC.|5217973964116651322[0] + constructor (kotlin/String) // examples.classes/OC.|1280618353163213788[0] final fun c() // examples.classes/OC.c|-2724918380551733646[0] + final val ix // examples.classes/OC.ix|5586787718776183610[0] + final fun (): kotlin/Int // examples.classes/OC.ix.|6412451035637198934[0] + final val iy // examples.classes/OC.iy|-4863546984470764583[0] + final fun (): kotlin/Long // examples.classes/OC.iy.|-4932930205028660775[0] + final val iz // examples.classes/OC.iz|-6916102702480625188[0] + final fun (): kotlin/String // examples.classes/OC.iz.|8935463596906645831[0] + final var x // examples.classes/OC.x|-8060530855978347579[0] + final fun (): kotlin/Int // examples.classes/OC.x.|1482705010654679335[0] + final fun (kotlin/Int) // examples.classes/OC.x.|-740209739415615559[0] + final var y // examples.classes/OC.y|3625903257357557171[0] + final fun (): kotlin/Int // examples.classes/OC.y.|-7902422373892128922[0] + final fun (kotlin/Int) // examples.classes/OC.y.|2154335559382602722[0] + final var z // examples.classes/OC.z|7549650372729116193[0] + final fun (): kotlin/Int // examples.classes/OC.z.|4925813204745917177[0] + final fun (kotlin/Int) // examples.classes/OC.z.|8486465404430625584[0] open fun o(): kotlin/Int // examples.classes/OC.o|-3264635847192431671[0] } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump index 5b397f2..cfcf695 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.guessed.linux.dump @@ -11,7 +11,11 @@ abstract class examples.classes/AC { // examples.classes/AC|null[0] constructor () // examples.classes/AC.|(){}[0] final fun b() // examples.classes/AC.b|b(){}[0] } +abstract fun interface examples.classes/FI { // examples.classes/FI|null[0] + abstract fun a() // examples.classes/FI.a|a(){}[0] +} abstract interface examples.classes/I // examples.classes/I|null[0] +abstract interface examples.classes/II // examples.classes/II|null[0] final class examples.classes/C { // examples.classes/C|null[0] constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] final fun m() // examples.classes/C.m|m(){}[0] @@ -28,17 +32,34 @@ final class examples.classes/D { // examples.classes/D|null[0] final val x // examples.classes/D.x|{}x[0] final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] } +final class examples.classes/IC : examples.classes/II { // examples.classes/IC|null[0] + constructor () // examples.classes/IC.|(){}[0] +} final class examples.classes/Outer { // examples.classes/Outer|null[0] constructor () // examples.classes/Outer.|(){}[0] final class Nested { // examples.classes/Outer.Nested|null[0] constructor () // examples.classes/Outer.Nested.|(){}[0] + final enum class NE : kotlin/Enum { // examples.classes/Outer.Nested.NE|null[0] + enum entry A // examples.classes/Outer.Nested.NE.A|null[0] + enum entry B // examples.classes/Outer.Nested.NE.B|null[0] + enum entry C // examples.classes/Outer.Nested.NE.C|null[0] + final fun valueOf(kotlin/String): examples.classes/Outer.Nested.NE // examples.classes/Outer.Nested.NE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/Outer.Nested.NE.values|values#static(){}[0] + final val entries // examples.classes/Outer.Nested.NE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/Outer.Nested.NE.entries.|#static(){}[0] + } final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] } + final inner class YetAnotherInner { // examples.classes/Outer.Nested.YetAnotherInner|null[0] + constructor () // examples.classes/Outer.Nested.YetAnotherInner.|(){}[0] + } } } final const val examples.classes/con // examples.classes/con|{}con[0] final fun (): kotlin/String // examples.classes/con.|(){}[0] +final const val examples.classes/intCon // examples.classes/intCon|{}intCon[0] + final fun (): kotlin/Int // examples.classes/intCon.|(){}[0] final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] enum entry A // examples.classes/E.A|null[0] enum entry B // examples.classes/E.B|null[0] @@ -48,20 +69,60 @@ final enum class examples.classes/E : kotlin/Enum { // examp final val entries // examples.classes/E.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] } +final enum class examples.classes/EE : kotlin/Enum { // examples.classes/EE|null[0] + enum entry AA // examples.classes/EE.AA|null[0] + enum entry BB // examples.classes/EE.BB|null[0] + enum entry CC // examples.classes/EE.CC|null[0] + final fun valueOf(kotlin/String): examples.classes/EE // examples.classes/EE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/EE.values|values#static(){}[0] + final val entries // examples.classes/EE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/EE.entries.|#static(){}[0] +} final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] final object examples.classes/O // examples.classes/O|null[0] +final object examples.classes/OO // examples.classes/OO|null[0] +final val examples.classes/a // examples.classes/a|{}a[0] + final fun (): kotlin/Any // examples.classes/a.|(){}[0] +final val examples.classes/i // examples.classes/i|{}i[0] + final fun (): kotlin/Int // examples.classes/i.|(){}[0] final val examples.classes/l // examples.classes/l|{}l[0] final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/d // examples.classes/d|{}d[0] + final fun (): kotlin/Double // examples.classes/d.|(){}[0] + final fun (kotlin/Double) // examples.classes/d.|(kotlin.Double){}[0] final var examples.classes/r // examples.classes/r|{}r[0] final fun (): kotlin/Float // examples.classes/r.|(){}[0] final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] constructor () // examples.classes/A.|(){}[0] } +open annotation class examples.classes/AA : kotlin/Annotation { // examples.classes/AA|null[0] + constructor () // examples.classes/AA.|(){}[0] +} +open annotation class examples.classes/AAA : kotlin/Annotation { // examples.classes/AAA|null[0] + constructor () // examples.classes/AAA.|(){}[0] +} open class examples.classes/OC { // examples.classes/OC|null[0] - constructor () // examples.classes/OC.|(){}[0] + constructor (kotlin/Int) // examples.classes/OC.|(kotlin.Int){}[0] + constructor (kotlin/Long) // examples.classes/OC.|(kotlin.Long){}[0] + constructor (kotlin/String) // examples.classes/OC.|(kotlin.String){}[0] final fun c() // examples.classes/OC.c|c(){}[0] + final val ix // examples.classes/OC.ix|{}ix[0] + final fun (): kotlin/Int // examples.classes/OC.ix.|(){}[0] + final val iy // examples.classes/OC.iy|{}iy[0] + final fun (): kotlin/Long // examples.classes/OC.iy.|(){}[0] + final val iz // examples.classes/OC.iz|{}iz[0] + final fun (): kotlin/String // examples.classes/OC.iz.|(){}[0] + final var x // examples.classes/OC.x|{}x[0] + final fun (): kotlin/Int // examples.classes/OC.x.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.x.|(kotlin.Int){}[0] + final var y // examples.classes/OC.y|{}y[0] + final fun (): kotlin/Int // examples.classes/OC.y.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.y.|(kotlin.Int){}[0] + final var z // examples.classes/OC.z|{}z[0] + final fun (): kotlin/Int // examples.classes/OC.z.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.z.|(kotlin.Int){}[0] open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump index c7bb38f..fdc5d28 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.klib.with.linux.dump @@ -11,7 +11,11 @@ abstract class examples.classes/AC { // examples.classes/AC|null[0] constructor () // examples.classes/AC.|(){}[0] final fun b() // examples.classes/AC.b|b(){}[0] } +abstract fun interface examples.classes/FI { // examples.classes/FI|null[0] + abstract fun a() // examples.classes/FI.a|a(){}[0] +} abstract interface examples.classes/I // examples.classes/I|null[0] +abstract interface examples.classes/II // examples.classes/II|null[0] final class examples.classes/C { // examples.classes/C|null[0] constructor (kotlin/Any) // examples.classes/C.|(kotlin.Any){}[0] final fun m() // examples.classes/C.m|m(){}[0] @@ -28,17 +32,34 @@ final class examples.classes/D { // examples.classes/D|null[0] final val x // examples.classes/D.x|{}x[0] final fun (): kotlin/Int // examples.classes/D.x.|(){}[0] } +final class examples.classes/IC : examples.classes/II { // examples.classes/IC|null[0] + constructor () // examples.classes/IC.|(){}[0] +} final class examples.classes/Outer { // examples.classes/Outer|null[0] constructor () // examples.classes/Outer.|(){}[0] final class Nested { // examples.classes/Outer.Nested|null[0] constructor () // examples.classes/Outer.Nested.|(){}[0] + final enum class NE : kotlin/Enum { // examples.classes/Outer.Nested.NE|null[0] + enum entry A // examples.classes/Outer.Nested.NE.A|null[0] + enum entry B // examples.classes/Outer.Nested.NE.B|null[0] + enum entry C // examples.classes/Outer.Nested.NE.C|null[0] + final fun valueOf(kotlin/String): examples.classes/Outer.Nested.NE // examples.classes/Outer.Nested.NE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/Outer.Nested.NE.values|values#static(){}[0] + final val entries // examples.classes/Outer.Nested.NE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/Outer.Nested.NE.entries.|#static(){}[0] + } final inner class Inner { // examples.classes/Outer.Nested.Inner|null[0] constructor () // examples.classes/Outer.Nested.Inner.|(){}[0] } + final inner class YetAnotherInner { // examples.classes/Outer.Nested.YetAnotherInner|null[0] + constructor () // examples.classes/Outer.Nested.YetAnotherInner.|(){}[0] + } } } final const val examples.classes/con // examples.classes/con|{}con[0] final fun (): kotlin/String // examples.classes/con.|(){}[0] +final const val examples.classes/intCon // examples.classes/intCon|{}intCon[0] + final fun (): kotlin/Int // examples.classes/intCon.|(){}[0] final enum class examples.classes/E : kotlin/Enum { // examples.classes/E|null[0] enum entry A // examples.classes/E.A|null[0] enum entry B // examples.classes/E.B|null[0] @@ -48,20 +69,60 @@ final enum class examples.classes/E : kotlin/Enum { // examp final val entries // examples.classes/E.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // examples.classes/E.entries.|#static(){}[0] } +final enum class examples.classes/EE : kotlin/Enum { // examples.classes/EE|null[0] + enum entry AA // examples.classes/EE.AA|null[0] + enum entry BB // examples.classes/EE.BB|null[0] + enum entry CC // examples.classes/EE.CC|null[0] + final fun valueOf(kotlin/String): examples.classes/EE // examples.classes/EE.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // examples.classes/EE.values|values#static(){}[0] + final val entries // examples.classes/EE.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // examples.classes/EE.entries.|#static(){}[0] +} final fun <#A: kotlin/Any?> examples.classes/consume(#A) // examples.classes/consume|consume(0:0){0§}[0] final fun examples.classes/testFun(): kotlin/Int // examples.classes/testFun|testFun(){}[0] final inline fun examples.classes/testInlineFun() // examples.classes/testInlineFun|testInlineFun(){}[0] final object examples.classes/O // examples.classes/O|null[0] +final object examples.classes/OO // examples.classes/OO|null[0] +final val examples.classes/a // examples.classes/a|{}a[0] + final fun (): kotlin/Any // examples.classes/a.|(){}[0] +final val examples.classes/i // examples.classes/i|{}i[0] + final fun (): kotlin/Int // examples.classes/i.|(){}[0] final val examples.classes/l // examples.classes/l|{}l[0] final fun (): kotlin/Long // examples.classes/l.|(){}[0] +final var examples.classes/d // examples.classes/d|{}d[0] + final fun (): kotlin/Double // examples.classes/d.|(){}[0] + final fun (kotlin/Double) // examples.classes/d.|(kotlin.Double){}[0] final var examples.classes/r // examples.classes/r|{}r[0] final fun (): kotlin/Float // examples.classes/r.|(){}[0] final fun (kotlin/Float) // examples.classes/r.|(kotlin.Float){}[0] open annotation class examples.classes/A : kotlin/Annotation { // examples.classes/A|null[0] constructor () // examples.classes/A.|(){}[0] } +open annotation class examples.classes/AA : kotlin/Annotation { // examples.classes/AA|null[0] + constructor () // examples.classes/AA.|(){}[0] +} +open annotation class examples.classes/AAA : kotlin/Annotation { // examples.classes/AAA|null[0] + constructor () // examples.classes/AAA.|(){}[0] +} open class examples.classes/OC { // examples.classes/OC|null[0] - constructor () // examples.classes/OC.|(){}[0] + constructor (kotlin/Int) // examples.classes/OC.|(kotlin.Int){}[0] + constructor (kotlin/Long) // examples.classes/OC.|(kotlin.Long){}[0] + constructor (kotlin/String) // examples.classes/OC.|(kotlin.String){}[0] final fun c() // examples.classes/OC.c|c(){}[0] + final val ix // examples.classes/OC.ix|{}ix[0] + final fun (): kotlin/Int // examples.classes/OC.ix.|(){}[0] + final val iy // examples.classes/OC.iy|{}iy[0] + final fun (): kotlin/Long // examples.classes/OC.iy.|(){}[0] + final val iz // examples.classes/OC.iz|{}iz[0] + final fun (): kotlin/String // examples.classes/OC.iz.|(){}[0] + final var x // examples.classes/OC.x|{}x[0] + final fun (): kotlin/Int // examples.classes/OC.x.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.x.|(kotlin.Int){}[0] + final var y // examples.classes/OC.y|{}y[0] + final fun (): kotlin/Int // examples.classes/OC.y.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.y.|(kotlin.Int){}[0] + final var z // examples.classes/OC.z|{}z[0] + final fun (): kotlin/Int // examples.classes/OC.z.|(){}[0] + final fun (kotlin/Int) // examples.classes/OC.z.|(kotlin.Int){}[0] open fun o(): kotlin/Int // examples.classes/OC.o|o(){}[0] } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt index 577e3f9..4d0cb4b 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/TopLevelDeclarations.kt @@ -6,30 +6,66 @@ public fun testFun(): Int = 42 public fun consume(arg: T) = Unit public inline fun testInlineFun() = Unit public const val con: String = "I'm a constant!" +public const val intCon: Int = 42 public val l: Long = 0xc001 +public val i: Int = 0xc002 public var r: Float = 3.14f +public var d: Double = 3.14 +public val a = Any() public annotation class A +public annotation class AA +public annotation class AAA + public interface I +public interface II +public fun interface FI { + fun a(): Unit +} + public data class D(val x: Int) public class C(public val v: Any) { public fun m() = Unit } +public class IC : II + public object O +public object OO + public enum class E { A, B, C } +public enum class EE { AA, BB, CC } + public abstract class AC { public abstract fun a() public fun b() = Unit } public open class OC { + constructor(i: Int) + constructor(l: Long) + constructor(s: String) + + public var x: Int = 1 + public var y: Int = 2 + public var z: Int = 3 + public val ix: Int = 4 + public val iy: Long = 5L + public val iz: String = "" public open fun o(): Int = 42 public fun c() = Unit } public class Outer { public class Nested { - public inner class Inner + public inner class Inner { + + } + + public inner class YetAnotherInner { + + } + + enum class NE { A, B, C } } } From 049044a1166c0f35ca847531727fb964d494bd98 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:34:20 +0200 Subject: [PATCH 21/37] don't cache dump task if api dir is invalid --- .../src/main/kotlin/tasks/BCVApiDumpTask.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt index e96e281..fa72906 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiDumpTask.kt @@ -34,17 +34,26 @@ constructor( "subproject '${project.path}'" } + init { + outputs.cacheIf { task -> + require(task is BCVApiDumpTask) + task.validateApiDir() + } + } + @TaskAction fun action() { - validateApiDir() + validateApiDir { msg -> error(msg) } updateDumpDir() } - private fun validateApiDir() { + private fun validateApiDir(ifInvalid: (msg: String) -> Unit = {}): Boolean { val projectDir = layout.projectDirectory.asFile.toPath().toAbsolutePath().normalize() val apiDir = projectDir.resolve(apiDirectory.get().asFile.toPath()).normalize() - require(apiDir.startsWith(projectDir)) { - /* language=text */ """ + val valid = apiDir.startsWith(projectDir) + if (!valid) { + ifInvalid( + /* language=text */ """ |Error: Invalid output apiDirectory | |apiDirectory is set to a custom directory, outside of the current project directory. @@ -55,7 +64,9 @@ constructor( |Project directory: ${projectDir.invariantSeparatorsPathString} |apiDirectory: ${apiDir.invariantSeparatorsPathString} """.trimMargin() + ) } + return valid } private fun updateDumpDir() { From f7bd2da83b25350906a719d17e633d3b167cae2c Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:34:38 +0200 Subject: [PATCH 22/37] tidying... --- .../src/main/kotlin/tasks/BCVApiCheckTask.kt | 4 +- .../main/kotlin/tasks/BCVApiGenerateTask.kt | 73 +++++++++++-------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index 1efb0d2..261ef3a 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -69,8 +69,8 @@ constructor( val projectApiDir = projectApiDir.orNull ?: error( """ - Expected folder with API declarations '${expectedApiDirPath.get()}' does not exist. - Please ensure that task '$apiDumpTaskPath' was executed in order to get API dump to compare the build against. + Expected folder with API declarations '${expectedApiDirPath.get()}' does not exist. + Please ensure that task '$apiDumpTaskPath' was executed in order to get API dump to compare the build against. """.trimIndent() ) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 677db68..0eb9d32 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -9,7 +9,6 @@ import java.io.File import javax.inject.Inject import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileSystemOperations import org.gradle.api.model.ObjectFactory @@ -58,24 +57,20 @@ constructor( abstract val outputApiBuildDir: DirectoryProperty @get:LocalState - val supportedTargetsDir: File get() = temporaryDir.resolve("klib-supported") + internal val workDir: File get() = temporaryDir - @get:LocalState - val unsupportedTargetsDir: File get() = temporaryDir.resolve("klib-unsupported") + private val klibTargetsDir = object { + private val klibDir: File get() = workDir.resolve("klib") + val supported: File get() = klibDir.resolve("supported") + val unsupported: File get() = klibDir.resolve("unsupported") + val extracted: File get() = klibDir.resolve("extracted") + } @TaskAction fun generate() { val workQueue = prepareWorkQueue() - val outputApiBuildDir = outputApiBuildDir.get() - fs.delete { delete(outputApiBuildDir) } - outputApiBuildDir.asFile.mkdirs() - - fs.delete { delete(supportedTargetsDir) } - supportedTargetsDir.mkdirs() - - fs.delete { delete(unsupportedTargetsDir) } - unsupportedTargetsDir.mkdirs() + prepareDirectories() logger.lifecycle("[$path] got ${targets.size} targets : ${targets.joinToString { it.name }}") @@ -84,7 +79,7 @@ constructor( generateJvmTargets( workQueue = workQueue, jvmTargets = enabledTargets.withType(), - outputApiBuildDir = outputApiBuildDir, + outputApiBuildDir = outputApiBuildDir.get().asFile, ) // TODO log when klib file doesn't exist @@ -96,7 +91,7 @@ constructor( generateKLibTargets( workQueue = workQueue, klibTargets = klibTargets, - outputApiBuildDir = outputApiBuildDir, + outputApiBuildDir = outputApiBuildDir.get().asFile, ) // The worker queue is asynchronous, so any code here won't wait for the workers to finish. @@ -113,11 +108,26 @@ constructor( } } + private fun prepareDirectories() { + val outputApiBuildDir = outputApiBuildDir.get() + fs.delete { delete(outputApiBuildDir) } + outputApiBuildDir.asFile.mkdirs() + + fs.delete { delete(klibTargetsDir.supported) } + klibTargetsDir.supported.mkdirs() + + fs.delete { delete(klibTargetsDir.unsupported) } + klibTargetsDir.unsupported.mkdirs() + + fs.delete { delete(klibTargetsDir.extracted) } + klibTargetsDir.extracted.mkdirs() + } + //region JVM private fun generateJvmTargets( workQueue: WorkQueue, - outputApiBuildDir: Directory, + outputApiBuildDir: File, jvmTargets: Collection, ) { if (jvmTargets.isEmpty()) { @@ -131,12 +141,12 @@ constructor( val outputDir = if (jvmTargets.size == 1) { outputApiBuildDir } else { - outputApiBuildDir.dir(target.platformType) + outputApiBuildDir.resolve(target.platformType) } workQueue.submit( target = target, - outputDir = outputDir.asFile, + outputDir = outputDir, ) } } @@ -173,10 +183,13 @@ constructor( private fun generateKLibTargets( workQueue: WorkQueue, - outputApiBuildDir: Directory, + outputApiBuildDir: File, klibTargets: Collection, ) { - if (klibTargets.isEmpty()) return + if (klibTargets.isEmpty()) { + logger.info("[$path] No enabled KLib targets") + return + } logger.lifecycle("[$path] generating ${klibTargets.size} KLib targets : ${klibTargets.joinToString { it.name }}") val (supportedKLibTargets, unsupportedKLibTargets) = @@ -188,10 +201,10 @@ constructor( workQueue.await() val allTargetDumpFiles = - supportedTargetsDir.walk().filter { it.isFile }.toSet() union - unsupportedTargetsDir.walk().filter { it.isFile }.toSet() + klibTargetsDir.supported.walk().filter { it.isFile }.toSet() union + klibTargetsDir.unsupported.walk().filter { it.isFile }.toSet() - mergeDumpFiles(workQueue, allTargetDumpFiles, outputApiBuildDir.asFile) + mergeDumpFiles(workQueue, allTargetDumpFiles, outputApiBuildDir) } private fun generateSupportedKLibTargets( @@ -200,17 +213,17 @@ constructor( ) { logger.lifecycle("[$path] generating ${supportedKLibTargets.size} supported KLib targets : ${supportedKLibTargets.joinToString { it.name }}") - val supportedKLibGenDuration = measureTime { + val duration = measureTime { supportedKLibTargets.forEach { target -> workQueue.submit( target = target, - outputDir = supportedTargetsDir, + outputDir = klibTargetsDir.supported, ) } workQueue.await() } - logger.lifecycle("[$path] finished generating supported KLib targets in $supportedKLibGenDuration") + logger.lifecycle("[$path] finished generating supported KLib targets in $duration") } private fun generateUnsupportedKLibTargets( @@ -219,19 +232,19 @@ constructor( ) { logger.lifecycle("[$path] generating ${unsupportedKLibTargets.size} unsupported KLib targets : ${unsupportedKLibTargets.joinToString { it.name }}") - val unsupportedKLibGenDuration = measureTime { + val duration = measureTime { unsupportedKLibTargets.forEach { target -> workQueue.inferKLib( target = target, - supportedTargetDumpFiles = supportedTargetsDir.walk().filter { it.isFile }.toSet(), + supportedTargetDumpFiles = klibTargetsDir.supported.walk().filter { it.isFile }.toSet(), extantApiDumpFile = extantApiDumpDir.asFile.orNull?.walk()?.filter { it.isFile } ?.firstOrNull(), - outputDir = unsupportedTargetsDir, + outputDir = klibTargetsDir.unsupported, ) } } - logger.lifecycle("[$path] finished generating unsupported KLib targets in $unsupportedKLibGenDuration") + logger.lifecycle("[$path] finished generating unsupported KLib targets in $duration") } From 1cdbcc07417c29fffb79d5c251cd42df0142bb45 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:35:24 +0200 Subject: [PATCH 23/37] enable build cache by default in tests (so they are faster!) --- gradle.properties | 2 -- .../validation/test/DefaultConfigTests.kt | 9 +++--- .../validation/test/IgnoredClassesTests.kt | 5 ++-- .../validation/test/JvmProjectTests.kt | 3 +- .../validation/test/KlibVerificationTests.kt | 4 +-- .../test/MultiPlatformSingleJvmTargetTest.kt | 4 +-- .../validation/test/MultipleJvmTargetsTest.kt | 5 ++-- .../validation/test/NonPublicMarkersTest.kt | 3 +- .../validation/test/OutputDirectoryTests.kt | 7 ++--- .../src/testFixtures/kotlin/api/TestDsl.kt | 2 +- .../kotlin/kotestGradleAssertions.kt | 30 +++++++++++++++---- 11 files changed, 46 insertions(+), 28 deletions(-) diff --git a/gradle.properties b/gradle.properties index 6fb6d5f..c3d299b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,8 @@ org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -Xmx4g -XX:MaxMetaspaceSize=1g org.gradle.caching=true - org.gradle.configuration-cache=true org.gradle.configuration-cache-problems=warn - org.gradle.parallel=true org.gradle.welcome=never diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt index abc63fc..ac56e0d 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt @@ -5,8 +5,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import io.kotest.matchers.comparables.shouldBeEqualComparingTo import io.kotest.matchers.file.shouldBeEmpty import io.kotest.matchers.string.shouldContain -import org.gradle.testkit.runner.TaskOutcome.FAILED -import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.* import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -160,7 +159,7 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) assertTrue( rootProjectApiDump.exists(), @@ -187,7 +186,7 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) val apiDumpFile = rootProjectDir.resolve("api/testproject.api") assertTrue(apiDumpFile.exists(), "api dump file ${apiDumpFile.path} should exist") @@ -217,7 +216,7 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) assertTrue(rootProjectApiDump.exists(), "api dump file should exist") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt index 9403873..6944983 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/IgnoredClassesTests.kt @@ -4,6 +4,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveRunTask import io.kotest.matchers.comparables.shouldBeEqualComparingTo +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -78,7 +79,7 @@ internal class IgnoredClassesTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) assertTrue(rootProjectApiDump.exists(), "api dump file should exist") @@ -107,7 +108,7 @@ internal class IgnoredClassesTests : BaseKotlinGradleTest() { } runner.build().apply { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) assertTrue(rootProjectApiDump.exists(), "api dump file should exist") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt index e5a9f1a..defe40d 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/JvmProjectTests.kt @@ -5,6 +5,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build import dev.adamko.kotlin.binary_compatibility_validator.test.utils.invariantNewlines import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Test @@ -24,7 +25,7 @@ class JvmProjectTests : BaseKotlinGradleTest() { } } runner.build { - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) val expected = readResourceFile("/examples/classes/GeneratedSources.dump") rootProjectApiDump.readText().invariantNewlines() shouldBe expected diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 115e4d5..c6b7b51 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -692,7 +692,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) rootProjectApiDump.parentFile.shouldBeEmptyDirectory() } } @@ -767,7 +767,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { projectName: String = "testproject", ) { withClue(output) { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) val generatedDump = rootProjectAbiDump(projectName) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt index e9c80a0..f5e6003 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultiPlatformSingleJvmTargetTest.kt @@ -115,7 +115,7 @@ internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { } runner.build { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) val mainExpectedApi = """ |${readResourceFile("/examples/classes/Subsub1Class.dump").trim()} @@ -145,7 +145,7 @@ internal class MultiPlatformSingleJvmTargetTest : BaseKotlinGradleTest() { runner.build { // shouldHaveTaskWithOutcome(":jvmApiDump", SKIPPED) // shouldHaveTaskWithOutcome(":apiDump", UP_TO_DATE) - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt index 548f13e..9763888 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt @@ -7,8 +7,7 @@ import io.kotest.matchers.file.shouldBeAFile import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import java.io.File -import org.gradle.testkit.runner.TaskOutcome.FAILED -import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.* import org.junit.jupiter.api.Test internal class MultipleJvmTargetsTest : BaseKotlinGradleTest() { @@ -117,7 +116,7 @@ internal class MultipleJvmTargetsTest : BaseKotlinGradleTest() { } runner.build { withClue(output) { - shouldHaveRunTask(":apiDump", SUCCESS) + shouldHaveRunTask(":apiDump", SUCCESS, FROM_CACHE) val anotherExpectedApi = readResourceFile("/examples/classes/Subsub1Class.dump") anotherApiDump.shouldBeAFile() diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt index f021aea..e7f73f1 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/NonPublicMarkersTest.kt @@ -5,6 +5,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.build import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTaskWithOutcome import io.kotest.matchers.file.shouldExist import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -97,7 +98,7 @@ class NonPublicMarkersTest : BaseKotlinGradleTest() { runner .forwardOutput() .build { - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) rootProjectApiDump.shouldExist() diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt index 59fde83..6cadb0d 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/OutputDirectoryTests.kt @@ -8,8 +8,7 @@ import io.kotest.assertions.withClue import io.kotest.matchers.file.shouldExist import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain -import org.gradle.testkit.runner.TaskOutcome.FAILED -import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.* import org.junit.jupiter.api.Test class OutputDirectoryTests : BaseKotlinGradleTest() { @@ -35,7 +34,7 @@ class OutputDirectoryTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) val dumpFile = rootProjectDir.resolve("custom/${rootProjectDir.name}.api") dumpFile.shouldExist() @@ -95,7 +94,7 @@ class OutputDirectoryTests : BaseKotlinGradleTest() { } runner.build { - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) val dumpFile = rootProjectDir.resolve("validation/api/${rootProjectDir.name}.api") dumpFile.shouldExist() diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt index 01f64e1..fcfe3f5 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt @@ -221,7 +221,7 @@ class Runner { var configurationCache: Boolean = true var rerunTasks: Boolean = false var rerunTask: Boolean = false - var buildCache: Boolean = false + var buildCache: Boolean = true var stacktrace: Boolean = true var continues: Boolean = true diff --git a/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt b/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt index 3d13342..c88aa15 100644 --- a/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt +++ b/modules/bcv-gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt @@ -34,14 +34,18 @@ infix fun BuildResult?.shouldHaveRunTask(taskPath: String): BuildTask { return this?.task(taskPath)!! } -/** Assert that a task ran, with an [expected outcome][expectedOutcome]. */ +/** Assert that a task ran, with any [expected outcome][expectedOutcomes]. */ fun BuildResult?.shouldHaveRunTask( taskPath: String, - expectedOutcome: TaskOutcome + vararg expectedOutcomes: TaskOutcome ): BuildTask { this should haveTask(taskPath) val task = this?.task(taskPath)!! - task should haveOutcome(expectedOutcome) + if (expectedOutcomes.size == 1) { + task should haveOutcome(expectedOutcomes.single()) + } else { + task should haveAnyOutcome(expectedOutcomes.toList()) + } return task } @@ -71,6 +75,10 @@ infix fun BuildTask?.shouldHaveOutcome(outcome: TaskOutcome) { this should haveOutcome(outcome) } +fun BuildTask?.shouldHaveAnyOutcome(vararg outcomes: TaskOutcome) { + this should haveAnyOutcome(outcomes.toList()) +} + infix fun BuildTask?.shouldNotHaveOutcome(outcome: TaskOutcome) { this shouldNot haveOutcome(outcome) } @@ -84,6 +92,18 @@ private fun haveOutcome(outcome: TaskOutcome): Matcher = ) } -fun BuildResult.shouldHaveTaskWithOutcome(taskPath: String, outcome: TaskOutcome) { - this shouldHaveRunTask taskPath shouldHaveOutcome outcome +private fun haveAnyOutcome(outcomes: List): Matcher = + neverNullMatcher { value -> + MatcherResult( + value.outcome in outcomes, + { "Task ${value.path} should have any outcome ${outcomes.sorted()}, but was ${value.outcome}" }, + { "Task ${value.path} should not have any outcome ${outcomes.sorted()}, but was ${value.outcome}" }, + ) + } + +fun BuildResult.shouldHaveTaskWithOutcome( + taskPath: String, + vararg outcomes: TaskOutcome +) { + (this shouldHaveRunTask taskPath).shouldHaveAnyOutcome(outcomes = outcomes) } From cd5190026bdb7f5ba3420228ac5687b39716a110 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:35:52 +0200 Subject: [PATCH 24/37] rm old assertion --- .../kotlin/kotlinx/validation/test/KlibVerificationTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index c6b7b51..9196518 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -431,7 +431,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { - shouldHaveTaskWithOutcome(":linuxArm64ApiInfer", FAILED) +// shouldHaveTaskWithOutcome(":linuxArm64ApiInfer", FAILED) output shouldContain "The target linuxArm64 is not supported by the host compiler and there are no targets similar to linuxArm64 to infer a dump from it." } } From 402a3f43452608daa8836cc550ea0eccc4f77f91 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:35:56 +0200 Subject: [PATCH 25/37] update dump --- modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index a49a87c..50b5eb2 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -153,9 +153,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract fun getOutputApiBuildDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getProjectName ()Lorg/gradle/api/provider/Property; public abstract fun getRuntimeClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; - public final fun getSupportedTargetsDir ()Ljava/io/File; public final fun getTargets ()Lorg/gradle/api/NamedDomainObjectContainer; - public final fun getUnsupportedTargetsDir ()Ljava/io/File; } public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVDefaultTask : org/gradle/api/DefaultTask { @@ -173,4 +171,3 @@ public final class org/gradle/kotlin/dsl/BcvGradleDslAccessorsKt { public static final fun binaryCompatibilityValidator (Lorg/gradle/api/initialization/Settings;Lkotlin/jvm/functions/Function1;)V public static final fun getBinaryCompatibilityValidator (Lorg/gradle/api/initialization/Settings;)Ldev/adamko/kotlin/binary_compatibility_validator/BCVSettingsPlugin$Extension; } - From eb6c79503311a50adc1e1e2d9e33231a755dbb66 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:41:58 +0200 Subject: [PATCH 26/37] sort BCVTargets --- modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api | 10 ++++++++-- .../src/main/kotlin/targets/BCVJvmTarget.kt | 2 +- .../src/main/kotlin/targets/BCVTarget.kt | 12 +++++++++++- .../src/main/kotlin/tasks/BCVApiGenerateTask.kt | 4 ++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index 50b5eb2..ede0870 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -83,7 +83,6 @@ public abstract interface annotation class dev/adamko/kotlin/binary_compatibilit public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVJvmTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget, org/gradle/api/Named { public abstract fun getInputClasses ()Lorg/gradle/api/file/ConfigurableFileCollection; public abstract fun getInputJar ()Lorg/gradle/api/file/RegularFileProperty; - public final fun getObjects ()Lorg/gradle/api/model/ObjectFactory; public final fun getPlatformType ()Ljava/lang/String; } @@ -95,8 +94,11 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/B public final fun getTargetName ()Ljava/lang/String; } -public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec, java/io/Serializable, org/gradle/api/Named, org/gradle/api/plugins/ExtensionAware { +public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget : dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec, java/io/Serializable, java/lang/Comparable, org/gradle/api/Named, org/gradle/api/plugins/ExtensionAware { + public static final field Companion Ldev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget$Companion; public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun compareTo (Ldev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget;)I + public synthetic fun compareTo (Ljava/lang/Object;)I public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; public abstract fun getIgnoredMarkers ()Lorg/gradle/api/provider/SetProperty; @@ -107,6 +109,9 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/targets/B public abstract fun getPublicPackages ()Lorg/gradle/api/provider/SetProperty; } +public final class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTarget$Companion { +} + public abstract interface class dev/adamko/kotlin/binary_compatibility_validator/targets/BCVTargetBaseSpec : java/io/Serializable { public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/SetProperty; @@ -171,3 +176,4 @@ public final class org/gradle/kotlin/dsl/BcvGradleDslAccessorsKt { public static final fun binaryCompatibilityValidator (Lorg/gradle/api/initialization/Settings;Lkotlin/jvm/functions/Function1;)V public static final fun getBinaryCompatibilityValidator (Lorg/gradle/api/initialization/Settings;)Ldev/adamko/kotlin/binary_compatibility_validator/BCVSettingsPlugin$Extension; } + diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt index 129db08..71bb32b 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVJvmTarget.kt @@ -23,7 +23,7 @@ constructor( @get:Internal val platformType: String, @get:Internal - val objects: ObjectFactory + internal val objects: ObjectFactory ) : BCVTarget(platformType), Named { @get:Classpath diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt index fe18e00..ab7609d 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTarget.kt @@ -15,7 +15,7 @@ sealed class BCVTarget @Inject constructor( private val named: String, -) : BCVTargetBaseSpec, Serializable, Named, ExtensionAware { +) : BCVTargetBaseSpec, Serializable, Named, ExtensionAware, Comparable { @get:Input @get:Optional @@ -60,6 +60,16 @@ constructor( @get:Optional abstract override val ignoredClasses: SetProperty + override fun compareTo(other: BCVTarget): Int = + this.string().compareTo(other.string()) + @Input override fun getName(): String = named + + companion object { + private fun BCVTarget.string(): String = when (this) { + is BCVJvmTarget -> "BCVJvmTarget(${named})" + is BCVKLibTarget -> "BCVKLibTarget(${named})" + } + } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 0eb9d32..67b87c6 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -78,7 +78,7 @@ constructor( generateJvmTargets( workQueue = workQueue, - jvmTargets = enabledTargets.withType(), + jvmTargets = enabledTargets.withType().sorted(), outputApiBuildDir = outputApiBuildDir.get().asFile, ) @@ -86,7 +86,7 @@ constructor( // TODO log warning when klibFile has >1 file val klibTargets = enabledTargets.withType() .filter { it.klibFile.singleOrNull()?.exists() == true } - .sortedBy { it.targetName } + .sorted() generateKLibTargets( workQueue = workQueue, From 0095fffc23364451186ebca7361f9ba09ce0308e Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:22:21 +0200 Subject: [PATCH 27/37] set gradle properties using `gradle.properties`, add `--parallel` --- .../src/testFixtures/kotlin/api/TestDsl.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt index fcfe3f5..89ff213 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt @@ -53,6 +53,10 @@ fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { } } + baseKotlinScope.gradleProperties { + addLine("org.gradle.jvmargs=" + baseKotlinScope.runner.gradleJvmArgs.joinToString(" ")) + } + return GradleRunner.create() .withProjectDir(rootProjectDir) .withGradleVersion(baseKotlinScope.runner.gradleVersion) @@ -218,12 +222,18 @@ class AppendableScope(val filePath: String) { class Runner { var gradleVersion: String = minimumGradleTestVersion + /** JVM args used by Gradle. Set as `org.gradle.jvmargs` in `gradle.properties`. */ + val gradleJvmArgs: MutableSet = mutableSetOf( + "-Dfile.encoding=UTF-8", + "org.gradle.welcome=never", + ) var configurationCache: Boolean = true var rerunTasks: Boolean = false var rerunTask: Boolean = false var buildCache: Boolean = true var stacktrace: Boolean = true var continues: Boolean = true + var parallel: Boolean = true val arguments: MutableList = mutableListOf() @@ -235,6 +245,7 @@ class Runner { if (rerunTask) add("--rerun-task") if (stacktrace) add("--stacktrace") if (continues) add("--continue") + if (parallel) add("--parallel") }.distinct() } From 3056ff4b57533f1e2db126c6f902406d832f2729 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:22:48 +0200 Subject: [PATCH 28/37] use stable temp dir for test projects --- .../build.gradle.kts | 8 +-- .../src/testFixtures/kotlin/TempDirFactory.kt | 52 +++++++++++++++++++ .../kotlin/api/BaseKotlinGradleTest.kt | 6 ++- .../testFixtures/kotlin/systemProperties.kt | 14 +++++ 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/TempDirFactory.kt create mode 100644 modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/systemProperties.kt diff --git a/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts b/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts index 19788d8..70a0893 100644 --- a/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts +++ b/modules/bcv-gradle-plugin-functional-tests/build.gradle.kts @@ -59,13 +59,13 @@ testing.suites { "devMavenRepoDir", devPublish.devMavenRepo.asFile.get().invariantSeparatorsPath, ) - val tmpDir = layout.buildDirectory.dir("test-temp").get().asFile + val testTempDir = layout.buildDirectory.dir("test-temp").get().asFile systemProperty( - "java.io.tmpdir", - tmpDir.absoluteFile.invariantSeparatorsPath + "testTempDir", + testTempDir.invariantSeparatorsPath ) doFirst { - tmpDir.mkdirs() + testTempDir.mkdirs() } } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/TempDirFactory.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/TempDirFactory.kt new file mode 100644 index 0000000..adf03f0 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/TempDirFactory.kt @@ -0,0 +1,52 @@ +package dev.adamko.kotlin.binary_compatibility_validator.test.utils + +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.deleteRecursively +import kotlin.jvm.optionals.getOrNull +import org.junit.jupiter.api.extension.AnnotatedElementContext +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.io.TempDirFactory + +/** + * Create temp dir with a stable name based on the current test name. + * + * @see TempDirFactory.Standard + */ +class TempTestNameDirFactory : TempDirFactory { + + @OptIn(ExperimentalPathApi::class) + override fun createTempDirectory( + elementContext: AnnotatedElementContext, + extensionContext: ExtensionContext, + ): Path { + + // convert `${TestClass.fqn}.${testDisplayName}` + // to file path, e.g. `f/q/n/TestClass/test-display-name` + val path = listOfNotNull( + extensionContext.testClass.getOrNull()?.canonicalName, + extensionContext.displayName, + ).joinToString("/") { segment -> + segment + .split(".") + .joinToString(separator = "/") { + it + .map { c -> if (c.isLetterOrDigit()) c else '-' } + .dropLastWhile { c -> !c.isLetterOrDigit() } + .dropWhile { c -> !c.isLetterOrDigit() } + .joinToString("") + } + } + + val dir = testTempDir.resolve(path) + dir.deleteRecursively() + dir.createDirectories() + return dir + } + + companion object { + private val testTempDir: Path by systemProperty(::Path) + } +} diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt index 70c92f9..833e325 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt @@ -1,11 +1,15 @@ package dev.adamko.kotlin.binary_compatibility_validator.test.utils.api +import dev.adamko.kotlin.binary_compatibility_validator.test.utils.TempTestNameDirFactory import java.io.File import org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS import org.junit.jupiter.api.io.TempDir open class BaseKotlinGradleTest { - @TempDir(cleanup = ON_SUCCESS) + @TempDir( + factory = TempTestNameDirFactory::class, + cleanup = ON_SUCCESS, + ) lateinit var testTempDir: File val rootProjectDir: File get() = testTempDir.resolve("bcv-test-project") diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/systemProperties.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/systemProperties.kt new file mode 100644 index 0000000..acb2150 --- /dev/null +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/systemProperties.kt @@ -0,0 +1,14 @@ +package dev.adamko.kotlin.binary_compatibility_validator.test.utils + +import kotlin.properties.ReadOnlyProperty + + +internal fun systemProperty( + convert: (String) -> T +): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val value = requireNotNull(System.getProperty(property.name)) { + "system property ${property.name} is unavailable" + } + convert(value) + } From 64298add901de17cdbbfc159d120f5b8102a1809 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:23:04 +0200 Subject: [PATCH 29/37] bump memory for Apple targets test --- gradle.properties | 2 +- .../kotlin/kotlinx/validation/test/KlibVerificationTests.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c3d299b..464a7dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -Xmx4g -XX:MaxMetaspaceSize=1g +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g -XX:+HeapDumpOnOutOfMemoryError org.gradle.caching=true org.gradle.configuration-cache=true diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 9196518..9a7328b 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -302,6 +302,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner { arguments.add(":apiCheck") + gradleJvmArgs += "-Xmx4g" } } From 878bfb861aabe80ae0092be31f7cda75a549fedc Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:23:19 +0200 Subject: [PATCH 30/37] fix test assertion to permit caching --- .../kotlin/kotlinx/validation/test/PublicMarkersTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt index 8ec9d81..809054b 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/PublicMarkersTest.kt @@ -8,6 +8,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.test.utils.shouldHaveTas import io.kotest.matchers.file.shouldBeAFile import io.kotest.matchers.file.shouldExist import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Test @@ -99,7 +100,7 @@ class PublicMarkersTest : BaseKotlinGradleTest() { } runner.build { - shouldHaveTaskWithOutcome(":apiDump", SUCCESS) + shouldHaveTaskWithOutcome(":apiDump", SUCCESS, FROM_CACHE) rootProjectApiDump.shouldExist() rootProjectApiDump.shouldBeAFile() From 04fb1d882b31a14d470fda3c1faf4736dbcb16db Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:24:39 +0200 Subject: [PATCH 31/37] tidy logging/docs --- .../src/main/kotlin/BCVProjectPlugin.kt | 2 +- .../src/main/kotlin/tasks/BCVApiCheckTask.kt | 2 +- .../workers/KLibInferSignaturesWorker.kt | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index b3b8d46..312cb74 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -165,7 +165,7 @@ constructor( project.tasks.withType().configureEach { bcvEnabled.convention(extension.enabled) - onlyIf("BCV is disabled") { bcvEnabled.get() } + onlyIf("BCV is enabled") { bcvEnabled.get() } } project.tasks.withType().configureEach { diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index 261ef3a..4c58eb0 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -124,7 +124,7 @@ constructor( | |$diffText | - |You can run '$apiDumpTaskPath' task to overwrite API declarations + |You can run '$apiDumpTaskPath' task to overwrite API declarations. """.trimMargin() ) } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt index ebc5af1..2eb0628 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibInferSignaturesWorker.kt @@ -17,6 +17,21 @@ import org.gradle.api.provider.Property import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters +/** + * Infers a possible KLib ABI dump for an unsupported target. + * + * To infer a dump, walk up the default targets hierarchy tree starting from the unsupported + * target until it finds a node corresponding to a group containing least one supported target. + * + * After that, dumps generated for such supported targets are merged and declarations that are + * common to all of them are considered as a common ABI that most likely will be shared by the + * unsupported target. + * + * At the next step, if a project contains an old dump, declarations specific to the unsupported + * target are copied from it and merged into the common ABI extracted previously. + * + * The resulting dump is then used as an inferred dump for the unsupported target. + */ @BCVInternalApi @BCVExperimentalApi @OptIn(ExperimentalBCVApi::class) @@ -38,6 +53,8 @@ abstract class KLibInferSignaturesWorker : WorkAction } + private val taskPath: String = parameters.taskPath.get() + override fun execute() { // Find a set of supported targets that are closer to unsupported target in the hierarchy. // Note that dumps are stored using configurable name, but grouped by the canonical target name. @@ -60,7 +77,7 @@ abstract class KLibInferSignaturesWorker : WorkAction Date: Sun, 2 Jun 2024 13:26:04 +0200 Subject: [PATCH 32/37] change strict klib validation so it's not configurable per target, but per project --- .../bcv-gradle-plugin/api/bcv-gradle-plugin.api | 6 ++++++ .../src/main/kotlin/BCVKLibConventions.kt | 17 +++++++++++++++++ .../src/main/kotlin/BCVProjectExtension.kt | 5 ++--- .../src/main/kotlin/BCVProjectPlugin.kt | 5 ++++- .../main/kotlin/targets/BCVTargetDefaults.kt | 5 +++-- .../main/kotlin/targets/KLibValidationSpec.kt | 10 ---------- .../src/main/kotlin/tasks/BCVApiGenerateTask.kt | 3 +++ 7 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 modules/bcv-gradle-plugin/src/main/kotlin/BCVKLibConventions.kt diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index ede0870..87e8012 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -1,3 +1,8 @@ +public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVKLibConventions : dev/adamko/kotlin/binary_compatibility_validator/targets/KLibValidationSpec { + public fun ()V + public abstract fun getStrictValidation ()Lorg/gradle/api/provider/Property; +} + public abstract class dev/adamko/kotlin/binary_compatibility_validator/BCVPlugin : org/gradle/api/Plugin { public static final field API_CHECK_TASK_NAME Ljava/lang/String; public static final field API_DIR Ljava/lang/String; @@ -158,6 +163,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract fun getOutputApiBuildDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getProjectName ()Lorg/gradle/api/provider/Property; public abstract fun getRuntimeClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getStrictKLibTargetValidation ()Lorg/gradle/api/provider/Property; public final fun getTargets ()Lorg/gradle/api/NamedDomainObjectContainer; } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVKLibConventions.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVKLibConventions.kt new file mode 100644 index 0000000..5d10aa8 --- /dev/null +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVKLibConventions.kt @@ -0,0 +1,17 @@ +package dev.adamko.kotlin.binary_compatibility_validator + +import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi +import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibValidationSpec +import org.gradle.api.provider.Property + +@OptIn(BCVExperimentalApi::class) +abstract class BCVKLibConventions : KLibValidationSpec { + + /** + * Fail validation if some build targets are not supported by the host compiler. + * + * By default, ABI dumped only for supported files will be validated. This option makes + * validation behavior stricter and treats having unsupported targets as an error. + */ + abstract val strictValidation: Property +} diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt index 5907ab2..4ac4d86 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectExtension.kt @@ -3,7 +3,6 @@ package dev.adamko.kotlin.binary_compatibility_validator import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTargetBaseSpec -import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibValidationSpec import javax.inject.Inject import org.gradle.api.file.DirectoryProperty import org.gradle.api.model.ObjectFactory @@ -46,9 +45,9 @@ constructor( abstract override val ignoredClasses: SetProperty @BCVExperimentalApi - val klib: KLibValidationSpec = + val klib: BCVKLibConventions = extensions.adding("klib") { - objects.newInstance(KLibValidationSpec::class) + objects.newInstance(BCVKLibConventions::class) } /** diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index 312cb74..f9c8026 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -100,7 +100,7 @@ constructor( enabled.convention(extension.klib.enabled) signatureVersion.convention(extension.klib.signatureVersion) - strictValidation.convention(extension.klib.strictValidation) +// strictValidation.convention(extension.klib.strictValidation) supportedByCurrentHost.convention(false) } @@ -179,6 +179,9 @@ constructor( outputApiBuildDir.convention(layout.buildDirectory.dir("bcv-api")) projectName.convention(extension.projectName) + @OptIn(BCVExperimentalApi::class) + strictKLibTargetValidation.convention(extension.klib.strictValidation) + onlyIf("Must have at least one target") { targets.isNotEmpty() } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt index 6e8dfbd..5919bc3 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/BCVTargetDefaults.kt @@ -1,5 +1,6 @@ package dev.adamko.kotlin.binary_compatibility_validator.targets +import dev.adamko.kotlin.binary_compatibility_validator.BCVKLibConventions import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.adding @@ -43,8 +44,8 @@ constructor( abstract override val ignoredClasses: SetProperty @BCVExperimentalApi - val klib: KLibValidationSpec = + val klib: BCVKLibConventions = extensions.adding("klib") { - objects.newInstance(KLibValidationSpec::class) + objects.newInstance(BCVKLibConventions::class) } } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt index 23db368..eb82bc2 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/targets/KLibValidationSpec.kt @@ -42,14 +42,4 @@ interface KLibValidationSpec { */ fun signatureVersion(version: Int): Unit = signatureVersion.set(KLibSignatureVersion.of(version)) - - /** - * Fail validation if some build targets are not supported by the host compiler. - * - * By default, ABI dumped only for supported files will be validated. This option makes - * validation behavior stricter and treats having unsupported targets as an error. - */ - @get:Input - @get:Optional - val strictValidation: Property } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 67b87c6..892d0b0 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -44,6 +44,9 @@ constructor( @get:Input abstract val projectName: Property + @get:Input + abstract val strictKLibTargetValidation: Property + /** * A directory containing a copy of any currently existing API Dump files. * Provided by [BCVApiGeneratePreparationTask]. From d994595a79478097ab24e11446306e296b78d085 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:26:42 +0200 Subject: [PATCH 33/37] wip trying to get klib validation working for supported/unsupported targets... --- .../main/kotlin/tasks/BCVApiGenerateTask.kt | 129 +++++++++++------- .../main/kotlin/workers/KLibMergeWorker.kt | 37 ++++- .../kotlin/workers/KLibSignaturesWorker.kt | 34 ++++- 3 files changed, 141 insertions(+), 59 deletions(-) diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 892d0b0..092b8f6 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -4,7 +4,10 @@ import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget -import dev.adamko.kotlin.binary_compatibility_validator.workers.* +import dev.adamko.kotlin.binary_compatibility_validator.workers.JvmSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibInferSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibMergeWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibSignaturesWorker import java.io.File import javax.inject.Inject import org.gradle.api.NamedDomainObjectContainer @@ -71,17 +74,18 @@ constructor( @TaskAction fun generate() { - val workQueue = prepareWorkQueue() - prepareDirectories() - logger.lifecycle("[$path] got ${targets.size} targets : ${targets.joinToString { it.name }}") + val workQueue = prepareWorkQueue() val enabledTargets = targets.matching { it.enabled.getOrElse(true) } + logger.lifecycle("[$path] got ${targets.size} targets (${enabledTargets.size} enabled) : ${targets.joinToString { it.name }}") + + val jvmTargets = enabledTargets.withType().sorted() generateJvmTargets( workQueue = workQueue, - jvmTargets = enabledTargets.withType().sorted(), + jvmTargets = jvmTargets, outputApiBuildDir = outputApiBuildDir.get().asFile, ) @@ -90,7 +94,6 @@ constructor( val klibTargets = enabledTargets.withType() .filter { it.klibFile.singleOrNull()?.exists() == true } .sorted() - generateKLibTargets( workQueue = workQueue, klibTargets = klibTargets, @@ -103,26 +106,19 @@ constructor( private fun prepareWorkQueue(): WorkQueue { - fs.delete { delete(temporaryDir) } - temporaryDir.mkdirs() - return workers.classLoaderIsolation { classpath.from(runtimeClasspath) } } private fun prepareDirectories() { - val outputApiBuildDir = outputApiBuildDir.get() fs.delete { delete(outputApiBuildDir) } - outputApiBuildDir.asFile.mkdirs() + outputApiBuildDir.get().asFile.mkdirs() - fs.delete { delete(klibTargetsDir.supported) } + fs.delete { delete(workDir) } + workDir.mkdirs() klibTargetsDir.supported.mkdirs() - - fs.delete { delete(klibTargetsDir.unsupported) } klibTargetsDir.unsupported.mkdirs() - - fs.delete { delete(klibTargetsDir.extracted) } klibTargetsDir.extracted.mkdirs() } @@ -187,7 +183,7 @@ constructor( private fun generateKLibTargets( workQueue: WorkQueue, outputApiBuildDir: File, - klibTargets: Collection, + klibTargets: List, ) { if (klibTargets.isEmpty()) { logger.info("[$path] No enabled KLib targets") @@ -201,19 +197,30 @@ constructor( generateSupportedKLibTargets(workQueue, supportedKLibTargets) generateUnsupportedKLibTargets(workQueue, unsupportedKLibTargets) - workQueue.await() + val allTargetDumpFiles = buildSet { + addAll(klibTargetsDir.supported.walk().filter { it.isFile }) + addAll(klibTargetsDir.unsupported.walk().filter { it.isFile }) + } - val allTargetDumpFiles = - klibTargetsDir.supported.walk().filter { it.isFile }.toSet() union - klibTargetsDir.unsupported.walk().filter { it.isFile }.toSet() + mergeDumpFiles( + workQueue = workQueue, + allTargetDumpFiles = allTargetDumpFiles, + outputApiBuildDir = outputApiBuildDir, + targets = klibTargets, + strictValidation = strictKLibTargetValidation.get(), + ) - mergeDumpFiles(workQueue, allTargetDumpFiles, outputApiBuildDir) +// workQueue.extract() } private fun generateSupportedKLibTargets( workQueue: WorkQueue, supportedKLibTargets: List ) { + if (supportedKLibTargets.isEmpty()) { + logger.info("[$path] No supported enabled KLib targets") + return + } logger.lifecycle("[$path] generating ${supportedKLibTargets.size} supported KLib targets : ${supportedKLibTargets.joinToString { it.name }}") val duration = measureTime { @@ -233,6 +240,10 @@ constructor( workQueue: WorkQueue, unsupportedKLibTargets: List ) { + if (unsupportedKLibTargets.isEmpty()) { + logger.info("[$path] No unsupported enabled KLib targets") + return + } logger.lifecycle("[$path] generating ${unsupportedKLibTargets.size} unsupported KLib targets : ${unsupportedKLibTargets.joinToString { it.name }}") val duration = measureTime { @@ -245,6 +256,7 @@ constructor( outputDir = klibTargetsDir.unsupported, ) } + workQueue.await() } logger.lifecycle("[$path] finished generating unsupported KLib targets in $duration") @@ -254,20 +266,26 @@ constructor( private fun mergeDumpFiles( workQueue: WorkQueue, allTargetDumpFiles: Set, - outputApiBuildDir: File + outputApiBuildDir: File, + targets: List, + strictValidation: Boolean, ) { logger.lifecycle("[$path] merging ${allTargetDumpFiles.size} dump files : ${allTargetDumpFiles.joinToString { it.name }}") - workQueue.merge( - projectName.get(), - targetDumpFiles = allTargetDumpFiles, - outputDir = outputApiBuildDir, - ) - workQueue.await() + val duration = measureTime { + workQueue.merge( + projectName.get(), + targetDumpFiles = allTargetDumpFiles, + outputDir = outputApiBuildDir, + supportedTargets = targets.filter { it.supportedByCurrentHost.get() }.map { it.targetName }, + strictValidation = strictValidation, + ) + workQueue.await() + } if (logger.isLifecycleEnabled) { val fileNames = outputApiBuildDir.walk().filter { it.isFile }.toList() - logger.lifecycle("[$path] merged ${allTargetDumpFiles.size} dump files : $fileNames") + logger.lifecycle("[$path] merged ${allTargetDumpFiles.size} dump files in $duration : $fileNames") } } @@ -288,8 +306,7 @@ constructor( this@worker.klib.set(target.klibFile.singleFile) this@worker.signatureVersion.set(target.signatureVersion) - this@worker.strictValidation.set(target.strictValidation) -// this@worker.supportedByCurrentHost.set(target.supportedByCurrentHost) +// this@worker.strictValidation.set(target.strictValidation) // this@worker.targets.addAll(klibTargets) @@ -325,37 +342,45 @@ constructor( projectName: String, targetDumpFiles: Set, outputDir: File, + supportedTargets: List, + strictValidation: Boolean, ) { val task = this@BCVApiGenerateTask @OptIn(BCVInternalApi::class) submit(KLibMergeWorker::class) worker@{ - this@worker.projectName.set(projectName) +// this@worker.projectName.set(projectName) this@worker.taskPath.set(task.path) - this@worker.outputApiDir.set(outputDir) +// this@worker.outputApiDir.set(outputDir) + this@worker.outputApiFile.set( + outputDir.resolve("$projectName.klib.api") + ) + + this@worker.strictValidation.set(strictValidation) + this@worker.supportedTargets.set(supportedTargets) this@worker.targetDumpFiles.from(targetDumpFiles) } } - @OptIn(BCVExperimentalApi::class) - private fun WorkQueue.extract( - target: BCVKLibTarget, - targetDumpFiles: Set, - outputDir: File, - ) { - val task = this@BCVApiGenerateTask - - @OptIn(BCVInternalApi::class) - submit(KLibExtractWorker::class) worker@{ - this@worker.taskPath.set(task.path) - this@worker.strictValidation.set(target.strictValidation) - -// this@worker.inputAbiFile.set() -// this@worker.outputAbiFile.set() -// this@worker.supportedTargets.set() - } - } +// @OptIn(BCVExperimentalApi::class) +// private fun WorkQueue.extract( +// target: BCVKLibTarget, +// targetDumpFiles: Set, +// outputDir: File, +// ) { +// val task = this@BCVApiGenerateTask +// +// @OptIn(BCVInternalApi::class) +// submit(KLibExtractWorker::class) worker@{ +// this@worker.taskPath.set(task.path) +// this@worker.strictValidation.set(target.strictValidation) +// +//// this@worker.inputAbiFile.set() +//// this@worker.outputAbiFile.set() +//// this@worker.supportedTargets.set() +// } +// } //endregion } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt index d47a335..f94d57e 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt @@ -4,13 +4,16 @@ import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimental import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import java.io.Serializable import kotlinx.validation.ExperimentalBCVApi +import kotlinx.validation.KlibValidationSettings import kotlinx.validation.api.klib.KlibDump +import kotlinx.validation.api.klib.KlibTarget import kotlinx.validation.api.klib.saveTo import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters @@ -18,15 +21,22 @@ import org.gradle.workers.WorkParameters @BCVExperimentalApi @OptIn(ExperimentalBCVApi::class) abstract class KLibMergeWorker : WorkAction { +// TODO rename 'Merge' to something that represents 'Merge' and 'Extract'... 'MergeSupported'? @BCVInternalApi interface Parameters : WorkParameters, Serializable { - val projectName: Property +// val projectName: Property - val outputApiDir: DirectoryProperty + val outputApiFile: RegularFileProperty val targetDumpFiles: ConfigurableFileCollection + /** Provider returning targets supported by the host compiler. */ + val supportedTargets: SetProperty + + /** Refer to [KlibValidationSettings.strictValidation] for details. */ + val strictValidation: Property + /** * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, * for log messages @@ -34,9 +44,17 @@ abstract class KLibMergeWorker : WorkAction { val taskPath: Property } + private val taskPath: String get() = parameters.taskPath.get() + override fun execute() { + logger.info("[${taskPath}] merging dump files ${parameters.targetDumpFiles} ") + val sourceFiles = parameters.targetDumpFiles.asFileTree - val outputFile = parameters.outputApiDir.get().asFile.resolve(parameters.projectName.get() + ".klib.api") +// val outputFile = parameters.outputApiDir.get().asFile.resolve(parameters.projectName.get() + ".klib.api") + val outputApiFile = parameters.outputApiFile.get().asFile + + + val supportedTargets = parameters.supportedTargets.get().map { KlibTarget.parse(it).targetName } val dump = KlibDump() @@ -44,7 +62,16 @@ abstract class KLibMergeWorker : WorkAction { dump.merge(dumpFile, dumpFile.name.substringBefore(".klib.api")) } - dump.saveTo(outputFile) +// // Filter out only unsupported files. +// // That ensures that target renaming will be caught and reported as a change. +// val unsupportedTargets = dump.targets.filter { it.targetName !in supportedTargets } +// +//// if (targetsToRemove.isNotEmpty() && parameters.strictValidation.get()) { +//// error("Validation could not be performed as some targets are not available and strictValidation mode is enabled") +//// } +// dump.remove(unsupportedTargets) + + dump.saveTo(outputApiFile) } @BCVInternalApi diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt index c567f43..96c12f8 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibSignaturesWorker.kt @@ -3,6 +3,7 @@ package dev.adamko.kotlin.binary_compatibility_validator.workers import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVExperimentalApi import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi import dev.adamko.kotlin.binary_compatibility_validator.targets.KLibSignatureVersion +import java.io.File import java.io.Serializable import kotlinx.validation.ExperimentalBCVApi import kotlinx.validation.api.klib.* @@ -32,7 +33,8 @@ abstract class KLibSignaturesWorker : WorkAction val signatureVersion: Property - val strictValidation: Property +// val strictValidation: Property + val supportedByCurrentHost: Property /** * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, @@ -45,6 +47,14 @@ abstract class KLibSignaturesWorker : WorkAction Date: Sun, 2 Jun 2024 16:58:07 +0200 Subject: [PATCH 34/37] fix adding gradle.properties to test projects --- .../validation/test/KlibVerificationTests.kt | 1 + .../testFixtures/kotlin/GradleTestKitUtils.kt | 2 +- .../src/testFixtures/kotlin/api/TestDsl.kt | 17 +++++++++++------ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 9a7328b..15eac40 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -303,6 +303,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { runner { arguments.add(":apiCheck") gradleJvmArgs += "-Xmx4g" +// gradleJvmArgs += "-XX:MaxMetaspaceSize=1g" } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt index d0aeebb..7407011 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/GradleTestKitUtils.kt @@ -163,7 +163,7 @@ fun gradleGroovyProjectTest( gradleProperties = """ |kotlin.mpp.stability.nowarn=true - |org.gradle.cache=true + | """.trimMargin() build() diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt index 89ff213..b3ef375 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/TestDsl.kt @@ -37,6 +37,13 @@ fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { fn(baseKotlinScope) + baseKotlinScope.gradleProperties { + addLine("org.gradle.jvmargs=" + baseKotlinScope.runner.gradleJvmArgs.joinToString(" ")) + baseKotlinScope.runner.gradleProperties.forEach { (k, v) -> + addLine("$k=$v") + } + } + baseKotlinScope.files.forEach { scope -> val fileWriteTo = rootProjectDir.resolve(scope.filePath) .apply { @@ -53,10 +60,6 @@ fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner { } } - baseKotlinScope.gradleProperties { - addLine("org.gradle.jvmargs=" + baseKotlinScope.runner.gradleJvmArgs.joinToString(" ")) - } - return GradleRunner.create() .withProjectDir(rootProjectDir) .withGradleVersion(baseKotlinScope.runner.gradleVersion) @@ -118,7 +121,7 @@ fun FileContainer.settingsGradleKts(fn: AppendableScope.() -> Unit) { } /** - * Shortcut for creating a `settings.gradle.kts` by using [file][FileContainer.file] + * Shortcut for creating a `gradle.properties` by using [file][FileContainer.file] */ fun FileContainer.gradleProperties(fn: AppendableScope.() -> Unit) { val fileName = "gradle.properties" @@ -225,7 +228,9 @@ class Runner { /** JVM args used by Gradle. Set as `org.gradle.jvmargs` in `gradle.properties`. */ val gradleJvmArgs: MutableSet = mutableSetOf( "-Dfile.encoding=UTF-8", - "org.gradle.welcome=never", + ) + val gradleProperties: MutableMap = mutableMapOf( + "org.gradle.welcome" to "never", ) var configurationCache: Boolean = true var rerunTasks: Boolean = false From bf11bb72709b386c04a471720908eb96668041b0 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:18:50 +0200 Subject: [PATCH 35/37] mostly working... just a few failing klibs --- .../validation/test/KlibVerificationTests.kt | 11 +- .../kotlin/api/BaseKotlinGradleTest.kt | 2 +- .../api/bcv-gradle-plugin.api | 3 +- .../src/main/kotlin/BCVProjectPlugin.kt | 20 ++- .../src/main/kotlin/tasks/BCVApiCheckTask.kt | 139 +++++++++++++++--- .../tasks/BCVApiGeneratePreparationTask.kt | 3 + .../main/kotlin/tasks/BCVApiGenerateTask.kt | 104 ++++++++----- .../main/kotlin/workers/KLibMergeWorker.kt | 14 +- 8 files changed, 221 insertions(+), 75 deletions(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index 15eac40..f55e25a 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -3,7 +3,6 @@ package kotlinx.validation.test import dev.adamko.kotlin.binary_compatibility_validator.test.utils.* import dev.adamko.kotlin.binary_compatibility_validator.test.utils.api.* import io.kotest.assertions.withClue -import io.kotest.inspectors.shouldForAll import io.kotest.matchers.file.shouldBeEmptyDirectory import io.kotest.matchers.file.shouldExist import io.kotest.matchers.shouldBe @@ -12,6 +11,7 @@ import kotlin.io.path.Path import kotlin.io.path.name import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome.* +import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.junit.jupiter.api.Assumptions.assumeTrue @@ -97,10 +97,8 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { + shouldHaveRunTask(":apiCheck", FAILED) output shouldContain "+final class com.company/BuildConfig { // com.company/BuildConfig|null[0]" - tasks.filter { it.path.endsWith("ApiCheck") }.shouldForAll { - it.shouldHaveOutcome(FAILED) - } } } @@ -268,6 +266,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiDump", FAILED) output shouldContain "Unsupported KLib signature version '100500'" } } @@ -433,6 +432,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiDump", FAILED) // shouldHaveTaskWithOutcome(":linuxArm64ApiInfer", FAILED) output shouldContain "The target linuxArm64 is not supported by the host compiler and there are no targets similar to linuxArm64 to infer a dump from it." } @@ -458,6 +458,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiDump", FAILED) output shouldContain "is not supported by the host compiler and there are no targets similar to" } } @@ -486,6 +487,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiCheck", FAILED) output shouldContain "KLib ABI dump/validation requires at least one enabled klib target, but none were found." } } @@ -678,6 +680,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiCheck", FAILED) output shouldContain " -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, linuxArm64.linux, linuxX64, mingwX64]" } } diff --git a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt index 833e325..1e311a6 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/testFixtures/kotlin/api/BaseKotlinGradleTest.kt @@ -5,7 +5,7 @@ import java.io.File import org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS import org.junit.jupiter.api.io.TempDir -open class BaseKotlinGradleTest { +abstract class BaseKotlinGradleTest { @TempDir( factory = TempTestNameDirFactory::class, cleanup = ON_SUCCESS, diff --git a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api index 87e8012..96696be 100644 --- a/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api +++ b/modules/bcv-gradle-plugin/api/bcv-gradle-plugin.api @@ -141,6 +141,7 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract fun getApiBuildDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getExpectedApiDirPath ()Lorg/gradle/api/provider/Property; public final fun getProjectApiDir ()Lorg/gradle/api/provider/Provider; + public final fun getTargets ()Lorg/gradle/api/NamedDomainObjectContainer; public final fun verify ()V } @@ -158,9 +159,9 @@ public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCV public abstract class dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVApiGenerateTask : dev/adamko/kotlin/binary_compatibility_validator/tasks/BCVDefaultTask { public final fun generate ()V - public abstract fun getExtantApiDumpDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getInputDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public abstract fun getOutputApiBuildDir ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getProjectApiDumpDir ()Lorg/gradle/api/file/DirectoryProperty; public abstract fun getProjectName ()Lorg/gradle/api/provider/Property; public abstract fun getRuntimeClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; public abstract fun getStrictKLibTargetValidation ()Lorg/gradle/api/provider/Property; diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt index f9c8026..0dc81dc 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/BCVProjectPlugin.kt @@ -168,10 +168,10 @@ constructor( onlyIf("BCV is enabled") { bcvEnabled.get() } } - project.tasks.withType().configureEach { - apiDumpFiles.from(extension.outputApiDir) - apiDirectory.convention(objects.directoryProperty().fileValue(temporaryDir)) - } +// project.tasks.withType().configureEach { +// apiDumpFiles.from(extension.outputApiDir) +// apiDirectory.convention(objects.directoryProperty().fileValue(temporaryDir)) +// } project.tasks.withType().configureEach { runtimeClasspath.from(bcvGenerateClasspath) @@ -182,15 +182,19 @@ constructor( @OptIn(BCVExperimentalApi::class) strictKLibTargetValidation.convention(extension.klib.strictValidation) + projectApiDumpDir.convention(extension.outputApiDir) + onlyIf("Must have at least one target") { targets.isNotEmpty() } } project.tasks.withType().configureEach { - outputs.dir(temporaryDir) // dummy output, so up-to-date checks work + targets.addAllLater(providers.provider { extension.targets }) +// outputs.dir(temporaryDir) // dummy output, so up-to-date checks work expectedProjectName.convention(extension.projectName) expectedApiDirPath.convention( extension.outputApiDir.map { it.asFile.canonicalFile.invariantSeparatorsPath } ) +// runtimeClasspath.from(bcvGenerateClasspath) } project.tasks.withType().configureEach { @@ -204,12 +208,12 @@ constructor( private fun registerBcvTasks(project: Project) { - val prepareApiGenerateTask = - project.tasks.register(PREPARE_API_GENERATE_TASK_NAME, BCVApiGeneratePreparationTask::class) +// val prepareApiGenerateTask = +// project.tasks.register(PREPARE_API_GENERATE_TASK_NAME, BCVApiGeneratePreparationTask::class) val apiGenerateTask = project.tasks.register(API_GENERATE_TASK_NAME, BCVApiGenerateTask::class) { - extantApiDumpDir.convention(prepareApiGenerateTask.flatMap { it.apiDirectory }) +// projectApiDumpDir.convention(prepareApiGenerateTask.flatMap { it.apiDirectory }) } project.tasks.register(API_DUMP_TASK_NAME, BCVApiDumpTask::class) { diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt index 4c58eb0..6f2a9d1 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiCheckTask.kt @@ -3,13 +3,16 @@ package dev.adamko.kotlin.binary_compatibility_validator.tasks import com.github.difflib.DiffUtils import com.github.difflib.UnifiedDiffUtils import dev.adamko.kotlin.binary_compatibility_validator.BCVPlugin.Companion.API_DUMP_TASK_NAME -import dev.adamko.kotlin.binary_compatibility_validator.internal.BCVInternalApi -import dev.adamko.kotlin.binary_compatibility_validator.internal.GradlePath -import dev.adamko.kotlin.binary_compatibility_validator.internal.fullPath +import dev.adamko.kotlin.binary_compatibility_validator.internal.* +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget +import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget +import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibExtractWorker import java.io.File import java.util.TreeMap import javax.inject.Inject import kotlin.io.path.listDirectoryEntries +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileVisitDetails import org.gradle.api.file.RelativePath @@ -19,16 +22,24 @@ import org.gradle.api.provider.Provider import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.* import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.kotlin.dsl.* +import org.gradle.workers.WorkQueue +import org.gradle.workers.WorkerExecutor @CacheableTask abstract class BCVApiCheckTask @BCVInternalApi @Inject constructor( + private val workers: WorkerExecutor, private val objects: ObjectFactory, private val providers: ProviderFactory, ) : BCVDefaultTask() { + @get:Nested + val targets: NamedDomainObjectContainer = + extensions.adding("targets") { objects.domainObjectContainer() } + @get:InputDirectory @get:Optional @get:PathSensitive(RELATIVE) @@ -43,9 +54,15 @@ constructor( @get:InputDirectory @get:PathSensitive(RELATIVE) abstract val apiBuildDir: DirectoryProperty +// +// @get:LocalState +// internal val tempDir: File get() = temporaryDir @get:Input internal abstract val expectedProjectName: Property +// +// @get:Input +// abstract val strictKLibTargetValidation: Property // Project and tasks paths are used for creating better error messages private val projectFullPath: String = project.fullPath @@ -53,6 +70,9 @@ constructor( private val rootDir: File = project.rootProject.rootDir +// @get:Classpath +// abstract val runtimeClasspath: ConfigurableFileCollection + init { super.onlyIf { task -> require(task is BCVApiCheckTask) @@ -76,28 +96,73 @@ constructor( val apiBuildDir = apiBuildDir.get().asFile - val checkApiDeclarationPaths = projectApiDir.relativePathsOfContent { !isDirectory } - val builtApiDeclarationPaths = apiBuildDir.relativePathsOfContent { !isDirectory } - logger.info("[$path] checkApiDeclarationPaths: $checkApiDeclarationPaths") + verifyJvm(projectApiDir, apiBuildDir) + +// val klibTargets = targets.withType().filter { it.enabled.get() } +// verifyKLib(projectApiDir, apiBuildDir, klibTargets) + + // TODO need to verify that all .api files in projectApiDir have a match + } + + private fun verifyJvm( + projectApiDir: File, + apiBuildDir: File, + ) { + - checkApiDeclarationPaths.forEach { checkApiDeclarationPath -> +// val jvmTargets = targets.withType().filter { it.enabled.get() } +// +// +// jvmTargets.forEach { target -> +// if (jvmTargets.size > 1) { +// val expectedApiFile = projectApiDir +// .resolve(target.name) +// .resolve(target.platformType) +// val actualApiFile = apiBuildDir +// .resolve(target.name) +// .resolve(target.platformType) +// checkTarget( +// expectedApiDeclaration = expectedApiFile, +// actualApiDeclaration = actualApiFile, +// ) +// } else { +// val expectedApiFile = projectApiDir +// .resolve(target.platformType) +// val actualApiFile = apiBuildDir +// .resolve(target.platformType) +// checkTarget( +// expectedApiDeclaration = expectedApiFile, +// actualApiDeclaration = actualApiFile, +// ) +// } +// } + + val expectedApiFiles = projectApiDir.relativePathsOfContent { + file.name.substringAfter(".") == "api" + } + val actualApiFiles = apiBuildDir.relativePathsOfContent { + file.name.substringAfter(".") == "api" + } + logger.info("[$path] expectedApiFiles: $expectedApiFiles") + + expectedApiFiles.forEach { expectedApiFile -> checkTarget( - checkApiDeclaration = checkApiDeclarationPath.getFile(projectApiDir), + expectedApiDeclaration = expectedApiFile.getFile(projectApiDir), // fetch the builtFile, using the case-insensitive map - builtApiDeclaration = builtApiDeclarationPaths[checkApiDeclarationPath]?.getFile(apiBuildDir) + actualApiDeclaration = actualApiFiles[expectedApiFile]?.getFile(apiBuildDir) ) } } private fun checkTarget( - checkApiDeclaration: File, - builtApiDeclaration: File?, + expectedApiDeclaration: File, + actualApiDeclaration: File?, ) { - logger.info("[$path] checkApiDeclaration: $checkApiDeclaration") - logger.info("[$path] builtApiDeclaration: $builtApiDeclaration") + logger.info("[$path] expectedApiDeclaration: $expectedApiDeclaration") + logger.info("[$path] actualApiDeclaration: $actualApiDeclaration") - val allBuiltFilePaths = builtApiDeclaration?.parentFile.relativePathsOfContent() - val allCheckFilePaths = checkApiDeclaration.parentFile.relativePathsOfContent() + val allBuiltFilePaths = actualApiDeclaration?.parentFile.relativePathsOfContent() + val allCheckFilePaths = expectedApiDeclaration.parentFile.relativePathsOfContent() logger.info("[$path] allBuiltPaths: $allBuiltFilePaths") logger.info("[$path] allCheckFiles: $allCheckFilePaths") @@ -105,7 +170,7 @@ constructor( val builtFilePath = allBuiltFilePaths.singleOrNull() ?: error("[$path] Expected a single file ${expectedProjectName.get()}.api, but found ${allBuiltFilePaths.size}: $allBuiltFilePaths") - if (builtApiDeclaration == null || builtFilePath !in allCheckFilePaths) { + if (actualApiDeclaration == null || builtFilePath !in allCheckFilePaths) { val relativeDirPath = projectApiDir.get().toRelativeString(rootDir) + File.separator error( "[$path] File ${builtFilePath.lastName} is missing from ${relativeDirPath}, please run '$apiDumpTaskPath' task to generate one" @@ -113,8 +178,8 @@ constructor( } val diffText = compareFiles( - checkFile = checkApiDeclaration, - builtFile = builtApiDeclaration, + checkFile = expectedApiDeclaration, + builtFile = actualApiDeclaration, )?.trim() if (!diffText.isNullOrBlank()) { @@ -130,7 +195,7 @@ constructor( } } - /** Get the relative paths of all files and folders inside a directory */ + /** Get the relative paths of all files inside a directory. */ private fun File?.relativePathsOfContent( filter: FileVisitDetails.() -> Boolean = { true }, ): RelativePaths { @@ -163,6 +228,42 @@ constructor( ) return diff.joinToString("\n") } + + +// private fun verifyKLib( +// projectApiDir: File, +// apiBuildDir: File, +// targets: List +// ) { +// if (targets.isEmpty()) return +// val klibFile = projectApiDir.resolve(expectedProjectName.get()) +// +// } +// private fun prepareWorkQueue(): WorkQueue { +// return workers.classLoaderIsolation { +// classpath.from(runtimeClasspath) +// } +// } + + +// @OptIn(BCVExperimentalApi::class) +// private fun WorkQueue.extract( +// target: BCVKLibTarget, +// targetDumpFiles: Set, +// outputDir: File, +// ) { +// val task = this@BCVApiCheckTask +// +// @OptIn(BCVInternalApi::class) +// submit(KLibExtractWorker::class) worker@{ +// this@worker.taskPath.set(task.path) +// this@worker.strictValidation.set(task.strictKLibTargetValidation) +// +//// this@worker.inputAbiFile.set() +//// this@worker.outputAbiFile.set() +//// this@worker.supportedTargets.set() +// } +// } } /** diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt index a9f8524..d76d0be 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGeneratePreparationTask.kt @@ -14,6 +14,9 @@ import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Performs simple file operations, not worth caching") abstract class BCVApiGeneratePreparationTask +// TODO This task probably isn't needed and can be removed +// The generate task can just directly use files in `$projectDir/api`, +// so long as it only reads them. @BCVInternalApi @Inject constructor( diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt index 092b8f6..f967608 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/tasks/BCVApiGenerateTask.kt @@ -4,10 +4,7 @@ import dev.adamko.kotlin.binary_compatibility_validator.internal.* import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVJvmTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVKLibTarget import dev.adamko.kotlin.binary_compatibility_validator.targets.BCVTarget -import dev.adamko.kotlin.binary_compatibility_validator.workers.JvmSignaturesWorker -import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibInferSignaturesWorker -import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibMergeWorker -import dev.adamko.kotlin.binary_compatibility_validator.workers.KLibSignaturesWorker +import dev.adamko.kotlin.binary_compatibility_validator.workers.* import java.io.File import javax.inject.Inject import org.gradle.api.NamedDomainObjectContainer @@ -51,13 +48,20 @@ constructor( abstract val strictKLibTargetValidation: Property /** - * A directory containing a copy of any currently existing API Dump files. - * Provided by [BCVApiGeneratePreparationTask]. + * A directory containing any currently existing API Dump files. */ - @get:InputDirectory +// * Provided by [BCVApiGeneratePreparationTask]. + @get:Internal + abstract val projectApiDumpDir: DirectoryProperty + + @get:InputFiles @get:PathSensitive(RELATIVE) @get:Optional - abstract val extantApiDumpDir: DirectoryProperty + // Gradle sucks and doesn't allow an optional non-existing input dir 🙄 + internal val projectApiDumpDirFiles: List + get() = projectApiDumpDir.orNull?.asFile?.walk() + ?.filter { it.isFile } + ?.toList()?.sorted().orEmpty() @get:OutputDirectory abstract val outputApiBuildDir: DirectoryProperty @@ -195,6 +199,7 @@ constructor( klibTargets.partition { it.supportedByCurrentHost.get() } generateSupportedKLibTargets(workQueue, supportedKLibTargets) + extractSupportedKLibs(workQueue, supportedKLibTargets) generateUnsupportedKLibTargets(workQueue, unsupportedKLibTargets) val allTargetDumpFiles = buildSet { @@ -215,16 +220,16 @@ constructor( private fun generateSupportedKLibTargets( workQueue: WorkQueue, - supportedKLibTargets: List + supportedTargets: List ) { - if (supportedKLibTargets.isEmpty()) { + if (supportedTargets.isEmpty()) { logger.info("[$path] No supported enabled KLib targets") return } - logger.lifecycle("[$path] generating ${supportedKLibTargets.size} supported KLib targets : ${supportedKLibTargets.joinToString { it.name }}") + logger.lifecycle("[$path] generating ${supportedTargets.size} supported KLib targets : ${supportedTargets.joinToString { it.name }}") val duration = measureTime { - supportedKLibTargets.forEach { target -> + supportedTargets.forEach { target -> workQueue.submit( target = target, outputDir = klibTargetsDir.supported, @@ -236,22 +241,42 @@ constructor( logger.lifecycle("[$path] finished generating supported KLib targets in $duration") } + private fun extractSupportedKLibs( + workQueue: WorkQueue, + supportedTargets: List + ) { + if (supportedTargets.isEmpty()) { + logger.info("[$path] No supported enabled KLib targets for extraction") + return + } + logger.lifecycle("[$path] extracting ${supportedTargets.size} supported KLib targets : ${supportedTargets.joinToString { it.name }}") + + val duration = measureTime { + workQueue.extract( + supportedTargets = supportedTargets + ) + workQueue.await() + } + + logger.lifecycle("[$path] finished extracting supported KLib targets in $duration") + } + private fun generateUnsupportedKLibTargets( workQueue: WorkQueue, - unsupportedKLibTargets: List + unsupportedTargets: List ) { - if (unsupportedKLibTargets.isEmpty()) { + if (unsupportedTargets.isEmpty()) { logger.info("[$path] No unsupported enabled KLib targets") return } - logger.lifecycle("[$path] generating ${unsupportedKLibTargets.size} unsupported KLib targets : ${unsupportedKLibTargets.joinToString { it.name }}") + logger.lifecycle("[$path] generating ${unsupportedTargets.size} unsupported KLib targets : ${unsupportedTargets.joinToString { it.name }}") val duration = measureTime { - unsupportedKLibTargets.forEach { target -> + unsupportedTargets.forEach { target -> workQueue.inferKLib( target = target, supportedTargetDumpFiles = klibTargetsDir.supported.walk().filter { it.isFile }.toSet(), - extantApiDumpFile = extantApiDumpDir.asFile.orNull?.walk()?.filter { it.isFile } + extantApiDumpFile = projectApiDumpDir.asFile.orNull?.walk()?.filter { it.isFile } ?.firstOrNull(), outputDir = klibTargetsDir.unsupported, ) @@ -357,30 +382,41 @@ constructor( outputDir.resolve("$projectName.klib.api") ) - this@worker.strictValidation.set(strictValidation) - this@worker.supportedTargets.set(supportedTargets) +// this@worker.strictValidation.set(strictValidation) +// this@worker.supportedTargets.set(supportedTargets) this@worker.targetDumpFiles.from(targetDumpFiles) } } -// @OptIn(BCVExperimentalApi::class) -// private fun WorkQueue.extract( + @OptIn(BCVExperimentalApi::class) + private fun WorkQueue.extract( // target: BCVKLibTarget, // targetDumpFiles: Set, // outputDir: File, -// ) { -// val task = this@BCVApiGenerateTask -// -// @OptIn(BCVInternalApi::class) -// submit(KLibExtractWorker::class) worker@{ -// this@worker.taskPath.set(task.path) -// this@worker.strictValidation.set(target.strictValidation) -// -//// this@worker.inputAbiFile.set() -//// this@worker.outputAbiFile.set() -//// this@worker.supportedTargets.set() -// } -// } + supportedTargets: List, + ) { + val task = this@BCVApiGenerateTask + + val inputFile = projectApiDumpDir.file(projectName.map { "$it.klib.api" }).get().asFile + if (!inputFile.exists()) return + + @OptIn(BCVInternalApi::class) + submit(KLibExtractWorker::class) worker@{ + this@worker.taskPath.set(task.path) + this@worker.strictValidation.set(strictKLibTargetValidation) + + this@worker.inputAbiFile.set( + projectApiDumpDir.file(projectName.map { "$it.klib.api" }) + ) +// this@worker.outputAbiFile.set() + this@worker.supportedTargets.set( + supportedTargets.map { it.targetName } + ) + this@worker.outputAbiFile.set( + klibTargetsDir.extracted.resolve(projectName.map { "$it.klib.api" }.get()) + ) + } + } //endregion } diff --git a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt index f94d57e..e575e8c 100644 --- a/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt +++ b/modules/bcv-gradle-plugin/src/main/kotlin/workers/KLibMergeWorker.kt @@ -21,7 +21,6 @@ import org.gradle.workers.WorkParameters @BCVExperimentalApi @OptIn(ExperimentalBCVApi::class) abstract class KLibMergeWorker : WorkAction { -// TODO rename 'Merge' to something that represents 'Merge' and 'Extract'... 'MergeSupported'? @BCVInternalApi interface Parameters : WorkParameters, Serializable { @@ -31,11 +30,11 @@ abstract class KLibMergeWorker : WorkAction { val targetDumpFiles: ConfigurableFileCollection - /** Provider returning targets supported by the host compiler. */ - val supportedTargets: SetProperty - - /** Refer to [KlibValidationSettings.strictValidation] for details. */ - val strictValidation: Property +// /** Provider returning targets supported by the host compiler. */ +// val supportedTargets: SetProperty +// +// /** Refer to [KlibValidationSettings.strictValidation] for details. */ +// val strictValidation: Property /** * [Task path][org.gradle.api.Task.getPath] of the task that invoked this worker, @@ -53,8 +52,7 @@ abstract class KLibMergeWorker : WorkAction { // val outputFile = parameters.outputApiDir.get().asFile.resolve(parameters.projectName.get() + ".klib.api") val outputApiFile = parameters.outputApiFile.get().asFile - - val supportedTargets = parameters.supportedTargets.get().map { KlibTarget.parse(it).targetName } +// val supportedTargets = parameters.supportedTargets.get().map { KlibTarget.parse(it).targetName } val dump = KlibDump() From 745fc9d6d325300492d320d2f2bda08e963ec299 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Mon, 15 Jul 2024 23:49:14 +0200 Subject: [PATCH 36/37] update bcv to 0.15.1 --- README.md | 4 +- gradle/libs.versions.toml | 2 +- .../validation/test/KlibVerificationTests.kt | 78 ++++++++++++++++++- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1c44472..f4ba888 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ or (**experimentally**) [as a Settings plugin](#settings-plugin) in `settings.gr The minimal supported Gradle version is 7.6.4. -By default, BCV-MU uses BCV version `0.15.0-Beta.2`, which can be overridden, but may introduce runtime +By default, BCV-MU uses BCV version `0.15.1`, which can be overridden, but may introduce runtime errors. ### Build plugin @@ -100,7 +100,7 @@ binaryCompatibilityValidator { bcvEnabled.set(true) // Override the default BCV version - kotlinxBinaryCompatibilityValidatorVersion.set("0.15.0-Beta.2") + kotlinxBinaryCompatibilityValidatorVersion.set("0.15.1") } ``` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 90e2c88..705859f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ kotlinGradle = "1.9.24" javaDiffUtils = "4.12" junit = "5.10.2" kotest = "5.9.0" -kotlinx-bcv = "0.15.0-Beta.2" +kotlinx-bcv = "0.15.1" gradlePluginPublishPlugin = "1.2.1" shadowPlugin = "8.1.1" diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index f55e25a..ea5e60f 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -464,7 +464,7 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } @Test - fun `klibCheck if all klib-targets are unavailable`() { + fun `klibCheck should not fail if all klib-targets are unavailable`() { val runner = test { baseProjectSetting() addToSrcSet("/examples/classes/TopLevelDeclarations.kt") @@ -492,6 +492,37 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } + @Test + fun `klibCheck should fail with strict validation if all klib-targets are unavailable`() { + val runner = test { + baseProjectSetting() + additionalBuildConfig("/examples/gradle/configuration/unsupported/enforce.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + abiFile(projectName = "testproject") { + // note that the regular dump is used, where linuxArm64 is presented + resolve("/examples/classes/TopLevelDeclarations.klib.dump") + } + + disableKLibTargets( + "linuxArm64", + "linuxX64", + "mingwX64", + "androidNativeArm32", + "androidNativeArm64", + "androidNativeX64", + "androidNativeX86", + ) + runner { + arguments.add(":apiCheck") + } + } + + runner.buildAndFail { + shouldHaveTaskWithOutcome(":apiCheck", FAILED) + output shouldContain "KLib ABI dump/validation requires at least one enabled klib target, but none were found." + } + } + @Test fun `target name clashing with a group name`() { val runner = test { @@ -702,6 +733,26 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } + @Test + fun `apiDump should remove dump file if the project does not contain sources anymore`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", sourceSet = "commonTest") + abiFile(projectName = "testproject") { + resolve("/examples/classes/AnotherBuildConfig.klib.dump") + } + + runner { + arguments.add(":apiDump") + } + } + runner.build { + shouldHaveRunTask(":apiDump", SUCCESS) + } + rootProjectApiDump.parentFile.shouldBeEmptyDirectory() +// assertFalse(runner.projectDir.resolve("api").resolve("testproject.klib.api").exists()) + } + @Test fun `apiDump should not fail if there is only one target`() { val runner = test { @@ -767,8 +818,27 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { } } + @Test + fun `apiCheck should fail after a source set was removed`() { + val runner = test { + baseProjectSetting() + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", "linuxX64Main") + addToSrcSet("/examples/classes/AnotherBuildConfig.kt", "linuxArm64Main") + abiFile(projectName = "testproject") { + resolve("/examples/classes/AnotherBuildConfig.klib.dump") + } + runner { + arguments.add(":apiCheck") + } + } + runner.buildAndFail { + shouldHaveRunTask(":apiCheck", FAILED) + } + } + private fun BuildResult.checkKLibDump( - expectedDumpFileName: String, + @Language("file-reference") + expected: String, projectName: String = "testproject", ) { withClue(output) { @@ -778,8 +848,8 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { generatedDump.shouldExist() - val expected = readResourceFile(expectedDumpFileName) - generatedDump.readText().invariantNewlines() shouldBe expected + val expectedFile = readResourceFile(expected) + generatedDump.readText().invariantNewlines() shouldBe expectedFile } } From ebd183cc194fd549a58eb6427e9b2fb25a9e766d Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Tue, 16 Jul 2024 00:01:07 +0200 Subject: [PATCH 37/37] tidy --- .../examples/classes/AnotherBuildConfigLinuxArm64.kt | 5 ----- .../examples/classes/AnotherBuildConfigModified.kt | 5 ----- .../functionalTest/resources/examples/classes/SubPackage.kt | 6 ------ .../functionalTest/resources/examples/classes/Subclasses.kt | 5 ----- 4 files changed, 21 deletions(-) diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt index f5352c0..7a54055 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigLinuxArm64.kt @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - package org.different.pack fun BuildConfig.linuxArm64Specific(): Int = 42 diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt index 8165117..1127802 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/AnotherBuildConfigModified.kt @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2020 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - package org.different.pack public class BuildConfig { diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt index c5a298b..fbaa691 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/SubPackage.kt @@ -1,10 +1,4 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - package com.company.division public class ClassWithinSubPackage { } - diff --git a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt index 7f3f639..b50cd50 100644 --- a/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt +++ b/modules/bcv-gradle-plugin-functional-tests/src/functionalTest/resources/examples/classes/Subclasses.kt @@ -1,8 +1,3 @@ -/* - * Copyright 2016-2023 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - package subclasses public class A {