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 1/3] 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 2/3] - 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 3/3] 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() )