From 070c8c3a5035e32841ccd1efbf5fd9ae179e669d Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Tue, 25 Nov 2025 17:43:22 +0100 Subject: [PATCH 01/11] Fixed bundled KGP with the plugin --- gradle-plugin/build.gradle.kts | 19 ++++++++--- .../kotlin/kotlinx/rpc/RpcGradlePlugin.kt | 22 ++++++++++++ .../rpc/protoc/DefaultProtoSourceSet.kt | 7 ++-- .../test/kotlin/kotlinx/rpc/base/BaseTest.kt | 18 ++++++++-- .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 26 +++++++++++--- .../buf_tasks/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../custom_buf_cli_flags/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../java_source_sets/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../no_grpc/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../test_only_sources/build.gradle.kts | 2 +- .../buf_tasks/build.gradle.kts | 34 ++++++++++++------- .../kmp_hierarchy/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../no_grpc/build.gradle.kts | 2 +- .../no_jvm_targets/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../test_only_sources/build.gradle.kts | 2 +- .../with_other_targets/build.gradle.kts | 2 +- .../resources/template.settings.gradle.kts | 12 +++++++ versions-root/libs.versions.toml | 2 ++ 29 files changed, 133 insertions(+), 49 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index edce9f75d..fa16b4692 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -29,7 +29,8 @@ tasks.withType().configureEach { } dependencies { - implementation(libs.kotlin.gradle.plugin) + compileOnly(libs.kotlin.gradle.plugin) + compileOnly(libs.android.gradle.plugin) testImplementation(libs.kotlin.gradle.plugin) testImplementation(gradleTestKit()) @@ -50,9 +51,17 @@ tasks.test { useJUnitPlatform() - val includedBuild = gradle.includedBuild("protoc-gen") - dependsOn(includedBuild.task(":grpc:publishAllPublicationsToBuildRepoRepository")) - dependsOn(includedBuild.task(":protobuf:publishAllPublicationsToBuildRepoRepository")) + val protocGen = gradle.includedBuild("protoc-gen") + dependsOn(protocGen.task(":grpc:publishAllPublicationsToBuildRepoRepository")) + dependsOn(protocGen.task(":protobuf:publishAllPublicationsToBuildRepoRepository")) + + val compilerPlugin = gradle.includedBuild("compiler-plugin") + dependsOn(compilerPlugin.task(":compiler-plugin-cli:publishAllPublicationsToBuildRepoRepository")) + dependsOn(compilerPlugin.task(":compiler-plugin-k2:publishAllPublicationsToBuildRepoRepository")) + dependsOn(compilerPlugin.task(":compiler-plugin-backend:publishAllPublicationsToBuildRepoRepository")) + dependsOn(compilerPlugin.task(":compiler-plugin-common:publishAllPublicationsToBuildRepoRepository")) + + dependsOn(":publishAllPublicationsToBuildRepoRepository") } // This block is needed to show plugin tasks on --dry-run @@ -103,7 +112,7 @@ generateSource( text = """ package kotlinx.rpc - const val KOTLIN_VERSION: String = "${libs.versions.kotlin.lang.get()}" + const val RPC_VERSION: String = "${libs.versions.kotlinx.rpc.get()}" const val BUILD_REPO: String = "${File(globalRootDir).resolve("build/repo").absolutePath}" """.trimIndent(), diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt index f9f8cfa6f..bb11a9c39 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt @@ -6,6 +6,7 @@ package kotlinx.rpc import kotlinx.rpc.protoc.configurePluginProtections import kotlinx.rpc.protoc.createProtoExtensions +import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.create @@ -13,6 +14,8 @@ import org.gradle.kotlin.dsl.create @Suppress("unused") public class RpcGradlePlugin : Plugin { override fun apply(target: Project) { + checkKGPIsInTheClasspath() + target.extensions.create("rpc") applyCompilerPlugin(target) @@ -28,3 +31,22 @@ public class RpcGradlePlugin : Plugin { target.plugins.apply(CompilerPluginCli::class.java) } } + +private fun checkKGPIsInTheClasspath() { + try { + Class.forName("org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin") + } catch (_: ClassNotFoundException) { + throw GradleException( + """ + Kotlin Gradle plugin is not applied to the project. + Please ensure that Kotlin Gradle plugin is applied to the project in the plugins block: + + plugins { + kotlin("jvm") version "..." // or + kotlin("multiplatform") version "..." // or + kotlin("android") version "..." + } + """.trimIndent() + ) + } +} diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index 2f7bf07ce..c39fa1aa7 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -26,7 +26,7 @@ import org.gradle.kotlin.dsl.property import org.gradle.kotlin.dsl.setProperty import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode -import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import java.io.File import java.util.* import java.util.function.Consumer @@ -57,7 +57,7 @@ internal open class DefaultProtoSourceSet( ) private val explicitApiModeEnabled = project.provider { - project.the().explicitApi != ExplicitApiMode.Disabled + project.the().explicitApi != ExplicitApiMode.Disabled } val plugins = project.objects.setProperty() @@ -137,7 +137,8 @@ internal fun Project.createProtoExtensions() { findOrCreateAndConfigure(name, this) } - project.extensions.configure("sourceSets") { + // this@createProtoExtensions: fails on KGP 2.0.0 otherwise + this@createProtoExtensions.project.extensions.configure("sourceSets") { all { val protoSourceSet = findOrCreateAndConfigure(name, this) diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt index f006bb3f4..59abdadc9 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -17,6 +17,7 @@ import kotlin.io.path.copyToRecursively import kotlin.io.path.readText import kotlin.io.path.writeText import kotlinx.rpc.BUILD_REPO +import kotlinx.rpc.RPC_VERSION import org.junit.jupiter.api.DynamicTest import java.util.stream.Stream import kotlin.io.path.absolute @@ -26,7 +27,17 @@ import kotlin.io.path.deleteRecursively class VersionsEnv( val gradle: String, val kotlin: String, -) +) { + val kotlinSemver = run { + val (major, minor, patch) = kotlin.split(".").map { it.toInt() } + KotlinVersion(major, minor, patch) + } +} + +internal object KtVersion { + val v2_2_20 = KotlinVersion(2, 2, 20) + val v2_0_0 = KotlinVersion(2, 0, 0) +} private val GradleVersions = listOf( VersionsEnv("9.2.1", "2.2.21"), @@ -103,6 +114,7 @@ abstract class BaseTest { val buildScriptFile = projectDir.resolve("build.gradle.kts") buildScriptFile .replace("", versions.kotlin) + .replace("", RPC_VERSION) println(""" Setup project '$projectName' @@ -121,7 +133,6 @@ abstract class BaseTest { .withProjectDir(projectDir.absolute().toFile()) .withTestKitDir(TEST_KIT_PATH.absolute().toFile()) .withGradleVersion(versions.gradle) - .withPluginClasspath() .withArguments( listOfNotNull( task, @@ -193,8 +204,9 @@ abstract class BaseTest { } } - protected fun Path.replace(oldValue: String, newValue: String) { + protected fun Path.replace(oldValue: String, newValue: String): Path { writeText(readText().replace(oldValue, newValue)) + return this } companion object { diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt index 7b6203a58..891db5838 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -247,13 +247,25 @@ abstract class GrpcBaseTest : BaseTest() { sourceSet: SSets, vararg imports: SSets, ) { + if (!sourceSet.applicable()) { + println("Skipping ${sourceSet.capital} source set because it's not applicable for the current Kotlin version") + return + } + val ktFile = "${sourceSet.capital}.kt" - val importsSet = imports.toSet() + val importsSet = imports + .onEach { + if (!it.applicable()) { + println("Skipping ${it.capital} import source set because it's not applicable for the current Kotlin version") + } + } + .filter { it.applicable() } + .toSet() assertTaskExecuted( sourceSet = sourceSet, protoFiles = listOf(Path("${sourceSet.name}.proto")), - importProtoFiles = imports.map { + importProtoFiles = importsSet.map { Path("${it.name}.proto") }, generatedFiles = listOf( @@ -319,16 +331,20 @@ abstract class GrpcBaseTest : BaseTest() { .resolve(name) .resolve("proto") } + + fun SSets.applicable(): Boolean { + return versions.kotlinSemver >= minKotlin + } } - @Suppress("EnumEntryName") - enum class SSets { + @Suppress("EnumEntryName", "detekt.EnumNaming") + enum class SSets(val minKotlin: KotlinVersion = KtVersion.v2_0_0) { main, test, commonMain, commonTest, jvmMain, jvmTest, androidMain, androidTest, - webMain, webTest, + webMain(KtVersion.v2_2_20), webTest(KtVersion.v2_2_20), jsMain, jsTest, nativeMain, nativeTest, appleMain, appleTest, diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts index cbb0cc5d5..fcf4fe51b 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts @@ -12,7 +12,7 @@ import javax.inject.Inject plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/can_add_custom_protoc_plugins/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/can_add_custom_protoc_plugins/build.gradle.kts index 6e50370ca..5bd7c9a26 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/can_add_custom_protoc_plugins/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/can_add_custom_protoc_plugins/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } rpc { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts index aa739abe1..6dee22de0 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts @@ -10,7 +10,7 @@ import kotlin.time.Duration.Companion.seconds plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } rpc { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_protoc_plugins_must_declare_an_artifact/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_protoc_plugins_must_declare_an_artifact/build.gradle.kts index 7cd17a0a8..2d3377da7 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_protoc_plugins_must_declare_an_artifact/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_protoc_plugins_must_declare_an_artifact/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin.sourceSets.main.proto { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts index 9cb883293..4171c3647 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin.sourceSets.apply { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/build.gradle.kts index a0d0a518b..74c09f820 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } sourceSets { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/build.gradle.kts index 8b007f1fb..6c7b6b026 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.proto plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } sourceSets { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts index f1d4dc607..32405f452 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } rpc { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts index f1d4dc607..32405f452 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } rpc { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/no_grpc/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/no_grpc/build.gradle.kts index cc6194819..47894db45 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/no_grpc/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/no_grpc/build.gradle.kts @@ -6,5 +6,5 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts index 56ee2e979..604231372 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.kotlinMultiplatform plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } rpc { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/skip_generation_when_no_proto_files/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/skip_generation_when_no_proto_files/build.gradle.kts index 810cdd8a7..9ccd5153f 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/skip_generation_when_no_proto_files/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/skip_generation_when_no_proto_files/build.gradle.kts @@ -7,7 +7,7 @@ import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin.sourceSets.main { diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts index f1d4dc607..32405f452 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("jvm") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } rpc { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts index b67cb1b57..aef77d75f 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts @@ -7,12 +7,11 @@ import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version import kotlinx.rpc.buf.tasks.* import kotlinx.rpc.buf.* -import org.gradle.api.provider.Provider import javax.inject.Inject plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { @@ -43,13 +42,24 @@ rpc { fun Iterable.toNames() = map { it.name }.toSet() +fun String.parseToKotlinVersion(): KotlinVersion { + val (major, minor, patch) = substringBefore('-').split(".").map { it.toInt() } + return KotlinVersion(major, minor, patch) +} + +val currentKotlin = "".parseToKotlinVersion() + +fun String.ifKotlinAtLeast(minVersion: String): String? { + return if (currentKotlin >= minVersion.parseToKotlinVersion()) this else null +} + fun assertTasks( tag: String, tasks: Iterable, - vararg expected: String, + vararg expected: String?, ) { val names = tasks.toNames() - val expectedSet = expected.toSet() + val expectedSet = expected.filterNotNull().toSet() if (expectedSet != names) { throw GradleException( """ @@ -73,7 +83,7 @@ tasks.register("test_tasks") { "bufGenerateMacosMain", "bufGenerateMacosTest", "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", "bufGenerateJvmMain", "bufGenerateJvmTest", - "bufGenerateWebMain", "bufGenerateWebTest", + "bufGenerateWebMain".ifKotlinAtLeast("2.2.20"), "bufGenerateWebTest".ifKotlinAtLeast("2.2.20"), "bufGenerateJsMain", "bufGenerateJsTest", ) @@ -85,7 +95,7 @@ tasks.register("test_tasks") { "bufGenerateMacosTest", "bufGenerateMacosArm64Test", "bufGenerateJvmTest", - "bufGenerateWebTest", + "bufGenerateWebTest".ifKotlinAtLeast("2.2.20"), "bufGenerateJsTest", ) @@ -97,7 +107,7 @@ tasks.register("test_tasks") { "bufGenerateMacosMain", "bufGenerateMacosArm64Main", "bufGenerateJvmMain", - "bufGenerateWebMain", + "bufGenerateWebMain".ifKotlinAtLeast("2.2.20"), "bufGenerateJsMain", ) @@ -207,7 +217,7 @@ tasks.register("test_tasks") { "buf depends on jsTest", genTasks.matchingSourceSet("jsTest").single().bufDependsOn(), "bufGenerateCommonMain", "bufGenerateCommonTest", - "bufGenerateWebMain", "bufGenerateWebTest", + "bufGenerateWebMain".ifKotlinAtLeast("2.2.20"), "bufGenerateWebTest".ifKotlinAtLeast("2.2.20"), "bufGenerateJsMain", ) @@ -222,7 +232,7 @@ tasks.register("test_tasks") { "bufGenerateMacosMain", "bufGenerateMacosTest", "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", "bufGenerateJvmMain", "bufGenerateJvmTest", - "bufGenerateWebMain", "bufGenerateWebTest", + "bufGenerateWebMain".ifKotlinAtLeast("2.2.20"), "bufGenerateWebTest".ifKotlinAtLeast("2.2.20"), "bufGenerateJsMain", "bufGenerateJsTest", // lint @@ -232,7 +242,7 @@ tasks.register("test_tasks") { "bufLintMacosMain", "bufLintMacosTest", "bufLintMacosArm64Main", "bufLintMacosArm64Test", "bufLintJvmMain", "bufLintJvmTest", - "bufLintWebMain", "bufLintWebTest", + "bufLintWebMain".ifKotlinAtLeast("2.2.20"), "bufLintWebTest".ifKotlinAtLeast("2.2.20"), "bufLintJsMain", "bufLintJsTest", ) @@ -245,7 +255,7 @@ tasks.register("test_tasks") { "bufGenerateMacosMain", "bufGenerateMacosTest", "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", "bufGenerateJvmMain", "bufGenerateJvmTest", - "bufGenerateWebMain", "bufGenerateWebTest", + "bufGenerateWebMain".ifKotlinAtLeast("2.2.20"), "bufGenerateWebTest".ifKotlinAtLeast("2.2.20"), "bufGenerateJsMain", "bufGenerateJsTest", ) @@ -257,7 +267,7 @@ tasks.register("test_tasks") { "bufLintMacosMain", "bufLintMacosTest", "bufLintMacosArm64Main", "bufLintMacosArm64Test", "bufLintJvmMain", "bufLintJvmTest", - "bufLintWebMain", "bufLintWebTest", + "bufLintWebMain".ifKotlinAtLeast("2.2.20"), "bufLintWebTest".ifKotlinAtLeast("2.2.20"), "bufLintJsMain", "bufLintJsTest", ) diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/build.gradle.kts index 20bdf1041..b07662ad4 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts index a4500a1c1..9b8449a29 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts index a4500a1c1..9b8449a29 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_grpc/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_grpc/build.gradle.kts index 1b1d627a6..af25b4955 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_grpc/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_grpc/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } // should nonetheless be available diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_jvm_targets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_jvm_targets/build.gradle.kts index 7c8d7c9c6..4762a5f89 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_jvm_targets/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/no_jvm_targets/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/build.gradle.kts index 20bdf1041..b07662ad4 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts index a4500a1c1..9b8449a29 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/with_other_targets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/with_other_targets/build.gradle.kts index 20bdf1041..b07662ad4 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/with_other_targets/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/with_other_targets/build.gradle.kts @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.version plugins { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { diff --git a/gradle-plugin/src/test/resources/template.settings.gradle.kts b/gradle-plugin/src/test/resources/template.settings.gradle.kts index 56e4c1ebd..bbf8da2a6 100644 --- a/gradle-plugin/src/test/resources/template.settings.gradle.kts +++ b/gradle-plugin/src/test/resources/template.settings.gradle.kts @@ -10,9 +10,21 @@ buildCache { } } +pluginManagement { + repositories { + repositories { + mavenCentral() + maven("") + google() + gradlePluginPortal() + } + } +} + dependencyResolutionManagement { repositories { mavenCentral() maven("") + google() } } diff --git a/versions-root/libs.versions.toml b/versions-root/libs.versions.toml index 287ff7a37..81b597e02 100644 --- a/versions-root/libs.versions.toml +++ b/versions-root/libs.versions.toml @@ -36,6 +36,7 @@ grpc-kotlin = "1.4.1" protobuf = "4.31.1" protobuf-gradle = "0.9.5" buf-tool = "1.55.1" +agp = "8.1.0" [libraries] # kotlinx.rpc – references to the included builds @@ -138,6 +139,7 @@ kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", ve dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } gradle-publish-gradle-plugin = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "gradle-plugin-publish" } compat-patrouille-gradle-plugin = { module = "com.gradleup.compat.patrouille:compat-patrouille-gradle-plugin", version.ref = "compat-patrouille" } +android-gradle-plugin = { module = "com.android.tools.build:gradle-api", version.ref = "agp" } [plugins] kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin-lang" } From bc4234b3609e6314d30a25ee16a8b249669078cd Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Wed, 3 Dec 2025 20:53:19 +0100 Subject: [PATCH 02/11] Added support for Android (not KMP) projects KRPC-203 --- gradle-plugin/build.gradle.kts | 1 + .../src/main/kotlin/kotlinx/rpc/Extensions.kt | 11 +- .../kotlinx/rpc/buf/tasks/BufExecTask.kt | 40 ++- .../kotlinx/rpc/buf/tasks/BufGenerateTask.kt | 52 ++- .../kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt | 79 ++++- .../rpc/protoc/DefaultProtoSourceSet.kt | 171 +++++++-- .../rpc/protoc/DefaultProtocExtension.kt | 328 ++++++++++-------- .../kotlinx/rpc/protoc/ProcessProtoFiles.kt | 38 +- .../kotlinx/rpc/protoc/ProtoSourceSet.kt | 20 ++ .../kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt | 9 + .../kotlinx/rpc/protoc/android/variants.kt | 45 +++ .../kotlin/kotlinx/rpc/protoc/directory.kt | 5 - .../src/main/kotlin/kotlinx/rpc/util/kgp.kt | 65 +++- versions-root/libs.versions.toml | 5 +- 14 files changed, 610 insertions(+), 259 deletions(-) create mode 100644 gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index fa16b4692..2058f0d19 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -31,6 +31,7 @@ tasks.withType().configureEach { dependencies { compileOnly(libs.kotlin.gradle.plugin) compileOnly(libs.android.gradle.plugin) + compileOnly(libs.android.gradle.plugin.api) testImplementation(libs.kotlin.gradle.plugin) testImplementation(gradleTestKit()) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt index 77a5bac5c..cbf52ce55 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt @@ -83,14 +83,13 @@ public open class RpcExtension @Inject internal constructor(objects: ObjectFacto internal val protocApplied = AtomicBoolean(false) internal val protocInternal by lazy { - if (protocApplied.get()) { + if (protocApplied.compareAndSet(false, true)) { + objects.newInstance().apply { + callbacks.forEach { it.execute(this) } + } + } else { error("Illegal access to protoc extension during DefaultProtocExtension.init") } - - protocApplied.set(true) - objects.newInstance().apply { - callbacks.forEach { it.execute(this) } - } } private val callbacks = mutableListOf>() diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt index 2203bb6d7..f9821d73f 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.buf.tasks +import com.android.build.api.variant.Variant import kotlinx.rpc.buf.BUF_EXECUTABLE_CONFIGURATION import kotlinx.rpc.buf.BufExtension import kotlinx.rpc.buf.execBuf @@ -29,7 +30,9 @@ import kotlinx.rpc.buf.BufTasksExtension import org.gradle.api.logging.LogLevel import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.SkipWhenEmpty import javax.inject.Inject /** @@ -37,12 +40,21 @@ import javax.inject.Inject */ public abstract class BufExecTask @Inject constructor( @Internal - public val properties: Properties + public val properties: Properties, ) : DefaultTask() { init { group = PROTO_GROUP } + // unsued, but required for Gradle to properly recognise inputs + @get:InputFiles + @get:SkipWhenEmpty + internal abstract val protoFiles: ListProperty + + // unsued, but required for Gradle to properly recognise inputs + @get:InputFiles + internal abstract val importProtoFiles: ListProperty + // list of buf task dependencies of the same type @get:Internal internal abstract val bufTaskDependencies: SetProperty @@ -78,17 +90,33 @@ public abstract class BufExecTask @Inject constructor( isTest: Boolean, sourceSetName: String, /** - * Name of the android flavor this task is associated with. + * Name of the android flavors this task is associated with. + * + * @see [Variant.productFlavors] */ - public val flavor: String, + public val flavors: List, /** * Name of the android build type this task is associated with. + * + * @see Variant.buildType */ - public val buildType: String, + public val buildType: String?, /** * Name of the android variant this task is associated with. + * + * @see Variant.name */ public val variant: String, + + /** + * Whether the task is for instrumentation tests. + */ + public val isAndroidTest: Boolean, + + /** + * Whether the task is for unit tests. + */ + public val isUnitTest: Boolean, ) : Properties(isTest, sourceSetName) /** @@ -164,8 +192,8 @@ internal fun Project.registerBufExecTask( configuration: T.() -> Unit = {}, ): TaskProvider = tasks.register(name, clazz, properties).apply { configure { - val executableConfiguration = configurations.getByName(BUF_EXECUTABLE_CONFIGURATION) - bufExecutable.set(executableConfiguration.singleFile) + val executableConfiguration = configurations.named(BUF_EXECUTABLE_CONFIGURATION) + bufExecutable.set(project.provider { executableConfiguration.get().singleFile }) this.workingDir.set(workingDir) val buf = provider { rpcExtension().protoc.get().buf } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt index 1ac7988df..9232d123b 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt @@ -16,8 +16,15 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskProvider import java.io.File import kotlinx.rpc.buf.BufGenerateExtension +import kotlinx.rpc.protoc.DefaultProtoSourceSet +import kotlinx.rpc.protoc.bufExecProperties +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.provider.Provider import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectories +import org.gradle.api.tasks.SkipWhenEmpty +import org.gradle.kotlin.dsl.listProperty import javax.inject.Inject /** @@ -26,16 +33,12 @@ import javax.inject.Inject * @see buf generate */ public abstract class BufGenerateTask @Inject internal constructor(properties: Properties) : BufExecTask(properties) { - // unsued, but required for Gradle to properly recognise inputs - @get:InputDirectory - internal abstract val protoFilesDir: Property - - // unsued, but required for Gradle to properly recognise inputs - @get:InputDirectory - internal abstract val importFilesDir: Property + // used to properly calculate output directories + @get:Input + internal abstract val pluginNames: ListProperty /** - * List of files used during `buf generate` command execution. + * List of executable files used during `buf generate` command execution. * * @see [ProtocPlugin.Artifact.Local.executableFiles] */ @@ -82,11 +85,22 @@ public abstract class BufGenerateTask @Inject internal constructor(properties: P public abstract val additionalArgs: ListProperty /** - * The directory to output generated files. + * The directory to output generated files to, used as a `buf generate --output` argument, + * not the directory for sources. For that see [outputSourceDirectories]. */ @get:OutputDirectory public abstract val outputDirectory: Property + private val outputSourceDirectoriesInternal: ListProperty = project.objects.listProperty() + + /** + * Generated source directories by plugin name. + * + * Can be used in [SourceDirectorySet.srcDirs]. + */ + @get:OutputDirectories + public val outputSourceDirectories: Provider> = outputSourceDirectoriesInternal + init { command.set("generate") @@ -110,6 +124,11 @@ public abstract class BufGenerateTask @Inject internal constructor(properties: P } this.args.set(args) + + outputSourceDirectoriesInternal.set(pluginNames.map { plugins -> + val out = outputDirectory.get() + plugins.map { out.resolve(it) } + }) } internal companion object { @@ -118,20 +137,16 @@ public abstract class BufGenerateTask @Inject internal constructor(properties: P } internal fun Project.registerBufGenerateTask( - sourceSetName: String, + protoSourceSet: DefaultProtoSourceSet, workingDir: File, outputDirectory: File, - protoFilesDir: File, - importFilesDir: File, + includedPlugins: Provider>, configure: BufGenerateTask.() -> Unit = {}, ): TaskProvider { - val capitalName = sourceSetName.replaceFirstChar { it.uppercase() } + val capitalName = protoSourceSet.name.replaceFirstChar { it.uppercase() } val bufGenerateTaskName = "${BufGenerateTask.NAME_PREFIX}$capitalName" - val properties = BufExecTask.Properties( - isTest = sourceSetName.lowercase().endsWith("test"), - sourceSetName = sourceSetName, - ) + val properties = protoSourceSet.bufExecProperties() return registerBufExecTask(bufGenerateTaskName, provider { workingDir }, properties) { group = PROTO_GROUP @@ -145,8 +160,7 @@ internal fun Project.registerBufGenerateTask( this.outputDirectory.set(outputDirectory) - this.protoFilesDir.set(protoFilesDir) - this.importFilesDir.set(importFilesDir) + this.pluginNames.set(includedPlugins.map { it.map { plugin -> plugin.name } }) configure() } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt index a4c57ff7e..1b10c4391 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt @@ -333,7 +333,35 @@ public sealed interface BufTasks : TaskCollection + + /** + * Android only. + * + * Filters tasks by where [BufExecTask.AndroidProperties.isAndroidTest] is `true`. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().androidTestTasks() + * } + * ``` + */ + public fun androidTestTasks(): BufTasks + + /** + * Android only. + * + * Filters tasks by where [BufExecTask.AndroidProperties.flavors] matches the given flavor. * * Only returns Android tasks. * @@ -346,6 +374,8 @@ public sealed interface BufTasks : TaskCollection /** + * Android only. + * * Filters tasks by where [BufExecTask.AndroidProperties.buildType] matches the given buildType. * * Only returns Android tasks. @@ -356,9 +386,11 @@ public sealed interface BufTasks : TaskCollection + public fun matchingBuildType(buildType: String?): BufTasks /** + * Android only. + * * Filters tasks by where [BufExecTask.AndroidProperties.variant] matches the given variant. * * Only returns Android tasks. @@ -532,21 +564,53 @@ internal open class BufTasksImpl internal constructor( return BufTasksImpl(project, collection.matching { !it.properties.isTest }, kClass) } + override fun unitTestTasks(): BufTasks { + return BufTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? BufExecTask.AndroidProperties + ?: return@matching false + + properties.isUnitTest + }, + kClass = kClass, + ) + } + + override fun androidTestTasks(): BufTasks { + return BufTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? BufExecTask.AndroidProperties + ?: return@matching false + + properties.isAndroidTest + }, + kClass = kClass, + ) + } + override fun matchingFlavor(flavor: String): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - (it.properties as? BufExecTask.AndroidProperties)?.flavor == flavor + val properties = it.properties as? BufExecTask.AndroidProperties + ?: return@matching false + + properties.flavors.contains(flavor) }, kClass = kClass, ) } - override fun matchingBuildType(buildType: String): BufTasks { + override fun matchingBuildType(buildType: String?): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - (it.properties as? BufExecTask.AndroidProperties)?.buildType == buildType + val properties = it.properties as? BufExecTask.AndroidProperties + ?: return@matching false + + properties.buildType == buildType }, kClass = kClass, ) @@ -556,7 +620,10 @@ internal open class BufTasksImpl internal constructor( return BufTasksImpl( project = project, collection = collection.matching { - (it.properties as? BufExecTask.AndroidProperties)?.variant == variant + val properties = it.properties as? BufExecTask.AndroidProperties + ?: return@matching false + + properties.variant == variant }, kClass = kClass, ) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index c39fa1aa7..a8fbdb6b0 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -4,22 +4,24 @@ package kotlinx.rpc.protoc +import kotlinx.rpc.buf.tasks.BufExecTask import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.rpcExtension import kotlinx.rpc.util.findOrCreate -import kotlinx.rpc.util.withLazyKotlinJvmExtension -import kotlinx.rpc.util.withLazyKotlinKmpExtension +import kotlinx.rpc.util.withLazyJavaPluginExtension +import kotlinx.rpc.util.withKotlinSourceSets import org.gradle.api.Action import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectFactory import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider -import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.provider.SetProperty import org.gradle.kotlin.dsl.add import org.gradle.kotlin.dsl.listProperty import org.gradle.kotlin.dsl.property @@ -27,6 +29,8 @@ import org.gradle.kotlin.dsl.setProperty import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer +import org.jetbrains.kotlin.tooling.core.extrasKeyOf import java.io.File import java.util.* import java.util.function.Consumer @@ -37,30 +41,52 @@ internal val Project.protoSourceSets: ProtoSourceSets get() = extensions.findByName(PROTO_SOURCE_SETS) as? ProtoSourceSets ?: throw GradleException("Unable to find proto source sets in project $name") -internal class ProtoSourceSetFactory(private val project: Project) : NamedDomainObjectFactory { +internal class ProtoSourceSetFactory( + private val project: Project, +) : NamedDomainObjectFactory { override fun create(name: String): ProtoSourceSet { - return project.objects.newInstance(DefaultProtoSourceSet::class.java, project, name) + val isAndroid = project.the() + .sourceSets.findByName(name) + ?.extras + ?.get(isAndroidKey) + ?: false + + return project.objects.newInstance(DefaultProtoSourceSet::class.java, project, name, isAndroid) } } +internal fun Project.findOrCreateProtoSourceSets(): NamedDomainObjectContainer = + project.findOrCreate(PROTO_SOURCE_SETS) { + val container = objects.domainObjectContainer( + ProtoSourceSet::class.java, + ProtoSourceSetFactory(project) + ) + + project.extensions.add(PROTO_SOURCE_SETS, container) + + container + } + internal open class DefaultProtoSourceSet( internal val project: Project, - private val sourceDirectorySet: SourceDirectorySet, + internal val sourceDirectorySet: SourceDirectorySet, + internal val isAndroid: Boolean, ) : ProtoSourceSet, SourceDirectorySet by sourceDirectorySet { @Inject - constructor(project: Project, protoName: String) : this( + constructor(project: Project, protoName: String, isAndroid: Boolean) : this( project = project, sourceDirectorySet = project.objects.sourceDirectorySet(protoName, "Proto sources for $protoName").apply { srcDirs("src/${protoName}/proto") }, + isAndroid = isAndroid, ) private val explicitApiModeEnabled = project.provider { project.the().explicitApi != ExplicitApiMode.Disabled } - val plugins = project.objects.setProperty() + override val plugins = project.objects.setProperty() override fun plugin(plugin: ProtocPlugin, configure: Action?) { plugins.add(plugin.copy().also { initPlugin(it, configure) }) @@ -99,7 +125,82 @@ internal open class DefaultProtoSourceSet( } val languageSourceSets: ListProperty = project.objects.listProperty() - val generateTask: Property = project.objects.property() + val generateTask: Property = project.objects.property() + + // main/test for Kotlin/Android, androidMain/androidTest for KMP + // only set for variant.name sourceSets + val androidRoot: Property = project.objects.property() + // used to track test tasks' dependencies for main tasks, e.g.: + // - bufGenerateTestDebug depends on bufGenerateDebug + // + // so we need to track this dependency using this property + val androidMain: Property = project.objects.property() + + // only set for variant.name sourceSets + val androidProperties: Property = + project.objects.property() + .convention(null) + + override val imports: SetProperty = project.objects.setProperty() + override val fileImports: ConfigurableFileCollection = project.objects.fileCollection() + + override fun importsFrom(protoSourceSet: ProtoSourceSet) { + imports.add(protoSourceSet.checkSelfImport()) + imports.addAll(protoSourceSet.imports.checkSelfImport()) + } + + override fun importsFrom(protoSourceSet: Provider) { + imports.add(protoSourceSet.checkSelfImport()) + imports.addAll(protoSourceSet.flatMap { it.imports.checkSelfImport() }) + } + + override fun importsAllFrom(protoSourceSet: Provider>) { + imports.addAll(protoSourceSet.checkSelfImport()) + imports.addAll(protoSourceSet.map { list -> list.flatMap { it.imports.checkSelfImport().get() } }) + } + + override fun importsFrom(protoSourceSet: NamedDomainObjectProvider) { + imports.add(protoSourceSet.checkSelfImport()) + imports.addAll(protoSourceSet.flatMap { it.imports.checkSelfImport() }) + } + + override fun extendsFrom(protoSourceSet: ProtoSourceSet) { + if (this == protoSourceSet) { + throw IllegalArgumentException("$name proto source set cannot extend from self") + } + + if (protoSourceSet !is DefaultProtoSourceSet) { + throw IllegalArgumentException( + "$name proto source set can only extend from other default proto source sets." + + "${protoSourceSet.name} is not a ${DefaultProtoSourceSet::class.simpleName}", + ) + } + + source(protoSourceSet.sourceDirectorySet) + imports.addAll(protoSourceSet.imports.checkSelfImport()) + } + + @JvmName("checkSelfImport_provider") + private fun Provider.checkSelfImport() = map { + it.checkSelfImport() + } + + private fun SetProperty.checkSelfImport() = map { set -> + set.onEach { it.checkSelfImport() } + } + + @JvmName("checkSelfImport_provider_list") + private fun Provider>.checkSelfImport() = map { set -> + set.onEach { it.checkSelfImport() } + } + + private fun ProtoSourceSet.checkSelfImport(): ProtoSourceSet { + if (this@DefaultProtoSourceSet == this) { + throw IllegalArgumentException("${this@DefaultProtoSourceSet.name} proto source set cannot import from itself") + } + + return this + } // Java default methods @@ -113,18 +214,11 @@ internal open class DefaultProtoSourceSet( } internal fun Project.createProtoExtensions() { - fun findOrCreateAndConfigure(languageSourceSetName: String, languageSourceSet: Any?): ProtoSourceSet { - val container = project.findOrCreate(PROTO_SOURCE_SETS) { - val container = objects.domainObjectContainer( - ProtoSourceSet::class.java, - ProtoSourceSetFactory(project) - ) - - project.extensions.add(PROTO_SOURCE_SETS, container) - - container - } - + fun findOrCreateAndConfigure( + languageSourceSetName: String, + languageSourceSet: Any?, + ): ProtoSourceSet { + val container = findOrCreateProtoSourceSets() val protoSourceSet = container.maybeCreate(languageSourceSetName) as DefaultProtoSourceSet languageSourceSet?.let { protoSourceSet.languageSourceSets.add(it) } @@ -132,26 +226,35 @@ internal fun Project.createProtoExtensions() { return protoSourceSet } - project.withLazyKotlinJvmExtension { - sourceSets.all { + project.withKotlinSourceSets { isAndroid, extension -> + extension.sourceSets.all { + extras[isAndroidKey] = isAndroid findOrCreateAndConfigure(name, this) } + } - // this@createProtoExtensions: fails on KGP 2.0.0 otherwise - this@createProtoExtensions.project.extensions.configure("sourceSets") { - all { - val protoSourceSet = findOrCreateAndConfigure(name, this) + project.withLazyJavaPluginExtension { + sourceSets.configureEach { + val protoSourceSet = findOrCreateAndConfigure(name, this) - findOrCreate(PROTO_SOURCE_SET_EXTENSION_NAME) { - extensions.add(PROTO_SOURCE_SET_EXTENSION_NAME, protoSourceSet) - } + findOrCreate(PROTO_SOURCE_SET_EXTENSION_NAME) { + extensions.add(PROTO_SOURCE_SET_EXTENSION_NAME, protoSourceSet) } } } +} - project.withLazyKotlinKmpExtension { - sourceSets.all { - findOrCreateAndConfigure(name, this) - } +private val isAndroidKey = extrasKeyOf("kxrpc_proto_source_set_is_android") + +internal fun DefaultProtoSourceSet.bufExecProperties(): BufExecTask.Properties { + // main has androidRoot.isPresent=false and should not be included, although it is an Android source set + return if (isAndroid && androidRoot.isPresent) { + androidProperties.orNull + ?: throw GradleException("Android properties are not set for source set $name") + } else { + BufExecTask.Properties( + isTest = name.lowercase().endsWith("test"), + sourceSetName = name, + ) } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index 8dc9443af..243025c61 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -4,6 +4,8 @@ package kotlinx.rpc.protoc +import com.android.build.api.variant.Variant +import com.android.build.gradle.BaseExtension import kotlinx.rpc.buf.BufExtension import kotlinx.rpc.buf.configureBufExecutable import kotlinx.rpc.buf.tasks.BufExecTask @@ -16,27 +18,23 @@ import kotlinx.rpc.buf.tasks.registerGenerateBufGenYamlTask import kotlinx.rpc.buf.tasks.registerGenerateBufYamlTask import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM +import kotlinx.rpc.protoc.android.AndroidRootSourceSets +import kotlinx.rpc.protoc.android.sourceSets import kotlinx.rpc.util.ensureDirectoryExists -import kotlinx.rpc.util.kotlinJvmExtensionOrNull -import kotlinx.rpc.util.kotlinKmpExtensionOrNull +import kotlinx.rpc.util.withLazyAndroidComponentsExtension import org.gradle.api.Action import org.gradle.api.GradleException -import org.gradle.api.Named import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileTree import org.gradle.api.file.SourceDirectorySet import org.gradle.api.model.ObjectFactory -import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.Provider import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.compile.JavaCompile -import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.newInstance -import org.gradle.kotlin.dsl.withType +import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import java.io.File import javax.inject.Inject import kotlin.collections.filterIsInstance @@ -88,15 +86,101 @@ internal open class DefaultProtocExtension @Inject constructor( } project.protoSourceSets.all { - if (this !is DefaultProtoSourceSet) { + if (this !is DefaultProtoSourceSet || isAndroid) { return@all } project.configureTasks(this) } + + project.withLazyAndroidComponentsExtension { + val rootSourceSets = AndroidRootSourceSets.entries + .mapNotNull { rootName -> + val sourceSet = project.protoSourceSets.findByName(rootName.stringValue) + as? DefaultProtoSourceSet + + sourceSet?.let { rootName to it } + }.toMap() + + val mainRoot = rootSourceSets.getValue(AndroidRootSourceSets.Main) + val testRoot = rootSourceSets[AndroidRootSourceSets.Test] + val androidTestRoot = rootSourceSets[AndroidRootSourceSets.AndroidTest] + val testFixturesRoot = rootSourceSets[AndroidRootSourceSets.TestFixtures] + + // todo KMP imports + + testRoot?.extendsFrom(testFixturesRoot ?: mainRoot) + androidTestRoot?.extendsFrom(testFixturesRoot ?: mainRoot) + testFixturesRoot?.importsFrom(mainRoot) + + val extension = project.the() + onVariants { variant: Variant -> + val testBuildType = extension.testBuildType + rootSourceSets.forEach { (rootName, rootSourceSet) -> + // testFixtures don't have variants + if (rootName == AndroidRootSourceSets.TestFixtures) { + return@forEach + } + + if (rootName == AndroidRootSourceSets.AndroidTest && variant.buildType != testBuildType) { + return@forEach + } + + // but testFixtures still have source sets based on flavors + val testFixtureSets = if (rootName != AndroidRootSourceSets.Main) { + variant.sourceSets(AndroidRootSourceSets.TestFixtures) + } else emptyList() + + val sourceSets = variant.sourceSets(rootName) + val variantSourceSetName = sourceSets.lastOrNull() + ?: throw GradleException("No source sets found for variant ${variant.name}") + + val variantProtoSourceSet = project.protoSourceSets.named(variantSourceSetName) + + variantProtoSourceSet.configure { + val default = this as? DefaultProtoSourceSet ?: return@configure + + default.androidRoot.set(rootSourceSet) + val properties = BufExecTask.AndroidProperties( + isTest = rootName != AndroidRootSourceSets.Main, + isAndroidTest = rootName == AndroidRootSourceSets.AndroidTest, + isUnitTest = rootName == AndroidRootSourceSets.Test, + sourceSetName = variantSourceSetName, + buildType = variant.buildType, + flavors = variant.productFlavors.map { (_, flavor) -> flavor }, + variant = variant.name, + ) + default.androidProperties.set(properties) + + (sourceSets + testFixtureSets).forEach { dependency -> + val dependencyProtoSourceSet = project.protoSourceSets.findByName(dependency) + ?: return@forEach + + if (name != dependency) { + extendsFrom(dependencyProtoSourceSet) + } + } + + if (rootName != AndroidRootSourceSets.Main) { + val mainVariantProtoSourceSet = project.protoSourceSets.findByName(variant.name) + as? DefaultProtoSourceSet + + if (mainVariantProtoSourceSet != null) { + default.androidMain.set(mainVariantProtoSourceSet) + + importsFrom(mainVariantProtoSourceSet) + } + } + + project.configureTasks(default) + } + } + } + } } private fun ProtocPlugin.defaultOptions() { + isKotlin.set(true) options.put("debugOutput", "protoc-gen-$name.log") options.put("generateComments", buf.generate.comments.copyComments) @@ -118,7 +202,7 @@ internal open class DefaultProtocExtension @Inject constructor( .ensureDirectoryExists() // only resolve in task's 'execute' due to the deferred nature of dependsOn - val importsProvider = protoSourceSet.getImports(protoSourceSets) + protoSourceSet.setupDefaultImports(protoSourceSets) val includedProtocPlugins = provider { protoSourceSet.plugins.get().also { list -> @@ -133,18 +217,19 @@ internal open class DefaultProtocExtension @Inject constructor( } } - val protoFiles = protoSourceSet as SourceDirectorySet + val protoFilesDirectorySet = protoSourceSet as SourceDirectorySet val processProtoTask = registerProcessProtoFilesTask( name = baseName, destination = buildSourceSetsProtoDir, - protoFiles = protoFiles, + protoFilesDirectorySet = protoFilesDirectorySet, ) val processImportProtoTask = registerProcessProtoFilesImportsTask( name = baseName, destination = buildSourceSetsImportDir, - importsProvider = importsProvider, + importsProvider = protoSourceSet.imports, + rawImports = protoSourceSet.fileImports, ) { dependsOn(processProtoTask) } @@ -154,7 +239,7 @@ internal open class DefaultProtocExtension @Inject constructor( buildSourceSetsDir = buildSourceSetsDir, buildSourceSetsProtoDir = buildSourceSetsProtoDir, buildSourceSetsImportDir = buildSourceSetsImportDir, - withImport = importsProvider.map { it.isNotEmpty() }, + withImport = protoSourceSet.imports.map { it.isNotEmpty() }, ) { dependsOn(processProtoTask) } @@ -170,11 +255,10 @@ internal open class DefaultProtocExtension @Inject constructor( val sourceSetsProtoDirFileTree = fileTree(buildSourceSetsProtoDir) val bufGenerateTask = registerBufGenerateTask( - sourceSetName = baseName, + protoSourceSet = protoSourceSet, workingDir = buildSourceSetsDir, outputDirectory = protoBuildDirGenerated.resolve(baseName), - protoFilesDir = buildSourceSetsProtoDir, - importFilesDir = buildSourceSetsImportDir, + includedPlugins = includedProtocPlugins, ) { executableFiles.addAll( includedProtocPlugins.map { list -> @@ -190,19 +274,22 @@ internal open class DefaultProtocExtension @Inject constructor( } ) + protoFiles.set(processProtoTask.map { it.outputs.files }) + importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) + dependsOn(generateBufGenYamlTask) dependsOn(generateBufYamlTask) dependsOn(processProtoTask) dependsOn(processImportProtoTask) val dependencies = project.provider { - protoSourceSet.getDependsOn(protoSourceSets).map { it.generateTask.get() } + protoSourceSet.getDependsOnTasksOf(protoSourceSets).mapNotNull { it.generateTask.orNull } } dependsOn(dependencies) - bufTaskDependencies.set(importsProvider.map { list -> - list.map { it.generateTask.get().name } + bufTaskDependencies.set(protoSourceSet.imports.map { list -> + list.filterIsInstance().mapNotNull { it.generateTask.orNull?.name } }) onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } @@ -210,54 +297,13 @@ internal open class DefaultProtocExtension @Inject constructor( protoSourceSet.generateTask.set(bufGenerateTask) - val compilationNameTestTag = if (baseName.lowercase().endsWith("test")) "Test" else "" - val compileTargetName = baseName.replaceFirstChar { it.uppercase() } - .removeSuffix("Main") - .removeSuffix("Test") - .removeSuffix("main") - .removeSuffix("test") - - // compileKotlin - main - // compileTestKotlin - test - // compileKotlinJvm - jvmMain - // compileTestKotlinJvm - jvmTest - // compileKotlinIosArm64 - iosArm64Main - // compileTestKotlinIosArm64 - iosArm64Test - val kotlinCompilationName = "compile${compilationNameTestTag}Kotlin${compileTargetName}" - - project.tasks.withType>().all { - if (name == kotlinCompilationName) { - dependsOn(bufGenerateTask) - } - } - - project.tasks.withType().all { - // compileJvmTestJava - test (java, kmp) - // compileJvmMainJava - main (java, kmp) - // compileJava - main (java) - // compileTestJava - test (java) - val taskNameAsSourceSet = when (name) { - "compileJvmTestJava" -> "test" - "compileJvmMainJava" -> "main" - "compileJava" -> "main" - "compileTestJava" -> "test" - - else -> throw GradleException("Unknown java compile task name: $name") - } - - if (taskNameAsSourceSet == baseName) { - dependsOn(bufGenerateTask) - } - } - configureSourceDirectories( - baseName = baseName, + protoSourceSet = protoSourceSet, includedProtocPlugins = includedProtocPlugins, bufGenerateTask = bufGenerateTask, ) configureCustomTasks( - baseName = baseName, protoSourceSet = protoSourceSet, buildSourceSetsDir = buildSourceSetsDir, generateBufYamlTask = generateBufYamlTask, @@ -265,49 +311,47 @@ internal open class DefaultProtocExtension @Inject constructor( processProtoTask = processProtoTask, processImportProtoTask = processImportProtoTask, sourceSetsProtoDirFileTree = sourceSetsProtoDirFileTree, - importsProvider = importsProvider, - ) + ) { + protoFiles.set(processProtoTask.map { it.outputs.files }) + importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) + } } - private fun Project.configureSourceDirectories( - baseName: String, + private fun configureSourceDirectories( + protoSourceSet: DefaultProtoSourceSet, includedProtocPlugins: Provider>, bufGenerateTask: TaskProvider, ) { - // locates correctly jvmMain, main jvmTest, test - extensions.findByType()?.sourceSets?.all { - val javaSourceSet = this - - if (javaSourceSet.name == baseName) { - val javaSourcesProvider = includedProtocPlugins.map { plugins -> - val out = bufGenerateTask.get().outputDirectory.get() - plugins.filter { it.isJava.get() }.map { out.resolve(it.name) } - } + val languageSets = protoSourceSet.languageSourceSets.get() - javaSourceSet.java.srcDirs(javaSourcesProvider) - } + val javaOutputs = languageOutputs(includedProtocPlugins, bufGenerateTask) { it.isJava.get() } + val kotlinOutputs = languageOutputs(includedProtocPlugins, bufGenerateTask) { it.isKotlin.get() } + + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.java.srcDirs(javaOutputs) } - val kotlinSourcesProvider = includedProtocPlugins.map { plugins -> - val out = bufGenerateTask.get().outputDirectory.get() - plugins.filter { !it.isJava.get() }.map { out.resolve(it.name) } + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.kotlin.srcDirs(kotlinOutputs) } + } - project.kotlinJvmExtensionOrNull?.sourceSets?.all { - if (name == baseName) { - kotlin.srcDirs(kotlinSourcesProvider) - } + private fun languageOutputs( + includedProtocPlugins: Provider>, + bufGenerateTask: TaskProvider, + pluginFilter: (ProtocPlugin) -> Boolean, + ): Provider> { + val plugins = includedProtocPlugins.map { plugins -> + plugins.filter(pluginFilter).map { it.name }.toSet() } - project.kotlinKmpExtensionOrNull?.sourceSets?.all { - if (name == baseName) { - kotlin.srcDirs(kotlinSourcesProvider) - } + return bufGenerateTask.flatMap { task -> + val pluginsSet = plugins.get() + task.outputSourceDirectories.map { list -> list.filter { it.name in pluginsSet } } } } private fun Project.configureCustomTasks( - baseName: String, protoSourceSet: DefaultProtoSourceSet, buildSourceSetsDir: File, generateBufYamlTask: TaskProvider, @@ -315,8 +359,10 @@ internal open class DefaultProtocExtension @Inject constructor( processProtoTask: TaskProvider, processImportProtoTask: TaskProvider, sourceSetsProtoDirFileTree: ConfigurableFileTree, - importsProvider: Provider>, + configure: BufExecTask.() -> Unit, ) { + val baseName = protoSourceSet.name + buf.tasks.customTasks.all { val taskCapital = name.replaceFirstChar { it.uppercase() } fun taskName(baseName: String): String { @@ -324,15 +370,10 @@ internal open class DefaultProtocExtension @Inject constructor( return "buf$taskCapital$baseCapital" } - val properties = BufExecTask.Properties( - isTest = baseName.lowercase().endsWith("test"), - sourceSetName = baseName, - ) - registerBufExecTask( clazz = kClass, workingDir = provider { buildSourceSetsDir }, - properties = properties, + properties = protoSourceSet.bufExecProperties(), name = taskName(baseName), ) { dependsOn(generateBufYamlTask) @@ -341,77 +382,84 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(processImportProtoTask) val dependencies = project.provider { - protoSourceSet.getDependsOn(protoSourceSets).map { dependency -> + protoSourceSet.getDependsOnTasksOf(protoSourceSets).map { dependency -> project.tasks.named(taskName(dependency.name), kClass.java).get() } } dependsOn(dependencies) - bufTaskDependencies.set(importsProvider.map { list -> - list.map { dependency -> - project.tasks.named(taskName(dependency.name), kClass.java).get().name + bufTaskDependencies.set(protoSourceSet.imports.map { list -> + list.mapNotNull { dependency -> + project.tasks.findByName(taskName(dependency.name))?.name } }) + configure() + onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } } } } - private fun DefaultProtoSourceSet.getImports( - protoSourceSets: ProtoSourceSets, - ): Provider> { - return when { + private fun DefaultProtoSourceSet.setupDefaultImports(protoSourceSets: ProtoSourceSets) { + if (isAndroid) { + // imports are set up on variant + return + } + + val calculatedImports: Provider> = when { name.lowercase().endsWith("main") -> { getImportsForTestOrMain(protoSourceSets) } name.lowercase().endsWith("test") -> { - val main = getImportsForTestOrMain(protoSourceSets) - val test = (project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet) + val test = getImportsForTestOrMain(protoSourceSets) + val main = (correspondingMainNameSourceSet(name, protoSourceSets) as? DefaultProtoSourceSet) ?.getImportsForTestOrMain(protoSourceSets) - if (test == null) main else main.zip(test) { a, b -> a + b } + if (main == null) test else test.zip(main) { a, b -> a + b } } else -> { - throw GradleException("Unknown source set name: $name") + project.provider { emptyList() } } } + + importsAllFrom(calculatedImports) } private fun DefaultProtoSourceSet.getImportsForTestOrMain( protoSourceSets: ProtoSourceSets, - ): Provider> { + ): Provider> { return languageSourceSets.map { list -> - list.filterIsInstance().flatMap { - it.dependsOn.mapNotNull { dependency -> - (protoSourceSets.getByName(dependency.name) as? DefaultProtoSourceSet) - }.flatMap { proto -> - // can't use plus because DefaultProtoSourceSet is Iterable - proto.getImportsForTestOrMain(protoSourceSets).get().toMutableList().apply { - add(proto) - } - } - } + list.filterIsInstance().mapNotNull { - if (it.name.endsWith("Test")) { - project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet - } else { - null - } - } + list.filterIsInstance().mapNotNull { - if (it.name == SourceSet.TEST_SOURCE_SET_NAME) { - project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet - } else { - null - } + val kotlin = list.filterIsInstance() + val java = list.filterIsInstance() + + kotlin.flatMap { + it.dependsOn.mapNotNull { dep -> protoSourceSets.getByName(dep.name) } + } + kotlin.mapNotNull { + correspondingMainNameSourceSet(it.name, protoSourceSets) + } + java.mapNotNull { + correspondingMainNameSourceSet(it.name, protoSourceSets) } } } } -internal fun DefaultProtoSourceSet.getDependsOn(protoSourceSets: ProtoSourceSets): List { +/** + * Return a list of [DefaultProtoSourceSet] that have tasks that [this] will depend on. + * It's a different list from [DefaultProtoSourceSet.imports], + * as not all [DefaultProtoSourceSet] have associated tasks (on Android, for example) + */ +internal fun DefaultProtoSourceSet.getDependsOnTasksOf(protoSourceSets: ProtoSourceSets): List { + // todo check KMP + // androidMain/androidTest for KMP are evaluated as usual (they don't have androidRoot prop) + // androidRoot.isPresent == this is not androidMain/androidTest + if (isAndroid && androidRoot.isPresent) { + return listOfNotNull(androidRoot.get(), androidMain.orNull) + } + val sourceSets = languageSourceSets.get() val kmpDependsOn = sourceSets @@ -424,22 +472,20 @@ internal fun DefaultProtoSourceSet.getDependsOn(protoSourceSets: ProtoSourceSets protoSourceSets.getByName(it) as? DefaultProtoSourceSet } - val kmp = if (name.endsWith("Test")) { - (protoSourceSets.getByName(correspondingMainName()) as? DefaultProtoSourceSet) - } else { - null - } + val main = correspondingMainNameSourceSet(name, protoSourceSets) as? DefaultProtoSourceSet + + return (kmpDependsOn + main).filterNotNull() +} - val jvm = if (name == SourceSet.TEST_SOURCE_SET_NAME) { - (protoSourceSets.getByName(correspondingMainName()) as? DefaultProtoSourceSet) +private fun correspondingMainNameSourceSet(name: String, protoSourceSets: ProtoSourceSets): ProtoSourceSet? { + return if (name.lowercase().endsWith("test")) { + protoSourceSets.findByName(correspondingMainName(name)) } else { null } - - return (kmpDependsOn + kmp + jvm).filterNotNull() } -private fun Named.correspondingMainName(): String { +private fun correspondingMainName(name: String): String { return when { name == "test" -> "main" name.endsWith("Test") -> name.removeSuffix("Test") + "Main" diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt index 1b317f712..ed630d9ce 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt @@ -4,12 +4,12 @@ package kotlinx.rpc.protoc -import kotlinx.rpc.util.ensureRegularFileExists import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.register import java.io.File @@ -17,7 +17,7 @@ import java.io.File /** * Copy proto files to a temporary directory for Buf to process. */ -public abstract class ProcessProtoFiles internal constructor(): Copy() { +public abstract class ProcessProtoFiles internal constructor(): Sync() { init { group = PROTO_GROUP } @@ -26,29 +26,19 @@ public abstract class ProcessProtoFiles internal constructor(): Copy() { internal fun Project.registerProcessProtoFilesTask( name: String, destination: File, - protoFiles: SourceDirectorySet, + protoFilesDirectorySet: SourceDirectorySet, configure: ProcessProtoFiles.() -> Unit = {}, ): TaskProvider { val capitalName = name.replaceFirstChar { it.uppercase() } return tasks.register("process${capitalName}ProtoFiles") { - // this task deletes the destination directory if it results in NO-SOURCE, - // which breaks the configuration cache for bufGenerate - // so we prevent NO-SOURCE by creating a .keep file in the destination directory - val keep = protoBuildDirSourceSetsKeep.ensureRegularFileExists() - from(keep) - - from(files(protoFiles.sourceDirectories)) { - include(protoFiles.includes) - exclude(protoFiles.excludes) + from(files(protoFilesDirectorySet.sourceDirectories)) { + include(protoFilesDirectorySet.includes) + exclude(protoFilesDirectorySet.excludes) } into(destination) - doFirst { - destination.deleteRecursively() - } - configure() } } @@ -56,18 +46,13 @@ internal fun Project.registerProcessProtoFilesTask( internal fun Project.registerProcessProtoFilesImportsTask( name: String, destination: File, - importsProvider: Provider>, + importsProvider: Provider>, + rawImports: ConfigurableFileCollection, configure: ProcessProtoFiles.() -> Unit = {}, ): TaskProvider { val capitalName = name.replaceFirstChar { it.uppercase() } return tasks.register("process${capitalName}ProtoFilesImports") { - // this task deletes the destination directory if it results in NO-SOURCE, - // which breaks the configuration cache for bufGenerate - // so we prevent NO-SOURCE by creating a .keep file in the destination directory - val keep = protoBuildDirSourceSetsKeep.ensureRegularFileExists() - from(keep) - duplicatesStrategy = DuplicatesStrategy.FAIL val allImports = importsProvider.map { list -> @@ -80,13 +65,10 @@ internal fun Project.registerProcessProtoFilesImportsTask( } from(allImports) + from(rawImports) into(destination) - doFirst { - destination.deleteRecursively() - } - configure() } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt index 208683b20..119d55222 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt @@ -7,10 +7,14 @@ package kotlinx.rpc.protoc import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Provider +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.SourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import java.io.File public typealias ProtoSourceSets = NamedDomainObjectContainer @@ -114,6 +118,22 @@ public sealed interface ProtoSourceSet : SourceDirectorySet { configure: Action? = null, select: NamedDomainObjectContainer.() -> ProtocPlugin, ) + + public val plugins: SetProperty + + public fun importsFrom(protoSourceSet: ProtoSourceSet) + + public fun importsFrom(protoSourceSet: Provider) + + public fun importsAllFrom(protoSourceSet: Provider>) + + public fun importsFrom(protoSourceSet: NamedDomainObjectProvider) + + public val imports: SetProperty + + public val fileImports: ConfigurableFileCollection + + public fun extendsFrom(protoSourceSet: ProtoSourceSet) } /** diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt index 9c60a07f0..c019cbd13 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt @@ -67,6 +67,14 @@ public open class ProtocPlugin internal constructor( */ public val isJava: Property = project.objects.property().convention(false) + /** + * Whether the plugin generates Kotlin code. + * + * Plugins that have this property set to `true` will have their output directory + * added to the source set's `kotlin` source directory set if present. + */ + public val isKotlin: Property = project.objects.property().convention(false) + /** * Protoc plugins options. * @@ -285,6 +293,7 @@ public open class ProtocPlugin internal constructor( return ProtocPlugin(name, project) .also { it.isJava.set(isJava) + it.isKotlin.set(isKotlin) it.options.set(options) it.artifact.set(artifact) it.strategy.set(strategy) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt new file mode 100644 index 000000000..b019fa9d8 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.rpc.protoc.android + +import com.android.build.api.variant.Variant + +internal enum class AndroidRootSourceSets(val stringValue: String) { + // todo KMP? + Main("main"), + Test("test"), + AndroidTest("androidTest"), + TestFixtures("testFixtures"), + ; + + override fun toString(): String = stringValue +} + +internal fun Variant.sourceSets(rootName: AndroidRootSourceSets) = buildList { + fun String.prefixed() = prefixed(rootName) + + add(rootName.stringValue) + + productFlavors.reversed().forEach { (_, flavorName) -> + add(flavorName.prefixed()) + } + + if (productFlavors.size > 1) { + val combinationName = productFlavors + .joinToString("") { (_, name) -> name.replaceFirstChar { it.uppercase() } } + .replaceFirstChar { it.lowercase() } + + add(combinationName.prefixed()) + } + + buildType?.let { add(it.prefixed()) } + add(name.prefixed()) +} + +internal fun String.prefixed(rootName: AndroidRootSourceSets) = if (rootName == AndroidRootSourceSets.Main) { + this +} else { + "${rootName.stringValue}${this.replaceFirstChar { it.uppercase() }}" +} diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt index 02fb6a789..5a4f944cf 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt @@ -19,11 +19,6 @@ internal val Project.protoBuildDirSourceSets: File return protoBuildDir.resolve(PROTO_BUILD_SOURCE_SETS) } -internal val Project.protoBuildDirSourceSetsKeep: File - get() { - return protoBuildDirSourceSets.resolve(".keep") - } - internal val Project.protoBuildDirGenerated: File get() { return protoBuildDir.resolve(PROTO_BUILD_GENERATED) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt index b6377254a..f959184ac 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt @@ -4,32 +4,73 @@ package kotlinx.rpc.util +import com.android.build.api.dsl.CommonExtension +import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.DynamicFeatureAndroidComponentsExtension +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import com.android.build.api.variant.TestAndroidComponentsExtension +import com.android.build.api.variant.Variant import org.gradle.api.Action import org.gradle.api.Project -import org.gradle.kotlin.dsl.findByType +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.kotlin.dsl.the -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer private const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" private const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm" +private const val KOTLIN_ANDROID_PLUGIN_ID = "org.jetbrains.kotlin.android" -internal fun Project.withLazyKotlinJvmExtension(action: Action) { +private const val ANDROID_APPLICATION = "com.android.application" +private const val ANDROID_LIBRARY = "com.android.library" +private const val ANDROID_DYNAMIC_FEATURE = "com.android.dynamic-feature" +private const val ANDROID_TEST = "com.android.test" + +internal fun Project.withKotlinSourceSets(action: (isAndroid: Boolean, KotlinSourceSetContainer) -> Unit) { plugins.withId(KOTLIN_JVM_PLUGIN_ID) { - the().apply { action.execute(this) } + the().apply { + action(false, this) + } } -} -internal fun Project.withLazyKotlinKmpExtension(action: Action) { plugins.withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { - the().apply { action.execute(this) } + the().apply { + // todo huh? + action(false, this) + } + } + + plugins.withId(KOTLIN_ANDROID_PLUGIN_ID) { + the().apply { + action(true, this) + } } } -internal val Project.kotlinJvmExtensionOrNull: KotlinJvmProjectExtension? get() { - return extensions.findByType() +internal fun Project.withLazyAndroidComponentsExtension( + action: Action, *, out Variant>>, +) { + plugins.withId(ANDROID_LIBRARY) { + the().apply { action.execute(this) } + } + + plugins.withId(ANDROID_APPLICATION) { + the().apply { action.execute(this) } + } + + plugins.withId(ANDROID_DYNAMIC_FEATURE) { + the().apply { action.execute(this) } + } + + plugins.withId(ANDROID_TEST) { + the().apply { action.execute(this) } + } } -internal val Project.kotlinKmpExtensionOrNull: KotlinMultiplatformExtension? get() { - return extensions.findByType() +internal fun Project.withLazyJavaPluginExtension(action: Action) { + plugins.withType { + the().apply { action.execute(this) } + } } diff --git a/versions-root/libs.versions.toml b/versions-root/libs.versions.toml index 81b597e02..f95b895a8 100644 --- a/versions-root/libs.versions.toml +++ b/versions-root/libs.versions.toml @@ -36,7 +36,7 @@ grpc-kotlin = "1.4.1" protobuf = "4.31.1" protobuf-gradle = "0.9.5" buf-tool = "1.55.1" -agp = "8.1.0" +agp = "8.4.0" [libraries] # kotlinx.rpc – references to the included builds @@ -139,7 +139,8 @@ kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", ve dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } gradle-publish-gradle-plugin = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "gradle-plugin-publish" } compat-patrouille-gradle-plugin = { module = "com.gradleup.compat.patrouille:compat-patrouille-gradle-plugin", version.ref = "compat-patrouille" } -android-gradle-plugin = { module = "com.android.tools.build:gradle-api", version.ref = "agp" } +android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } +android-gradle-plugin-api = { module = "com.android.tools.build:gradle-api", version.ref = "agp" } [plugins] kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin-lang" } From d18c095798ba85c987f2b08b89c8ff156ec07b35 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Wed, 3 Dec 2025 20:53:35 +0100 Subject: [PATCH 03/11] Added test for Android non-KMP --- .../kotlinx/rpc/GrpcAndroidProjectTest.kt | 351 +++++++++++ .../kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt | 14 +- .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 580 ++++++++---------- .../test/kotlin/kotlinx/rpc/base/BaseTest.kt | 21 +- .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 209 ++++++- .../all_default_source_sets/build.gradle.kts | 20 + .../src/androidTest/proto/androidTest.proto | 5 + .../proto/androidTestDebug.proto | 5 + .../src/debug/proto/debug.proto | 5 + .../src/main/proto/main.proto | 5 + .../src/release/proto/release.proto | 5 + .../src/test/proto/test.proto | 5 + .../src/testDebug/proto/testDebug.proto | 5 + .../src/testFixtures/proto/testFixtures.proto | 5 + .../proto/testFixturesDebug.proto | 5 + .../proto/testFixturesRelease.proto | 5 + .../src/testRelease/proto/testRelease.proto | 5 + .../buf_tasks_default/build.gradle.kts | 226 +++++++ .../buf_tasks_extended/build.gradle.kts | 480 +++++++++++++++ .../build.gradle.kts | 20 + .../src/main/proto/ok/ok.proto | 7 + .../src/main/proto/some.proto | 5 + .../build.gradle.kts | 20 + .../src/main/proto/ok/ok.proto | 7 + .../src/main/proto/some.proto | 5 + .../build.gradle.kts | 20 + .../src/main/proto/ok/ok.proto | 7 + .../src/main/proto/some.proto | 5 + .../build.gradle.kts | 24 + .../some/build.gradle.kts | 15 + .../src/main/proto/ok/ok.proto | 7 + .../src/main/proto/some.proto | 5 + .../more_flavors/build.gradle.kts | 37 ++ .../no_grpc/build.gradle.kts | 11 + .../build.gradle.kts | 20 + .../src/androidTest/proto/androidTest.proto | 5 + .../proto/androidTestDebug.proto | 5 + .../src/debug/proto/debug.proto | 5 + .../src/main/proto/main.proto | 5 + .../src/release/proto/release.proto | 5 + .../src/test/proto/test.proto | 5 + .../src/testDebug/proto/testDebug.proto | 5 + .../src/testFixtures/proto/testFixtures.proto | 5 + .../proto/testFixturesDebug.proto | 5 + .../proto/testFixturesRelease.proto | 5 + .../src/testRelease/proto/testRelease.proto | 5 + .../test_only_sources/build.gradle.kts | 20 + .../src/testFixtures/proto/ok/ok.proto | 7 + .../src/testFixtures/proto/some.proto | 5 + 49 files changed, 1883 insertions(+), 375 deletions(-) create mode 100644 gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTest/proto/androidTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTestDebug/proto/androidTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/debug/proto/debug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/main/proto/main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/release/proto/release.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/test/proto/test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testDebug/proto/testDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixtures/proto/testFixtures.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesDebug/proto/testFixturesDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesRelease/proto/testFixturesRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testRelease/proto/testRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/ok/ok.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/some.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/ok/ok.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/some.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/ok/ok.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/some.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/some/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/ok/ok.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/some.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/more_flavors/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/no_grpc/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTest/proto/androidTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTestDebug/proto/androidTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/debug/proto/debug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/main/proto/main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/release/proto/release.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/test/proto/test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testDebug/proto/testDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixtures/proto/testFixtures.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesDebug/proto/testFixturesDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesRelease/proto/testFixturesRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testRelease/proto/testRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/ok/ok.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/some.proto diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt new file mode 100644 index 000000000..f832dfe4a --- /dev/null +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt @@ -0,0 +1,351 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.rpc + +import kotlinx.rpc.base.GrpcBaseTest +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.TestInstance +import kotlin.io.path.Path + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +class GrpcAndroidProjectTest : GrpcBaseTest() { + override val type: Type = Type.Android + + @TestFactory + fun `Minimal gRPC Configuration`() = minimalGrpcConfiguration() + + @TestFactory + fun `Minimal gRPC Configuration Library`() = minimalGrpcConfiguration() + + @TestFactory + fun `Minimal gRPC Configuration Dynamic Feature`() = minimalGrpcConfiguration() + + private fun minimalGrpcConfiguration() = runGrpcTest { + fun runForSetOnlyMain(sourceSet: SSets, vararg extraTasks: SSetsAndroid) { + val result = runForSet(sourceSet) + + result.assertTaskExecuted( + sourceSet = sourceSet, + protoFiles = listOf( + Path("some.proto"), + Path("ok", "ok.proto"), + ), + importProtoFiles = emptyList(), + generatedFiles = listOf( + Path("Some.kt"), + Path(RPC_INTERNAL, "Some.kt"), + Path("ok", "Ok.kt"), + Path("ok", RPC_INTERNAL, "Ok.kt"), + ), + notExecuted = SSetsAndroid.Default.entries - extraTasks.toSet() - sourceSet, + ) + } + + fun runForSetOnlyTest(sourceSet: SSets, vararg extraTasks: SSetsAndroid) { + val result = runForSet(sourceSet) + + result.assertTaskExecuted( + sourceSet = sourceSet, + protoFiles = emptyList(), + importProtoFiles = listOf( + Path("some.proto"), + Path("ok", "ok.proto"), + ), + generatedFiles = emptyList(), + notExecuted = SSetsAndroid.Default.entries - extraTasks.toSet() - sourceSet, + ) + } + + runForSetOnlyMain(SSetsAndroid.Default.debug) + runForSetOnlyMain(SSetsAndroid.Default.release) + runForSetOnlyTest(SSetsAndroid.Default.testDebug, SSetsAndroid.Default.debug) + runForSetOnlyTest(SSetsAndroid.Default.testRelease, SSetsAndroid.Default.release) + runForSetOnlyTest(SSetsAndroid.Default.androidTestDebug, SSetsAndroid.Default.debug) + } + + @TestFactory + fun `Minimal gRPC Configuration Test`() = runGrpcTest { + fun runForSetOnlyMain(sourceSet: SSets, vararg extraTasks: SSetsAndroid) { + val result = runForSet(sourceSet) + + result.assertTaskExecuted( + sourceSet = sourceSet, + protoFiles = listOf( + Path("some.proto"), + Path("ok", "ok.proto"), + ), + importProtoFiles = emptyList(), + generatedFiles = listOf( + Path("Some.kt"), + Path(RPC_INTERNAL, "Some.kt"), + Path("ok", "Ok.kt"), + Path("ok", RPC_INTERNAL, "Ok.kt"), + ), + notExecuted = SSetsAndroid.Test.entries - extraTasks.toSet() - sourceSet, + ) + } + + runForSetOnlyMain(SSetsAndroid.Test.debug) + + SSetsAndroid.Default.entries.forEach { + if (it != SSetsAndroid.Default.debug) { + runNonExistentTasksForSourceSet(it) + } + } + } + + @TestFactory + fun `Test-Only Sources`() = runGrpcTest { + fun runForSetOnlyMain(sourceSet: SSets, vararg extraTasks: SSetsAndroid) { + val result = runForSet(sourceSet) + + result.assertTaskExecuted( + sourceSet = sourceSet, + protoFiles = emptyList(), + importProtoFiles = emptyList(), + generatedFiles = emptyList(), + notExecuted = SSetsAndroid.Default.entries - extraTasks.toSet() - sourceSet, + ) + } + + fun runForSetOnlyTest(sourceSet: SSets, vararg extraTasks: SSetsAndroid) { + val result = runForSet(sourceSet) + + result.assertTaskExecuted( + sourceSet = sourceSet, + protoFiles = listOf( + Path("some.proto"), + Path("ok", "ok.proto"), + ), + importProtoFiles = emptyList(), + generatedFiles = listOf( + Path("Some.kt"), + Path(RPC_INTERNAL, "Some.kt"), + Path("ok", "Ok.kt"), + Path("ok", RPC_INTERNAL, "Ok.kt"), + ), + notExecuted = SSetsAndroid.Default.entries - extraTasks.toSet() - sourceSet, + ) + } + + runForSetOnlyMain(SSetsAndroid.Default.debug) + runForSetOnlyMain(SSetsAndroid.Default.release) + runForSetOnlyTest(SSetsAndroid.Default.testDebug, SSetsAndroid.Default.debug) + runForSetOnlyTest(SSetsAndroid.Default.testRelease, SSetsAndroid.Default.release) + runForSetOnlyTest(SSetsAndroid.Default.androidTestDebug, SSetsAndroid.Default.debug) + } + + @TestFactory + fun `All Default Source Sets`() = runGrpcTest { + runAndCheckFiles( + SSetsAndroid.Default.debug, + extended = listOf(SSetsAndroid.Default.main), + ) + runAndCheckFiles( + SSetsAndroid.Default.release, + extended = listOf(SSetsAndroid.Default.main) + ) + runAndCheckFiles( + SSetsAndroid.Default.testDebug, + SSetsAndroid.Default.debug, SSetsAndroid.Default.main, + extended = listOf( + SSetsAndroid.Default.test, + SSetsAndroid.Default.testFixtures, + SSetsAndroid.Default.testFixturesDebug, + ) + ) + runAndCheckFiles( + SSetsAndroid.Default.testRelease, + SSetsAndroid.Default.release, SSetsAndroid.Default.main, + extended = listOf( + SSetsAndroid.Default.test, + SSetsAndroid.Default.testFixtures, + SSetsAndroid.Default.testFixturesRelease, + ) + ) + runAndCheckFiles( + SSetsAndroid.Default.androidTestDebug, + SSetsAndroid.Default.debug, SSetsAndroid.Default.main, + extended = listOf( + SSetsAndroid.Default.androidTest, + SSetsAndroid.Default.testFixtures, + SSetsAndroid.Default.testFixturesDebug, + ) + ) + } + + @TestFactory + fun `No gRPC`() = runGrpcTest { + SSetsAndroid.Default.entries.forEach { + runNonExistentTasksForSourceSet(it) + } + } + + @TestFactory + fun `Proto Tasks Are Cached Properly`() = runGrpcTest { + val firstRunDebug = runForSet(SSetsAndroid.Default.debug) + + firstRunDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + // didn't run + firstRunDebug.assertOutcomes(SSetsAndroid.Default.release) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.androidTestDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testRelease) + + val secondRunDebug = runForSet(SSetsAndroid.Default.debug) + + secondRunDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + cleanProtoBuildDir() + + val thirdRunDebug = runForSet(SSetsAndroid.Default.debug) + + thirdRunDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + SSetsAndroid.Default.debug.sourceDir() + .resolve("debug.proto") + .replace("content = 1", "content = 2") + + val fourthRunDebug = runForSet(SSetsAndroid.Default.debug) + + fourthRunDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + val firstRunTestDebug = runForSet(SSetsAndroid.Default.testDebug) + + firstRunTestDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + firstRunTestDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.testDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + firstRunDebug.assertOutcomes(SSetsAndroid.Default.release) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.androidTestDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testRelease) + + val firstRunAndroidTestDebug = runForSet(SSetsAndroid.Default.androidTestDebug) + + firstRunAndroidTestDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + firstRunAndroidTestDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.androidTestDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + firstRunDebug.assertOutcomes(SSetsAndroid.Default.release) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testRelease) + + SSetsAndroid.Default.testDebug.sourceDir() + .resolve("testDebug.proto") + .replace("content = 1", "content = 2") + + val fifthRunDebug = runForSet(SSetsAndroid.Default.debug) + + fifthRunDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + // didn't run + firstRunDebug.assertOutcomes(SSetsAndroid.Default.release) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.androidTestDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testRelease) + + val secondRunTestDebug = runForSet(SSetsAndroid.Default.testDebug) + + secondRunTestDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.debug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + secondRunTestDebug.assertOutcomes( + sourceSet = SSetsAndroid.Default.testDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + // didn't run + firstRunDebug.assertOutcomes(SSetsAndroid.Default.release) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.androidTestDebug) + firstRunDebug.assertOutcomes(SSetsAndroid.Default.testRelease) + } + + @TestFactory + fun `Buf Tasks Default`() = runGrpcTest { + runGradle("test_tasks", "--no-configuration-cache") + } + + @TestFactory + fun `Buf Tasks Extended`() = runGrpcTest { + runGradle("test_tasks", "--no-configuration-cache") + } +} diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt index 3f0ba6d08..73f8dc9c7 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt @@ -13,7 +13,7 @@ import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GrpcJvmProjectTest : GrpcBaseTest() { - override val isKmp: Boolean = false + override val type: Type = Type.Jvm @TestFactory fun `Minimal gRPC Configuration`() = runGrpcTest { @@ -32,15 +32,9 @@ class GrpcJvmProjectTest : GrpcBaseTest() { @TestFactory fun `No gRPC`() = runGrpcTest { - runNonExistentTask(bufGenerateCommonMain) - runNonExistentTask(bufGenerateCommonTest) - runNonExistentTask(processCommonMainProtoFiles) - runNonExistentTask(processCommonTestProtoFiles) - runNonExistentTask(processCommonTestProtoFilesImports) - runNonExistentTask(generateBufYamlCommonMain) - runNonExistentTask(generateBufYamlCommonTest) - runNonExistentTask(generateBufGenYamlCommonMain) - runNonExistentTask(generateBufGenYamlCommonTest) + SSetsJvm.entries.forEach { + runNonExistentTasksForSourceSet(it) + } } @TestFactory diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index 36a4c8391..a38edbd21 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -14,7 +14,7 @@ import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GrpcKmpProjectTest : GrpcBaseTest() { - override val isKmp: Boolean = true + override val type: Type = Type.Kmp @TestFactory fun `Minimal gRPC Configuration`() = runGrpcTest { @@ -33,15 +33,9 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @TestFactory fun `No gRPC`() = runGrpcTest { - runNonExistentTask(bufGenerateCommonMain) - runNonExistentTask(bufGenerateCommonTest) - runNonExistentTask(processCommonMainProtoFiles) - runNonExistentTask(processCommonTestProtoFiles) - runNonExistentTask(processCommonTestProtoFilesImports) - runNonExistentTask(generateBufYamlCommonMain) - runNonExistentTask(generateBufYamlCommonTest) - runNonExistentTask(generateBufGenYamlCommonMain) - runNonExistentTask(generateBufGenYamlCommonTest) + SSetsKmp.entries.forEach { + runNonExistentTasksForSourceSet(it) + } } @TestFactory @@ -127,171 +121,164 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @TestFactory fun `KMP Hierarchy`() = runGrpcTest { - runKmpAndCheckFiles( - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.commonMain, ) - runKmpAndCheckFiles( - SSets.commonTest, - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.commonTest, + SSetsKmp.commonMain, ) - runKmpAndCheckFiles( - SSets.nativeMain, - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.nativeMain, + SSetsKmp.commonMain, ) - runKmpAndCheckFiles( - SSets.nativeTest, - SSets.commonMain, SSets.nativeMain, - SSets.commonTest, + runAndCheckFiles( + SSetsKmp.nativeTest, + SSetsKmp.commonMain, SSetsKmp.nativeMain, + SSetsKmp.commonTest, ) - runKmpAndCheckFiles( - SSets.jvmMain, - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.jvmMain, + SSetsKmp.commonMain, ) - runKmpAndCheckFiles( - SSets.jvmTest, - SSets.commonMain, SSets.jvmMain, - SSets.commonTest, + runAndCheckFiles( + SSetsKmp.jvmTest, + SSetsKmp.commonMain, SSetsKmp.jvmMain, + SSetsKmp.commonTest, ) - runKmpAndCheckFiles( - SSets.jsMain, - SSets.commonMain, SSets.webMain, + runAndCheckFiles( + SSetsKmp.jsMain, + SSetsKmp.commonMain, SSetsKmp.webMain, ) - runKmpAndCheckFiles( - SSets.jsTest, - SSets.commonMain, SSets.webMain, SSets.jsMain, - SSets.commonTest, SSets.webTest + runAndCheckFiles( + SSetsKmp.jsTest, + SSetsKmp.commonMain, SSetsKmp.webMain, SSetsKmp.jsMain, + SSetsKmp.commonTest, SSetsKmp.webTest ) - runKmpAndCheckFiles( - SSets.appleMain, - SSets.commonMain, SSets.nativeMain, + runAndCheckFiles( + SSetsKmp.appleMain, + SSetsKmp.commonMain, SSetsKmp.nativeMain, ) - runKmpAndCheckFiles( - SSets.appleTest, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, - SSets.commonTest, SSets.nativeTest + runAndCheckFiles( + SSetsKmp.appleTest, + SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, + SSetsKmp.commonTest, SSetsKmp.nativeTest ) - runKmpAndCheckFiles( - SSets.macosMain, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, + runAndCheckFiles( + SSetsKmp.macosMain, + SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, ) - runKmpAndCheckFiles( - SSets.macosTest, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, - SSets.commonTest, SSets.nativeTest, SSets.appleTest + runAndCheckFiles( + SSetsKmp.macosTest, + SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, SSetsKmp.macosMain, + SSetsKmp.commonTest, SSetsKmp.nativeTest, SSetsKmp.appleTest ) - runKmpAndCheckFiles( - SSets.macosArm64Main, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, + runAndCheckFiles( + SSetsKmp.macosArm64Main, + SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, SSetsKmp.macosMain, ) - runKmpAndCheckFiles( - SSets.macosArm64Test, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, SSets.macosArm64Main, - SSets.commonTest, SSets.nativeTest, SSets.appleTest, SSets.macosTest, - clean = false, + runAndCheckFiles( + SSetsKmp.macosArm64Test, + SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, SSetsKmp.macosMain, SSetsKmp.macosArm64Main, + SSetsKmp.commonTest, SSetsKmp.nativeTest, SSetsKmp.appleTest, SSetsKmp.macosTest, ) } @TestFactory fun `Proto Tasks Are Cached Properly`() = runGrpcTest { - val firstRunCommonMain = runKmp(SSets.commonMain) + val firstRunCommonMain = runForSet(SSetsKmp.commonMain) - assertOutcomes( - result = firstRunCommonMain, - sourceSet = SSets.commonMain, + firstRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, protoFiles = TaskOutcome.SUCCESS, - protoFilesImports = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, ) // didn't run - assertOutcomes(firstRunCommonMain, SSets.commonTest) - assertOutcomes(firstRunCommonMain, SSets.nativeMain) - assertOutcomes(firstRunCommonMain, SSets.nativeTest) - assertOutcomes(firstRunCommonMain, SSets.jvmMain) - assertOutcomes(firstRunCommonMain, SSets.jvmTest) - assertOutcomes(firstRunCommonMain, SSets.webMain) - assertOutcomes(firstRunCommonMain, SSets.webTest) - assertOutcomes(firstRunCommonMain, SSets.jsMain) - assertOutcomes(firstRunCommonMain, SSets.jsTest) - assertOutcomes(firstRunCommonMain, SSets.appleMain) - assertOutcomes(firstRunCommonMain, SSets.appleTest) - assertOutcomes(firstRunCommonMain, SSets.macosMain) - assertOutcomes(firstRunCommonMain, SSets.macosTest) - assertOutcomes(firstRunCommonMain, SSets.macosArm64Main) - assertOutcomes(firstRunCommonMain, SSets.macosArm64Test) - - val secondRunCommonMain = runKmp(SSets.commonMain) - - assertOutcomes( - result = secondRunCommonMain, - sourceSet = SSets.commonMain, + firstRunCommonMain.assertOutcomes(SSetsKmp.commonTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.nativeMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.nativeTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.jvmMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.jvmTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.webMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.webTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.jsMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.jsTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.appleMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.appleTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.macosMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.macosTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Main) + firstRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Test) + + val secondRunCommonMain = runForSet(SSetsKmp.commonMain) + + secondRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) cleanProtoBuildDir() - val thirdRunCommonMain = runKmp(SSets.commonMain) + val thirdRunCommonMain = runForSet(SSetsKmp.commonMain) - assertOutcomes( - result = thirdRunCommonMain, - sourceSet = SSets.commonMain, + thirdRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, protoFiles = TaskOutcome.SUCCESS, - protoFilesImports = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - SSets.commonMain.sourceDir() + SSetsKmp.commonMain.sourceDir() .resolve("commonMain.proto") .replace("content = 1", "content = 2") - val fourthRunCommonMain = runKmp(SSets.commonMain) + val fourthRunCommonMain = runForSet(SSetsKmp.commonMain) - assertOutcomes( - result = fourthRunCommonMain, - sourceSet = SSets.commonMain, + fourthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.SUCCESS, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - val firstRunMacosArm64Main = runKmp(SSets.macosArm64Main) + val firstRunMacosArm64Main = runForSet(SSetsKmp.macosArm64Main) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.commonMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.nativeMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.nativeMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -299,9 +286,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.appleMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.appleMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -309,9 +295,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.macosMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.macosMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -319,9 +304,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.macosArm64Main, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.macosArm64Main, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -330,32 +314,30 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - assertOutcomes(firstRunMacosArm64Main, SSets.nativeTest) - assertOutcomes(firstRunMacosArm64Main, SSets.jvmMain) - assertOutcomes(firstRunMacosArm64Main, SSets.jvmTest) - assertOutcomes(firstRunMacosArm64Main, SSets.webMain) - assertOutcomes(firstRunMacosArm64Main, SSets.webTest) - assertOutcomes(firstRunMacosArm64Main, SSets.jsMain) - assertOutcomes(firstRunMacosArm64Main, SSets.jsTest) - assertOutcomes(firstRunMacosArm64Main, SSets.appleTest) - assertOutcomes(firstRunMacosArm64Main, SSets.macosTest) - assertOutcomes(firstRunMacosArm64Main, SSets.macosArm64Test) - - val firstRunMacosArm64Test = runKmp(SSets.macosArm64Test) - - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.commonMain, + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.nativeTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmMain) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.webMain) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.webTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jsMain) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jsTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.appleTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.macosTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.macosArm64Test) + + val firstRunMacosArm64Test = runForSet(SSetsKmp.macosArm64Test) + + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.nativeMain, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -363,9 +345,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.appleMain, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -373,9 +354,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosMain, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -383,9 +363,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosArm64Main, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosArm64Main, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -393,9 +372,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.nativeTest, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.nativeTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -403,9 +381,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.appleTest, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.appleTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -413,9 +390,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosTest, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -423,9 +399,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosArm64Test, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosArm64Test, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -434,61 +409,58 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - assertOutcomes(firstRunMacosArm64Test, SSets.jvmMain) - assertOutcomes(firstRunMacosArm64Test, SSets.jvmTest) - assertOutcomes(firstRunMacosArm64Test, SSets.webMain) - assertOutcomes(firstRunMacosArm64Test, SSets.webTest) - assertOutcomes(firstRunMacosArm64Test, SSets.jsMain) - assertOutcomes(firstRunMacosArm64Test, SSets.jsTest) - - SSets.macosMain.sourceDir() + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmMain) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmTest) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.webMain) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.webTest) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jsMain) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jsTest) + + SSetsKmp.macosMain.sourceDir() .resolve("macosMain.proto") .replace("content = 1", "content = 2") - val fifthRunCommonMain = runKmp(SSets.commonMain) + val fifthRunCommonMain = runForSet(SSetsKmp.commonMain) - assertOutcomes( - result = fifthRunCommonMain, - sourceSet = SSets.commonMain, + fifthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) // didn't run - assertOutcomes(fifthRunCommonMain, SSets.commonTest) - assertOutcomes(fifthRunCommonMain, SSets.nativeMain) - assertOutcomes(fifthRunCommonMain, SSets.nativeTest) - assertOutcomes(fifthRunCommonMain, SSets.jvmMain) - assertOutcomes(fifthRunCommonMain, SSets.jvmTest) - assertOutcomes(fifthRunCommonMain, SSets.webMain) - assertOutcomes(fifthRunCommonMain, SSets.webTest) - assertOutcomes(fifthRunCommonMain, SSets.jsMain) - assertOutcomes(fifthRunCommonMain, SSets.jsTest) - assertOutcomes(fifthRunCommonMain, SSets.appleMain) - assertOutcomes(fifthRunCommonMain, SSets.appleTest) - assertOutcomes(fifthRunCommonMain, SSets.macosMain) - assertOutcomes(fifthRunCommonMain, SSets.macosTest) - assertOutcomes(fifthRunCommonMain, SSets.macosArm64Main) - assertOutcomes(fifthRunCommonMain, SSets.macosArm64Test) - - val secondRunMacosArm64Main = runKmp(SSets.macosArm64Main) - - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.commonMain, + fifthRunCommonMain.assertOutcomes(SSetsKmp.commonTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.nativeMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.nativeTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.jvmMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.jvmTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.webMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.webTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.jsMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.jsTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.appleMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.appleTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.macosMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.macosTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Main) + fifthRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Test) + + val secondRunMacosArm64Main = runForSet(SSetsKmp.macosArm64Main) + + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.nativeMain, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -496,9 +468,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.appleMain, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -506,9 +477,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.macosMain, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.macosMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -516,9 +486,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.macosArm64Main, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.macosArm64Main, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -527,32 +496,30 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - assertOutcomes(secondRunMacosArm64Main, SSets.nativeTest) - assertOutcomes(secondRunMacosArm64Main, SSets.jvmMain) - assertOutcomes(secondRunMacosArm64Main, SSets.jvmTest) - assertOutcomes(secondRunMacosArm64Main, SSets.webMain) - assertOutcomes(secondRunMacosArm64Main, SSets.webTest) - assertOutcomes(secondRunMacosArm64Main, SSets.jsMain) - assertOutcomes(secondRunMacosArm64Main, SSets.jsTest) - assertOutcomes(secondRunMacosArm64Main, SSets.appleTest) - assertOutcomes(secondRunMacosArm64Main, SSets.macosTest) - assertOutcomes(secondRunMacosArm64Main, SSets.macosArm64Test) - - val secondRunMacosArm64Test = runKmp(SSets.macosArm64Test) - - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.commonMain, + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.nativeTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmMain) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.webMain) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.webTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jsMain) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jsTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.appleTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.macosTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.macosArm64Test) + + val secondRunMacosArm64Test = runForSet(SSetsKmp.macosArm64Test) + + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.nativeMain, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -560,9 +527,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.appleMain, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -570,9 +536,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosMain, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -580,9 +545,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosArm64Main, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosArm64Main, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -590,9 +554,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.nativeTest, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.nativeTest, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -600,9 +563,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.appleTest, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.appleTest, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -610,9 +572,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosTest, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -620,9 +581,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosArm64Test, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.macosArm64Test, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -631,28 +591,26 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - assertOutcomes(secondRunMacosArm64Test, SSets.jvmMain) - assertOutcomes(secondRunMacosArm64Test, SSets.jvmTest) - assertOutcomes(secondRunMacosArm64Test, SSets.webMain) - assertOutcomes(secondRunMacosArm64Test, SSets.webTest) - assertOutcomes(secondRunMacosArm64Test, SSets.jsMain) - assertOutcomes(secondRunMacosArm64Test, SSets.jsTest) - - val firstRunJvmMain = runKmp(SSets.jvmMain) - - assertOutcomes( - result = firstRunJvmMain, - sourceSet = SSets.commonMain, + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmMain) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmTest) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.webMain) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.webTest) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jsMain) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jsTest) + + val firstRunJvmMain = runForSet(SSetsKmp.jvmMain) + + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - assertOutcomes( - result = firstRunJvmMain, - sourceSet = SSets.jvmMain, + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.jvmMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -661,40 +619,38 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - assertOutcomes(firstRunJvmMain, SSets.commonTest) - assertOutcomes(firstRunJvmMain, SSets.nativeMain) - assertOutcomes(firstRunJvmMain, SSets.nativeTest) - assertOutcomes(firstRunJvmMain, SSets.jvmTest) - assertOutcomes(firstRunJvmMain, SSets.webMain) - assertOutcomes(firstRunJvmMain, SSets.webTest) - assertOutcomes(firstRunJvmMain, SSets.jsMain) - assertOutcomes(firstRunJvmMain, SSets.jsTest) - assertOutcomes(firstRunJvmMain, SSets.appleMain) - assertOutcomes(firstRunJvmMain, SSets.appleTest) - assertOutcomes(firstRunJvmMain, SSets.macosMain) - assertOutcomes(firstRunJvmMain, SSets.macosTest) - assertOutcomes(firstRunJvmMain, SSets.macosArm64Main) - assertOutcomes(firstRunJvmMain, SSets.macosArm64Test) - - SSets.jvmMain.sourceDir() + firstRunJvmMain.assertOutcomes(SSetsKmp.commonTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.nativeMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.nativeTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.jvmTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.webMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.webTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.jsMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.jsTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.appleMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.appleTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.macosMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.macosTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Main) + firstRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Test) + + SSetsKmp.jvmMain.sourceDir() .resolve("jvmMain.proto") .replace("content = 1", "content = 2") - val secondRunJvmMain = runKmp(SSets.jvmMain) + val secondRunJvmMain = runForSet(SSetsKmp.jvmMain) - assertOutcomes( - result = secondRunJvmMain, - sourceSet = SSets.commonMain, + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, protoFiles = TaskOutcome.UP_TO_DATE, - protoFilesImports = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - assertOutcomes( - result = secondRunJvmMain, - sourceSet = SSets.jvmMain, + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.jvmMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -703,52 +659,20 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - assertOutcomes(secondRunJvmMain, SSets.commonTest) - assertOutcomes(secondRunJvmMain, SSets.nativeMain) - assertOutcomes(secondRunJvmMain, SSets.nativeTest) - assertOutcomes(secondRunJvmMain, SSets.jvmTest) - assertOutcomes(secondRunJvmMain, SSets.webMain) - assertOutcomes(secondRunJvmMain, SSets.webTest) - assertOutcomes(secondRunJvmMain, SSets.jsMain) - assertOutcomes(secondRunJvmMain, SSets.jsTest) - assertOutcomes(secondRunJvmMain, SSets.appleMain) - assertOutcomes(secondRunJvmMain, SSets.appleTest) - assertOutcomes(secondRunJvmMain, SSets.macosMain) - assertOutcomes(secondRunJvmMain, SSets.macosTest) - assertOutcomes(secondRunJvmMain, SSets.macosArm64Main) - assertOutcomes(secondRunJvmMain, SSets.macosArm64Test) - } - - private fun GrpcTestEnv.runKmpAndCheckFiles( - sourceSet: SSets, - vararg imports: SSets, - clean: Boolean = true, - ) { - runKmp(sourceSet).assertKmpSourceSet(sourceSet, *imports) - - if (clean) { - cleanProtoBuildDir() - } - } - - private fun GrpcTestEnv.runKmp(sourceSet: SSets): BuildResult { - return runGradle(bufGenerate(sourceSet)) - } - - private fun GrpcTestEnv.assertOutcomes( - result: BuildResult, - sourceSet: SSets, - generate: TaskOutcome? = null, - bufYaml: TaskOutcome? = null, - bufGenYaml: TaskOutcome? = null, - protoFiles: TaskOutcome? = null, - protoFilesImports: TaskOutcome? = null, - ) { - assertEquals(generate, result.protoTaskOutcomeOrNull(bufGenerate(sourceSet))) - assertEquals(bufYaml, result.protoTaskOutcomeOrNull(generateBufYaml(sourceSet))) - assertEquals(bufGenYaml, result.protoTaskOutcomeOrNull(generateBufGenYaml(sourceSet))) - assertEquals(protoFiles, result.protoTaskOutcomeOrNull(processProtoFiles(sourceSet))) - assertEquals(protoFilesImports, result.protoTaskOutcomeOrNull(processProtoFilesImports(sourceSet))) + secondRunJvmMain.assertOutcomes(SSetsKmp.commonTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.nativeMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.nativeTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.jvmTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.webMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.webTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.jsMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.jsTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.appleMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.appleTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.macosMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.macosTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Main) + secondRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Test) } @TestFactory diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt index 59abdadc9..e4a5cfd4a 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -21,12 +21,15 @@ import kotlinx.rpc.RPC_VERSION import org.junit.jupiter.api.DynamicTest import java.util.stream.Stream import kotlin.io.path.absolute +import kotlin.io.path.appendText import kotlin.io.path.createDirectories import kotlin.io.path.deleteRecursively +import kotlin.io.path.readLines class VersionsEnv( val gradle: String, val kotlin: String, + val android: String, ) { val kotlinSemver = run { val (major, minor, patch) = kotlin.split(".").map { it.toInt() } @@ -40,9 +43,9 @@ internal object KtVersion { } private val GradleVersions = listOf( - VersionsEnv("9.2.1", "2.2.21"), - VersionsEnv("8.14.1", "2.2.0"), - VersionsEnv("8.8", "2.0.0"), + VersionsEnv("9.2.1", "2.2.21", "8.13.1"), + VersionsEnv("8.14.1", "2.2.0", "8.6.1"), + VersionsEnv("8.8", "2.0.0", "8.4.0"), ) internal fun BaseTest.runWithAllGradleVersions(body: (VersionsEnv) -> Unit): Stream { @@ -115,6 +118,15 @@ abstract class BaseTest { buildScriptFile .replace("", versions.kotlin) .replace("", RPC_VERSION) + .replace("", versions.android) + + buildScriptFile.readLines().filter { + it.startsWith("// include:") + }.map { + it.removePrefix("// include:").trim() + }.forEach { project -> + settingsFile.appendText("\ninclude(\":$project\")") + } println(""" Setup project '$projectName' @@ -154,7 +166,7 @@ abstract class BaseTest { protected fun runTest(testEnv: T, body: T.() -> Unit) { try { testEnv.body() - } catch (e: Throwable) { + } finally { val output = testEnv.latestBuild?.output if (output != null) { println("Latest gradle build output:") @@ -162,7 +174,6 @@ abstract class BaseTest { } else { println("No gradle build output available") } - throw e } } diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt index 891db5838..86d1bd89d 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -2,6 +2,8 @@ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("EnumEntryName", "detekt.EnumNaming") + package kotlinx.rpc.base import org.gradle.testkit.runner.BuildResult @@ -17,13 +19,44 @@ import kotlin.test.fail @TestInstance(TestInstance.Lifecycle.PER_METHOD) abstract class GrpcBaseTest : BaseTest() { - abstract val isKmp: Boolean + enum class Type { + Jvm, Kmp, Android; + } + + abstract val type: Type + + protected fun GrpcTestEnv.runNonExistentTasksForSourceSet(set: SSets) { + runNonExistentTask(bufGenerate(set)) + runNonExistentTask(processProtoFiles(set)) + runNonExistentTask(processProtoFilesImports(set)) + runNonExistentTask(generateBufYaml(set)) + runNonExistentTask(generateBufGenYaml(set)) + } protected fun runGrpcTest(test: GrpcTestEnv.() -> Unit): Stream = runWithAllGradleVersions { runTest(GrpcTestEnv(it), test) } inner class GrpcTestEnv(versions: VersionsEnv) : TestEnv(versions) { + fun BuildResult.assertOutcomes( + sourceSet: SSets, + generate: TaskOutcome? = null, + bufYaml: TaskOutcome? = null, + bufGenYaml: TaskOutcome? = null, + protoFiles: TaskOutcome? = null, + protoFilesImports: TaskOutcome? = null, + ) { + assertOutcome(generate, bufGenerate(sourceSet)) + assertOutcome(bufYaml, generateBufYaml(sourceSet)) + assertOutcome(bufGenYaml, generateBufGenYaml(sourceSet)) + assertOutcome(protoFiles, processProtoFiles(sourceSet)) + assertOutcome(protoFilesImports, processProtoFilesImports(sourceSet)) + } + + fun BuildResult.assertOutcome(expected: TaskOutcome?, task: String) { + assertEquals(expected, protoTaskOutcomeOrNull(task), "Outcome for task $task") + } + fun BuildResult.protoTaskOutcome(name: String): TaskOutcome { return tasks.find { it.path == ":$name" }?.outcome ?: fail("Task ':$name' was not present in the build result") @@ -69,6 +102,14 @@ abstract class GrpcBaseTest : BaseTest() { val dir = protoBuildDirGenerated.resolve(sourceSet.name).resolve(KOTLIN_MULTIPLATFORM_DIR) fun Path.doAssert() { + if (!exists()) { + if (files.isEmpty()) { + return + } + + fail("Directory '${this.relativeTo(dir)}' does not exist, but expected files: ${files.toList()}") + } + listDirectoryEntries().forEach { entry -> when { entry.isDirectory() -> { @@ -84,7 +125,7 @@ abstract class GrpcBaseTest : BaseTest() { } else -> { - fail("File '${entry}' in '$this' should not exist") + fail("File '${entry.relativeTo(dir)}' in '${this.relativeTo(dir)}' should not exist") } } } @@ -159,10 +200,10 @@ abstract class GrpcBaseTest : BaseTest() { protoFiles: List, generatedFiles: List, ) { - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(bufGenerateCommonMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processCommonMainProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlCommonMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlCommonMain)) + assertOutcome(TaskOutcome.SUCCESS, bufGenerateCommonMain) + assertOutcome(TaskOutcome.SUCCESS, processCommonMainProtoFiles) + assertOutcome(TaskOutcome.SUCCESS, generateBufYamlCommonMain) + assertOutcome(TaskOutcome.SUCCESS, generateBufGenYamlCommonMain) assertProtoTaskNotExecuted(bufGenerateCommonTest) assertProtoTaskNotExecuted(processCommonTestProtoFiles) @@ -186,11 +227,21 @@ abstract class GrpcBaseTest : BaseTest() { generatedFiles: List, importGeneratedFiles: List, ) { - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(bufGenerateCommonTest)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processCommonTestProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processCommonTestProtoFilesImports)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlCommonTest)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlCommonTest)) + assertOutcome(TaskOutcome.SUCCESS, bufGenerateCommonTest) + val processProtoOutcome = if (protoFiles.isEmpty()) { + TaskOutcome.NO_SOURCE + } else { + TaskOutcome.SUCCESS + } + assertOutcome(processProtoOutcome, processCommonTestProtoFiles) + val processProtoImportsOutcome = if (importProtoFiles.isEmpty()) { + TaskOutcome.NO_SOURCE + } else { + TaskOutcome.SUCCESS + } + assertOutcome(processProtoImportsOutcome, processCommonTestProtoFilesImports) + assertOutcome(TaskOutcome.SUCCESS, generateBufYamlCommonTest) + assertOutcome(TaskOutcome.SUCCESS, generateBufGenYamlCommonTest) val mainGenerateOutcome = if (importProtoFiles.isEmpty()) { TaskOutcome.SKIPPED @@ -198,10 +249,15 @@ abstract class GrpcBaseTest : BaseTest() { TaskOutcome.SUCCESS } - assertEquals(mainGenerateOutcome, protoTaskOutcome(bufGenerateCommonMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processCommonMainProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlCommonMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlCommonMain)) + assertOutcome(mainGenerateOutcome, bufGenerateCommonMain) + val mainProcessOutcome = if (importProtoFiles.isEmpty()) { + TaskOutcome.NO_SOURCE + } else { + TaskOutcome.SUCCESS + } + assertOutcome(mainProcessOutcome, processCommonMainProtoFiles) + assertOutcome(TaskOutcome.SUCCESS, generateBufYamlCommonMain) + assertOutcome(TaskOutcome.SUCCESS, generateBufGenYamlCommonMain) assertSourceCodeGenerated(testSourceSet, *generatedFiles.toTypedArray()) assertSourceCodeNotGenerated(mainSourceSet, *generatedFiles.toTypedArray()) @@ -223,11 +279,29 @@ abstract class GrpcBaseTest : BaseTest() { generatedFiles: List, notExecuted: List, ) { - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(bufGenerate(sourceSet))) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processProtoFiles(sourceSet))) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processProtoFilesImports(sourceSet))) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYaml(sourceSet))) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYaml(sourceSet))) + val generateOutcome = if (protoFiles.isEmpty()) { + TaskOutcome.SKIPPED + } else { + TaskOutcome.SUCCESS + } + assertOutcome(generateOutcome, bufGenerate(sourceSet)) + + val processProtoOutcome = if (protoFiles.isEmpty()) { + TaskOutcome.NO_SOURCE + } else { + TaskOutcome.SUCCESS + } + assertOutcome(processProtoOutcome, processProtoFiles(sourceSet)) + + val processImportProtoOutcome = if (importProtoFiles.isEmpty()) { + TaskOutcome.NO_SOURCE + } else { + TaskOutcome.SUCCESS + } + assertOutcome(processImportProtoOutcome, processProtoFilesImports(sourceSet)) + + assertOutcome(TaskOutcome.SUCCESS, generateBufYaml(sourceSet)) + assertOutcome(TaskOutcome.SUCCESS, generateBufGenYaml(sourceSet)) assertSourceCodeGenerated(sourceSet, *generatedFiles.toTypedArray()) assertSourceCodeNotGeneratedExcept(sourceSet, *generatedFiles.toTypedArray()) @@ -243,16 +317,29 @@ abstract class GrpcBaseTest : BaseTest() { } } - fun BuildResult.assertKmpSourceSet( + fun GrpcTestEnv.runAndCheckFiles( sourceSet: SSets, vararg imports: SSets, + extended: List = emptyList(), + ) { + cleanProtoBuildDir() + runForSet(sourceSet).assertSourceSet(sourceSet, *imports, extendedProto = extended) + } + + fun GrpcTestEnv.runForSet(sourceSet: SSets): BuildResult { + return runGradle(bufGenerate(sourceSet)) + } + + fun BuildResult.assertSourceSet( + sourceSet: SSets, + vararg imports: SSets, + extendedProto: List, ) { if (!sourceSet.applicable()) { println("Skipping ${sourceSet.capital} source set because it's not applicable for the current Kotlin version") return } - val ktFile = "${sourceSet.capital}.kt" val importsSet = imports .onEach { if (!it.applicable()) { @@ -262,17 +349,23 @@ abstract class GrpcBaseTest : BaseTest() { .filter { it.applicable() } .toSet() + val generateFor = extendedProto + sourceSet + assertTaskExecuted( sourceSet = sourceSet, - protoFiles = listOf(Path("${sourceSet.name}.proto")), + protoFiles = generateFor.map { Path("${it.name}.proto") }, importProtoFiles = importsSet.map { Path("${it.name}.proto") }, - generatedFiles = listOf( - Path(ktFile), - Path(RPC_INTERNAL, ktFile), - ), - notExecuted = SSets.entries.filter { it != sourceSet && it !in importsSet }, + generatedFiles = generateFor.flatMap { + val ktFile = "${it.capital}.kt" + + listOf( + Path(ktFile), + Path(RPC_INTERNAL, ktFile), + ) + }, + notExecuted = sourceSet.all().filter { it != sourceSet && it !in importsSet }, ) } @@ -282,8 +375,17 @@ abstract class GrpcBaseTest : BaseTest() { fun generateBufYaml(sourceSet: SSets) = "generateBufYaml${sourceSet.capital}" fun generateBufGenYaml(sourceSet: SSets) = "generateBufGenYaml${sourceSet.capital}" - val mainSourceSet = if (isKmp) SSets.commonMain else SSets.main - val testSourceSet = if (isKmp) SSets.commonTest else SSets.test + val mainSourceSet: SSets = when (type) { + Type.Kmp -> SSetsKmp.commonMain + Type.Jvm -> SSetsJvm.main + Type.Android -> SSetsAndroid.Default.main + } + + val testSourceSet: SSets = when (type) { + Type.Kmp -> SSetsKmp.commonTest + Type.Jvm -> SSetsJvm.test + Type.Android -> SSetsAndroid.Default.test + } val bufGenerateCommonMain = bufGenerate(mainSourceSet) val bufGenerateCommonTest = bufGenerate(testSourceSet) @@ -337,10 +439,22 @@ abstract class GrpcBaseTest : BaseTest() { } } - @Suppress("EnumEntryName", "detekt.EnumNaming") - enum class SSets(val minKotlin: KotlinVersion = KtVersion.v2_0_0) { + interface SSets { + val minKotlin: KotlinVersion + val name: String + fun all(): List + } + + enum class SSetsJvm(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSets { main, test, + ; + + override fun all(): List { + return entries + } + } + enum class SSetsKmp(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSets { commonMain, commonTest, jvmMain, jvmTest, androidMain, androidTest, @@ -350,6 +464,37 @@ abstract class GrpcBaseTest : BaseTest() { appleMain, appleTest, macosMain, macosTest, macosArm64Main, macosArm64Test, + ; + + override fun all(): List { + return entries + } + } + + sealed interface SSetsAndroid : SSets { + enum class Default(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsAndroid { + main, test, + androidTest, testFixtures, + + debug, release, + androidTestDebug, + testFixturesDebug, testFixturesRelease, + testDebug, testRelease, + ; + + override fun all(): List { + return entries + } + } + + enum class Test(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsAndroid { + main, debug, + ; + + override fun all(): List { + return entries + } + } } private val SSets.capital get() = name.replaceFirstChar { it.titlecase() } diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/build.gradle.kts new file mode 100644 index 000000000..806d88e85 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTest/proto/androidTest.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTest/proto/androidTest.proto new file mode 100644 index 000000000..509c020b7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTest/proto/androidTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTestDebug/proto/androidTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTestDebug/proto/androidTestDebug.proto new file mode 100644 index 000000000..7e68a56ae --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/androidTestDebug/proto/androidTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/debug/proto/debug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/debug/proto/debug.proto new file mode 100644 index 000000000..280702f10 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/debug/proto/debug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Debug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/main/proto/main.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/main/proto/main.proto new file mode 100644 index 000000000..588046d54 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/main/proto/main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/release/proto/release.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/release/proto/release.proto new file mode 100644 index 000000000..c3d9a0bc4 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/release/proto/release.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Release { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/test/proto/test.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/test/proto/test.proto new file mode 100644 index 000000000..51cfb5882 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/test/proto/test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testDebug/proto/testDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testDebug/proto/testDebug.proto new file mode 100644 index 000000000..d3efa4a23 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testDebug/proto/testDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixtures/proto/testFixtures.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixtures/proto/testFixtures.proto new file mode 100644 index 000000000..16a35e7c9 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixtures/proto/testFixtures.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixtures { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesDebug/proto/testFixturesDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesDebug/proto/testFixturesDebug.proto new file mode 100644 index 000000000..d09209564 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesDebug/proto/testFixturesDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesRelease/proto/testFixturesRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesRelease/proto/testFixturesRelease.proto new file mode 100644 index 000000000..090356c1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testFixturesRelease/proto/testFixturesRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testRelease/proto/testRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testRelease/proto/testRelease.proto new file mode 100644 index 000000000..bb2b93616 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/all_default_source_sets/src/testRelease/proto/testRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts new file mode 100644 index 000000000..608a5a4e4 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts @@ -0,0 +1,226 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.kotlin.dsl.version +import kotlinx.rpc.buf.tasks.* +import kotlinx.rpc.buf.* +import org.gradle.api.provider.Provider +import javax.inject.Inject + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} + +public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { + init { + command.set("lint") + args.set(emptyList()) + } +} + +rpc { + protoc { + buf { + tasks { + registerWorkspaceTask("lint") + } + } + } +} + +fun Iterable.toNames() = map { it.name }.toSet() + +fun assertTasks( + tag: String, + tasks: Iterable, + vararg expected: String, +) { + val names = tasks.toNames() + if (expected.toSet() != names) { + throw GradleException("[$tag] Expected: ${expected.toSet()}, actual: $names") + } +} + +tasks.register("test_tasks") { + doLast { + val genTasks = rpc.protoc.get().buf.generate.allTasks() + + assertTasks( + "gen all", genTasks, + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + "bufGenerateDebug", + "bufGenerateRelease", + ) + + assertTasks( + "testTasks", genTasks.testTasks(), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + ) + + assertTasks( + "testTasks", genTasks.unitTestTasks(), + "bufGenerateTestDebug", + "bufGenerateTestRelease", + ) + + assertTasks( + "testTasks", genTasks.androidTestTasks(), + "bufGenerateAndroidTestDebug", + ) + + assertTasks( + "nonTestTasks", genTasks.nonTestTasks(), + "bufGenerateDebug", + "bufGenerateRelease", + ) + + assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) + assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) + assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) + assertTasks("executedForSourceSet main", genTasks.executedForSourceSet("main")) + assertTasks("executedForSourceSet test", genTasks.executedForSourceSet("test")) + + assertTasks( + "matchingSourceSet debug", + genTasks.matchingSourceSet("debug"), + "bufGenerateDebug", + ) + + assertTasks( + "matchingSourceSet testDebug", + genTasks.matchingSourceSet("testDebug"), + "bufGenerateTestDebug", + ) + + assertTasks( + "matchingSourceSet androidTestDebug", + genTasks.matchingSourceSet("androidTestDebug"), + "bufGenerateAndroidTestDebug", + ) + + assertTasks( + "executedForSourceSet debug", + genTasks.executedForSourceSet("debug"), + "bufGenerateDebug", + ) + + assertTasks( + "executedForSourceSet testDebug", + genTasks.executedForSourceSet("testDebug"), + "bufGenerateDebug", + "bufGenerateTestDebug", + ) + + assertTasks( + "executedForSourceSet androidTestDebug", + genTasks.executedForSourceSet("androidTestDebug"), + "bufGenerateDebug", + "bufGenerateAndroidTestDebug", + ) + + assertTasks( + "matchingFlavor freeapp", genTasks.matchingFlavor("freeapp"), + ) + + assertTasks( + "matchingBuildType debug", genTasks.matchingBuildType("debug"), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateDebug", + ) + + assertTasks( + "matchingBuildType release", genTasks.matchingBuildType("release"), + "bufGenerateTestRelease", + "bufGenerateRelease", + ) + + assertTasks( + "matchingVariant debug", genTasks.matchingVariant("debug"), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateDebug", + ) + + assertTasks( + "matchingBuildType debug unit tests", genTasks.matchingBuildType("debug").unitTestTasks(), + "bufGenerateTestDebug", + ) + + assertTasks( + "matchingBuildType debug android tests", genTasks.matchingBuildType("debug").androidTestTasks(), + "bufGenerateAndroidTestDebug", + ) + + val allTasks = rpc.protoc.get().buf.tasks.all() + + assertTasks( + "all", allTasks, + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + "bufGenerateDebug", + "bufGenerateRelease", + + "bufLintAndroidTestDebug", + "bufLintTestDebug", + "bufLintTestRelease", + "bufLintDebug", + "bufLintRelease", + ) + + assertTasks( + "all by type generate", allTasks.matchingType(), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + "bufGenerateDebug", + "bufGenerateRelease", + ) + + assertTasks( + "all by type lint", allTasks.matchingType(), + + "bufLintAndroidTestDebug", + "bufLintTestDebug", + "bufLintTestRelease", + "bufLintDebug", + "bufLintRelease", + ) + + assertTasks( + "all matchingSourceSet debug", + allTasks.matchingSourceSet("debug"), + "bufGenerateDebug", + "bufLintDebug", + ) + + assertTasks( + "all matchingSourceSet testDebug", + allTasks.matchingSourceSet("testDebug"), + "bufGenerateTestDebug", + "bufLintTestDebug", + ) + + assertTasks( + "all matchingSourceSet androidTestDebug", + allTasks.matchingSourceSet("androidTestDebug"), + "bufGenerateAndroidTestDebug", + "bufLintAndroidTestDebug", + ) + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts new file mode 100644 index 000000000..28cea23f0 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts @@ -0,0 +1,480 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.kotlin.dsl.version +import kotlinx.rpc.buf.tasks.* +import kotlinx.rpc.buf.* +import org.gradle.api.provider.Provider +import javax.inject.Inject + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 + + flavorDimensions("abi", "version") + + productFlavors { + create("freeapp") { + dimension = "version" + } + create("retailapp") { + dimension = "version" + } + create("x86") { + dimension = "abi" + } + create("arm") { + dimension = "abi" + } + } +} + +public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { + init { + command.set("lint") + args.set(emptyList()) + } +} + +rpc { + protoc { + buf { + tasks { + registerWorkspaceTask("lint") + } + } + } +} + +fun Iterable.toNames() = map { it.name }.toSet() + +fun assertTasks( + tag: String, + tasks: Iterable, + vararg expected: String, +) { + val names = tasks.toNames() + if (expected.toSet() != names) { + throw GradleException("[$tag] Expected: ${expected.toSet()}, actual: $names") + } +} + +tasks.register("test_tasks") { + doLast { + val genTasks = rpc.protoc.get().buf.generate.allTasks() + + assertTasks( + "gen all", genTasks, + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + ) + + assertTasks( + "testTasks", genTasks.testTasks(), + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + ) + + assertTasks( + "testTasks", genTasks.unitTestTasks(), + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + ) + + assertTasks( + "testTasks", genTasks.androidTestTasks(), + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + ) + + assertTasks( + "nonTestTasks", genTasks.nonTestTasks(), + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + ) + + assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) + assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) + assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) + assertTasks("executedForSourceSet main", genTasks.executedForSourceSet("main")) + assertTasks("executedForSourceSet test", genTasks.executedForSourceSet("test")) + + assertTasks( + "matchingSourceSet armFreeappDebug", + genTasks.matchingSourceSet("armFreeappDebug"), + "bufGenerateArmFreeappDebug", + ) + + assertTasks( + "matchingSourceSet testArmFreeappDebug", + genTasks.matchingSourceSet("testArmFreeappDebug"), + "bufGenerateTestArmFreeappDebug", + ) + + assertTasks( + "matchingSourceSet androidTestArmFreeappDebug", + genTasks.matchingSourceSet("androidTestArmFreeappDebug"), + "bufGenerateAndroidTestArmFreeappDebug", + ) + + assertTasks( + "executedForSourceSet armFreeappDebug", + genTasks.executedForSourceSet("armFreeappDebug"), + "bufGenerateArmFreeappDebug", + ) + + assertTasks( + "executedForSourceSet testArmFreeappDebug", + genTasks.executedForSourceSet("testArmFreeappDebug"), + "bufGenerateArmFreeappDebug", + "bufGenerateTestArmFreeappDebug", + ) + + assertTasks( + "executedForSourceSet androidTestArmFreeappDebug", + genTasks.executedForSourceSet("androidTestArmFreeappDebug"), + "bufGenerateArmFreeappDebug", + "bufGenerateAndroidTestArmFreeappDebug", + ) + + assertTasks( + "matchingFlavor freeapp", genTasks.matchingFlavor("freeapp"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + ) + + assertTasks( + "matchingFlavor arm", genTasks.matchingFlavor("arm"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + ) + + assertTasks( + "matchingBuildType debug", genTasks.matchingBuildType("debug"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmRetailappDebug", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86RetailappDebug", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmRetailappDebug", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86RetailappDebug", + ) + + assertTasks( + "matchingBuildType relase", genTasks.matchingBuildType("release"), + + // test arm + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappRelease", + + // arm + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappRelease", + ) + + assertTasks( + "matchingVariant armFreeappDebug", genTasks.matchingVariant("armFreeappDebug"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + + // arm + "bufGenerateArmFreeappDebug", + ) + + assertTasks( + "matchingVariant armFreeappDebug udet tests", genTasks.matchingVariant("armFreeappDebug").unitTestTasks(), + "bufGenerateTestArmFreeappDebug", + ) + + assertTasks( + "armFreeappDebug armFreeappDebug android tests", genTasks.matchingVariant("armFreeappDebug").androidTestTasks(), + "bufGenerateAndroidTestArmFreeappDebug", + ) + + val allTasks = rpc.protoc.get().buf.tasks.all() + + assertTasks( + "all", allTasks, + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + + // android test + "bufLintAndroidTestArmFreeappDebug", + "bufLintAndroidTestArmRetailappDebug", + "bufLintAndroidTestX86FreeappDebug", + "bufLintAndroidTestX86RetailappDebug", + + // test arm + "bufLintTestArmFreeappDebug", + "bufLintTestArmFreeappRelease", + "bufLintTestArmRetailappDebug", + "bufLintTestArmRetailappRelease", + + // test x86 + "bufLintTestX86FreeappDebug", + "bufLintTestX86FreeappRelease", + "bufLintTestX86RetailappDebug", + "bufLintTestX86RetailappRelease", + + // arm + "bufLintArmFreeappDebug", + "bufLintArmFreeappRelease", + "bufLintArmRetailappDebug", + "bufLintArmRetailappRelease", + + // x86 + "bufLintX86FreeappDebug", + "bufLintX86FreeappRelease", + "bufLintX86RetailappDebug", + "bufLintX86RetailappRelease", + ) + + assertTasks( + "all by type generate", + allTasks.matchingType(), + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + ) + + assertTasks( + "all by type lint", allTasks.matchingType(), + + // android test + "bufLintAndroidTestArmFreeappDebug", + "bufLintAndroidTestArmRetailappDebug", + "bufLintAndroidTestX86FreeappDebug", + "bufLintAndroidTestX86RetailappDebug", + + // test arm + "bufLintTestArmFreeappDebug", + "bufLintTestArmFreeappRelease", + "bufLintTestArmRetailappDebug", + "bufLintTestArmRetailappRelease", + + // test x86 + "bufLintTestX86FreeappDebug", + "bufLintTestX86FreeappRelease", + "bufLintTestX86RetailappDebug", + "bufLintTestX86RetailappRelease", + + // arm + "bufLintArmFreeappDebug", + "bufLintArmFreeappRelease", + "bufLintArmRetailappDebug", + "bufLintArmRetailappRelease", + + // x86 + "bufLintX86FreeappDebug", + "bufLintX86FreeappRelease", + "bufLintX86RetailappDebug", + "bufLintX86RetailappRelease", + ) + + assertTasks( + "all matchingSourceSet armFreeappDebug", + allTasks.matchingSourceSet("armFreeappDebug"), + "bufGenerateArmFreeappDebug", + "bufLintArmFreeappDebug", + ) + + assertTasks( + "all matchingSourceSet testArmFreeappDebug", + allTasks.matchingSourceSet("testArmFreeappDebug"), + "bufGenerateTestArmFreeappDebug", + "bufLintTestArmFreeappDebug", + ) + + assertTasks( + "all matchingSourceSet androidTestArmFreeappDebug", + allTasks.matchingSourceSet("androidTestArmFreeappDebug"), + "bufGenerateAndroidTestArmFreeappDebug", + "bufLintAndroidTestArmFreeappDebug", + ) + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/build.gradle.kts new file mode 100644 index 000000000..806d88e85 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/ok/ok.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/ok/ok.proto new file mode 100644 index 000000000..496efb705 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/ok/ok.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package ok; + +message Ok { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration/src/main/proto/some.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Some { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/build.gradle.kts new file mode 100644 index 000000000..9d5001244 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.dynamic-feature") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/ok/ok.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/ok/ok.proto new file mode 100644 index 000000000..496efb705 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/ok/ok.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package ok; + +message Ok { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_dynamic_feature/src/main/proto/some.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Some { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/build.gradle.kts new file mode 100644 index 000000000..87d7cf4bc --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.library") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/ok/ok.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/ok/ok.proto new file mode 100644 index 000000000..496efb705 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/ok/ok.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package ok; + +message Ok { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_library/src/main/proto/some.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Some { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/build.gradle.kts new file mode 100644 index 000000000..3517fd37c --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/build.gradle.kts @@ -0,0 +1,24 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +// include: some + +plugins { + id("com.android.test") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 + + targetProjectPath = "some" +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/some/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/some/build.gradle.kts new file mode 100644 index 000000000..01a86351d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/some/build.gradle.kts @@ -0,0 +1,15 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") + kotlin("android") +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/ok/ok.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/ok/ok.proto new file mode 100644 index 000000000..496efb705 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/ok/ok.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package ok; + +message Ok { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/minimal_grpc_configuration_test/src/main/proto/some.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Some { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/more_flavors/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/more_flavors/build.gradle.kts new file mode 100644 index 000000000..4936d9bc5 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/more_flavors/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 + + flavorDimensions("abi", "version") + + productFlavors { + create("freeapp") { + dimension = "version" + } + create("retailapp") { + dimension = "version" + } + create("x86") { + dimension = "abi" + } + create("arm") { + dimension = "abi" + } + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/no_grpc/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/no_grpc/build.gradle.kts new file mode 100644 index 000000000..3c7de2a9d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/no_grpc/build.gradle.kts @@ -0,0 +1,11 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/build.gradle.kts new file mode 100644 index 000000000..806d88e85 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTest/proto/androidTest.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTest/proto/androidTest.proto new file mode 100644 index 000000000..509c020b7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTest/proto/androidTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTestDebug/proto/androidTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTestDebug/proto/androidTestDebug.proto new file mode 100644 index 000000000..7e68a56ae --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/androidTestDebug/proto/androidTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/debug/proto/debug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/debug/proto/debug.proto new file mode 100644 index 000000000..280702f10 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/debug/proto/debug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Debug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/main/proto/main.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/main/proto/main.proto new file mode 100644 index 000000000..588046d54 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/main/proto/main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/release/proto/release.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/release/proto/release.proto new file mode 100644 index 000000000..c3d9a0bc4 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/release/proto/release.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Release { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/test/proto/test.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/test/proto/test.proto new file mode 100644 index 000000000..51cfb5882 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/test/proto/test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testDebug/proto/testDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testDebug/proto/testDebug.proto new file mode 100644 index 000000000..d3efa4a23 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testDebug/proto/testDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixtures/proto/testFixtures.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixtures/proto/testFixtures.proto new file mode 100644 index 000000000..16a35e7c9 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixtures/proto/testFixtures.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixtures { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesDebug/proto/testFixturesDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesDebug/proto/testFixturesDebug.proto new file mode 100644 index 000000000..d09209564 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesDebug/proto/testFixturesDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesRelease/proto/testFixturesRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesRelease/proto/testFixturesRelease.proto new file mode 100644 index 000000000..090356c1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testFixturesRelease/proto/testFixturesRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testRelease/proto/testRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testRelease/proto/testRelease.proto new file mode 100644 index 000000000..bb2b93616 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/proto_tasks_are_cached_properly/src/testRelease/proto/testRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/build.gradle.kts new file mode 100644 index 000000000..806d88e85 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + id("com.android.application") version "" + kotlin("android") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" +} + +rpc { + protoc() +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/ok/ok.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/ok/ok.proto new file mode 100644 index 000000000..496efb705 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/ok/ok.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package ok; + +message Ok { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/test_only_sources/src/testFixtures/proto/some.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Some { + string content = 1; +} From da15eb077a69a2d24d734786cc63e4ec5315c2d5 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Sun, 7 Dec 2025 22:30:25 +0100 Subject: [PATCH 04/11] Android + KMP setup --- .../kotlinx/rpc/buf/tasks/BufExecTask.kt | 24 +- .../kotlinx/rpc/buf/tasks/BufGenerateTask.kt | 6 +- .../kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt | 72 ++- .../rpc/protoc/DefaultProtoSourceSet.kt | 115 +++-- .../rpc/protoc/DefaultProtocExtension.kt | 436 +++++++++++++----- .../kotlinx/rpc/protoc/ProcessProtoFiles.kt | 2 + .../kotlinx/rpc/protoc/android/variants.kt | 82 +++- .../src/main/kotlin/kotlinx/rpc/util/kgp.kt | 92 ++-- .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 2 - .../buf_tasks_default/build.gradle.kts | 18 +- .../buf_tasks_extended/build.gradle.kts | 20 +- .../buf_tasks/build.gradle.kts | 2 +- .../buf_tasks/build.gradle.kts | 3 +- 13 files changed, 618 insertions(+), 256 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt index f9821d73f..514da0f31 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt @@ -4,7 +4,6 @@ package kotlinx.rpc.buf.tasks -import com.android.build.api.variant.Variant import kotlinx.rpc.buf.BUF_EXECUTABLE_CONFIGURATION import kotlinx.rpc.buf.BufExtension import kotlinx.rpc.buf.execBuf @@ -40,7 +39,7 @@ import javax.inject.Inject */ public abstract class BufExecTask @Inject constructor( @Internal - public val properties: Properties, + public val properties: Provider, ) : DefaultTask() { init { group = PROTO_GROUP @@ -89,29 +88,36 @@ public abstract class BufExecTask @Inject constructor( public class AndroidProperties internal constructor( isTest: Boolean, sourceSetName: String, + /** * Name of the android flavors this task is associated with. * - * @see [Variant.productFlavors] + * Can be empty for 'com.android.kotlin.multiplatform.library' source sets. + * + * @see com.android.build.api.variant.Variant.productFlavors */ public val flavors: List, + /** * Name of the android build type this task is associated with. * - * @see Variant.buildType + * @see com.android.build.api.variant.Variant.buildType */ public val buildType: String?, + /** * Name of the android variant this task is associated with. * - * @see Variant.name + * Can be `null` for 'com.android.kotlin.multiplatform.library' source sets. + * + * @see com.android.build.api.variant.Variant.name */ - public val variant: String, + public val variant: String?, /** * Whether the task is for instrumentation tests. */ - public val isAndroidTest: Boolean, + public val isInstrumentedTest: Boolean, /** * Whether the task is for unit tests. @@ -179,7 +185,7 @@ public abstract class BufExecTask @Inject constructor( public inline fun Project.registerBufExecTask( name: String, workingDir: Provider, - properties: BufExecTask.Properties, + properties: Provider, noinline configuration: T.() -> Unit, ): TaskProvider = registerBufExecTask(T::class, name, workingDir, properties, configuration) @@ -188,7 +194,7 @@ internal fun Project.registerBufExecTask( clazz: KClass, name: String, workingDir: Provider, - properties: BufExecTask.Properties, + properties: Provider, configuration: T.() -> Unit = {}, ): TaskProvider = tasks.register(name, clazz, properties).apply { configure { diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt index 9232d123b..f96b58dc8 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt @@ -20,10 +20,8 @@ import kotlinx.rpc.protoc.DefaultProtoSourceSet import kotlinx.rpc.protoc.bufExecProperties import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider -import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputDirectories -import org.gradle.api.tasks.SkipWhenEmpty import org.gradle.kotlin.dsl.listProperty import javax.inject.Inject @@ -32,7 +30,9 @@ import javax.inject.Inject * * @see buf generate */ -public abstract class BufGenerateTask @Inject internal constructor(properties: Properties) : BufExecTask(properties) { +public abstract class BufGenerateTask @Inject internal constructor( + properties: Provider, +) : BufExecTask(properties) { // used to properly calculate output directories @get:Input internal abstract val pluginNames: ListProperty diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt index 1b10c4391..014008203 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt @@ -330,78 +330,68 @@ public sealed interface BufTasks : TaskCollection - // android - /** - * Android only. - * * Filters tasks by where [BufExecTask.AndroidProperties.isUnitTest] is `true`. * * ```kotlin * rpc.protoc { - * buf.tasks.all().unitTestTasks() + * buf.tasks.all().androidUnitTestTasks() * } * ``` */ - public fun unitTestTasks(): BufTasks + public fun androidUnitTestTasks(): BufTasks /** - * Android only. - * - * Filters tasks by where [BufExecTask.AndroidProperties.isAndroidTest] is `true`. + * Filters tasks by where [BufExecTask.AndroidProperties.isInstrumentedTest] is `true`. * * ```kotlin * rpc.protoc { - * buf.tasks.all().androidTestTasks() + * buf.tasks.all().androidInstrumentedTestTasks() * } * ``` */ - public fun androidTestTasks(): BufTasks + public fun androidInstrumentedTestTasks(): BufTasks /** - * Android only. - * * Filters tasks by where [BufExecTask.AndroidProperties.flavors] matches the given flavor. * + * When `null` is passed, only variants without flavors are returned. + * * Only returns Android tasks. * * ```kotlin * rpc.protoc { - * buf.tasks.all().matchingFlavor("freeApp") + * buf.tasks.all().matchingAndroidFlavor("freeApp") * } * ``` */ - public fun matchingFlavor(flavor: String): BufTasks + public fun matchingAndroidFlavor(flavor: String?): BufTasks /** - * Android only. - * * Filters tasks by where [BufExecTask.AndroidProperties.buildType] matches the given buildType. * * Only returns Android tasks. * * ```kotlin * rpc.protoc { - * buf.tasks.all().matchingBuildType("debug") + * buf.tasks.all().matchingAndroidBuildType("debug") * } * ``` */ - public fun matchingBuildType(buildType: String?): BufTasks + public fun matchingAndroidBuildType(buildType: String?): BufTasks /** - * Android only. - * * Filters tasks by where [BufExecTask.AndroidProperties.variant] matches the given variant. * * Only returns Android tasks. * * ```kotlin * rpc.protoc { - * buf.tasks.all().matchingVariant("freeAppDebug") + * buf.tasks.all().matchingAndroidVariant("freeAppDebug") * } * ``` */ - public fun matchingVariant(variant: String): BufTasks + public fun matchingAndroidVariant(variant: String?): BufTasks } /** @@ -503,7 +493,7 @@ internal open class BufTasksImpl internal constructor( override fun matchingSourceSet(sourceSetName: String): BufTasks { return BufTasksImpl( project, - collection.matching { it.properties.sourceSetName == sourceSetName }, + collection.matching { it.properties.get().sourceSetName == sourceSetName }, kClass ) } @@ -526,7 +516,7 @@ internal open class BufTasksImpl internal constructor( override fun executedForSourceSet(sourceSetName: String): BufTasks { val allExecuted = project.tasks.withType(kClass.java).matching { - it.properties.sourceSetName == sourceSetName + it.properties.get().sourceSetName == sourceSetName }.singleOrNull()?.bufDependsOn(kClass) ?: return empty() val allExecutedLazySet = lazy { allExecuted.map { it.name }.toSet() } @@ -534,7 +524,7 @@ internal open class BufTasksImpl internal constructor( return BufTasksImpl( project = project, collection = collection.matching { dependency -> - dependency.properties.sourceSetName == sourceSetName || dependency.name in allExecutedLazySet.value + dependency.properties.get().sourceSetName == sourceSetName || dependency.name in allExecutedLazySet.value }, kClass = kClass, ) @@ -557,18 +547,18 @@ internal open class BufTasksImpl internal constructor( } override fun testTasks(): BufTasks { - return BufTasksImpl(project, collection.matching { it.properties.isTest }, kClass) + return BufTasksImpl(project, collection.matching { it.properties.get().isTest }, kClass) } override fun nonTestTasks(): BufTasks { - return BufTasksImpl(project, collection.matching { !it.properties.isTest }, kClass) + return BufTasksImpl(project, collection.matching { !it.properties.get().isTest }, kClass) } - override fun unitTestTasks(): BufTasks { + override fun androidUnitTestTasks(): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - val properties = it.properties as? BufExecTask.AndroidProperties + val properties = it.properties.get() as? BufExecTask.AndroidProperties ?: return@matching false properties.isUnitTest @@ -577,37 +567,41 @@ internal open class BufTasksImpl internal constructor( ) } - override fun androidTestTasks(): BufTasks { + override fun androidInstrumentedTestTasks(): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - val properties = it.properties as? BufExecTask.AndroidProperties + val properties = it.properties.get() as? BufExecTask.AndroidProperties ?: return@matching false - properties.isAndroidTest + properties.isInstrumentedTest }, kClass = kClass, ) } - override fun matchingFlavor(flavor: String): BufTasks { + override fun matchingAndroidFlavor(flavor: String?): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - val properties = it.properties as? BufExecTask.AndroidProperties + val properties = it.properties.get() as? BufExecTask.AndroidProperties ?: return@matching false + if (flavor == null) { + return@matching properties.flavors.isEmpty() + } + properties.flavors.contains(flavor) }, kClass = kClass, ) } - override fun matchingBuildType(buildType: String?): BufTasks { + override fun matchingAndroidBuildType(buildType: String?): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - val properties = it.properties as? BufExecTask.AndroidProperties + val properties = it.properties.get() as? BufExecTask.AndroidProperties ?: return@matching false properties.buildType == buildType @@ -616,11 +610,11 @@ internal open class BufTasksImpl internal constructor( ) } - override fun matchingVariant(variant: String): BufTasks { + override fun matchingAndroidVariant(variant: String?): BufTasks { return BufTasksImpl( project = project, collection = collection.matching { - val properties = it.properties as? BufExecTask.AndroidProperties + val properties = it.properties.get() as? BufExecTask.AndroidProperties ?: return@matching false properties.variant == variant diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index a8fbdb6b0..9f9b730f9 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -7,7 +7,11 @@ package kotlinx.rpc.protoc import kotlinx.rpc.buf.tasks.BufExecTask import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.rpcExtension +import kotlinx.rpc.util.KotlinPluginId import kotlinx.rpc.util.findOrCreate +import kotlinx.rpc.util.withAndroid +import kotlinx.rpc.util.withAndroidSourceSets +import kotlinx.rpc.util.withKotlin import kotlinx.rpc.util.withLazyJavaPluginExtension import kotlinx.rpc.util.withKotlinSourceSets import org.gradle.api.Action @@ -29,8 +33,6 @@ import org.gradle.kotlin.dsl.setProperty import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer -import org.jetbrains.kotlin.tooling.core.extrasKeyOf import java.io.File import java.util.* import java.util.function.Consumer @@ -45,13 +47,7 @@ internal class ProtoSourceSetFactory( private val project: Project, ) : NamedDomainObjectFactory { override fun create(name: String): ProtoSourceSet { - val isAndroid = project.the() - .sourceSets.findByName(name) - ?.extras - ?.get(isAndroidKey) - ?: false - - return project.objects.newInstance(DefaultProtoSourceSet::class.java, project, name, isAndroid) + return project.objects.newInstance(DefaultProtoSourceSet::class.java, project, name) } } @@ -70,16 +66,16 @@ internal fun Project.findOrCreateProtoSourceSets(): NamedDomainObjectContainer

= project.objects.listProperty() val generateTask: Property = project.objects.property() - // main/test for Kotlin/Android, androidMain/androidTest for KMP - // only set for variant.name sourceSets - val androidRoot: Property = project.objects.property() - // used to track test tasks' dependencies for main tasks, e.g.: + // source set that is associated with com.android.(application|library|test|dynamic-feature) plugin + // + // androidMain/androidHostTest/androidDeviceTest source sets for com.android.kotlin.multiplatform.library + // are NOT considered to be legacy Android source sets + // + // androidDebug, androidInstrumentedTest, androidInstrumentedTestDebug, androidMain, etc. from + // the combination of com.android.* and kotlin.(multiplatform|android) are considered legacy Android source sets + internal val isLegacyAndroid: Property = project.objects.property() + .convention(false) + + // when com.android.* (not kotlin.multiplatform.library) is applied - kotlin has proxy source sets: + // | AndroidSourceSet | KotlinSourceSet | + // | main | androidMain | + // | test | androidUnitTest | + // | debug | androidDebug | + // ... + // + // these kotlin 'proxy' source sets have propper dependsOn values, but should not have tasks configures, + // as tasks are configured once for the android source sets + internal val isKotlinProxyLegacyAndroid: Property = project.objects.property() + .convention(false) + + // used to track tasks' dependencies for main/commonMain/commonTest tasks, e.g.: // - bufGenerateTestDebug depends on bufGenerateDebug + // - bufGenerateTestDebug depends on bufGenerateCommonTest and bufGenerateDebug for KMP // - // so we need to track this dependency using this property - val androidMain: Property = project.objects.property() + // So we need to track this dependency using this property + val androidDependencies: SetProperty = project.objects.setProperty() // only set for variant.name sourceSets val androidProperties: Property = project.objects.property() - .convention(null) override val imports: SetProperty = project.objects.setProperty() override val fileImports: ConfigurableFileCollection = project.objects.fileCollection() @@ -164,7 +180,13 @@ internal open class DefaultProtoSourceSet( imports.addAll(protoSourceSet.flatMap { it.imports.checkSelfImport() }) } + private val extendsFrom: MutableSet = mutableSetOf() + override fun extendsFrom(protoSourceSet: ProtoSourceSet) { + if (extendsFrom.contains(protoSourceSet)) { + return + } + if (this == protoSourceSet) { throw IllegalArgumentException("$name proto source set cannot extend from self") } @@ -176,6 +198,8 @@ internal open class DefaultProtoSourceSet( ) } + extendsFrom += protoSourceSet + source(protoSourceSet.sourceDirectorySet) imports.addAll(protoSourceSet.imports.checkSelfImport()) } @@ -217,7 +241,7 @@ internal fun Project.createProtoExtensions() { fun findOrCreateAndConfigure( languageSourceSetName: String, languageSourceSet: Any?, - ): ProtoSourceSet { + ): DefaultProtoSourceSet { val container = findOrCreateProtoSourceSets() val protoSourceSet = container.maybeCreate(languageSourceSetName) as DefaultProtoSourceSet @@ -226,15 +250,30 @@ internal fun Project.createProtoExtensions() { return protoSourceSet } - project.withKotlinSourceSets { isAndroid, extension -> - extension.sourceSets.all { - extras[isAndroidKey] = isAndroid - findOrCreateAndConfigure(name, this) + // CCE free check for kotlin + withKotlin { + withKotlinSourceSets { id, extension -> + extension.sourceSets.all { + findOrCreateAndConfigure(name, this) + } + + if (id != KotlinPluginId.MULTIPLATFORM) { + return@withKotlinSourceSets + } + } + } + + // CCE free check for android + withAndroid { + withAndroidSourceSets { sourceSets -> + sourceSets.all { + findOrCreateAndConfigure(name, this) + } } } - project.withLazyJavaPluginExtension { - sourceSets.configureEach { + withLazyJavaPluginExtension { + sourceSets.all { val protoSourceSet = findOrCreateAndConfigure(name, this) findOrCreate(PROTO_SOURCE_SET_EXTENSION_NAME) { @@ -244,17 +283,15 @@ internal fun Project.createProtoExtensions() { } } -private val isAndroidKey = extrasKeyOf("kxrpc_proto_source_set_is_android") - -internal fun DefaultProtoSourceSet.bufExecProperties(): BufExecTask.Properties { - // main has androidRoot.isPresent=false and should not be included, although it is an Android source set - return if (isAndroid && androidRoot.isPresent) { - androidProperties.orNull - ?: throw GradleException("Android properties are not set for source set $name") - } else { - BufExecTask.Properties( - isTest = name.lowercase().endsWith("test"), - sourceSetName = name, - ) +internal fun DefaultProtoSourceSet.bufExecProperties(): Provider { + return project.provider { + if (androidProperties.isPresent) { + androidProperties.get() + } else { + BufExecTask.Properties( + isTest = name.lowercase().endsWith("test"), + sourceSetName = name, + ) + } } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index 243025c61..eceb3bb3b 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.protoc +import com.android.build.api.dsl.AndroidSourceSet import com.android.build.api.variant.Variant import com.android.build.gradle.BaseExtension import kotlinx.rpc.buf.BufExtension @@ -18,10 +19,20 @@ import kotlinx.rpc.buf.tasks.registerGenerateBufGenYamlTask import kotlinx.rpc.buf.tasks.registerGenerateBufYamlTask import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM -import kotlinx.rpc.protoc.android.AndroidRootSourceSets -import kotlinx.rpc.protoc.android.sourceSets +import kotlinx.rpc.protoc.android.KmpLibraryAndroidLeafSourceSets +import kotlinx.rpc.protoc.android.LegacyAndroidRootSourceSets +import kotlinx.rpc.protoc.android.androidOriginFromKotlinProxySourceSetName +import kotlinx.rpc.protoc.android.dependencySourceSets +import kotlinx.rpc.protoc.android.kotlinProxyFromAndroidOriginSourceSetName +import kotlinx.rpc.util.ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY +import kotlinx.rpc.util.AndroidComponents +import kotlinx.rpc.util.KotlinPluginId import kotlinx.rpc.util.ensureDirectoryExists -import kotlinx.rpc.util.withLazyAndroidComponentsExtension +import kotlinx.rpc.util.hasAndroid +import kotlinx.rpc.util.kotlinPluginId +import kotlinx.rpc.util.withAndroid +import kotlinx.rpc.util.withKotlin +import kotlinx.rpc.util.withLazyLegacyAndroidComponentsExtension import org.gradle.api.Action import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectContainer @@ -34,7 +45,10 @@ import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.newInstance import org.gradle.kotlin.dsl.the +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget +import org.jetbrains.kotlin.gradle.utils.ObservableSet import java.io.File import javax.inject.Inject import kotlin.collections.filterIsInstance @@ -43,7 +57,7 @@ import kotlin.collections.plus internal open class DefaultProtocExtension @Inject constructor( objects: ObjectFactory, - project: Project, + private val project: Project, ) : ProtocExtension { override val buf: BufExtension = project.objects.newInstance() override fun buf(action: Action) { @@ -85,95 +99,50 @@ internal open class DefaultProtocExtension @Inject constructor( defaultOptions() } - project.protoSourceSets.all { - if (this !is DefaultProtoSourceSet || isAndroid) { - return@all - } - - project.configureTasks(this) - } - - project.withLazyAndroidComponentsExtension { - val rootSourceSets = AndroidRootSourceSets.entries - .mapNotNull { rootName -> - val sourceSet = project.protoSourceSets.findByName(rootName.stringValue) - as? DefaultProtoSourceSet - - sourceSet?.let { rootName to it } - }.toMap() + // no way to configure tasks before evaluation is done + project.afterEvaluate { + configureMultiplatformAndroidSourceSets configure@{ protoSourceSet -> + // done in configureLegacyAndroidVariants + if (protoSourceSet.isLegacyAndroid.get()) { + protoSourceSet.setupDefaultImports(protoSourceSets) + val originSourceSetName = protoSourceSet.name.androidOriginFromKotlinProxySourceSetName() + ?: return@configure - val mainRoot = rootSourceSets.getValue(AndroidRootSourceSets.Main) - val testRoot = rootSourceSets[AndroidRootSourceSets.Test] - val androidTestRoot = rootSourceSets[AndroidRootSourceSets.AndroidTest] - val testFixturesRoot = rootSourceSets[AndroidRootSourceSets.TestFixtures] + val originSourceSet = protoSourceSets.findByName(originSourceSetName) + ?: return@configure - // todo KMP imports + originSourceSet.extendsFrom(protoSourceSet) - testRoot?.extendsFrom(testFixturesRoot ?: mainRoot) - androidTestRoot?.extendsFrom(testFixturesRoot ?: mainRoot) - testFixturesRoot?.importsFrom(mainRoot) + return@configure + } - val extension = project.the() - onVariants { variant: Variant -> - val testBuildType = extension.testBuildType - rootSourceSets.forEach { (rootName, rootSourceSet) -> - // testFixtures don't have variants - if (rootName == AndroidRootSourceSets.TestFixtures) { - return@forEach - } + configureTasks(protoSourceSet) + } - if (rootName == AndroidRootSourceSets.AndroidTest && variant.buildType != testBuildType) { - return@forEach + protoSourceSets.all { + withFullyInitializedProtoSourceSet(this) { protoSourceSet -> + // done in configureLegacyAndroidVariants + if (protoSourceSet.isLegacyAndroid.get()) { + return@withFullyInitializedProtoSourceSet } - // but testFixtures still have source sets based on flavors - val testFixtureSets = if (rootName != AndroidRootSourceSets.Main) { - variant.sourceSets(AndroidRootSourceSets.TestFixtures) - } else emptyList() - - val sourceSets = variant.sourceSets(rootName) - val variantSourceSetName = sourceSets.lastOrNull() - ?: throw GradleException("No source sets found for variant ${variant.name}") - - val variantProtoSourceSet = project.protoSourceSets.named(variantSourceSetName) - - variantProtoSourceSet.configure { - val default = this as? DefaultProtoSourceSet ?: return@configure - - default.androidRoot.set(rootSourceSet) - val properties = BufExecTask.AndroidProperties( - isTest = rootName != AndroidRootSourceSets.Main, - isAndroidTest = rootName == AndroidRootSourceSets.AndroidTest, - isUnitTest = rootName == AndroidRootSourceSets.Test, - sourceSetName = variantSourceSetName, - buildType = variant.buildType, - flavors = variant.productFlavors.map { (_, flavor) -> flavor }, - variant = variant.name, - ) - default.androidProperties.set(properties) - - (sourceSets + testFixtureSets).forEach { dependency -> - val dependencyProtoSourceSet = project.protoSourceSets.findByName(dependency) - ?: return@forEach - - if (name != dependency) { - extendsFrom(dependencyProtoSourceSet) - } - } - - if (rootName != AndroidRootSourceSets.Main) { - val mainVariantProtoSourceSet = project.protoSourceSets.findByName(variant.name) - as? DefaultProtoSourceSet - - if (mainVariantProtoSourceSet != null) { - default.androidMain.set(mainVariantProtoSourceSet) + configureTasks(protoSourceSet) + } + } + } - importsFrom(mainVariantProtoSourceSet) - } - } + project.withAndroid { + if (project.plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) { + configureKmpLibAndroidVariants(project) + return@withAndroid + } - project.configureTasks(default) - } + withLazyLegacyAndroidComponentsExtension { + configureLegacyAndroidVariants( + project = project, + isKmp = project.kotlinPluginId == KotlinPluginId.MULTIPLATFORM + ) { + configureTasks(it) } } } @@ -189,7 +158,7 @@ internal open class DefaultProtocExtension @Inject constructor( } @Suppress("detekt.LongMethod", "detekt.CyclomaticComplexMethod", "detekt.ThrowsCount") - private fun Project.configureTasks(protoSourceSet: DefaultProtoSourceSet) { + private fun configureTasks(protoSourceSet: DefaultProtoSourceSet) { val baseName = protoSourceSet.name val buildSourceSetsDir = project.protoBuildDirSourceSets.resolve(baseName) @@ -202,9 +171,9 @@ internal open class DefaultProtocExtension @Inject constructor( .ensureDirectoryExists() // only resolve in task's 'execute' due to the deferred nature of dependsOn - protoSourceSet.setupDefaultImports(protoSourceSets) + protoSourceSet.setupDefaultImports(project.protoSourceSets) - val includedProtocPlugins = provider { + val includedProtocPlugins = project.provider { protoSourceSet.plugins.get().also { list -> list.forEach { plugin -> if (!plugin.artifact.isPresent) { @@ -219,13 +188,13 @@ internal open class DefaultProtocExtension @Inject constructor( val protoFilesDirectorySet = protoSourceSet as SourceDirectorySet - val processProtoTask = registerProcessProtoFilesTask( + val processProtoTask = project.registerProcessProtoFilesTask( name = baseName, destination = buildSourceSetsProtoDir, protoFilesDirectorySet = protoFilesDirectorySet, ) - val processImportProtoTask = registerProcessProtoFilesImportsTask( + val processImportProtoTask = project.registerProcessProtoFilesImportsTask( name = baseName, destination = buildSourceSetsImportDir, importsProvider = protoSourceSet.imports, @@ -234,7 +203,7 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(processProtoTask) } - val generateBufYamlTask = registerGenerateBufYamlTask( + val generateBufYamlTask = project.registerGenerateBufYamlTask( name = baseName, buildSourceSetsDir = buildSourceSetsDir, buildSourceSetsProtoDir = buildSourceSetsProtoDir, @@ -244,7 +213,7 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(processProtoTask) } - val generateBufGenYamlTask = registerGenerateBufGenYamlTask( + val generateBufGenYamlTask = project.registerGenerateBufGenYamlTask( name = baseName, buildSourceSetsDir = buildSourceSetsDir, protocPlugins = includedProtocPlugins, @@ -252,12 +221,12 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(generateBufYamlTask) } - val sourceSetsProtoDirFileTree = fileTree(buildSourceSetsProtoDir) + val sourceSetsProtoDirFileTree = project.fileTree(buildSourceSetsProtoDir) - val bufGenerateTask = registerBufGenerateTask( + val bufGenerateTask = project.registerBufGenerateTask( protoSourceSet = protoSourceSet, workingDir = buildSourceSetsDir, - outputDirectory = protoBuildDirGenerated.resolve(baseName), + outputDirectory = project.protoBuildDirGenerated.resolve(baseName), includedPlugins = includedProtocPlugins, ) { executableFiles.addAll( @@ -283,7 +252,7 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(processImportProtoTask) val dependencies = project.provider { - protoSourceSet.getDependsOnTasksOf(protoSourceSets).mapNotNull { it.generateTask.orNull } + protoSourceSet.getDependsOnTasksOf(project.protoSourceSets).mapNotNull { it.generateTask.orNull } } dependsOn(dependencies) @@ -331,8 +300,20 @@ internal open class DefaultProtocExtension @Inject constructor( sourceSet.java.srcDirs(javaOutputs) } - languageSets.filterIsInstance().forEach { sourceSet -> - sourceSet.kotlin.srcDirs(kotlinOutputs) + project.withKotlin { + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.kotlin.srcDirs(kotlinOutputs) + } + } + + project.withAndroid { + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.java.srcDirs(javaOutputs) + } + + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.kotlin.srcDirs(kotlinOutputs) + } } } @@ -351,7 +332,7 @@ internal open class DefaultProtocExtension @Inject constructor( } } - private fun Project.configureCustomTasks( + private fun configureCustomTasks( protoSourceSet: DefaultProtoSourceSet, buildSourceSetsDir: File, generateBufYamlTask: TaskProvider, @@ -370,9 +351,9 @@ internal open class DefaultProtocExtension @Inject constructor( return "buf$taskCapital$baseCapital" } - registerBufExecTask( + project.registerBufExecTask( clazz = kClass, - workingDir = provider { buildSourceSetsDir }, + workingDir = project.provider { buildSourceSetsDir }, properties = protoSourceSet.bufExecProperties(), name = taskName(baseName), ) { @@ -382,7 +363,7 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(processImportProtoTask) val dependencies = project.provider { - protoSourceSet.getDependsOnTasksOf(protoSourceSets).map { dependency -> + protoSourceSet.getDependsOnTasksOf(project.protoSourceSets).map { dependency -> project.tasks.named(taskName(dependency.name), kClass.java).get() } } @@ -403,8 +384,9 @@ internal open class DefaultProtocExtension @Inject constructor( } private fun DefaultProtoSourceSet.setupDefaultImports(protoSourceSets: ProtoSourceSets) { - if (isAndroid) { - // imports are set up on variant + // isKotlinProxyLegacyAndroid -> this source set has proper dependsOn, that need to be set up + // isLegacyAndroid -> not a 'com.android.kotlin.multiplatform.library' source set, imports are set up on variant + if (!isKotlinProxyLegacyAndroid.get() && isLegacyAndroid.get()) { return } @@ -447,17 +429,255 @@ internal open class DefaultProtocExtension @Inject constructor( } } +// todo will return null always on findByName +private fun configureKmpLibAndroidVariants(project: Project) { + // not considered propper variants, they fit kmp source sets and configured as them, + // except for androidProperties + val sourceSets = KmpLibraryAndroidLeafSourceSets.entries + .mapNotNull { rootName -> + val sourceSet = project.protoSourceSets.findByName(rootName.stringValue) + as? DefaultProtoSourceSet + + sourceSet?.let { rootName to it } + }.toMap() + + val mainRoot = sourceSets.getValue(KmpLibraryAndroidLeafSourceSets.Main) + val unitTestRoot = sourceSets[KmpLibraryAndroidLeafSourceSets.HostTest] + val instrumentedTestRoot = sourceSets[KmpLibraryAndroidLeafSourceSets.DeviceTest] + + // no testFixtures, because they can have a dependency on commonTest in dependOn section + unitTestRoot?.importsFrom(mainRoot) + instrumentedTestRoot?.importsFrom(mainRoot) + + sourceSets.forEach { (rootName) -> + val protoSourceSet = project.protoSourceSets.findByName(rootName.stringValue) + as? DefaultProtoSourceSet ?: return@forEach + + val properties = BufExecTask.AndroidProperties( + isTest = rootName != KmpLibraryAndroidLeafSourceSets.Main, + isInstrumentedTest = rootName == KmpLibraryAndroidLeafSourceSets.DeviceTest, + isUnitTest = rootName == KmpLibraryAndroidLeafSourceSets.HostTest, + sourceSetName = protoSourceSet.name, + buildType = null, + flavors = emptyList(), + variant = null, + ) + + protoSourceSet.androidProperties.set(properties) + } +} + +// init isLegacyAndroid, isKotlinProxyLegacyAndroid +private fun Project.withFullyInitializedProtoSourceSet( + sourceSet: ProtoSourceSet, + body: (DefaultProtoSourceSet) -> Unit, +) { + if (sourceSet !is DefaultProtoSourceSet) { + return + } + + val kotlinPluginId = kotlinPluginId + + val languageSets = sourceSet.languageSourceSets.get() + + val anyAndroid = hasAndroid && languageSets.any { it is AndroidSourceSet } + val anyKotlin = kotlinPluginId != null && languageSets.any { it is KotlinSourceSet } + + if (anyAndroid && !anyKotlin) { + sourceSet.isLegacyAndroid.set(true) + sourceSet.isKotlinProxyLegacyAndroid.set(false) + body(sourceSet) + return + } + + when (kotlinPluginId) { + KotlinPluginId.ANDROID -> { + sourceSet.isLegacyAndroid.set(true) + sourceSet.isKotlinProxyLegacyAndroid.set(false) + body(sourceSet) + } + + KotlinPluginId.JVM -> { + sourceSet.isLegacyAndroid.set(false) + sourceSet.isKotlinProxyLegacyAndroid.set(false) + body(sourceSet) + } + + KotlinPluginId.MULTIPLATFORM -> { + if (plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) || !hasAndroid || !anyKotlin) { + sourceSet.isLegacyAndroid.set(false) + sourceSet.isKotlinProxyLegacyAndroid.set(false) + body(sourceSet) + } + // no else here, it is handled by configureMultiplatformAndroidSourceSets + } + + null -> { + sourceSet.isLegacyAndroid.set(false) + sourceSet.isKotlinProxyLegacyAndroid.set(false) + body(sourceSet) + } + } +} + +private fun Project.configureMultiplatformAndroidSourceSets(body: (DefaultProtoSourceSet) -> Unit) { + if (!hasAndroid || plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) || kotlinPluginId != KotlinPluginId.MULTIPLATFORM) { + return + } + + project.the() + .targets.all { + val target = this + + compilations.all { + val compilationSourceSets = allKotlinSourceSets as ObservableSet + + compilationSourceSets.forAll { + val protoSourceSet = protoSourceSets.getByName(it.name) + as? DefaultProtoSourceSet ?: return@forAll + + val isAndroid = target is KotlinAndroidTarget + protoSourceSet.isLegacyAndroid.set(isAndroid) + protoSourceSet.isKotlinProxyLegacyAndroid.set(isAndroid) + body(protoSourceSet) + } + } + } +} + +private fun AndroidComponents.configureLegacyAndroidVariants( + project: Project, + isKmp: Boolean, + configureTasks: (DefaultProtoSourceSet) -> Unit, +) { + val rootSourceSets = LegacyAndroidRootSourceSets.entries + .mapNotNull { rootName -> + val sourceSet = project.protoSourceSets.findByName(rootName.stringValue) + as? DefaultProtoSourceSet + + sourceSet?.let { rootName to it } + }.toMap() + + val mainRoot = rootSourceSets.getValue(LegacyAndroidRootSourceSets.Main) + val testRoot = rootSourceSets[LegacyAndroidRootSourceSets.Test] + val androidTestRoot = rootSourceSets[LegacyAndroidRootSourceSets.AndroidTest] + val testFixturesRoot = rootSourceSets[LegacyAndroidRootSourceSets.TestFixtures] + + testRoot?.extendsFrom(testFixturesRoot ?: mainRoot) + androidTestRoot?.extendsFrom(testFixturesRoot ?: mainRoot) + testFixturesRoot?.importsFrom(mainRoot) + + val extension = project.the() + onVariants { variant: Variant -> + val testBuildType = extension.testBuildType + rootSourceSets.forEach { (rootName) -> + // testFixtures don't have variants + if (rootName == LegacyAndroidRootSourceSets.TestFixtures) { + return@forEach + } + + if (rootName == LegacyAndroidRootSourceSets.AndroidTest && variant.buildType != testBuildType) { + return@forEach + } + + // but testFixtures still have source sets based on flavors + val testFixtureSetNames = if (rootName != LegacyAndroidRootSourceSets.Main) { + variant.dependencySourceSets(LegacyAndroidRootSourceSets.TestFixtures) + } else emptyList() + + val dependencySourceSetNames = variant.dependencySourceSets(rootName) + val variantSourceSetName = dependencySourceSetNames.lastOrNull() + ?: throw GradleException("No source sets found for variant ${variant.name}") + + val variantProtoSourceSet = project.protoSourceSets.named(variantSourceSetName) + + variantProtoSourceSet.configure { + val default = this as? DefaultProtoSourceSet ?: return@configure + + val properties = BufExecTask.AndroidProperties( + isTest = rootName != LegacyAndroidRootSourceSets.Main, + isInstrumentedTest = rootName == LegacyAndroidRootSourceSets.AndroidTest, + isUnitTest = rootName == LegacyAndroidRootSourceSets.Test, + sourceSetName = variantSourceSetName, + buildType = variant.buildType, + flavors = variant.productFlavors.map { (_, flavor) -> flavor }, + variant = variant.name, + ) + default.androidProperties.set(properties) + + (dependencySourceSetNames + testFixtureSetNames).forEach { dependencyName -> + val dependencyProtoSourceSet = project.protoSourceSets.findByName(dependencyName) + ?: return@forEach + + val proxyName = dependencyName.kotlinProxyFromAndroidOriginSourceSetName(rootName) + // can be also null, if not yet configured on the KGP side + val proxyDependency = if (isKmp && proxyName != null) { + project.protoSourceSets.findByName(proxyName) as? DefaultProtoSourceSet + } else { + null + } + + if (proxyDependency != null) { + default.extendsFrom(proxyDependency) + } + + if (name != dependencyName) { + default.extendsFrom(dependencyProtoSourceSet) + } + } + + if (rootName != LegacyAndroidRootSourceSets.Main) { + val mainVariantProtoSourceSet = project.protoSourceSets.findByName(variant.name) + as? DefaultProtoSourceSet + + if (mainVariantProtoSourceSet != null) { + default.androidDependencies.add(mainVariantProtoSourceSet) + + default.importsFrom(mainVariantProtoSourceSet) + } + } + + if (isKmp) { + if (rootName == LegacyAndroidRootSourceSets.Main) { + val commonMain = project.protoSourceSets + .findByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) + as? DefaultProtoSourceSet + + if (commonMain != null) { + default.androidDependencies.add(commonMain) + } + } else { + val commonTest = project.protoSourceSets + .findByName(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) + as? DefaultProtoSourceSet + + if (commonTest != null) { + default.androidDependencies.add(commonTest) + } + } + } + + configureTasks(default) + } + } + } +} + /** * Return a list of [DefaultProtoSourceSet] that have tasks that [this] will depend on. * It's a different list from [DefaultProtoSourceSet.imports], * as not all [DefaultProtoSourceSet] have associated tasks (on Android, for example) */ internal fun DefaultProtoSourceSet.getDependsOnTasksOf(protoSourceSets: ProtoSourceSets): List { - // todo check KMP - // androidMain/androidTest for KMP are evaluated as usual (they don't have androidRoot prop) - // androidRoot.isPresent == this is not androidMain/androidTest - if (isAndroid && androidRoot.isPresent) { - return listOfNotNull(androidRoot.get(), androidMain.orNull) + // isLegacyAndroid -> this is not KMP android library source set -> + // -> this is a source set from configureLegacyAndroidVariants -> only two possible dependencies: + // - if pure android - main variant (if this is a test variant: debug for testDebug, release for testRelease, etc.) + // - if KMP - also commonMain or commonTest correspondingly + // (and if commonTest - it will already have a 'dependsOn' commonMain) + // + // so in both cases we store it in androidDependencies + if (isLegacyAndroid.get()) { + return androidDependencies.get().toList() } val sourceSets = languageSourceSets.get() diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt index ed630d9ce..4cbfc7939 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt @@ -32,6 +32,8 @@ internal fun Project.registerProcessProtoFilesTask( val capitalName = name.replaceFirstChar { it.uppercase() } return tasks.register("process${capitalName}ProtoFiles") { + duplicatesStrategy = DuplicatesStrategy.FAIL + from(files(protoFilesDirectorySet.sourceDirectories)) { include(protoFilesDirectorySet.includes) exclude(protoFilesDirectorySet.excludes) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt index b019fa9d8..324e877b3 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt @@ -6,8 +6,8 @@ package kotlinx.rpc.protoc.android import com.android.build.api.variant.Variant -internal enum class AndroidRootSourceSets(val stringValue: String) { - // todo KMP? +// root source sets for 'com.android.(application|library|test|dynamic-feature)' +internal enum class LegacyAndroidRootSourceSets(val stringValue: String) { Main("main"), Test("test"), AndroidTest("androidTest"), @@ -17,7 +17,25 @@ internal enum class AndroidRootSourceSets(val stringValue: String) { override fun toString(): String = stringValue } -internal fun Variant.sourceSets(rootName: AndroidRootSourceSets) = buildList { +// leaf (not root as above) source sets for 'com.android.kotlin.multiplatform.library' +internal enum class KmpLibraryAndroidLeafSourceSets(val stringValue: String) { + Main("androidMain"), + HostTest("androidHostTest"), + DeviceTest("androidDeviceTest"), + ; + + override fun toString(): String = stringValue +} + +// returns a list of source set names this variant consists +// for armFreeappDebugTest (where arm is a flavor): +// - test +// - testArm +// - testFreeapp +// - testArmFreeapp +// - testDebug +// - testArmFreeappDebug +internal fun Variant.dependencySourceSets(rootName: LegacyAndroidRootSourceSets) = buildList { fun String.prefixed() = prefixed(rootName) add(rootName.stringValue) @@ -38,8 +56,58 @@ internal fun Variant.sourceSets(rootName: AndroidRootSourceSets) = buildList { add(name.prefixed()) } -internal fun String.prefixed(rootName: AndroidRootSourceSets) = if (rootName == AndroidRootSourceSets.Main) { - this -} else { - "${rootName.stringValue}${this.replaceFirstChar { it.uppercase() }}" +// debug -> androidDebug +// androidTest -> androidInstrumentedTest +// androidTestDebug -> androidInstrumentedTestDebug +// main -> androidMain +// release -> androidRelease +// test -> androidUnitTest +// testDebug -> androidUnitTestDebug +// testRelease -> androidUnitTestRelease +/** + * @see kotlinx.rpc.protoc.DefaultProtoSourceSet.isKotlinProxyLegacyAndroid + */ +internal fun String.kotlinProxyFromAndroidOriginSourceSetName(rootName: LegacyAndroidRootSourceSets): String? { + return when (rootName) { + LegacyAndroidRootSourceSets.Main -> { + "android${replaceFirstChar { it.uppercase() }}" + } + + LegacyAndroidRootSourceSets.Test -> { + "androidUnit${replaceFirstChar { it.uppercase() }}" + } + + LegacyAndroidRootSourceSets.AndroidTest -> { + "androidInstrumented${removePrefix("android").replaceFirstChar { it.uppercase() }}" + } + + LegacyAndroidRootSourceSets.TestFixtures -> null + } +} + +// androidDebug -> debug +// androidInstrumentedTest -> androidTest +// androidInstrumentedTestDebug -> androidTestDebug +// androidMain -> main +// androidRelease -> release +// androidUnitTest -> test +// androidUnitTestDebug -> testDebug +// androidUnitTestRelease -> testRelease +/** + * @see kotlinx.rpc.protoc.DefaultProtoSourceSet.isKotlinProxyLegacyAndroid + */ +internal fun String.androidOriginFromKotlinProxySourceSetName(): String? { + return when { + startsWith("androidUnit") -> removePrefix("androidUnit").replaceFirstChar { it.lowercase() } + startsWith("androidInstrumented") -> "android${removePrefix("androidInstrumented")}" + startsWith("android") -> removePrefix("android").replaceFirstChar { it.lowercase() } + else -> null + } } + +internal fun String.prefixed(rootName: LegacyAndroidRootSourceSets) = + if (rootName == LegacyAndroidRootSourceSets.Main) { + this + } else { + "${rootName.stringValue}${this.replaceFirstChar { it.uppercase() }}" + } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt index f959184ac..0ec2d79a9 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.util +import com.android.build.api.dsl.AndroidSourceSet import com.android.build.api.dsl.CommonExtension import com.android.build.api.variant.AndroidComponentsExtension import com.android.build.api.variant.ApplicationAndroidComponentsExtension @@ -11,7 +12,9 @@ import com.android.build.api.variant.DynamicFeatureAndroidComponentsExtension import com.android.build.api.variant.LibraryAndroidComponentsExtension import com.android.build.api.variant.TestAndroidComponentsExtension import com.android.build.api.variant.Variant +import com.android.build.gradle.BaseExtension import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.plugins.JavaPluginExtension @@ -19,53 +22,86 @@ import org.gradle.kotlin.dsl.the import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer -private const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" -private const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm" -private const val KOTLIN_ANDROID_PLUGIN_ID = "org.jetbrains.kotlin.android" +internal enum class KotlinPluginId(val id: String) { + JVM("org.jetbrains.kotlin.jvm"), + MULTIPLATFORM("org.jetbrains.kotlin.multiplatform"), + ANDROID("org.jetbrains.kotlin.android") +} + +internal const val ANDROID_APPLICATION = "com.android.application" +internal const val ANDROID_LIBRARY = "com.android.library" +internal const val ANDROID_DYNAMIC_FEATURE = "com.android.dynamic-feature" +internal const val ANDROID_TEST = "com.android.test" +internal const val ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY = "com.android.kotlin.multiplatform.library" -private const val ANDROID_APPLICATION = "com.android.application" -private const val ANDROID_LIBRARY = "com.android.library" -private const val ANDROID_DYNAMIC_FEATURE = "com.android.dynamic-feature" -private const val ANDROID_TEST = "com.android.test" +internal val Project.kotlinPluginId: KotlinPluginId? + get() = plugins.findPlugin(KotlinPluginId.JVM.id)?.let { KotlinPluginId.JVM } + ?: plugins.findPlugin(KotlinPluginId.MULTIPLATFORM.id)?.let { KotlinPluginId.MULTIPLATFORM } + ?: plugins.findPlugin(KotlinPluginId.ANDROID.id)?.let { KotlinPluginId.ANDROID } + +internal fun Project.withKotlin(action: (id: KotlinPluginId) -> Unit) { + plugins.withId(KotlinPluginId.JVM.id) { + action(KotlinPluginId.JVM) + } -internal fun Project.withKotlinSourceSets(action: (isAndroid: Boolean, KotlinSourceSetContainer) -> Unit) { - plugins.withId(KOTLIN_JVM_PLUGIN_ID) { - the().apply { - action(false, this) - } + plugins.withId(KotlinPluginId.MULTIPLATFORM.id) { + action(KotlinPluginId.MULTIPLATFORM) } - plugins.withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { - the().apply { - // todo huh? - action(false, this) - } + plugins.withId(KotlinPluginId.ANDROID.id) { + action(KotlinPluginId.ANDROID) } +} - plugins.withId(KOTLIN_ANDROID_PLUGIN_ID) { - the().apply { - action(true, this) - } +internal fun Project.withKotlinSourceSets(action: (id: KotlinPluginId, KotlinSourceSetContainer) -> Unit) { + withKotlin { id -> + action(id, the()) } } -internal fun Project.withLazyAndroidComponentsExtension( - action: Action, *, out Variant>>, -) { +internal val Project.hasAndroid: Boolean + get() = plugins.hasPlugin(ANDROID_LIBRARY) || + plugins.hasPlugin(ANDROID_APPLICATION) || + plugins.hasPlugin(ANDROID_DYNAMIC_FEATURE) || + plugins.hasPlugin(ANDROID_TEST) || + plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) + +internal fun Project.withAndroid(action: AndroidApplied.() -> Unit) { plugins.withId(ANDROID_LIBRARY) { - the().apply { action.execute(this) } + action(AndroidApplied(project, ANDROID_LIBRARY)) } plugins.withId(ANDROID_APPLICATION) { - the().apply { action.execute(this) } + action(AndroidApplied(project, ANDROID_APPLICATION)) } plugins.withId(ANDROID_DYNAMIC_FEATURE) { - the().apply { action.execute(this) } + action(AndroidApplied(project, ANDROID_DYNAMIC_FEATURE)) } plugins.withId(ANDROID_TEST) { - the().apply { action.execute(this) } + action(AndroidApplied(project, ANDROID_TEST)) + } + + plugins.withId(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) { + action(AndroidApplied(project, ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) + } +} + +internal class AndroidApplied(val project: Project, val id: String) + +internal fun AndroidApplied.withAndroidSourceSets(action: (NamedDomainObjectContainer) -> Unit) { + action(project.the().sourceSets) +} + +internal typealias AndroidComponents = AndroidComponentsExtension, *, out Variant> + +internal fun AndroidApplied.withLazyLegacyAndroidComponentsExtension(action: Action) { + when (id) { + ANDROID_LIBRARY -> action.execute(project.the()) + ANDROID_APPLICATION -> action.execute(project.the()) + ANDROID_DYNAMIC_FEATURE -> action.execute(project.the()) + ANDROID_TEST -> action.execute(project.the()) } } diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index a38edbd21..26f3621d0 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -5,12 +5,10 @@ package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest -import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInstance import kotlin.io.path.Path -import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GrpcKmpProjectTest : GrpcBaseTest() { diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts index 608a5a4e4..88cfba615 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts @@ -21,7 +21,7 @@ android { compileSdk = 34 } -public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -72,13 +72,13 @@ tasks.register("test_tasks") { ) assertTasks( - "testTasks", genTasks.unitTestTasks(), + "unit test tasks", genTasks.androidUnitTestTasks(), "bufGenerateTestDebug", "bufGenerateTestRelease", ) assertTasks( - "testTasks", genTasks.androidTestTasks(), + "instrumented test tasks", genTasks.androidInstrumentedTestTasks(), "bufGenerateAndroidTestDebug", ) @@ -133,36 +133,36 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingFlavor freeapp", genTasks.matchingFlavor("freeapp"), + "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), ) assertTasks( - "matchingBuildType debug", genTasks.matchingBuildType("debug"), + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType("debug"), "bufGenerateAndroidTestDebug", "bufGenerateTestDebug", "bufGenerateDebug", ) assertTasks( - "matchingBuildType release", genTasks.matchingBuildType("release"), + "matchingAndroidBuildType release", genTasks.matchingAndroidBuildType("release"), "bufGenerateTestRelease", "bufGenerateRelease", ) assertTasks( - "matchingVariant debug", genTasks.matchingVariant("debug"), + "matchingAndroidVariant debug", genTasks.matchingAndroidVariant("debug"), "bufGenerateAndroidTestDebug", "bufGenerateTestDebug", "bufGenerateDebug", ) assertTasks( - "matchingBuildType debug unit tests", genTasks.matchingBuildType("debug").unitTestTasks(), + "matchingAndroidBuildType debug unit tests", genTasks.matchingAndroidBuildType("debug").androidUnitTestTasks(), "bufGenerateTestDebug", ) assertTasks( - "matchingBuildType debug android tests", genTasks.matchingBuildType("debug").androidTestTasks(), + "matchingAndroidBuildType debug android tests", genTasks.matchingAndroidBuildType("debug").androidInstrumentedTestTasks(), "bufGenerateAndroidTestDebug", ) diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts index 28cea23f0..68de3f8d2 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts @@ -38,7 +38,7 @@ android { } } -public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -128,7 +128,7 @@ tasks.register("test_tasks") { ) assertTasks( - "testTasks", genTasks.unitTestTasks(), + "unit test tasks", genTasks.androidUnitTestTasks(), // test arm "bufGenerateTestArmFreeappDebug", "bufGenerateTestArmFreeappRelease", @@ -143,7 +143,7 @@ tasks.register("test_tasks") { ) assertTasks( - "testTasks", genTasks.androidTestTasks(), + "instrumented test tasks", genTasks.androidInstrumentedTestTasks(), // android test "bufGenerateAndroidTestArmFreeappDebug", "bufGenerateAndroidTestArmRetailappDebug", @@ -211,7 +211,7 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingFlavor freeapp", genTasks.matchingFlavor("freeapp"), + "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), // android test "bufGenerateAndroidTestArmFreeappDebug", @@ -235,7 +235,7 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingFlavor arm", genTasks.matchingFlavor("arm"), + "matchingAndroidFlavor arm", genTasks.matchingAndroidFlavor("arm"), // android test "bufGenerateAndroidTestArmFreeappDebug", @@ -255,7 +255,7 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingBuildType debug", genTasks.matchingBuildType("debug"), + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType("debug"), // android test "bufGenerateAndroidTestArmFreeappDebug", @@ -281,7 +281,7 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingBuildType relase", genTasks.matchingBuildType("release"), + "matchingAndroidBuildType relase", genTasks.matchingAndroidBuildType("release"), // test arm "bufGenerateTestArmFreeappRelease", @@ -301,7 +301,7 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingVariant armFreeappDebug", genTasks.matchingVariant("armFreeappDebug"), + "matchingAndroidVariant armFreeappDebug", genTasks.matchingAndroidVariant("armFreeappDebug"), // android test "bufGenerateAndroidTestArmFreeappDebug", @@ -314,12 +314,12 @@ tasks.register("test_tasks") { ) assertTasks( - "matchingVariant armFreeappDebug udet tests", genTasks.matchingVariant("armFreeappDebug").unitTestTasks(), + "matchingAndroidVariant armFreeappDebug udet tests", genTasks.matchingAndroidVariant("armFreeappDebug").androidUnitTestTasks(), "bufGenerateTestArmFreeappDebug", ) assertTasks( - "armFreeappDebug armFreeappDebug android tests", genTasks.matchingVariant("armFreeappDebug").androidTestTasks(), + "armFreeappDebug armFreeappDebug android tests", genTasks.matchingAndroidVariant("armFreeappDebug").androidInstrumentedTestTasks(), "bufGenerateAndroidTestArmFreeappDebug", ) diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts index fcf4fe51b..222c0a0a6 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts @@ -15,7 +15,7 @@ plugins { id("org.jetbrains.kotlinx.rpc.plugin") version "" } -public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts index aef77d75f..a43dd9950 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts @@ -7,6 +7,7 @@ import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version import kotlinx.rpc.buf.tasks.* import kotlinx.rpc.buf.* +import org.gradle.api.provider.Provider import javax.inject.Inject plugins { @@ -23,7 +24,7 @@ kotlin { } -public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) From 40352ec550703fb4acc3b1fc48d022a1108c6dbb Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Mon, 8 Dec 2025 13:37:28 +0100 Subject: [PATCH 05/11] Changed `BufTasks` into `ProtoTasks` --- gradle-plugin/build.gradle.kts | 2 + .../kotlin/kotlinx/rpc/buf/BufExtensions.kt | 27 +- .../kotlinx/rpc/buf/tasks/BufExecTask.kt | 84 +-- .../kotlinx/rpc/buf/tasks/BufGenerateTask.kt | 8 +- .../kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt | 656 ------------------ .../rpc/buf/tasks/GenerateBufGenYaml.kt | 21 +- .../kotlinx/rpc/buf/tasks/GenerateBufYaml.kt | 21 +- ...gureLocalProtocGenDevelopmentDependency.kt | 5 +- .../rpc/protoc/DefaultProtoSourceSet.kt | 20 +- .../rpc/protoc/DefaultProtocExtension.kt | 91 +-- .../kotlinx/rpc/protoc/ProcessProtoFiles.kt | 20 +- .../kotlinx/rpc/protoc/ProtoSourceSet.kt | 2 - .../kotlin/kotlinx/rpc/protoc/ProtoTask.kt | 90 +++ .../kotlin/kotlinx/rpc/protoc/ProtoTasks.kt | 326 +++++++++ .../kotlinx/rpc/protoc/android/variants.kt | 4 +- .../buf_tasks_default/build.gradle.kts | 30 +- .../buf_tasks_extended/build.gradle.kts | 30 +- .../buf_tasks/build.gradle.kts | 13 +- .../buf_tasks/build.gradle.kts | 122 +--- protobuf/protobuf-core/build.gradle.kts | 16 +- tests/protobuf-conformance/build.gradle.kts | 9 +- 21 files changed, 559 insertions(+), 1038 deletions(-) delete mode 100644 gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt create mode 100644 gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt create mode 100644 gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 2058f0d19..8aff8ad90 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -19,6 +19,8 @@ kotlin { explicitApi() jvmToolchain(17) + + compilerOptions.optIn.add("kotlinx.rpc.internal.InternalRpcApi") } tasks.withType().configureEach { diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt index 54f4846c5..774d8ce56 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt @@ -4,13 +4,10 @@ package kotlinx.rpc.buf -import kotlinx.rpc.buf.tasks.BufAllTasks -import kotlinx.rpc.buf.tasks.BufAllTasksImpl import kotlinx.rpc.buf.tasks.BufExecTask -import kotlinx.rpc.buf.tasks.BufGenerateTask -import kotlinx.rpc.buf.tasks.BufTasks -import kotlinx.rpc.buf.tasks.BufTasksImpl +import kotlinx.rpc.protoc.ProtoTasks import kotlinx.rpc.protoc.ProtocPlugin +import kotlinx.rpc.protoc.protoTasks import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.model.ObjectFactory @@ -104,13 +101,6 @@ public open class BufExtension @Inject internal constructor(objects: ObjectFacto * Allows registering custom Buf tasks that can operate on the generated workspace. */ public open class BufTasksExtension @Inject internal constructor(internal val project: Project) { - /** - * Returns a collection of all `buf` tasks registered in the project. - */ - public fun all(): BufAllTasks { - return BufAllTasksImpl(project, project.tasks.withType(BufExecTask::class.java)) - } - /** * Registers a custom Buf task that operates on the generated workspace. * @@ -123,11 +113,11 @@ public open class BufTasksExtension @Inject internal constructor(internal val pr kClass: KClass, name: String, configure: Action = Action {}, - ): BufTasks { + ): ProtoTasks { @Suppress("UNCHECKED_CAST") customTasks.add(Definition(name, kClass, configure)) - return all().matchingType(kClass) + return project.protoTasks.matchingType(kClass) } /** @@ -141,7 +131,7 @@ public open class BufTasksExtension @Inject internal constructor(internal val pr public inline fun registerWorkspaceTask( name: String, configure: Action = Action {}, - ): BufTasks { + ): ProtoTasks { return registerWorkspaceTask(T::class, name, configure) } @@ -161,13 +151,6 @@ public open class BufTasksExtension @Inject internal constructor(internal val pr * @see [BUF_GEN_YAML] */ public open class BufGenerateExtension @Inject internal constructor(internal val project: Project) { - /** - * Returns a collection of all `buf generate` tasks registered in the project. - */ - public fun allTasks(): BufTasks { - return BufTasksImpl(project, project.tasks.withType(BufGenerateTask::class.java), BufGenerateTask::class) - } - /** * `--include-imports` option. * diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt index 514da0f31..911e4155e 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt @@ -7,9 +7,7 @@ package kotlinx.rpc.buf.tasks import kotlinx.rpc.buf.BUF_EXECUTABLE_CONFIGURATION import kotlinx.rpc.buf.BufExtension import kotlinx.rpc.buf.execBuf -import kotlinx.rpc.protoc.PROTO_GROUP import kotlinx.rpc.rpcExtension -import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property @@ -26,11 +24,11 @@ import kotlin.reflect.KClass import kotlinx.rpc.buf.BUF_GEN_YAML import kotlinx.rpc.buf.BUF_YAML import kotlinx.rpc.buf.BufTasksExtension +import kotlinx.rpc.protoc.DefaultProtoTask +import kotlinx.rpc.protoc.ProtoTask import org.gradle.api.logging.LogLevel -import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.SkipWhenEmpty import javax.inject.Inject @@ -38,13 +36,8 @@ import javax.inject.Inject * Abstract base class for `buf` tasks. */ public abstract class BufExecTask @Inject constructor( - @Internal - public val properties: Provider, -) : DefaultTask() { - init { - group = PROTO_GROUP - } - + properties: ProtoTask.Properties, +) : DefaultProtoTask(properties) { // unsued, but required for Gradle to properly recognise inputs @get:InputFiles @get:SkipWhenEmpty @@ -54,77 +47,12 @@ public abstract class BufExecTask @Inject constructor( @get:InputFiles internal abstract val importProtoFiles: ListProperty - // list of buf task dependencies of the same type - @get:Internal - internal abstract val bufTaskDependencies: SetProperty - @get:InputFile internal abstract val bufExecutable: Property @get:Input internal abstract val debug: Property - /** - * Properties of the buf task. - * - * Can be used with [BufTasks] to filter tasks. - */ - public open class Properties internal constructor( - /** - * Whether the task is for a test source set. - */ - public val isTest: Boolean, - /** - * Name of the [kotlinx.rpc.protoc.ProtoSourceSet] this task is associated with. - */ - public val sourceSetName: String, - ) - - /** - * Properties of the buf task for android source sets. - * - * Can be used with [BufTasks] to filter tasks. - */ - public class AndroidProperties internal constructor( - isTest: Boolean, - sourceSetName: String, - - /** - * Name of the android flavors this task is associated with. - * - * Can be empty for 'com.android.kotlin.multiplatform.library' source sets. - * - * @see com.android.build.api.variant.Variant.productFlavors - */ - public val flavors: List, - - /** - * Name of the android build type this task is associated with. - * - * @see com.android.build.api.variant.Variant.buildType - */ - public val buildType: String?, - - /** - * Name of the android variant this task is associated with. - * - * Can be `null` for 'com.android.kotlin.multiplatform.library' source sets. - * - * @see com.android.build.api.variant.Variant.name - */ - public val variant: String?, - - /** - * Whether the task is for instrumentation tests. - */ - public val isInstrumentedTest: Boolean, - - /** - * Whether the task is for unit tests. - */ - public val isUnitTest: Boolean, - ) : Properties(isTest, sourceSetName) - /** * The `buf` command to execute. * @@ -185,7 +113,7 @@ public abstract class BufExecTask @Inject constructor( public inline fun Project.registerBufExecTask( name: String, workingDir: Provider, - properties: Provider, + properties: ProtoTask.Properties, noinline configuration: T.() -> Unit, ): TaskProvider = registerBufExecTask(T::class, name, workingDir, properties, configuration) @@ -194,7 +122,7 @@ internal fun Project.registerBufExecTask( clazz: KClass, name: String, workingDir: Provider, - properties: Provider, + properties: ProtoTask.Properties, configuration: T.() -> Unit = {}, ): TaskProvider = tasks.register(name, clazz, properties).apply { configure { diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt index f96b58dc8..e33150eec 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt @@ -17,7 +17,8 @@ import org.gradle.api.tasks.TaskProvider import java.io.File import kotlinx.rpc.buf.BufGenerateExtension import kotlinx.rpc.protoc.DefaultProtoSourceSet -import kotlinx.rpc.protoc.bufExecProperties +import kotlinx.rpc.protoc.ProtoTask +import kotlinx.rpc.protoc.protoTaskProperties import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider import org.gradle.api.tasks.InputFiles @@ -31,7 +32,7 @@ import javax.inject.Inject * @see buf generate */ public abstract class BufGenerateTask @Inject internal constructor( - properties: Provider, + properties: ProtoTask.Properties, ) : BufExecTask(properties) { // used to properly calculate output directories @get:Input @@ -141,13 +142,12 @@ internal fun Project.registerBufGenerateTask( workingDir: File, outputDirectory: File, includedPlugins: Provider>, + properties: ProtoTask.Properties, configure: BufGenerateTask.() -> Unit = {}, ): TaskProvider { val capitalName = protoSourceSet.name.replaceFirstChar { it.uppercase() } val bufGenerateTaskName = "${BufGenerateTask.NAME_PREFIX}$capitalName" - val properties = protoSourceSet.bufExecProperties() - return registerBufExecTask(bufGenerateTaskName, provider { workingDir }, properties) { group = PROTO_GROUP description = "Generates code from .proto files using 'buf generate'" diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt deleted file mode 100644 index 014008203..000000000 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.rpc.buf.tasks - -import org.gradle.api.NamedDomainObjectProvider -import org.gradle.api.Project -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.TaskCollection -import org.gradle.kotlin.dsl.withType -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import java.util.function.IntFunction -import kotlin.reflect.KClass - -/** - * Represents a collection of buf tasks of a given type. - * - * Allows for better filtering using additional method on top of Gradle's [TaskCollection]. - * - * Example: - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingSourceSet("main") - * buf.tasks.all().testTasks() - * buf.tasks.all() - * .testTasks() - * .matching { ... } - * .all { ... } - * } - * ``` - */ -public sealed interface BufTasks : TaskCollection { - /** - * Filters tasks by source set name. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingSourceSet("main") - * } - * ``` - */ - public fun matchingSourceSet(sourceSetName: String): BufTasks - - /** - * Filters tasks by a Kotlin source set. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingSourceSet(kotlin.sourceSets.getByName("main")) - * } - * ``` - */ - public fun matchingKotlinSourceSet(sourceSet: KotlinSourceSet): BufTasks - - /** - * Filters tasks by a Kotlin source set. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingSourceSet(kotlin.sourceSets.commonMain) - * } - * ``` - */ - public fun matchingKotlinSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks - - /** - * Filters tasks by a source set. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingSourceSet(sourceSets.getByName("main")) - * } - * ``` - */ - public fun matchingSourceSet(sourceSet: SourceSet): BufTasks - - /** - * Filters tasks by a source set. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingSourceSet(sourceSets.main) - * } - * ``` - */ - public fun matchingSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks - - /** - * Returns a collection of all buf tasks of the given type [BufTask] that will be executed for the given source set. - * - * This functionality uses [KotlinSourceSet.dependsOn] and inherits it's limitations: - * > Note that the Kotlin Gradle plugin may add additional required source sets - * on late stages of Gradle configuration - * and the most reliable way to get a full final set is to use this property - * as a task input with [org.gradle.api.provider.Provider] type. - * - * Unlike [KotlinSourceSet.dependsOn], this method also considers the `test -> main` dependency. - * - * Correct example: - * ```kotlin - * // use bufTaskNames in other tasks as in input, e.g. - * // - * // returns bufGenerateCommonMain, bufGenerateNativeMain, bufGenerateAppleMain - * // in default Kotlin source set hierarchy - * val bufTaskNames = project.provider { - * rpc.protoc.get() - * .buf.tasks.all() - * .executedForSourceSet("bufGenerateAppleMain") - * .map { it.name } - * } - * ``` - * - * Incorrect example that will print only `bufGenerateAppleMain`, - * because [KotlinSourceSet.dependsOn] won't yet be resolved: - * ```kotlin - * rpc.protoc { - * buf.tasks.all() - * .executedForSourceSet("bufGenerateAppleMain") - * .all { println(it.name) } - * } - * ``` - * - * Mind the difference with [bufDependsOn]: - * [executedForSourceSet] also includes the task for the given source set. - * - * For the correct example: - * - [bufDependsOn] returns `bufGenerateCommonMain` and `bufGenerateNativeMain` - * - [executedForSourceSet] returns `bufGenerateCommonMain`, `bufGenerateNativeMain` and `bufGenerateAppleMain` - */ - public fun executedForSourceSet(sourceSetName: String): BufTasks - - /** - * Returns a collection of all buf tasks of the given type [BufTask] that will be executed for the given source set. - * - * This functionality uses [KotlinSourceSet.dependsOn] and inherits it's limitations: - * > Note that the Kotlin Gradle plugin may add additional required source sets - * on late stages of Gradle configuration - * and the most reliable way to get a full final set is to use this property - * as a task input with [org.gradle.api.provider.Provider] type. - * - * Unlike [KotlinSourceSet.dependsOn], this method also considers the `test -> main` dependency. - * - * Correct example: - * ```kotlin - * // use bufTaskNames in other tasks as in input, e.g. - * // - * // returns bufGenerateCommonMain, bufGenerateNativeMain, bufGenerateAppleMain - * // in default Kotlin source set hierarchy - * val bufTaskNames = project.provider { - * rpc.protoc.get() - * .buf.tasks.all() - * .executedForSourceSet(kotlin.sourceSets.appleMain.get()) - * .map { it.name } - * } - * ``` - * - * Incorrect example that will print only `bufGenerateAppleMain`, - * because [KotlinSourceSet.dependsOn] won't yet be resolved: - * ```kotlin - * rpc.protoc { - * buf.tasks.all() - * .executedForSourceSet(kotlin.sourceSets.appleMain.get()) - * .all { println(it.name) } - * } - * ``` - * - * Mind the difference with [bufDependsOn]: - * [executedForKotlinSourceSet] also includes the task for the given source set. - * - * For the correct example: - * - [bufDependsOn] returns `bufGenerateCommonMain` and `bufGenerateNativeMain` - * - [executedForKotlinSourceSet] returns `bufGenerateCommonMain`, `bufGenerateNativeMain` and `bufGenerateAppleMain` - */ - public fun executedForKotlinSourceSet(sourceSet: KotlinSourceSet): BufTasks - - /** - * Returns a collection of all buf tasks of the given type [BufTask] that will be executed for the given source set. - * - * This functionality uses [KotlinSourceSet.dependsOn] and inherits it's limitations: - * > Note that the Kotlin Gradle plugin may add additional required source sets - * on late stages of Gradle configuration - * and the most reliable way to get a full final set is to use this property - * as a task input with [org.gradle.api.provider.Provider] type. - * - * Unlike [KotlinSourceSet.dependsOn], this method also considers the `test -> main` dependency. - * - * Correct example: - * ```kotlin - * // use bufTaskNames in other tasks as in input, e.g. - * // - * // returns bufGenerateCommonMain, bufGenerateNativeMain, bufGenerateAppleMain - * // in default Kotlin source set hierarchy - * val bufTaskNames = project.provider { - * rpc.protoc.get() - * .buf.tasks.all() - * .executedForSourceSet(kotlin.sourceSets.appleMain) - * .map { it.name } - * } - * ``` - * - * Incorrect example that will print only `bufGenerateAppleMain`, - * because [KotlinSourceSet.dependsOn] won't yet be resolved: - * ```kotlin - * rpc.protoc { - * buf.tasks.all() - * .executedForSourceSet(kotlin.sourceSets.appleMain) - * .all { println(it.name) } - * } - * ``` - * - * Mind the difference with [bufDependsOn]: - * [executedForKotlinSourceSet] also includes the task for the given source set. - * - * For the correct example: - * - [bufDependsOn] returns `bufGenerateCommonMain` and `bufGenerateNativeMain` - * - [executedForKotlinSourceSet] returns `bufGenerateCommonMain`, `bufGenerateNativeMain` and `bufGenerateAppleMain` - */ - public fun executedForKotlinSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks - - /** - * Returns a collection of all buf tasks of the given type [BufTask] that will be executed for the given source set. - * - * This functionality uses [KotlinSourceSet.dependsOn] and inherits it's limitations: - * > Note that the Kotlin Gradle plugin may add additional required source sets - * on late stages of Gradle configuration - * and the most reliable way to get a full final set is to use this property - * as a task input with [org.gradle.api.provider.Provider] type. - * - * Unlike [KotlinSourceSet.dependsOn], this method also considers the `test -> main` dependency. - * - * Correct example: - * ```kotlin - * // use bufTaskNames in other tasks as in input, e.g. - * // - * // returns bufGenerateMain, bufGenerateTest - * // in default Kotlin source set hierarchy - * val bufTaskNames = project.provider { - * rpc.protoc.get() - * .buf.tasks.all() - * .executedForSourceSet(sourceSets.test.get()) - * .map { it.name } - * } - * ``` - * - * Incorrect example. - * Although, the result will be correct in Kotlin/JVM projects (will print`bufGenerateMain`), - * this is a bad pattern because [KotlinSourceSet.dependsOn] won't yet be resolved: - * ```kotlin - * rpc.protoc { - * buf.tasks.all() - * .executedForSourceSet(sourceSets.test.get()) - * .all { println(it.name) } - * } - * ``` - * - * Mind the difference with [bufDependsOn]: - * [executedForSourceSet] also includes the task for the given source set. - * - * For the correct example: - * - [bufDependsOn] returns `bufGenerateMain` - * - [executedForSourceSet] returns `bufGenerateMain` and `bufGenerateTest` - */ - public fun executedForSourceSet(sourceSet: SourceSet): BufTasks - - /** - * Returns a collection of all buf tasks of the given type [BufTask] that will be executed for the given source set. - * - * This functionality uses [KotlinSourceSet.dependsOn] and inherits it's limitations: - * > Note that the Kotlin Gradle plugin may add additional required source sets - * on late stages of Gradle configuration - * and the most reliable way to get a full final set is to use this property - * as a task input with [org.gradle.api.provider.Provider] type. - * - * Unlike [KotlinSourceSet.dependsOn], this method also considers the `test -> main` dependency. - * - * Correct example: - * ```kotlin - * // use bufTaskNames in other tasks as in input, e.g. - * // - * // returns bufGenerateMain, bufGenerateTest - * // in default Kotlin source set hierarchy - * val bufTaskNames = project.provider { - * rpc.protoc.get() - * .buf.tasks.all() - * .executedForSourceSet(sourceSets.test) - * .map { it.name } - * } - * ``` - * - * Incorrect example. - * Although, the result will be correct in Kotlin/JVM projects (will print`bufGenerateMain`), - * this is a bad pattern because [KotlinSourceSet.dependsOn] won't yet be resolved: - * ```kotlin - * rpc.protoc { - * buf.tasks.all() - * .executedForSourceSet(sourceSets.test) - * .all { println(it.name) } - * } - * ``` - * - * Mind the difference with [bufDependsOn]: - * [executedForSourceSet] also includes the task for the given source set. - * - * For the correct example: - * - [bufDependsOn] returns `bufGenerateMain` - * - [executedForSourceSet] returns `bufGenerateMain` and `bufGenerateTest` - */ - public fun executedForSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks - - /** - * Filters tasks by where [BufExecTask.Properties.isTest] is `true`. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().testTasks() - * } - * ``` - */ - public fun testTasks(): BufTasks - - /** - * Filters tasks by where [BufExecTask.Properties.isTest] is `false`. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().nonTestTasks() - * } - * ``` - */ - public fun nonTestTasks(): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.isUnitTest] is `true`. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().androidUnitTestTasks() - * } - * ``` - */ - public fun androidUnitTestTasks(): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.isInstrumentedTest] is `true`. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().androidInstrumentedTestTasks() - * } - * ``` - */ - public fun androidInstrumentedTestTasks(): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.flavors] matches the given flavor. - * - * When `null` is passed, only variants without flavors are returned. - * - * Only returns Android tasks. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingAndroidFlavor("freeApp") - * } - * ``` - */ - public fun matchingAndroidFlavor(flavor: String?): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.buildType] matches the given buildType. - * - * Only returns Android tasks. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingAndroidBuildType("debug") - * } - * ``` - */ - public fun matchingAndroidBuildType(buildType: String?): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.variant] matches the given variant. - * - * Only returns Android tasks. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingAndroidVariant("freeAppDebug") - * } - * ``` - */ - public fun matchingAndroidVariant(variant: String?): BufTasks -} - -/** - * A version of [BufTasks] that contains all buf tasks and allows filtering by type. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingType() - * } - * ``` - */ -public sealed interface BufAllTasks : BufTasks { - /** - * Filters tasks by type. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingType(BufGenerateTask::class) - * } - * ``` - */ - public fun matchingType(kClass: KClass): BufTasks -} - -/** - * Filters tasks by type. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingType() - * } - * ``` - */ -public inline fun BufAllTasks.matchingType(): BufTasks { - return matchingType(BufTask::class) -} - -/** - * Returns a collection of all buf tasks of the given type [BufTask] that this task depends on. - * - * This functionality uses [KotlinSourceSet.dependsOn] and inherits it's limitations: - * > Note that the Kotlin Gradle plugin may add additional required source sets - * on late stages of Gradle configuration - * and the most reliable way to get a full final set is to use this property - * as a task input with [org.gradle.api.provider.Provider] type. - * - * Unlike [KotlinSourceSet.dependsOn], this method also considers the `test -> main` dependency. - * - * Correct example: - * ```kotlin - * // use bufTaskNames in other tasks as in input, e.g. - * // - * // returns bufGenerateCommonMain, bufGenerateNativeMain - * // in default Kotlin source set hierarchy - * val bufTaskNames = project.provider { - * rpc.protoc.get() - * .buf.tasks.all() - * .getByName("bufGenerateAppleMain") - * .bufDependsOn() - * .map { it.name } - * } - * ``` - * - * Incorrect example that will print nothing, because [KotlinSourceSet.dependsOn] won't yet be resolved: - * ```kotlin - * rpc.protoc { - * buf.tasks.all() - * .getByName("bufGenerateAppleMain") - * .bufDependsOn() - * .all { println(it.name) } - * } - * ``` - * - * Mind the difference with [BufTasks.executedForSourceSet]: - * [bufDependsOn] doesn't include the task for the given source set. - * - * For the correct example: - * - [bufDependsOn] returns `bufGenerateCommonMain` and `bufGenerateNativeMain` - * - [BufTasks.executedForSourceSet] returns `bufGenerateCommonMain`, `bufGenerateNativeMain` and `bufGenerateAppleMain` - */ -public inline fun BufTask.bufDependsOn(): BufTasks { - return bufDependsOn(BufTask::class) -} - -@PublishedApi -internal fun BufTask.bufDependsOn(kClass: KClass): BufTasks { - return BufTasksImpl( - project, - project.tasks.withType(kClass).matching { it.name in bufTaskDependencies.get() }, - kClass - ) -} - -internal open class BufTasksImpl internal constructor( - private val project: Project, - private val collection: TaskCollection, - private val kClass: KClass, -) : BufTasks, TaskCollection by collection { - override fun matchingSourceSet(sourceSetName: String): BufTasks { - return BufTasksImpl( - project, - collection.matching { it.properties.get().sourceSetName == sourceSetName }, - kClass - ) - } - - override fun matchingKotlinSourceSet(sourceSet: KotlinSourceSet): BufTasks { - return matchingSourceSet(sourceSet.name) - } - - override fun matchingKotlinSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks { - return matchingSourceSet(sourceSet.name) - } - - override fun matchingSourceSet(sourceSet: SourceSet): BufTasks { - return matchingSourceSet(sourceSet.name) - } - - override fun matchingSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks { - return matchingSourceSet(sourceSet.name) - } - - override fun executedForSourceSet(sourceSetName: String): BufTasks { - val allExecuted = project.tasks.withType(kClass.java).matching { - it.properties.get().sourceSetName == sourceSetName - }.singleOrNull()?.bufDependsOn(kClass) ?: return empty() - - val allExecutedLazySet = lazy { allExecuted.map { it.name }.toSet() } - - return BufTasksImpl( - project = project, - collection = collection.matching { dependency -> - dependency.properties.get().sourceSetName == sourceSetName || dependency.name in allExecutedLazySet.value - }, - kClass = kClass, - ) - } - - override fun executedForKotlinSourceSet(sourceSet: KotlinSourceSet): BufTasks { - return executedForSourceSet(sourceSet.name) - } - - override fun executedForKotlinSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks { - return executedForSourceSet(sourceSet.name) - } - - override fun executedForSourceSet(sourceSet: SourceSet): BufTasks { - return executedForSourceSet(sourceSet.name) - } - - override fun executedForSourceSet(sourceSet: NamedDomainObjectProvider): BufTasks { - return executedForSourceSet(sourceSet.name) - } - - override fun testTasks(): BufTasks { - return BufTasksImpl(project, collection.matching { it.properties.get().isTest }, kClass) - } - - override fun nonTestTasks(): BufTasks { - return BufTasksImpl(project, collection.matching { !it.properties.get().isTest }, kClass) - } - - override fun androidUnitTestTasks(): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - val properties = it.properties.get() as? BufExecTask.AndroidProperties - ?: return@matching false - - properties.isUnitTest - }, - kClass = kClass, - ) - } - - override fun androidInstrumentedTestTasks(): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - val properties = it.properties.get() as? BufExecTask.AndroidProperties - ?: return@matching false - - properties.isInstrumentedTest - }, - kClass = kClass, - ) - } - - override fun matchingAndroidFlavor(flavor: String?): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - val properties = it.properties.get() as? BufExecTask.AndroidProperties - ?: return@matching false - - if (flavor == null) { - return@matching properties.flavors.isEmpty() - } - - properties.flavors.contains(flavor) - }, - kClass = kClass, - ) - } - - override fun matchingAndroidBuildType(buildType: String?): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - val properties = it.properties.get() as? BufExecTask.AndroidProperties - ?: return@matching false - - properties.buildType == buildType - }, - kClass = kClass, - ) - } - - override fun matchingAndroidVariant(variant: String?): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - val properties = it.properties.get() as? BufExecTask.AndroidProperties - ?: return@matching false - - properties.variant == variant - }, - kClass = kClass, - ) - } - - // Java default method override - @Deprecated("Deprecated in Java") - final override fun toArray(generator: IntFunction?>): Array? { - @Suppress("DEPRECATION") - return super.toArray(generator) - } - - fun empty() = BufTasksImpl(project, matching { false }, kClass) -} - -internal open class BufAllTasksImpl internal constructor( - private val project: Project, - private val collection: BufTasksImpl, -) : BufAllTasks, BufTasks by collection { - constructor(project: Project, collection: TaskCollection) : this( - project, - BufTasksImpl(project, collection, BufExecTask::class) - ) - - override fun matchingType(kClass: KClass): BufTasks { - @Suppress("UNCHECKED_CAST") - return BufTasksImpl(project, collection.matching { kClass.isInstance(it) } as TaskCollection, kClass) - } - - // Java default method override - @Deprecated("Deprecated in Java") - final override fun toArray(generator: IntFunction?>): Array? { - @Suppress("DEPRECATION") - return super.toArray(generator) - } -} diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt index 812ab819c..885f87ba3 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt @@ -5,12 +5,11 @@ package kotlinx.rpc.buf.tasks import kotlinx.rpc.buf.BUF_GEN_YAML +import kotlinx.rpc.protoc.DefaultProtoTask import kotlinx.rpc.protoc.PROTO_FILES_DIR -import kotlinx.rpc.protoc.PROTO_GROUP +import kotlinx.rpc.protoc.ProtoTask import kotlinx.rpc.protoc.ProtocPlugin import kotlinx.rpc.util.ensureRegularFileExists -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property @@ -22,6 +21,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.register import java.io.File import java.io.Serializable +import javax.inject.Inject internal data class ResolvedGrpcPlugin( val type: Type, @@ -49,7 +49,9 @@ internal data class ResolvedGrpcPlugin( /** * Generates/updates Buf `buf.gen.yaml` file. */ -public abstract class GenerateBufGenYaml internal constructor(): DefaultTask() { +public abstract class GenerateBufGenYaml @Inject internal constructor( + properties: ProtoTask.Properties, +) : DefaultProtoTask(properties) { @get:Input internal abstract val plugins: ListProperty @@ -59,10 +61,6 @@ public abstract class GenerateBufGenYaml internal constructor(): DefaultTask() { @get:OutputFile public abstract val bufGenFile: Property - init { - group = PROTO_GROUP - } - @TaskAction @Suppress("detekt.CyclomaticComplexMethod", "detekt.NestedBlockDepth") internal fun generate() { @@ -138,10 +136,13 @@ internal fun Project.registerGenerateBufGenYamlTask( name: String, buildSourceSetsDir: File, protocPlugins: Provider>, + properties: ProtoTask.Properties, configure: GenerateBufGenYaml.() -> Unit = {}, ): TaskProvider { val capitalizeName = name.replaceFirstChar { it.uppercase() } - return project.tasks.register("${GenerateBufGenYaml.NAME_PREFIX}$capitalizeName") { + val task = project.tasks.register("${GenerateBufGenYaml.NAME_PREFIX}$capitalizeName", GenerateBufGenYaml::class, properties) + + task.configure { val pluginsProvider = project.provider { protocPlugins.get().map { plugin -> val artifact = plugin.artifact.get() @@ -178,4 +179,6 @@ internal fun Project.registerGenerateBufGenYamlTask( configure() } + + return task } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt index b84456b0e..019214cc9 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt @@ -5,10 +5,9 @@ package kotlinx.rpc.buf.tasks import kotlinx.rpc.buf.BUF_YAML -import kotlinx.rpc.protoc.DefaultProtoSourceSet -import kotlinx.rpc.protoc.PROTO_GROUP +import kotlinx.rpc.protoc.DefaultProtoTask +import kotlinx.rpc.protoc.ProtoTask import kotlinx.rpc.util.ensureRegularFileExists -import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -18,11 +17,14 @@ import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.register import java.io.File +import javax.inject.Inject /** * Generates/updates a Buf `buf.yaml` file. */ -public abstract class GenerateBufYaml internal constructor(): DefaultTask() { +public abstract class GenerateBufYaml @Inject internal constructor( + properties: ProtoTask.Properties, +) : DefaultProtoTask(properties) { @get:Input internal abstract val protoSourceDir: Property @@ -38,10 +40,6 @@ public abstract class GenerateBufYaml internal constructor(): DefaultTask() { @get:OutputFile public abstract val bufFile: Property - init { - group = PROTO_GROUP - } - @TaskAction internal fun generate() { val file = bufFile.get() @@ -91,10 +89,13 @@ internal fun Project.registerGenerateBufYamlTask( buildSourceSetsProtoDir: File, buildSourceSetsImportDir: File, withImport: Provider, + properties: ProtoTask.Properties, configure: GenerateBufYaml.() -> Unit = {}, ): TaskProvider { val capitalizeName = name.replaceFirstChar { it.uppercase() } - return tasks.register("${GenerateBufYaml.NAME_PREFIX}$capitalizeName") { + val task = tasks.register("${GenerateBufYaml.NAME_PREFIX}$capitalizeName", GenerateBufYaml::class, properties) + + task.configure { protoSourceDir.set(buildSourceSetsProtoDir.name) importSourceDir.set(buildSourceSetsImportDir.name) this.withImport.set(withImport) @@ -107,4 +108,6 @@ internal fun Project.registerGenerateBufYamlTask( configure() } + + return task } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt index 4078e7a89..d58847d48 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt @@ -4,8 +4,11 @@ package kotlinx.rpc.internal +import kotlinx.rpc.protoc.buf +import kotlinx.rpc.protoc.generate import kotlinx.rpc.protoc.grpcKotlinMultiplatform import kotlinx.rpc.protoc.kotlinMultiplatform +import kotlinx.rpc.protoc.protoTasks import kotlinx.rpc.rpcExtension import org.gradle.api.Project import org.gradle.internal.extensions.core.extra @@ -33,7 +36,7 @@ public fun Project.configureLocalProtocGenDevelopmentDependency( } } - buf.generate.allTasks() + protoTasks.buf.generate .matching { task -> sourceSetSuffix.any { task.name.endsWith(it) } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index 9f9b730f9..e037861a8 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -4,7 +4,6 @@ package kotlinx.rpc.protoc -import kotlinx.rpc.buf.tasks.BufExecTask import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.rpcExtension import kotlinx.rpc.util.KotlinPluginId @@ -154,8 +153,7 @@ internal open class DefaultProtoSourceSet( val androidDependencies: SetProperty = project.objects.setProperty() // only set for variant.name sourceSets - val androidProperties: Property = - project.objects.property() + val androidProperties: Property = project.objects.property() override val imports: SetProperty = project.objects.setProperty() override val fileImports: ConfigurableFileCollection = project.objects.fileCollection() @@ -283,15 +281,9 @@ internal fun Project.createProtoExtensions() { } } -internal fun DefaultProtoSourceSet.bufExecProperties(): Provider { - return project.provider { - if (androidProperties.isPresent) { - androidProperties.get() - } else { - BufExecTask.Properties( - isTest = name.lowercase().endsWith("test"), - sourceSetName = name, - ) - } - } +internal fun DefaultProtoSourceSet.protoTaskProperties(): ProtoTask.Properties { + return androidProperties.orNull ?: ProtoTask.Properties( + isTest = name.lowercase().endsWith("test"), + sourceSetName = name, + ) } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index eceb3bb3b..f202e3037 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -126,6 +126,10 @@ internal open class DefaultProtocExtension @Inject constructor( return@withFullyInitializedProtoSourceSet } + if (project.plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) { + project.tryConfigureKmpLibAndroidVariant(protoSourceSet) + } + configureTasks(protoSourceSet) } } @@ -133,7 +137,7 @@ internal open class DefaultProtocExtension @Inject constructor( project.withAndroid { if (project.plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) { - configureKmpLibAndroidVariants(project) + // done in tryConfigureKmpLibAndroidVariant return@withAndroid } @@ -187,11 +191,13 @@ internal open class DefaultProtocExtension @Inject constructor( } val protoFilesDirectorySet = protoSourceSet as SourceDirectorySet + val properties = protoSourceSet.protoTaskProperties() val processProtoTask = project.registerProcessProtoFilesTask( name = baseName, destination = buildSourceSetsProtoDir, protoFilesDirectorySet = protoFilesDirectorySet, + properties = properties, ) val processImportProtoTask = project.registerProcessProtoFilesImportsTask( @@ -199,6 +205,7 @@ internal open class DefaultProtocExtension @Inject constructor( destination = buildSourceSetsImportDir, importsProvider = protoSourceSet.imports, rawImports = protoSourceSet.fileImports, + properties = properties, ) { dependsOn(processProtoTask) } @@ -209,6 +216,7 @@ internal open class DefaultProtocExtension @Inject constructor( buildSourceSetsProtoDir = buildSourceSetsProtoDir, buildSourceSetsImportDir = buildSourceSetsImportDir, withImport = protoSourceSet.imports.map { it.isNotEmpty() }, + properties = properties, ) { dependsOn(processProtoTask) } @@ -217,6 +225,7 @@ internal open class DefaultProtocExtension @Inject constructor( name = baseName, buildSourceSetsDir = buildSourceSetsDir, protocPlugins = includedProtocPlugins, + properties = properties, ) { dependsOn(generateBufYamlTask) } @@ -228,6 +237,7 @@ internal open class DefaultProtocExtension @Inject constructor( workingDir = buildSourceSetsDir, outputDirectory = project.protoBuildDirGenerated.resolve(baseName), includedPlugins = includedProtocPlugins, + properties = properties, ) { executableFiles.addAll( includedProtocPlugins.map { list -> @@ -257,10 +267,6 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(dependencies) - bufTaskDependencies.set(protoSourceSet.imports.map { list -> - list.filterIsInstance().mapNotNull { it.generateTask.orNull?.name } - }) - onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } } @@ -280,6 +286,7 @@ internal open class DefaultProtocExtension @Inject constructor( processProtoTask = processProtoTask, processImportProtoTask = processImportProtoTask, sourceSetsProtoDirFileTree = sourceSetsProtoDirFileTree, + properties = properties, ) { protoFiles.set(processProtoTask.map { it.outputs.files }) importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) @@ -340,6 +347,7 @@ internal open class DefaultProtocExtension @Inject constructor( processProtoTask: TaskProvider, processImportProtoTask: TaskProvider, sourceSetsProtoDirFileTree: ConfigurableFileTree, + properties: ProtoTask.Properties, configure: BufExecTask.() -> Unit, ) { val baseName = protoSourceSet.name @@ -354,7 +362,7 @@ internal open class DefaultProtocExtension @Inject constructor( project.registerBufExecTask( clazz = kClass, workingDir = project.provider { buildSourceSetsDir }, - properties = protoSourceSet.bufExecProperties(), + properties = properties, name = taskName(baseName), ) { dependsOn(generateBufYamlTask) @@ -370,12 +378,6 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(dependencies) - bufTaskDependencies.set(protoSourceSet.imports.map { list -> - list.mapNotNull { dependency -> - project.tasks.findByName(taskName(dependency.name))?.name - } - }) - configure() onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } @@ -429,42 +431,43 @@ internal open class DefaultProtocExtension @Inject constructor( } } -// todo will return null always on findByName -private fun configureKmpLibAndroidVariants(project: Project) { - // not considered propper variants, they fit kmp source sets and configured as them, - // except for androidProperties - val sourceSets = KmpLibraryAndroidLeafSourceSets.entries - .mapNotNull { rootName -> - val sourceSet = project.protoSourceSets.findByName(rootName.stringValue) - as? DefaultProtoSourceSet +// not considered propper variants, they fit kmp source sets and configured as them, +// except for androidProperties +private fun Project.tryConfigureKmpLibAndroidVariant(protoSourceSet: DefaultProtoSourceSet) { + val sourceSetEntry = KmpLibraryAndroidLeafSourceSets.entries + .find { it.sourceSetName == protoSourceSet.name } + ?: return - sourceSet?.let { rootName to it } - }.toMap() + // no testFixtures, because they can have a dependency on commonTest in dependOn section + if (sourceSetEntry == KmpLibraryAndroidLeafSourceSets.Main) { + val unitTestSourceSet = project.protoSourceSets + .findByName(KmpLibraryAndroidLeafSourceSets.HostTest.sourceSetName) - val mainRoot = sourceSets.getValue(KmpLibraryAndroidLeafSourceSets.Main) - val unitTestRoot = sourceSets[KmpLibraryAndroidLeafSourceSets.HostTest] - val instrumentedTestRoot = sourceSets[KmpLibraryAndroidLeafSourceSets.DeviceTest] + val instrumentedTestSourceSet = project.protoSourceSets + .findByName(KmpLibraryAndroidLeafSourceSets.DeviceTest.sourceSetName) - // no testFixtures, because they can have a dependency on commonTest in dependOn section - unitTestRoot?.importsFrom(mainRoot) - instrumentedTestRoot?.importsFrom(mainRoot) - - sourceSets.forEach { (rootName) -> - val protoSourceSet = project.protoSourceSets.findByName(rootName.stringValue) - as? DefaultProtoSourceSet ?: return@forEach - - val properties = BufExecTask.AndroidProperties( - isTest = rootName != KmpLibraryAndroidLeafSourceSets.Main, - isInstrumentedTest = rootName == KmpLibraryAndroidLeafSourceSets.DeviceTest, - isUnitTest = rootName == KmpLibraryAndroidLeafSourceSets.HostTest, - sourceSetName = protoSourceSet.name, - buildType = null, - flavors = emptyList(), - variant = null, - ) + unitTestSourceSet?.importsFrom(protoSourceSet) + instrumentedTestSourceSet?.importsFrom(protoSourceSet) + } else { + val mainRoot = project.protoSourceSets + .findByName(KmpLibraryAndroidLeafSourceSets.Main.sourceSetName) - protoSourceSet.androidProperties.set(properties) + if (mainRoot != null) { + protoSourceSet.importsFrom(mainRoot) + } } + + val properties = ProtoTask.AndroidProperties( + isTest = sourceSetEntry != KmpLibraryAndroidLeafSourceSets.Main, + isInstrumentedTest = sourceSetEntry == KmpLibraryAndroidLeafSourceSets.DeviceTest, + isUnitTest = sourceSetEntry == KmpLibraryAndroidLeafSourceSets.HostTest, + sourceSetName = protoSourceSet.name, + buildType = null, + flavors = emptyList(), + variant = null, + ) + + protoSourceSet.androidProperties.set(properties) } // init isLegacyAndroid, isKotlinProxyLegacyAndroid @@ -594,7 +597,7 @@ private fun AndroidComponents.configureLegacyAndroidVariants( variantProtoSourceSet.configure { val default = this as? DefaultProtoSourceSet ?: return@configure - val properties = BufExecTask.AndroidProperties( + val properties = ProtoTask.AndroidProperties( isTest = rootName != LegacyAndroidRootSourceSets.Main, isInstrumentedTest = rootName == LegacyAndroidRootSourceSets.AndroidTest, isUnitTest = rootName == LegacyAndroidRootSourceSets.Test, diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt index 4cbfc7939..7d708d177 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt @@ -9,15 +9,20 @@ import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.register import java.io.File +import javax.inject.Inject /** * Copy proto files to a temporary directory for Buf to process. */ -public abstract class ProcessProtoFiles internal constructor(): Sync() { +public abstract class ProcessProtoFiles @Inject internal constructor( + @get:Internal + override val properties: ProtoTask.Properties, +) : Sync(), ProtoTask { init { group = PROTO_GROUP } @@ -27,11 +32,14 @@ internal fun Project.registerProcessProtoFilesTask( name: String, destination: File, protoFilesDirectorySet: SourceDirectorySet, + properties: ProtoTask.Properties, configure: ProcessProtoFiles.() -> Unit = {}, ): TaskProvider { val capitalName = name.replaceFirstChar { it.uppercase() } - return tasks.register("process${capitalName}ProtoFiles") { + val task = tasks.register("process${capitalName}ProtoFiles", ProcessProtoFiles::class, properties) + + task.configure { duplicatesStrategy = DuplicatesStrategy.FAIL from(files(protoFilesDirectorySet.sourceDirectories)) { @@ -43,6 +51,8 @@ internal fun Project.registerProcessProtoFilesTask( configure() } + + return task } internal fun Project.registerProcessProtoFilesImportsTask( @@ -50,11 +60,13 @@ internal fun Project.registerProcessProtoFilesImportsTask( destination: File, importsProvider: Provider>, rawImports: ConfigurableFileCollection, + properties: ProtoTask.Properties, configure: ProcessProtoFiles.() -> Unit = {}, ): TaskProvider { val capitalName = name.replaceFirstChar { it.uppercase() } - return tasks.register("process${capitalName}ProtoFilesImports") { + val task = tasks.register("process${capitalName}ProtoFilesImports", ProcessProtoFiles::class, properties) + task.configure { duplicatesStrategy = DuplicatesStrategy.FAIL val allImports = importsProvider.map { list -> @@ -73,4 +85,6 @@ internal fun Project.registerProcessProtoFilesImportsTask( configure() } + + return task } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt index 119d55222..1d50338b1 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt @@ -9,12 +9,10 @@ import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.SourceDirectorySet -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.SourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import java.io.File public typealias ProtoSourceSets = NamedDomainObjectContainer diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt new file mode 100644 index 000000000..9507ab03b --- /dev/null +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.rpc.protoc + +import org.gradle.api.DefaultTask +import org.gradle.api.Task +import org.gradle.api.tasks.Internal + +/** + * Abstract base interface for tasks that work with .proto files. + */ +public interface ProtoTask : Task { + @get:Internal + public val properties: Properties + + /** + * Properties of the buf task. + * + * Can be used with [ProtoTasks] to filter tasks. + */ + public open class Properties internal constructor( + /** + * Whether the task is for a test source set. + */ + public val isTest: Boolean, + /** + * Name of the [kotlinx.rpc.protoc.ProtoSourceSet] this task is associated with. + */ + public val sourceSetName: String, + ) + + /** + * Properties of the buf task for android source sets. + * + * Can be used with [ProtoTasks] to filter tasks. + */ + public class AndroidProperties internal constructor( + isTest: Boolean, + sourceSetName: String, + + /** + * Name of the android flavors this task is associated with. + * + * Can be empty for 'com.android.kotlin.multiplatform.library' source sets. + * + * @see com.android.build.api.variant.Variant.productFlavors + */ + public val flavors: List, + + /** + * Name of the android build type this task is associated with. + * + * @see com.android.build.api.variant.Variant.buildType + */ + public val buildType: String?, + + /** + * Name of the android variant this task is associated with. + * + * Can be `null` for 'com.android.kotlin.multiplatform.library' source sets. + * + * @see com.android.build.api.variant.Variant.name + */ + public val variant: String?, + + /** + * Whether the task is for instrumentation tests. + */ + public val isInstrumentedTest: Boolean, + + /** + * Whether the task is for unit tests. + */ + public val isUnitTest: Boolean, + ) : Properties(isTest, sourceSetName) +} + +/** + * Default implementation of [ProtoTask] with [Task.group] set to [PROTO_GROUP]. + */ +public abstract class DefaultProtoTask( + @get:Internal + final override val properties: ProtoTask.Properties, +) : ProtoTask, DefaultTask() { + init { + group = PROTO_GROUP + } +} diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt new file mode 100644 index 000000000..2bcec019f --- /dev/null +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt @@ -0,0 +1,326 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.rpc.protoc + +import kotlinx.rpc.buf.tasks.BufExecTask +import kotlinx.rpc.buf.tasks.BufGenerateTask +import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.Project +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.TaskCollection +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import java.util.function.IntFunction +import kotlin.reflect.KClass + +/** + * Returns a collection of all proto tasks registered in the project. + */ +public val Project.protoTasks: ProtoTasks get() = ProtoTasksImpl(this) + +/** + * Returns a collection of all `buf` tasks registered in the project. + */ +public val ProtoTasks.buf: ProtoTasks get() = matchingType(BufExecTask::class) + +/** + * Returns a collection of all `buf generate` tasks registered in the project. + */ +public val ProtoTasks.generate: ProtoTasks get() = matchingType(BufGenerateTask::class) + +/** + * Represents a collection of [ProtoTask] tasks of a given type. + * + * Allows for better filtering using additional method on top of Gradle's [TaskCollection]. + * + * Example: + * ```kotlin + * protoTasks.matchingSourceSet("main") + * protoTasks.testTasks() + * protoTasks. + * .testTasks() + * .matching { ... } + * .all { ... } + * ``` + */ +public sealed interface ProtoTasks : TaskCollection { + /** + * Filters tasks by type. + * + * ```kotlin + * protoTasks.matchingType(BufGenerateTask::class) + * ``` + */ + public fun matchingType(kClass: KClass): ProtoTasks + + /** + * Filters tasks by source set name. + * + * ```kotlin + * protoTasks.matchingSourceSet("main") + * ``` + */ + public fun matchingSourceSet(sourceSetName: String): ProtoTasks + + /** + * Filters tasks by a Kotlin source set. + * + * ```kotlin + * protoTasks.matchingKotlinSourceSet(kotlin.sourceSets.getByName("main")) + * ``` + */ + public fun matchingKotlinSourceSet(sourceSet: KotlinSourceSet): ProtoTasks + + /** + * Filters tasks by a Kotlin source set. + * + * ```kotlin + * protoTasks.matchingKotlinSourceSet(kotlin.sourceSets.commonMain) + * ``` + */ + public fun matchingKotlinSourceSet(sourceSet: NamedDomainObjectProvider): ProtoTasks + + /** + * Filters tasks by a source set. + * + * ```kotlin + * protoTasks.matchingSourceSet(sourceSets.getByName("main") + * ``` + */ + public fun matchingSourceSet(sourceSet: SourceSet): ProtoTasks + + /** + * Filters tasks by a source set. + * + * ```kotlin + * protoTasks.matchingSourceSet(sourceSets.main) + * ``` + */ + public fun matchingSourceSet(sourceSet: NamedDomainObjectProvider): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.Properties.isTest] is `true`. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().testTasks() + * } + * ``` + */ + public fun testTasks(): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.Properties.isTest] is `false`. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().nonTestTasks() + * } + * ``` + */ + public fun nonTestTasks(): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.isUnitTest] is `true`. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().androidUnitTestTasks() + * } + * ``` + */ + public fun androidUnitTestTasks(): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.isInstrumentedTest] is `true`. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().androidInstrumentedTestTasks() + * } + * ``` + */ + public fun androidInstrumentedTestTasks(): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.flavors] matches the given flavor. + * + * When `null` is passed, only variants without flavors are returned. + * + * Only returns Android tasks. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().matchingAndroidFlavor("freeApp") + * } + * ``` + */ + public fun matchingAndroidFlavor(flavor: String?): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.buildType] matches the given buildType. + * + * Only returns Android tasks. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().matchingAndroidBuildType("debug") + * } + * ``` + */ + public fun matchingAndroidBuildType(buildType: String?): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.variant] matches the given variant. + * + * Only returns Android tasks. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().matchingAndroidVariant("freeAppDebug") + * } + * ``` + */ + public fun matchingAndroidVariant(variant: String?): ProtoTasks +} + + +/** + * Filters tasks by type. + * + * ```kotlin + * rpc.protoc { + * buf.tasks.all().matchingType() + * } + * ``` + */ +public inline fun ProtoTasks<*>.matchingType(): ProtoTasks { + return matchingType(ProtoTaskT::class) +} + +internal inline fun ProtoTasksImpl(project: Project): ProtoTasksImpl { + return ProtoTasksImpl(project, project.tasks.withType(ProtoTaskT::class), ProtoTaskT::class) +} + +internal open class ProtoTasksImpl( + private val project: Project, + private val collection: TaskCollection, + private val kClass: KClass, +) : ProtoTasks, TaskCollection by collection { + override fun matchingType(kClass: KClass): ProtoTasks { + @Suppress("UNCHECKED_CAST") + return ProtoTasksImpl(project, collection.matching { kClass.isInstance(it) } as TaskCollection, kClass) + } + + override fun matchingSourceSet(sourceSetName: String): ProtoTasks { + return ProtoTasksImpl( + project, + collection.matching { it.properties.sourceSetName == sourceSetName }, + kClass + ) + } + + override fun matchingKotlinSourceSet(sourceSet: KotlinSourceSet): ProtoTasks { + return matchingSourceSet(sourceSet.name) + } + + override fun matchingKotlinSourceSet(sourceSet: NamedDomainObjectProvider): ProtoTasks { + return matchingSourceSet(sourceSet.name) + } + + override fun matchingSourceSet(sourceSet: SourceSet): ProtoTasks { + return matchingSourceSet(sourceSet.name) + } + + override fun matchingSourceSet(sourceSet: NamedDomainObjectProvider): ProtoTasks { + return matchingSourceSet(sourceSet.name) + } + + override fun testTasks(): ProtoTasks { + return ProtoTasksImpl(project, collection.matching { it.properties.isTest }, kClass) + } + + override fun nonTestTasks(): ProtoTasks { + return ProtoTasksImpl(project, collection.matching { !it.properties.isTest }, kClass) + } + + override fun androidUnitTestTasks(): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? ProtoTask.AndroidProperties + ?: return@matching false + + properties.isUnitTest + }, + kClass = kClass, + ) + } + + override fun androidInstrumentedTestTasks(): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? ProtoTask.AndroidProperties + ?: return@matching false + + properties.isInstrumentedTest + }, + kClass = kClass, + ) + } + + override fun matchingAndroidFlavor(flavor: String?): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? ProtoTask.AndroidProperties + ?: return@matching false + + if (flavor == null) { + return@matching properties.flavors.isEmpty() + } + + properties.flavors.contains(flavor) + }, + kClass = kClass, + ) + } + + override fun matchingAndroidBuildType(buildType: String?): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? ProtoTask.AndroidProperties + ?: return@matching false + + properties.buildType == buildType + }, + kClass = kClass, + ) + } + + override fun matchingAndroidVariant(variant: String?): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matching { + val properties = it.properties as? ProtoTask.AndroidProperties + ?: return@matching false + + properties.variant == variant + }, + kClass = kClass, + ) + } + + fun empty() = ProtoTasksImpl(project, matching { false }, kClass) + + // Java default method override + @Deprecated("Deprecated in Java") + final override fun toArray(generator: IntFunction?>): Array? { + @Suppress("DEPRECATION") + return super.toArray(generator) + } +} diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt index 324e877b3..f1a56aef0 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt @@ -18,13 +18,13 @@ internal enum class LegacyAndroidRootSourceSets(val stringValue: String) { } // leaf (not root as above) source sets for 'com.android.kotlin.multiplatform.library' -internal enum class KmpLibraryAndroidLeafSourceSets(val stringValue: String) { +internal enum class KmpLibraryAndroidLeafSourceSets(val sourceSetName: String) { Main("androidMain"), HostTest("androidHostTest"), DeviceTest("androidDeviceTest"), ; - override fun toString(): String = stringValue + override fun toString(): String = sourceSetName } // returns a list of source set names this variant consists diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts index 88cfba615..6d18202ac 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts @@ -7,7 +7,7 @@ import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version import kotlinx.rpc.buf.tasks.* import kotlinx.rpc.buf.* -import org.gradle.api.provider.Provider +import kotlinx.rpc.protoc.* import javax.inject.Inject plugins { @@ -21,7 +21,7 @@ android { compileSdk = 34 } -public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -53,7 +53,7 @@ fun assertTasks( tasks.register("test_tasks") { doLast { - val genTasks = rpc.protoc.get().buf.generate.allTasks() + val genTasks = protoTasks.buf.generate assertTasks( "gen all", genTasks, @@ -91,8 +91,6 @@ tasks.register("test_tasks") { assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) - assertTasks("executedForSourceSet main", genTasks.executedForSourceSet("main")) - assertTasks("executedForSourceSet test", genTasks.executedForSourceSet("test")) assertTasks( "matchingSourceSet debug", @@ -112,26 +110,6 @@ tasks.register("test_tasks") { "bufGenerateAndroidTestDebug", ) - assertTasks( - "executedForSourceSet debug", - genTasks.executedForSourceSet("debug"), - "bufGenerateDebug", - ) - - assertTasks( - "executedForSourceSet testDebug", - genTasks.executedForSourceSet("testDebug"), - "bufGenerateDebug", - "bufGenerateTestDebug", - ) - - assertTasks( - "executedForSourceSet androidTestDebug", - genTasks.executedForSourceSet("androidTestDebug"), - "bufGenerateDebug", - "bufGenerateAndroidTestDebug", - ) - assertTasks( "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), ) @@ -166,7 +144,7 @@ tasks.register("test_tasks") { "bufGenerateAndroidTestDebug", ) - val allTasks = rpc.protoc.get().buf.tasks.all() + val allTasks = protoTasks.buf assertTasks( "all", allTasks, diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts index 68de3f8d2..a7cdd0eb0 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts @@ -7,7 +7,7 @@ import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version import kotlinx.rpc.buf.tasks.* import kotlinx.rpc.buf.* -import org.gradle.api.provider.Provider +import kotlinx.rpc.protoc.* import javax.inject.Inject plugins { @@ -38,7 +38,7 @@ android { } } -public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -70,7 +70,7 @@ fun assertTasks( tasks.register("test_tasks") { doLast { - val genTasks = rpc.protoc.get().buf.generate.allTasks() + val genTasks = protoTasks.buf.generate assertTasks( "gen all", genTasks, @@ -169,8 +169,6 @@ tasks.register("test_tasks") { assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) - assertTasks("executedForSourceSet main", genTasks.executedForSourceSet("main")) - assertTasks("executedForSourceSet test", genTasks.executedForSourceSet("test")) assertTasks( "matchingSourceSet armFreeappDebug", @@ -190,26 +188,6 @@ tasks.register("test_tasks") { "bufGenerateAndroidTestArmFreeappDebug", ) - assertTasks( - "executedForSourceSet armFreeappDebug", - genTasks.executedForSourceSet("armFreeappDebug"), - "bufGenerateArmFreeappDebug", - ) - - assertTasks( - "executedForSourceSet testArmFreeappDebug", - genTasks.executedForSourceSet("testArmFreeappDebug"), - "bufGenerateArmFreeappDebug", - "bufGenerateTestArmFreeappDebug", - ) - - assertTasks( - "executedForSourceSet androidTestArmFreeappDebug", - genTasks.executedForSourceSet("androidTestArmFreeappDebug"), - "bufGenerateArmFreeappDebug", - "bufGenerateAndroidTestArmFreeappDebug", - ) - assertTasks( "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), @@ -323,7 +301,7 @@ tasks.register("test_tasks") { "bufGenerateAndroidTestArmFreeappDebug", ) - val allTasks = rpc.protoc.get().buf.tasks.all() + val allTasks = protoTasks.buf assertTasks( "all", allTasks, diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts index 222c0a0a6..b502de1b8 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts @@ -7,7 +7,7 @@ import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version import kotlinx.rpc.buf.tasks.* import kotlinx.rpc.buf.* -import org.gradle.api.provider.Provider +import kotlinx.rpc.protoc.* import javax.inject.Inject plugins { @@ -15,7 +15,7 @@ plugins { id("org.jetbrains.kotlinx.rpc.plugin") version "" } -public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -47,7 +47,7 @@ fun assertTasks( tasks.register("test_tasks") { doLast { - val genTasks = rpc.protoc.get().buf.generate.allTasks() + val genTasks = protoTasks.buf.generate assertTasks("gen all", genTasks, "bufGenerateMain", "bufGenerateTest") assertTasks("testTasks", genTasks.testTasks(), "bufGenerateTest") @@ -55,13 +55,8 @@ tasks.register("test_tasks") { assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main"), "bufGenerateMain") assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test"), "bufGenerateTest") - assertTasks("executedForSourceSet main", genTasks.executedForSourceSet("main"), "bufGenerateMain") - assertTasks("executedForSourceSet test", genTasks.executedForSourceSet("test"), "bufGenerateMain", "bufGenerateTest") - assertTasks("buf depends on main", genTasks.matchingSourceSet("main").single().bufDependsOn()) - assertTasks("buf depends on test", genTasks.matchingSourceSet("test").single().bufDependsOn(), "bufGenerateMain") - - val allTasks = rpc.protoc.get().buf.tasks.all() + val allTasks = protoTasks.buf assertTasks("all", allTasks, "bufGenerateMain", "bufGenerateTest", "bufLintMain", "bufLintTest") assertTasks("all by type generate", allTasks.matchingType(), "bufGenerateMain", "bufGenerateTest") diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts index a43dd9950..f94ba4326 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks/build.gradle.kts @@ -7,7 +7,7 @@ import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version import kotlinx.rpc.buf.tasks.* import kotlinx.rpc.buf.* -import org.gradle.api.provider.Provider +import kotlinx.rpc.protoc.* import javax.inject.Inject plugins { @@ -23,8 +23,7 @@ kotlin { macosArm64() } - -public abstract class BufLintTask @Inject constructor(properties: Provider) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -74,7 +73,7 @@ fun assertTasks( tasks.register("test_tasks") { doLast { - val genTasks = rpc.protoc.get().buf.generate.allTasks() + val genTasks = protoTasks.buf.generate assertTasks( "gen all", genTasks, @@ -117,112 +116,7 @@ tasks.register("test_tasks") { assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) - assertTasks( - "executedForSourceSet commonMain", - genTasks.executedForSourceSet("commonMain"), - "bufGenerateCommonMain", - ) - - assertTasks( - "executedForSourceSet jvmMain", - genTasks.executedForSourceSet("jvmMain"), - "bufGenerateCommonMain", - "bufGenerateJvmMain", - ) - - assertTasks( - "executedForSourceSet macosArm64Main", - genTasks.executedForSourceSet("macosArm64Main"), - "bufGenerateCommonMain", - "bufGenerateNativeMain", - "bufGenerateAppleMain", - "bufGenerateMacosMain", - "bufGenerateMacosArm64Main", - ) - - assertTasks( - "executedForSourceSet commonTest", - genTasks.executedForSourceSet("commonTest"), - "bufGenerateCommonMain", "bufGenerateCommonTest", - ) - - assertTasks( - "executedForSourceSet jvmTest", - genTasks.executedForSourceSet("jvmTest"), - "bufGenerateCommonMain", "bufGenerateCommonTest", - "bufGenerateJvmMain", "bufGenerateJvmTest", - ) - - assertTasks( - "executedForSourceSet macosArm64Test", - genTasks.executedForSourceSet("macosArm64Test"), - "bufGenerateCommonMain", "bufGenerateCommonTest", - "bufGenerateNativeMain", "bufGenerateNativeTest", - "bufGenerateAppleMain", "bufGenerateAppleTest", - "bufGenerateMacosMain", "bufGenerateMacosTest", - "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", - ) - - assertTasks( - "executedForSourceSet macosArm64Test test tasks", - genTasks.executedForSourceSet("macosArm64Test").testTasks(), - "bufGenerateCommonTest", - "bufGenerateNativeTest", - "bufGenerateAppleTest", - "bufGenerateMacosTest", - "bufGenerateMacosArm64Test", - ) - - assertTasks( - "executedForSourceSet macosArm64Test non test tasks", - genTasks.executedForSourceSet("macosArm64Test").nonTestTasks(), - "bufGenerateCommonMain", - "bufGenerateNativeMain", - "bufGenerateAppleMain", - "bufGenerateMacosMain", - "bufGenerateMacosArm64Main", - ) - - assertTasks( - "executedForSourceSet macosArm64Test non test tasks matching macosMain", - genTasks.executedForSourceSet("macosArm64Test").nonTestTasks().matchingSourceSet("macosMain"), - "bufGenerateMacosMain", - ) - - assertTasks( - "executedForSourceSet macosArm64Test non test tasks matching jvmMain", - genTasks.executedForSourceSet("macosArm64Test").nonTestTasks().matchingSourceSet("jvmMain"), - ) - - assertTasks( - "executedForSourceSet macosArm64Test non test tasks executed for nativeMain", - genTasks.executedForSourceSet("macosArm64Test").nonTestTasks().executedForSourceSet("nativeMain"), - "bufGenerateCommonMain", - "bufGenerateNativeMain", - ) - - assertTasks("buf depends on commonMain", genTasks.matchingSourceSet("commonMain").single().bufDependsOn()) - assertTasks( - "buf depends on macosMain", genTasks.matchingSourceSet("macosMain").single().bufDependsOn(), - "bufGenerateCommonMain", - "bufGenerateNativeMain", - "bufGenerateAppleMain", - ) - assertTasks( - "buf depends on commonTest", - genTasks.matchingSourceSet("commonTest").single().bufDependsOn(), - "bufGenerateCommonMain", - ) - - assertTasks( - "buf depends on jsTest", - genTasks.matchingSourceSet("jsTest").single().bufDependsOn(), - "bufGenerateCommonMain", "bufGenerateCommonTest", - "bufGenerateWebMain".ifKotlinAtLeast("2.2.20"), "bufGenerateWebTest".ifKotlinAtLeast("2.2.20"), - "bufGenerateJsMain", - ) - - val allTasks = rpc.protoc.get().buf.tasks.all() + val allTasks = protoTasks.buf assertTasks( "all", allTasks, @@ -271,13 +165,5 @@ tasks.register("test_tasks") { "bufLintWebMain".ifKotlinAtLeast("2.2.20"), "bufLintWebTest".ifKotlinAtLeast("2.2.20"), "bufLintJsMain", "bufLintJsTest", ) - - assertTasks( - "all by type lint for macosMain", allTasks.matchingType().executedForSourceSet("macosMain"), - "bufLintCommonMain", - "bufLintNativeMain", - "bufLintAppleMain", - "bufLintMacosMain", - ) } } diff --git a/protobuf/protobuf-core/build.gradle.kts b/protobuf/protobuf-core/build.gradle.kts index 9118bdf97..d499701d2 100644 --- a/protobuf/protobuf-core/build.gradle.kts +++ b/protobuf/protobuf-core/build.gradle.kts @@ -6,7 +6,10 @@ import kotlinx.rpc.internal.InternalRpcApi import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency +import kotlinx.rpc.protoc.buf +import kotlinx.rpc.protoc.generate import kotlinx.rpc.protoc.proto +import kotlinx.rpc.protoc.protoTasks import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import util.configureCLibCInterop @@ -81,7 +84,7 @@ rpc { includeFileLevelComments = false } - buf.generate.allTasks().matchingKotlinSourceSet(kotlin.sourceSets.commonMain).configureEach { + protoTasks.buf.generate.matchingKotlinSourceSet(kotlin.sourceSets.commonMain).configureEach { includeWkt = true outputDirectory = generatedCodeDir(properties.sourceSetName) } @@ -89,14 +92,3 @@ rpc { } configureLocalProtocGenDevelopmentDependency("Main", "Test") - -// TODO: What is the correct way to declare this dependency? (KRPC-223) -// (without it fails when executing "publishAllPublicationsToBuildRepository") -val bufGenerateCommonMain: TaskProvider = tasks.named("bufGenerateCommonMain") - -tasks.withType().configureEach { - // Only for sources jars - if (archiveClassifier.orNull == "sources" || name.endsWith("SourcesJar")) { - dependsOn(bufGenerateCommonMain) - } -} diff --git a/tests/protobuf-conformance/build.gradle.kts b/tests/protobuf-conformance/build.gradle.kts index 53439d359..860a4b94d 100644 --- a/tests/protobuf-conformance/build.gradle.kts +++ b/tests/protobuf-conformance/build.gradle.kts @@ -6,6 +6,9 @@ import kotlinx.rpc.internal.InternalRpcApi import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency +import kotlinx.rpc.protoc.buf +import kotlinx.rpc.protoc.generate +import kotlinx.rpc.protoc.protoTasks import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode import util.other.localProperties import util.tasks.CONFORMANCE_PB @@ -47,7 +50,7 @@ rpc { includeFileLevelComments = false } - buf.generate.allTasks().nonTestTasks().configureEach { + protoTasks.buf.generate.nonTestTasks().configureEach { outputDirectory = generatedCodeDir(properties.sourceSetName) } } @@ -76,7 +79,7 @@ val generateConformanceTests = tasks.register("generateConformanceTest classpath = sourceSets.main.get().runtimeClasspath dependsOn(mockClientJar) - dependsOn(tasks.named("bufGenerateMain")) + dependsOn(protoTasks.buf.generate.matchingSourceSet("main")) args = listOf( mockClientJar.get().archiveFile.get().asFile.absolutePath @@ -96,7 +99,7 @@ tasks.register("runConformanceTest") { classpath = sourceSets.main.get().runtimeClasspath dependsOn(mockClientJar) - dependsOn(tasks.named("bufGenerateMain")) + dependsOn(protoTasks.buf.generate.matchingSourceSet("main")) dependsOn(generateConformanceFileDescriptorSet) args = listOfNotNull( From beb9b356a84c8952c5c9827f0f7223ac5ab11799 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Thu, 11 Dec 2025 18:13:30 +0100 Subject: [PATCH 06/11] Fixed full KMP + Android support --- gradle-plugin/build.gradle.kts | 5 - .../kotlinx/rpc/buf/tasks/BufGenerateTask.kt | 16 +- .../rpc/protoc/DefaultProtoSourceSet.kt | 30 +- .../rpc/protoc/DefaultProtocExtension.kt | 286 ++++++++++-------- .../kotlin/kotlinx/rpc/protoc/ProtoTask.kt | 27 +- .../kotlin/kotlinx/rpc/protoc/ProtoTasks.kt | 124 +++++--- .../kotlinx/rpc/protoc/android/variants.kt | 34 +-- .../src/main/kotlin/kotlinx/rpc/util/kgp.kt | 34 +-- protobuf/protobuf-core/build.gradle.kts | 10 +- tests/protobuf-conformance/build.gradle.kts | 8 +- 10 files changed, 302 insertions(+), 272 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 8aff8ad90..99715591e 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -47,11 +47,6 @@ dependencies { } tasks.test { - val forwardOutput: Boolean = (properties.getOrDefault("gradle.test.forward.output", "false") - as String).toBooleanStrictOrNull() ?: false - - systemProperty("gradle.test.forward.output", forwardOutput) - useJUnitPlatform() val protocGen = gradle.includedBuild("protoc-gen") diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt index e33150eec..2d86b84bd 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt @@ -18,12 +18,10 @@ import java.io.File import kotlinx.rpc.buf.BufGenerateExtension import kotlinx.rpc.protoc.DefaultProtoSourceSet import kotlinx.rpc.protoc.ProtoTask -import kotlinx.rpc.protoc.protoTaskProperties import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputDirectories -import org.gradle.kotlin.dsl.listProperty import javax.inject.Inject /** @@ -92,15 +90,16 @@ public abstract class BufGenerateTask @Inject internal constructor( @get:OutputDirectory public abstract val outputDirectory: Property - private val outputSourceDirectoriesInternal: ListProperty = project.objects.listProperty() - /** * Generated source directories by plugin name. * - * Can be used in [SourceDirectorySet.srcDirs]. + * Can be used in [SourceDirectorySet.srcDir] or similar `srcDir` functions from other source set directories. */ @get:OutputDirectories - public val outputSourceDirectories: Provider> = outputSourceDirectoriesInternal + public val outputSourceDirectories: Provider> = pluginNames.map { plugins -> + val out = outputDirectory.get() + plugins.map { out.resolve(it) } + } init { command.set("generate") @@ -125,11 +124,6 @@ public abstract class BufGenerateTask @Inject internal constructor( } this.args.set(args) - - outputSourceDirectoriesInternal.set(pluginNames.map { plugins -> - val out = outputDirectory.get() - plugins.map { out.resolve(it) } - }) } internal companion object { diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index e037861a8..72541bf00 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -6,9 +6,8 @@ package kotlinx.rpc.protoc import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.rpcExtension -import kotlinx.rpc.util.KotlinPluginId import kotlinx.rpc.util.findOrCreate -import kotlinx.rpc.util.withAndroid +import kotlinx.rpc.util.withLegacyAndroid import kotlinx.rpc.util.withAndroidSourceSets import kotlinx.rpc.util.withKotlin import kotlinx.rpc.util.withLazyJavaPluginExtension @@ -51,7 +50,7 @@ internal class ProtoSourceSetFactory( } internal fun Project.findOrCreateProtoSourceSets(): NamedDomainObjectContainer = - project.findOrCreate(PROTO_SOURCE_SETS) { + findOrCreate(PROTO_SOURCE_SETS) { val container = objects.domainObjectContainer( ProtoSourceSet::class.java, ProtoSourceSetFactory(project) @@ -119,6 +118,9 @@ internal open class DefaultProtoSourceSet( } } + val tasksConfigured: Property = project.objects.property() + .convention(false) + // Collection of AndroidSourceSet, KotlinSourceSet, SourceSet (java) associated with this proto source set val languageSourceSets: ListProperty = project.objects.listProperty() val generateTask: Property = project.objects.property() @@ -133,18 +135,6 @@ internal open class DefaultProtoSourceSet( internal val isLegacyAndroid: Property = project.objects.property() .convention(false) - // when com.android.* (not kotlin.multiplatform.library) is applied - kotlin has proxy source sets: - // | AndroidSourceSet | KotlinSourceSet | - // | main | androidMain | - // | test | androidUnitTest | - // | debug | androidDebug | - // ... - // - // these kotlin 'proxy' source sets have propper dependsOn values, but should not have tasks configures, - // as tasks are configured once for the android source sets - internal val isKotlinProxyLegacyAndroid: Property = project.objects.property() - .convention(false) - // used to track tasks' dependencies for main/commonMain/commonTest tasks, e.g.: // - bufGenerateTestDebug depends on bufGenerateDebug // - bufGenerateTestDebug depends on bufGenerateCommonTest and bufGenerateDebug for KMP @@ -250,19 +240,15 @@ internal fun Project.createProtoExtensions() { // CCE free check for kotlin withKotlin { - withKotlinSourceSets { id, extension -> + withKotlinSourceSets { extension -> extension.sourceSets.all { findOrCreateAndConfigure(name, this) } - - if (id != KotlinPluginId.MULTIPLATFORM) { - return@withKotlinSourceSets - } } } // CCE free check for android - withAndroid { + withLegacyAndroid { withAndroidSourceSets { sourceSets -> sourceSets.all { findOrCreateAndConfigure(name, this) @@ -284,6 +270,6 @@ internal fun Project.createProtoExtensions() { internal fun DefaultProtoSourceSet.protoTaskProperties(): ProtoTask.Properties { return androidProperties.orNull ?: ProtoTask.Properties( isTest = name.lowercase().endsWith("test"), - sourceSetName = name, + sourceSetNames = setOf(name), ) } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index f202e3037..f26c370c1 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -21,16 +21,15 @@ import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM import kotlinx.rpc.protoc.android.KmpLibraryAndroidLeafSourceSets import kotlinx.rpc.protoc.android.LegacyAndroidRootSourceSets -import kotlinx.rpc.protoc.android.androidOriginFromKotlinProxySourceSetName import kotlinx.rpc.protoc.android.dependencySourceSets import kotlinx.rpc.protoc.android.kotlinProxyFromAndroidOriginSourceSetName -import kotlinx.rpc.util.ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY import kotlinx.rpc.util.AndroidComponents import kotlinx.rpc.util.KotlinPluginId import kotlinx.rpc.util.ensureDirectoryExists -import kotlinx.rpc.util.hasAndroid +import kotlinx.rpc.util.hasAndroidKmpLibrary +import kotlinx.rpc.util.hasLegacyAndroid import kotlinx.rpc.util.kotlinPluginId -import kotlinx.rpc.util.withAndroid +import kotlinx.rpc.util.withLegacyAndroid import kotlinx.rpc.util.withKotlin import kotlinx.rpc.util.withLazyLegacyAndroidComponentsExtension import org.gradle.api.Action @@ -101,18 +100,10 @@ internal open class DefaultProtocExtension @Inject constructor( // no way to configure tasks before evaluation is done project.afterEvaluate { - configureMultiplatformAndroidSourceSets configure@{ protoSourceSet -> - // done in configureLegacyAndroidVariants + configureMultiplatformWithAndroidSourceSets configure@{ protoSourceSet -> + // configureTasks is done in configureLegacyAndroidVariants even for KMP if (protoSourceSet.isLegacyAndroid.get()) { protoSourceSet.setupDefaultImports(protoSourceSets) - val originSourceSetName = protoSourceSet.name.androidOriginFromKotlinProxySourceSetName() - ?: return@configure - - val originSourceSet = protoSourceSets.findByName(originSourceSetName) - ?: return@configure - - originSourceSet.extendsFrom(protoSourceSet) - return@configure } @@ -120,38 +111,59 @@ internal open class DefaultProtocExtension @Inject constructor( } protoSourceSets.all { + if (this !is DefaultProtoSourceSet) { + return@all + } + withFullyInitializedProtoSourceSet(this) { protoSourceSet -> - // done in configureLegacyAndroidVariants + // configureTasks is done in configureLegacyAndroidVariants if (protoSourceSet.isLegacyAndroid.get()) { return@withFullyInitializedProtoSourceSet } - if (project.plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) { + if (hasAndroidKmpLibrary) { project.tryConfigureKmpLibAndroidVariant(protoSourceSet) } configureTasks(protoSourceSet) } - } - } - project.withAndroid { - if (project.plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) { - // done in tryConfigureKmpLibAndroidVariant - return@withAndroid + sourceSetCreated(this) } + } + project.withLegacyAndroid { withLazyLegacyAndroidComponentsExtension { configureLegacyAndroidVariants( project = project, - isKmp = project.kotlinPluginId == KotlinPluginId.MULTIPLATFORM - ) { - configureTasks(it) - } + isKmp = project.kotlinPluginId == KotlinPluginId.MULTIPLATFORM, + onSourceSet = ::whenSourceSetIsCreated, + configureTasks = ::configureTasks + ) } } } + private val sourceSetCallbacks = mutableMapOf Unit>>() + private fun whenSourceSetIsCreated(name: String?, configure: (DefaultProtoSourceSet) -> Unit) { + if (name == null) { + return + } + + project.protoSourceSets.findByName(name)?.let { protoSourceSet -> + configure(protoSourceSet as DefaultProtoSourceSet) + return + } + + sourceSetCallbacks.computeIfAbsent(name) { mutableListOf() }.add(configure) + } + + private fun sourceSetCreated(protoSourceSet: DefaultProtoSourceSet) { + sourceSetCallbacks.remove(protoSourceSet.name).orEmpty().forEach { configure -> + configure(protoSourceSet) + } + } + private fun ProtocPlugin.defaultOptions() { isKotlin.set(true) options.put("debugOutput", "protoc-gen-$name.log") @@ -163,6 +175,10 @@ internal open class DefaultProtocExtension @Inject constructor( @Suppress("detekt.LongMethod", "detekt.CyclomaticComplexMethod", "detekt.ThrowsCount") private fun configureTasks(protoSourceSet: DefaultProtoSourceSet) { + if (protoSourceSet.tasksConfigured.get()) { + return + } + val baseName = protoSourceSet.name val buildSourceSetsDir = project.protoBuildDirSourceSets.resolve(baseName) @@ -174,7 +190,6 @@ internal open class DefaultProtocExtension @Inject constructor( val buildSourceSetsImportDir = buildSourceSetsDir.resolve(PROTO_FILES_IMPORT_DIR) .ensureDirectoryExists() - // only resolve in task's 'execute' due to the deferred nature of dependsOn protoSourceSet.setupDefaultImports(project.protoSourceSets) val includedProtocPlugins = project.provider { @@ -291,6 +306,8 @@ internal open class DefaultProtocExtension @Inject constructor( protoFiles.set(processProtoTask.map { it.outputs.files }) importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) } + + protoSourceSet.tasksConfigured.set(true) } private fun configureSourceDirectories( @@ -304,23 +321,20 @@ internal open class DefaultProtocExtension @Inject constructor( val kotlinOutputs = languageOutputs(includedProtocPlugins, bufGenerateTask) { it.isKotlin.get() } languageSets.filterIsInstance().forEach { sourceSet -> - sourceSet.java.srcDirs(javaOutputs) + sourceSet.java.srcDir(javaOutputs) } project.withKotlin { languageSets.filterIsInstance().forEach { sourceSet -> - sourceSet.kotlin.srcDirs(kotlinOutputs) + sourceSet.kotlin.srcDir(kotlinOutputs) } } - project.withAndroid { + project.withLegacyAndroid { + // android + kotlin is always done in withKotlin above languageSets.filterIsInstance().forEach { sourceSet -> sourceSet.java.srcDirs(javaOutputs) } - - languageSets.filterIsInstance().forEach { sourceSet -> - sourceSet.kotlin.srcDirs(kotlinOutputs) - } } } @@ -386,46 +400,40 @@ internal open class DefaultProtocExtension @Inject constructor( } private fun DefaultProtoSourceSet.setupDefaultImports(protoSourceSets: ProtoSourceSets) { - // isKotlinProxyLegacyAndroid -> this source set has proper dependsOn, that need to be set up - // isLegacyAndroid -> not a 'com.android.kotlin.multiplatform.library' source set, imports are set up on variant - if (!isKotlinProxyLegacyAndroid.get() && isLegacyAndroid.get()) { + importsAllFrom(getDependsOnImports(protoSourceSets)) + + // isLegacyAndroid -> not a 'com.android.kotlin.multiplatform.library' source set, + // other imports are set up on variant + if (isLegacyAndroid.get()) { return } - val calculatedImports: Provider> = when { - name.lowercase().endsWith("main") -> { - getImportsForTestOrMain(protoSourceSets) - } - - name.lowercase().endsWith("test") -> { - val test = getImportsForTestOrMain(protoSourceSets) - val main = (correspondingMainNameSourceSet(name, protoSourceSets) as? DefaultProtoSourceSet) - ?.getImportsForTestOrMain(protoSourceSets) - - if (main == null) test else test.zip(main) { a, b -> a + b } - } - - else -> { - project.provider { emptyList() } - } + if (name.lowercase().endsWith("test")) { + importsAllFrom(getImportsCorrespondingMainSourceSets(protoSourceSets)) } - - importsAllFrom(calculatedImports) } - private fun DefaultProtoSourceSet.getImportsForTestOrMain( + private fun DefaultProtoSourceSet.getImportsCorrespondingMainSourceSets( protoSourceSets: ProtoSourceSets, ): Provider> { return languageSourceSets.map { list -> val kotlin = list.filterIsInstance() val java = list.filterIsInstance() - kotlin.flatMap { - it.dependsOn.mapNotNull { dep -> protoSourceSets.getByName(dep.name) } - } + kotlin.mapNotNull { + kotlin.mapNotNull { correspondingMainNameSourceSet(it.name, protoSourceSets) } + java.mapNotNull { correspondingMainNameSourceSet(it.name, protoSourceSets) + }.distinct() + } + } + + private fun DefaultProtoSourceSet.getDependsOnImports(protoSourceSets: ProtoSourceSets): Provider> { + return languageSourceSets.map { list -> + val kotlin = list.filterIsInstance() + + kotlin.flatMap { + it.dependsOn.mapNotNull { dep -> protoSourceSets.getByName(dep.name) } } } } @@ -461,7 +469,7 @@ private fun Project.tryConfigureKmpLibAndroidVariant(protoSourceSet: DefaultProt isTest = sourceSetEntry != KmpLibraryAndroidLeafSourceSets.Main, isInstrumentedTest = sourceSetEntry == KmpLibraryAndroidLeafSourceSets.DeviceTest, isUnitTest = sourceSetEntry == KmpLibraryAndroidLeafSourceSets.HostTest, - sourceSetName = protoSourceSet.name, + sourceSetNames = setOf(protoSourceSet.name), buildType = null, flavors = emptyList(), variant = null, @@ -470,7 +478,7 @@ private fun Project.tryConfigureKmpLibAndroidVariant(protoSourceSet: DefaultProt protoSourceSet.androidProperties.set(properties) } -// init isLegacyAndroid, isKotlinProxyLegacyAndroid +// init isLegacyAndroid private fun Project.withFullyInitializedProtoSourceSet( sourceSet: ProtoSourceSet, body: (DefaultProtoSourceSet) -> Unit, @@ -483,48 +491,41 @@ private fun Project.withFullyInitializedProtoSourceSet( val languageSets = sourceSet.languageSourceSets.get() - val anyAndroid = hasAndroid && languageSets.any { it is AndroidSourceSet } + val anyLegacyAndroid = hasLegacyAndroid && languageSets.any { it is AndroidSourceSet } val anyKotlin = kotlinPluginId != null && languageSets.any { it is KotlinSourceSet } - if (anyAndroid && !anyKotlin) { + if (anyLegacyAndroid && !anyKotlin) { sourceSet.isLegacyAndroid.set(true) - sourceSet.isKotlinProxyLegacyAndroid.set(false) body(sourceSet) return } when (kotlinPluginId) { KotlinPluginId.ANDROID -> { + // todo should be .set(anyLegacyAndroid), + // but some phantom kotlin source sets ruin the picture. + // Should investigate where they are coming from (debugAndroidTest, debugUnitTest, releaseUnitTest). sourceSet.isLegacyAndroid.set(true) - sourceSet.isKotlinProxyLegacyAndroid.set(false) - body(sourceSet) - } - - KotlinPluginId.JVM -> { - sourceSet.isLegacyAndroid.set(false) - sourceSet.isKotlinProxyLegacyAndroid.set(false) body(sourceSet) } KotlinPluginId.MULTIPLATFORM -> { - if (plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) || !hasAndroid || !anyKotlin) { + if (!hasLegacyAndroid || hasAndroidKmpLibrary || !anyKotlin) { sourceSet.isLegacyAndroid.set(false) - sourceSet.isKotlinProxyLegacyAndroid.set(false) body(sourceSet) } - // no else here, it is handled by configureMultiplatformAndroidSourceSets + // no else here, it is handled by configureMultiplatformWithAndroidSourceSets } - null -> { + KotlinPluginId.JVM, null -> { sourceSet.isLegacyAndroid.set(false) - sourceSet.isKotlinProxyLegacyAndroid.set(false) body(sourceSet) } } } -private fun Project.configureMultiplatformAndroidSourceSets(body: (DefaultProtoSourceSet) -> Unit) { - if (!hasAndroid || plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) || kotlinPluginId != KotlinPluginId.MULTIPLATFORM) { +private fun Project.configureMultiplatformWithAndroidSourceSets(body: (DefaultProtoSourceSet) -> Unit) { + if (!hasLegacyAndroid || hasAndroidKmpLibrary || kotlinPluginId != KotlinPluginId.MULTIPLATFORM) { return } @@ -539,9 +540,11 @@ private fun Project.configureMultiplatformAndroidSourceSets(body: (DefaultProtoS val protoSourceSet = protoSourceSets.getByName(it.name) as? DefaultProtoSourceSet ?: return@forAll - val isAndroid = target is KotlinAndroidTarget - protoSourceSet.isLegacyAndroid.set(isAndroid) - protoSourceSet.isKotlinProxyLegacyAndroid.set(isAndroid) + if (protoSourceSet.tasksConfigured.orElse(false).get()) { + return@forAll + } + + protoSourceSet.isLegacyAndroid.set(target is KotlinAndroidTarget) body(protoSourceSet) } } @@ -551,6 +554,7 @@ private fun Project.configureMultiplatformAndroidSourceSets(body: (DefaultProtoS private fun AndroidComponents.configureLegacyAndroidVariants( project: Project, isKmp: Boolean, + onSourceSet: (String?, (DefaultProtoSourceSet) -> Unit) -> Unit, configureTasks: (DefaultProtoSourceSet) -> Unit, ) { val rootSourceSets = LegacyAndroidRootSourceSets.entries @@ -592,80 +596,100 @@ private fun AndroidComponents.configureLegacyAndroidVariants( val variantSourceSetName = dependencySourceSetNames.lastOrNull() ?: throw GradleException("No source sets found for variant ${variant.name}") - val variantProtoSourceSet = project.protoSourceSets.named(variantSourceSetName) - - variantProtoSourceSet.configure { - val default = this as? DefaultProtoSourceSet ?: return@configure - - val properties = ProtoTask.AndroidProperties( - isTest = rootName != LegacyAndroidRootSourceSets.Main, - isInstrumentedTest = rootName == LegacyAndroidRootSourceSets.AndroidTest, - isUnitTest = rootName == LegacyAndroidRootSourceSets.Test, - sourceSetName = variantSourceSetName, - buildType = variant.buildType, - flavors = variant.productFlavors.map { (_, flavor) -> flavor }, - variant = variant.name, - ) - default.androidProperties.set(properties) + val variantProtoSourceSet = project.protoSourceSets.getByName(variantSourceSetName) + as? DefaultProtoSourceSet ?: return@forEach - (dependencySourceSetNames + testFixtureSetNames).forEach { dependencyName -> + val proxyNames = mutableListOf() + (dependencySourceSetNames + testFixtureSetNames) + .filter { it != variantSourceSetName } + .forEach { dependencyName -> val dependencyProtoSourceSet = project.protoSourceSets.findByName(dependencyName) ?: return@forEach val proxyName = dependencyName.kotlinProxyFromAndroidOriginSourceSetName(rootName) - // can be also null, if not yet configured on the KGP side - val proxyDependency = if (isKmp && proxyName != null) { - project.protoSourceSets.findByName(proxyName) as? DefaultProtoSourceSet - } else { - null + if (proxyName != null && isKmp) { + onSourceSet(proxyName) { proxyDependency -> + proxyNames += proxyName + variantProtoSourceSet.extendsFrom(proxyDependency) + } } - if (proxyDependency != null) { - default.extendsFrom(proxyDependency) - } + variantProtoSourceSet.extendsFrom(dependencyProtoSourceSet) + } + + // for pure android or kotlin.android the variant source set with the associated tasks are android-like + // for kmp + legacy android - the tasks are associated with kmp-style source set + if (isKmp) { + val variantProtoSourceSetProxyName = + variantSourceSetName.kotlinProxyFromAndroidOriginSourceSetName(rootName) - if (name != dependencyName) { - default.extendsFrom(dependencyProtoSourceSet) + onSourceSet(variantProtoSourceSetProxyName) { variantProtoSourceSetProxy -> + variantProtoSourceSetProxy.extendsFrom(variantProtoSourceSet) + + if (rootName != LegacyAndroidRootSourceSets.Main) { + val mainVariantProtoSourceSetProxyName = + variant.name.kotlinProxyFromAndroidOriginSourceSetName(LegacyAndroidRootSourceSets.Main) + + onSourceSet(mainVariantProtoSourceSetProxyName) { mainVariantProtoSourceSetProxy -> + variantProtoSourceSetProxy.androidDependencies.add(mainVariantProtoSourceSetProxy) + + variantProtoSourceSetProxy.importsFrom(mainVariantProtoSourceSetProxy) + } } - } + val sourceSetNames = dependencySourceSetNames + proxyNames + variantProtoSourceSetProxy.name + + val properties = androidProperties( + rootName = rootName, + variant = variant, + sourceSetNames = sourceSetNames, + ) + + variantProtoSourceSetProxy.androidProperties.set(properties) + + configureTasks(variantProtoSourceSetProxy) + } + } else { if (rootName != LegacyAndroidRootSourceSets.Main) { val mainVariantProtoSourceSet = project.protoSourceSets.findByName(variant.name) as? DefaultProtoSourceSet if (mainVariantProtoSourceSet != null) { - default.androidDependencies.add(mainVariantProtoSourceSet) + variantProtoSourceSet.androidDependencies.add(mainVariantProtoSourceSet) - default.importsFrom(mainVariantProtoSourceSet) + variantProtoSourceSet.importsFrom(mainVariantProtoSourceSet) } } - if (isKmp) { - if (rootName == LegacyAndroidRootSourceSets.Main) { - val commonMain = project.protoSourceSets - .findByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) - as? DefaultProtoSourceSet - - if (commonMain != null) { - default.androidDependencies.add(commonMain) - } - } else { - val commonTest = project.protoSourceSets - .findByName(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) - as? DefaultProtoSourceSet - - if (commonTest != null) { - default.androidDependencies.add(commonTest) - } - } - } + val properties = androidProperties( + rootName = rootName, + variant = variant, + sourceSetNames = dependencySourceSetNames, + ) + variantProtoSourceSet.androidProperties.set(properties) - configureTasks(default) + configureTasks(variantProtoSourceSet) } } } } +private fun androidProperties( + rootName: LegacyAndroidRootSourceSets, + variant: Variant, + sourceSetNames: Iterable, +): ProtoTask.AndroidProperties { + return ProtoTask.AndroidProperties( + isTest = rootName != LegacyAndroidRootSourceSets.Main, + isInstrumentedTest = rootName == LegacyAndroidRootSourceSets.AndroidTest, + isUnitTest = rootName == LegacyAndroidRootSourceSets.Test, + sourceSetNames = sourceSetNames.toSet(), + buildType = variant.buildType, + flavors = variant.productFlavors.map { (_, flavor) -> flavor }, + variant = variant.name, + ) +} + /** * Return a list of [DefaultProtoSourceSet] that have tasks that [this] will depend on. * It's a different list from [DefaultProtoSourceSet.imports], @@ -711,6 +735,8 @@ private fun correspondingMainNameSourceSet(name: String, protoSourceSets: ProtoS private fun correspondingMainName(name: String): String { return when { name == "test" -> "main" + name.endsWith("HostTest") -> name.removeSuffix("HostTest") + "Main" + name.endsWith("DeviceTest") -> name.removeSuffix("DeviceTest") + "Main" name.endsWith("Test") -> name.removeSuffix("Test") + "Main" else -> throw GradleException("Unknown test source set name: $name") } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt index 9507ab03b..7d1fba579 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt @@ -20,15 +20,23 @@ public interface ProtoTask : Task { * * Can be used with [ProtoTasks] to filter tasks. */ - public open class Properties internal constructor( + public open class Properties( /** * Whether the task is for a test source set. */ public val isTest: Boolean, /** - * Name of the [kotlinx.rpc.protoc.ProtoSourceSet] this task is associated with. + * Names of all [kotlinx.rpc.protoc.ProtoSourceSet]s this task is associated with. + * + * For Kotlin/JVM it has only one source set (`main`, or `test`, for example). + * + * For Kotlin/Multiplatform it also has only one source set (`commonMain`, `commonTest`, or `jsMain`, for example). + * + * For Android, Kotlin/Android, and Kotlin/Multiplatform + Android it can have multiple source sets: + * - `["androidMain", "androidDebug", "main", "debug"]` + * - `["androidTest", "androidUnitTestDebug", "test", "testDebug"]` */ - public val sourceSetName: String, + public val sourceSetNames: Set, ) /** @@ -36,9 +44,16 @@ public interface ProtoTask : Task { * * Can be used with [ProtoTasks] to filter tasks. */ - public class AndroidProperties internal constructor( + public class AndroidProperties( + /** + * @see Properties.isTest + */ isTest: Boolean, - sourceSetName: String, + + /** + * @see Properties.sourceSetNames + */ + sourceSetNames: Set, /** * Name of the android flavors this task is associated with. @@ -74,7 +89,7 @@ public interface ProtoTask : Task { * Whether the task is for unit tests. */ public val isUnitTest: Boolean, - ) : Properties(isTest, sourceSetName) + ) : Properties(isTest, sourceSetNames) } /** diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt index 2bcec019f..5b8e2ee8c 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt @@ -17,16 +17,46 @@ import kotlin.reflect.KClass /** * Returns a collection of all proto tasks registered in the project. + * + * Example: + * ```kotlin + * protoTasks.matchingSourceSet("main") + * protoTasks.testTasks() + * protoTasks. + * .testTasks() + * .matching { ... } + * .all { ... } + * ``` */ public val Project.protoTasks: ProtoTasks get() = ProtoTasksImpl(this) /** * Returns a collection of all `buf` tasks registered in the project. + * + * Example: + * ```kotlin + * protoTasks.buf.matchingSourceSet("main") + * protoTasks.buf.testTasks() + * protoTasks.buf + * .testTasks() + * .matching { ... } + * .all { ... } + * ``` */ public val ProtoTasks.buf: ProtoTasks get() = matchingType(BufExecTask::class) /** * Returns a collection of all `buf generate` tasks registered in the project. + * + * Example: + * ```kotlin + * protoTasks.buf.generate.matchingSourceSet("main") + * protoTasks.buf.generate.testTasks() + * protoTasks.buf.generate + * .testTasks() + * .matching { ... } + * .all { ... } + * ``` */ public val ProtoTasks.generate: ProtoTasks get() = matchingType(BufGenerateTask::class) @@ -39,7 +69,7 @@ public val ProtoTasks.generate: ProtoTasks get() = * ```kotlin * protoTasks.matchingSourceSet("main") * protoTasks.testTasks() - * protoTasks. + * protoTasks * .testTasks() * .matching { ... } * .all { ... } @@ -104,9 +134,7 @@ public sealed interface ProtoTasks : TaskCollection @@ -115,31 +143,42 @@ public sealed interface ProtoTasks : TaskCollection /** - * Filters tasks by where [ProtoTask.AndroidProperties.isUnitTest] is `true`. + * Filters tasks by where [ProtoTask.properties] are of type [ProtoTask.AndroidProperties]. + * + * Takes optional predicate to filter on [ProtoTask.AndroidProperties]. * * ```kotlin - * rpc.protoc { - * buf.tasks.all().androidUnitTestTasks() + * protoTasks.androidTasks() + * + * protoTasks.androidTasks { _, properties -> + * properties.buildType == "debug" * } * ``` */ + public fun androidTasks( + predicate: (ProtoTaskT, ProtoTask.AndroidProperties) -> Boolean = { _, _ -> true }, + ): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.isUnitTest] is `true`. + * + * ```kotlin + * protoTasks.androidUnitTestTasks() + * ``` + */ public fun androidUnitTestTasks(): ProtoTasks /** * Filters tasks by where [ProtoTask.AndroidProperties.isInstrumentedTest] is `true`. * * ```kotlin - * rpc.protoc { - * buf.tasks.all().androidInstrumentedTestTasks() - * } + * protoTasks.androidInstrumentedTestTasks() * ``` */ public fun androidInstrumentedTestTasks(): ProtoTasks @@ -152,9 +191,7 @@ public sealed interface ProtoTasks : TaskCollection @@ -165,9 +202,7 @@ public sealed interface ProtoTasks : TaskCollection @@ -178,9 +213,7 @@ public sealed interface ProtoTasks : TaskCollection @@ -191,9 +224,7 @@ public sealed interface ProtoTasks : TaskCollection() - * } + * protoTasks.matchingType() * ``` */ public inline fun ProtoTasks<*>.matchingType(): ProtoTasks { @@ -217,7 +248,7 @@ internal open class ProtoTasksImpl( override fun matchingSourceSet(sourceSetName: String): ProtoTasks { return ProtoTasksImpl( project, - collection.matching { it.properties.sourceSetName == sourceSetName }, + collection.matching { sourceSetName in it.properties.sourceSetNames }, kClass ) } @@ -246,13 +277,16 @@ internal open class ProtoTasksImpl( return ProtoTasksImpl(project, collection.matching { !it.properties.isTest }, kClass) } + override fun androidTasks( + predicate: (ProtoTaskT, ProtoTask.AndroidProperties) -> Boolean, + ): ProtoTasks { + return ProtoTasksImpl(project, collection.matchingAndroid(predicate), kClass) + } + override fun androidUnitTestTasks(): ProtoTasks { return ProtoTasksImpl( project = project, - collection = collection.matching { - val properties = it.properties as? ProtoTask.AndroidProperties - ?: return@matching false - + collection = collection.matchingAndroid { _, properties -> properties.isUnitTest }, kClass = kClass, @@ -262,10 +296,7 @@ internal open class ProtoTasksImpl( override fun androidInstrumentedTestTasks(): ProtoTasks { return ProtoTasksImpl( project = project, - collection = collection.matching { - val properties = it.properties as? ProtoTask.AndroidProperties - ?: return@matching false - + collection = collection.matchingAndroid { _, properties -> properties.isInstrumentedTest }, kClass = kClass, @@ -275,12 +306,9 @@ internal open class ProtoTasksImpl( override fun matchingAndroidFlavor(flavor: String?): ProtoTasks { return ProtoTasksImpl( project = project, - collection = collection.matching { - val properties = it.properties as? ProtoTask.AndroidProperties - ?: return@matching false - + collection = collection.matchingAndroid { _, properties -> if (flavor == null) { - return@matching properties.flavors.isEmpty() + return@matchingAndroid properties.flavors.isEmpty() } properties.flavors.contains(flavor) @@ -292,10 +320,7 @@ internal open class ProtoTasksImpl( override fun matchingAndroidBuildType(buildType: String?): ProtoTasks { return ProtoTasksImpl( project = project, - collection = collection.matching { - val properties = it.properties as? ProtoTask.AndroidProperties - ?: return@matching false - + collection = collection.matchingAndroid { _, properties -> properties.buildType == buildType }, kClass = kClass, @@ -305,17 +330,20 @@ internal open class ProtoTasksImpl( override fun matchingAndroidVariant(variant: String?): ProtoTasks { return ProtoTasksImpl( project = project, - collection = collection.matching { - val properties = it.properties as? ProtoTask.AndroidProperties - ?: return@matching false - + collection = collection.matchingAndroid { _, properties -> properties.variant == variant }, kClass = kClass, ) } - fun empty() = ProtoTasksImpl(project, matching { false }, kClass) + private fun TaskCollection.matchingAndroid( + predicate: (ProtoTaskT, ProtoTask.AndroidProperties) -> Boolean = { _, _ -> true }, + ): TaskCollection { + return matching { + it.properties is ProtoTask.AndroidProperties && predicate(it, it.properties as ProtoTask.AndroidProperties) + } + } // Java default method override @Deprecated("Deprecated in Java") diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt index f1a56aef0..b63995cbe 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt @@ -56,6 +56,17 @@ internal fun Variant.dependencySourceSets(rootName: LegacyAndroidRootSourceSets) add(name.prefixed()) } +// when com.android.* (not kotlin.multiplatform.library) is applied - kotlin has proxy source sets: +// | AndroidSourceSet | KotlinSourceSet | +// | main | androidMain | +// | test | androidUnitTest | +// | debug | androidDebug | +// ... +// +// these kotlin 'proxy' source sets have propper dependsOn values, and should have tasks configured for them +// instead of tasks being configured for the android source sets +// +// Examples: // debug -> androidDebug // androidTest -> androidInstrumentedTest // androidTestDebug -> androidInstrumentedTestDebug @@ -64,9 +75,6 @@ internal fun Variant.dependencySourceSets(rootName: LegacyAndroidRootSourceSets) // test -> androidUnitTest // testDebug -> androidUnitTestDebug // testRelease -> androidUnitTestRelease -/** - * @see kotlinx.rpc.protoc.DefaultProtoSourceSet.isKotlinProxyLegacyAndroid - */ internal fun String.kotlinProxyFromAndroidOriginSourceSetName(rootName: LegacyAndroidRootSourceSets): String? { return when (rootName) { LegacyAndroidRootSourceSets.Main -> { @@ -85,26 +93,6 @@ internal fun String.kotlinProxyFromAndroidOriginSourceSetName(rootName: LegacyAn } } -// androidDebug -> debug -// androidInstrumentedTest -> androidTest -// androidInstrumentedTestDebug -> androidTestDebug -// androidMain -> main -// androidRelease -> release -// androidUnitTest -> test -// androidUnitTestDebug -> testDebug -// androidUnitTestRelease -> testRelease -/** - * @see kotlinx.rpc.protoc.DefaultProtoSourceSet.isKotlinProxyLegacyAndroid - */ -internal fun String.androidOriginFromKotlinProxySourceSetName(): String? { - return when { - startsWith("androidUnit") -> removePrefix("androidUnit").replaceFirstChar { it.lowercase() } - startsWith("androidInstrumented") -> "android${removePrefix("androidInstrumented")}" - startsWith("android") -> removePrefix("android").replaceFirstChar { it.lowercase() } - else -> null - } -} - internal fun String.prefixed(rootName: LegacyAndroidRootSourceSets) = if (rootName == LegacyAndroidRootSourceSets.Main) { this diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt index 0ec2d79a9..10dc1fa7c 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt @@ -53,50 +53,48 @@ internal fun Project.withKotlin(action: (id: KotlinPluginId) -> Unit) { } } -internal fun Project.withKotlinSourceSets(action: (id: KotlinPluginId, KotlinSourceSetContainer) -> Unit) { - withKotlin { id -> - action(id, the()) +internal fun Project.withKotlinSourceSets(action: (KotlinSourceSetContainer) -> Unit) { + withKotlin { + action(the()) } } -internal val Project.hasAndroid: Boolean +internal val Project.hasLegacyAndroid: Boolean get() = plugins.hasPlugin(ANDROID_LIBRARY) || plugins.hasPlugin(ANDROID_APPLICATION) || plugins.hasPlugin(ANDROID_DYNAMIC_FEATURE) || - plugins.hasPlugin(ANDROID_TEST) || - plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) + plugins.hasPlugin(ANDROID_TEST) -internal fun Project.withAndroid(action: AndroidApplied.() -> Unit) { +internal val Project.hasAndroidKmpLibrary: Boolean + get() = plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) + +internal fun Project.withLegacyAndroid(action: LegacyAndroidApplied.() -> Unit) { plugins.withId(ANDROID_LIBRARY) { - action(AndroidApplied(project, ANDROID_LIBRARY)) + action(LegacyAndroidApplied(project, ANDROID_LIBRARY)) } plugins.withId(ANDROID_APPLICATION) { - action(AndroidApplied(project, ANDROID_APPLICATION)) + action(LegacyAndroidApplied(project, ANDROID_APPLICATION)) } plugins.withId(ANDROID_DYNAMIC_FEATURE) { - action(AndroidApplied(project, ANDROID_DYNAMIC_FEATURE)) + action(LegacyAndroidApplied(project, ANDROID_DYNAMIC_FEATURE)) } plugins.withId(ANDROID_TEST) { - action(AndroidApplied(project, ANDROID_TEST)) - } - - plugins.withId(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) { - action(AndroidApplied(project, ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY)) + action(LegacyAndroidApplied(project, ANDROID_TEST)) } } -internal class AndroidApplied(val project: Project, val id: String) +internal class LegacyAndroidApplied(val project: Project, val id: String) -internal fun AndroidApplied.withAndroidSourceSets(action: (NamedDomainObjectContainer) -> Unit) { +internal fun LegacyAndroidApplied.withAndroidSourceSets(action: (NamedDomainObjectContainer) -> Unit) { action(project.the().sourceSets) } internal typealias AndroidComponents = AndroidComponentsExtension, *, out Variant> -internal fun AndroidApplied.withLazyLegacyAndroidComponentsExtension(action: Action) { +internal fun LegacyAndroidApplied.withLazyLegacyAndroidComponentsExtension(action: Action) { when (id) { ANDROID_LIBRARY -> action.execute(project.the()) ANDROID_APPLICATION -> action.execute(project.the()) diff --git a/protobuf/protobuf-core/build.gradle.kts b/protobuf/protobuf-core/build.gradle.kts index d499701d2..07d2945b2 100644 --- a/protobuf/protobuf-core/build.gradle.kts +++ b/protobuf/protobuf-core/build.gradle.kts @@ -83,12 +83,12 @@ rpc { buf.generate.comments { includeFileLevelComments = false } - - protoTasks.buf.generate.matchingKotlinSourceSet(kotlin.sourceSets.commonMain).configureEach { - includeWkt = true - outputDirectory = generatedCodeDir(properties.sourceSetName) - } } } +protoTasks.buf.generate.matchingKotlinSourceSet(kotlin.sourceSets.commonMain).configureEach { + includeWkt = true + outputDirectory = generatedCodeDir(properties.sourceSetNames.single()) +} + configureLocalProtocGenDevelopmentDependency("Main", "Test") diff --git a/tests/protobuf-conformance/build.gradle.kts b/tests/protobuf-conformance/build.gradle.kts index 860a4b94d..34b2ba986 100644 --- a/tests/protobuf-conformance/build.gradle.kts +++ b/tests/protobuf-conformance/build.gradle.kts @@ -49,13 +49,13 @@ rpc { buf.generate.comments { includeFileLevelComments = false } - - protoTasks.buf.generate.nonTestTasks().configureEach { - outputDirectory = generatedCodeDir(properties.sourceSetName) - } } } +protoTasks.buf.generate.nonTestTasks().configureEach { + outputDirectory = generatedCodeDir(properties.sourceSetNames.single()) +} + val mockClientJar = tasks.register("mockClientJar") { archiveBaseName.set("mockClient") archiveVersion.set("") From 3009c30318af1349965bdfc3577457a6970011a6 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Thu, 11 Dec 2025 18:13:42 +0100 Subject: [PATCH 07/11] Tests for KMP + Android support --- .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 1177 ++++++++++++++--- .../test/kotlin/kotlinx/rpc/base/BaseTest.kt | 68 +- .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 85 +- .../buf_tasks_default/build.gradle.kts | 47 +- .../buf_tasks_extended/build.gradle.kts | 122 +- .../buf_tasks/build.gradle.kts | 13 +- .../build.gradle.kts | 176 +++ .../build.gradle.kts | 202 +++ .../buf_tasks_legacy_android/build.gradle.kts | 307 +++++ .../build.gradle.kts | 23 + .../src/androidMain/proto/androidMain.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../build.gradle.kts | 31 + .../proto/androidDeviceTest.proto | 5 + .../proto/androidHostTest.proto | 5 + .../src/androidMain/proto/androidMain.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../build.gradle.kts | 27 + .../proto/androidDeviceTest.proto | 5 + .../proto/androidHostTest.proto | 5 + .../src/androidMain/proto/androidMain.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../build.gradle.kts | 35 + .../src/androidDebug/proto/androidDebug.proto | 5 + .../proto/androidInstrumentedTest.proto | 5 + .../proto/androidInstrumentedTestDebug.proto | 5 + .../src/androidMain/proto/androidMain.proto | 5 + .../androidRelease/proto/androidRelease.proto | 5 + .../src/androidTest/proto/androidTest.proto | 5 + .../proto/androidTestDebug.proto | 5 + .../proto/androidUnitTest.proto | 5 + .../proto/androidUnitTestDebug.proto | 5 + .../proto/androidUnitTestRelease.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/debug/proto/debug.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../src/main/proto/main.proto | 5 + .../src/release/proto/release.proto | 5 + .../src/test/proto/test.proto | 5 + .../src/testDebug/proto/testDebug.proto | 5 + .../src/testFixtures/proto/testFixtures.proto | 5 + .../proto/testFixturesDebug.proto | 5 + .../proto/testFixturesRelease.proto | 5 + .../src/testRelease/proto/testRelease.proto | 5 + .../build.gradle.kts | 26 + .../src/androidDebug/proto/androidDebug.proto | 5 + .../proto/androidInstrumentedTest.proto | 5 + .../proto/androidInstrumentedTestDebug.proto | 5 + .../src/androidMain/proto/androidMain.proto | 5 + .../androidRelease/proto/androidRelease.proto | 5 + .../src/androidTest/proto/androidTest.proto | 5 + .../proto/androidTestDebug.proto | 5 + .../proto/androidUnitTest.proto | 5 + .../proto/androidUnitTestDebug.proto | 5 + .../proto/androidUnitTestRelease.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/debug/proto/debug.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../src/main/proto/main.proto | 5 + .../src/release/proto/release.proto | 5 + .../src/test/proto/test.proto | 5 + .../src/testDebug/proto/testDebug.proto | 5 + .../src/testFixtures/proto/testFixtures.proto | 5 + .../proto/testFixturesDebug.proto | 5 + .../proto/testFixturesRelease.proto | 5 + .../src/testRelease/proto/testRelease.proto | 5 + .../build.gradle.kts | 31 + .../proto/androidDeviceTest.proto | 5 + .../proto/androidHostTest.proto | 5 + .../src/androidMain/proto/androidMain.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../build.gradle.kts | 35 + .../src/androidDebug/proto/androidDebug.proto | 5 + .../proto/androidInstrumentedTest.proto | 5 + .../proto/androidInstrumentedTestDebug.proto | 5 + .../src/androidMain/proto/androidMain.proto | 5 + .../androidRelease/proto/androidRelease.proto | 5 + .../src/androidTest/proto/androidTest.proto | 5 + .../proto/androidTestDebug.proto | 5 + .../proto/androidUnitTest.proto | 5 + .../proto/androidUnitTestDebug.proto | 5 + .../proto/androidUnitTestRelease.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/debug/proto/debug.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../src/main/proto/main.proto | 5 + .../src/release/proto/release.proto | 5 + .../src/test/proto/test.proto | 5 + .../src/testDebug/proto/testDebug.proto | 5 + .../src/testFixtures/proto/testFixtures.proto | 5 + .../proto/testFixturesDebug.proto | 5 + .../proto/testFixturesRelease.proto | 5 + .../src/testRelease/proto/testRelease.proto | 5 + 111 files changed, 2632 insertions(+), 248 deletions(-) create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library_with_test_tasks/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_legacy_android/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidDeviceTest/proto/androidDeviceTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidHostTest/proto/androidHostTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidDeviceTest/proto/androidDeviceTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidHostTest/proto/androidHostTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidDebug/proto/androidDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidRelease/proto/androidRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTest/proto/androidTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/debug/proto/debug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/main/proto/main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/release/proto/release.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/test/proto/test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testDebug/proto/testDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixtures/proto/testFixtures.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testRelease/proto/testRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidDebug/proto/androidDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidRelease/proto/androidRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTest/proto/androidTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTestDebug/proto/androidTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTest/proto/androidUnitTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/debug/proto/debug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/main/proto/main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/release/proto/release.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/test/proto/test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testDebug/proto/testDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixtures/proto/testFixtures.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesDebug/proto/testFixturesDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesRelease/proto/testFixturesRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testRelease/proto/testRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidDeviceTest/proto/androidDeviceTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidHostTest/proto/androidHostTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidDebug/proto/androidDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidMain/proto/androidMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidRelease/proto/androidRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTest/proto/androidTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/debug/proto/debug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/main/proto/main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/release/proto/release.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/test/proto/test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testDebug/proto/testDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixtures/proto/testFixtures.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testRelease/proto/testRelease.proto diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index 26f3621d0..e49f19d91 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -5,6 +5,7 @@ package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest +import kotlinx.rpc.base.testTestsForAndroidKmpLibExist import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInstance @@ -31,7 +32,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @TestFactory fun `No gRPC`() = runGrpcTest { - SSetsKmp.entries.forEach { + SSetsKmp.Default.entries.forEach { runNonExistentTasksForSourceSet(it) } } @@ -120,87 +121,378 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @TestFactory fun `KMP Hierarchy`() = runGrpcTest { runAndCheckFiles( - SSetsKmp.commonMain, + SSetsKmp.Default.commonMain, ) runAndCheckFiles( - SSetsKmp.commonTest, - SSetsKmp.commonMain, + SSetsKmp.Default.commonTest, + SSetsKmp.Default.commonMain, ) runAndCheckFiles( - SSetsKmp.nativeMain, - SSetsKmp.commonMain, + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.commonMain, ) runAndCheckFiles( - SSetsKmp.nativeTest, - SSetsKmp.commonMain, SSetsKmp.nativeMain, - SSetsKmp.commonTest, + SSetsKmp.Default.nativeTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, + SSetsKmp.Default.commonTest, ) runAndCheckFiles( - SSetsKmp.jvmMain, - SSetsKmp.commonMain, + SSetsKmp.Default.jvmMain, + SSetsKmp.Default.commonMain, ) runAndCheckFiles( - SSetsKmp.jvmTest, - SSetsKmp.commonMain, SSetsKmp.jvmMain, - SSetsKmp.commonTest, + SSetsKmp.Default.jvmTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.jvmMain, + SSetsKmp.Default.commonTest, ) runAndCheckFiles( - SSetsKmp.jsMain, - SSetsKmp.commonMain, SSetsKmp.webMain, + SSetsKmp.Default.jsMain, + SSetsKmp.Default.commonMain, SSetsKmp.Default.webMain, ) runAndCheckFiles( - SSetsKmp.jsTest, - SSetsKmp.commonMain, SSetsKmp.webMain, SSetsKmp.jsMain, - SSetsKmp.commonTest, SSetsKmp.webTest + SSetsKmp.Default.jsTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.webMain, SSetsKmp.Default.jsMain, + SSetsKmp.Default.commonTest, SSetsKmp.Default.webTest ) runAndCheckFiles( - SSetsKmp.appleMain, - SSetsKmp.commonMain, SSetsKmp.nativeMain, + SSetsKmp.Default.appleMain, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, ) runAndCheckFiles( - SSetsKmp.appleTest, - SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, - SSetsKmp.commonTest, SSetsKmp.nativeTest + SSetsKmp.Default.appleTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, SSetsKmp.Default.appleMain, + SSetsKmp.Default.commonTest, SSetsKmp.Default.nativeTest ) runAndCheckFiles( - SSetsKmp.macosMain, - SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, + SSetsKmp.Default.macosMain, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, SSetsKmp.Default.appleMain, ) runAndCheckFiles( - SSetsKmp.macosTest, - SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, SSetsKmp.macosMain, - SSetsKmp.commonTest, SSetsKmp.nativeTest, SSetsKmp.appleTest + SSetsKmp.Default.macosTest, + SSetsKmp.Default.commonMain, + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.appleMain, + SSetsKmp.Default.macosMain, + SSetsKmp.Default.commonTest, + SSetsKmp.Default.nativeTest, + SSetsKmp.Default.appleTest ) runAndCheckFiles( - SSetsKmp.macosArm64Main, - SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, SSetsKmp.macosMain, + SSetsKmp.Default.macosArm64Main, + SSetsKmp.Default.commonMain, + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.appleMain, + SSetsKmp.Default.macosMain, ) runAndCheckFiles( - SSetsKmp.macosArm64Test, - SSetsKmp.commonMain, SSetsKmp.nativeMain, SSetsKmp.appleMain, SSetsKmp.macosMain, SSetsKmp.macosArm64Main, - SSetsKmp.commonTest, SSetsKmp.nativeTest, SSetsKmp.appleTest, SSetsKmp.macosTest, + SSetsKmp.Default.macosArm64Test, + SSetsKmp.Default.commonMain, + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.appleMain, + SSetsKmp.Default.macosMain, + SSetsKmp.Default.macosArm64Main, + SSetsKmp.Default.commonTest, + SSetsKmp.Default.nativeTest, + SSetsKmp.Default.appleTest, + SSetsKmp.Default.macosTest, + ) + } + + @TestFactory + fun `KMP Hierarchy Android KMP Library`() = runGrpcTest { + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonTest, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidMain, + SSetsKmp.AndroidKmpLib.commonMain, + ) + } + + @TestFactory + fun `KMP Hierarchy Android KMP Library With Test Tasks`() = runGrpcTest( + versionsPredicate = { testTestsForAndroidKmpLibExist() }, + ) { + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonTest, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidMain, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidHostTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.androidMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidDeviceTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.androidMain, + ) + } + + @TestFactory + fun `KMP Hierarchy Android KMP Library With Test Tasks Not Wired`() = runGrpcTest( + versionsPredicate = { testTestsForAndroidKmpLibExist() }, + ) { + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonTest, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidMain, + SSetsKmp.AndroidKmpLib.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidHostTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.androidMain, + ) + + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.androidDeviceTest, + SSetsKmp.AndroidKmpLib.commonMain, SSetsKmp.AndroidKmpLib.androidMain, + ) + } + + @TestFactory + fun `KMP Hierarchy Legacy Android`() = runGrpcTest { + runAndCheckFiles( + SSetsKmp.LegacyAndroid.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.commonTest, + SSetsKmp.LegacyAndroid.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.jvmMain, + SSetsKmp.LegacyAndroid.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.jvmTest, + SSetsKmp.LegacyAndroid.commonMain, SSetsKmp.LegacyAndroid.jvmMain, + SSetsKmp.LegacyAndroid.commonTest, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidDebug, + SSetsKmp.LegacyAndroid.commonMain, + extended = listOf( + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, + SSetsKmp.LegacyAndroid.debug, + ), + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidRelease, + SSetsKmp.LegacyAndroid.commonMain, + extended = listOf( + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, + SSetsKmp.LegacyAndroid.release, + ) + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidUnitTestDebug, + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, SSetsKmp.LegacyAndroid.commonMain, + SSetsKmp.LegacyAndroid.debug, SSetsKmp.LegacyAndroid.androidDebug, + SSetsKmp.LegacyAndroid.commonTest, + extended = listOf( + SSetsKmp.LegacyAndroid.test, + SSetsKmp.LegacyAndroid.androidUnitTest, + SSetsKmp.LegacyAndroid.testDebug, + SSetsKmp.LegacyAndroid.testFixtures, + SSetsKmp.LegacyAndroid.testFixturesDebug, + ) + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidUnitTestRelease, + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, SSetsKmp.LegacyAndroid.commonMain, + SSetsKmp.LegacyAndroid.release, SSetsKmp.LegacyAndroid.androidRelease, + SSetsKmp.LegacyAndroid.commonTest, + extended = listOf( + SSetsKmp.LegacyAndroid.test, + SSetsKmp.LegacyAndroid.androidUnitTest, + SSetsKmp.LegacyAndroid.testRelease, + SSetsKmp.LegacyAndroid.testFixtures, + SSetsKmp.LegacyAndroid.testFixturesRelease, + ) + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidInstrumentedTestDebug, + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, SSetsKmp.LegacyAndroid.commonMain, + SSetsKmp.LegacyAndroid.debug, SSetsKmp.LegacyAndroid.androidDebug, + SSetsKmp.LegacyAndroid.commonTest, + extended = listOf( + SSetsKmp.LegacyAndroid.androidTest, + SSetsKmp.LegacyAndroid.androidInstrumentedTest, + SSetsKmp.LegacyAndroid.androidTestDebug, + SSetsKmp.LegacyAndroid.testFixtures, + SSetsKmp.LegacyAndroid.testFixturesDebug, + ) + ) + } + + @TestFactory + fun `KMP Hierarchy Legacy Android Not Wired`() = runGrpcTest { + runAndCheckFiles( + SSetsKmp.LegacyAndroid.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.commonTest, + SSetsKmp.LegacyAndroid.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.jvmMain, + SSetsKmp.LegacyAndroid.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.jvmTest, + SSetsKmp.LegacyAndroid.commonMain, SSetsKmp.LegacyAndroid.jvmMain, + SSetsKmp.LegacyAndroid.commonTest, + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidDebug, + SSetsKmp.LegacyAndroid.commonMain, + extended = listOf( + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, + SSetsKmp.LegacyAndroid.debug, + ), + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidRelease, + SSetsKmp.LegacyAndroid.commonMain, + extended = listOf( + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, + SSetsKmp.LegacyAndroid.release, + ) + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidUnitTestDebug, + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, SSetsKmp.LegacyAndroid.commonMain, + SSetsKmp.LegacyAndroid.debug, SSetsKmp.LegacyAndroid.androidDebug, + SSetsKmp.LegacyAndroid.commonTest, + extended = listOf( + SSetsKmp.LegacyAndroid.test, + SSetsKmp.LegacyAndroid.androidUnitTest, + SSetsKmp.LegacyAndroid.testDebug, + SSetsKmp.LegacyAndroid.testFixtures, + SSetsKmp.LegacyAndroid.testFixturesDebug, + ) + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidUnitTestRelease, + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, SSetsKmp.LegacyAndroid.commonMain, + SSetsKmp.LegacyAndroid.release, SSetsKmp.LegacyAndroid.androidRelease, + SSetsKmp.LegacyAndroid.commonTest, + extended = listOf( + SSetsKmp.LegacyAndroid.test, + SSetsKmp.LegacyAndroid.androidUnitTest, + SSetsKmp.LegacyAndroid.testRelease, + SSetsKmp.LegacyAndroid.testFixtures, + SSetsKmp.LegacyAndroid.testFixturesRelease, + ) + ) + + runAndCheckFiles( + SSetsKmp.LegacyAndroid.androidInstrumentedTestDebug, + SSetsKmp.LegacyAndroid.main, SSetsKmp.LegacyAndroid.androidMain, SSetsKmp.LegacyAndroid.commonMain, + SSetsKmp.LegacyAndroid.debug, SSetsKmp.LegacyAndroid.androidDebug, + extended = listOf( + SSetsKmp.LegacyAndroid.androidTest, + SSetsKmp.LegacyAndroid.androidInstrumentedTest, + SSetsKmp.LegacyAndroid.androidTestDebug, + SSetsKmp.LegacyAndroid.testFixtures, + SSetsKmp.LegacyAndroid.testFixturesDebug, + ) ) } @TestFactory fun `Proto Tasks Are Cached Properly`() = runGrpcTest { - val firstRunCommonMain = runForSet(SSetsKmp.commonMain) + val firstRunCommonMain = runForSet(SSetsKmp.Default.commonMain) firstRunCommonMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -209,26 +501,26 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - firstRunCommonMain.assertOutcomes(SSetsKmp.commonTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.nativeMain) - firstRunCommonMain.assertOutcomes(SSetsKmp.nativeTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.jvmMain) - firstRunCommonMain.assertOutcomes(SSetsKmp.jvmTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.webMain) - firstRunCommonMain.assertOutcomes(SSetsKmp.webTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.jsMain) - firstRunCommonMain.assertOutcomes(SSetsKmp.jsTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.appleMain) - firstRunCommonMain.assertOutcomes(SSetsKmp.appleTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.macosMain) - firstRunCommonMain.assertOutcomes(SSetsKmp.macosTest) - firstRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Main) - firstRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Test) - - val secondRunCommonMain = runForSet(SSetsKmp.commonMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.commonTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.nativeMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.nativeTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.jvmMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.jvmTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.webMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.webTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.jsMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.jsTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.appleMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.appleTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.macosMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.macosTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.macosArm64Main) + firstRunCommonMain.assertOutcomes(SSetsKmp.Default.macosArm64Test) + + val secondRunCommonMain = runForSet(SSetsKmp.Default.commonMain) secondRunCommonMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -238,10 +530,10 @@ class GrpcKmpProjectTest : GrpcBaseTest() { cleanProtoBuildDir() - val thirdRunCommonMain = runForSet(SSetsKmp.commonMain) + val thirdRunCommonMain = runForSet(SSetsKmp.Default.commonMain) thirdRunCommonMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -249,14 +541,14 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.NO_SOURCE, ) - SSetsKmp.commonMain.sourceDir() + SSetsKmp.Default.commonMain.sourceDir() .resolve("commonMain.proto") .replace("content = 1", "content = 2") - val fourthRunCommonMain = runForSet(SSetsKmp.commonMain) + val fourthRunCommonMain = runForSet(SSetsKmp.Default.commonMain) fourthRunCommonMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -264,10 +556,10 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.NO_SOURCE, ) - val firstRunMacosArm64Main = runForSet(SSetsKmp.macosArm64Main) + val firstRunMacosArm64Main = runForSet(SSetsKmp.Default.macosArm64Main) firstRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -276,7 +568,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.nativeMain, + sourceSet = SSetsKmp.Default.nativeMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -285,7 +577,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.appleMain, + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -294,7 +586,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.macosMain, + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -303,7 +595,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.macosArm64Main, + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -312,21 +604,21 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.nativeTest) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmMain) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmTest) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.webMain) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.webTest) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jsMain) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.jsTest) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.appleTest) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.macosTest) - firstRunMacosArm64Main.assertOutcomes(SSetsKmp.macosArm64Test) - - val firstRunMacosArm64Test = runForSet(SSetsKmp.macosArm64Test) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.nativeTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jvmMain) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jvmTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.webMain) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.webTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jsMain) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jsTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.appleTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.macosTest) + firstRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.macosArm64Test) + + val firstRunMacosArm64Test = runForSet(SSetsKmp.Default.macosArm64Test) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -335,7 +627,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.nativeMain, + sourceSet = SSetsKmp.Default.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -344,7 +636,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.appleMain, + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -353,7 +645,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosMain, + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -362,7 +654,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosArm64Main, + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -371,7 +663,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.nativeTest, + sourceSet = SSetsKmp.Default.nativeTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -380,7 +672,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.appleTest, + sourceSet = SSetsKmp.Default.appleTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -389,7 +681,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosTest, + sourceSet = SSetsKmp.Default.macosTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -398,7 +690,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosArm64Test, + sourceSet = SSetsKmp.Default.macosArm64Test, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -407,21 +699,21 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmMain) - firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmTest) - firstRunMacosArm64Test.assertOutcomes(SSetsKmp.webMain) - firstRunMacosArm64Test.assertOutcomes(SSetsKmp.webTest) - firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jsMain) - firstRunMacosArm64Test.assertOutcomes(SSetsKmp.jsTest) - - SSetsKmp.macosMain.sourceDir() + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jvmMain) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jvmTest) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.webMain) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.webTest) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jsMain) + firstRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jsTest) + + SSetsKmp.Default.macosMain.sourceDir() .resolve("macosMain.proto") .replace("content = 1", "content = 2") - val fifthRunCommonMain = runForSet(SSetsKmp.commonMain) + val fifthRunCommonMain = runForSet(SSetsKmp.Default.commonMain) fifthRunCommonMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -430,26 +722,26 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - fifthRunCommonMain.assertOutcomes(SSetsKmp.commonTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.nativeMain) - fifthRunCommonMain.assertOutcomes(SSetsKmp.nativeTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.jvmMain) - fifthRunCommonMain.assertOutcomes(SSetsKmp.jvmTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.webMain) - fifthRunCommonMain.assertOutcomes(SSetsKmp.webTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.jsMain) - fifthRunCommonMain.assertOutcomes(SSetsKmp.jsTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.appleMain) - fifthRunCommonMain.assertOutcomes(SSetsKmp.appleTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.macosMain) - fifthRunCommonMain.assertOutcomes(SSetsKmp.macosTest) - fifthRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Main) - fifthRunCommonMain.assertOutcomes(SSetsKmp.macosArm64Test) - - val secondRunMacosArm64Main = runForSet(SSetsKmp.macosArm64Main) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.commonTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.nativeMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.nativeTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.jvmMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.jvmTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.webMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.webTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.jsMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.jsTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.appleMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.appleTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.macosMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.macosTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.macosArm64Main) + fifthRunCommonMain.assertOutcomes(SSetsKmp.Default.macosArm64Test) + + val secondRunMacosArm64Main = runForSet(SSetsKmp.Default.macosArm64Main) secondRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -458,7 +750,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.nativeMain, + sourceSet = SSetsKmp.Default.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -467,7 +759,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.appleMain, + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -476,7 +768,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.macosMain, + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -485,7 +777,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Main.assertOutcomes( - sourceSet = SSetsKmp.macosArm64Main, + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -494,21 +786,21 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.nativeTest) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmMain) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jvmTest) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.webMain) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.webTest) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jsMain) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.jsTest) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.appleTest) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.macosTest) - secondRunMacosArm64Main.assertOutcomes(SSetsKmp.macosArm64Test) - - val secondRunMacosArm64Test = runForSet(SSetsKmp.macosArm64Test) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.nativeTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jvmMain) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jvmTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.webMain) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.webTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jsMain) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.jsTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.appleTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.macosTest) + secondRunMacosArm64Main.assertOutcomes(SSetsKmp.Default.macosArm64Test) + + val secondRunMacosArm64Test = runForSet(SSetsKmp.Default.macosArm64Test) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -517,7 +809,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.nativeMain, + sourceSet = SSetsKmp.Default.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -526,7 +818,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.appleMain, + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -535,7 +827,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosMain, + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -544,7 +836,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosArm64Main, + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -553,7 +845,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.nativeTest, + sourceSet = SSetsKmp.Default.nativeTest, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -562,7 +854,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.appleTest, + sourceSet = SSetsKmp.Default.appleTest, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -571,7 +863,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosTest, + sourceSet = SSetsKmp.Default.macosTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -580,7 +872,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunMacosArm64Test.assertOutcomes( - sourceSet = SSetsKmp.macosArm64Test, + sourceSet = SSetsKmp.Default.macosArm64Test, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -589,17 +881,17 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmMain) - secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jvmTest) - secondRunMacosArm64Test.assertOutcomes(SSetsKmp.webMain) - secondRunMacosArm64Test.assertOutcomes(SSetsKmp.webTest) - secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jsMain) - secondRunMacosArm64Test.assertOutcomes(SSetsKmp.jsTest) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jvmMain) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jvmTest) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.webMain) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.webTest) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jsMain) + secondRunMacosArm64Test.assertOutcomes(SSetsKmp.Default.jsTest) - val firstRunJvmMain = runForSet(SSetsKmp.jvmMain) + val firstRunJvmMain = runForSet(SSetsKmp.Default.jvmMain) firstRunJvmMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -608,7 +900,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) firstRunJvmMain.assertOutcomes( - sourceSet = SSetsKmp.jvmMain, + sourceSet = SSetsKmp.Default.jvmMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -617,29 +909,29 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - firstRunJvmMain.assertOutcomes(SSetsKmp.commonTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.nativeMain) - firstRunJvmMain.assertOutcomes(SSetsKmp.nativeTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.jvmTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.webMain) - firstRunJvmMain.assertOutcomes(SSetsKmp.webTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.jsMain) - firstRunJvmMain.assertOutcomes(SSetsKmp.jsTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.appleMain) - firstRunJvmMain.assertOutcomes(SSetsKmp.appleTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.macosMain) - firstRunJvmMain.assertOutcomes(SSetsKmp.macosTest) - firstRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Main) - firstRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Test) - - SSetsKmp.jvmMain.sourceDir() + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.commonTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.nativeMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.nativeTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.jvmTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.webMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.webTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.jsMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.jsTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.appleMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.appleTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.macosMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.macosTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.macosArm64Main) + firstRunJvmMain.assertOutcomes(SSetsKmp.Default.macosArm64Test) + + SSetsKmp.Default.jvmMain.sourceDir() .resolve("jvmMain.proto") .replace("content = 1", "content = 2") - val secondRunJvmMain = runForSet(SSetsKmp.jvmMain) + val secondRunJvmMain = runForSet(SSetsKmp.Default.jvmMain) secondRunJvmMain.assertOutcomes( - sourceSet = SSetsKmp.commonMain, + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -648,7 +940,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) secondRunJvmMain.assertOutcomes( - sourceSet = SSetsKmp.jvmMain, + sourceSet = SSetsKmp.Default.jvmMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -657,24 +949,547 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) // didn't run - secondRunJvmMain.assertOutcomes(SSetsKmp.commonTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.nativeMain) - secondRunJvmMain.assertOutcomes(SSetsKmp.nativeTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.jvmTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.webMain) - secondRunJvmMain.assertOutcomes(SSetsKmp.webTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.jsMain) - secondRunJvmMain.assertOutcomes(SSetsKmp.jsTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.appleMain) - secondRunJvmMain.assertOutcomes(SSetsKmp.appleTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.macosMain) - secondRunJvmMain.assertOutcomes(SSetsKmp.macosTest) - secondRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Main) - secondRunJvmMain.assertOutcomes(SSetsKmp.macosArm64Test) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.commonTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.nativeMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.nativeTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.jvmTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.webMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.webTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.jsMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.jsTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.appleMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.appleTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.macosMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.macosTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.macosArm64Main) + secondRunJvmMain.assertOutcomes(SSetsKmp.Default.macosArm64Test) + } + + @TestFactory + fun `Proto Tasks Are Cached Properly Android KMP Library`() = runGrpcTest( + versionsPredicate = { testTestsForAndroidKmpLibExist() } + ) { + val firstRunCommonMain = runForSet(SSetsKmp.AndroidKmpLib.commonMain) + + firstRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + // didn't run + firstRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.commonTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.jvmMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.jvmTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidHostTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidDeviceTest) + + val secondRunCommonMain = runForSet(SSetsKmp.AndroidKmpLib.commonMain) + + secondRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + cleanProtoBuildDir() + + val thirdRunCommonMain = runForSet(SSetsKmp.AndroidKmpLib.commonMain) + + thirdRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + SSetsKmp.AndroidKmpLib.commonMain.sourceDir() + .resolve("commonMain.proto") + .replace("content = 1", "content = 2") + + val fourthRunCommonMain = runForSet(SSetsKmp.AndroidKmpLib.commonMain) + + fourthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + SSetsKmp.AndroidKmpLib.androidMain.sourceDir() + .resolve("androidMain.proto") + .replace("content = 1", "content = 2") + + val fifthRunCommonMain = runForSet(SSetsKmp.AndroidKmpLib.commonMain) + + fifthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + // didn't run + fifthRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.commonTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.jvmMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.jvmTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidHostTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidDeviceTest) + + val firstRunAndroidMain = runForSet(SSetsKmp.AndroidKmpLib.androidMain) + + firstRunAndroidMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.androidMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + val firstRunAndroidHostTest = runForSet(SSetsKmp.AndroidKmpLib.androidHostTest) + + firstRunAndroidHostTest.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.androidMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + firstRunAndroidHostTest.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.androidHostTest, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + SSetsKmp.AndroidKmpLib.androidHostTest.sourceDir() + .resolve("androidHostTest.proto") + .replace("content = 1", "content = 2") + + val secondRunAndroidHostTest = runForSet(SSetsKmp.AndroidKmpLib.androidHostTest) + + secondRunAndroidHostTest.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.androidMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + secondRunAndroidHostTest.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.androidHostTest, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + val firstRunJvmMain = runForSet(SSetsKmp.AndroidKmpLib.jvmMain) + + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.jvmMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + firstRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.commonTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.jvmTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidMain) + firstRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidHostTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidDeviceTest) + + SSetsKmp.AndroidKmpLib.jvmMain.sourceDir() + .resolve("jvmMain.proto") + .replace("content = 1", "content = 2") + + val secondRunJvmMain = runForSet(SSetsKmp.AndroidKmpLib.jvmMain) + + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.AndroidKmpLib.jvmMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + // didn't run + secondRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.commonTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.jvmTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidMain) + secondRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidHostTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.AndroidKmpLib.androidDeviceTest) + } + + @TestFactory + fun `Proto Tasks Are Cached Properly Legacy Android`() = runGrpcTest { + val firstRunCommonMain = runForSet(SSetsKmp.LegacyAndroid.commonMain) + + firstRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + // didn't run + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.commonTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.jvmMain) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.jvmTest) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidDebug) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidRelease) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestRelease) + firstRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidInstrumentedTestDebug) + + val secondRunCommonMain = runForSet(SSetsKmp.LegacyAndroid.commonMain) + + secondRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + cleanProtoBuildDir() + + val thirdRunCommonMain = runForSet(SSetsKmp.LegacyAndroid.commonMain) + + thirdRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + SSetsKmp.LegacyAndroid.commonMain.sourceDir() + .resolve("commonMain.proto") + .replace("content = 1", "content = 2") + + val fourthRunCommonMain = runForSet(SSetsKmp.LegacyAndroid.commonMain) + + fourthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + SSetsKmp.LegacyAndroid.androidMain.sourceDir() + .resolve("androidMain.proto") + .replace("content = 1", "content = 2") + + val fifthRunCommonMain = runForSet(SSetsKmp.LegacyAndroid.commonMain) + + fifthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + // didn't run + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.commonTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.jvmMain) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.jvmTest) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidDebug) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidRelease) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestRelease) + fifthRunCommonMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidInstrumentedTestDebug) + + val firstRunAndroidDebug = runForSet(SSetsKmp.LegacyAndroid.androidDebug) + + firstRunAndroidDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + SSetsKmp.LegacyAndroid.main.sourceDir() + .resolve("main.proto") + .replace("content = 1", "content = 2") + + val secondRunAndroidDebug = runForSet(SSetsKmp.LegacyAndroid.androidDebug) + + secondRunAndroidDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + SSetsKmp.LegacyAndroid.debug.sourceDir() + .resolve("debug.proto") + .replace("content = 1", "content = 2") + + val thirdRunAndroidDebug = runForSet(SSetsKmp.LegacyAndroid.androidDebug) + + thirdRunAndroidDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + val firstRunAndroidUnitTestDebug = runForSet(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + + firstRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + firstRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidUnitTestDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + SSetsKmp.LegacyAndroid.androidUnitTestDebug.sourceDir() + .resolve("androidUnitTestDebug.proto") + .replace("content = 1", "content = 2") + + val secondRunAndroidUnitTestDebug = runForSet(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + + secondRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + secondRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidUnitTestDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + SSetsKmp.LegacyAndroid.testFixtures.sourceDir() + .resolve("testFixtures.proto") + .replace("content = 1", "content = 2") + + val thirdRunAndroidUnitTestDebug = runForSet(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + + thirdRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + thirdRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidUnitTestDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + SSetsKmp.LegacyAndroid.testFixturesRelease.sourceDir() + .resolve("testFixturesRelease.proto") + .replace("content = 1", "content = 2") + + val fourthRunAndroidUnitTestDebug = runForSet(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + + fourthRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + fourthRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidUnitTestDebug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + SSetsKmp.LegacyAndroid.test.sourceDir() + .resolve("test.proto") + .replace("content = 1", "content = 2") + + val fifthRunAndroidUnitTestDebug = runForSet(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + + fifthRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidDebug, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + fifthRunAndroidUnitTestDebug.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.androidUnitTestDebug, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + val firstRunJvmMain = runForSet(SSetsKmp.LegacyAndroid.jvmMain) + + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.jvmMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.commonTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.jvmTest) + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidDebug) + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidRelease) + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestRelease) + firstRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidInstrumentedTestDebug) + + SSetsKmp.LegacyAndroid.jvmMain.sourceDir() + .resolve("jvmMain.proto") + .replace("content = 1", "content = 2") + + val secondRunJvmMain = runForSet(SSetsKmp.LegacyAndroid.jvmMain) + + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.commonMain, + generate = TaskOutcome.UP_TO_DATE, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.NO_SOURCE, + ) + + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.LegacyAndroid.jvmMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + // didn't run + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.commonTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.jvmTest) + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidDebug) + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidRelease) + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestDebug) + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidUnitTestRelease) + secondRunJvmMain.assertOutcomes(SSetsKmp.LegacyAndroid.androidInstrumentedTestDebug) } @TestFactory fun `Buf Tasks`() = runGrpcTest { runGradle("test_tasks", "--no-configuration-cache") } + + @TestFactory + fun `Buf Tasks Android Kmp Library`() = runGrpcTest { + runGradle("test_tasks", "--no-configuration-cache") + } + + @TestFactory + fun `Buf Tasks Android Kmp Library With Test Tasks`() = runGrpcTest( + versionsPredicate = { testTestsForAndroidKmpLibExist() } + ) { + runGradle("test_tasks", "--no-configuration-cache") + } + + @TestFactory + fun `Buf Tasks Legacy Android`() = runGrpcTest { + runGradle("test_tasks", "--no-configuration-cache") + } } diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt index e4a5cfd4a..9761bf4e5 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -35,6 +35,11 @@ class VersionsEnv( val (major, minor, patch) = kotlin.split(".").map { it.toInt() } KotlinVersion(major, minor, patch) } + + val androidSemver = run { + val (major, minor, patch) = android.split(".").map { it.toInt() } + KotlinVersion(major, minor, patch) + } } internal object KtVersion { @@ -42,20 +47,31 @@ internal object KtVersion { val v2_0_0 = KotlinVersion(2, 0, 0) } +typealias VersionsPredicate = VersionsEnv.() -> Boolean + +internal fun VersionsEnv.testTestsForAndroidKmpLibExist(): Boolean { + return androidSemver.isAtLeast(8, 10, 0) +} + private val GradleVersions = listOf( VersionsEnv("9.2.1", "2.2.21", "8.13.1"), - VersionsEnv("8.14.1", "2.2.0", "8.6.1"), + VersionsEnv("8.14.1", "2.2.0", "8.10.0"), VersionsEnv("8.8", "2.0.0", "8.4.0"), ) -internal fun BaseTest.runWithAllGradleVersions(body: (VersionsEnv) -> Unit): Stream { - return GradleVersions.stream().map { - setupTest(it) - - DynamicTest.dynamicTest("Gradle ${it.gradle}, Kotlin ${it.kotlin}") { - body(it) +internal fun BaseTest.runWithGradleVersions( + predicate: VersionsPredicate = { true }, + body: (VersionsEnv) -> Unit, +): Stream { + return GradleVersions.stream() + .filter { predicate(it) } + .map { versions -> + setupTest(versions) + + DynamicTest.dynamicTest("Gradle ${versions.gradle}, KGP ${versions.kotlin}, AGP ${versions.android}") { + body(versions) + } } - } } @OptIn(ExperimentalPathApi::class) @@ -65,6 +81,7 @@ abstract class BaseTest { private lateinit var testMethodName: String private lateinit var baseDir: Path private lateinit var projectDir: Path + private lateinit var testKitDir: Path @BeforeEach protected fun setup(testInfo: TestInfo) { @@ -84,7 +101,10 @@ abstract class BaseTest { } fun setupTest(versions: VersionsEnv) { - val versioned = baseDir.resolve(versions.gradle.replace(".", "_")) + val gradleDir = versions.gradle.replace(".", "_") + testKitDir = TEST_KIT_PATH.resolve(gradleDir) + + val versioned = baseDir.resolve(gradleDir) projectDir = versioned.resolve(PROJECT_DIR) val buildCacheDir = versioned.resolve(BUILD_CACHE_DIR) @@ -128,11 +148,13 @@ abstract class BaseTest { settingsFile.appendText("\ninclude(\":$project\")") } - println(""" + println( + """ Setup project '$projectName' - in directory: ${projectDir.absolutePathString()} - from directory: ${testTemplateDirectory.absolutePathString()} - """.trimIndent()) + """.trimIndent() + ) } private fun runGradleInternal( @@ -143,8 +165,9 @@ abstract class BaseTest { ): BuildResult { val gradleRunner = GradleRunner.create() .withProjectDir(projectDir.absolute().toFile()) - .withTestKitDir(TEST_KIT_PATH.absolute().toFile()) + .withTestKitDir(testKitDir.absolute().toFile()) .withGradleVersion(versions.gradle) + .forwardOutput() .withArguments( listOfNotNull( task, @@ -153,28 +176,14 @@ abstract class BaseTest { "-Dorg.gradle.kotlin.dsl.scriptCompilationAvoidance=false", *args, ) - ).apply { - if (forwardOutput) { - forwardOutput() - } - } + ) println("Running Gradle task '$task' with arguments: [${args.joinToString()}]") return gradleRunner.body() } protected fun runTest(testEnv: T, body: T.() -> Unit) { - try { - testEnv.body() - } finally { - val output = testEnv.latestBuild?.output - if (output != null) { - println("Latest gradle build output:") - println(output) - } else { - println("No gradle build output available") - } - } + testEnv.body() } open inner class TestEnv( @@ -221,9 +230,6 @@ abstract class BaseTest { } companion object { - private val forwardOutput = System.getProperty("gradle.test.forward.output") - ?.toBooleanStrictOrNull() ?: false - private val nameRegex = Regex("[ .,-]") private val TEST_PROJECTS_PATH = Path.of("build", "gradle-test") diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt index 86d1bd89d..e90a4aa0c 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -33,7 +33,10 @@ abstract class GrpcBaseTest : BaseTest() { runNonExistentTask(generateBufGenYaml(set)) } - protected fun runGrpcTest(test: GrpcTestEnv.() -> Unit): Stream = runWithAllGradleVersions { + protected fun runGrpcTest( + versionsPredicate: VersionsPredicate = { true }, + test: GrpcTestEnv.() -> Unit, + ): Stream = runWithGradleVersions(versionsPredicate) { runTest(GrpcTestEnv(it), test) } @@ -148,7 +151,7 @@ abstract class GrpcBaseTest : BaseTest() { protoSources.walk().forEach { val pathString = it.relativeTo(protoSources).pathString if (it.isRegularFile() && it.extension == "proto" && pathString !in included) { - fail("File '${it}' in '$protoSources' is not expected") + fail("File '${it.relativeTo(protoSources)}' in '$protoSources' is not expected") } } } @@ -376,13 +379,13 @@ abstract class GrpcBaseTest : BaseTest() { fun generateBufGenYaml(sourceSet: SSets) = "generateBufGenYaml${sourceSet.capital}" val mainSourceSet: SSets = when (type) { - Type.Kmp -> SSetsKmp.commonMain + Type.Kmp -> SSetsKmp.Default.commonMain Type.Jvm -> SSetsJvm.main Type.Android -> SSetsAndroid.Default.main } val testSourceSet: SSets = when (type) { - Type.Kmp -> SSetsKmp.commonTest + Type.Kmp -> SSetsKmp.Default.commonTest Type.Jvm -> SSetsJvm.test Type.Android -> SSetsAndroid.Default.test } @@ -454,32 +457,74 @@ abstract class GrpcBaseTest : BaseTest() { } } - enum class SSetsKmp(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSets { - commonMain, commonTest, - jvmMain, jvmTest, - androidMain, androidTest, - webMain(KtVersion.v2_2_20), webTest(KtVersion.v2_2_20), - jsMain, jsTest, - nativeMain, nativeTest, - appleMain, appleTest, - macosMain, macosTest, - macosArm64Main, macosArm64Test, - ; + sealed interface SSetsKmp : SSets { + enum class Default(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsKmp { + commonMain, commonTest, + jvmMain, jvmTest, + webMain(KtVersion.v2_2_20), webTest(KtVersion.v2_2_20), + jsMain, jsTest, + nativeMain, nativeTest, + appleMain, appleTest, + macosMain, macosTest, + macosArm64Main, macosArm64Test, + ; - override fun all(): List { - return entries + override fun all(): List { + return entries + } + } + + enum class AndroidKmpLib(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsKmp { + commonMain, commonTest, + jvmMain, jvmTest, + androidMain, androidHostTest, androidDeviceTest, + ; + + override fun all(): List { + return entries + } + } + + enum class LegacyAndroid(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsKmp { + commonMain, commonTest, + jvmMain, jvmTest, + + // kmp, non-executable + androidMain, + androidUnitTest, androidInstrumentedTest, + + // kmp, executable + androidDebug, androidRelease, + androidUnitTestDebug, androidUnitTestRelease, + androidInstrumentedTestDebug, + + // legacy, non-executable + main, test, + testFixtures, testFixturesDebug, testFixturesRelease, + androidTest, + + debug, release, + testDebug, testRelease, + androidTestDebug, + ; + + override fun all(): List { + return entries + } } } sealed interface SSetsAndroid : SSets { enum class Default(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsAndroid { + // non-executable main, test, - androidTest, testFixtures, + testFixtures, testFixturesDebug, testFixturesRelease, + androidTest, + // executable debug, release, - androidTestDebug, - testFixturesDebug, testFixturesRelease, testDebug, testRelease, + androidTestDebug, ; override fun all(): List { diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts index 6d18202ac..1f465b5b2 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts @@ -2,12 +2,12 @@ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import kotlinx.rpc.buf.* +import kotlinx.rpc.buf.tasks.* +import kotlinx.rpc.protoc.* import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.kotlin.dsl.version -import kotlinx.rpc.buf.tasks.* -import kotlinx.rpc.buf.* -import kotlinx.rpc.protoc.* import javax.inject.Inject plugins { @@ -43,11 +43,18 @@ fun Iterable.toNames() = map { it.name }.toSet() fun assertTasks( tag: String, tasks: Iterable, - vararg expected: String, + vararg expected: String?, ) { val names = tasks.toNames() - if (expected.toSet() != names) { - throw GradleException("[$tag] Expected: ${expected.toSet()}, actual: $names") + val expectedSet = expected.filterNotNull().toSet() + if (expectedSet != names) { + throw GradleException( + """ + [$tag] Expected: ${expectedSet}, actual: $names + Missing: ${expectedSet - names} + Extra: ${names - expectedSet} + """.trimIndent() + ) } } @@ -89,8 +96,32 @@ tasks.register("test_tasks") { ) assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) - assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) - assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) + + assertTasks( + "matchingSourceSet main", genTasks.matchingSourceSet("main"), + "bufGenerateDebug", + "bufGenerateRelease", + ) + + assertTasks( + "matchingSourceSet test", genTasks.matchingSourceSet("test"), + "bufGenerateTestDebug", + "bufGenerateTestRelease", + ) + + assertTasks( + "matchingSourceSet androidTest", genTasks.matchingSourceSet("androidTest"), + "bufGenerateAndroidTestDebug", + ) + + assertTasks( + "gen all android", genTasks.androidTasks(), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + "bufGenerateDebug", + "bufGenerateRelease", + ) assertTasks( "matchingSourceSet debug", diff --git a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts index a7cdd0eb0..d4e43d8d8 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts @@ -60,11 +60,18 @@ fun Iterable.toNames() = map { it.name }.toSet() fun assertTasks( tag: String, tasks: Iterable, - vararg expected: String, + vararg expected: String?, ) { val names = tasks.toNames() - if (expected.toSet() != names) { - throw GradleException("[$tag] Expected: ${expected.toSet()}, actual: $names") + val expectedSet = expected.filterNotNull().toSet() + if (expectedSet != names) { + throw GradleException( + """ + [$tag] Expected: ${expectedSet}, actual: $names + Missing: ${expectedSet - names} + Extra: ${names - expectedSet} + """.trimIndent() + ) } } @@ -167,8 +174,113 @@ tasks.register("test_tasks") { ) assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) - assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) - assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) + + assertTasks( + "matchingSourceSet main", genTasks.matchingSourceSet("main"), + + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + ) + + assertTasks( + "matchingSourceSet freeapp", genTasks.matchingSourceSet("freeapp"), + + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + ) + + assertTasks( + "matchingSourceSet arm", genTasks.matchingSourceSet("arm"), + + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + ) + + assertTasks( + "matchingSourceSet x86", genTasks.matchingSourceSet("x86"), + + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + ) + + assertTasks( + "matchingSourceSet test", genTasks.matchingSourceSet("test"), + + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + ) + + assertTasks( + "matchingSourceSet armFreeapp", genTasks.matchingSourceSet("armFreeapp"), + + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + ) + + assertTasks( + "matchingSourceSet androidTest", genTasks.matchingSourceSet("androidTest"), + + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + ) + + assertTasks( + "gen all android", genTasks.androidTasks(), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappDebug", + "bufGenerateX86RetailappRelease", + ) assertTasks( "matchingSourceSet armFreeappDebug", diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts index b502de1b8..c57ba3793 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/buf_tasks/build.gradle.kts @@ -37,11 +37,18 @@ fun Iterable.toNames() = map { it.name }.toSet() fun assertTasks( tag: String, tasks: Iterable, - vararg expected: String, + vararg expected: String?, ) { val names = tasks.toNames() - if (expected.toSet() != names) { - throw GradleException("[$tag] Expected: ${expected.toSet()}, actual: $names") + val expectedSet = expected.filterNotNull().toSet() + if (expectedSet != names) { + throw GradleException( + """ + [$tag] Expected: ${expectedSet}, actual: $names + Missing: ${expectedSet - names} + Extra: ${names - expectedSet} + """.trimIndent() + ) } } diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library/build.gradle.kts new file mode 100644 index 000000000..24f2b2448 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library/build.gradle.kts @@ -0,0 +1,176 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.kotlin.dsl.version +import kotlinx.rpc.buf.tasks.* +import kotlinx.rpc.buf.* +import kotlinx.rpc.protoc.* +import javax.inject.Inject + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.kotlin.multiplatform.library") version "" +} + +kotlin { + jvm() + macosArm64() +} + +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { + init { + command.set("lint") + args.set(emptyList()) + } +} + +rpc { + protoc { + buf { + tasks { + registerWorkspaceTask("lint") + } + } + } +} + +fun Iterable.toNames() = map { it.name }.toSet() + +fun assertTasks( + tag: String, + tasks: Iterable, + vararg expected: String?, +) { + val names = tasks.toNames() + val expectedSet = expected.filterNotNull().toSet() + if (expectedSet != names) { + throw GradleException( + """ + [$tag] Expected: ${expectedSet}, actual: $names + Missing: ${expectedSet - names} + Extra: ${names - expectedSet} + """.trimIndent() + ) + } +} + +tasks.register("test_tasks") { + doLast { + val genTasks = protoTasks.buf.generate + + assertTasks( + "gen all", genTasks, + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + "bufGenerateAndroidMain", + ) + + assertTasks( + "testTasks", genTasks.testTasks(), + "bufGenerateCommonTest", + "bufGenerateNativeTest", + "bufGenerateAppleTest", + "bufGenerateMacosTest", + "bufGenerateMacosArm64Test", + "bufGenerateJvmTest", + ) + + assertTasks( + "nonTestTasks", genTasks.nonTestTasks(), + "bufGenerateCommonMain", + "bufGenerateNativeMain", + "bufGenerateAppleMain", + "bufGenerateMacosMain", + "bufGenerateMacosArm64Main", + "bufGenerateJvmMain", + "bufGenerateAndroidMain", + ) + + assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) + assertTasks("unit test tasks", genTasks.androidUnitTestTasks()) + assertTasks("instrumented test tasks", genTasks.androidInstrumentedTestTasks()) + + assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) + assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) + + assertTasks( + "gen all android", genTasks.androidTasks(), + "bufGenerateAndroidMain", + ) + + assertTasks( + "gen all android non test", genTasks.androidTasks().nonTestTasks(), + "bufGenerateAndroidMain", + ) + + assertTasks( + "matchingAndroidFlavor null", genTasks.matchingAndroidFlavor(null), + "bufGenerateAndroidMain", + ) + + assertTasks( + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType(null), + "bufGenerateAndroidMain", + ) + + assertTasks( + "matchingAndroidVariant debug", genTasks.matchingAndroidVariant(null), + "bufGenerateAndroidMain", + ) + + + val allTasks = protoTasks.buf + + assertTasks( + "all", allTasks, + // generate + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + "bufGenerateAndroidMain", + + // lint + "bufLintCommonMain", "bufLintCommonTest", + "bufLintNativeMain", "bufLintNativeTest", + "bufLintAppleMain", "bufLintAppleTest", + "bufLintMacosMain", "bufLintMacosTest", + "bufLintMacosArm64Main", "bufLintMacosArm64Test", + "bufLintJvmMain", "bufLintJvmTest", + "bufLintAndroidMain", + ) + + assertTasks( + "all by type generate", + allTasks.matchingType(), + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + "bufGenerateAndroidMain", + ) + + assertTasks( + "all by type lint", allTasks.matchingType(), + "bufLintCommonMain", "bufLintCommonTest", + "bufLintNativeMain", "bufLintNativeTest", + "bufLintAppleMain", "bufLintAppleTest", + "bufLintMacosMain", "bufLintMacosTest", + "bufLintMacosArm64Main", "bufLintMacosArm64Test", + "bufLintJvmMain", "bufLintJvmTest", + "bufLintAndroidMain", + ) + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library_with_test_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library_with_test_tasks/build.gradle.kts new file mode 100644 index 000000000..bf0823211 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_android_kmp_library_with_test_tasks/build.gradle.kts @@ -0,0 +1,202 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import kotlinx.rpc.buf.* +import kotlinx.rpc.buf.tasks.* +import kotlinx.rpc.protoc.* +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.kotlin.dsl.version +import javax.inject.Inject + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.kotlin.multiplatform.library") version "" +} + +kotlin { + jvm() + macosArm64() + + androidLibrary { + namespace = "com.example.namespace" + compileSdk = 34 + + withDeviceTestBuilder { + sourceSetTreeName = "test" + } + + withHostTestBuilder { + sourceSetTreeName = "test" + } + } +} + +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { + init { + command.set("lint") + args.set(emptyList()) + } +} + +rpc { + protoc { + buf { + tasks { + registerWorkspaceTask("lint") + } + } + } +} + +fun Iterable.toNames() = map { it.name }.toSet() + +fun assertTasks( + tag: String, + tasks: Iterable, + vararg expected: String?, +) { + val names = tasks.toNames() + val expectedSet = expected.filterNotNull().toSet() + if (expectedSet != names) { + throw GradleException( + """ + [$tag] Expected: ${expectedSet}, actual: $names + Missing: ${expectedSet - names} + Extra: ${names - expectedSet} + """.trimIndent() + ) + } +} + +tasks.register("test_tasks") { + doLast { + val genTasks = protoTasks.buf.generate + + assertTasks( + "gen all", genTasks, + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "testTasks", genTasks.testTasks(), + "bufGenerateCommonTest", + "bufGenerateNativeTest", + "bufGenerateAppleTest", + "bufGenerateMacosTest", + "bufGenerateMacosArm64Test", + "bufGenerateJvmTest", + "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "nonTestTasks", genTasks.nonTestTasks(), + "bufGenerateCommonMain", + "bufGenerateNativeMain", + "bufGenerateAppleMain", + "bufGenerateMacosMain", + "bufGenerateMacosArm64Main", + "bufGenerateJvmMain", + "bufGenerateAndroidMain", + ) + + assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) + + assertTasks( + "unit test tasks", genTasks.androidUnitTestTasks(), + "bufGenerateAndroidHostTest", + ) + + assertTasks( + "instrumented test tasks", genTasks.androidInstrumentedTestTasks(), + "bufGenerateAndroidDeviceTest", + ) + + assertTasks("matchingSourceSet main", genTasks.matchingSourceSet("main")) + assertTasks("matchingSourceSet test", genTasks.matchingSourceSet("test")) + + assertTasks( + "gen all android", genTasks.androidTasks(), + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "gen all android non test", genTasks.androidTasks().nonTestTasks(), + "bufGenerateAndroidMain", + ) + + assertTasks( + "gen all android non test", genTasks.androidTasks().testTasks(), + "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "matchingAndroidFlavor null", genTasks.matchingAndroidFlavor(null), + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType(null), + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "matchingAndroidVariant debug", genTasks.matchingAndroidVariant(null), + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + val allTasks = protoTasks.buf + + assertTasks( + "all", allTasks, + // generate + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + + // lint + "bufLintCommonMain", "bufLintCommonTest", + "bufLintNativeMain", "bufLintNativeTest", + "bufLintAppleMain", "bufLintAppleTest", + "bufLintMacosMain", "bufLintMacosTest", + "bufLintMacosArm64Main", "bufLintMacosArm64Test", + "bufLintJvmMain", "bufLintJvmTest", + "bufLintAndroidMain", "bufLintAndroidDeviceTest", "bufLintAndroidHostTest", + ) + + assertTasks( + "all by type generate", + allTasks.matchingType(), + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + "bufGenerateAndroidMain", "bufGenerateAndroidDeviceTest", "bufGenerateAndroidHostTest", + ) + + assertTasks( + "all by type lint", allTasks.matchingType(), + "bufLintCommonMain", "bufLintCommonTest", + "bufLintNativeMain", "bufLintNativeTest", + "bufLintAppleMain", "bufLintAppleTest", + "bufLintMacosMain", "bufLintMacosTest", + "bufLintMacosArm64Main", "bufLintMacosArm64Test", + "bufLintJvmMain", "bufLintJvmTest", + "bufLintAndroidMain", "bufLintAndroidDeviceTest", "bufLintAndroidHostTest", + ) + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_legacy_android/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_legacy_android/build.gradle.kts new file mode 100644 index 000000000..c9bb8b208 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/buf_tasks_legacy_android/build.gradle.kts @@ -0,0 +1,307 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import kotlinx.rpc.buf.* +import kotlinx.rpc.buf.tasks.* +import kotlinx.rpc.protoc.* +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.kotlin.dsl.version +import javax.inject.Inject + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.library") version "" +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} + +kotlin { + jvm() + macosArm64() + androidTarget() +} + +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { + init { + command.set("lint") + args.set(emptyList()) + } +} + +rpc { + protoc { + buf { + tasks { + registerWorkspaceTask("lint") + } + } + } +} + +fun Iterable.toNames() = map { it.name }.toSet() + +fun assertTasks( + tag: String, + tasks: Iterable, + vararg expected: String?, +) { + val names = tasks.toNames() + val expectedSet = expected.filterNotNull().toSet() + if (expectedSet != names) { + throw GradleException( + """ + [$tag] Expected: ${expectedSet}, actual: $names + Missing: ${expectedSet - names} + Extra: ${names - expectedSet} + """.trimIndent() + ) + } +} + +tasks.register("test_tasks") { + doLast { + val genTasks = protoTasks.buf.generate + + assertTasks( + "gen all", genTasks, + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + + // android + "bufGenerateAndroidDebug", + "bufGenerateAndroidRelease", + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "testTasks", genTasks.testTasks(), + "bufGenerateCommonTest", + "bufGenerateNativeTest", + "bufGenerateAppleTest", + "bufGenerateMacosTest", + "bufGenerateMacosArm64Test", + "bufGenerateJvmTest", + + // android + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "nonTestTasks", genTasks.nonTestTasks(), + "bufGenerateCommonMain", + "bufGenerateNativeMain", + "bufGenerateAppleMain", + "bufGenerateMacosMain", + "bufGenerateMacosArm64Main", + "bufGenerateJvmMain", + + // android + "bufGenerateAndroidDebug", + "bufGenerateAndroidRelease", + ) + + assertTasks( + "unit test tasks", genTasks.androidUnitTestTasks(), + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "instrumented test tasks", genTasks.androidInstrumentedTestTasks(), + "bufGenerateAndroidInstrumentedTestDebug", + ) + + assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) + + assertTasks( + "matchingSourceSet main", genTasks.matchingSourceSet("main"), + "bufGenerateAndroidDebug", + "bufGenerateAndroidRelease", + ) + + assertTasks( + "matchingSourceSet test", genTasks.matchingSourceSet("test"), + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "matchingSourceSet androidUnitTest", genTasks.matchingSourceSet("androidUnitTest"), + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "matchingSourceSet androidInstrumentedTest", genTasks.matchingSourceSet("androidInstrumentedTest"), + "bufGenerateAndroidInstrumentedTestDebug", + ) + + assertTasks( + "matchingSourceSet androidTest", genTasks.matchingSourceSet("androidTest"), + "bufGenerateAndroidInstrumentedTestDebug", + ) + + assertTasks( + "gen all android", genTasks.androidTasks(), + "bufGenerateAndroidDebug", + "bufGenerateAndroidRelease", + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "matchingSourceSet debug", + genTasks.matchingSourceSet("debug"), + "bufGenerateAndroidDebug", + ) + + assertTasks( + "matchingSourceSet testDebug", + genTasks.matchingSourceSet("testDebug"), + "bufGenerateAndroidUnitTestDebug", + ) + + assertTasks( + "matchingSourceSet androidTestDebug", + genTasks.matchingSourceSet("androidTestDebug"), + "bufGenerateAndroidInstrumentedTestDebug", + ) + + assertTasks( + "matchingSourceSet androidDebug", + genTasks.matchingSourceSet("androidDebug"), + "bufGenerateAndroidDebug", + ) + + assertTasks( + "matchingSourceSet androidUnitTestDebug", + genTasks.matchingSourceSet("androidUnitTestDebug"), + "bufGenerateAndroidUnitTestDebug", + ) + + assertTasks( + "matchingSourceSet androidInstrumentedTestDebug", + genTasks.matchingSourceSet("androidInstrumentedTestDebug"), + "bufGenerateAndroidInstrumentedTestDebug", + ) + + assertTasks( + "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), + ) + + assertTasks( + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType("debug"), + "bufGenerateAndroidDebug", + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + ) + + assertTasks( + "matchingAndroidBuildType release", genTasks.matchingAndroidBuildType("release"), + "bufGenerateAndroidRelease", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "matchingAndroidVariant debug", genTasks.matchingAndroidVariant("debug"), + "bufGenerateAndroidDebug", + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + ) + + assertTasks( + "matchingAndroidBuildType debug unit tests", genTasks.matchingAndroidBuildType("debug").androidUnitTestTasks(), + "bufGenerateAndroidUnitTestDebug", + ) + + assertTasks( + "matchingAndroidBuildType debug android tests", genTasks.matchingAndroidBuildType("debug").androidInstrumentedTestTasks(), + "bufGenerateAndroidInstrumentedTestDebug", + ) + + val allTasks = protoTasks.buf + + assertTasks( + "all", allTasks, + // generate + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + + // android generate + "bufGenerateAndroidDebug", + "bufGenerateAndroidRelease", + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + + // lint + "bufLintCommonMain", "bufLintCommonTest", + "bufLintNativeMain", "bufLintNativeTest", + "bufLintAppleMain", "bufLintAppleTest", + "bufLintMacosMain", "bufLintMacosTest", + "bufLintMacosArm64Main", "bufLintMacosArm64Test", + "bufLintJvmMain", "bufLintJvmTest", + + // android lint + "bufLintAndroidDebug", + "bufLintAndroidRelease", + "bufLintAndroidInstrumentedTestDebug", + "bufLintAndroidUnitTestDebug", + "bufLintAndroidUnitTestRelease", + ) + + assertTasks( + "all by type generate", + allTasks.matchingType(), + "bufGenerateCommonMain", "bufGenerateCommonTest", + "bufGenerateNativeMain", "bufGenerateNativeTest", + "bufGenerateAppleMain", "bufGenerateAppleTest", + "bufGenerateMacosMain", "bufGenerateMacosTest", + "bufGenerateMacosArm64Main", "bufGenerateMacosArm64Test", + "bufGenerateJvmMain", "bufGenerateJvmTest", + + // android generate + "bufGenerateAndroidDebug", + "bufGenerateAndroidRelease", + "bufGenerateAndroidInstrumentedTestDebug", + "bufGenerateAndroidUnitTestDebug", + "bufGenerateAndroidUnitTestRelease", + ) + + assertTasks( + "all by type lint", allTasks.matchingType(), + "bufLintCommonMain", "bufLintCommonTest", + "bufLintNativeMain", "bufLintNativeTest", + "bufLintAppleMain", "bufLintAppleTest", + "bufLintMacosMain", "bufLintMacosTest", + "bufLintMacosArm64Main", "bufLintMacosArm64Test", + "bufLintJvmMain", "bufLintJvmTest", + + // android lint + "bufLintAndroidDebug", + "bufLintAndroidRelease", + "bufLintAndroidInstrumentedTestDebug", + "bufLintAndroidUnitTestDebug", + "bufLintAndroidUnitTestRelease", + ) + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/build.gradle.kts new file mode 100644 index 000000000..ed854a661 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/build.gradle.kts @@ -0,0 +1,23 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.kotlin.multiplatform.library") version "" +} + +kotlin { + jvm() + androidLibrary { + namespace = "com.example.namespace" + compileSdk = 34 + } +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/build.gradle.kts new file mode 100644 index 000000000..2de54cd46 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/build.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.kotlin.multiplatform.library") version "" +} + +kotlin { + jvm() + androidLibrary { + namespace = "com.example.namespace" + compileSdk = 34 + + withDeviceTestBuilder { + sourceSetTreeName = "test" + } + + withHostTestBuilder { + sourceSetTreeName = "test" + } + } +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidDeviceTest/proto/androidDeviceTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidDeviceTest/proto/androidDeviceTest.proto new file mode 100644 index 000000000..b978df487 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidDeviceTest/proto/androidDeviceTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidDeviceTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidHostTest/proto/androidHostTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidHostTest/proto/androidHostTest.proto new file mode 100644 index 000000000..8a56fde22 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidHostTest/proto/androidHostTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidHostTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/build.gradle.kts new file mode 100644 index 000000000..f79deb9bc --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.kotlin.multiplatform.library") version "" +} + +kotlin { + jvm() + androidLibrary { + namespace = "com.example.namespace" + compileSdk = 34 + + withDeviceTestBuilder { } + + withHostTestBuilder { } + } +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidDeviceTest/proto/androidDeviceTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidDeviceTest/proto/androidDeviceTest.proto new file mode 100644 index 000000000..b978df487 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidDeviceTest/proto/androidDeviceTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidDeviceTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidHostTest/proto/androidHostTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidHostTest/proto/androidHostTest.proto new file mode 100644 index 000000000..8a56fde22 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidHostTest/proto/androidHostTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidHostTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_android_kmp_library_with_test_tasks_not_wired/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/build.gradle.kts new file mode 100644 index 000000000..0f2cfba02 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.application") version "" +} + +kotlin { + jvm() + + androidTarget { + instrumentedTestVariant { + sourceSetTree.set(KotlinSourceSetTree.test) + } + + unitTestVariant { + sourceSetTree.set(KotlinSourceSetTree.test) + } + } +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidDebug/proto/androidDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidDebug/proto/androidDebug.proto new file mode 100644 index 000000000..a4cda85be --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidDebug/proto/androidDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto new file mode 100644 index 000000000..3545f8cd7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidInstrumentedTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto new file mode 100644 index 000000000..35a822595 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidInstrumentedTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidRelease/proto/androidRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidRelease/proto/androidRelease.proto new file mode 100644 index 000000000..86933df39 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidRelease/proto/androidRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTest/proto/androidTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTest/proto/androidTest.proto new file mode 100644 index 000000000..509c020b7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTest/proto/androidTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto new file mode 100644 index 000000000..7e68a56ae --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto new file mode 100644 index 000000000..ee889e075 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto new file mode 100644 index 000000000..1c04f89da --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto new file mode 100644 index 000000000..d6103b2cc --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/debug/proto/debug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/debug/proto/debug.proto new file mode 100644 index 000000000..280702f10 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/debug/proto/debug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Debug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/main/proto/main.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/main/proto/main.proto new file mode 100644 index 000000000..588046d54 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/main/proto/main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/release/proto/release.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/release/proto/release.proto new file mode 100644 index 000000000..c3d9a0bc4 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/release/proto/release.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Release { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/test/proto/test.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/test/proto/test.proto new file mode 100644 index 000000000..51cfb5882 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/test/proto/test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testDebug/proto/testDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testDebug/proto/testDebug.proto new file mode 100644 index 000000000..d3efa4a23 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testDebug/proto/testDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixtures/proto/testFixtures.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixtures/proto/testFixtures.proto new file mode 100644 index 000000000..16a35e7c9 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixtures/proto/testFixtures.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixtures { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto new file mode 100644 index 000000000..d09209564 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto new file mode 100644 index 000000000..090356c1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testRelease/proto/testRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testRelease/proto/testRelease.proto new file mode 100644 index 000000000..bb2b93616 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android/src/testRelease/proto/testRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/build.gradle.kts new file mode 100644 index 000000000..0c8625ff7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/build.gradle.kts @@ -0,0 +1,26 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.application") version "" +} + +kotlin { + jvm() + + androidTarget { } +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidDebug/proto/androidDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidDebug/proto/androidDebug.proto new file mode 100644 index 000000000..a4cda85be --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidDebug/proto/androidDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto new file mode 100644 index 000000000..3545f8cd7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidInstrumentedTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto new file mode 100644 index 000000000..35a822595 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidInstrumentedTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidRelease/proto/androidRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidRelease/proto/androidRelease.proto new file mode 100644 index 000000000..86933df39 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidRelease/proto/androidRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTest/proto/androidTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTest/proto/androidTest.proto new file mode 100644 index 000000000..509c020b7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTest/proto/androidTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTestDebug/proto/androidTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTestDebug/proto/androidTestDebug.proto new file mode 100644 index 000000000..7e68a56ae --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidTestDebug/proto/androidTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTest/proto/androidUnitTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTest/proto/androidUnitTest.proto new file mode 100644 index 000000000..ee889e075 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTest/proto/androidUnitTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto new file mode 100644 index 000000000..1c04f89da --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto new file mode 100644 index 000000000..d6103b2cc --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/debug/proto/debug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/debug/proto/debug.proto new file mode 100644 index 000000000..280702f10 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/debug/proto/debug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Debug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/main/proto/main.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/main/proto/main.proto new file mode 100644 index 000000000..588046d54 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/main/proto/main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/release/proto/release.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/release/proto/release.proto new file mode 100644 index 000000000..c3d9a0bc4 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/release/proto/release.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Release { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/test/proto/test.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/test/proto/test.proto new file mode 100644 index 000000000..51cfb5882 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/test/proto/test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testDebug/proto/testDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testDebug/proto/testDebug.proto new file mode 100644 index 000000000..d3efa4a23 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testDebug/proto/testDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixtures/proto/testFixtures.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixtures/proto/testFixtures.proto new file mode 100644 index 000000000..16a35e7c9 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixtures/proto/testFixtures.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixtures { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesDebug/proto/testFixturesDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesDebug/proto/testFixturesDebug.proto new file mode 100644 index 000000000..d09209564 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesDebug/proto/testFixturesDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesRelease/proto/testFixturesRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesRelease/proto/testFixturesRelease.proto new file mode 100644 index 000000000..090356c1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testFixturesRelease/proto/testFixturesRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testRelease/proto/testRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testRelease/proto/testRelease.proto new file mode 100644 index 000000000..bb2b93616 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy_legacy_android_not_wired/src/testRelease/proto/testRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/build.gradle.kts new file mode 100644 index 000000000..2de54cd46 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/build.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.kotlin.multiplatform.library") version "" +} + +kotlin { + jvm() + androidLibrary { + namespace = "com.example.namespace" + compileSdk = 34 + + withDeviceTestBuilder { + sourceSetTreeName = "test" + } + + withHostTestBuilder { + sourceSetTreeName = "test" + } + } +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidDeviceTest/proto/androidDeviceTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidDeviceTest/proto/androidDeviceTest.proto new file mode 100644 index 000000000..b978df487 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidDeviceTest/proto/androidDeviceTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidDeviceTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidHostTest/proto/androidHostTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidHostTest/proto/androidHostTest.proto new file mode 100644 index 000000000..8a56fde22 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidHostTest/proto/androidHostTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidHostTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_android_kmp_library/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/build.gradle.kts new file mode 100644 index 000000000..0f2cfba02 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.kotlin.dsl.version +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree + +plugins { + kotlin("multiplatform") version "" + id("org.jetbrains.kotlinx.rpc.plugin") version "" + id("com.android.application") version "" +} + +kotlin { + jvm() + + androidTarget { + instrumentedTestVariant { + sourceSetTree.set(KotlinSourceSetTree.test) + } + + unitTestVariant { + sourceSetTree.set(KotlinSourceSetTree.test) + } + } +} + +android { + namespace = "com.example.myapp" + compileSdk = 34 +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidDebug/proto/androidDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidDebug/proto/androidDebug.proto new file mode 100644 index 000000000..a4cda85be --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidDebug/proto/androidDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto new file mode 100644 index 000000000..3545f8cd7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTest/proto/androidInstrumentedTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidInstrumentedTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto new file mode 100644 index 000000000..35a822595 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidInstrumentedTestDebug/proto/androidInstrumentedTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidInstrumentedTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidMain/proto/androidMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidMain/proto/androidMain.proto new file mode 100644 index 000000000..0ad5be028 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidMain/proto/androidMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidRelease/proto/androidRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidRelease/proto/androidRelease.proto new file mode 100644 index 000000000..86933df39 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidRelease/proto/androidRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTest/proto/androidTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTest/proto/androidTest.proto new file mode 100644 index 000000000..509c020b7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTest/proto/androidTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto new file mode 100644 index 000000000..7e68a56ae --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidTestDebug/proto/androidTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto new file mode 100644 index 000000000..ee889e075 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTest/proto/androidUnitTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto new file mode 100644 index 000000000..1c04f89da --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestDebug/proto/androidUnitTestDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto new file mode 100644 index 000000000..d6103b2cc --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/androidUnitTestRelease/proto/androidUnitTestRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AndroidUnitTestRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonMain/proto/commonMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/commonTest/proto/commonTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message CommonTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/debug/proto/debug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/debug/proto/debug.proto new file mode 100644 index 000000000..280702f10 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/debug/proto/debug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Debug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmMain/proto/jvmMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/jvmTest/proto/jvmTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JvmTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/main/proto/main.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/main/proto/main.proto new file mode 100644 index 000000000..588046d54 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/main/proto/main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/release/proto/release.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/release/proto/release.proto new file mode 100644 index 000000000..c3d9a0bc4 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/release/proto/release.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Release { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/test/proto/test.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/test/proto/test.proto new file mode 100644 index 000000000..51cfb5882 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/test/proto/test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testDebug/proto/testDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testDebug/proto/testDebug.proto new file mode 100644 index 000000000..d3efa4a23 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testDebug/proto/testDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixtures/proto/testFixtures.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixtures/proto/testFixtures.proto new file mode 100644 index 000000000..16a35e7c9 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixtures/proto/testFixtures.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixtures { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto new file mode 100644 index 000000000..d09209564 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesDebug/proto/testFixturesDebug.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesDebug { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto new file mode 100644 index 000000000..090356c1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testFixturesRelease/proto/testFixturesRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestFixturesRelease { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testRelease/proto/testRelease.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testRelease/proto/testRelease.proto new file mode 100644 index 000000000..bb2b93616 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly_legacy_android/src/testRelease/proto/testRelease.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message TestRelease { + string content = 1; +} From a62819b6be8c30def9f5b41378c3f0bfdf135c6e Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Sat, 13 Dec 2025 19:03:18 +0100 Subject: [PATCH 08/11] Fixed source sets locations and task inputs --- docs/environment.md | 6 + gradle-plugin/build.gradle.kts | 15 +- .../kotlinx/rpc/buf/tasks/BufExecTask.kt | 19 ++- .../kotlinx/rpc/buf/tasks/BufGenerateTask.kt | 27 ++-- .../rpc/buf/tasks/GenerateBufGenYaml.kt | 4 +- .../kotlinx/rpc/buf/tasks/GenerateBufYaml.kt | 8 +- .../rpc/protoc/DefaultProtoSourceSet.kt | 16 +- .../rpc/protoc/DefaultProtocExtension.kt | 27 +++- .../kotlinx/rpc/protoc/ProtoSourceSet.kt | 105 ++++++++++++- .../kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt | 8 + .../kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt | 7 + .../test/kotlin/kotlinx/rpc/base/BaseTest.kt | 2 + .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 143 +++++++++++++++--- .../test/resources/template.gradle.properties | 3 - 14 files changed, 327 insertions(+), 63 deletions(-) diff --git a/docs/environment.md b/docs/environment.md index 34a81c063..4e61d1f0d 100644 --- a/docs/environment.md +++ b/docs/environment.md @@ -61,6 +61,12 @@ Go to `Settings` -> `Build, Execution, Deployment` -> `Build Tools` -> `Gradle` - `Distribution` is set to `Wrapper` - `Gradle JVM` is set to `JAVA_HOME` and the value is correct +## Android SDK +In the root of the project you should create a `local.properties` file with the location of the Android SDK: +```properties +sdk.dir=/Users//Library/Android/sdk +``` + ## Git and GitHub Make sure your commit signing is set up. Check the diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 99715591e..390e81733 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -4,6 +4,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import util.other.generateSource +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.readLines plugins { `kotlin-dsl` @@ -105,6 +108,14 @@ generateSource( val globalRootDir: String by extra +val androidHome = System.getenv("ANDROID_HOME") + ?: Path(globalRootDir, "local.properties") + .readLines() + .find { it.startsWith("sdk.dir=") } + ?.substringAfter("=") + ?.trim() + ?: error("ANDROID_HOME is not set") + generateSource( name = "TestVersions", text = """ @@ -112,7 +123,9 @@ generateSource( const val RPC_VERSION: String = "${libs.versions.kotlinx.rpc.get()}" - const val BUILD_REPO: String = "${File(globalRootDir).resolve("build/repo").absolutePath}" + const val ANDROID_HOME_DIR: String = "$androidHome" + + const val BUILD_REPO: String = "${Path(globalRootDir, "build", "repo").absolutePathString()}" """.trimIndent(), chooseSourceSet = { test } ) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt index 911e4155e..7adeda55f 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt @@ -50,8 +50,13 @@ public abstract class BufExecTask @Inject constructor( @get:InputFile internal abstract val bufExecutable: Property + /** + * Whether to enable debug logging for the `buf` command using `--debug` option. + * + * @see Debugging + */ @get:Input - internal abstract val debug: Property + public abstract val debug: Property /** * The `buf` command to execute. @@ -127,14 +132,14 @@ internal fun Project.registerBufExecTask( ): TaskProvider = tasks.register(name, clazz, properties).apply { configure { val executableConfiguration = configurations.named(BUF_EXECUTABLE_CONFIGURATION) - bufExecutable.set(project.provider { executableConfiguration.get().singleFile }) - this.workingDir.set(workingDir) + bufExecutable.convention(project.provider { executableConfiguration.get().singleFile }) + this.workingDir.convention(workingDir) val buf = provider { rpcExtension().protoc.get().buf } - configFile.set(buf.flatMap { it.configFile }) - logFormat.set(buf.flatMap { it.logFormat }) - bufTimeoutInWholeSeconds.set(buf.flatMap { it.timeout.map { duration -> duration.inWholeSeconds } }) - debug.set(gradle.startParameter.logLevel == LogLevel.DEBUG) + configFile.convention(buf.flatMap { it.configFile }) + logFormat.convention(buf.flatMap { it.logFormat }) + bufTimeoutInWholeSeconds.convention(buf.flatMap { it.timeout.map { duration -> duration.inWholeSeconds } }) + debug.convention(gradle.startParameter.logLevel == LogLevel.DEBUG) configuration() } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt index 2d86b84bd..d9dc63527 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufGenerateTask.kt @@ -5,7 +5,6 @@ package kotlinx.rpc.buf.tasks import kotlinx.rpc.protoc.PROTO_GROUP -import kotlinx.rpc.rpcExtension import kotlinx.rpc.protoc.ProtocPlugin import org.gradle.api.Project import org.gradle.api.provider.ListProperty @@ -17,6 +16,7 @@ import org.gradle.api.tasks.TaskProvider import java.io.File import kotlinx.rpc.buf.BufGenerateExtension import kotlinx.rpc.protoc.DefaultProtoSourceSet +import kotlinx.rpc.protoc.DefaultProtocExtension import kotlinx.rpc.protoc.ProtoTask import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider @@ -32,16 +32,22 @@ import javax.inject.Inject public abstract class BufGenerateTask @Inject internal constructor( properties: ProtoTask.Properties, ) : BufExecTask(properties) { - // used to properly calculate output directories + /** + * List of plugin names used during `buf generate` command execution. + * + * @see kotlinx.rpc.protoc.ProtoSourceSet.plugins + * @see kotlinx.rpc.protoc.ProtocExtension.plugins + * @see ProtocPlugin + */ @get:Input - internal abstract val pluginNames: ListProperty + public abstract val pluginNames: ListProperty /** * List of executable files used during `buf generate` command execution. * * @see [ProtocPlugin.Artifact.Local.executableFiles] */ - // unsued, but required for Gradle to properly recognise inputs + // unsued, but required for Gradle to properly recognize inputs @get:InputFiles public abstract val executableFiles: ListProperty @@ -132,6 +138,7 @@ public abstract class BufGenerateTask @Inject internal constructor( } internal fun Project.registerBufGenerateTask( + protocExtension: DefaultProtocExtension, protoSourceSet: DefaultProtoSourceSet, workingDir: File, outputDirectory: File, @@ -146,15 +153,15 @@ internal fun Project.registerBufGenerateTask( group = PROTO_GROUP description = "Generates code from .proto files using 'buf generate'" - val generate = provider { rpcExtension().protoc.get().buf.generate } + val generate = protocExtension.buf.generate - includeImports.set(generate.flatMap { it.includeImports }) - includeWkt.set(generate.flatMap { it.includeWkt }) - errorFormat.set(generate.flatMap { it.errorFormat }) + includeImports.convention(generate.includeImports) + includeWkt.convention(generate.includeWkt) + errorFormat.convention(generate.errorFormat) - this.outputDirectory.set(outputDirectory) + this.outputDirectory.convention(outputDirectory) - this.pluginNames.set(includedPlugins.map { it.map { plugin -> plugin.name } }) + pluginNames.convention(includedPlugins.map { it.map { plugin -> plugin.name } }) configure() } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt index 885f87ba3..1d6abaed5 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt @@ -169,13 +169,13 @@ internal fun Project.registerGenerateBufGenYamlTask( } } - plugins.set(pluginsProvider) + plugins.convention(pluginsProvider) val bufGenYamlFile = buildSourceSetsDir .resolve(BUF_GEN_YAML) .ensureRegularFileExists() - bufGenFile.set(bufGenYamlFile) + bufGenFile.convention(bufGenYamlFile) configure() } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt index 019214cc9..5d0f2112c 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufYaml.kt @@ -96,15 +96,15 @@ internal fun Project.registerGenerateBufYamlTask( val task = tasks.register("${GenerateBufYaml.NAME_PREFIX}$capitalizeName", GenerateBufYaml::class, properties) task.configure { - protoSourceDir.set(buildSourceSetsProtoDir.name) - importSourceDir.set(buildSourceSetsImportDir.name) - this.withImport.set(withImport) + protoSourceDir.convention(buildSourceSetsProtoDir.name) + importSourceDir.convention(buildSourceSetsImportDir.name) + this.withImport.convention(withImport) val bufYamlFile = buildSourceSetsDir .resolve(BUF_YAML) .ensureRegularFileExists() - bufFile.set(bufYamlFile) + bufFile.convention(bufYamlFile) configure() } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index 72541bf00..a5f314865 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -77,7 +77,9 @@ internal open class DefaultProtoSourceSet( ) private val explicitApiModeEnabled = project.provider { - project.the().explicitApi != ExplicitApiMode.Disabled + val isMain = androidProperties.orNull?.isTest?.not() ?: name.lowercase().endsWith("main") + + isMain && project.the().explicitApi != ExplicitApiMode.Disabled } override val plugins = project.objects.setProperty() @@ -104,9 +106,7 @@ internal open class DefaultProtoSourceSet( } private fun initPlugin(copy: ProtocPlugin, configure: Action?) { - if (this@DefaultProtoSourceSet.name.lowercase().endsWith("main")) { - copy.options.put("explicitApiModeEnabled", explicitApiModeEnabled) - } + copy.options.put("explicitApiModeEnabled", explicitApiModeEnabled) configure?.execute(copy) } @@ -158,9 +158,9 @@ internal open class DefaultProtoSourceSet( imports.addAll(protoSourceSet.flatMap { it.imports.checkSelfImport() }) } - override fun importsAllFrom(protoSourceSet: Provider>) { - imports.addAll(protoSourceSet.checkSelfImport()) - imports.addAll(protoSourceSet.map { list -> list.flatMap { it.imports.checkSelfImport().get() } }) + override fun importsAllFrom(protoSourceSets: Provider>) { + imports.addAll(protoSourceSets.checkSelfImport()) + imports.addAll(protoSourceSets.map { list -> list.flatMap { it.imports.checkSelfImport().get() } }) } override fun importsFrom(protoSourceSet: NamedDomainObjectProvider) { @@ -190,6 +190,8 @@ internal open class DefaultProtoSourceSet( source(protoSourceSet.sourceDirectorySet) imports.addAll(protoSourceSet.imports.checkSelfImport()) + + plugins.addAll(protoSourceSet.plugins) } @JvmName("checkSelfImport_provider") diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index f26c370c1..b505fa679 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -248,6 +248,7 @@ internal open class DefaultProtocExtension @Inject constructor( val sourceSetsProtoDirFileTree = project.fileTree(buildSourceSetsProtoDir) val bufGenerateTask = project.registerBufGenerateTask( + protocExtension = this, protoSourceSet = protoSourceSet, workingDir = buildSourceSetsDir, outputDirectory = project.protoBuildDirGenerated.resolve(baseName), @@ -268,8 +269,8 @@ internal open class DefaultProtocExtension @Inject constructor( } ) - protoFiles.set(processProtoTask.map { it.outputs.files }) - importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) + protoFiles.convention(processProtoTask.map { it.outputs.files }) + importProtoFiles.convention(processImportProtoTask.map { it.outputs.files }) dependsOn(generateBufGenYamlTask) dependsOn(generateBufYamlTask) @@ -303,8 +304,8 @@ internal open class DefaultProtocExtension @Inject constructor( sourceSetsProtoDirFileTree = sourceSetsProtoDirFileTree, properties = properties, ) { - protoFiles.set(processProtoTask.map { it.outputs.files }) - importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) + protoFiles.convention(processProtoTask.map { it.outputs.files }) + importProtoFiles.convention(processImportProtoTask.map { it.outputs.files }) } protoSourceSet.tasksConfigured.set(true) @@ -333,7 +334,19 @@ internal open class DefaultProtocExtension @Inject constructor( project.withLegacyAndroid { // android + kotlin is always done in withKotlin above languageSets.filterIsInstance().forEach { sourceSet -> - sourceSet.java.srcDirs(javaOutputs) + // todo fails with + // + // Caused by: org.gradle.internal.typeconversion.UnsupportedNotationException: Cannot convert the provided notation to a File: []. + // The following types/formats are supported: + // - A String or CharSequence path, for example 'src/main/java' or '/usr/include'. + // - A String or CharSequence URI, for example 'file:/usr/include'. + // - A File instance. + // - A Path instance. + // - A Directory instance. + // - A RegularFile instance. + // - A URI or URL instance of file. + // - A TextResource instance. +// sourceSet.java.srcDir(javaOutputs) } } } @@ -347,9 +360,9 @@ internal open class DefaultProtocExtension @Inject constructor( plugins.filter(pluginFilter).map { it.name }.toSet() } - return bufGenerateTask.flatMap { task -> + return bufGenerateTask.map { task -> val pluginsSet = plugins.get() - task.outputSourceDirectories.map { list -> list.filter { it.name in pluginsSet } } + task.outputSourceDirectories.get().filter { it.name in pluginsSet } } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt index 1d50338b1..98bc10109 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt @@ -117,20 +117,121 @@ public sealed interface ProtoSourceSet : SourceDirectorySet { select: NamedDomainObjectContainer.() -> ProtocPlugin, ) + /** + * Protoc plugins that will be applied to proto files in this source set. + * + * Combined with the plugins from [extendsFrom] source sets. + * + * Plugins from [importsFrom] and [importsAllFrom] source sets are not included. + */ public val plugins: SetProperty + /** + * Proto files from [protoSourceSet] will be available as import, but will not be used for code generation. + * + * Example: + * ```kotlin + * kotlin.sourceSets { + * val someProto = create("someProto") + * commonMain { + * proto { + * importsFrom(protoSourceSets.getByName("someProto")) + * } + * } + * } + * ``` + */ public fun importsFrom(protoSourceSet: ProtoSourceSet) + /** + * Proto files from [protoSourceSet] will be available as import, but will not be used for code generation. + * + * Example: + * ```kotlin + * kotlin.sourceSets { + * val someProto = create("someProto") + * commonMain { + * proto { + * importsFrom(protoSourceSets.getByName("someProto")) + * } + * } + * } + * ``` + */ public fun importsFrom(protoSourceSet: Provider) - public fun importsAllFrom(protoSourceSet: Provider>) - + /** + * Proto files from [protoSourceSet] will be available as import, but will not be used for code generation. + * + * Example: + * ```kotlin + * kotlin.sourceSets { + * val someProto = create("someProto") + * commonMain { + * proto { + * importsFrom(protoSourceSets.getByName("someProto")) + * } + * } + * } + * ``` + */ public fun importsFrom(protoSourceSet: NamedDomainObjectProvider) + /** + * Proto files from [protoSourceSets] will be available as import, but will not be used for code generation. + * + * Example: + * ```kotlin + * kotlin.sourceSets { + * val someProto = create("someProto") + * commonMain { + * proto { + * importsAllFrom(project.provider { protoSourceSets.filter { it.name == someProto.name } }) + * } + * } + * } + * ``` + */ + public fun importsAllFrom(protoSourceSets: Provider>) + + /** + * List of all imported proto source sets. + */ public val imports: SetProperty + /** + * A collection of proto files that are imported by this source set but do not belong to any other source set. + * + * Example: + * ```kotlin + * kotlin.sourceSets { + * commonMain { + * proto { + * fileImports.from("path/to/proto/file.proto") + * } + * } + * } + * ``` + */ public val fileImports: ConfigurableFileCollection + /** + * Proto source sets that this source set extends from. + * All proto files from [protoSourceSet] will be used for code generation. + * All imports [protoSourceSet] will be included as well. + * + * Example: + * ```kotlin + * kotlin.sourceSets { + * val someProto = create("someProto") + * commonMain { + * proto { + * extendsFrom(protoSourceSets.getByName("someProto")) + * } + * } + * } + * ``` + */ public fun extendsFrom(protoSourceSet: ProtoSourceSet) } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt index c019cbd13..0a641d3f6 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt @@ -289,6 +289,14 @@ public open class ProtocPlugin internal constructor( } } + override fun equals(other: Any?): Boolean { + return other is ProtocPlugin && name == other.name + } + + override fun hashCode(): Int { + return name.hashCode() + } + internal fun copy(): ProtocPlugin { return ProtocPlugin(name, project) .also { diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt index 73f8dc9c7..d0159a2fd 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt @@ -68,6 +68,8 @@ class GrpcJvmProjectTest : GrpcBaseTest() { ) ) + dryRunCompilation(SSetsJvm.main) + cleanProtoBuildDir() val testRun = runGradle(bufGenerateCommonTest) @@ -88,6 +90,8 @@ class GrpcJvmProjectTest : GrpcBaseTest() { Path(RPC_INTERNAL, "Some.kt"), ) ) + + dryRunCompilation(SSetsJvm.test) } @TestFactory @@ -282,6 +286,7 @@ plugins: - generateComments=true - generateFileLevelComments=true - indentSize=4 + - explicitApiModeEnabled=false - local: [protoc-gen-grpc-kotlin-multiplatform] out: grpc-kotlin-multiplatform opt: @@ -289,10 +294,12 @@ plugins: - generateComments=true - generateFileLevelComments=true - indentSize=4 + - explicitApiModeEnabled=false - remote: my.remote.plugin out: myRemotePlugin opt: - hello=world + - explicitApiModeEnabled=false - only=in test inputs: - directory: proto diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt index 9761bf4e5..dd86fe59b 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.base +import kotlinx.rpc.ANDROID_HOME_DIR import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.junit.jupiter.api.BeforeEach @@ -168,6 +169,7 @@ abstract class BaseTest { .withTestKitDir(testKitDir.absolute().toFile()) .withGradleVersion(versions.gradle) .forwardOutput() + .withEnvironment(mapOf("ANDROID_HOME" to ANDROID_HOME_DIR)) .withArguments( listOfNotNull( task, diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt index e90a4aa0c..567f535b4 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -6,6 +6,8 @@ package kotlinx.rpc.base +import kotlinx.rpc.base.GrpcBaseTest.CompileTaskMode +import kotlinx.rpc.base.GrpcBaseTest.SSets import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome import org.intellij.lang.annotations.Language @@ -14,6 +16,7 @@ import org.junit.jupiter.api.TestInstance import java.nio.file.Path import java.util.stream.Stream import kotlin.io.path.* +import kotlin.io.path.name import kotlin.test.assertEquals import kotlin.test.fail @@ -326,7 +329,20 @@ abstract class GrpcBaseTest : BaseTest() { extended: List = emptyList(), ) { cleanProtoBuildDir() + runForSet(sourceSet).assertSourceSet(sourceSet, *imports, extendedProto = extended) + + dryRunCompilation(sourceSet) + } + + fun dryRunCompilation(sourceSet: SSets) { + val compileTask = sourceSet.compileTask() ?: return + + val result = runGradle(compileTask, "--dry-run", "--no-configuration-cache") + + assert(result.output.contains(":${bufGenerate(sourceSet)} SKIPPED")) { + "${bufGenerate(sourceSet)} task should be present in dry-run mode for $compileTask execution" + } } fun GrpcTestEnv.runForSet(sourceSet: SSets): BuildResult { @@ -444,29 +460,38 @@ abstract class GrpcBaseTest : BaseTest() { interface SSets { val minKotlin: KotlinVersion + val mode: CompileTaskMode? val name: String fun all(): List + fun compileTask(): String? { + return mode?.let { compileTaskName(it) } + } } enum class SSetsJvm(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSets { main, test, ; + override val mode: CompileTaskMode = ctm.j + override fun all(): List { return entries } } sealed interface SSetsKmp : SSets { - enum class Default(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsKmp { + enum class Default( + override val mode: CompileTaskMode? = null, + override val minKotlin: KotlinVersion = KtVersion.v2_0_0, + ) : SSetsKmp { commonMain, commonTest, - jvmMain, jvmTest, - webMain(KtVersion.v2_2_20), webTest(KtVersion.v2_2_20), - jsMain, jsTest, + jvmMain(ctm.k), jvmTest(ctm.k), + webMain(minKotlin = KtVersion.v2_2_20), webTest(minKotlin = KtVersion.v2_2_20), + jsMain(ctm.k), jsTest(ctm.k), nativeMain, nativeTest, appleMain, appleTest, macosMain, macosTest, - macosArm64Main, macosArm64Test, + macosArm64Main(ctm.k), macosArm64Test(ctm.k), ; override fun all(): List { @@ -474,10 +499,13 @@ abstract class GrpcBaseTest : BaseTest() { } } - enum class AndroidKmpLib(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsKmp { + enum class AndroidKmpLib( + override val mode: CompileTaskMode? = null, + override val minKotlin: KotlinVersion = KtVersion.v2_0_0, + ) : SSetsKmp { commonMain, commonTest, - jvmMain, jvmTest, - androidMain, androidHostTest, androidDeviceTest, + jvmMain(ctm.k), jvmTest(ctm.k), + androidMain(ctm.ak), androidHostTest(ctm.ak), androidDeviceTest(ctm.ak), ; override fun all(): List { @@ -485,18 +513,21 @@ abstract class GrpcBaseTest : BaseTest() { } } - enum class LegacyAndroid(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsKmp { + enum class LegacyAndroid( + override val mode: CompileTaskMode? = null, + override val minKotlin: KotlinVersion = KtVersion.v2_0_0, + ) : SSetsKmp { commonMain, commonTest, - jvmMain, jvmTest, + jvmMain(ctm.k), jvmTest(ctm.k), // kmp, non-executable androidMain, androidUnitTest, androidInstrumentedTest, // kmp, executable - androidDebug, androidRelease, - androidUnitTestDebug, androidUnitTestRelease, - androidInstrumentedTestDebug, + androidDebug(ctm.al), androidRelease(ctm.al), + androidUnitTestDebug(ctm.al), androidUnitTestRelease(ctm.al), + androidInstrumentedTestDebug(ctm.al), // legacy, non-executable main, test, @@ -515,16 +546,19 @@ abstract class GrpcBaseTest : BaseTest() { } sealed interface SSetsAndroid : SSets { - enum class Default(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsAndroid { + enum class Default( + override val mode: CompileTaskMode? = null, + override val minKotlin: KotlinVersion = KtVersion.v2_0_0, + ) : SSetsAndroid { // non-executable main, test, testFixtures, testFixturesDebug, testFixturesRelease, androidTest, // executable - debug, release, - testDebug, testRelease, - androidTestDebug, + debug(ctm.a), release(ctm.a), + testDebug(ctm.a), testRelease(ctm.a), + androidTestDebug(ctm.a), ; override fun all(): List { @@ -532,8 +566,11 @@ abstract class GrpcBaseTest : BaseTest() { } } - enum class Test(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSetsAndroid { - main, debug, + enum class Test( + override val mode: CompileTaskMode? = null, + override val minKotlin: KotlinVersion = KtVersion.v2_0_0, + ) : SSetsAndroid { + main, debug(ctm.a), ; override fun all(): List { @@ -542,10 +579,76 @@ abstract class GrpcBaseTest : BaseTest() { } } - private val SSets.capital get() = name.replaceFirstChar { it.titlecase() } + enum class CompileTaskMode { + Jvm, Kmp, Android, LegacyAndroidKmp, AndroidKmpLib; + + companion object { + val j = Jvm + val k = Kmp + val a = Android + val al = LegacyAndroidKmp + val ak = AndroidKmpLib + } + } companion object { private const val KOTLIN_MULTIPLATFORM_DIR = "kotlin-multiplatform" const val RPC_INTERNAL = "_rpc_internal" } } + +typealias ctm = CompileTaskMode + +private val SSets.capital get() = name.replaceFirstChar { it.titlecase() } + +private fun SSets.compileTaskName(mode: CompileTaskMode): String { + return when (mode) { + CompileTaskMode.Jvm -> if (name == "main") "compileKotlin" else "compileTestKotlin" + CompileTaskMode.Kmp -> { + val platform = capital.removeSuffix("Main").removeSuffix("Test") + if (name.endsWith("Test")) "compileTestKotlin$platform" else "compileKotlin$platform" + } + + CompileTaskMode.Android -> { + // compileX86FreeappDebugUnitTestKotlin + // compileArmFreeappDebugAndroidTestKotlin + // compileArmFreeappDebugKotlin + when { + name.startsWith("androidTest") -> { + "compile${name.removePrefix("androidTest")}AndroidTestKotlin" + } + + name.startsWith("test") -> { + "compile${name.removePrefix("test")}UnitTestKotlin" + } + + else -> "compile${capital}Kotlin" + } + } + + CompileTaskMode.LegacyAndroidKmp -> { + // compileDebugAndroidTestKotlinAndroid + // compileDebugUnitTestKotlinAndroid + // compileDebugKotlinAndroid + val withoutPrefix = name.removePrefix("android") + when { + withoutPrefix.startsWith("InstrumentedTest") -> { + "compile${withoutPrefix.removePrefix("InstrumentedTest")}AndroidTestKotlinAndroid" + } + + withoutPrefix.startsWith("UnitTest") -> { + "compile${withoutPrefix.removePrefix("UnitTest")}UnitTestKotlinAndroid" + } + + else -> "compile${withoutPrefix}KotlinAndroid" + } + } + + CompileTaskMode.AndroidKmpLib -> { + // compileAndroidDeviceTest + // compileAndroidHostTest + // compileAndroidMain + "compile${capital}" + } + } +} diff --git a/gradle-plugin/src/test/resources/template.gradle.properties b/gradle-plugin/src/test/resources/template.gradle.properties index c94b4e963..693518b6c 100644 --- a/gradle-plugin/src/test/resources/template.gradle.properties +++ b/gradle-plugin/src/test/resources/template.gradle.properties @@ -15,6 +15,3 @@ org.gradle.parallel=true org.gradle.workers.max=2 org.gradle.caching=true org.gradle.configuration-cache=true - -# development mode for kotlinx.rpc gradle plugin. Uses local project paths to apply the compiler plugin -kotlinx.rpc.plugin.internalDevelopment=true From a161d930785f74a96216cd633f67de616e488c3e Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Sat, 13 Dec 2025 19:05:17 +0100 Subject: [PATCH 09/11] abiDump --- gradle-plugin/api/gradle-plugin.api | 121 +++++++++++++++++----------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/gradle-plugin/api/gradle-plugin.api b/gradle-plugin/api/gradle-plugin.api index 1ceaf1917..d0d3582fc 100644 --- a/gradle-plugin/api/gradle-plugin.api +++ b/gradle-plugin/api/gradle-plugin.api @@ -66,7 +66,6 @@ public final class kotlinx/rpc/buf/BufExtension$LogFormat : java/lang/Enum { } public class kotlinx/rpc/buf/BufGenerateExtension { - public final fun allTasks ()Lkotlinx/rpc/buf/tasks/BufTasks; public final fun comments (Lorg/gradle/api/Action;)V public final fun getComments ()Lkotlinx/rpc/buf/BufCommentsExtension; public final fun getErrorFormat ()Lorg/gradle/api/provider/Property; @@ -88,9 +87,8 @@ public final class kotlinx/rpc/buf/BufGenerateExtension$ErrorFormat : java/lang/ } public class kotlinx/rpc/buf/BufTasksExtension { - public final fun all ()Lkotlinx/rpc/buf/tasks/BufAllTasks; - public final fun registerWorkspaceTask (Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/Action;)Lkotlinx/rpc/buf/tasks/BufTasks; - public static synthetic fun registerWorkspaceTask$default (Lkotlinx/rpc/buf/BufTasksExtension;Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/Action;ILjava/lang/Object;)Lkotlinx/rpc/buf/tasks/BufTasks; + public final fun registerWorkspaceTask (Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/Action;)Lkotlinx/rpc/protoc/ProtoTasks; + public static synthetic fun registerWorkspaceTask$default (Lkotlinx/rpc/buf/BufTasksExtension;Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/Action;ILjava/lang/Object;)Lkotlinx/rpc/protoc/ProtoTasks; } public final class kotlinx/rpc/buf/ConstsKt { @@ -99,35 +97,20 @@ public final class kotlinx/rpc/buf/ConstsKt { public static final field BUF_YAML Ljava/lang/String; } -public abstract interface class kotlinx/rpc/buf/tasks/BufAllTasks : kotlinx/rpc/buf/tasks/BufTasks { - public abstract fun matchingType (Lkotlin/reflect/KClass;)Lkotlinx/rpc/buf/tasks/BufTasks; -} - -public abstract class kotlinx/rpc/buf/tasks/BufExecTask : org/gradle/api/DefaultTask { - public fun (Lkotlinx/rpc/buf/tasks/BufExecTask$Properties;)V +public abstract class kotlinx/rpc/buf/tasks/BufExecTask : kotlinx/rpc/protoc/DefaultProtoTask { + public fun (Lkotlinx/rpc/protoc/ProtoTask$Properties;)V public abstract fun getArgs ()Lorg/gradle/api/provider/ListProperty; public abstract fun getBufTimeoutInWholeSeconds ()Lorg/gradle/api/provider/Property; public abstract fun getCommand ()Lorg/gradle/api/provider/Property; public abstract fun getConfigFile ()Lorg/gradle/api/provider/Property; + public abstract fun getDebug ()Lorg/gradle/api/provider/Property; public abstract fun getLogFormat ()Lorg/gradle/api/provider/Property; - public final fun getProperties ()Lkotlinx/rpc/buf/tasks/BufExecTask$Properties; public abstract fun getWorkingDir ()Lorg/gradle/api/provider/Property; } -public final class kotlinx/rpc/buf/tasks/BufExecTask$AndroidProperties : kotlinx/rpc/buf/tasks/BufExecTask$Properties { - public final fun getBuildType ()Ljava/lang/String; - public final fun getFlavor ()Ljava/lang/String; - public final fun getVariant ()Ljava/lang/String; -} - -public class kotlinx/rpc/buf/tasks/BufExecTask$Properties { - public final fun getSourceSetName ()Ljava/lang/String; - public final fun isTest ()Z -} - public final class kotlinx/rpc/buf/tasks/BufExecTaskKt { - public static final fun registerBufExecTask (Lorg/gradle/api/Project;Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/provider/Provider;Lkotlinx/rpc/buf/tasks/BufExecTask$Properties;Lkotlin/jvm/functions/Function1;)Lorg/gradle/api/tasks/TaskProvider; - public static synthetic fun registerBufExecTask$default (Lorg/gradle/api/Project;Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/provider/Provider;Lkotlinx/rpc/buf/tasks/BufExecTask$Properties;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/gradle/api/tasks/TaskProvider; + public static final fun registerBufExecTask (Lorg/gradle/api/Project;Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/provider/Provider;Lkotlinx/rpc/protoc/ProtoTask$Properties;Lkotlin/jvm/functions/Function1;)Lorg/gradle/api/tasks/TaskProvider; + public static synthetic fun registerBufExecTask$default (Lorg/gradle/api/Project;Lkotlin/reflect/KClass;Ljava/lang/String;Lorg/gradle/api/provider/Provider;Lkotlinx/rpc/protoc/ProtoTask$Properties;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/gradle/api/tasks/TaskProvider; } public abstract class kotlinx/rpc/buf/tasks/BufGenerateTask : kotlinx/rpc/buf/tasks/BufExecTask { @@ -138,36 +121,16 @@ public abstract class kotlinx/rpc/buf/tasks/BufGenerateTask : kotlinx/rpc/buf/ta public abstract fun getIncludeImports ()Lorg/gradle/api/provider/Property; public abstract fun getIncludeWkt ()Lorg/gradle/api/provider/Property; public abstract fun getOutputDirectory ()Lorg/gradle/api/provider/Property; + public final fun getOutputSourceDirectories ()Lorg/gradle/api/provider/Provider; + public abstract fun getPluginNames ()Lorg/gradle/api/provider/ListProperty; } -public abstract interface class kotlinx/rpc/buf/tasks/BufTasks : org/gradle/api/tasks/TaskCollection { - public abstract fun executedForKotlinSourceSet (Lorg/gradle/api/NamedDomainObjectProvider;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun executedForKotlinSourceSet (Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun executedForSourceSet (Ljava/lang/String;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun executedForSourceSet (Lorg/gradle/api/NamedDomainObjectProvider;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun executedForSourceSet (Lorg/gradle/api/tasks/SourceSet;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingBuildType (Ljava/lang/String;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingFlavor (Ljava/lang/String;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingKotlinSourceSet (Lorg/gradle/api/NamedDomainObjectProvider;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingKotlinSourceSet (Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingSourceSet (Ljava/lang/String;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingSourceSet (Lorg/gradle/api/NamedDomainObjectProvider;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingSourceSet (Lorg/gradle/api/tasks/SourceSet;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun matchingVariant (Ljava/lang/String;)Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun nonTestTasks ()Lkotlinx/rpc/buf/tasks/BufTasks; - public abstract fun testTasks ()Lkotlinx/rpc/buf/tasks/BufTasks; -} - -public final class kotlinx/rpc/buf/tasks/BufTasksKt { - public static final fun bufDependsOn (Lkotlinx/rpc/buf/tasks/BufExecTask;Lkotlin/reflect/KClass;)Lkotlinx/rpc/buf/tasks/BufTasks; -} - -public abstract class kotlinx/rpc/buf/tasks/GenerateBufGenYaml : org/gradle/api/DefaultTask { +public abstract class kotlinx/rpc/buf/tasks/GenerateBufGenYaml : kotlinx/rpc/protoc/DefaultProtoTask { public static final field NAME_PREFIX Ljava/lang/String; public abstract fun getBufGenFile ()Lorg/gradle/api/provider/Property; } -public abstract class kotlinx/rpc/buf/tasks/GenerateBufYaml : org/gradle/api/DefaultTask { +public abstract class kotlinx/rpc/buf/tasks/GenerateBufYaml : kotlinx/rpc/protoc/DefaultProtoTask { public static final field NAME_PREFIX Ljava/lang/String; public abstract fun getBufFile ()Lorg/gradle/api/provider/Property; } @@ -186,15 +149,29 @@ public final class kotlinx/rpc/protoc/ConstsKt { public static final field PROTO_SOURCE_SET_EXTENSION_NAME Ljava/lang/String; } +public abstract class kotlinx/rpc/protoc/DefaultProtoTask : org/gradle/api/DefaultTask, kotlinx/rpc/protoc/ProtoTask { + public fun (Lkotlinx/rpc/protoc/ProtoTask$Properties;)V + public final fun getProperties ()Lkotlinx/rpc/protoc/ProtoTask$Properties; +} + public final class kotlinx/rpc/protoc/PluginJarsKt { public static final fun getGrpcKotlinMultiplatformProtocPluginJarPath (Lorg/gradle/api/Project;)Lorg/gradle/api/provider/Provider; public static final fun getKotlinMultiplatformProtocPluginJarPath (Lorg/gradle/api/Project;)Lorg/gradle/api/provider/Provider; } -public abstract class kotlinx/rpc/protoc/ProcessProtoFiles : org/gradle/api/tasks/Copy { +public abstract class kotlinx/rpc/protoc/ProcessProtoFiles : org/gradle/api/tasks/Sync, kotlinx/rpc/protoc/ProtoTask { + public fun getProperties ()Lkotlinx/rpc/protoc/ProtoTask$Properties; } public abstract interface class kotlinx/rpc/protoc/ProtoSourceSet : org/gradle/api/file/SourceDirectorySet { + public abstract fun extendsFrom (Lkotlinx/rpc/protoc/ProtoSourceSet;)V + public abstract fun getFileImports ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getImports ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getPlugins ()Lorg/gradle/api/provider/SetProperty; + public abstract fun importsAllFrom (Lorg/gradle/api/provider/Provider;)V + public abstract fun importsFrom (Lkotlinx/rpc/protoc/ProtoSourceSet;)V + public abstract fun importsFrom (Lorg/gradle/api/NamedDomainObjectProvider;)V + public abstract fun importsFrom (Lorg/gradle/api/provider/Provider;)V public abstract fun plugin (Lkotlinx/rpc/protoc/ProtocPlugin;Lorg/gradle/api/Action;)V public abstract fun plugin (Lorg/gradle/api/Action;Lkotlin/jvm/functions/Function1;)V public abstract fun plugin (Lorg/gradle/api/NamedDomainObjectProvider;Lorg/gradle/api/Action;)V @@ -216,6 +193,49 @@ public final class kotlinx/rpc/protoc/ProtoSourceSetKt { public static final fun proto_kotlin (Lorg/gradle/api/NamedDomainObjectProvider;Lorg/gradle/api/Action;)V } +public abstract interface class kotlinx/rpc/protoc/ProtoTask : org/gradle/api/Task { + public abstract fun getProperties ()Lkotlinx/rpc/protoc/ProtoTask$Properties; +} + +public final class kotlinx/rpc/protoc/ProtoTask$AndroidProperties : kotlinx/rpc/protoc/ProtoTask$Properties { + public fun (ZLjava/util/Set;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;ZZ)V + public final fun getBuildType ()Ljava/lang/String; + public final fun getFlavors ()Ljava/util/List; + public final fun getVariant ()Ljava/lang/String; + public final fun isInstrumentedTest ()Z + public final fun isUnitTest ()Z +} + +public class kotlinx/rpc/protoc/ProtoTask$Properties { + public fun (ZLjava/util/Set;)V + public final fun getSourceSetNames ()Ljava/util/Set; + public final fun isTest ()Z +} + +public abstract interface class kotlinx/rpc/protoc/ProtoTasks : org/gradle/api/tasks/TaskCollection { + public abstract fun androidInstrumentedTestTasks ()Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun androidTasks (Lkotlin/jvm/functions/Function2;)Lkotlinx/rpc/protoc/ProtoTasks; + public static synthetic fun androidTasks$default (Lkotlinx/rpc/protoc/ProtoTasks;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun androidUnitTestTasks ()Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingAndroidBuildType (Ljava/lang/String;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingAndroidFlavor (Ljava/lang/String;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingAndroidVariant (Ljava/lang/String;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingKotlinSourceSet (Lorg/gradle/api/NamedDomainObjectProvider;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingKotlinSourceSet (Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingSourceSet (Ljava/lang/String;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingSourceSet (Lorg/gradle/api/NamedDomainObjectProvider;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingSourceSet (Lorg/gradle/api/tasks/SourceSet;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun matchingType (Lkotlin/reflect/KClass;)Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun nonTestTasks ()Lkotlinx/rpc/protoc/ProtoTasks; + public abstract fun testTasks ()Lkotlinx/rpc/protoc/ProtoTasks; +} + +public final class kotlinx/rpc/protoc/ProtoTasksKt { + public static final fun getBuf (Lkotlinx/rpc/protoc/ProtoTasks;)Lkotlinx/rpc/protoc/ProtoTasks; + public static final fun getGenerate (Lkotlinx/rpc/protoc/ProtoTasks;)Lkotlinx/rpc/protoc/ProtoTasks; + public static final fun getProtoTasks (Lorg/gradle/api/Project;)Lkotlinx/rpc/protoc/ProtoTasks; +} + public abstract interface class kotlinx/rpc/protoc/ProtocExtension { public abstract fun buf (Lorg/gradle/api/Action;)V public abstract fun getBuf ()Lkotlinx/rpc/buf/BufExtension; @@ -227,6 +247,7 @@ public class kotlinx/rpc/protoc/ProtocPlugin { public static final field Companion Lkotlinx/rpc/protoc/ProtocPlugin$Companion; public static final field GRPC_KOTLIN_MULTIPLATFORM Ljava/lang/String; public static final field KOTLIN_MULTIPLATFORM Ljava/lang/String; + public fun equals (Ljava/lang/Object;)Z public final fun getArtifact ()Lorg/gradle/api/provider/Property; public final fun getExcludeTypes ()Lorg/gradle/api/provider/ListProperty; public final fun getIncludeImports ()Lorg/gradle/api/provider/Property; @@ -235,7 +256,9 @@ public class kotlinx/rpc/protoc/ProtocPlugin { public final fun getOptions ()Lorg/gradle/api/provider/MapProperty; public final fun getStrategy ()Lorg/gradle/api/provider/Property; public final fun getTypes ()Lorg/gradle/api/provider/ListProperty; + public fun hashCode ()I public final fun isJava ()Lorg/gradle/api/provider/Property; + public final fun isKotlin ()Lorg/gradle/api/provider/Property; public final fun local (Lorg/gradle/api/Action;)V public final fun remote (Lorg/gradle/api/Action;)V } From 2b458427fbb8a051b44f2a27518d7f116b281d83 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Sat, 13 Dec 2025 19:11:07 +0100 Subject: [PATCH 10/11] detekt --- .../rpc/buf/tasks/GenerateBufGenYaml.kt | 6 +++- .../rpc/protoc/DefaultProtoSourceSet.kt | 16 +++++------ .../rpc/protoc/DefaultProtocExtension.kt | 13 +++++++-- .../kotlin/kotlinx/rpc/protoc/ProtoTask.kt | 3 +- .../kotlin/kotlinx/rpc/protoc/ProtoTasks.kt | 4 ++- .../kotlinx/rpc/protoc/android/variants.kt | 4 ++- .../src/main/kotlin/kotlinx/rpc/util/kgp.kt | 4 ++- .../kotlinx/rpc/GrpcAndroidProjectTest.kt | 2 ++ .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 2 ++ .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 28 +++++++++++++++---- 10 files changed, 59 insertions(+), 23 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt index 1d6abaed5..b7116327f 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt @@ -140,7 +140,11 @@ internal fun Project.registerGenerateBufGenYamlTask( configure: GenerateBufGenYaml.() -> Unit = {}, ): TaskProvider { val capitalizeName = name.replaceFirstChar { it.uppercase() } - val task = project.tasks.register("${GenerateBufGenYaml.NAME_PREFIX}$capitalizeName", GenerateBufGenYaml::class, properties) + val task = project.tasks.register( + "${GenerateBufGenYaml.NAME_PREFIX}$capitalizeName", + GenerateBufGenYaml::class, + properties, + ) task.configure { val pluginsProvider = project.provider { diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index a5f314865..97f37f0f4 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -175,15 +175,13 @@ internal open class DefaultProtoSourceSet( return } - if (this == protoSourceSet) { - throw IllegalArgumentException("$name proto source set cannot extend from self") + require(this != protoSourceSet) { + "$name proto source set cannot extend from self" } - if (protoSourceSet !is DefaultProtoSourceSet) { - throw IllegalArgumentException( - "$name proto source set can only extend from other default proto source sets." + - "${protoSourceSet.name} is not a ${DefaultProtoSourceSet::class.simpleName}", - ) + require(protoSourceSet is DefaultProtoSourceSet) { + "$name proto source set can only extend from other default proto source sets." + + "${protoSourceSet.name} is not a ${DefaultProtoSourceSet::class.simpleName}" } extendsFrom += protoSourceSet @@ -209,8 +207,8 @@ internal open class DefaultProtoSourceSet( } private fun ProtoSourceSet.checkSelfImport(): ProtoSourceSet { - if (this@DefaultProtoSourceSet == this) { - throw IllegalArgumentException("${this@DefaultProtoSourceSet.name} proto source set cannot import from itself") + require(this@DefaultProtoSourceSet != this) { + "${this@DefaultProtoSourceSet.name} proto source set cannot import from itself" } return this diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index b505fa679..415c5cff7 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -336,7 +336,8 @@ internal open class DefaultProtocExtension @Inject constructor( languageSets.filterIsInstance().forEach { sourceSet -> // todo fails with // - // Caused by: org.gradle.internal.typeconversion.UnsupportedNotationException: Cannot convert the provided notation to a File: []. + // Caused by: org.gradle.internal.typeconversion.UnsupportedNotationException: + // Cannot convert the provided notation to a File: []. // The following types/formats are supported: // - A String or CharSequence path, for example 'src/main/java' or '/usr/include'. // - A String or CharSequence URI, for example 'file:/usr/include'. @@ -366,6 +367,7 @@ internal open class DefaultProtocExtension @Inject constructor( } } + @Suppress("detekt.LongParameterList") private fun configureCustomTasks( protoSourceSet: DefaultProtoSourceSet, buildSourceSetsDir: File, @@ -441,7 +443,9 @@ internal open class DefaultProtocExtension @Inject constructor( } } - private fun DefaultProtoSourceSet.getDependsOnImports(protoSourceSets: ProtoSourceSets): Provider> { + private fun DefaultProtoSourceSet.getDependsOnImports( + protoSourceSets: ProtoSourceSets, + ): Provider> { return languageSourceSets.map { list -> val kotlin = list.filterIsInstance() @@ -564,6 +568,7 @@ private fun Project.configureMultiplatformWithAndroidSourceSets(body: (DefaultPr } } +@Suppress("detekt.CyclomaticComplexMethod") private fun AndroidComponents.configureLegacyAndroidVariants( project: Project, isKmp: Boolean, @@ -603,7 +608,9 @@ private fun AndroidComponents.configureLegacyAndroidVariants( // but testFixtures still have source sets based on flavors val testFixtureSetNames = if (rootName != LegacyAndroidRootSourceSets.Main) { variant.dependencySourceSets(LegacyAndroidRootSourceSets.TestFixtures) - } else emptyList() + } else { + emptyList() + } val dependencySourceSetNames = variant.dependencySourceSets(rootName) val variantSourceSetName = dependencySourceSetNames.lastOrNull() diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt index 7d1fba579..7f39c8c5f 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt @@ -30,7 +30,8 @@ public interface ProtoTask : Task { * * For Kotlin/JVM it has only one source set (`main`, or `test`, for example). * - * For Kotlin/Multiplatform it also has only one source set (`commonMain`, `commonTest`, or `jsMain`, for example). + * For Kotlin/Multiplatform it also has only one source set + * (`commonMain`, `commonTest`, or `jsMain`, for example). * * For Android, Kotlin/Android, and Kotlin/Multiplatform + Android it can have multiple source sets: * - `["androidMain", "androidDebug", "main", "debug"]` diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt index 5b8e2ee8c..519f4c257 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt @@ -257,7 +257,9 @@ internal open class ProtoTasksImpl( return matchingSourceSet(sourceSet.name) } - override fun matchingKotlinSourceSet(sourceSet: NamedDomainObjectProvider): ProtoTasks { + override fun matchingKotlinSourceSet( + sourceSet: NamedDomainObjectProvider, + ): ProtoTasks { return matchingSourceSet(sourceSet.name) } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt index b63995cbe..76b3e03da 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt @@ -89,7 +89,9 @@ internal fun String.kotlinProxyFromAndroidOriginSourceSetName(rootName: LegacyAn "androidInstrumented${removePrefix("android").replaceFirstChar { it.uppercase() }}" } - LegacyAndroidRootSourceSets.TestFixtures -> null + LegacyAndroidRootSourceSets.TestFixtures -> { + null + } } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt index 10dc1fa7c..f214317b2 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt @@ -88,7 +88,9 @@ internal fun Project.withLegacyAndroid(action: LegacyAndroidApplied.() -> Unit) internal class LegacyAndroidApplied(val project: Project, val id: String) -internal fun LegacyAndroidApplied.withAndroidSourceSets(action: (NamedDomainObjectContainer) -> Unit) { +internal fun LegacyAndroidApplied.withAndroidSourceSets( + action: (NamedDomainObjectContainer) -> Unit, +) { action(project.the().sourceSets) } diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt index f832dfe4a..f4ccda5a0 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt @@ -2,6 +2,8 @@ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("detekt.LongMethod", "detekt.LargeClass") + package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index e49f19d91..4511bbd2a 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -2,6 +2,8 @@ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("detekt.LongMethod", "detekt.LargeClass") + package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt index 567f535b4..b6465f04c 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -355,14 +355,20 @@ abstract class GrpcBaseTest : BaseTest() { extendedProto: List, ) { if (!sourceSet.applicable()) { - println("Skipping ${sourceSet.capital} source set because it's not applicable for the current Kotlin version") + println( + "Skipping ${sourceSet.capital} source set " + + "because it's not applicable for the current Kotlin version" + ) return } val importsSet = imports .onEach { if (!it.applicable()) { - println("Skipping ${it.capital} import source set because it's not applicable for the current Kotlin version") + println( + "Skipping ${it.capital} import source set " + + "because it's not applicable for the current Kotlin version" + ) } } .filter { it.applicable() } @@ -603,10 +609,16 @@ private val SSets.capital get() = name.replaceFirstChar { it.titlecase() } private fun SSets.compileTaskName(mode: CompileTaskMode): String { return when (mode) { - CompileTaskMode.Jvm -> if (name == "main") "compileKotlin" else "compileTestKotlin" + CompileTaskMode.Jvm -> { + if (name == "main") "compileKotlin" else "compileTestKotlin" + } CompileTaskMode.Kmp -> { val platform = capital.removeSuffix("Main").removeSuffix("Test") - if (name.endsWith("Test")) "compileTestKotlin$platform" else "compileKotlin$platform" + if (name.endsWith("Test")) { + "compileTestKotlin$platform" + } else { + "compileKotlin$platform" + } } CompileTaskMode.Android -> { @@ -622,7 +634,9 @@ private fun SSets.compileTaskName(mode: CompileTaskMode): String { "compile${name.removePrefix("test")}UnitTestKotlin" } - else -> "compile${capital}Kotlin" + else -> { + "compile${capital}Kotlin" + } } } @@ -640,7 +654,9 @@ private fun SSets.compileTaskName(mode: CompileTaskMode): String { "compile${withoutPrefix.removePrefix("UnitTest")}UnitTestKotlinAndroid" } - else -> "compile${withoutPrefix}KotlinAndroid" + else -> { + "compile${withoutPrefix}KotlinAndroid" + } } } From 2fb31682184594af6113651352877a7f11ba8a60 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Sat, 13 Dec 2025 19:35:52 +0100 Subject: [PATCH 11/11] small fixes --- gradle-plugin/build.gradle.kts | 7 +++++-- .../src/test/kotlin/kotlinx/rpc/base/BaseTest.kt | 8 ++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 390e81733..337caf67f 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -6,6 +6,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import util.other.generateSource import kotlin.io.path.Path import kotlin.io.path.absolutePathString +import kotlin.io.path.exists import kotlin.io.path.readLines plugins { @@ -109,9 +110,11 @@ generateSource( val globalRootDir: String by extra val androidHome = System.getenv("ANDROID_HOME") + ?: System.getProperty("ANDROID_SDK_HOME") ?: Path(globalRootDir, "local.properties") - .readLines() - .find { it.startsWith("sdk.dir=") } + .takeIf { it.exists() } + ?.readLines() + ?.find { it.startsWith("sdk.dir=") } ?.substringAfter("=") ?.trim() ?: error("ANDROID_HOME is not set") diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt index dd86fe59b..e4313dcbb 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -168,13 +168,11 @@ abstract class BaseTest { .withProjectDir(projectDir.absolute().toFile()) .withTestKitDir(testKitDir.absolute().toFile()) .withGradleVersion(versions.gradle) - .forwardOutput() .withEnvironment(mapOf("ANDROID_HOME" to ANDROID_HOME_DIR)) .withArguments( listOfNotNull( task, "--stacktrace", - "--info", "-Dorg.gradle.kotlin.dsl.scriptCompilationAvoidance=false", *args, ) @@ -192,15 +190,13 @@ abstract class BaseTest { val versions: VersionsEnv, ) { val projectDir: Path get() = this@BaseTest.projectDir - var latestBuild: BuildResult? = null - private set fun runGradle( task: String, vararg args: String, ): BuildResult { return runGradleInternal(task, versions, *args) { - build().also { latestBuild = it } + build() } } @@ -209,7 +205,7 @@ abstract class BaseTest { vararg args: String, ): BuildResult { return runGradleInternal(task, versions, *args) { - buildAndFail().also { latestBuild = it } + buildAndFail() } }