From 7b59c4d298806d12c0907d792cc15f33d08c76c4 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Wed, 19 Nov 2025 15:36:32 +0100 Subject: [PATCH 1/4] Added full KMP support with proto source sets hierarchy and better plugin management --- .../main/kotlin/conventions-jvm.gradle.kts | 5 +- .../main/kotlin/util/other/generateSource.kt | 30 +- .../src/main/kotlin/kotlinx/rpc/Extensions.kt | 51 +++- .../kotlin/kotlinx/rpc/buf/BufExtensions.kt | 9 +- .../kotlinx/rpc/buf/tasks/BufExecTask.kt | 2 +- .../kotlinx/rpc/buf/tasks/BufGenerateTask.kt | 2 +- .../rpc/buf/tasks/GenerateBufGenYaml.kt | 8 +- .../kotlinx/rpc/buf/tasks/GenerateBufYaml.kt | 4 +- ...gureLocalProtocGenDevelopmentDependency.kt | 5 +- .../rpc/protoc/DefaultProtoSourceSet.kt | 147 +++++---- .../rpc/protoc/DefaultProtocExtension.kt | 245 +++++++++++---- .../kotlinx/rpc/protoc/ProcessProtoFiles.kt | 48 ++- .../kotlinx/rpc/protoc/ProtoSourceSet.kt | 81 +++-- .../kotlinx/rpc/protoc/ProtocExtension.kt | 13 +- .../kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt | 19 +- .../main/kotlin/kotlinx/rpc/protoc/consts.kt | 5 + .../src/main/kotlin/kotlinx/rpc/util/kgp.kt | 13 +- .../kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt | 281 ++++++++++++------ .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 46 +-- .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 78 ++--- .../build.gradle.kts | 13 +- .../build.gradle.kts | 8 +- .../build.gradle.kts | 3 +- .../src/main/proto/exclude.proto | 0 .../src/main/proto/exclude/no1.proto | 0 .../src/main/proto/exclude/no2.proto | 0 .../src/main/proto/ok/ok.proto | 0 .../src/main/proto/some.proto | 0 .../src/test/proto/include.proto | 0 .../src/test/proto/include/yes1.proto | 0 .../src/test/proto/include/yes2.proto | 0 .../src/test/proto/other.proto | 0 .../proto/some/package/hello/world/file.proto | 0 .../build.gradle.kts | 52 ++++ .../src/main/proto/no.proto | 0 .../src/main/proto/some.proto | 5 + .../src/test/proto/no2.proto | 0 .../src/test/proto/other.proto | 5 + .../java_source_sets/build.gradle.kts | 29 ++ .../java_source_sets/src/main/proto/no.proto | 0 .../src/main/proto/some.proto | 5 + .../java_source_sets/src/test/proto/no2.proto | 0 .../src/test/proto/other.proto | 5 + .../no_grpc/build.gradle.kts | 6 - .../build.gradle.kts | 9 +- protobuf/protobuf-core/build.gradle.kts | 19 +- tests/protobuf-conformance/build.gradle.kts | 6 +- 47 files changed, 867 insertions(+), 390 deletions(-) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/build.gradle.kts (91%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/main/proto/exclude.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/main/proto/exclude/no1.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/main/proto/exclude/no2.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/main/proto/ok/ok.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/main/proto/some.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/test/proto/include.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/test/proto/include/yes1.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/test/proto/include/yes2.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/test/proto/other.proto (100%) rename gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/{exclude_and_include_in_protosourcesets => exclude_and_include_in_proto_sourcesets}/src/test/proto/some/package/hello/world/file.proto (100%) create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/main/proto/no.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/main/proto/some.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/no2.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/other.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/main/proto/no.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/main/proto/some.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/test/proto/no2.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/test/proto/other.proto diff --git a/gradle-conventions/src/main/kotlin/conventions-jvm.gradle.kts b/gradle-conventions/src/main/kotlin/conventions-jvm.gradle.kts index 89c0c1b2e..48b394dcc 100644 --- a/gradle-conventions/src/main/kotlin/conventions-jvm.gradle.kts +++ b/gradle-conventions/src/main/kotlin/conventions-jvm.gradle.kts @@ -20,7 +20,10 @@ java { withSourcesJar() } -configureJavaCompatibility(8) +if (project.name != "gradle-plugin") { + configureJavaCompatibility(8) +} + configureKotlinCompatibility("2.0.0") kotlin { diff --git a/gradle-conventions/src/main/kotlin/util/other/generateSource.kt b/gradle-conventions/src/main/kotlin/util/other/generateSource.kt index 4fa33c3f5..3cd91c096 100644 --- a/gradle-conventions/src/main/kotlin/util/other/generateSource.kt +++ b/gradle-conventions/src/main/kotlin/util/other/generateSource.kt @@ -40,20 +40,28 @@ fun Project.generateSource( text: String, chooseSourceSet: NamedDomainObjectContainer.() -> NamedDomainObjectProvider, ) { - val sourcesDir = File(project.layout.buildDirectory.asFile.get(), "generated-sources/kotlin") - - val generatePluginVersionTask = - tasks.register("generateSources_$name", name, text, sourcesDir) - withKotlinJvmExtension { - chooseSourceSet(sourceSets).configure { - kotlin.srcDir(generatePluginVersionTask.map { it.sourcesDir }) - } + generateSource(sourceSets, name, text, chooseSourceSet) } withKotlinKmpExtension { - chooseSourceSet(sourceSets).configure { - kotlin.srcDir(generatePluginVersionTask.map { it.sourcesDir }) - } + generateSource(sourceSets, name, text, chooseSourceSet) + } +} + +private fun Project.generateSource( + sourceSets: NamedDomainObjectContainer, + name: String, + text: String, + chooseSourceSet: NamedDomainObjectContainer.() -> NamedDomainObjectProvider, +) { + val sourceSet = chooseSourceSet(sourceSets) + val sourcesDir = File(project.layout.buildDirectory.asFile.get(), "generated-sources/kotlin/${sourceSet.name}") + + val generatePluginVersionTask = + tasks.register("generateSources_$name", name, text, sourcesDir) + + sourceSet.configure { + kotlin.srcDir(generatePluginVersionTask.map { it.sourcesDir }) } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt index e0a595383..f8c5e2606 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt @@ -24,7 +24,7 @@ internal fun Project.rpcExtensionOrNull(): RpcExtension? = extensions.findByType internal fun Project.rpcExtension(): RpcExtension = rpcExtensionOrNull() ?: error("Rpc extension not found. Please apply the plugin to the project") -public open class RpcExtension @Inject constructor(objects: ObjectFactory, private val project: Project) { +public open class RpcExtension @Inject constructor(objects: ObjectFactory, project: Project) { /** * Controls `@Rpc` [annotation type-safety](https://github.com/Kotlin/kotlinx-rpc/pull/240) compile-time checkers. * @@ -48,25 +48,60 @@ public open class RpcExtension @Inject constructor(objects: ObjectFactory, priva configure.execute(strict) } - internal val protocApplied = AtomicBoolean(false) /** * Protoc settings. + * + * Can't be called in a lazy context if not initialized. */ - public val protoc: ProtocExtension by lazy { - if (protocApplied.get()) { - error("Illegal access to protoc extension during DefaultProtocExtension.init") + public val protoc: Provider = project.provider { + if (!protocApplied.get()) { + error(""" + Protoc extension was not initialized. + Please, apply the plugin by using the following declaration: + + rpc { + protoc() + } + + If the error persists, check if you are using this property lazily, + i.e. by calling 'protoc.map { }' and not 'protoc.get()', + otherwise the order of initialization may be wrong. + """.trimIndent()) } - protocApplied.set(true) - objects.newInstance() + protocInternal } /** * Protoc settings. */ public fun protoc(configure: Action = Action {}) { - configure.execute(protoc) + configure.execute(protocInternal) + } + + internal val protocApplied = AtomicBoolean(false) + + internal val protocInternal by lazy { + if (protocApplied.get()) { + error("Illegal access to protoc extension during DefaultProtocExtension.init") + } + + protocApplied.set(true) + objects.newInstance().apply { + callbacks.forEach { it.execute(this) } + } + } + + private val callbacks = mutableListOf>() + + internal fun whenProtocApplied(action: Action) { + if (protocApplied.get()) { + action.execute(protoc.get()) + return + } + + callbacks.add(action) } } 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 449ae074b..b6cf264da 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt @@ -24,7 +24,7 @@ import kotlin.time.Duration * * @see buf commands */ -public open class BufExtension @Inject constructor(objects: ObjectFactory) { +public open class BufExtension @Inject internal constructor(objects: ObjectFactory) { /** * `--config` argument value. * @@ -101,7 +101,7 @@ public open class BufExtension @Inject constructor(objects: ObjectFactory) { /** * Allows registering custom Buf tasks that can operate on the generated workspace. */ -public open class BufTasksExtension @Inject constructor(internal val project: Project) { +public open class BufTasksExtension @Inject internal constructor(internal val project: Project) { /** * Registers a custom Buf task that operates on the generated workspace. @@ -109,9 +109,9 @@ public open class BufTasksExtension @Inject constructor(internal val project: Pr * Name conventions: * `lint` input for [name] will result in tasks * named 'bufLintMain' and 'bufLintTest' for Kotlin/JVM projects - * and 'bufLintCommonMain' and 'bufLintCommonTest' for Kotlin/Multiplatform projects. + * and 'bufLintCommonMain', 'bufLintCommonTest', 'bufLintNativeMain', etc., for Kotlin/Multiplatform projects. * - * Note the by default 'test' task doesn't depend on 'main' task. + * Note the by default 'test' task doesn't depend on the 'main' task. */ public fun registerWorkspaceTask( kClass: KClass, @@ -129,7 +129,6 @@ public open class BufTasksExtension @Inject constructor(internal val project: Pr return provider } - /** * Registers a custom Buf task that operates on the generated workspace. * 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 e2a147401..5110a568b 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 @@ -117,7 +117,7 @@ internal fun Project.registerBufExecTask( bufExecutable.set(executableConfiguration.singleFile) this.workingDir.set(workingDir) - val buf = provider { rpcExtension().protoc.buf } + val buf = provider { rpcExtension().protoc.get().buf } configFile.set(buf.flatMap { it.configFile }) logFormat.set(buf.flatMap { it.logFormat }) bufTimeoutInWholeSeconds.set(buf.flatMap { it.timeout.map { duration -> duration.inWholeSeconds } }) 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 dc6d62077..94db55c8e 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 @@ -131,7 +131,7 @@ internal fun Project.registerBufGenerateTask( group = PROTO_GROUP description = "Generates code from .proto files using 'buf generate'" - val generate = provider { rpcExtension().protoc.buf.generate } + val generate = provider { rpcExtension().protoc.get().buf.generate } includeImports.set(generate.flatMap { it.includeImports }) includeWkt.set(generate.flatMap { it.includeWkt }) 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 c281706b8..a01a002b0 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 @@ -137,19 +137,13 @@ public abstract class GenerateBufGenYaml : DefaultTask() { internal fun Project.registerGenerateBufGenYamlTask( name: String, buildSourceSetsDir: File, - protocPlugins: Provider>, + protocPlugins: Provider>, configure: GenerateBufGenYaml.() -> Unit = {}, ): TaskProvider { val capitalizeName = name.replaceFirstChar { it.uppercase() } return project.tasks.register("${GenerateBufGenYaml.NAME_PREFIX}$capitalizeName") { val pluginsProvider = project.provider { protocPlugins.get().map { plugin -> - if (!plugin.artifact.isPresent) { - throw GradleException( - "Artifact is not specified for protoc plugin ${plugin.name}. " + - "Use `local {}` or `remote {}` to specify it.") - } - val artifact = plugin.artifact.get() val locator = when (artifact) { is ProtocPlugin.Artifact.Local -> artifact.executor.get() 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 3b2d8b63e..112025541 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,11 +5,13 @@ 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.util.ensureRegularFileExists import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @@ -88,7 +90,7 @@ internal fun Project.registerGenerateBufYamlTask( buildSourceSetsDir: File, buildSourceSetsProtoDir: File, buildSourceSetsImportDir: File, - withImport: Boolean, + withImport: Provider, configure: GenerateBufYaml.() -> Unit = {}, ): TaskProvider { val capitalizeName = name.replaceFirstChar { it.uppercase() } 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 c742ec391..ce9a1a154 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt @@ -7,7 +7,6 @@ package kotlinx.rpc.internal import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.protoc.grpcKotlinMultiplatform import kotlinx.rpc.protoc.kotlinMultiplatform -import kotlinx.rpc.protoc.protoSourceSets import kotlinx.rpc.rpcExtension import org.gradle.api.Project import org.gradle.internal.extensions.core.extra @@ -21,9 +20,7 @@ public fun Project.configureLocalProtocGenDevelopmentDependency( val globalRootDir: String by extra // init - rpcExtension().protoc() - - protoSourceSets.all { + rpcExtension().protoc { plugins { kotlinMultiplatform { local { 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 e2cce027e..2f7bf07ce 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -4,31 +4,32 @@ package kotlinx.rpc.protoc -import kotlinx.rpc.buf.BufCommentsExtension -import kotlinx.rpc.buf.BufGenerateExtension import kotlinx.rpc.buf.tasks.BufGenerateTask -import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM -import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM import kotlinx.rpc.rpcExtension -import kotlinx.rpc.rpcExtensionOrNull import kotlinx.rpc.util.findOrCreate -import kotlinx.rpc.util.withKotlinJvmExtension -import kotlinx.rpc.util.withKotlinKmpExtension -import org.gradle.api.* +import kotlinx.rpc.util.withLazyKotlinJvmExtension +import kotlinx.rpc.util.withLazyKotlinKmpExtension +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.SourceDirectorySet import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.add import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.newInstance 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.targets.js.npm.includedRange +import java.io.File +import java.util.* +import java.util.function.Consumer import javax.inject.Inject @Suppress("UNCHECKED_CAST") @@ -42,75 +43,77 @@ internal class ProtoSourceSetFactory(private val project: Project) : NamedDomain } } -internal open class DefaultProtoSourceSet @Inject constructor( +internal open class DefaultProtoSourceSet( internal val project: Project, - override val name: String, -) : ProtoSourceSet { - override val plugins: NamedDomainObjectContainer = - project.objects.domainObjectContainer(ProtocPlugin::class.java) { name -> - ProtocPlugin(name, project) - } - - override fun plugins(action: Action>) { - action.execute(plugins) + private val sourceDirectorySet: SourceDirectorySet, +) : ProtoSourceSet, SourceDirectorySet by sourceDirectorySet { + + @Inject + constructor(project: Project, protoName: String) : this( + project = project, + sourceDirectorySet = project.objects.sourceDirectorySet(protoName, "Proto sources for $protoName").apply { + srcDirs("src/${protoName}/proto") + }, + ) + + private val explicitApiModeEnabled = project.provider { + project.the().explicitApi != ExplicitApiMode.Disabled } - init { - val explicitApiModeEnabled = project.provider { - project.the().explicitApi != ExplicitApiMode.Disabled - } - - plugins.create(KOTLIN_MULTIPLATFORM) { - local { - javaJar(project.kotlinMultiplatformProtocPluginJarPath) - } + val plugins = project.objects.setProperty() - defaultOptions(explicitApiModeEnabled) - } + override fun plugin(plugin: ProtocPlugin, configure: Action?) { + plugins.add(plugin.copy().also { initPlugin(it, configure) }) + } - plugins.create(GRPC_KOTLIN_MULTIPLATFORM) { - local { - javaJar(project.grpcKotlinMultiplatformProtocPluginJarPath) - } + override fun plugin(provider: NamedDomainObjectProvider, configure: Action?) { + plugins.add(provider.map { plugin -> plugin.copy().also { initPlugin(it, configure) } }) + } - defaultOptions(explicitApiModeEnabled) - } + override fun plugin(provider: Provider, configure: Action?) { + plugins.add(provider.map { plugin -> plugin.copy().also { initPlugin(it, configure) } }) } - private fun ProtocPlugin.defaultOptions(explicitApiModeEnabled: Provider) { - options.put("debugOutput", "protoc-gen-$name.log") + override fun plugin( + configure: Action?, + select: NamedDomainObjectContainer.() -> ProtocPlugin, + ) { + plugins.add(project.rpcExtension().protoc.map { protoc -> + protoc.plugins.select().copy().also { initPlugin(it, configure) } + }) + } + private fun initPlugin(copy: ProtocPlugin, configure: Action?) { if (this@DefaultProtoSourceSet.name.lowercase().endsWith("main")) { - options.put("explicitApiModeEnabled", explicitApiModeEnabled) + copy.options.put("explicitApiModeEnabled", explicitApiModeEnabled) } - val comments: Provider = project.provider { - project.rpcExtensionOrNull()?.run { protoc.buf.generate } - ?: project.objects.newInstance() - } + configure?.execute(copy) + } - options.put("generateComments", comments.flatMap { it.comments.copyComments }) - options.put("generateFileLevelComments", comments.flatMap { it.comments.includeFileLevelComments }) - options.put("indentSize", comments.flatMap { it.indentSize }) + init { + project.rpcExtension().whenProtocApplied { + plugin(plugins.kotlinMultiplatform) + plugin(plugins.grpcKotlinMultiplatform) + } } val languageSourceSets: ListProperty = project.objects.listProperty() val generateTask: Property = project.objects.property() - override val proto: SourceDirectorySet = project.objects.sourceDirectorySet( - PROTO_SOURCE_DIRECTORY_NAME, - "Proto sources", - ).apply { - srcDirs("src/${this@DefaultProtoSourceSet.name}/proto") + // Java default methods + + override fun forEach(action: Consumer?) { + sourceDirectorySet.forEach(action) } - override fun proto(action: Action) { - action.execute(proto) + override fun spliterator(): Spliterator { + return sourceDirectorySet.spliterator() } } internal fun Project.createProtoExtensions() { - fun findOrCreateAndConfigure(languageSourceSetName: String, languageSourceSet: Any?) { + fun findOrCreateAndConfigure(languageSourceSetName: String, languageSourceSet: Any?): ProtoSourceSet { val container = project.findOrCreate(PROTO_SOURCE_SETS) { val container = objects.domainObjectContainer( ProtoSourceSet::class.java, @@ -125,35 +128,29 @@ internal fun Project.createProtoExtensions() { val protoSourceSet = container.maybeCreate(languageSourceSetName) as DefaultProtoSourceSet languageSourceSet?.let { protoSourceSet.languageSourceSets.add(it) } - } - project.withKotlinJvmExtension { - findOrCreateAndConfigure("main", null) - findOrCreateAndConfigure("test", null) + return protoSourceSet + } - sourceSets.configureEach { - if (name == SourceSet.MAIN_SOURCE_SET_NAME || name == SourceSet.TEST_SOURCE_SET_NAME) { - findOrCreateAndConfigure(name, this) - } + project.withLazyKotlinJvmExtension { + sourceSets.all { + findOrCreateAndConfigure(name, this) } project.extensions.configure("sourceSets") { - configureEach { - if (name == SourceSet.MAIN_SOURCE_SET_NAME || name == SourceSet.TEST_SOURCE_SET_NAME) { - findOrCreateAndConfigure(name, this) + all { + val protoSourceSet = findOrCreateAndConfigure(name, this) + + findOrCreate(PROTO_SOURCE_SET_EXTENSION_NAME) { + extensions.add(PROTO_SOURCE_SET_EXTENSION_NAME, protoSourceSet) } } } } - project.withKotlinKmpExtension { - findOrCreateAndConfigure("commonMain", null) - findOrCreateAndConfigure("commonTest", null) - - sourceSets.configureEach { - if (name == "commonMain" || name == "commonTest") { - findOrCreateAndConfigure(name, this) - } + project.withLazyKotlinKmpExtension { + sourceSets.all { + findOrCreateAndConfigure(name, this) } } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index a491d32ba..e2855d699 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -13,14 +13,22 @@ import kotlinx.rpc.buf.tasks.registerBufExecTask import kotlinx.rpc.buf.tasks.registerBufGenerateTask 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.util.ensureDirectoryExists +import kotlinx.rpc.util.kotlinJvmExtensionOrNull +import kotlinx.rpc.util.kotlinKmpExtensionOrNull 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 @@ -30,6 +38,9 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import java.io.File import javax.inject.Inject +import kotlin.collections.filterIsInstance +import kotlin.collections.filterNotNull +import kotlin.collections.plus internal open class DefaultProtocExtension @Inject constructor( objects: ObjectFactory, @@ -40,6 +51,15 @@ internal open class DefaultProtocExtension @Inject constructor( action.execute(buf) } + override val plugins: NamedDomainObjectContainer = + project.objects.domainObjectContainer(ProtocPlugin::class.java) { name -> + ProtocPlugin(name, project) + } + + override fun plugins(action: Action>) { + action.execute(plugins) + } + init { project.configureBufExecutable() project.configureKotlinMultiplatformPluginJarConfiguration() @@ -50,6 +70,22 @@ internal open class DefaultProtocExtension @Inject constructor( project.normalization.runtimeClasspath.ignore("**/protoc-gen-grpc-kotlin-multiplatform.log") project.normalization.runtimeClasspath.ignore("**/.keep") + plugins.create(KOTLIN_MULTIPLATFORM) { + local { + javaJar(project.kotlinMultiplatformProtocPluginJarPath) + } + + defaultOptions() + } + + plugins.create(GRPC_KOTLIN_MULTIPLATFORM) { + local { + javaJar(project.grpcKotlinMultiplatformProtocPluginJarPath) + } + + defaultOptions() + } + project.protoSourceSets.all { if (this !is DefaultProtoSourceSet) { return@all @@ -59,6 +95,14 @@ internal open class DefaultProtocExtension @Inject constructor( } } + private fun ProtocPlugin.defaultOptions() { + options.put("debugOutput", "protoc-gen-$name.log") + + options.put("generateComments", buf.generate.comments.copyComments) + options.put("generateFileLevelComments", buf.generate.comments.includeFileLevelComments) + options.put("indentSize", buf.generate.indentSize) + } + @Suppress("detekt.LongMethod", "detekt.CyclomaticComplexMethod", "detekt.ThrowsCount") private fun Project.configureTasks(protoSourceSet: DefaultProtoSourceSet) { val baseName = protoSourceSet.name @@ -72,13 +116,22 @@ internal open class DefaultProtocExtension @Inject constructor( val buildSourceSetsImportDir = buildSourceSetsDir.resolve(PROTO_FILES_IMPORT_DIR) .ensureDirectoryExists() - val pairSourceSet = protoSourceSet.correspondingMainSourceSetOrNull() + // only resolve in task's 'execute' due to the deferred nature of dependsOn + val importsProvider = protoSourceSet.getImports(protoSourceSets) val includedProtocPlugins = provider { - protoSourceSet.plugins.distinct() + protoSourceSet.plugins.get().also { list -> + list.forEach { plugin -> + if (!plugin.artifact.isPresent) { + throw GradleException( + "Artifact is not specified for protoc plugin ${plugin.name}. " + + "Use `local {}` or `remote {}` to specify it.") + } + } + } } - val protoFiles = protoSourceSet.proto + val protoFiles = protoSourceSet as SourceDirectorySet val processProtoTask = registerProcessProtoFilesTask( name = baseName, @@ -86,18 +139,12 @@ internal open class DefaultProtocExtension @Inject constructor( protoFiles = protoFiles, ) - val processImportProtoTask = if (pairSourceSet != null) { - val importProtoFiles = pairSourceSet.proto - - registerProcessProtoFilesTask( - name = "${baseName}Import", - destination = buildSourceSetsImportDir, - protoFiles = importProtoFiles, - ) { - dependsOn(processProtoTask) - } - } else { - null + val processImportProtoTask = registerProcessProtoFilesImportsTask( + name = baseName, + destination = buildSourceSetsImportDir, + importsProvider = importsProvider, + ) { + dependsOn(processProtoTask) } val generateBufYamlTask = registerGenerateBufYamlTask( @@ -105,12 +152,9 @@ internal open class DefaultProtocExtension @Inject constructor( buildSourceSetsDir = buildSourceSetsDir, buildSourceSetsProtoDir = buildSourceSetsProtoDir, buildSourceSetsImportDir = buildSourceSetsImportDir, - withImport = pairSourceSet != null, + withImport = importsProvider.map { it.isNotEmpty() }, ) { dependsOn(processProtoTask) - if (processImportProtoTask != null) { - dependsOn(processImportProtoTask) - } } val generateBufGenYamlTask = registerGenerateBufGenYamlTask( @@ -147,14 +191,14 @@ internal open class DefaultProtocExtension @Inject constructor( dependsOn(generateBufGenYamlTask) dependsOn(generateBufYamlTask) dependsOn(processProtoTask) - if (processImportProtoTask != null) { - dependsOn(processImportProtoTask) - } + dependsOn(processImportProtoTask) - if (pairSourceSet != null) { - dependsOn(pairSourceSet.generateTask) + val dependencies = project.provider { + protoSourceSet.getDependsOn(protoSourceSets).map { it.generateTask.get() } } + dependsOn(dependencies) + onlyIf { !sourceSetsProtoDirFileTree.filter { it.extension == "proto" }.isEmpty } } @@ -194,53 +238,69 @@ internal open class DefaultProtocExtension @Inject constructor( } } - configureAfterEvaluate( + configureSourceDirectories( baseName = baseName, - protoSourceSet = protoSourceSet, - buildSourceSetsDir = buildSourceSetsDir, includedProtocPlugins = includedProtocPlugins, - generateBufYamlTask = generateBufYamlTask, - generateBufGenYamlTask = generateBufGenYamlTask, - processProtoTask = processProtoTask, - processImportProtoTask = processImportProtoTask, bufGenerateTask = bufGenerateTask, - sourceSetsProtoDirFileTree = sourceSetsProtoDirFileTree, ) + +// configureCustomTasks( +// baseName = baseName, +// buildSourceSetsDir = buildSourceSetsDir, +// generateBufYamlTask = generateBufYamlTask, +// generateBufGenYamlTask = generateBufGenYamlTask, +// processProtoTask = processProtoTask, +// processImportProtoTask = processImportProtoTask, +// sourceSetsProtoDirFileTree = sourceSetsProtoDirFileTree, +// ) } - private fun Project.configureAfterEvaluate( + private fun Project.configureSourceDirectories( + baseName: String, + 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) } + } + + javaSourceSet.java.srcDirs(javaSourcesProvider) + } + } + + val kotlinSourcesProvider = includedProtocPlugins.map { plugins -> + val out = bufGenerateTask.get().outputDirectory.get() + plugins.filter { !it.isJava.get() }.map { out.resolve(it.name) } + } + + project.kotlinJvmExtensionOrNull?.sourceSets?.all { + if (name == baseName) { + kotlin.srcDirs(kotlinSourcesProvider) + } + } + + project.kotlinKmpExtensionOrNull?.sourceSets?.all { + if (name == baseName) { + kotlin.srcDirs(kotlinSourcesProvider) + } + } + } + + private fun Project.configureCustomTasks( baseName: String, - protoSourceSet: DefaultProtoSourceSet, buildSourceSetsDir: File, - includedProtocPlugins: Provider>, generateBufYamlTask: TaskProvider, generateBufGenYamlTask: TaskProvider, processProtoTask: TaskProvider, processImportProtoTask: TaskProvider?, - bufGenerateTask: TaskProvider, sourceSetsProtoDirFileTree: ConfigurableFileTree, ) = afterEvaluate { - val out = bufGenerateTask.get().outputDirectory.get() - val plugins = includedProtocPlugins.get() - - plugins.forEach { plugin -> - // locates correctly jvmMain, main jvmTest, test - val javaSourceSet = extensions.findByType() - ?.sourceSets?.findByName(baseName)?.java - - if (plugin.isJava.get() && javaSourceSet != null) { - javaSourceSet.srcDirs(out.resolve(plugin.name)) - } else { - protoSourceSet.languageSourceSets.get().find { it is KotlinSourceSet }?.let { - (it as KotlinSourceSet) - .kotlin.srcDirs(out.resolve(plugin.name)) - } ?: error( - "Unable to find fitting source directory set " + - "for plugin '${plugin.name}' in '$protoSourceSet' proto source set" - ) - } - } - val baseCapital = baseName.replaceFirstChar { it.uppercase() } buf.tasks.customTasks.get().forEach { definition -> val capital = definition.name.replaceFirstChar { it.uppercase() } @@ -277,14 +337,20 @@ internal open class DefaultProtocExtension @Inject constructor( } } - private fun DefaultProtoSourceSet.correspondingMainSourceSetOrNull(): DefaultProtoSourceSet? { + private fun DefaultProtoSourceSet.getImports( + protoSourceSets: ProtoSourceSets, + ): Provider> { return when { name.lowercase().endsWith("main") -> { - null + getImportsForTestOrMain(protoSourceSets) } name.lowercase().endsWith("test") -> { - project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet + val main = getImportsForTestOrMain(protoSourceSets) + val test = (project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet) + ?.getImportsForTestOrMain(protoSourceSets) + + if (test == null) main else main.zip(test) { a, b -> a + b } } else -> { @@ -293,7 +359,64 @@ internal open class DefaultProtocExtension @Inject constructor( } } - private fun ProtoSourceSet.correspondingMainName(): String { + 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) + } + } + } + list.filterIsInstance().mapNotNull { + if (it.name.endsWith("Test")) { + project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet + } else { + null + } + } + list.filterIsInstance().mapNotNull { + if (it.name == SourceSet.TEST_SOURCE_SET_NAME) { + project.protoSourceSets.findByName(correspondingMainName()) as? DefaultProtoSourceSet + } else { + null + } + } + } + } + + private fun DefaultProtoSourceSet.getDependsOn(protoSourceSets: ProtoSourceSets): List { + val sourceSets = languageSourceSets.get() + + val kmpDependsOn = sourceSets + .filterIsInstance() + .flatMap { + it.dependsOn.map { dependency -> dependency.name } + } + .distinct() + .mapNotNull { + protoSourceSets.getByName(it) as? DefaultProtoSourceSet + } + + val kmp = if (name.endsWith("Test")) { + (protoSourceSets.getByName(correspondingMainName()) as? DefaultProtoSourceSet) + } else { + null + } + + val jvm = if (name == SourceSet.TEST_SOURCE_SET_NAME) { + (protoSourceSets.getByName(correspondingMainName()) as? DefaultProtoSourceSet) + } else { + null + } + + return (kmpDependsOn + kmp + jvm).filterNotNull() + } + + private fun Named.correspondingMainName(): String { return when { name == "test" -> "main" name.endsWith("Test") -> name.removeSuffix("Test") + "Main" diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt index 7bc197c2b..fc9e56697 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt @@ -6,7 +6,9 @@ package kotlinx.rpc.protoc import kotlinx.rpc.util.ensureRegularFileExists import org.gradle.api.Project +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.TaskProvider import org.gradle.kotlin.dsl.register @@ -32,10 +34,52 @@ internal fun Project.registerProcessProtoFilesTask( 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 + // so we prevent NO-SOURCE by creating a .keep file in the destination directory val keep = protoBuildDirSourceSetsKeep.ensureRegularFileExists() from(keep) - from(protoFiles) + + from(files(protoFiles.sourceDirectories)) { + include(protoFiles.includes) + exclude(protoFiles.excludes) + } + + into(destination) + + doFirst { + destination.deleteRecursively() + } + + configure() + } +} + +internal fun Project.registerProcessProtoFilesImportsTask( + name: String, + destination: File, + importsProvider: Provider>, + configure: ProcessProtoFiles.() -> Unit = {}, +): TaskProvider { + val capitalName = name.replaceFirstChar { it.uppercase() } + + return tasks.register("process${capitalName}ProtoFilesImports") { + // this task deletes the destination directory if it results in NO-SOURCE, + // which breaks the configuration cache for bufGenerate + // so we prevent NO-SOURCE by creating a .keep file in the destination directory + val keep = protoBuildDirSourceSetsKeep.ensureRegularFileExists() + from(keep) + + duplicatesStrategy = DuplicatesStrategy.FAIL + + val allImports = importsProvider.map { list -> + list.map { import -> + files(import.sourceDirectories) { + include(import.includes) + exclude(import.excludes) + } + } + } + + from(allImports) into(destination) 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 1087d3ddf..95ac93cbd 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt @@ -8,37 +8,66 @@ import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.SourceSet +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet public typealias ProtoSourceSets = NamedDomainObjectContainer /** * Represents a source set for proto files. */ -public interface ProtoSourceSet { - /** - * Name of the source set. - */ - public val name: String - - /** - * Container of protoc plugins to be applied to the source set. - */ - public val plugins: NamedDomainObjectContainer - - /** - * Configures the protoc plugins. - */ - public fun plugins(action: Action>) - - /** - * Default [SourceDirectorySet] for proto files. - */ - public val proto: SourceDirectorySet - - /** - * Configures [proto] source directory set. - */ - public fun proto(action: Action) { - action.execute(proto) +public sealed interface ProtoSourceSet : SourceDirectorySet { + public fun plugin(plugin: ProtocPlugin, configure: Action? = null) + + public fun plugin(provider: NamedDomainObjectProvider, configure: Action? = null) + + public fun plugin(provider: Provider, configure: Action? = null) + + public fun plugin( + configure: Action? = null, + select: NamedDomainObjectContainer.() -> ProtocPlugin, + ) +} + +public val KotlinSourceSet.proto: ProtoSourceSet + get(): ProtoSourceSet { + return project.protoSourceSets.getByName(name) + } + +public fun KotlinSourceSet.proto(action: Action) { + proto.apply(action::execute) +} + +@get:JvmName("proto_kotlin") +public val NamedDomainObjectProvider.proto: Provider + get() { + return map { it.proto } + } + +@JvmName("proto_kotlin") +public fun NamedDomainObjectProvider.proto(action: Action) { + configure { + proto(action) + } +} + +public val SourceSet.proto: ProtoSourceSet + get(): ProtoSourceSet { + return extensions.getByType(ProtoSourceSet::class.java) + } + +public fun SourceSet.proto(action: Action) { + extensions.configure(ProtoSourceSet::class.java, action::execute) +} + +public val NamedDomainObjectProvider.proto: Provider + get() { + return map { it.proto } + } + +public fun NamedDomainObjectProvider.proto(action: Action) { + configure { + proto(action) } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt index b1ed62e3e..023e55e78 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt @@ -6,6 +6,7 @@ package kotlinx.rpc.protoc import kotlinx.rpc.buf.BufExtension import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer /** * Configuration for the Protoc capabilities. @@ -17,7 +18,7 @@ import org.gradle.api.Action * } * ``` */ -public interface ProtocExtension { +public sealed interface ProtocExtension { /** * Configuration for the Buf build tool. */ @@ -27,4 +28,14 @@ public interface ProtocExtension { * Configures the Buf build tool. */ public fun buf(action: Action) + + /** + * Container of protoc plugins. + */ + public val plugins: NamedDomainObjectContainer + + /** + * Configures the protoc plugins. + */ + public fun plugins(action: Action>) } 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 c5f0c7774..75036b673 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt @@ -50,9 +50,9 @@ public fun NamedDomainObjectContainer.grpcKotlinMultiplatform(acti /** * Access to a specific protoc plugin. */ -public open class ProtocPlugin( +public open class ProtocPlugin internal constructor( public val name: String, - private val project: Project, + internal val project: Project, ) { /** * Whether the plugin generates Java code. @@ -275,4 +275,19 @@ public open class ProtocPlugin( public val locator: Property = project.objects.property() } } + + // todo check if coping works + internal fun copy(): ProtocPlugin { + return ProtocPlugin(name, project) + .also { + it.isJava.set(isJava) + it.options.set(options) + it.artifact.set(artifact) + it.strategy.set(strategy) + it.includeImports.set(includeImports) + it.includeWkt.set(includeWkt) + it.types.set(types) + it.excludeTypes.set(excludeTypes) + } + } } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt index 5448e8156..35d8435a3 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt @@ -16,6 +16,11 @@ public const val PROTO_GROUP: String = "proto" */ public const val PROTO_SOURCE_SETS: String = "protoSourceSets" +/** + * Name of the extension that is created for [org.gradle.api.tasks.SourceSet] instances. + */ +public const val PROTO_SOURCE_SET_EXTENSION_NAME: String = "proto" + /** * Name of the default source directory set for proto files in [PROTO_SOURCE_SETS]. */ 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 dce7f0305..b6377254a 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/util/kgp.kt @@ -6,6 +6,7 @@ package kotlinx.rpc.util import org.gradle.api.Action import org.gradle.api.Project +import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension @@ -13,14 +14,22 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension private const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" private const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm" -internal fun Project.withKotlinJvmExtension(action: Action) { +internal fun Project.withLazyKotlinJvmExtension(action: Action) { plugins.withId(KOTLIN_JVM_PLUGIN_ID) { the().apply { action.execute(this) } } } -internal fun Project.withKotlinKmpExtension(action: Action) { +internal fun Project.withLazyKotlinKmpExtension(action: Action) { plugins.withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { the().apply { action.execute(this) } } } + +internal val Project.kotlinJvmExtensionOrNull: KotlinJvmProjectExtension? get() { + return extensions.findByType() +} + +internal val Project.kotlinKmpExtensionOrNull: KotlinMultiplatformExtension? get() { + return extensions.findByType() +} diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt index 4103cfda5..c58340b97 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt @@ -17,7 +17,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { @Test fun `Minimal gRPC Configuration`() = runGrpcTest { - val result = runGradle(bufGenerateMain) + val result = runGradle(bufGenerateCommonMain) result.assertMainTaskExecuted( protoFiles = listOf( @@ -32,20 +32,20 @@ class GrpcJvmProjectTest : GrpcBaseTest() { @Test fun `No gRPC`() = runGrpcTest { - runNonExistentTask(bufGenerateMain) - runNonExistentTask(bufGenerateTest) - runNonExistentTask(processMainProtoFiles) - runNonExistentTask(processTestProtoFiles) - runNonExistentTask(processTestImportProtoFiles) - runNonExistentTask(generateBufYamlMain) - runNonExistentTask(generateBufYamlTest) - runNonExistentTask(generateBufGenYamlMain) - runNonExistentTask(generateBufGenYamlTest) + runNonExistentTask(bufGenerateCommonMain) + runNonExistentTask(bufGenerateCommonTest) + runNonExistentTask(processCommonMainProtoFiles) + runNonExistentTask(processCommonTestProtoFiles) + runNonExistentTask(processCommonTestProtoFilesImports) + runNonExistentTask(generateBufYamlCommonMain) + runNonExistentTask(generateBufYamlCommonTest) + runNonExistentTask(generateBufGenYamlCommonMain) + runNonExistentTask(generateBufGenYamlCommonTest) } @Test fun `Test-Only Sources`() = runGrpcTest { - val result = runGradle(bufGenerateTest) + val result = runGradle(bufGenerateCommonTest) result.assertTestTaskExecuted( protoFiles = listOf( @@ -62,7 +62,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { @Test fun `Main and Test Mixed Sources`() = runGrpcTest { - val mainRun = runGradle(bufGenerateMain) + val mainRun = runGradle(bufGenerateCommonMain) mainRun.assertMainTaskExecuted( protoFiles = listOf( @@ -76,7 +76,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { cleanProtoBuildDir() - val testRun = runGradle(bufGenerateTest) + val testRun = runGradle(bufGenerateCommonTest) testRun.assertTestTaskExecuted( protoFiles = listOf( @@ -97,8 +97,106 @@ class GrpcJvmProjectTest : GrpcBaseTest() { } @Test - fun `Exclude and Include in protoSourceSets`() = runGrpcTest { - runGradle(processMainProtoFiles) + fun `Java Source Sets`() = runGrpcTest { + val mainRun = runGradle(bufGenerateCommonMain) + + mainRun.assertMainTaskExecuted( + protoFiles = listOf( + Path("some.proto") + ), + generatedFiles = listOf( + Path("Some.kt"), + Path(RPC_INTERNAL, "Some.kt"), + ) + ) + + cleanProtoBuildDir() + + val testRun = runGradle(bufGenerateCommonTest) + + testRun.assertTestTaskExecuted( + protoFiles = listOf( + Path("other.proto") + ), + importProtoFiles = listOf( + Path("some.proto") + ), + generatedFiles = listOf( + Path("Other.kt"), + Path(RPC_INTERNAL, "Other.kt"), + ), + importGeneratedFiles = listOf( + Path("Some.kt"), + Path(RPC_INTERNAL, "Some.kt"), + ) + ) + } + + @Test + fun `Java and Kotlin Source Sets`() = runGrpcTest { + runGradle(processCommonMainProtoFiles) + runGradle(generateBufGenYamlCommonMain) + + assertWorkspaceProtoFilesCopied( + mainSourceSet, + Path("some.proto"), + ) + + assertBufGenYaml( + sourceSet = mainSourceSet, + content = """ +version: v2 +clean: true +plugins: + - local: some + out: myPlugin + opt: + - explicitApiModeEnabled=true + - local: some2 + out: myPlugin2 + opt: + - explicitApiModeEnabled=true + - local: [protoc-gen-kotlin-multiplatform] + out: kotlin-multiplatform + opt: + - debugOutput=protoc-gen-kotlin-multiplatform.log + - generateComments=true + - generateFileLevelComments=true + - indentSize=4 + - explicitApiModeEnabled=true + - local: [protoc-gen-grpc-kotlin-multiplatform] + out: grpc-kotlin-multiplatform + opt: + - debugOutput=protoc-gen-grpc-kotlin-multiplatform.log + - generateComments=true + - generateFileLevelComments=true + - indentSize=4 + - explicitApiModeEnabled=true +inputs: + - directory: proto + """.trimIndent() + ) + + cleanProtoBuildDir() + + runGradle(processCommonTestProtoFiles) + runGradle(processCommonTestProtoFilesImports) + runGradle(generateBufGenYamlCommonTest) + + assertWorkspaceProtoFilesCopied( + testSourceSet, + Path("other.proto"), + ) + + assertWorkspaceImportProtoFilesCopied( + testSourceSet, + Path("some.proto"), + ) + } + + @Test + fun `Exclude and Include in proto sourceSets`() = runGrpcTest { + runGradle(processCommonMainProtoFiles) assertWorkspaceProtoFilesCopied( mainSourceSet, @@ -106,7 +204,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { Path("ok", "ok.proto"), ) - runGradle(processTestProtoFiles) + runGradle(processCommonTestProtoFiles) assertWorkspaceProtoFilesCopied( testSourceSet, @@ -116,7 +214,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { Path("include", "yes2.proto"), ) - runGradle(processTestImportProtoFiles) + runGradle(processCommonTestProtoFilesImports) assertWorkspaceImportProtoFilesCopied( testSourceSet, @@ -127,7 +225,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { @Test fun `Can Add Custom Protoc Plugins`() = runGrpcTest { - runGradle(generateBufGenYamlMain) + runGradle(generateBufGenYamlCommonMain) assertBufGenYaml( sourceSet = mainSourceSet, @@ -135,21 +233,28 @@ class GrpcJvmProjectTest : GrpcBaseTest() { version: v2 clean: true plugins: - - local: [protoc-gen-grpc-kotlin-multiplatform] - out: grpc-kotlin-multiplatform - opt: - - debugOutput=protoc-gen-grpc-kotlin-multiplatform.log - - explicitApiModeEnabled=true - local: [protoc-gen-kotlin-multiplatform] out: kotlin-multiplatform opt: - debugOutput=protoc-gen-kotlin-multiplatform.log + - generateComments=true + - generateFileLevelComments=true + - indentSize=4 + - explicitApiModeEnabled=true + - local: [protoc-gen-grpc-kotlin-multiplatform] + out: grpc-kotlin-multiplatform + opt: + - debugOutput=protoc-gen-grpc-kotlin-multiplatform.log + - generateComments=true + - generateFileLevelComments=true + - indentSize=4 - explicitApiModeEnabled=true - local: [path, to, protoc-gen-myplugin.exe] out: myPlugin opt: - hello=world - foo=bar + - explicitApiModeEnabled=true strategy: all include_imports: true include_wkt: false @@ -159,6 +264,8 @@ plugins: - my.type.Nope - remote: my.remote.plugin out: myRemotePlugin + opt: + - explicitApiModeEnabled=true inputs: - directory: proto """.trimIndent() @@ -167,7 +274,7 @@ inputs: @Test fun `Custom Protoc Plugins Must Declare an Artifact`() = runGrpcTest { - val result = runGradleToFail(generateBufGenYamlMain) + val result = runGradleToFail(generateBufGenYamlCommonMain) assert(result.output.contains("Artifact is not specified for protoc plugin myPlugin")) } @@ -177,7 +284,7 @@ inputs: @Test fun `Custom Buf CLI Flags`() = runGrpcTest { - val result = runGradleToFail(bufGenerateMain) + val result = runGradleToFail(bufGenerateCommonMain) assert(result.output.contains("could not read file: open some.buf.yaml: no such file or directory")) { "Expected failure due to missing some.buf.yaml file" } @@ -190,105 +297,105 @@ inputs: @Test fun `Skip Generation When No Proto Files`() = runGrpcTest { - val result = runGradle(bufGenerateMain) + val result = runGradle(bufGenerateCommonMain) - assertEquals(TaskOutcome.SKIPPED, result.protoTaskOutcome(bufGenerateMain)) + assertEquals(TaskOutcome.SKIPPED, result.protoTaskOutcome(bufGenerateCommonMain)) } @Test fun `Proto Tasks Are Cached Properly`() = runGrpcTest { - val firstRunMain = runGradle(bufGenerateMain) + val firstRunMain = runGradle(bufGenerateCommonMain) - assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, firstRunMain.protoTaskOutcome(processCommonMainProtoFiles)) - val secondRunMain = runGradle(bufGenerateMain) + val secondRunMain = runGradle(bufGenerateCommonMain) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunMain.protoTaskOutcome(processCommonMainProtoFiles)) cleanProtoBuildDir() - val thirdRunMain = runGradle(bufGenerateMain) + val thirdRunMain = runGradle(bufGenerateCommonMain) - assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, thirdRunMain.protoTaskOutcome(processCommonMainProtoFiles)) mainProtoFileSources .resolve("some.proto") .replace("content = 1", "content = 2") - val fourthRunMain = runGradle(bufGenerateMain) + val fourthRunMain = runGradle(bufGenerateCommonMain) - assertEquals(TaskOutcome.SUCCESS, fourthRunMain.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunMain.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunMain.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.SUCCESS, fourthRunMain.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, fourthRunMain.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunMain.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunMain.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, fourthRunMain.protoTaskOutcome(processCommonMainProtoFiles)) - val firstRunTest = runGradle(bufGenerateTest) + val firstRunTest = runGradle(bufGenerateCommonTest) - assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(bufGenerateTest)) - assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(generateBufYamlTest)) - assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(generateBufGenYamlTest)) - assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(processTestProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(processTestImportProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(bufGenerateCommonTest)) + assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(generateBufYamlCommonTest)) + assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(generateBufGenYamlCommonTest)) + assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(processCommonTestProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, firstRunTest.protoTaskOutcome(processCommonTestProtoFilesImports)) - assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, firstRunTest.protoTaskOutcome(processCommonMainProtoFiles)) - val secondRunTest = runGradle(bufGenerateTest) + val secondRunTest = runGradle(bufGenerateCommonTest) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(bufGenerateTest)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufYamlTest)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufGenYamlTest)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(processTestProtoFiles)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(processTestImportProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(bufGenerateCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufYamlCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufGenYamlCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(processCommonTestProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(processCommonTestProtoFilesImports)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, secondRunTest.protoTaskOutcome(processCommonMainProtoFiles)) testProtoFileSources .resolve("other.proto") .replace("content = 1", "content = 2") - val thirdRunTest = runGradle(bufGenerateTest) + val thirdRunTest = runGradle(bufGenerateCommonTest) - assertEquals(TaskOutcome.SUCCESS, thirdRunTest.protoTaskOutcome(bufGenerateTest)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufYamlTest)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufGenYamlTest)) - assertEquals(TaskOutcome.SUCCESS, thirdRunTest.protoTaskOutcome(processTestProtoFiles)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(processTestImportProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, thirdRunTest.protoTaskOutcome(bufGenerateCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufYamlCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufGenYamlCommonTest)) + assertEquals(TaskOutcome.SUCCESS, thirdRunTest.protoTaskOutcome(processCommonTestProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(processCommonTestProtoFilesImports)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, thirdRunTest.protoTaskOutcome(processCommonMainProtoFiles)) mainProtoFileSources .resolve("some.proto") .replace("content = 2", "content = 3") - val fourthRunTest = runGradle(bufGenerateTest) + val fourthRunTest = runGradle(bufGenerateCommonTest) - assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(bufGenerateTest)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufYamlTest)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufGenYamlTest)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(processTestProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(processTestImportProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(bufGenerateCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufYamlCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufGenYamlCommonTest)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(processCommonTestProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(processCommonTestProtoFilesImports)) - assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufGenYamlMain)) - assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(processMainProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.UP_TO_DATE, fourthRunTest.protoTaskOutcome(generateBufGenYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, fourthRunTest.protoTaskOutcome(processCommonMainProtoFiles)) } } diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index bd0dafedd..865d20576 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -15,7 +15,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @Test fun `Minimal gRPC Configuration`() = runGrpcTest { - val result = runGradle(bufGenerateMain) + val result = runGradle(bufGenerateCommonMain) result.assertMainTaskExecuted( protoFiles = listOf( @@ -30,20 +30,20 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @Test fun `No gRPC`() = runGrpcTest { - runNonExistentTask(bufGenerateMain) - runNonExistentTask(bufGenerateTest) - runNonExistentTask(processMainProtoFiles) - runNonExistentTask(processTestProtoFiles) - runNonExistentTask(processTestImportProtoFiles) - runNonExistentTask(generateBufYamlMain) - runNonExistentTask(generateBufYamlTest) - runNonExistentTask(generateBufGenYamlMain) - runNonExistentTask(generateBufGenYamlTest) + runNonExistentTask(bufGenerateCommonMain) + runNonExistentTask(bufGenerateCommonTest) + runNonExistentTask(processCommonMainProtoFiles) + runNonExistentTask(processCommonTestProtoFiles) + runNonExistentTask(processCommonTestProtoFilesImports) + runNonExistentTask(generateBufYamlCommonMain) + runNonExistentTask(generateBufYamlCommonTest) + runNonExistentTask(generateBufGenYamlCommonMain) + runNonExistentTask(generateBufGenYamlCommonTest) } @Test fun `Test-Only Sources`() = runGrpcTest { - val result = runGradle(bufGenerateTest) + val result = runGradle(bufGenerateCommonTest) result.assertTestTaskExecuted( protoFiles = listOf( @@ -60,7 +60,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @Test fun `Main and Test Mixed Sources`() = runGrpcTest { - val mainRun = runGradle(bufGenerateMain) + val mainRun = runGradle(bufGenerateCommonMain) mainRun.assertMainTaskExecuted( protoFiles = listOf( @@ -74,7 +74,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { cleanProtoBuildDir() - val testRun = runGradle(bufGenerateTest) + val testRun = runGradle(bufGenerateCommonTest) testRun.assertTestTaskExecuted( protoFiles = listOf( @@ -96,20 +96,20 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @Test fun `No JVM Targets`() = runGrpcTest { - runNonExistentTask(bufGenerateMain) - runNonExistentTask(bufGenerateTest) - runNonExistentTask(processMainProtoFiles) - runNonExistentTask(processTestProtoFiles) - runNonExistentTask(processTestImportProtoFiles) - runNonExistentTask(generateBufYamlMain) - runNonExistentTask(generateBufYamlTest) - runNonExistentTask(generateBufGenYamlMain) - runNonExistentTask(generateBufGenYamlTest) + runNonExistentTask(bufGenerateCommonMain) + runNonExistentTask(bufGenerateCommonTest) + runNonExistentTask(processCommonMainProtoFiles) + runNonExistentTask(processCommonTestProtoFiles) + runNonExistentTask(processCommonTestProtoFilesImports) + runNonExistentTask(generateBufYamlCommonMain) + runNonExistentTask(generateBufYamlCommonTest) + runNonExistentTask(generateBufGenYamlCommonMain) + runNonExistentTask(generateBufGenYamlCommonTest) } @Test fun `With Other Targets`() = runGrpcTest { - val result = runGradle(bufGenerateMain) + val result = runGradle(bufGenerateCommonMain) result.assertMainTaskExecuted( protoFiles = listOf( 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 89ca6c01a..176bf46f4 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -44,7 +44,7 @@ abstract class GrpcBaseTest : BaseTest() { files.forEach { file -> assert(dir.resolve(file).exists()) { - "File 'file://${file.absolutePathString()}' in '$dir' does not exist" + "File '${file}' in '$dir' does not exist" } } } @@ -54,7 +54,7 @@ abstract class GrpcBaseTest : BaseTest() { files.forEach { file -> assert(!dir.resolve(file).exists()) { - "File 'file://${file.absolutePathString()}' in '$dir' should not exist" + "File '${file}' in '$dir' should not exist" } } } @@ -65,7 +65,7 @@ abstract class GrpcBaseTest : BaseTest() { val included = files.map { file -> val resolved = protoSources.resolve(file) assert(resolved.exists()) { - "File 'file://${file.absolutePathString()}' in '$protoSources' does not exist" + "File '${file}' in '$protoSources' does not exist" } resolved.relativeTo(protoSources).pathString }.toSet() @@ -73,7 +73,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 'file://${it.absolutePathString()}' in '$protoSources' is not expected") + fail("File '${it}' in '$protoSources' is not expected") } } } @@ -123,16 +123,16 @@ abstract class GrpcBaseTest : BaseTest() { protoFiles: List, generatedFiles: List, ) { - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processMainProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlMain)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processCommonMainProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlCommonMain)) - assertProtoTaskNotExecuted(bufGenerateTest) - assertProtoTaskNotExecuted(processTestProtoFiles) - assertProtoTaskNotExecuted(processTestImportProtoFiles) - assertProtoTaskNotExecuted(generateBufYamlTest) - assertProtoTaskNotExecuted(generateBufGenYamlTest) + assertProtoTaskNotExecuted(bufGenerateCommonTest) + assertProtoTaskNotExecuted(processCommonTestProtoFiles) + assertProtoTaskNotExecuted(processCommonTestProtoFilesImports) + assertProtoTaskNotExecuted(generateBufYamlCommonTest) + assertProtoTaskNotExecuted(generateBufGenYamlCommonTest) assertSourceCodeGenerated(mainSourceSet, *generatedFiles.toTypedArray()) assertSourceCodeNotGenerated(testSourceSet, *generatedFiles.toTypedArray()) @@ -150,11 +150,11 @@ abstract class GrpcBaseTest : BaseTest() { generatedFiles: List, importGeneratedFiles: List, ) { - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(bufGenerateTest)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processTestProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processTestImportProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlTest)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlTest)) + 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)) val mainGenerateOutcome = if (importProtoFiles.isEmpty()) { TaskOutcome.SKIPPED @@ -162,10 +162,10 @@ abstract class GrpcBaseTest : BaseTest() { TaskOutcome.SUCCESS } - assertEquals(mainGenerateOutcome, protoTaskOutcome(bufGenerateMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processMainProtoFiles)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlMain)) - assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlMain)) + assertEquals(mainGenerateOutcome, protoTaskOutcome(bufGenerateCommonMain)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(processCommonMainProtoFiles)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufYamlCommonMain)) + assertEquals(TaskOutcome.SUCCESS, protoTaskOutcome(generateBufGenYamlCommonMain)) assertSourceCodeGenerated(testSourceSet, *generatedFiles.toTypedArray()) assertSourceCodeNotGenerated(mainSourceSet, *generatedFiles.toTypedArray()) @@ -182,20 +182,20 @@ abstract class GrpcBaseTest : BaseTest() { val mainSourceSet = if (isKmp) "${KMP_SOURCE_SET}Main" else "main" val testSourceSet = if (isKmp) "${KMP_SOURCE_SET}Test" else "test" - val bufGenerateMain = if (isKmp) "bufGenerate${KMP_SOURCE_SET_CAPITAL}Main" else "bufGenerateMain" - val bufGenerateTest = if (isKmp) "bufGenerate${KMP_SOURCE_SET_CAPITAL}Test" else "bufGenerateTest" - val processMainProtoFiles = - if (isKmp) "process${KMP_SOURCE_SET_CAPITAL}MainProtoFiles" else "processMainProtoFiles" - val processTestProtoFiles = - if (isKmp) "process${KMP_SOURCE_SET_CAPITAL}TestProtoFiles" else "processTestProtoFiles" - val processTestImportProtoFiles = - if (isKmp) "process${KMP_SOURCE_SET_CAPITAL}TestImportProtoFiles" else "processTestImportProtoFiles" - val generateBufYamlMain = if (isKmp) "generateBufYaml${KMP_SOURCE_SET_CAPITAL}Main" else "generateBufYamlMain" - val generateBufYamlTest = if (isKmp) "generateBufYaml${KMP_SOURCE_SET_CAPITAL}Test" else "generateBufYamlTest" - val generateBufGenYamlMain = - if (isKmp) "generateBufGenYaml${KMP_SOURCE_SET_CAPITAL}Main" else "generateBufGenYamlMain" - val generateBufGenYamlTest = - if (isKmp) "generateBufGenYaml${KMP_SOURCE_SET_CAPITAL}Test" else "generateBufGenYamlTest" + val bufGenerateCommonMain = if (isKmp) "bufGenerate${KMP_COMMON_SOURCE_SET_CAPITAL}Main" else "bufGenerateMain" + val bufGenerateCommonTest = if (isKmp) "bufGenerate${KMP_COMMON_SOURCE_SET_CAPITAL}Test" else "bufGenerateTest" + val processCommonMainProtoFiles = + if (isKmp) "process${KMP_COMMON_SOURCE_SET_CAPITAL}MainProtoFiles" else "processMainProtoFiles" + val processCommonTestProtoFiles = + if (isKmp) "process${KMP_COMMON_SOURCE_SET_CAPITAL}TestProtoFiles" else "processTestProtoFiles" + val processCommonTestProtoFilesImports = + if (isKmp) "process${KMP_COMMON_SOURCE_SET_CAPITAL}TestProtoFilesImports" else "processTestProtoFilesImports" + val generateBufYamlCommonMain = if (isKmp) "generateBufYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Main" else "generateBufYamlMain" + val generateBufYamlCommonTest = if (isKmp) "generateBufYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Test" else "generateBufYamlTest" + val generateBufGenYamlCommonMain = + if (isKmp) "generateBufGenYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Main" else "generateBufGenYamlMain" + val generateBufGenYamlCommonTest = + if (isKmp) "generateBufGenYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Test" else "generateBufGenYamlTest" val protoBuildDir: Path by lazy { projectDir @@ -213,12 +213,12 @@ abstract class GrpcBaseTest : BaseTest() { .resolve("generated") } - val mainProtoFileSources = projectDir + val mainProtoFileSources: Path = projectDir .resolve("src") .resolve(mainSourceSet) .resolve("proto") - val testProtoFileSources = projectDir + val testProtoFileSources: Path = projectDir .resolve("src") .resolve(testSourceSet) .resolve("proto") @@ -226,7 +226,7 @@ abstract class GrpcBaseTest : BaseTest() { companion object { private const val KMP_SOURCE_SET = "common" - private val KMP_SOURCE_SET_CAPITAL = KMP_SOURCE_SET.replaceFirstChar(Char::uppercaseChar) + private val KMP_COMMON_SOURCE_SET_CAPITAL = KMP_SOURCE_SET.replaceFirstChar(Char::uppercaseChar) private const val KOTLIN_MULTIPLATFORM_DIR = "kotlin-multiplatform" const val RPC_INTERNAL = "_rpc_internal" } 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 e6e1a3eac..3e354eeca 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 @@ -11,11 +11,7 @@ plugins { } rpc { - protoc() -} - -protoSourceSets { - main { + protoc { plugins { create("myPlugin") { local { @@ -39,3 +35,10 @@ protoSourceSets { } } } + +kotlin.sourceSets { + main.proto { + plugin { getByName("myPlugin") } + plugin { getByName("myRemotePlugin") } + } +} 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 44c4b5591..7cd17a0a8 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 @@ -10,12 +10,12 @@ plugins { id("org.jetbrains.kotlinx.rpc.plugin") } -rpc { - protoc() +kotlin.sourceSets.main.proto { + plugin { getByName("myPlugin") } } -protoSourceSets { - main { +rpc { + protoc { plugins { create("myPlugin") {} } diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts similarity index 91% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/build.gradle.kts rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts index 60d6d646d..9cb883293 100644 --- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/build.gradle.kts +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/build.gradle.kts @@ -3,13 +3,14 @@ */ import org.gradle.kotlin.dsl.version +import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" id("org.jetbrains.kotlinx.rpc.plugin") } -protoSourceSets { +kotlin.sourceSets.apply { main { proto { exclude("exclude/**") diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/exclude.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/exclude.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/exclude.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/exclude.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/exclude/no1.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/exclude/no1.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/exclude/no1.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/exclude/no1.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/exclude/no2.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/exclude/no2.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/exclude/no2.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/exclude/no2.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/ok/ok.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/ok/ok.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/ok/ok.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/ok/ok.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/some.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/main/proto/some.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/main/proto/some.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/include.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/include.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/include.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/include.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/include/yes1.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/include/yes1.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/include/yes1.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/include/yes1.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/include/yes2.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/include/yes2.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/include/yes2.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/include/yes2.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/other.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/other.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/other.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/other.proto diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/some/package/hello/world/file.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/some/package/hello/world/file.proto similarity index 100% rename from gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_protosourcesets/src/test/proto/some/package/hello/world/file.proto rename to gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/exclude_and_include_in_proto_sourcesets/src/test/proto/some/package/hello/world/file.proto 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 new file mode 100644 index 000000000..a0d0a518b --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * 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 kotlinx.rpc.protoc.* + +plugins { + kotlin("jvm") version "" + id("org.jetbrains.kotlinx.rpc.plugin") +} + +sourceSets { + main { + proto { + plugin { getByName("myPlugin") } + exclude("no.proto") + } + } + + test { + proto { + exclude("no2.proto") + } + } +} + +kotlin { + sourceSets.main { + proto { + plugin { getByName("myPlugin2") } + } + } +} + +rpc { + protoc { + plugins { + create("myPlugin") { + local { + executor("some") + } + } + + create("myPlugin2") { + local { + executor("some2") + } + } + } + } +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/main/proto/no.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/main/proto/no.proto new file mode 100644 index 000000000..e69de29bb diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/main/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/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/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/no2.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/no2.proto new file mode 100644 index 000000000..e69de29bb diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/other.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/other.proto new file mode 100644 index 000000000..7ad86c779 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_and_kotlin_source_sets/src/test/proto/other.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Other { + string content = 1; +} 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 new file mode 100644 index 000000000..8b007f1fb --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/build.gradle.kts @@ -0,0 +1,29 @@ +/* + * 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 kotlinx.rpc.protoc.proto + +plugins { + kotlin("jvm") version "" + id("org.jetbrains.kotlinx.rpc.plugin") +} + +sourceSets { + main { + proto { + exclude("no.proto") + } + } + + test { + proto { + exclude("no2.proto") + } + } +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/main/proto/no.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/main/proto/no.proto new file mode 100644 index 000000000..e69de29bb diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/main/proto/some.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/main/proto/some.proto new file mode 100644 index 000000000..9ce4a4156 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/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/GrpcJvmProjectTest/java_source_sets/src/test/proto/no2.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/test/proto/no2.proto new file mode 100644 index 000000000..e69de29bb diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/test/proto/other.proto b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/test/proto/other.proto new file mode 100644 index 000000000..7ad86c779 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/java_source_sets/src/test/proto/other.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Other { + string content = 1; +} 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 85727fd2f..cc6194819 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 @@ -8,9 +8,3 @@ plugins { kotlin("jvm") version "" id("org.jetbrains.kotlinx.rpc.plugin") } - -// should nonetheless be available -protoSourceSets { - main {} - test {} -} 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 9d75c4634..06f29653b 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 @@ -4,17 +4,16 @@ import org.gradle.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.include import org.gradle.kotlin.dsl.version +import kotlinx.rpc.protoc.* plugins { kotlin("jvm") version "" id("org.jetbrains.kotlinx.rpc.plugin") } -protoSourceSets { - main { - proto { - exclude("some.proto") - } +kotlin.sourceSets.main { + proto { + exclude("some.proto") } } diff --git a/protobuf/protobuf-core/build.gradle.kts b/protobuf/protobuf-core/build.gradle.kts index 500d37acb..8623c62f8 100644 --- a/protobuf/protobuf-core/build.gradle.kts +++ b/protobuf/protobuf-core/build.gradle.kts @@ -7,6 +7,7 @@ import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.internal.InternalRpcApi import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency +import kotlinx.rpc.protoc.proto import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import util.configureCLibCInterop @@ -31,6 +32,10 @@ kotlin { api(libs.kotlinx.io.core) } + + proto { + exclude("exclude/**") + } } commonTest { @@ -64,17 +69,11 @@ kotlin { } } -protoSourceSets { - commonTest { - proto { - exclude("exclude/**") - } - } -} - rpc { - protoc.buf.generate.comments { - includeFileLevelComments = false + protoc { + buf.generate.comments { + includeFileLevelComments = false + } } } diff --git a/tests/protobuf-conformance/build.gradle.kts b/tests/protobuf-conformance/build.gradle.kts index 265437f26..c8e4c6f80 100644 --- a/tests/protobuf-conformance/build.gradle.kts +++ b/tests/protobuf-conformance/build.gradle.kts @@ -37,8 +37,10 @@ setupProtobufConformanceResources() configureLocalProtocGenDevelopmentDependency("Main") rpc { - protoc.buf.generate.comments { - includeFileLevelComments = false + protoc { + buf.generate.comments { + includeFileLevelComments = false + } } } From 0c1f294269ed8c1e9fb65c92b5ce10c82c2737c2 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Wed, 19 Nov 2025 21:23:47 +0100 Subject: [PATCH 2/4] Added testing with multiple Gradle versions --- gradle-plugin/build.gradle.kts | 6 +- .../kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt | 26 +++---- .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 14 ++-- .../test/kotlin/kotlinx/rpc/base/BaseTest.kt | 70 ++++++++++++++----- .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 28 +++++--- .../build.gradle.kts | 6 +- .../build.gradle.kts | 1 - .../no_jvm_targets/build.gradle.kts | 6 -- .../with_other_targets/build.gradle.kts | 9 +-- 9 files changed, 97 insertions(+), 69 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 011f8c3ae..edce9f75d 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -5,10 +5,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import util.other.generateSource -/* - * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - plugins { `kotlin-dsl` alias(libs.plugins.conventions.jvm) @@ -22,7 +18,7 @@ version = rootProject.libs.versions.kotlinx.rpc.get() kotlin { explicitApi() - jvmToolchain(11) + jvmToolchain(17) } tasks.withType().configureEach { diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt index c58340b97..d381eb67c 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt @@ -6,16 +6,16 @@ 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 -import kotlin.test.Test import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GrpcJvmProjectTest : GrpcBaseTest() { override val isKmp: Boolean = false - @Test + @TestFactory fun `Minimal gRPC Configuration`() = runGrpcTest { val result = runGradle(bufGenerateCommonMain) @@ -30,7 +30,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `No gRPC`() = runGrpcTest { runNonExistentTask(bufGenerateCommonMain) runNonExistentTask(bufGenerateCommonTest) @@ -43,7 +43,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { runNonExistentTask(generateBufGenYamlCommonTest) } - @Test + @TestFactory fun `Test-Only Sources`() = runGrpcTest { val result = runGradle(bufGenerateCommonTest) @@ -60,7 +60,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `Main and Test Mixed Sources`() = runGrpcTest { val mainRun = runGradle(bufGenerateCommonMain) @@ -96,7 +96,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `Java Source Sets`() = runGrpcTest { val mainRun = runGradle(bufGenerateCommonMain) @@ -132,7 +132,7 @@ class GrpcJvmProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `Java and Kotlin Source Sets`() = runGrpcTest { runGradle(processCommonMainProtoFiles) runGradle(generateBufGenYamlCommonMain) @@ -194,7 +194,7 @@ inputs: ) } - @Test + @TestFactory fun `Exclude and Include in proto sourceSets`() = runGrpcTest { runGradle(processCommonMainProtoFiles) @@ -223,7 +223,7 @@ inputs: ) } - @Test + @TestFactory fun `Can Add Custom Protoc Plugins`() = runGrpcTest { runGradle(generateBufGenYamlCommonMain) @@ -272,7 +272,7 @@ inputs: ) } - @Test + @TestFactory fun `Custom Protoc Plugins Must Declare an Artifact`() = runGrpcTest { val result = runGradleToFail(generateBufGenYamlCommonMain) assert(result.output.contains("Artifact is not specified for protoc plugin myPlugin")) @@ -282,7 +282,7 @@ inputs: private val cliFlagsRegex = "- Buf Arguments: \\[.*?, generate, --output, .*?, --include-imports, --include-wkt, --error-format, json, --config, some\\.buf\\.yaml, --log-format, json, --timeout, 60s]".toRegex() - @Test + @TestFactory fun `Custom Buf CLI Flags`() = runGrpcTest { val result = runGradleToFail(bufGenerateCommonMain) assert(result.output.contains("could not read file: open some.buf.yaml: no such file or directory")) { @@ -295,14 +295,14 @@ inputs: } } - @Test + @TestFactory fun `Skip Generation When No Proto Files`() = runGrpcTest { val result = runGradle(bufGenerateCommonMain) assertEquals(TaskOutcome.SKIPPED, result.protoTaskOutcome(bufGenerateCommonMain)) } - @Test + @TestFactory fun `Proto Tasks Are Cached Properly`() = runGrpcTest { val firstRunMain = runGradle(bufGenerateCommonMain) diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index 865d20576..b5a477ec1 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -5,15 +5,15 @@ package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest +import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInstance import kotlin.io.path.Path -import kotlin.test.Test @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GrpcKmpProjectTest : GrpcBaseTest() { override val isKmp: Boolean = true - @Test + @TestFactory fun `Minimal gRPC Configuration`() = runGrpcTest { val result = runGradle(bufGenerateCommonMain) @@ -28,7 +28,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `No gRPC`() = runGrpcTest { runNonExistentTask(bufGenerateCommonMain) runNonExistentTask(bufGenerateCommonTest) @@ -41,7 +41,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { runNonExistentTask(generateBufGenYamlCommonTest) } - @Test + @TestFactory fun `Test-Only Sources`() = runGrpcTest { val result = runGradle(bufGenerateCommonTest) @@ -58,7 +58,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `Main and Test Mixed Sources`() = runGrpcTest { val mainRun = runGradle(bufGenerateCommonMain) @@ -94,7 +94,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) } - @Test + @TestFactory fun `No JVM Targets`() = runGrpcTest { runNonExistentTask(bufGenerateCommonMain) runNonExistentTask(bufGenerateCommonTest) @@ -107,7 +107,7 @@ class GrpcKmpProjectTest : GrpcBaseTest() { runNonExistentTask(generateBufGenYamlCommonTest) } - @Test + @TestFactory fun `With Other Targets`() = runGrpcTest { val result = runGradle(bufGenerateCommonMain) 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 febb3e71b..f006bb3f4 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/BaseTest.kt @@ -16,35 +16,64 @@ import kotlin.io.path.copyTo import kotlin.io.path.copyToRecursively import kotlin.io.path.readText import kotlin.io.path.writeText -import kotlinx.rpc.KOTLIN_VERSION import kotlinx.rpc.BUILD_REPO +import org.junit.jupiter.api.DynamicTest +import java.util.stream.Stream import kotlin.io.path.absolute import kotlin.io.path.createDirectories import kotlin.io.path.deleteRecursively +class VersionsEnv( + val gradle: String, + val kotlin: String, +) + +private val GradleVersions = listOf( + VersionsEnv("9.2.1", "2.2.21"), + VersionsEnv("8.14.1", "2.2.0"), + VersionsEnv("8.8", "2.0.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) + } + } +} + @OptIn(ExperimentalPathApi::class) @TestInstance(TestInstance.Lifecycle.PER_METHOD) abstract class BaseTest { + private lateinit var testClassName: String + private lateinit var testMethodName: String + private lateinit var baseDir: Path private lateinit var projectDir: Path @BeforeEach protected fun setup(testInfo: TestInfo) { TEST_KIT_PATH.createDirectories() - val testClassName = testInfo.testClass.get().simpleName - val testMethodName = testInfo.testMethod.get().name + testClassName = testInfo.testClass.get().simpleName + testMethodName = testInfo.testMethod.get().name .replace(nameRegex, "_") .lowercase() - val baseDir = TEST_PROJECTS_PATH + baseDir = TEST_PROJECTS_PATH .resolve(testClassName) .resolve(testMethodName) baseDir.deleteRecursively() baseDir.createDirectories() + } + + fun setupTest(versions: VersionsEnv) { + val versioned = baseDir.resolve(versions.gradle.replace(".", "_")) - projectDir = baseDir.resolve(PROJECT_DIR) - val buildCacheDir = baseDir.resolve(BUILD_CACHE_DIR) + projectDir = versioned.resolve(PROJECT_DIR) + val buildCacheDir = versioned.resolve(BUILD_CACHE_DIR) projectDir.createDirectories() buildCacheDir.createDirectories() @@ -72,7 +101,8 @@ abstract class BaseTest { testTemplateDirectory.copyToRecursively(projectDir, followLinks = false, overwrite = true) val buildScriptFile = projectDir.resolve("build.gradle.kts") - buildScriptFile.replace("", KOTLIN_VERSION) + buildScriptFile + .replace("", versions.kotlin) println(""" Setup project '$projectName' @@ -83,12 +113,14 @@ abstract class BaseTest { private fun runGradleInternal( task: String, + versions: VersionsEnv, vararg args: String, body: GradleRunner.() -> BuildResult, ): BuildResult { val gradleRunner = GradleRunner.create() .withProjectDir(projectDir.absolute().toFile()) .withTestKitDir(TEST_KIT_PATH.absolute().toFile()) + .withGradleVersion(versions.gradle) .withPluginClasspath() .withArguments( listOfNotNull( @@ -108,10 +140,6 @@ abstract class BaseTest { return gradleRunner.body() } - protected fun runBaseTest(body: TestEnv.() -> Unit) { - runTest(TestEnv(), body) - } - protected fun runTest(testEnv: T, body: T.() -> Unit) { try { testEnv.body() @@ -127,19 +155,27 @@ abstract class BaseTest { } } - open inner class TestEnv { + open inner class TestEnv( + val versions: VersionsEnv, + ) { val projectDir: Path get() = this@BaseTest.projectDir var latestBuild: BuildResult? = null private set - fun runGradle(task: String, vararg args: String): BuildResult { - return runGradleInternal(task, *args) { + fun runGradle( + task: String, + vararg args: String, + ): BuildResult { + return runGradleInternal(task, versions, *args) { build().also { latestBuild = it } } } - fun runGradleToFail(task: String, vararg args: String): BuildResult { - return runGradleInternal(task, *args) { + fun runGradleToFail( + task: String, + vararg args: String, + ): BuildResult { + return runGradleInternal(task, versions, *args) { buildAndFail().also { latestBuild = it } } } @@ -151,7 +187,7 @@ abstract class BaseTest { } fun BuildResult.assertNoTask(name: String) { - assert(output.contains("Task '$name' not found")) { + assert(tasks.none { it.path.endsWith(":$name") }) { "Task '$name' should not be present in the project" } } 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 176bf46f4..b05a8de2e 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -7,8 +7,10 @@ package kotlinx.rpc.base import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.TestInstance import java.nio.file.Path +import java.util.stream.Stream import kotlin.io.path.* import kotlin.test.assertEquals import kotlin.test.fail @@ -17,11 +19,11 @@ import kotlin.test.fail abstract class GrpcBaseTest : BaseTest() { abstract val isKmp: Boolean - protected fun runGrpcTest(test: GrpcTestEnv.() -> Unit) { - runTest(GrpcTestEnv(), test) + protected fun runGrpcTest(test: GrpcTestEnv.() -> Unit): Stream = runWithAllGradleVersions { + runTest(GrpcTestEnv(it), test) } - inner class GrpcTestEnv : TestEnv() { + inner class GrpcTestEnv(versions: VersionsEnv) : TestEnv(versions) { fun BuildResult.protoTaskOutcome(name: String): TaskOutcome { return tasks.find { it.path == ":$name" }?.outcome ?: fail("Task ':$name' was not present in the build result") @@ -213,15 +215,19 @@ abstract class GrpcBaseTest : BaseTest() { .resolve("generated") } - val mainProtoFileSources: Path = projectDir - .resolve("src") - .resolve(mainSourceSet) - .resolve("proto") + val mainProtoFileSources: Path by lazy { + projectDir + .resolve("src") + .resolve(mainSourceSet) + .resolve("proto") + } - val testProtoFileSources: Path = projectDir - .resolve("src") - .resolve(testSourceSet) - .resolve("proto") + val testProtoFileSources: Path by lazy { + projectDir + .resolve("src") + .resolve(testSourceSet) + .resolve("proto") + } } companion object { 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 3e354eeca..e7c9e3fa6 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 @@ -21,9 +21,9 @@ rpc { options.put("hello", "world") options.put("foo", "bar") - strategy = ProtocPlugin.Strategy.All - includeImports = true - includeWkt = false + strategy.set(ProtocPlugin.Strategy.All) + includeImports.set(true) + includeWkt.set(false) types = listOf("my.type.Yay") excludeTypes = listOf("my.type.Nope") } 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 06f29653b..810cdd8a7 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 @@ -2,7 +2,6 @@ * 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.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.include import org.gradle.kotlin.dsl.version import kotlinx.rpc.protoc.* 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 0e712e9ca..7c8d7c9c6 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 @@ -13,9 +13,3 @@ kotlin { js() macosArm64() } - -// should nonetheless be available -protoSourceSets { - commonMain {} - commonTest {} -} 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 1a8ba4661..20bdf1041 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 @@ -11,15 +11,12 @@ plugins { kotlin { jvm() - js() + js { + nodejs() + } macosArm64() } -protoSourceSets { - commonMain {} - commonTest {} -} - rpc { protoc() } From 5be747ed99140b17e7bd4fbc367edbc324644f4b Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Thu, 20 Nov 2025 15:14:46 +0100 Subject: [PATCH 3/4] Added execution task order test for KMP --- .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 92 ++++++++++ .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 161 ++++++++++++++---- .../kmp_hierarchy/build.gradle.kts | 22 +++ .../src/appleMain/proto/appleMain.proto | 5 + .../src/appleTest/proto/appleTest.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/jsMain/proto/jsMain.proto | 5 + .../src/jsTest/proto/jsTest.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../macosArm64Main/proto/macosArm64Main.proto | 5 + .../macosArm64Test/proto/macosArm64Test.proto | 5 + .../src/macosMain/proto/macosMain.proto | 5 + .../src/macosTest/proto/macosTest.proto | 5 + .../src/nativeMain/proto/nativeMain.proto | 5 + .../src/nativeTest/proto/nativeTest.proto | 5 + 17 files changed, 315 insertions(+), 30 deletions(-) create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleMain/proto/appleMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleTest/proto/appleTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsMain/proto/jsMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsTest/proto/jsTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Main/proto/macosArm64Main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Test/proto/macosArm64Test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosMain/proto/macosMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosTest/proto/macosTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeMain/proto/nativeMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeTest/proto/nativeTest.proto diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index b5a477ec1..b8f86d5dc 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -121,4 +121,96 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) ) } + + @TestFactory + fun `KMP Hierarchy`() = runGrpcTest { + runKmp( + SSets.commonMain, + ) + + runKmp( + SSets.commonTest, + SSets.commonMain, + ) + + runKmp( + SSets.nativeMain, + SSets.commonMain, + ) + + runKmp( + SSets.nativeTest, + SSets.commonMain, SSets.nativeMain, + SSets.commonTest, + ) + + runKmp( + SSets.jvmMain, + SSets.commonMain, + ) + + runKmp( + SSets.jvmTest, + SSets.commonMain, SSets.jvmMain, + SSets.commonTest, + ) + + runKmp( + SSets.jsMain, + SSets.commonMain, + ) + + runKmp( + SSets.jsTest, + SSets.commonMain, SSets.jsMain, + SSets.commonTest, + ) + + runKmp( + SSets.appleMain, + SSets.commonMain, SSets.nativeMain, + ) + + runKmp( + SSets.appleTest, + SSets.commonMain, SSets.nativeMain, SSets.appleMain, + SSets.commonTest, SSets.nativeTest + ) + + runKmp( + SSets.macosMain, + SSets.commonMain, SSets.nativeMain, SSets.appleMain, + ) + + runKmp( + SSets.macosTest, + SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, + SSets.commonTest, SSets.nativeTest, SSets.appleTest + ) + + runKmp( + SSets.macosArm64Main, + SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, + ) + + runKmp( + SSets.macosArm64Test, + SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, SSets.macosArm64Main, + SSets.commonTest, SSets.nativeTest, SSets.appleTest, SSets.macosTest, + clean = false, + ) + } + + private fun GrpcTestEnv.runKmp( + sourceSet: SSets, + vararg imports: SSets, + clean: Boolean = true, + ) { + runGradle(bufGenerate(sourceSet)) + .assertKmpSourceSet(sourceSet, *imports) + + if (clean) { + cleanProtoBuildDir() + } + } } 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 b05a8de2e..fc2893d85 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -36,8 +36,8 @@ abstract class GrpcBaseTest : BaseTest() { } } - fun assertSourceCodeGenerated(sourceSet: String, vararg files: Path) { - val dir = protoBuildDirGenerated.resolve(sourceSet).resolve(KOTLIN_MULTIPLATFORM_DIR) + fun assertSourceCodeGenerated(sourceSet: SSets, vararg files: Path) { + val dir = protoBuildDirGenerated.resolve(sourceSet.name).resolve(KOTLIN_MULTIPLATFORM_DIR) if (files.isNotEmpty()) { assert(dir.exists()) { "Directory '$dir' with generated sources does not exist" @@ -51,8 +51,8 @@ abstract class GrpcBaseTest : BaseTest() { } } - fun assertSourceCodeNotGenerated(sourceSet: String, vararg files: Path) { - val dir = protoBuildDirGenerated.resolve(sourceSet).resolve(KOTLIN_MULTIPLATFORM_DIR) + fun assertSourceCodeNotGenerated(sourceSet: SSets, vararg files: Path) { + val dir = protoBuildDirGenerated.resolve(sourceSet.name).resolve(KOTLIN_MULTIPLATFORM_DIR) files.forEach { file -> assert(!dir.resolve(file).exists()) { @@ -61,9 +61,37 @@ abstract class GrpcBaseTest : BaseTest() { } } + fun assertSourceCodeNotGeneratedExcept(sourceSet: SSets, vararg files: Path) { + val dir = protoBuildDirGenerated.resolve(sourceSet.name).resolve(KOTLIN_MULTIPLATFORM_DIR) + + fun Path.doAssert() { + listDirectoryEntries().forEach { entry -> + when { + entry.isDirectory() -> { + entry.doAssert() + } + + entry.isRegularFile() && entry.relativeTo(dir) in files -> { + // fine + } + + entry.name == ".keep" -> { + // fine + } + + else -> { + fail("File '${entry}' in '$this' should not exist") + } + } + } + } + + dir.doAssert() + } + @OptIn(ExperimentalPathApi::class) - private fun assertWorkspaceProtoFilesCopiedInternal(vararg files: Path, sourceSet: String, dir: String) { - val protoSources = protoBuildDirSourceSets.resolve(sourceSet).resolve(dir) + private fun assertWorkspaceProtoFilesCopiedInternal(vararg files: Path, sourceSet: SSets, dir: String) { + val protoSources = protoBuildDirSourceSets.resolve(sourceSet.name).resolve(dir) val included = files.map { file -> val resolved = protoSources.resolve(file) assert(resolved.exists()) { @@ -80,11 +108,11 @@ abstract class GrpcBaseTest : BaseTest() { } } - fun assertWorkspaceProtoFilesCopied(sourceSet: String, vararg files: Path) { + fun assertWorkspaceProtoFilesCopied(sourceSet: SSets, vararg files: Path) { assertWorkspaceProtoFilesCopiedInternal(*files, sourceSet = sourceSet, dir = "proto") } - fun assertWorkspaceImportProtoFilesCopied(sourceSet: String, vararg files: Path) { + fun assertWorkspaceImportProtoFilesCopied(sourceSet: SSets, vararg files: Path) { assertWorkspaceProtoFilesCopiedInternal(*files, sourceSet = sourceSet, dir = "import") } @@ -93,9 +121,9 @@ abstract class GrpcBaseTest : BaseTest() { protoBuildDir.deleteRecursively() } - fun assertBufGenYaml(sourceSet: String, @Language("Yaml") content: String) { + fun assertBufGenYaml(sourceSet: SSets, @Language("Yaml") content: String) { val file = protoBuildDirSourceSets - .resolve(sourceSet) + .resolve(sourceSet.name) .resolve("buf.gen.yaml") assert(file.exists()) { @@ -107,9 +135,11 @@ abstract class GrpcBaseTest : BaseTest() { it.contains("protoc-gen-kotlin-multiplatform") -> { it.replace(localPluginExecRegex, "[protoc-gen-kotlin-multiplatform]") } + it.contains("protoc-gen-grpc-kotlin-multiplatform") -> { it.replace(localPluginExecRegex, "[protoc-gen-grpc-kotlin-multiplatform]") } + else -> { it } @@ -182,22 +212,72 @@ abstract class GrpcBaseTest : BaseTest() { assertWorkspaceImportProtoFilesCopied(mainSourceSet) } - val mainSourceSet = if (isKmp) "${KMP_SOURCE_SET}Main" else "main" - val testSourceSet = if (isKmp) "${KMP_SOURCE_SET}Test" else "test" - val bufGenerateCommonMain = if (isKmp) "bufGenerate${KMP_COMMON_SOURCE_SET_CAPITAL}Main" else "bufGenerateMain" - val bufGenerateCommonTest = if (isKmp) "bufGenerate${KMP_COMMON_SOURCE_SET_CAPITAL}Test" else "bufGenerateTest" - val processCommonMainProtoFiles = - if (isKmp) "process${KMP_COMMON_SOURCE_SET_CAPITAL}MainProtoFiles" else "processMainProtoFiles" - val processCommonTestProtoFiles = - if (isKmp) "process${KMP_COMMON_SOURCE_SET_CAPITAL}TestProtoFiles" else "processTestProtoFiles" - val processCommonTestProtoFilesImports = - if (isKmp) "process${KMP_COMMON_SOURCE_SET_CAPITAL}TestProtoFilesImports" else "processTestProtoFilesImports" - val generateBufYamlCommonMain = if (isKmp) "generateBufYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Main" else "generateBufYamlMain" - val generateBufYamlCommonTest = if (isKmp) "generateBufYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Test" else "generateBufYamlTest" - val generateBufGenYamlCommonMain = - if (isKmp) "generateBufGenYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Main" else "generateBufGenYamlMain" - val generateBufGenYamlCommonTest = - if (isKmp) "generateBufGenYaml${KMP_COMMON_SOURCE_SET_CAPITAL}Test" else "generateBufGenYamlTest" + fun BuildResult.assertTaskExecuted( + sourceSet: SSets, + protoFiles: List, + importProtoFiles: List, + 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))) + + assertSourceCodeGenerated(sourceSet, *generatedFiles.toTypedArray()) + assertSourceCodeNotGeneratedExcept(sourceSet, *generatedFiles.toTypedArray()) + assertWorkspaceProtoFilesCopied(sourceSet, *protoFiles.toTypedArray()) + assertWorkspaceImportProtoFilesCopied(sourceSet, *importProtoFiles.toTypedArray()) + + notExecuted.forEach { + assertProtoTaskNotExecuted(bufGenerate(it)) + assertProtoTaskNotExecuted(processProtoFiles(it)) + assertProtoTaskNotExecuted(processProtoFilesImports(it)) + assertProtoTaskNotExecuted(generateBufYaml(it)) + assertProtoTaskNotExecuted(generateBufGenYaml(it)) + } + } + + fun BuildResult.assertKmpSourceSet( + sourceSet: SSets, + vararg imports: SSets, + ) { + val ktFile = "${sourceSet.capital}.kt" + val importsSet = imports.toSet() + + assertTaskExecuted( + sourceSet = sourceSet, + protoFiles = listOf(Path("${sourceSet.name}.proto")), + importProtoFiles = imports.map { + Path("${it.name}.proto") + }, + generatedFiles = listOf( + Path(ktFile), + Path(RPC_INTERNAL, ktFile), + ), + notExecuted = SSets.entries.filter { it != sourceSet && it !in importsSet }, + ) + } + + fun bufGenerate(sourceSet: SSets) = "bufGenerate${sourceSet.capital}" + fun processProtoFiles(sourceSet: SSets) = "process${sourceSet.capital}ProtoFiles" + fun processProtoFilesImports(sourceSet: SSets) = "process${sourceSet.capital}ProtoFilesImports" + 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 bufGenerateCommonMain = bufGenerate(mainSourceSet) + val bufGenerateCommonTest = bufGenerate(testSourceSet) + val processCommonMainProtoFiles = processProtoFiles(mainSourceSet) + val processCommonTestProtoFiles = processProtoFiles(testSourceSet) + val processCommonTestProtoFilesImports = processProtoFilesImports(testSourceSet) + val generateBufYamlCommonMain = generateBufYaml(mainSourceSet) + val generateBufYamlCommonTest = generateBufYaml(testSourceSet) + val generateBufGenYamlCommonMain = generateBufGenYaml(mainSourceSet) + val generateBufGenYamlCommonTest = generateBufGenYaml(testSourceSet) val protoBuildDir: Path by lazy { projectDir @@ -218,21 +298,42 @@ abstract class GrpcBaseTest : BaseTest() { val mainProtoFileSources: Path by lazy { projectDir .resolve("src") - .resolve(mainSourceSet) + .resolve(mainSourceSet.name) .resolve("proto") } val testProtoFileSources: Path by lazy { projectDir .resolve("src") - .resolve(testSourceSet) + .resolve(testSourceSet.name) + .resolve("proto") + } + + fun SSets.sourceDir(): Path { + return projectDir + .resolve("src") + .resolve(name) .resolve("proto") } } + @Suppress("EnumEntryName") + enum class SSets { + main, test, + + commonMain, commonTest, + jvmMain, jvmTest, + androidMain, androidTest, + jsMain, jsTest, + nativeMain, nativeTest, + appleMain, appleTest, + macosMain, macosTest, + macosArm64Main, macosArm64Test, + } + + private val SSets.capital get() = name.replaceFirstChar { it.titlecase() } + companion object { - private const val KMP_SOURCE_SET = "common" - private val KMP_COMMON_SOURCE_SET_CAPITAL = KMP_SOURCE_SET.replaceFirstChar(Char::uppercaseChar) private const val KOTLIN_MULTIPLATFORM_DIR = "kotlin-multiplatform" const val RPC_INTERNAL = "_rpc_internal" } 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 new file mode 100644 index 000000000..20bdf1041 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * 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") +} + +kotlin { + jvm() + js { + nodejs() + } + macosArm64() +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleMain/proto/appleMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleMain/proto/appleMain.proto new file mode 100644 index 000000000..dd73f50fb --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleMain/proto/appleMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AppleMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleTest/proto/appleTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleTest/proto/appleTest.proto new file mode 100644 index 000000000..b3c6b034f --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/appleTest/proto/appleTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AppleTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/commonMain/proto/commonMain.proto new file mode 100644 index 000000000..ef46aa298 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/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/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/commonTest/proto/commonTest.proto new file mode 100644 index 000000000..f2aa03a69 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/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/src/jsMain/proto/jsMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsMain/proto/jsMain.proto new file mode 100644 index 000000000..94cedbed5 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsMain/proto/jsMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JsMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsTest/proto/jsTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsTest/proto/jsTest.proto new file mode 100644 index 000000000..9ea0eb27a --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jsTest/proto/jsTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JsTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jvmMain/proto/jvmMain.proto new file mode 100644 index 000000000..936ce0d1d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/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/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/jvmTest/proto/jvmTest.proto new file mode 100644 index 000000000..8786bedaa --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/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/src/macosArm64Main/proto/macosArm64Main.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Main/proto/macosArm64Main.proto new file mode 100644 index 000000000..41c097c16 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Main/proto/macosArm64Main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosArm64Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Test/proto/macosArm64Test.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Test/proto/macosArm64Test.proto new file mode 100644 index 000000000..a33f00ee7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosArm64Test/proto/macosArm64Test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosArm64Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosMain/proto/macosMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosMain/proto/macosMain.proto new file mode 100644 index 000000000..c65489499 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosMain/proto/macosMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosTest/proto/macosTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosTest/proto/macosTest.proto new file mode 100644 index 000000000..b65cd541c --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/macosTest/proto/macosTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeMain/proto/nativeMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeMain/proto/nativeMain.proto new file mode 100644 index 000000000..bbd13541d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeMain/proto/nativeMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message NativeMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeTest/proto/nativeTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeTest/proto/nativeTest.proto new file mode 100644 index 000000000..df96a67d0 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/kmp_hierarchy/src/nativeTest/proto/nativeTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message NativeTest { + string content = 1; +} From 5e727979ab73967300a7444d15f304777f2c5e69 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Sat, 22 Nov 2025 00:52:40 +0100 Subject: [PATCH 4/4] Added caching task test for KMP --- .../kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt | 555 +++++++++++++++++- .../kotlin/kotlinx/rpc/base/GrpcBaseTest.kt | 4 + .../build.gradle.kts | 22 + .../src/appleMain/proto/appleMain.proto | 5 + .../src/appleTest/proto/appleTest.proto | 5 + .../src/commonMain/proto/commonMain.proto | 5 + .../src/commonTest/proto/commonTest.proto | 5 + .../src/jsMain/proto/jsMain.proto | 5 + .../src/jsTest/proto/jsTest.proto | 5 + .../src/jvmMain/proto/jvmMain.proto | 5 + .../src/jvmTest/proto/jvmTest.proto | 5 + .../macosArm64Main/proto/macosArm64Main.proto | 5 + .../macosArm64Test/proto/macosArm64Test.proto | 5 + .../src/macosMain/proto/macosMain.proto | 5 + .../src/macosTest/proto/macosTest.proto | 5 + .../src/nativeMain/proto/nativeMain.proto | 5 + .../src/nativeTest/proto/nativeTest.proto | 5 + 17 files changed, 634 insertions(+), 17 deletions(-) create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleMain/proto/appleMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleTest/proto/appleTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/commonMain/proto/commonMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/commonTest/proto/commonTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsMain/proto/jsMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsTest/proto/jsTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jvmMain/proto/jvmMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jvmTest/proto/jvmTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Main/proto/macosArm64Main.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Test/proto/macosArm64Test.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosMain/proto/macosMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosTest/proto/macosTest.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeMain/proto/nativeMain.proto create mode 100644 gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeTest/proto/nativeTest.proto diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt index b8f86d5dc..728b0365a 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt @@ -5,9 +5,12 @@ package kotlinx.rpc import kotlinx.rpc.base.GrpcBaseTest +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInstance import kotlin.io.path.Path +import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_METHOD) class GrpcKmpProjectTest : GrpcBaseTest() { @@ -124,76 +127,76 @@ class GrpcKmpProjectTest : GrpcBaseTest() { @TestFactory fun `KMP Hierarchy`() = runGrpcTest { - runKmp( + runKmpAndCheckFiles( SSets.commonMain, ) - runKmp( + runKmpAndCheckFiles( SSets.commonTest, SSets.commonMain, ) - runKmp( + runKmpAndCheckFiles( SSets.nativeMain, SSets.commonMain, ) - runKmp( + runKmpAndCheckFiles( SSets.nativeTest, SSets.commonMain, SSets.nativeMain, SSets.commonTest, ) - runKmp( + runKmpAndCheckFiles( SSets.jvmMain, SSets.commonMain, ) - runKmp( + runKmpAndCheckFiles( SSets.jvmTest, SSets.commonMain, SSets.jvmMain, SSets.commonTest, ) - runKmp( + runKmpAndCheckFiles( SSets.jsMain, SSets.commonMain, ) - runKmp( + runKmpAndCheckFiles( SSets.jsTest, SSets.commonMain, SSets.jsMain, SSets.commonTest, ) - runKmp( + runKmpAndCheckFiles( SSets.appleMain, SSets.commonMain, SSets.nativeMain, ) - runKmp( + runKmpAndCheckFiles( SSets.appleTest, SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.commonTest, SSets.nativeTest ) - runKmp( + runKmpAndCheckFiles( SSets.macosMain, SSets.commonMain, SSets.nativeMain, SSets.appleMain, ) - runKmp( + runKmpAndCheckFiles( SSets.macosTest, SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, SSets.commonTest, SSets.nativeTest, SSets.appleTest ) - runKmp( + runKmpAndCheckFiles( SSets.macosArm64Main, SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, ) - runKmp( + runKmpAndCheckFiles( SSets.macosArm64Test, SSets.commonMain, SSets.nativeMain, SSets.appleMain, SSets.macosMain, SSets.macosArm64Main, SSets.commonTest, SSets.nativeTest, SSets.appleTest, SSets.macosTest, @@ -201,16 +204,534 @@ class GrpcKmpProjectTest : GrpcBaseTest() { ) } - private fun GrpcTestEnv.runKmp( + @TestFactory + fun `Proto Tasks Are Cached Properly`() = runGrpcTest { + val firstRunCommonMain = runKmp(SSets.commonMain) + + assertOutcomes( + result = firstRunCommonMain, + sourceSet = SSets.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + assertOutcomes(firstRunCommonMain, SSets.commonTest) + assertOutcomes(firstRunCommonMain, SSets.nativeMain) + assertOutcomes(firstRunCommonMain, SSets.nativeTest) + assertOutcomes(firstRunCommonMain, SSets.jvmMain) + assertOutcomes(firstRunCommonMain, SSets.jsTest) + 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, + 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, + ) + + cleanProtoBuildDir() + + val thirdRunCommonMain = runKmp(SSets.commonMain) + + assertOutcomes( + result = thirdRunCommonMain, + sourceSet = SSets.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + SSets.commonMain.sourceDir() + .resolve("commonMain.proto") + .replace("content = 1", "content = 2") + + val fourthRunCommonMain = runKmp(SSets.commonMain) + + assertOutcomes( + result = fourthRunCommonMain, + sourceSet = SSets.commonMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + val firstRunMacosArm64Main = runKmp(SSets.macosArm64Main) + + assertOutcomes( + result = firstRunMacosArm64Main, + sourceSet = SSets.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, + ) + + assertOutcomes( + result = firstRunMacosArm64Main, + sourceSet = SSets.nativeMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = firstRunMacosArm64Main, + sourceSet = SSets.appleMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = firstRunMacosArm64Main, + sourceSet = SSets.macosMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = firstRunMacosArm64Main, + sourceSet = SSets.macosArm64Main, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + assertOutcomes(firstRunMacosArm64Main, SSets.nativeTest) + assertOutcomes(firstRunMacosArm64Main, SSets.jvmMain) + assertOutcomes(firstRunMacosArm64Main, SSets.jsTest) + 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, + 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, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.nativeMain, + 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, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.appleMain, + 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, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.macosMain, + 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, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.macosArm64Main, + 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, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.nativeTest, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.appleTest, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.macosTest, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = firstRunMacosArm64Test, + sourceSet = SSets.macosArm64Test, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + assertOutcomes(firstRunMacosArm64Test, SSets.jvmMain) + assertOutcomes(firstRunMacosArm64Test, SSets.jsTest) + assertOutcomes(firstRunMacosArm64Test, SSets.jsMain) + assertOutcomes(firstRunMacosArm64Test, SSets.jsTest) + + SSets.macosMain.sourceDir() + .resolve("macosMain.proto") + .replace("content = 1", "content = 2") + + val fifthRunCommonMain = runKmp(SSets.commonMain) + + assertOutcomes( + result = fifthRunCommonMain, + sourceSet = SSets.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, + ) + + // didn't run + assertOutcomes(fifthRunCommonMain, SSets.commonTest) + assertOutcomes(fifthRunCommonMain, SSets.nativeMain) + assertOutcomes(fifthRunCommonMain, SSets.nativeTest) + assertOutcomes(fifthRunCommonMain, SSets.jvmMain) + assertOutcomes(fifthRunCommonMain, SSets.jsTest) + 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, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Main, + sourceSet = SSets.nativeMain, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Main, + sourceSet = SSets.appleMain, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Main, + sourceSet = SSets.macosMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.UP_TO_DATE, + ) + + assertOutcomes( + result = secondRunMacosArm64Main, + sourceSet = SSets.macosArm64Main, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + assertOutcomes(secondRunMacosArm64Main, SSets.nativeTest) + assertOutcomes(secondRunMacosArm64Main, SSets.jvmMain) + assertOutcomes(secondRunMacosArm64Main, SSets.jsTest) + 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, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.nativeMain, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.appleMain, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.macosMain, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.macosArm64Main, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.nativeTest, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.appleTest, + 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, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.macosTest, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + assertOutcomes( + result = secondRunMacosArm64Test, + sourceSet = SSets.macosArm64Test, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.UP_TO_DATE, + bufGenYaml = TaskOutcome.UP_TO_DATE, + protoFiles = TaskOutcome.UP_TO_DATE, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + assertOutcomes(secondRunMacosArm64Test, SSets.jvmMain) + assertOutcomes(secondRunMacosArm64Test, SSets.jsTest) + assertOutcomes(secondRunMacosArm64Test, SSets.jsMain) + assertOutcomes(secondRunMacosArm64Test, SSets.jsTest) + + val firstRunJvmMain = runKmp(SSets.jvmMain) + + assertOutcomes( + result = firstRunJvmMain, + sourceSet = SSets.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, + ) + + assertOutcomes( + result = firstRunJvmMain, + sourceSet = SSets.jvmMain, + generate = TaskOutcome.SUCCESS, + bufYaml = TaskOutcome.SUCCESS, + bufGenYaml = TaskOutcome.SUCCESS, + protoFiles = TaskOutcome.SUCCESS, + protoFilesImports = TaskOutcome.SUCCESS, + ) + + // didn't run + assertOutcomes(firstRunJvmMain, SSets.commonTest) + assertOutcomes(firstRunJvmMain, SSets.nativeMain) + assertOutcomes(firstRunJvmMain, SSets.nativeTest) + assertOutcomes(firstRunJvmMain, SSets.jsTest) + 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() + .resolve("jvmMain.proto") + .replace("content = 1", "content = 2") + + val secondRunJvmMain = runKmp(SSets.jvmMain) + + assertOutcomes( + result = secondRunJvmMain, + sourceSet = SSets.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, + ) + + assertOutcomes( + result = secondRunJvmMain, + sourceSet = SSets.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 + assertOutcomes(secondRunJvmMain, SSets.commonTest) + assertOutcomes(secondRunJvmMain, SSets.nativeMain) + assertOutcomes(secondRunJvmMain, SSets.nativeTest) + assertOutcomes(secondRunJvmMain, SSets.jsTest) + assertOutcomes(secondRunJvmMain, SSets.jsMain) + assertOutcomes(secondRunJvmMain, SSets.jsTest) + assertOutcomes(secondRunJvmMain, SSets.appleMain) + assertOutcomes(secondRunJvmMain, SSets.appleTest) + assertOutcomes(secondRunJvmMain, SSets.macosMain) + assertOutcomes(secondRunJvmMain, SSets.macosTest) + assertOutcomes(secondRunJvmMain, SSets.macosArm64Main) + assertOutcomes(secondRunJvmMain, SSets.macosArm64Test) + } + + private fun GrpcTestEnv.runKmpAndCheckFiles( sourceSet: SSets, vararg imports: SSets, clean: Boolean = true, ) { - runGradle(bufGenerate(sourceSet)) - .assertKmpSourceSet(sourceSet, *imports) + runKmp(sourceSet).assertKmpSourceSet(sourceSet, *imports) if (clean) { cleanProtoBuildDir() } } + + private fun GrpcTestEnv.runKmp(sourceSet: SSets): BuildResult { + return runGradle(bufGenerate(sourceSet)) + } + + private fun GrpcTestEnv.assertOutcomes( + result: BuildResult, + sourceSet: SSets, + generate: TaskOutcome? = null, + bufYaml: TaskOutcome? = null, + bufGenYaml: TaskOutcome? = null, + protoFiles: TaskOutcome? = null, + protoFilesImports: TaskOutcome? = null, + ) { + assertEquals(generate, result.protoTaskOutcomeOrNull(bufGenerate(sourceSet))) + assertEquals(bufYaml, result.protoTaskOutcomeOrNull(generateBufYaml(sourceSet))) + assertEquals(bufGenYaml, result.protoTaskOutcomeOrNull(generateBufGenYaml(sourceSet))) + assertEquals(protoFiles, result.protoTaskOutcomeOrNull(processProtoFiles(sourceSet))) + assertEquals(protoFilesImports, result.protoTaskOutcomeOrNull(processProtoFilesImports(sourceSet))) + } } 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 fc2893d85..2483aba44 100644 --- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt +++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt @@ -29,6 +29,10 @@ abstract class GrpcBaseTest : BaseTest() { ?: fail("Task ':$name' was not present in the build result") } + fun BuildResult.protoTaskOutcomeOrNull(name: String): TaskOutcome? { + return tasks.find { it.path == ":$name" }?.outcome + } + fun BuildResult.assertProtoTaskNotExecuted(name: String) { val task = tasks.find { it.path == ":$name" } if (task != null) { 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 new file mode 100644 index 000000000..20bdf1041 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * 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") +} + +kotlin { + jvm() + js { + nodejs() + } + macosArm64() +} + +rpc { + protoc() +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleMain/proto/appleMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleMain/proto/appleMain.proto new file mode 100644 index 000000000..dd73f50fb --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleMain/proto/appleMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AppleMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleTest/proto/appleTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleTest/proto/appleTest.proto new file mode 100644 index 000000000..b3c6b034f --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/appleTest/proto/appleTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message AppleTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/commonMain/proto/commonMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/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/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/src/commonTest/proto/commonTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/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/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/src/jsMain/proto/jsMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsMain/proto/jsMain.proto new file mode 100644 index 000000000..94cedbed5 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsMain/proto/jsMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JsMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsTest/proto/jsTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsTest/proto/jsTest.proto new file mode 100644 index 000000000..9ea0eb27a --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jsTest/proto/jsTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message JsTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/jvmMain/proto/jvmMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/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/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/src/jvmTest/proto/jvmTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/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/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/src/macosArm64Main/proto/macosArm64Main.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Main/proto/macosArm64Main.proto new file mode 100644 index 000000000..41c097c16 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Main/proto/macosArm64Main.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosArm64Main { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Test/proto/macosArm64Test.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Test/proto/macosArm64Test.proto new file mode 100644 index 000000000..a33f00ee7 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosArm64Test/proto/macosArm64Test.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosArm64Test { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosMain/proto/macosMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosMain/proto/macosMain.proto new file mode 100644 index 000000000..c65489499 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosMain/proto/macosMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosTest/proto/macosTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosTest/proto/macosTest.proto new file mode 100644 index 000000000..b65cd541c --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/macosTest/proto/macosTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message MacosTest { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeMain/proto/nativeMain.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeMain/proto/nativeMain.proto new file mode 100644 index 000000000..bbd13541d --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeMain/proto/nativeMain.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message NativeMain { + string content = 1; +} diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeTest/proto/nativeTest.proto b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeTest/proto/nativeTest.proto new file mode 100644 index 000000000..df96a67d0 --- /dev/null +++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/proto_tasks_are_cached_properly/src/nativeTest/proto/nativeTest.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message NativeTest { + string content = 1; +}