Skip to content

Commit 342fe7a

Browse files
committed
KRPC-238: Address review feedback — KMP DSL extensions, Android flavor codegen tests
1 parent fd431a3 commit 342fe7a

11 files changed

Lines changed: 231 additions & 0 deletions

File tree

gradle-plugin/api/gradle-plugin.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ public final class kotlinx/rpc/protoc/ProtoSourceSetKt {
194194
public static final fun getProto (Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;)Lkotlinx/rpc/protoc/ProtoSourceSet;
195195
public static final fun proto (Lorg/gradle/api/NamedDomainObjectProvider;Lorg/gradle/api/Action;)V
196196
public static final fun proto (Lorg/gradle/api/tasks/SourceSet;Lorg/gradle/api/Action;)V
197+
public static final fun proto (Lorg/jetbrains/kotlin/gradle/plugin/KotlinDependencyHandler;Ljava/lang/Object;)Lorg/gradle/api/artifacts/Dependency;
197198
public static final fun proto (Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;Lorg/gradle/api/Action;)V
199+
public static final fun protoImport (Lorg/jetbrains/kotlin/gradle/plugin/KotlinDependencyHandler;Ljava/lang/Object;)Lorg/gradle/api/artifacts/Dependency;
198200
public static final fun proto_kotlin (Lorg/gradle/api/NamedDomainObjectProvider;)Lorg/gradle/api/provider/Provider;
199201
public static final fun proto_kotlin (Lorg/gradle/api/NamedDomainObjectProvider;Lorg/gradle/api/Action;)V
200202
}

gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
package kotlinx.rpc.protoc
66

77
import org.gradle.api.Action
8+
import org.gradle.api.Named
89
import org.gradle.api.NamedDomainObjectContainer
910
import org.gradle.api.NamedDomainObjectProvider
11+
import org.gradle.api.artifacts.Dependency
1012
import org.gradle.api.file.ConfigurableFileCollection
1113
import org.gradle.api.file.SourceDirectorySet
1214
import org.gradle.api.provider.Property
1315
import org.gradle.api.provider.Provider
1416
import org.gradle.api.provider.SetProperty
1517
import org.gradle.api.tasks.SourceSet
18+
import org.jetbrains.kotlin.gradle.plugin.HasProject
19+
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
1620
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
1721

1822
public typealias ProtoSourceSets = NamedDomainObjectContainer<ProtoSourceSet>
@@ -344,3 +348,65 @@ public fun NamedDomainObjectProvider<SourceSet>.proto(action: Action<ProtoSource
344348
proto(action)
345349
}
346350
}
351+
352+
/**
353+
* Adds a proto dependency for code generation from within [KotlinSourceSet.dependencies].
354+
*
355+
* Proto files (`.proto`) from the resolved archives will be included in code generation.
356+
*
357+
* Example:
358+
* ```kotlin
359+
* kotlin.sourceSets {
360+
* commonMain {
361+
* dependencies {
362+
* proto("com.example:shared-protos:1.0")
363+
* }
364+
* }
365+
* }
366+
* ```
367+
*/
368+
public fun KotlinDependencyHandler.proto(dependencyNotation: Any): Dependency? {
369+
val configName = resolveProtoConfigurationName(this, "Proto")
370+
return project.dependencies.add(configName, dependencyNotation)
371+
}
372+
373+
/**
374+
* Adds a proto import dependency from within [KotlinSourceSet.dependencies].
375+
*
376+
* Proto files (`.proto`) from the resolved archives will be available as imports only,
377+
* but will not be used for code generation.
378+
*
379+
* Example:
380+
* ```kotlin
381+
* kotlin.sourceSets {
382+
* commonMain {
383+
* dependencies {
384+
* protoImport("com.example:common-protos:1.0")
385+
* }
386+
* }
387+
* }
388+
* ```
389+
*/
390+
public fun KotlinDependencyHandler.protoImport(dependencyNotation: Any): Dependency? {
391+
val configName = resolveProtoConfigurationName(this, "ProtoImport")
392+
return project.dependencies.add(configName, dependencyNotation)
393+
}
394+
395+
private fun resolveProtoConfigurationName(handler: KotlinDependencyHandler, suffix: String): String {
396+
val sourceSetName = try {
397+
val getParent = handler::class.java.getMethod("getParent")
398+
val parent = getParent.invoke(handler)
399+
(parent as? Named)?.name
400+
} catch (_: Exception) {
401+
null
402+
} ?: error(
403+
"Cannot determine source set name for proto dependency. " +
404+
"Use top-level dependencies { <sourceSetName>${suffix}(\"...\") } syntax instead."
405+
)
406+
407+
return if (sourceSetName == "main") {
408+
suffix.replaceFirstChar { it.lowercase() }
409+
} else {
410+
"$sourceSetName$suffix"
411+
}
412+
}

gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcAndroidProjectTest.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,40 @@ class GrpcAndroidProjectTest : GrpcBaseTest() {
262262
runGradle("test_tasks", "--no-configuration-cache")
263263
}
264264

265+
@TestFactory
266+
fun `Dependency Proto Import Flavors Codegen`() = runGrpcTest(versionsPredicate = { !isAgp9 }) {
267+
// Verify proto imports work across all flavor variants
268+
// protoImport declared on main → inherited by all variants
269+
270+
fun runForVariant(sourceSet: SSets, vararg extraTasks: SSets) {
271+
val result = runForSet(sourceSet)
272+
273+
result.assertTaskExecuted(
274+
sourceSet = sourceSet,
275+
protoFiles = listOf(
276+
Path("some.proto"),
277+
),
278+
importProtoFiles = listOf(
279+
Path("dependency.proto"),
280+
),
281+
generatedFiles = listOf(
282+
Path("Some.kt"),
283+
Path("Some.ext.kt"),
284+
Path(RPC_INTERNAL, "Some.kt"),
285+
),
286+
notExecuted = emptyList(),
287+
)
288+
}
289+
290+
// arm variants
291+
runForVariant(SSetsAndroid.Flavors.armDebug)
292+
runForVariant(SSetsAndroid.Flavors.armRelease)
293+
294+
// x86 variants
295+
runForVariant(SSetsAndroid.Flavors.x86Debug)
296+
runForVariant(SSetsAndroid.Flavors.x86Release)
297+
}
298+
265299
@TestFactory
266300
fun `No gRPC`() = runGrpcTest(versionsPredicate = { !isAgp9 }) {
267301
SSetsAndroid.Default.entries.forEach {

gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ class GrpcKmpProjectTest : GrpcBaseTest() {
3232
)
3333
}
3434

35+
@TestFactory
36+
fun `Dependency Proto Import KMP DSL`() = runGrpcTest {
37+
val result = runGradle(bufGenerateCommonMain)
38+
39+
result.assertOutcome(TaskOutcome.SUCCESS, bufGenerateCommonMain)
40+
result.assertOutcome(TaskOutcome.SUCCESS, processCommonMainProtoFiles)
41+
42+
assertWorkspaceProtoFilesCopied(mainSourceSet, Path("some.proto"))
43+
assertWorkspaceImportProtoFilesCopied(mainSourceSet, Path("dependency.proto"))
44+
45+
assertSourceCodeGenerated(
46+
mainSourceSet,
47+
Path("Some.kt"),
48+
Path(RPC_INTERNAL, "Some.kt"),
49+
)
50+
51+
dryRunCompilation(SSetsKmp.Default.jvmMain)
52+
}
53+
3554
@TestFactory
3655
fun `Dependency Proto Import`() = runGrpcTest {
3756
val result = runGradle(bufGenerateCommonMain)

gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,37 @@ inputs:
639639
return entries
640640
}
641641
}
642+
643+
enum class Flavors(
644+
override val mode: PluginMode? = null,
645+
override val minKotlin: KotlinVersion = KtVersion.v2_0_0,
646+
) : SSetsAndroid {
647+
// non-executable root source sets
648+
main, test, androidTest,
649+
650+
// per-flavor non-executable
651+
arm, x86,
652+
653+
// build-type non-executable
654+
debug, release,
655+
656+
// executable leaf variants
657+
armDebug(plm.a), armRelease(plm.a),
658+
x86Debug(plm.a), x86Release(plm.a),
659+
660+
// test variants
661+
testArmDebug(plm.a), testArmRelease(plm.a),
662+
testX86Debug(plm.a), testX86Release(plm.a),
663+
664+
// androidTest variants
665+
androidTestArmDebug(plm.a),
666+
androidTestX86Debug(plm.a),
667+
;
668+
669+
override fun all(): List<SSets> {
670+
return entries
671+
}
672+
}
642673
}
643674

644675
enum class PluginMode {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
import org.gradle.kotlin.dsl.version
6+
7+
plugins {
8+
id("com.android.application") version "<android-version>"
9+
kotlin("android") version "<kotlin-version>"
10+
id("org.jetbrains.kotlinx.rpc.plugin") version "<rpc-version>"
11+
}
12+
13+
rpc {
14+
protoc()
15+
}
16+
17+
android {
18+
namespace = "com.example.myapp"
19+
compileSdk = 34
20+
21+
flavorDimensions("abi")
22+
23+
productFlavors {
24+
create("arm") {
25+
dimension = "abi"
26+
}
27+
create("x86") {
28+
dimension = "abi"
29+
}
30+
}
31+
}
32+
33+
dependencies {
34+
// protoImport on main — inherited by all variants (debug, release, arm*, x86*)
35+
protoImport(files("dependency-protos.zip"))
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
syntax = "proto3";
2+
3+
import "dependency.proto";
4+
5+
message Some {
6+
string content = 1;
7+
dependency.DependencyMessage dep = 2;
8+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
import org.gradle.kotlin.dsl.version
6+
import kotlinx.rpc.protoc.*
7+
8+
plugins {
9+
kotlin("multiplatform") version "<kotlin-version>"
10+
id("org.jetbrains.kotlinx.rpc.plugin") version "<rpc-version>"
11+
}
12+
13+
kotlin {
14+
jvm()
15+
16+
sourceSets {
17+
commonMain {
18+
dependencies {
19+
protoImport(files("dependency-protos.zip"))
20+
}
21+
}
22+
}
23+
}
24+
25+
rpc {
26+
protoc()
27+
}

0 commit comments

Comments
 (0)