diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index edce9f75d..99715591e 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 { @@ -29,7 +31,9 @@ tasks.withType().configureEach { } dependencies { - implementation(libs.kotlin.gradle.plugin) + compileOnly(libs.kotlin.gradle.plugin) + compileOnly(libs.android.gradle.plugin) + compileOnly(libs.android.gradle.plugin.api) testImplementation(libs.kotlin.gradle.plugin) testImplementation(gradleTestKit()) @@ -43,16 +47,19 @@ dependencies { } tasks.test { - val forwardOutput: Boolean = (properties.getOrDefault("gradle.test.forward.output", "false") - as String).toBooleanStrictOrNull() ?: false + useJUnitPlatform() - systemProperty("gradle.test.forward.output", forwardOutput) + val protocGen = gradle.includedBuild("protoc-gen") + dependsOn(protocGen.task(":grpc:publishAllPublicationsToBuildRepoRepository")) + dependsOn(protocGen.task(":protobuf:publishAllPublicationsToBuildRepoRepository")) - useJUnitPlatform() + 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")) - val includedBuild = gradle.includedBuild("protoc-gen") - dependsOn(includedBuild.task(":grpc:publishAllPublicationsToBuildRepoRepository")) - dependsOn(includedBuild.task(":protobuf:publishAllPublicationsToBuildRepoRepository")) + dependsOn(":publishAllPublicationsToBuildRepoRepository") } // This block is needed to show plugin tasks on --dry-run @@ -103,7 +110,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/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/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/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 2203bb6d7..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,26 +24,28 @@ 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.Internal +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.SkipWhenEmpty import javax.inject.Inject /** * Abstract base class for `buf` tasks. */ public abstract class BufExecTask @Inject constructor( - @Internal - public val properties: Properties -) : DefaultTask() { - init { - group = PROTO_GROUP - } + properties: ProtoTask.Properties, +) : DefaultProtoTask(properties) { + // unsued, but required for Gradle to properly recognise inputs + @get:InputFiles + @get:SkipWhenEmpty + internal abstract val protoFiles: ListProperty - // list of buf task dependencies of the same type - @get:Internal - internal abstract val bufTaskDependencies: SetProperty + // unsued, but required for Gradle to properly recognise inputs + @get:InputFiles + internal abstract val importProtoFiles: ListProperty @get:InputFile internal abstract val bufExecutable: Property @@ -53,44 +53,6 @@ public abstract class BufExecTask @Inject constructor( @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 flavor this task is associated with. - */ - public val flavor: String, - /** - * Name of the android build type this task is associated with. - */ - public val buildType: String, - /** - * Name of the android variant this task is associated with. - */ - public val variant: String, - ) : Properties(isTest, sourceSetName) - /** * The `buf` command to execute. * @@ -151,7 +113,7 @@ public abstract class BufExecTask @Inject constructor( public inline fun Project.registerBufExecTask( name: String, workingDir: Provider, - properties: BufExecTask.Properties, + properties: ProtoTask.Properties, noinline configuration: T.() -> Unit, ): TaskProvider = registerBufExecTask(T::class, name, workingDir, properties, configuration) @@ -160,12 +122,12 @@ internal fun Project.registerBufExecTask( clazz: KClass, name: String, workingDir: Provider, - properties: BufExecTask.Properties, + properties: ProtoTask.Properties, 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..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 @@ -16,8 +16,12 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskProvider import java.io.File import kotlinx.rpc.buf.BufGenerateExtension -import org.gradle.api.tasks.InputDirectory +import kotlinx.rpc.protoc.DefaultProtoSourceSet +import kotlinx.rpc.protoc.ProtoTask +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 javax.inject.Inject /** @@ -25,17 +29,15 @@ 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 +public abstract class BufGenerateTask @Inject internal constructor( + properties: ProtoTask.Properties, +) : BufExecTask(properties) { + // 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 +84,23 @@ 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 + /** + * Generated source directories by plugin name. + * + * Can be used in [SourceDirectorySet.srcDir] or similar `srcDir` functions from other source set directories. + */ + @get:OutputDirectories + public val outputSourceDirectories: Provider> = pluginNames.map { plugins -> + val out = outputDirectory.get() + plugins.map { out.resolve(it) } + } + init { command.set("generate") @@ -118,21 +132,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>, + properties: ProtoTask.Properties, 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, - ) - return registerBufExecTask(bufGenerateTaskName, provider { workingDir }, properties) { group = PROTO_GROUP description = "Generates code from .proto files using 'buf generate'" @@ -145,8 +154,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 deleted file mode 100644 index a4c57ff7e..000000000 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufTasks.kt +++ /dev/null @@ -1,595 +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 - - // android - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.flavor] matches the given flavor. - * - * Only returns Android tasks. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingFlavor("freeApp") - * } - * ``` - */ - public fun matchingFlavor(flavor: String): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.buildType] matches the given buildType. - * - * Only returns Android tasks. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingBuildType("debug") - * } - * ``` - */ - public fun matchingBuildType(buildType: String): BufTasks - - /** - * Filters tasks by where [BufExecTask.AndroidProperties.variant] matches the given variant. - * - * Only returns Android tasks. - * - * ```kotlin - * rpc.protoc { - * buf.tasks.all().matchingVariant("freeAppDebug") - * } - * ``` - */ - public fun matchingVariant(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.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.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.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.isTest }, kClass) - } - - override fun nonTestTasks(): BufTasks { - return BufTasksImpl(project, collection.matching { !it.properties.isTest }, kClass) - } - - override fun matchingFlavor(flavor: String): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - (it.properties as? BufExecTask.AndroidProperties)?.flavor == flavor - }, - kClass = kClass, - ) - } - - override fun matchingBuildType(buildType: String): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - (it.properties as? BufExecTask.AndroidProperties)?.buildType == buildType - }, - kClass = kClass, - ) - } - - override fun matchingVariant(variant: String): BufTasks { - return BufTasksImpl( - project = project, - collection = collection.matching { - (it.properties as? BufExecTask.AndroidProperties)?.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 2f7bf07ce..72541bf00 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -7,26 +7,30 @@ package kotlinx.rpc.protoc 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.withLegacyAndroid +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 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 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 @@ -37,19 +41,35 @@ 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) } } +internal fun Project.findOrCreateProtoSourceSets(): NamedDomainObjectContainer = + 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, ) : ProtoSourceSet, SourceDirectorySet by sourceDirectorySet { - @Inject - constructor(project: Project, protoName: String) : this( + constructor( + project: Project, + protoName: String, + ) : this( project = project, sourceDirectorySet = project.objects.sourceDirectorySet(protoName, "Proto sources for $protoName").apply { srcDirs("src/${protoName}/proto") @@ -57,10 +77,10 @@ internal open class DefaultProtoSourceSet( ) private val explicitApiModeEnabled = project.provider { - project.the().explicitApi != ExplicitApiMode.Disabled + 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) }) @@ -98,8 +118,101 @@ 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() + val generateTask: Property = project.objects.property() + + // 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) + + // 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 androidDependencies: SetProperty = project.objects.setProperty() + + // only set for variant.name sourceSets + val androidProperties: Property = project.objects.property() + + 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() }) + } + + 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") + } + + 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}", + ) + } + + extendsFrom += protoSourceSet + + 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 +226,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?, + ): DefaultProtoSourceSet { + val container = findOrCreateProtoSourceSets() val protoSourceSet = container.maybeCreate(languageSourceSetName) as DefaultProtoSourceSet languageSourceSet?.let { protoSourceSet.languageSourceSets.add(it) } @@ -132,25 +238,38 @@ internal fun Project.createProtoExtensions() { return protoSourceSet } - project.withLazyKotlinJvmExtension { - sourceSets.all { - findOrCreateAndConfigure(name, this) + // CCE free check for kotlin + withKotlin { + withKotlinSourceSets { extension -> + extension.sourceSets.all { + findOrCreateAndConfigure(name, this) + } } + } - project.extensions.configure("sourceSets") { - all { - val protoSourceSet = findOrCreateAndConfigure(name, this) - - findOrCreate(PROTO_SOURCE_SET_EXTENSION_NAME) { - extensions.add(PROTO_SOURCE_SET_EXTENSION_NAME, protoSourceSet) - } + // CCE free check for android + withLegacyAndroid { + withAndroidSourceSets { sourceSets -> + sourceSets.all { + findOrCreateAndConfigure(name, this) } } } - project.withLazyKotlinKmpExtension { + withLazyJavaPluginExtension { sourceSets.all { - findOrCreateAndConfigure(name, this) + val protoSourceSet = findOrCreateAndConfigure(name, this) + + findOrCreate(PROTO_SOURCE_SET_EXTENSION_NAME) { + extensions.add(PROTO_SOURCE_SET_EXTENSION_NAME, protoSourceSet) + } } } } + +internal fun DefaultProtoSourceSet.protoTaskProperties(): ProtoTask.Properties { + return androidProperties.orNull ?: ProtoTask.Properties( + isTest = name.lowercase().endsWith("test"), + 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 8dc9443af..f26c370c1 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,9 @@ 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 import kotlinx.rpc.buf.configureBufExecutable import kotlinx.rpc.buf.tasks.BufExecTask @@ -16,27 +19,35 @@ 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.KmpLibraryAndroidLeafSourceSets +import kotlinx.rpc.protoc.android.LegacyAndroidRootSourceSets +import kotlinx.rpc.protoc.android.dependencySourceSets +import kotlinx.rpc.protoc.android.kotlinProxyFromAndroidOriginSourceSetName +import kotlinx.rpc.util.AndroidComponents +import kotlinx.rpc.util.KotlinPluginId import kotlinx.rpc.util.ensureDirectoryExists -import kotlinx.rpc.util.kotlinJvmExtensionOrNull -import kotlinx.rpc.util.kotlinKmpExtensionOrNull +import kotlinx.rpc.util.hasAndroidKmpLibrary +import kotlinx.rpc.util.hasLegacyAndroid +import kotlinx.rpc.util.kotlinPluginId +import kotlinx.rpc.util.withLegacyAndroid +import kotlinx.rpc.util.withKotlin +import kotlinx.rpc.util.withLazyLegacyAndroidComponentsExtension 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.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask +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 @@ -45,7 +56,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) { @@ -87,16 +98,74 @@ internal open class DefaultProtocExtension @Inject constructor( defaultOptions() } - project.protoSourceSets.all { - if (this !is DefaultProtoSourceSet) { - return@all + // no way to configure tasks before evaluation is done + project.afterEvaluate { + configureMultiplatformWithAndroidSourceSets configure@{ protoSourceSet -> + // configureTasks is done in configureLegacyAndroidVariants even for KMP + if (protoSourceSet.isLegacyAndroid.get()) { + protoSourceSet.setupDefaultImports(protoSourceSets) + return@configure + } + + configureTasks(protoSourceSet) + } + + protoSourceSets.all { + if (this !is DefaultProtoSourceSet) { + return@all + } + + withFullyInitializedProtoSourceSet(this) { protoSourceSet -> + // configureTasks is done in configureLegacyAndroidVariants + if (protoSourceSet.isLegacyAndroid.get()) { + return@withFullyInitializedProtoSourceSet + } + + if (hasAndroidKmpLibrary) { + project.tryConfigureKmpLibAndroidVariant(protoSourceSet) + } + + configureTasks(protoSourceSet) + } + + sourceSetCreated(this) + } + } + + project.withLegacyAndroid { + withLazyLegacyAndroidComponentsExtension { + configureLegacyAndroidVariants( + project = project, + isKmp = project.kotlinPluginId == KotlinPluginId.MULTIPLATFORM, + onSourceSet = ::whenSourceSetIsCreated, + configureTasks = ::configureTasks + ) } + } + } - project.configureTasks(this) + 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") options.put("generateComments", buf.generate.comments.copyComments) @@ -105,7 +174,11 @@ internal open class DefaultProtocExtension @Inject constructor( } @Suppress("detekt.LongMethod", "detekt.CyclomaticComplexMethod", "detekt.ThrowsCount") - private fun Project.configureTasks(protoSourceSet: DefaultProtoSourceSet) { + private fun configureTasks(protoSourceSet: DefaultProtoSourceSet) { + if (protoSourceSet.tasksConfigured.get()) { + return + } + val baseName = protoSourceSet.name val buildSourceSetsDir = project.protoBuildDirSourceSets.resolve(baseName) @@ -117,10 +190,9 @@ 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 - val importsProvider = protoSourceSet.getImports(protoSourceSets) + protoSourceSet.setupDefaultImports(project.protoSourceSets) - val includedProtocPlugins = provider { + val includedProtocPlugins = project.provider { protoSourceSet.plugins.get().also { list -> list.forEach { plugin -> if (!plugin.artifact.isPresent) { @@ -133,48 +205,54 @@ internal open class DefaultProtocExtension @Inject constructor( } } - val protoFiles = protoSourceSet as SourceDirectorySet + val protoFilesDirectorySet = protoSourceSet as SourceDirectorySet + val properties = protoSourceSet.protoTaskProperties() - val processProtoTask = registerProcessProtoFilesTask( + val processProtoTask = project.registerProcessProtoFilesTask( name = baseName, destination = buildSourceSetsProtoDir, - protoFiles = protoFiles, + protoFilesDirectorySet = protoFilesDirectorySet, + properties = properties, ) - val processImportProtoTask = registerProcessProtoFilesImportsTask( + val processImportProtoTask = project.registerProcessProtoFilesImportsTask( name = baseName, destination = buildSourceSetsImportDir, - importsProvider = importsProvider, + importsProvider = protoSourceSet.imports, + rawImports = protoSourceSet.fileImports, + properties = properties, ) { dependsOn(processProtoTask) } - val generateBufYamlTask = registerGenerateBufYamlTask( + val generateBufYamlTask = project.registerGenerateBufYamlTask( name = baseName, buildSourceSetsDir = buildSourceSetsDir, buildSourceSetsProtoDir = buildSourceSetsProtoDir, buildSourceSetsImportDir = buildSourceSetsImportDir, - withImport = importsProvider.map { it.isNotEmpty() }, + withImport = protoSourceSet.imports.map { it.isNotEmpty() }, + properties = properties, ) { dependsOn(processProtoTask) } - val generateBufGenYamlTask = registerGenerateBufGenYamlTask( + val generateBufGenYamlTask = project.registerGenerateBufGenYamlTask( name = baseName, buildSourceSetsDir = buildSourceSetsDir, protocPlugins = includedProtocPlugins, + properties = properties, ) { dependsOn(generateBufYamlTask) } - val sourceSetsProtoDirFileTree = fileTree(buildSourceSetsProtoDir) + val sourceSetsProtoDirFileTree = project.fileTree(buildSourceSetsProtoDir) - val bufGenerateTask = registerBufGenerateTask( - sourceSetName = baseName, + val bufGenerateTask = project.registerBufGenerateTask( + protoSourceSet = protoSourceSet, workingDir = buildSourceSetsDir, - outputDirectory = protoBuildDirGenerated.resolve(baseName), - protoFilesDir = buildSourceSetsProtoDir, - importFilesDir = buildSourceSetsImportDir, + outputDirectory = project.protoBuildDirGenerated.resolve(baseName), + includedPlugins = includedProtocPlugins, + properties = properties, ) { executableFiles.addAll( includedProtocPlugins.map { list -> @@ -190,74 +268,32 @@ 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(project.protoSourceSets).mapNotNull { it.generateTask.orNull } } dependsOn(dependencies) - bufTaskDependencies.set(importsProvider.map { list -> - list.map { it.generateTask.get().name } - }) - onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } } 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 +301,59 @@ internal open class DefaultProtocExtension @Inject constructor( processProtoTask = processProtoTask, processImportProtoTask = processImportProtoTask, sourceSetsProtoDirFileTree = sourceSetsProtoDirFileTree, - importsProvider = importsProvider, - ) + properties = properties, + ) { + protoFiles.set(processProtoTask.map { it.outputs.files }) + importProtoFiles.set(processImportProtoTask.map { it.outputs.files }) + } + + protoSourceSet.tasksConfigured.set(true) } - 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() } - 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.java.srcDir(javaOutputs) } - project.kotlinJvmExtensionOrNull?.sourceSets?.all { - if (name == baseName) { - kotlin.srcDirs(kotlinSourcesProvider) + project.withKotlin { + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.kotlin.srcDir(kotlinOutputs) } } - project.kotlinKmpExtensionOrNull?.sourceSets?.all { - if (name == baseName) { - kotlin.srcDirs(kotlinSourcesProvider) + project.withLegacyAndroid { + // android + kotlin is always done in withKotlin above + languageSets.filterIsInstance().forEach { sourceSet -> + sourceSet.java.srcDirs(javaOutputs) } } } - private fun Project.configureCustomTasks( - baseName: String, + private fun languageOutputs( + includedProtocPlugins: Provider>, + bufGenerateTask: TaskProvider, + pluginFilter: (ProtocPlugin) -> Boolean, + ): Provider> { + val plugins = includedProtocPlugins.map { plugins -> + plugins.filter(pluginFilter).map { it.name }.toSet() + } + + return bufGenerateTask.flatMap { task -> + val pluginsSet = plugins.get() + task.outputSourceDirectories.map { list -> list.filter { it.name in pluginsSet } } + } + } + + private fun configureCustomTasks( protoSourceSet: DefaultProtoSourceSet, buildSourceSetsDir: File, generateBufYamlTask: TaskProvider, @@ -315,8 +361,11 @@ internal open class DefaultProtocExtension @Inject constructor( processProtoTask: TaskProvider, processImportProtoTask: TaskProvider, sourceSetsProtoDirFileTree: ConfigurableFileTree, - importsProvider: Provider>, + properties: ProtoTask.Properties, + configure: BufExecTask.() -> Unit, ) { + val baseName = protoSourceSet.name + buf.tasks.customTasks.all { val taskCapital = name.replaceFirstChar { it.uppercase() } fun taskName(baseName: String): String { @@ -324,14 +373,9 @@ internal open class DefaultProtocExtension @Inject constructor( return "buf$taskCapital$baseCapital" } - val properties = BufExecTask.Properties( - isTest = baseName.lowercase().endsWith("test"), - sourceSetName = baseName, - ) - - registerBufExecTask( + project.registerBufExecTask( clazz = kClass, - workingDir = provider { buildSourceSetsDir }, + workingDir = project.provider { buildSourceSetsDir }, properties = properties, name = taskName(baseName), ) { @@ -341,77 +385,328 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(processImportProtoTask) val dependencies = project.provider { - protoSourceSet.getDependsOn(protoSourceSets).map { dependency -> + protoSourceSet.getDependsOnTasksOf(project.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 - } - }) + configure() onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } } } } - private fun DefaultProtoSourceSet.getImports( + private fun DefaultProtoSourceSet.setupDefaultImports(protoSourceSets: ProtoSourceSets) { + importsAllFrom(getDependsOnImports(protoSourceSets)) + + // isLegacyAndroid -> not a 'com.android.kotlin.multiplatform.library' source set, + // other imports are set up on variant + if (isLegacyAndroid.get()) { + return + } + + if (name.lowercase().endsWith("test")) { + importsAllFrom(getImportsCorrespondingMainSourceSets(protoSourceSets)) + } + } + + private fun DefaultProtoSourceSet.getImportsCorrespondingMainSourceSets( protoSourceSets: ProtoSourceSets, - ): Provider> { - return when { - name.lowercase().endsWith("main") -> { - getImportsForTestOrMain(protoSourceSets) - } + ): Provider> { + return languageSourceSets.map { list -> + val kotlin = list.filterIsInstance() + val java = list.filterIsInstance() + + kotlin.mapNotNull { + correspondingMainNameSourceSet(it.name, protoSourceSets) + } + java.mapNotNull { + correspondingMainNameSourceSet(it.name, protoSourceSets) + }.distinct() + } + } - name.lowercase().endsWith("test") -> { - val main = getImportsForTestOrMain(protoSourceSets) - val test = (project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet) - ?.getImportsForTestOrMain(protoSourceSets) + private fun DefaultProtoSourceSet.getDependsOnImports(protoSourceSets: ProtoSourceSets): Provider> { + return languageSourceSets.map { list -> + val kotlin = list.filterIsInstance() - if (test == null) main else main.zip(test) { a, b -> a + b } + kotlin.flatMap { + it.dependsOn.mapNotNull { dep -> protoSourceSets.getByName(dep.name) } } + } + } +} + +// 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 + + // 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 instrumentedTestSourceSet = project.protoSourceSets + .findByName(KmpLibraryAndroidLeafSourceSets.DeviceTest.sourceSetName) + + unitTestSourceSet?.importsFrom(protoSourceSet) + instrumentedTestSourceSet?.importsFrom(protoSourceSet) + } else { + val mainRoot = project.protoSourceSets + .findByName(KmpLibraryAndroidLeafSourceSets.Main.sourceSetName) + + if (mainRoot != null) { + protoSourceSet.importsFrom(mainRoot) + } + } + + val properties = ProtoTask.AndroidProperties( + isTest = sourceSetEntry != KmpLibraryAndroidLeafSourceSets.Main, + isInstrumentedTest = sourceSetEntry == KmpLibraryAndroidLeafSourceSets.DeviceTest, + isUnitTest = sourceSetEntry == KmpLibraryAndroidLeafSourceSets.HostTest, + sourceSetNames = setOf(protoSourceSet.name), + buildType = null, + flavors = emptyList(), + variant = null, + ) + + protoSourceSet.androidProperties.set(properties) +} + +// init isLegacyAndroid +private fun Project.withFullyInitializedProtoSourceSet( + sourceSet: ProtoSourceSet, + body: (DefaultProtoSourceSet) -> Unit, +) { + if (sourceSet !is DefaultProtoSourceSet) { + return + } + + val kotlinPluginId = kotlinPluginId + + val languageSets = sourceSet.languageSourceSets.get() + + val anyLegacyAndroid = hasLegacyAndroid && languageSets.any { it is AndroidSourceSet } + val anyKotlin = kotlinPluginId != null && languageSets.any { it is KotlinSourceSet } + + if (anyLegacyAndroid && !anyKotlin) { + sourceSet.isLegacyAndroid.set(true) + body(sourceSet) + return + } - else -> { - throw GradleException("Unknown source set name: $name") + 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) + body(sourceSet) + } + + KotlinPluginId.MULTIPLATFORM -> { + if (!hasLegacyAndroid || hasAndroidKmpLibrary || !anyKotlin) { + sourceSet.isLegacyAndroid.set(false) + body(sourceSet) } + // no else here, it is handled by configureMultiplatformWithAndroidSourceSets + } + + KotlinPluginId.JVM, null -> { + sourceSet.isLegacyAndroid.set(false) + body(sourceSet) } } +} - private fun DefaultProtoSourceSet.getImportsForTestOrMain( - protoSourceSets: ProtoSourceSets, - ): 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) +private fun Project.configureMultiplatformWithAndroidSourceSets(body: (DefaultProtoSourceSet) -> Unit) { + if (!hasLegacyAndroid || hasAndroidKmpLibrary || 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 + + if (protoSourceSet.tasksConfigured.orElse(false).get()) { + return@forAll } + + protoSourceSet.isLegacyAndroid.set(target is KotlinAndroidTarget) + body(protoSourceSet) } - } + list.filterIsInstance().mapNotNull { - if (it.name.endsWith("Test")) { - project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet - } else { - null + } + } +} + +private fun AndroidComponents.configureLegacyAndroidVariants( + project: Project, + isKmp: Boolean, + onSourceSet: (String?, (DefaultProtoSourceSet) -> Unit) -> Unit, + 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.getByName(variantSourceSetName) + as? DefaultProtoSourceSet ?: return@forEach + + val proxyNames = mutableListOf() + (dependencySourceSetNames + testFixtureSetNames) + .filter { it != variantSourceSetName } + .forEach { dependencyName -> + val dependencyProtoSourceSet = project.protoSourceSets.findByName(dependencyName) + ?: return@forEach + + val proxyName = dependencyName.kotlinProxyFromAndroidOriginSourceSetName(rootName) + if (proxyName != null && isKmp) { + onSourceSet(proxyName) { proxyDependency -> + proxyNames += proxyName + variantProtoSourceSet.extendsFrom(proxyDependency) + } + } + + variantProtoSourceSet.extendsFrom(dependencyProtoSourceSet) } - } + list.filterIsInstance().mapNotNull { - if (it.name == SourceSet.TEST_SOURCE_SET_NAME) { - project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet - } else { - null + + // 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) + + 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) { + variantProtoSourceSet.androidDependencies.add(mainVariantProtoSourceSet) + + variantProtoSourceSet.importsFrom(mainVariantProtoSourceSet) + } + } + + val properties = androidProperties( + rootName = rootName, + variant = variant, + sourceSetNames = dependencySourceSetNames, + ) + variantProtoSourceSet.androidProperties.set(properties) + + configureTasks(variantProtoSourceSet) } } } } -internal fun DefaultProtoSourceSet.getDependsOn(protoSourceSets: ProtoSourceSets): List { +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], + * as not all [DefaultProtoSourceSet] have associated tasks (on Android, for example) + */ +internal fun DefaultProtoSourceSet.getDependsOnTasksOf(protoSourceSets: ProtoSourceSets): List { + // 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() val kmpDependsOn = sourceSets @@ -424,24 +719,24 @@ 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("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/ProcessProtoFiles.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt index 1b317f712..7d708d177 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt @@ -4,20 +4,25 @@ 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.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(): Copy() { +public abstract class ProcessProtoFiles @Inject internal constructor( + @get:Internal + override val properties: ProtoTask.Properties, +) : Sync(), ProtoTask { init { group = PROTO_GROUP } @@ -26,48 +31,42 @@ public abstract class ProcessProtoFiles internal constructor(): Copy() { internal fun Project.registerProcessProtoFilesTask( name: String, destination: File, - protoFiles: SourceDirectorySet, + protoFilesDirectorySet: SourceDirectorySet, + properties: ProtoTask.Properties, 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) + val task = tasks.register("process${capitalName}ProtoFiles", ProcessProtoFiles::class, properties) - from(files(protoFiles.sourceDirectories)) { - include(protoFiles.includes) - exclude(protoFiles.excludes) + task.configure { + duplicatesStrategy = DuplicatesStrategy.FAIL + + from(files(protoFilesDirectorySet.sourceDirectories)) { + include(protoFilesDirectorySet.includes) + exclude(protoFilesDirectorySet.excludes) } into(destination) - doFirst { - destination.deleteRecursively() - } - configure() } + + return task } internal fun Project.registerProcessProtoFilesImportsTask( name: String, destination: File, - importsProvider: Provider>, + importsProvider: Provider>, + rawImports: ConfigurableFileCollection, + properties: ProtoTask.Properties, 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) - + val task = tasks.register("process${capitalName}ProtoFilesImports", ProcessProtoFiles::class, properties) + task.configure { duplicatesStrategy = DuplicatesStrategy.FAIL val allImports = importsProvider.map { list -> @@ -80,13 +79,12 @@ internal fun Project.registerProcessProtoFilesImportsTask( } from(allImports) + from(rawImports) into(destination) - doFirst { - destination.deleteRecursively() - } - 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 208683b20..1d50338b1 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt @@ -7,8 +7,10 @@ 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.Provider +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.SourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet @@ -114,6 +116,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/ProtoTask.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt new file mode 100644 index 000000000..7d1fba579 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTask.kt @@ -0,0 +1,105 @@ +/* + * 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( + /** + * Whether the task is for a test source set. + */ + public val isTest: Boolean, + /** + * 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 sourceSetNames: Set, + ) + + /** + * Properties of the buf task for android source sets. + * + * Can be used with [ProtoTasks] to filter tasks. + */ + public class AndroidProperties( + /** + * @see Properties.isTest + */ + isTest: Boolean, + + /** + * @see Properties.sourceSetNames + */ + sourceSetNames: Set, + + /** + * 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, sourceSetNames) +} + +/** + * 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..5b8e2ee8c --- /dev/null +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoTasks.kt @@ -0,0 +1,354 @@ +/* + * 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. + * + * 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) + +/** + * 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 + * protoTasks.testTasks() + * ``` + */ + public fun testTasks(): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.Properties.isTest] is `false`. + * + * ```kotlin + * protoTasks.nonTestTasks() + * ``` + */ + public fun nonTestTasks(): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.properties] are of type [ProtoTask.AndroidProperties]. + * + * Takes optional predicate to filter on [ProtoTask.AndroidProperties]. + * + * ```kotlin + * 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 + * protoTasks.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 + * protoTasks.matchingAndroidFlavor("freeApp") + * ``` + */ + public fun matchingAndroidFlavor(flavor: String?): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.buildType] matches the given buildType. + * + * Only returns Android tasks. + * + * ```kotlin + * protoTasks.matchingAndroidBuildType("debug") + * ``` + */ + public fun matchingAndroidBuildType(buildType: String?): ProtoTasks + + /** + * Filters tasks by where [ProtoTask.AndroidProperties.variant] matches the given variant. + * + * Only returns Android tasks. + * + * ```kotlin + * protoTasks.matchingAndroidVariant("freeAppDebug") + * ``` + */ + public fun matchingAndroidVariant(variant: String?): ProtoTasks +} + + +/** + * Filters tasks by type. + * + * ```kotlin + * protoTasks.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 { sourceSetName in it.properties.sourceSetNames }, + 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 androidTasks( + predicate: (ProtoTaskT, ProtoTask.AndroidProperties) -> Boolean, + ): ProtoTasks { + return ProtoTasksImpl(project, collection.matchingAndroid(predicate), kClass) + } + + override fun androidUnitTestTasks(): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matchingAndroid { _, properties -> + properties.isUnitTest + }, + kClass = kClass, + ) + } + + override fun androidInstrumentedTestTasks(): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matchingAndroid { _, properties -> + properties.isInstrumentedTest + }, + kClass = kClass, + ) + } + + override fun matchingAndroidFlavor(flavor: String?): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matchingAndroid { _, properties -> + if (flavor == null) { + return@matchingAndroid properties.flavors.isEmpty() + } + + properties.flavors.contains(flavor) + }, + kClass = kClass, + ) + } + + override fun matchingAndroidBuildType(buildType: String?): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matchingAndroid { _, properties -> + properties.buildType == buildType + }, + kClass = kClass, + ) + } + + override fun matchingAndroidVariant(variant: String?): ProtoTasks { + return ProtoTasksImpl( + project = project, + collection = collection.matchingAndroid { _, properties -> + properties.variant == variant + }, + kClass = 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") + final override fun toArray(generator: IntFunction?>): Array? { + @Suppress("DEPRECATION") + return super.toArray(generator) + } +} 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..b63995cbe --- /dev/null +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/android/variants.kt @@ -0,0 +1,101 @@ +/* + * 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 + +// root source sets for 'com.android.(application|library|test|dynamic-feature)' +internal enum class LegacyAndroidRootSourceSets(val stringValue: String) { + Main("main"), + Test("test"), + AndroidTest("androidTest"), + TestFixtures("testFixtures"), + ; + + override fun toString(): String = stringValue +} + +// leaf (not root as above) source sets for 'com.android.kotlin.multiplatform.library' +internal enum class KmpLibraryAndroidLeafSourceSets(val sourceSetName: String) { + Main("androidMain"), + HostTest("androidHostTest"), + DeviceTest("androidDeviceTest"), + ; + + override fun toString(): String = sourceSetName +} + +// 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) + + 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()) +} + +// 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 +// main -> androidMain +// release -> androidRelease +// test -> androidUnitTest +// testDebug -> androidUnitTestDebug +// testRelease -> androidUnitTestRelease +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 + } +} + +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/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..10dc1fa7c 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,107 @@ 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 +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.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" +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" + +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) + } + + plugins.withId(KotlinPluginId.MULTIPLATFORM.id) { + action(KotlinPluginId.MULTIPLATFORM) + } + + plugins.withId(KotlinPluginId.ANDROID.id) { + action(KotlinPluginId.ANDROID) + } +} -internal fun Project.withLazyKotlinJvmExtension(action: Action) { - plugins.withId(KOTLIN_JVM_PLUGIN_ID) { - the().apply { action.execute(this) } +internal fun Project.withKotlinSourceSets(action: (KotlinSourceSetContainer) -> Unit) { + withKotlin { + action(the()) } } -internal fun Project.withLazyKotlinKmpExtension(action: Action) { - plugins.withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { - the().apply { action.execute(this) } +internal val Project.hasLegacyAndroid: Boolean + get() = plugins.hasPlugin(ANDROID_LIBRARY) || + plugins.hasPlugin(ANDROID_APPLICATION) || + plugins.hasPlugin(ANDROID_DYNAMIC_FEATURE) || + plugins.hasPlugin(ANDROID_TEST) + +internal val Project.hasAndroidKmpLibrary: Boolean + get() = plugins.hasPlugin(ANDROID_KOTLIN_MULTIPLATFORM_LIBRARY) + +internal fun Project.withLegacyAndroid(action: LegacyAndroidApplied.() -> Unit) { + plugins.withId(ANDROID_LIBRARY) { + action(LegacyAndroidApplied(project, ANDROID_LIBRARY)) + } + + plugins.withId(ANDROID_APPLICATION) { + action(LegacyAndroidApplied(project, ANDROID_APPLICATION)) + } + + plugins.withId(ANDROID_DYNAMIC_FEATURE) { + action(LegacyAndroidApplied(project, ANDROID_DYNAMIC_FEATURE)) + } + + plugins.withId(ANDROID_TEST) { + action(LegacyAndroidApplied(project, ANDROID_TEST)) } } -internal val Project.kotlinJvmExtensionOrNull: KotlinJvmProjectExtension? get() { - return extensions.findByType() +internal class LegacyAndroidApplied(val project: Project, val id: String) + +internal fun LegacyAndroidApplied.withAndroidSourceSets(action: (NamedDomainObjectContainer) -> Unit) { + action(project.the().sourceSets) +} + +internal typealias AndroidComponents = AndroidComponentsExtension, *, out Variant> + +internal fun LegacyAndroidApplied.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()) + } } -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/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..79ef8e58a 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -5,16 +5,15 @@ package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest -import org.gradle.testkit.runner.BuildResult +import kotlinx.rpc.base.testTestsForAndroidKmpLibExist 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() { - override val isKmp: Boolean = true + override val type: Type = Type.Kmp @TestFactory fun `Minimal gRPC Configuration`() = runGrpcTest { @@ -33,15 +32,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.Default.entries.forEach { + runNonExistentTasksForSourceSet(it) + } } @TestFactory @@ -127,171 +120,455 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @TestFactory fun `KMP Hierarchy`() = runGrpcTest { - runKmpAndCheckFiles( - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.Default.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.Default.commonTest, + SSetsKmp.Default.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.commonMain, + ) + + runAndCheckFiles( + SSetsKmp.Default.nativeTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, + SSetsKmp.Default.commonTest, ) - runKmpAndCheckFiles( - SSets.commonTest, - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.Default.jvmMain, + SSetsKmp.Default.commonMain, ) - runKmpAndCheckFiles( - SSets.nativeMain, - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.Default.jvmTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.jvmMain, + SSetsKmp.Default.commonTest, ) - runKmpAndCheckFiles( - SSets.nativeTest, - SSets.commonMain, SSets.nativeMain, - SSets.commonTest, + runAndCheckFiles( + SSetsKmp.Default.jsMain, + SSetsKmp.Default.commonMain, SSetsKmp.Default.webMain, ) - runKmpAndCheckFiles( - SSets.jvmMain, - SSets.commonMain, + runAndCheckFiles( + SSetsKmp.Default.jsTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.webMain, SSetsKmp.Default.jsMain, + SSetsKmp.Default.commonTest, SSetsKmp.Default.webTest ) - runKmpAndCheckFiles( - SSets.jvmTest, - SSets.commonMain, SSets.jvmMain, - SSets.commonTest, + runAndCheckFiles( + SSetsKmp.Default.appleMain, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, ) - runKmpAndCheckFiles( - SSets.jsMain, - SSets.commonMain, SSets.webMain, + runAndCheckFiles( + SSetsKmp.Default.appleTest, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, SSetsKmp.Default.appleMain, + SSetsKmp.Default.commonTest, SSetsKmp.Default.nativeTest ) - runKmpAndCheckFiles( - SSets.jsTest, - SSets.commonMain, SSets.webMain, SSets.jsMain, - SSets.commonTest, SSets.webTest + runAndCheckFiles( + SSetsKmp.Default.macosMain, + SSetsKmp.Default.commonMain, SSetsKmp.Default.nativeMain, SSetsKmp.Default.appleMain, ) - runKmpAndCheckFiles( - SSets.appleMain, - SSets.commonMain, SSets.nativeMain, + runAndCheckFiles( + SSetsKmp.Default.macosTest, + SSetsKmp.Default.commonMain, + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.appleMain, + SSetsKmp.Default.macosMain, + SSetsKmp.Default.commonTest, + SSetsKmp.Default.nativeTest, + SSetsKmp.Default.appleTest ) - runKmpAndCheckFiles( - SSets.appleTest, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, - SSets.commonTest, SSets.nativeTest + runAndCheckFiles( + SSetsKmp.Default.macosArm64Main, + SSetsKmp.Default.commonMain, + SSetsKmp.Default.nativeMain, + SSetsKmp.Default.appleMain, + SSetsKmp.Default.macosMain, ) - runKmpAndCheckFiles( - SSets.macosMain, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, + runAndCheckFiles( + 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, ) - runKmpAndCheckFiles( - SSets.macosTest, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, - SSets.commonTest, SSets.nativeTest, SSets.appleTest + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.commonTest, + SSetsKmp.AndroidKmpLib.commonMain, ) - runKmpAndCheckFiles( - SSets.macosArm64Main, - SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, + runAndCheckFiles( + SSetsKmp.AndroidKmpLib.jvmMain, + SSetsKmp.AndroidKmpLib.commonMain, ) - 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.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 = runKmp(SSets.commonMain) + val firstRunCommonMain = runForSet(SSetsKmp.Default.commonMain) - assertOutcomes( - result = firstRunCommonMain, - sourceSet = SSets.commonMain, + firstRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.Default.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.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.Default.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.Default.commonMain) - assertOutcomes( - result = thirdRunCommonMain, - sourceSet = SSets.commonMain, + thirdRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.Default.commonMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, protoFiles = TaskOutcome.SUCCESS, - protoFilesImports = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.NO_SOURCE, ) - SSets.commonMain.sourceDir() + SSetsKmp.Default.commonMain.sourceDir() .resolve("commonMain.proto") .replace("content = 1", "content = 2") - val fourthRunCommonMain = runKmp(SSets.commonMain) + val fourthRunCommonMain = runForSet(SSetsKmp.Default.commonMain) - assertOutcomes( - result = fourthRunCommonMain, - sourceSet = SSets.commonMain, + fourthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.Default.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.Default.macosArm64Main) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.commonMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.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.Default.nativeMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -299,9 +576,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.appleMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -309,9 +585,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.macosMain, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -319,9 +594,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Main, - sourceSet = SSets.macosArm64Main, + firstRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -330,32 +604,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.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.Default.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.Default.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -363,9 +635,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.appleMain, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -373,9 +644,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosMain, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -383,9 +653,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosArm64Main, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -393,9 +662,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.nativeTest, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.nativeTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -403,9 +671,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.appleTest, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.appleTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -413,9 +680,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosTest, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -423,9 +689,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = firstRunMacosArm64Test, - sourceSet = SSets.macosArm64Test, + firstRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosArm64Test, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -434,61 +699,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.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 = runKmp(SSets.commonMain) + val fifthRunCommonMain = runForSet(SSetsKmp.Default.commonMain) - assertOutcomes( - result = fifthRunCommonMain, - sourceSet = SSets.commonMain, + fifthRunCommonMain.assertOutcomes( + sourceSet = SSetsKmp.Default.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.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.Default.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.Default.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -496,9 +758,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.appleMain, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -506,9 +767,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.macosMain, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -516,9 +776,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Main, - sourceSet = SSets.macosArm64Main, + secondRunMacosArm64Main.assertOutcomes( + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -527,32 +786,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.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.Default.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.Default.nativeMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -560,9 +817,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.appleMain, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.appleMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -570,9 +826,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosMain, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosMain, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -580,9 +835,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosArm64Main, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosArm64Main, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -590,9 +844,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.nativeTest, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.nativeTest, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -600,9 +853,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.appleTest, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.appleTest, generate = TaskOutcome.UP_TO_DATE, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -610,9 +862,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.UP_TO_DATE, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosTest, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosTest, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -620,9 +871,8 @@ class GrpcKmpProjectTest : GrpcBaseTest() { protoFilesImports = TaskOutcome.SUCCESS, ) - assertOutcomes( - result = secondRunMacosArm64Test, - sourceSet = SSets.macosArm64Test, + secondRunMacosArm64Test.assertOutcomes( + sourceSet = SSetsKmp.Default.macosArm64Test, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -631,28 +881,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.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.Default.jvmMain) + + firstRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.Default.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.Default.jvmMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.SUCCESS, bufGenYaml = TaskOutcome.SUCCESS, @@ -661,40 +909,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.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 = runKmp(SSets.jvmMain) + val secondRunJvmMain = runForSet(SSetsKmp.Default.jvmMain) - assertOutcomes( - result = secondRunJvmMain, - sourceSet = SSets.commonMain, + secondRunJvmMain.assertOutcomes( + sourceSet = SSetsKmp.Default.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.Default.jvmMain, generate = TaskOutcome.SUCCESS, bufYaml = TaskOutcome.UP_TO_DATE, bufGenYaml = TaskOutcome.UP_TO_DATE, @@ -703,56 +949,240 @@ 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) + 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) } - private fun GrpcTestEnv.runKmpAndCheckFiles( - sourceSet: SSets, - vararg imports: SSets, - clean: Boolean = true, + @TestFactory + fun `Proto Tasks Are Cached Properly Android KMP Library`() = runGrpcTest( + versionsPredicate = { testTestsForAndroidKmpLibExist() } ) { - runKmp(sourceSet).assertKmpSourceSet(sourceSet, *imports) + val firstRunCommonMain = runForSet(SSetsKmp.AndroidKmpLib.commonMain) - if (clean) { - cleanProtoBuildDir() - } + 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) } - private fun GrpcTestEnv.runKmp(sourceSet: SSets): BuildResult { - return runGradle(bufGenerate(sourceSet)) + @TestFactory + fun `Buf Tasks`() = runGrpcTest { + runGradle("test_tasks", "--no-configuration-cache") } - private fun GrpcTestEnv.assertOutcomes( - result: BuildResult, - sourceSet: SSets, - generate: TaskOutcome? = null, - bufYaml: TaskOutcome? = null, - bufGenYaml: TaskOutcome? = null, - protoFiles: TaskOutcome? = null, - protoFilesImports: TaskOutcome? = null, + @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() } ) { - 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))) + runGradle("test_tasks", "--no-configuration-cache") } @TestFactory - fun `Buf Tasks`() = runGrpcTest { + 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 f006bb3f4..9761bf4e5 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -17,31 +17,61 @@ 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 +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() } + KotlinVersion(major, minor, patch) + } + + val androidSemver = run { + val (major, minor, patch) = android.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) +} + +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"), - 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.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) @@ -51,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) { @@ -70,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) @@ -103,12 +137,24 @@ abstract class BaseTest { val buildScriptFile = projectDir.resolve("build.gradle.kts") 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(""" + println( + """ Setup project '$projectName' - in directory: ${projectDir.absolutePathString()} - from directory: ${testTemplateDirectory.absolutePathString()} - """.trimIndent()) + """.trimIndent() + ) } private fun runGradleInternal( @@ -119,9 +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) - .withPluginClasspath() + .forwardOutput() .withArguments( listOfNotNull( task, @@ -130,29 +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() - } catch (e: Throwable) { - val output = testEnv.latestBuild?.output - if (output != null) { - println("Latest gradle build output:") - println(output) - } else { - println("No gradle build output available") - } - throw e - } + testEnv.body() } open inner class TestEnv( @@ -193,14 +224,12 @@ 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 { - 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 7b6203a58..e90a4aa0c 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,47 @@ 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 runGrpcTest(test: GrpcTestEnv.() -> Unit): Stream = runWithAllGradleVersions { + protected fun GrpcTestEnv.runNonExistentTasksForSourceSet(set: SSets) { + runNonExistentTask(bufGenerate(set)) + runNonExistentTask(processProtoFiles(set)) + runNonExistentTask(processProtoFilesImports(set)) + runNonExistentTask(generateBufYaml(set)) + runNonExistentTask(generateBufGenYaml(set)) + } + + protected fun runGrpcTest( + versionsPredicate: VersionsPredicate = { true }, + test: GrpcTestEnv.() -> Unit, + ): Stream = runWithGradleVersions(versionsPredicate) { 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 +105,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 +128,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") } } } @@ -107,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") } } } @@ -159,10 +203,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 +230,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 +252,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 +282,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,24 +320,55 @@ abstract class GrpcBaseTest : BaseTest() { } } - fun BuildResult.assertKmpSourceSet( + fun GrpcTestEnv.runAndCheckFiles( sourceSet: SSets, vararg imports: SSets, + extended: List = emptyList(), ) { - val ktFile = "${sourceSet.capital}.kt" - val importsSet = imports.toSet() + 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 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() + + val generateFor = extendedProto + sourceSet assertTaskExecuted( sourceSet = sourceSet, - protoFiles = listOf(Path("${sourceSet.name}.proto")), - importProtoFiles = imports.map { + 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 }, ) } @@ -270,8 +378,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.Default.commonMain + Type.Jvm -> SSetsJvm.main + Type.Android -> SSetsAndroid.Default.main + } + + val testSourceSet: SSets = when (type) { + Type.Kmp -> SSetsKmp.Default.commonTest + Type.Jvm -> SSetsJvm.test + Type.Android -> SSetsAndroid.Default.test + } val bufGenerateCommonMain = bufGenerate(mainSourceSet) val bufGenerateCommonTest = bufGenerate(testSourceSet) @@ -319,21 +436,110 @@ abstract class GrpcBaseTest : BaseTest() { .resolve(name) .resolve("proto") } + + fun SSets.applicable(): Boolean { + return versions.kotlinSemver >= minKotlin + } + } + + interface SSets { + val minKotlin: KotlinVersion + val name: String + fun all(): List } - @Suppress("EnumEntryName") - enum class SSets { + enum class SSetsJvm(override val minKotlin: KotlinVersion = KtVersion.v2_0_0) : SSets { main, test, + ; - commonMain, commonTest, - jvmMain, jvmTest, - androidMain, androidTest, - webMain, webTest, - jsMain, jsTest, - nativeMain, nativeTest, - appleMain, appleTest, - macosMain, macosTest, - macosArm64Main, macosArm64Test, + override fun all(): List { + return entries + } + } + + 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 + } + } + + 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, + testFixtures, testFixturesDebug, testFixturesRelease, + androidTest, + + // executable + debug, release, + testDebug, testRelease, + androidTestDebug, + ; + + 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..1f465b5b2 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_default/build.gradle.kts @@ -0,0 +1,235 @@ +/* + * 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 { + 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: 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, + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + "bufGenerateDebug", + "bufGenerateRelease", + ) + + assertTasks( + "testTasks", genTasks.testTasks(), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateTestRelease", + ) + + assertTasks( + "unit test tasks", genTasks.androidUnitTestTasks(), + "bufGenerateTestDebug", + "bufGenerateTestRelease", + ) + + assertTasks( + "instrumented test tasks", genTasks.androidInstrumentedTestTasks(), + "bufGenerateAndroidTestDebug", + ) + + assertTasks( + "nonTestTasks", genTasks.nonTestTasks(), + "bufGenerateDebug", + "bufGenerateRelease", + ) + + assertTasks("nonTestTasks testTasks", genTasks.nonTestTasks().testTasks()) + + 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", + genTasks.matchingSourceSet("debug"), + "bufGenerateDebug", + ) + + assertTasks( + "matchingSourceSet testDebug", + genTasks.matchingSourceSet("testDebug"), + "bufGenerateTestDebug", + ) + + assertTasks( + "matchingSourceSet androidTestDebug", + genTasks.matchingSourceSet("androidTestDebug"), + "bufGenerateAndroidTestDebug", + ) + + assertTasks( + "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), + ) + + assertTasks( + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType("debug"), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateDebug", + ) + + assertTasks( + "matchingAndroidBuildType release", genTasks.matchingAndroidBuildType("release"), + "bufGenerateTestRelease", + "bufGenerateRelease", + ) + + assertTasks( + "matchingAndroidVariant debug", genTasks.matchingAndroidVariant("debug"), + "bufGenerateAndroidTestDebug", + "bufGenerateTestDebug", + "bufGenerateDebug", + ) + + assertTasks( + "matchingAndroidBuildType debug unit tests", genTasks.matchingAndroidBuildType("debug").androidUnitTestTasks(), + "bufGenerateTestDebug", + ) + + assertTasks( + "matchingAndroidBuildType debug android tests", genTasks.matchingAndroidBuildType("debug").androidInstrumentedTestTasks(), + "bufGenerateAndroidTestDebug", + ) + + val allTasks = protoTasks.buf + + 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..d4e43d8d8 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcAndroidProjectTest/buf_tasks_extended/build.gradle.kts @@ -0,0 +1,570 @@ +/* + * 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 { + 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: 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, + + // 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( + "unit test tasks", genTasks.androidUnitTestTasks(), + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappDebug", + "bufGenerateTestX86RetailappRelease", + ) + + assertTasks( + "instrumented test tasks", genTasks.androidInstrumentedTestTasks(), + // 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"), + + "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", + genTasks.matchingSourceSet("armFreeappDebug"), + "bufGenerateArmFreeappDebug", + ) + + assertTasks( + "matchingSourceSet testArmFreeappDebug", + genTasks.matchingSourceSet("testArmFreeappDebug"), + "bufGenerateTestArmFreeappDebug", + ) + + assertTasks( + "matchingSourceSet androidTestArmFreeappDebug", + genTasks.matchingSourceSet("androidTestArmFreeappDebug"), + "bufGenerateAndroidTestArmFreeappDebug", + ) + + assertTasks( + "matchingAndroidFlavor freeapp", genTasks.matchingAndroidFlavor("freeapp"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86FreeappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86FreeappRelease", + ) + + assertTasks( + "matchingAndroidFlavor arm", genTasks.matchingAndroidFlavor("arm"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappDebug", + "bufGenerateTestArmRetailappRelease", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappDebug", + "bufGenerateArmRetailappRelease", + ) + + assertTasks( + "matchingAndroidBuildType debug", genTasks.matchingAndroidBuildType("debug"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + "bufGenerateAndroidTestArmRetailappDebug", + "bufGenerateAndroidTestX86FreeappDebug", + "bufGenerateAndroidTestX86RetailappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + "bufGenerateTestArmRetailappDebug", + + // test x86 + "bufGenerateTestX86FreeappDebug", + "bufGenerateTestX86RetailappDebug", + + // arm + "bufGenerateArmFreeappDebug", + "bufGenerateArmRetailappDebug", + + // x86 + "bufGenerateX86FreeappDebug", + "bufGenerateX86RetailappDebug", + ) + + assertTasks( + "matchingAndroidBuildType relase", genTasks.matchingAndroidBuildType("release"), + + // test arm + "bufGenerateTestArmFreeappRelease", + "bufGenerateTestArmRetailappRelease", + + // test x86 + "bufGenerateTestX86FreeappRelease", + "bufGenerateTestX86RetailappRelease", + + // arm + "bufGenerateArmFreeappRelease", + "bufGenerateArmRetailappRelease", + + // x86 + "bufGenerateX86FreeappRelease", + "bufGenerateX86RetailappRelease", + ) + + assertTasks( + "matchingAndroidVariant armFreeappDebug", genTasks.matchingAndroidVariant("armFreeappDebug"), + + // android test + "bufGenerateAndroidTestArmFreeappDebug", + + // test arm + "bufGenerateTestArmFreeappDebug", + + // arm + "bufGenerateArmFreeappDebug", + ) + + assertTasks( + "matchingAndroidVariant armFreeappDebug udet tests", genTasks.matchingAndroidVariant("armFreeappDebug").androidUnitTestTasks(), + "bufGenerateTestArmFreeappDebug", + ) + + assertTasks( + "armFreeappDebug armFreeappDebug android tests", genTasks.matchingAndroidVariant("armFreeappDebug").androidInstrumentedTestTasks(), + "bufGenerateAndroidTestArmFreeappDebug", + ) + + val allTasks = protoTasks.buf + + 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; +} 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..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 @@ -7,15 +7,15 @@ 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 { 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) { +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -37,17 +37,24 @@ 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() + ) } } 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 +62,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/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..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,12 +7,12 @@ 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 { kotlin("multiplatform") version "" - id("org.jetbrains.kotlinx.rpc.plugin") + id("org.jetbrains.kotlinx.rpc.plugin") version "" } kotlin { @@ -23,8 +23,7 @@ kotlin { macosArm64() } - -public abstract class BufLintTask @Inject constructor(properties: BufExecTask.Properties) : BufExecTask(properties) { +public abstract class BufLintTask @Inject constructor(properties: ProtoTask.Properties) : BufExecTask(properties) { init { command.set("lint") args.set(emptyList()) @@ -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( """ @@ -63,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, @@ -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", ) @@ -106,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", "bufGenerateWebTest", - "bufGenerateJsMain", - ) - - val allTasks = rpc.protoc.get().buf.tasks.all() + val allTasks = protoTasks.buf assertTasks( "all", allTasks, @@ -222,7 +127,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 +137,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 +150,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,16 +162,8 @@ tasks.register("test_tasks") { "bufLintMacosMain", "bufLintMacosTest", "bufLintMacosArm64Main", "bufLintMacosArm64Test", "bufLintJvmMain", "bufLintJvmTest", - "bufLintWebMain", "bufLintWebTest", + "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/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/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/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/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/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; +} 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/protobuf/protobuf-core/build.gradle.kts b/protobuf/protobuf-core/build.gradle.kts index 9118bdf97..07d2945b2 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 @@ -80,23 +83,12 @@ rpc { buf.generate.comments { includeFileLevelComments = false } - - buf.generate.allTasks().matchingKotlinSourceSet(kotlin.sourceSets.commonMain).configureEach { - includeWkt = true - outputDirectory = generatedCodeDir(properties.sourceSetName) - } } } -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) - } +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 53439d359..34b2ba986 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 @@ -46,13 +49,13 @@ rpc { buf.generate.comments { includeFileLevelComments = false } - - buf.generate.allTasks().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("") @@ -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( diff --git a/versions-root/libs.versions.toml b/versions-root/libs.versions.toml index 287ff7a37..f95b895a8 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.4.0" [libraries] # kotlinx.rpc – references to the included builds @@ -138,6 +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", 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" }