diff --git a/.idea/kotlinx-rpc.iml b/.idea/kotlinx-rpc.iml
index 41ef6c5e5..5a1d82917 100644
--- a/.idea/kotlinx-rpc.iml
+++ b/.idea/kotlinx-rpc.iml
@@ -8,6 +8,13 @@
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/grpc/grpcpp-c/.bazelrc b/cinterop-c/.bazelrc
similarity index 100%
rename from grpc/grpcpp-c/.bazelrc
rename to cinterop-c/.bazelrc
diff --git a/grpc/grpcpp-c/.gitignore b/cinterop-c/.gitignore
similarity index 100%
rename from grpc/grpcpp-c/.gitignore
rename to cinterop-c/.gitignore
diff --git a/grpc/grpcpp-c/BUILD.bazel b/cinterop-c/BUILD.bazel
similarity index 100%
rename from grpc/grpcpp-c/BUILD.bazel
rename to cinterop-c/BUILD.bazel
diff --git a/grpc/grpcpp-c/MODULE.bazel b/cinterop-c/MODULE.bazel
similarity index 100%
rename from grpc/grpcpp-c/MODULE.bazel
rename to cinterop-c/MODULE.bazel
diff --git a/grpc/grpcpp-c/MODULE.bazel.lock b/cinterop-c/MODULE.bazel.lock
similarity index 100%
rename from grpc/grpcpp-c/MODULE.bazel.lock
rename to cinterop-c/MODULE.bazel.lock
diff --git a/grpc/grpcpp-c/include/grpcpp_c.h b/cinterop-c/include/grpcpp_c.h
similarity index 100%
rename from grpc/grpcpp-c/include/grpcpp_c.h
rename to cinterop-c/include/grpcpp_c.h
diff --git a/grpc/grpcpp-c/include/protowire.h b/cinterop-c/include/protowire.h
similarity index 100%
rename from grpc/grpcpp-c/include/protowire.h
rename to cinterop-c/include/protowire.h
diff --git a/grpc/grpcpp-c/src/grpcpp_c.cpp b/cinterop-c/src/grpcpp_c.cpp
similarity index 100%
rename from grpc/grpcpp-c/src/grpcpp_c.cpp
rename to cinterop-c/src/grpcpp_c.cpp
diff --git a/grpc/grpcpp-c/src/protowire.cpp b/cinterop-c/src/protowire.cpp
similarity index 100%
rename from grpc/grpcpp-c/src/protowire.cpp
rename to cinterop-c/src/protowire.cpp
diff --git a/gradle-conventions/src/main/kotlin/conventions-protoc-gen.gradle.kts b/gradle-conventions/src/main/kotlin/conventions-protoc-gen.gradle.kts
new file mode 100644
index 000000000..8c228ee27
--- /dev/null
+++ b/gradle-conventions/src/main/kotlin/conventions-protoc-gen.gradle.kts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import util.other.libs
+import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
+
+plugins {
+ id("conventions-jvm")
+}
+
+dependencies {
+ implementation(libs.protobuf.java)
+
+ implementation(libs.slf4j.api)
+ implementation(libs.logback.classic)
+
+ testImplementation(libs.kotlin.test)
+}
+
+tasks.jar {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ archiveClassifier = "all"
+
+ // Protoc plugins are all fat jars basically (the ones built on jvm)
+ // be really careful of what you put in the classpath here
+ from(
+ configurations.runtimeClasspath.map { prop ->
+ prop.map { if (it.isDirectory()) it else zipTree(it) }
+ }
+ )
+}
+
+kotlin {
+ explicitApi = ExplicitApiMode.Disabled
+}
+
+tasks.test {
+ useJUnitPlatform()
+}
diff --git a/gradle-conventions/src/main/kotlin/conventions-publishing.gradle.kts b/gradle-conventions/src/main/kotlin/conventions-publishing.gradle.kts
index 6f3d3b95f..26dcef37b 100644
--- a/gradle-conventions/src/main/kotlin/conventions-publishing.gradle.kts
+++ b/gradle-conventions/src/main/kotlin/conventions-publishing.gradle.kts
@@ -10,7 +10,7 @@ import util.other.isPublicModule
import util.tasks.ValidatePublishedArtifactsTask
val isGradlePlugin = project.name == "gradle-plugin"
-val isProtocGen = project.name == "protoc-gen"
+val isProtocGen = project.rootProject.name.startsWith(PROTOC_GEN)
val publishingExtension = project.extensions.findByType()
val globalRootDir: String by extra
diff --git a/gradle-conventions/src/main/kotlin/util/cinterop.kt b/gradle-conventions/src/main/kotlin/util/cinterop.kt
new file mode 100644
index 000000000..6a28b9f6c
--- /dev/null
+++ b/gradle-conventions/src/main/kotlin/util/cinterop.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package util
+
+import org.gradle.api.GradleException
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.Project
+import org.gradle.api.tasks.Exec
+import org.gradle.internal.extensions.stdlib.capitalized
+import org.gradle.kotlin.dsl.extra
+import org.gradle.kotlin.dsl.named
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.register
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
+import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
+import java.io.File
+import kotlin.io.resolve
+
+// works with the cinterop-c Bazel project
+fun KotlinMultiplatformExtension.configureCLibCInterop(
+ project: Project,
+ bazelTask: String,
+ configureCinterop: NamedDomainObjectContainer.(cinteropCLib: File) -> Unit,
+) {
+ val globalRootDir: String by project.extra
+
+ val cinteropCLib = project.layout.projectDirectory.dir("$globalRootDir/cinterop-c").asFile
+
+ // TODO: Replace function implementation, so it does not use an internal API
+ fun findProgram(name: String) = org.gradle.internal.os.OperatingSystem.current().findInPath(name)
+ val checkBazel = project.tasks.register("checkBazel") {
+ doLast {
+ val bazelPath = findProgram("bazel")
+ if (bazelPath != null) {
+ logger.debug("bazel: {}", bazelPath)
+ } else {
+ throw GradleException("'bazel' not found on PATH. Please install Bazel (https://bazel.build/).")
+ }
+ }
+ }
+
+ val buildCinteropCLib = project.tasks.register("buildCinteropCLib") {
+ group = "build"
+ workingDir = cinteropCLib
+ commandLine("bash", "-c", "bazel build $bazelTask --config=release")
+ inputs.files(project.fileTree(cinteropCLib) { exclude("bazel-*/**") })
+ outputs.dir(cinteropCLib.resolve("bazel-bin"))
+
+ dependsOn(checkBazel)
+ }
+
+ targets.filterIsInstance().forEach {
+ it.compilations.getByName("main") {
+ cinterops {
+ configureCinterop(cinteropCLib)
+ }
+
+ cinterops.all {
+ val interop = this
+
+ val interopTask = "cinterop${interop.name.capitalized()}${it.targetName.capitalized()}"
+ project.tasks.named(interopTask, CInteropProcess::class) {
+ dependsOn(buildCinteropCLib)
+ }
+ }
+ }
+ }
+}
diff --git a/gradle-conventions/src/main/kotlin/util/publication.kt b/gradle-conventions/src/main/kotlin/util/publication.kt
index ea8cc7e02..e64d4d0b3 100644
--- a/gradle-conventions/src/main/kotlin/util/publication.kt
+++ b/gradle-conventions/src/main/kotlin/util/publication.kt
@@ -18,7 +18,7 @@ import java.io.File
const val KOTLINX_RPC_PREFIX = "kotlinx-rpc"
const val PROTOC_GEN = "protoc-gen"
-const val PROTOC_GEN_KOTLIN_MULTIPLATFORM = "protoc-gen-kotlin-multiplatform"
+const val PROTOC_GEN_KOTLIN_MULTIPLATFORM = "kotlin-multiplatform"
/**
* Important to configure inside [KotlinTarget.mavenPublication]
@@ -28,8 +28,13 @@ const val PROTOC_GEN_KOTLIN_MULTIPLATFORM = "protoc-gen-kotlin-multiplatform"
fun MavenPublication.setPublicArtifactId(project: Project) {
val publication = this
- if (publication.artifactId == PROTOC_GEN) {
- publication.artifactId = PROTOC_GEN_KOTLIN_MULTIPLATFORM
+ if (project.rootProject.name == PROTOC_GEN) {
+ val middle = when (project.name) {
+ "grpc" -> "grpc-"
+ "protobuf" -> ""
+ else -> error("Unsupported project name in $PROTOC_GEN: ${project.name}")
+ }
+ publication.artifactId = "$PROTOC_GEN-$middle$PROTOC_GEN_KOTLIN_MULTIPLATFORM"
project.logger.info("Altered artifactId for $name publication: $artifactId")
} else if (!publication.artifactId.startsWith(KOTLINX_RPC_PREFIX)) {
publication.artifactId = "$KOTLINX_RPC_PREFIX-$artifactId"
diff --git a/gradle-plugin/api/gradle-plugin.api b/gradle-plugin/api/gradle-plugin.api
index 28ced8ae7..87a07f8f1 100644
--- a/gradle-plugin/api/gradle-plugin.api
+++ b/gradle-plugin/api/gradle-plugin.api
@@ -4,10 +4,10 @@ public abstract interface annotation class kotlinx/rpc/RpcDangerousApi : java/la
public class kotlinx/rpc/RpcExtension {
public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/Project;)V
public final fun getAnnotationTypeSafetyEnabled ()Lorg/gradle/api/provider/Provider;
- public final fun getGrpc ()Lkotlinx/rpc/grpc/GrpcExtension;
+ public final fun getProtoc ()Lkotlinx/rpc/protoc/ProtocExtension;
public final fun getStrict ()Lkotlinx/rpc/RpcStrictModeExtension;
- public final fun grpc (Lorg/gradle/api/Action;)V
- public static synthetic fun grpc$default (Lkotlinx/rpc/RpcExtension;Lorg/gradle/api/Action;ILjava/lang/Object;)V
+ public final fun protoc (Lorg/gradle/api/Action;)V
+ public static synthetic fun protoc$default (Lkotlinx/rpc/RpcExtension;Lorg/gradle/api/Action;ILjava/lang/Object;)V
public final fun strict (Lorg/gradle/api/Action;)V
}
@@ -21,6 +21,7 @@ public final class kotlinx/rpc/RpcStrictMode : java/lang/Enum {
public static final field ERROR Lkotlinx/rpc/RpcStrictMode;
public static final field NONE Lkotlinx/rpc/RpcStrictMode;
public static final field WARNING Lkotlinx/rpc/RpcStrictMode;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lkotlinx/rpc/RpcStrictMode;
public static fun values ()[Lkotlinx/rpc/RpcStrictMode;
}
@@ -61,6 +62,7 @@ public final class kotlinx/rpc/buf/BufExtension$LogFormat : java/lang/Enum {
public static final field Default Lkotlinx/rpc/buf/BufExtension$LogFormat;
public static final field Json Lkotlinx/rpc/buf/BufExtension$LogFormat;
public static final field Text Lkotlinx/rpc/buf/BufExtension$LogFormat;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lkotlinx/rpc/buf/BufExtension$LogFormat;
public static fun values ()[Lkotlinx/rpc/buf/BufExtension$LogFormat;
}
@@ -79,6 +81,7 @@ public final class kotlinx/rpc/buf/BufGenerateExtension$ErrorFormat : java/lang/
public static final field Junit Lkotlinx/rpc/buf/BufGenerateExtension$ErrorFormat;
public static final field Msvs Lkotlinx/rpc/buf/BufGenerateExtension$ErrorFormat;
public static final field Text Lkotlinx/rpc/buf/BufGenerateExtension$ErrorFormat;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lkotlinx/rpc/buf/BufGenerateExtension$ErrorFormat;
public static fun values ()[Lkotlinx/rpc/buf/BufGenerateExtension$ErrorFormat;
}
@@ -119,6 +122,7 @@ public abstract class kotlinx/rpc/buf/tasks/BufGenerateTask : kotlinx/rpc/buf/ta
public fun ()V
public abstract fun getAdditionalArgs ()Lorg/gradle/api/provider/ListProperty;
public abstract fun getErrorFormat ()Lorg/gradle/api/provider/Property;
+ public abstract fun getExecutableFiles ()Lorg/gradle/api/provider/ListProperty;
public abstract fun getIncludeImports ()Lorg/gradle/api/provider/Property;
public abstract fun getIncludeWkt ()Lorg/gradle/api/provider/Property;
public abstract fun getOutputDirectory ()Lorg/gradle/api/provider/Property;
@@ -146,14 +150,8 @@ public final class kotlinx/rpc/buf/tasks/GenerateBufYamlKt$inlined$sam$i$org_gra
public final synthetic fun execute (Ljava/lang/Object;)V
}
-public abstract interface class kotlinx/rpc/grpc/GrpcExtension {
- public abstract fun buf (Lorg/gradle/api/Action;)V
- public abstract fun getBuf ()Lkotlinx/rpc/buf/BufExtension;
- public abstract fun getProtocPlugins ()Lorg/gradle/api/NamedDomainObjectContainer;
- public abstract fun protocPlugins (Lorg/gradle/api/Action;)V
-}
-
-public final class kotlinx/rpc/proto/ConstsKt {
+public final class kotlinx/rpc/protoc/ConstsKt {
+ public static final field PROTOC_GEN_GRPC_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION Ljava/lang/String;
public static final field PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION Ljava/lang/String;
public static final field PROTO_BUILD_DIR Ljava/lang/String;
public static final field PROTO_BUILD_GENERATED Ljava/lang/String;
@@ -165,33 +163,39 @@ public final class kotlinx/rpc/proto/ConstsKt {
public static final field PROTO_SOURCE_SETS Ljava/lang/String;
}
-public final class kotlinx/rpc/proto/KotlinMultiplatformPluginJarKt {
+public final class kotlinx/rpc/protoc/PluginJarsKt {
+ public static final fun getGrpcKotlinMultiplatformProtocPluginJarPath (Lorg/gradle/api/Project;)Lorg/gradle/api/provider/Provider;
public static final fun getKotlinMultiplatformProtocPluginJarPath (Lorg/gradle/api/Project;)Lorg/gradle/api/provider/Provider;
}
-public abstract class kotlinx/rpc/proto/ProcessProtoFiles : org/gradle/api/tasks/Copy {
+public abstract class kotlinx/rpc/protoc/ProcessProtoFiles : org/gradle/api/tasks/Copy {
public fun ()V
}
-public final class kotlinx/rpc/proto/ProcessProtoFilesKt$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action {
+public final class kotlinx/rpc/protoc/ProcessProtoFilesKt$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action {
public fun (Lkotlin/jvm/functions/Function1;)V
public final synthetic fun execute (Ljava/lang/Object;)V
}
-public abstract interface class kotlinx/rpc/proto/ProtoSourceSet {
+public abstract interface class kotlinx/rpc/protoc/ProtoSourceSet {
public abstract fun getName ()Ljava/lang/String;
public abstract fun getProto ()Lorg/gradle/api/file/SourceDirectorySet;
public fun proto (Lorg/gradle/api/Action;)V
- public abstract fun protocPlugin (Lkotlinx/rpc/proto/ProtocPlugin;)V
+ public abstract fun protocPlugin (Lkotlinx/rpc/protoc/ProtocPlugin;)V
public abstract fun protocPlugin (Lorg/gradle/api/NamedDomainObjectProvider;)V
}
-public class kotlinx/rpc/proto/ProtocPlugin {
- public static final field Companion Lkotlinx/rpc/proto/ProtocPlugin$Companion;
- public static final field GRPC_JAVA Ljava/lang/String;
- public static final field GRPC_KOTLIN Ljava/lang/String;
+public abstract interface class kotlinx/rpc/protoc/ProtocExtension {
+ public abstract fun buf (Lorg/gradle/api/Action;)V
+ public abstract fun getBuf ()Lkotlinx/rpc/buf/BufExtension;
+ public abstract fun getPlugins ()Lorg/gradle/api/NamedDomainObjectContainer;
+ public abstract fun plugins (Lorg/gradle/api/Action;)V
+}
+
+public class kotlinx/rpc/protoc/ProtocPlugin {
+ public static final field Companion Lkotlinx/rpc/protoc/ProtocPlugin$Companion;
+ public static final field GRPC_KOTLIN_MULTIPLATFORM Ljava/lang/String;
public static final field KOTLIN_MULTIPLATFORM Ljava/lang/String;
- public static final field PROTOBUF_JAVA Ljava/lang/String;
public fun (Ljava/lang/String;Lorg/gradle/api/Project;)V
public final fun getArtifact ()Lorg/gradle/api/provider/Property;
public final fun getExcludeTypes ()Lorg/gradle/api/provider/ListProperty;
@@ -206,42 +210,40 @@ public class kotlinx/rpc/proto/ProtocPlugin {
public final fun remote (Lorg/gradle/api/Action;)V
}
-public abstract class kotlinx/rpc/proto/ProtocPlugin$Artifact {
+public abstract class kotlinx/rpc/protoc/ProtocPlugin$Artifact {
}
-public final class kotlinx/rpc/proto/ProtocPlugin$Artifact$Local : kotlinx/rpc/proto/ProtocPlugin$Artifact {
+public final class kotlinx/rpc/protoc/ProtocPlugin$Artifact$Local : kotlinx/rpc/protoc/ProtocPlugin$Artifact {
public fun (Lorg/gradle/api/Project;)V
public final fun executor (Lorg/gradle/api/provider/Provider;)V
public final fun executor ([Ljava/lang/String;)V
+ public final fun getExecutableFiles ()Lorg/gradle/api/provider/ListProperty;
public final fun getExecutor ()Lorg/gradle/api/provider/ListProperty;
public final fun javaJar (Ljava/lang/String;)V
public final fun javaJar (Lorg/gradle/api/provider/Provider;Lorg/gradle/api/provider/Provider;)V
- public static synthetic fun javaJar$default (Lkotlinx/rpc/proto/ProtocPlugin$Artifact$Local;Lorg/gradle/api/provider/Provider;Lorg/gradle/api/provider/Provider;ILjava/lang/Object;)V
+ public static synthetic fun javaJar$default (Lkotlinx/rpc/protoc/ProtocPlugin$Artifact$Local;Lorg/gradle/api/provider/Provider;Lorg/gradle/api/provider/Provider;ILjava/lang/Object;)V
}
-public final class kotlinx/rpc/proto/ProtocPlugin$Artifact$Remote : kotlinx/rpc/proto/ProtocPlugin$Artifact {
+public final class kotlinx/rpc/protoc/ProtocPlugin$Artifact$Remote : kotlinx/rpc/protoc/ProtocPlugin$Artifact {
public fun (Lorg/gradle/api/Project;)V
public final fun getLocator ()Lorg/gradle/api/provider/Property;
}
-public final class kotlinx/rpc/proto/ProtocPlugin$Companion {
+public final class kotlinx/rpc/protoc/ProtocPlugin$Companion {
}
-public final class kotlinx/rpc/proto/ProtocPlugin$Strategy : java/lang/Enum {
- public static final field All Lkotlinx/rpc/proto/ProtocPlugin$Strategy;
- public static final field Directory Lkotlinx/rpc/proto/ProtocPlugin$Strategy;
- public static fun valueOf (Ljava/lang/String;)Lkotlinx/rpc/proto/ProtocPlugin$Strategy;
- public static fun values ()[Lkotlinx/rpc/proto/ProtocPlugin$Strategy;
+public final class kotlinx/rpc/protoc/ProtocPlugin$Strategy : java/lang/Enum {
+ public static final field All Lkotlinx/rpc/protoc/ProtocPlugin$Strategy;
+ public static final field Directory Lkotlinx/rpc/protoc/ProtocPlugin$Strategy;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public static fun valueOf (Ljava/lang/String;)Lkotlinx/rpc/protoc/ProtocPlugin$Strategy;
+ public static fun values ()[Lkotlinx/rpc/protoc/ProtocPlugin$Strategy;
}
-public final class kotlinx/rpc/proto/ProtocPluginKt {
- public static final fun getGrpcJava (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
- public static final fun getGrpcKotlin (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
+public final class kotlinx/rpc/protoc/ProtocPluginKt {
+ public static final fun getGrpcKotlinMultiplatform (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public static final fun getKotlinMultiplatform (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
- public static final fun getProtobufJava (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
- public static final fun grpcJava (Lorg/gradle/api/NamedDomainObjectContainer;Lorg/gradle/api/Action;)V
- public static final fun grpcKotlin (Lorg/gradle/api/NamedDomainObjectContainer;Lorg/gradle/api/Action;)V
+ public static final fun grpcKotlinMultiplatform (Lorg/gradle/api/NamedDomainObjectContainer;Lorg/gradle/api/Action;)V
public static final fun kotlinMultiplatform (Lorg/gradle/api/NamedDomainObjectContainer;Lorg/gradle/api/Action;)V
- public static final fun protobufJava (Lorg/gradle/api/NamedDomainObjectContainer;Lorg/gradle/api/Action;)V
}
diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts
index 6f384d816..8b7d974c7 100644
--- a/gradle-plugin/build.gradle.kts
+++ b/gradle-plugin/build.gradle.kts
@@ -54,7 +54,9 @@ tasks.test {
useJUnitPlatform()
- dependsOn(gradle.includedBuild("protoc-gen").task(":publishAllPublicationsToBuildRepoRepository"))
+ val includedBuild = gradle.includedBuild("protoc-gen")
+ dependsOn(includedBuild.task(":grpc:publishAllPublicationsToBuildRepoRepository"))
+ dependsOn(includedBuild.task(":protobuf:publishAllPublicationsToBuildRepoRepository"))
}
// This block is needed to show plugin tasks on --dry-run
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt
index d02d8d458..b16a2afe7 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt
@@ -6,8 +6,8 @@
package kotlinx.rpc
-import kotlinx.rpc.grpc.DefaultGrpcExtension
-import kotlinx.rpc.grpc.GrpcExtension
+import kotlinx.rpc.protoc.DefaultProtocExtension
+import kotlinx.rpc.protoc.ProtocExtension
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
@@ -45,21 +45,21 @@ public open class RpcExtension @Inject constructor(objects: ObjectFactory, priva
configure.execute(strict)
}
- internal val grpcApplied = AtomicBoolean(false)
+ internal val protocApplied = AtomicBoolean(false)
/**
- * Grpc settings.
+ * Protoc settings.
*/
- public val grpc: GrpcExtension by lazy {
- grpcApplied.set(true)
- objects.newInstance()
+ public val protoc: ProtocExtension by lazy {
+ protocApplied.set(true)
+ objects.newInstance()
}
/**
- * Grpc settings.
+ * Protoc settings.
*/
- public fun grpc(configure: Action = Action {}) {
- configure.execute(grpc)
+ public fun protoc(configure: Action = Action {}) {
+ configure.execute(protoc)
}
}
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt
index 66442901c..f9f8cfa6f 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt
@@ -4,8 +4,8 @@
package kotlinx.rpc
-import kotlinx.rpc.grpc.configurePluginProtections
-import kotlinx.rpc.proto.createProtoExtensions
+import kotlinx.rpc.protoc.configurePluginProtections
+import kotlinx.rpc.protoc.createProtoExtensions
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.create
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 a69e37b7e..28941c66d 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt
@@ -5,7 +5,7 @@
package kotlinx.rpc.buf
import kotlinx.rpc.buf.tasks.BufExecTask
-import kotlinx.rpc.proto.ProtocPlugin
+import kotlinx.rpc.protoc.ProtocPlugin
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
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 ce85238e8..57e393cf1 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt
@@ -7,7 +7,7 @@ package kotlinx.rpc.buf.tasks
import kotlinx.rpc.buf.BUF_EXECUTABLE_CONFIGURATION
import kotlinx.rpc.buf.BufExtension
import kotlinx.rpc.buf.execBuf
-import kotlinx.rpc.proto.PROTO_GROUP
+import kotlinx.rpc.protoc.PROTO_GROUP
import kotlinx.rpc.rpcExtension
import org.gradle.api.DefaultTask
import org.gradle.api.Project
@@ -117,7 +117,7 @@ internal fun Project.registerBufExecTask(
bufExecutable.set(executableConfiguration.singleFile)
this.workingDir.set(workingDir)
- val buf = project.rpcExtension().grpc.buf
+ val buf = project.rpcExtension().protoc.buf
configFile.set(buf.configFile)
logFormat.set(buf.logFormat)
bufTimeoutInWholeSeconds.set(buf.timeout.map { it.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 27f14f9ca..e92f21555 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
@@ -4,8 +4,9 @@
package kotlinx.rpc.buf.tasks
-import kotlinx.rpc.proto.PROTO_GROUP
+import kotlinx.rpc.protoc.PROTO_GROUP
import kotlinx.rpc.rpcExtension
+import kotlinx.rpc.protoc.ProtocPlugin
import org.gradle.api.Project
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
@@ -16,6 +17,7 @@ import org.gradle.api.tasks.TaskProvider
import java.io.File
import kotlinx.rpc.buf.BufGenerateExtension
import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.InputFiles
/**
* Buf `generate` command.
@@ -31,6 +33,15 @@ public abstract class BufGenerateTask : BufExecTask() {
@get:InputDirectory
internal abstract val importFilesDir: Property
+ /**
+ * List of files used during `buf generate` command execution.
+ *
+ * @see [ProtocPlugin.Artifact.Local.executableFiles]
+ */
+ // unsued, but required for Gradle to properly recognise inputs
+ @get:InputFiles
+ public abstract val executableFiles: ListProperty
+
/**
* Whether to include imports.
*
@@ -118,7 +129,7 @@ internal fun Project.registerBufGenerateTask(
group = PROTO_GROUP
description = "Generates code from .proto files using 'buf generate'"
- val generate = project.rpcExtension().grpc.buf.generate
+ val generate = project.rpcExtension().protoc.buf.generate
includeImports.set(generate.includeImports)
includeWkt.set(generate.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 42c033386..455d99867 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/GenerateBufGenYaml.kt
@@ -5,9 +5,9 @@
package kotlinx.rpc.buf.tasks
import kotlinx.rpc.buf.BUF_GEN_YAML
-import kotlinx.rpc.proto.PROTO_FILES_DIR
-import kotlinx.rpc.proto.PROTO_GROUP
-import kotlinx.rpc.proto.ProtocPlugin
+import kotlinx.rpc.protoc.PROTO_FILES_DIR
+import kotlinx.rpc.protoc.PROTO_GROUP
+import kotlinx.rpc.protoc.ProtocPlugin
import kotlinx.rpc.util.ensureRegularFileExists
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
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 75e6d2859..3b2d8b63e 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,7 +5,7 @@
package kotlinx.rpc.buf.tasks
import kotlinx.rpc.buf.BUF_YAML
-import kotlinx.rpc.proto.PROTO_GROUP
+import kotlinx.rpc.protoc.PROTO_GROUP
import kotlinx.rpc.util.ensureRegularFileExists
import org.gradle.api.DefaultTask
import org.gradle.api.Project
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/InternalRpcApi.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/InternalRpcApi.kt
new file mode 100644
index 000000000..a6aa1bc55
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/InternalRpcApi.kt
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.internal
+
+@RequiresOptIn("This API is internal and should not be used outside of kotlinx-rpc")
+public annotation class InternalRpcApi
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt
new file mode 100644
index 000000000..15ef97c3e
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/internal/configureLocalProtocGenDevelopmentDependency.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.internal
+
+import kotlinx.rpc.RpcExtension
+import kotlinx.rpc.buf.tasks.BufGenerateTask
+import kotlinx.rpc.protoc.grpcKotlinMultiplatform
+import kotlinx.rpc.protoc.kotlinMultiplatform
+import org.gradle.api.Project
+import org.gradle.internal.extensions.core.extra
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.the
+import org.gradle.kotlin.dsl.withType
+
+@InternalRpcApi
+public fun Project.configureLocalProtocGenDevelopmentDependency() {
+ val globalRootDir: String by extra
+
+ the().protoc.plugins {
+ kotlinMultiplatform {
+ local {
+ javaJar("$globalRootDir/protoc-gen/protobuf/build/libs/protobuf-$version-all.jar")
+ }
+ }
+
+ grpcKotlinMultiplatform {
+ local {
+ javaJar("$globalRootDir/protoc-gen/grpc/build/libs/grpc-$version-all.jar")
+ }
+ }
+ }
+
+ tasks.withType().configureEach {
+ if (name.endsWith("Test")) {
+ val includedBuild = gradle.includedBuild("protoc-gen")
+ dependsOn(includedBuild.task(":grpc:jar"))
+ dependsOn(includedBuild.task(":protobuf:jar"))
+ }
+ }
+}
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/kotlinMultiplatformPluginJar.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/kotlinMultiplatformPluginJar.kt
deleted file mode 100644
index a1dcf08ac..000000000
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/kotlinMultiplatformPluginJar.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.proto
-
-import kotlinx.rpc.LIBRARY_VERSION
-import org.gradle.api.Project
-import org.gradle.api.provider.Provider
-
-/**
- * Absolute path to the `protoc-gen-kotlin-multiplatform` jar.
- *
- * Can be used to customise the java executable path:
- * ```kotlin
- * rpc.grpc.protocPlugins.kotlinMultiplatform {
- * local {
- * javaJar(kotlinMultiplatformProtocPluginJarPath, provider { "my-path-to-java" })
- * }
- * }
- * ```
- */
-public val Project.kotlinMultiplatformProtocPluginJarPath: Provider
- get() = project.configurations.named(PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION)
- .map { it.singleFile.absolutePath }
-
-internal fun Project.configureKotlinMultiplatformPluginJarConfiguration() {
- configurations.create(PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION)
-
- dependencies.add(
- PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION,
- mapOf(
- "group" to "org.jetbrains.kotlinx",
- "name" to "protoc-gen-kotlin-multiplatform",
- "version" to LIBRARY_VERSION,
- "classifier" to "all",
- "ext" to "jar",
- ),
- )
-}
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt
similarity index 99%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/DefaultProtoSourceSet.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt
index 6bc7d7858..35107ec4b 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/DefaultProtoSourceSet.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.proto
+package kotlinx.rpc.protoc
import kotlinx.rpc.buf.tasks.BufGenerateTask
import kotlinx.rpc.util.findOrCreate
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/DefaultGrpcExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt
similarity index 81%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/DefaultGrpcExtension.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt
index c24effdd5..5ec5800e2 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/DefaultGrpcExtension.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc
+package kotlinx.rpc.protoc
import kotlinx.rpc.buf.BufExtension
import kotlinx.rpc.buf.configureBufExecutable
@@ -10,8 +10,8 @@ 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.proto.*
-import kotlinx.rpc.proto.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM
+import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM
+import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM
import kotlinx.rpc.util.ensureDirectoryExists
import org.gradle.api.Action
import org.gradle.api.GradleException
@@ -27,20 +27,20 @@ import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import javax.inject.Inject
-internal open class DefaultGrpcExtension @Inject constructor(
+internal open class DefaultProtocExtension @Inject constructor(
objects: ObjectFactory,
private val project: Project,
-) : GrpcExtension {
- override val protocPlugins: NamedDomainObjectContainer =
+) : ProtocExtension {
+ override val plugins: NamedDomainObjectContainer =
objects.domainObjectContainer(ProtocPlugin::class.java) { name ->
ProtocPlugin(name, project)
}
- override fun protocPlugins(action: Action>) {
- action.execute(protocPlugins)
+ override fun plugins(action: Action>) {
+ action.execute(plugins)
}
override val buf: BufExtension = project.objects.newInstance()
@@ -51,11 +51,13 @@ internal open class DefaultGrpcExtension @Inject constructor(
init {
project.configureBufExecutable()
project.configureKotlinMultiplatformPluginJarConfiguration()
+ project.configureGrpcKotlinMultiplatformPluginJarConfiguration()
createDefaultProtocPlugins()
project.protoSourceSets.forEach { protoSourceSet ->
- protoSourceSet.protocPlugin(protocPlugins.kotlinMultiplatform)
+ protoSourceSet.protocPlugin(plugins.kotlinMultiplatform)
+ protoSourceSet.protocPlugin(plugins.grpcKotlinMultiplatform)
}
project.afterEvaluate {
@@ -71,16 +73,6 @@ internal open class DefaultGrpcExtension @Inject constructor(
@Suppress("detekt.LongMethod", "detekt.CyclomaticComplexMethod", "detekt.ThrowsCount")
private fun Project.configureTasks(protoSourceSet: DefaultProtoSourceSet) {
- // todo remove after KRPC-180
- if (!protoSourceSet.languageSourceSets.isPresent || protoSourceSet.languageSourceSets.get().isEmpty()) {
- logger.debug(
- "Language source sets are not set for proto source set '${protoSourceSet.name}', " +
- "skipping buf tasks configuration"
- )
-
- return
- }
-
val baseName = protoSourceSet.name
val buildSourceSetsDir = project.protoBuildDirSourceSets.resolve(baseName)
@@ -98,7 +90,7 @@ internal open class DefaultGrpcExtension @Inject constructor(
val protocPluginNames = (protoSourceSet.protocPlugins.get() + mainProtocPlugins).distinct()
val includedProtocPlugins = protocPluginNames.map {
- protocPlugins.findByName(it)
+ this@DefaultProtocExtension.plugins.findByName(it)
?: throw GradleException("Protoc plugin $it not found")
}
@@ -156,6 +148,18 @@ internal open class DefaultGrpcExtension @Inject constructor(
protoFilesDir = buildSourceSetsProtoDir,
importFilesDir = buildSourceSetsImportDir,
) {
+ includedProtocPlugins.forEach { plugin ->
+ executableFiles.addAll(
+ plugin.artifact.map {
+ if (it is ProtocPlugin.Artifact.Local) {
+ it.executableFiles.get()
+ } else {
+ emptyList()
+ }
+ }
+ )
+ }
+
dependsOn(generateBufGenYamlTask)
dependsOn(generateBufYamlTask)
dependsOn(processProtoTask)
@@ -172,32 +176,22 @@ internal open class DefaultGrpcExtension @Inject constructor(
protoSourceSet.generateTask.set(bufGenerateTask)
- // todo fix for common source sets
- tasks.withType().configureEach {
+ project.tasks.withType>().all {
// compileKotlin - main
// compileTestKotlin - test
// compileKotlinJvm - jvmMain
// compileTestKotlinJvm - jvmTest
// compileKotlinIosArm64 - iosArm64Main
// compileTestKotlinIosArm64 - iosArm64Test
- val taskNameAsSourceSet = name
- .removePrefix("compile").let {
- val suffix = it.substringBefore("Kotlin").takeIf { prefix ->
- prefix.isNotEmpty()
- } ?: "Main"
-
- (it.substringAfter("Kotlin")
- .replaceFirstChar { ch -> ch.lowercase() } + suffix)
- .takeIf { result -> result != suffix }
- ?: suffix.lowercase()
- }
-
- if (taskNameAsSourceSet == baseName) {
+ val isTest = name.startsWith("compileTest")
+ if (isTest && baseName.lowercase().endsWith("test")) {
+ dependsOn(bufGenerateTask)
+ } else if (!isTest && baseName.lowercase().endsWith("main")) {
dependsOn(bufGenerateTask)
}
}
- tasks.withType().configureEach {
+ project.tasks.withType().all {
// compileJvmTestJava - test (java, kmp)
// compileJvmMainJava - main (java, kmp)
// compileJava - main (java)
@@ -271,19 +265,31 @@ internal open class DefaultGrpcExtension @Inject constructor(
}
private fun createDefaultProtocPlugins() {
- protocPlugins.create(KOTLIN_MULTIPLATFORM) {
+ val explicitApiModeEnabled = project.provider {
+ project.the().explicitApi != ExplicitApiMode.Disabled
+ }
+
+ plugins.create(KOTLIN_MULTIPLATFORM) {
local {
javaJar(project.kotlinMultiplatformProtocPluginJarPath)
}
options.put("debugOutput", "protoc-gen-kotlin-multiplatform.log")
- options.put("explicitApiModeEnabled", project.provider {
- project.the().explicitApi != ExplicitApiMode.Disabled
- })
+ options.put("explicitApiModeEnabled", explicitApiModeEnabled)
+ }
+
+ plugins.create(GRPC_KOTLIN_MULTIPLATFORM) {
+ local {
+ javaJar(project.grpcKotlinMultiplatformProtocPluginJarPath)
+ }
+
+ options.put("debugOutput", "protoc-gen-grpc-kotlin-multiplatform.log")
+ options.put("explicitApiModeEnabled", explicitApiModeEnabled)
}
// ignore for bufGenerate task caching
project.normalization.runtimeClasspath.ignore("**/protoc-gen-kotlin-multiplatform.log")
+ project.normalization.runtimeClasspath.ignore("**/protoc-gen-grpc-kotlin-multiplatform.log")
project.normalization.runtimeClasspath.ignore("**/.keep")
}
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProcessProtoFiles.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt
similarity index 98%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProcessProtoFiles.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt
index ed6b7e61d..7bc197c2b 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProcessProtoFiles.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProcessProtoFiles.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.proto
+package kotlinx.rpc.protoc
import kotlinx.rpc.util.ensureRegularFileExists
import org.gradle.api.Project
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt
similarity index 89%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProtoSourceSet.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt
index 9b14d33a7..455e6182f 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProtoSourceSet.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.proto
+package kotlinx.rpc.protoc
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
@@ -25,7 +25,7 @@ public interface ProtoSourceSet {
*
* Example:
* ```kotlin
- * protocPlugin(rpc.grpc.protocPlugins.myPlugin)
+ * protocPlugin(rpc.protoc.plugins.myPlugin)
* ```
*/
public fun protocPlugin(plugin: NamedDomainObjectProvider)
@@ -35,7 +35,7 @@ public interface ProtoSourceSet {
*
* Example:
* ```kotlin
- * protocPlugin(rpc.grpc.protocPlugins.myPlugin.get())
+ * protocPlugin(rpc.protoc.plugins.myPlugin.get())
* ```
*/
public fun protocPlugin(plugin: ProtocPlugin)
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/GrpcExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt
similarity index 73%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/GrpcExtension.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt
index 265841fd5..ff8e843f9 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/GrpcExtension.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocExtension.kt
@@ -2,10 +2,9 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc
+package kotlinx.rpc.protoc
import kotlinx.rpc.buf.BufExtension
-import kotlinx.rpc.proto.ProtocPlugin
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
@@ -15,20 +14,20 @@ import org.gradle.api.NamedDomainObjectContainer
* To enable the gRPC capabilities, add the following minimal code to your `build.gradle.kts`:
* ```kotlin
* rpc {
- * grpc()
+ * protoc()
* }
* ```
*/
-public interface GrpcExtension {
+public interface ProtocExtension {
/**
* List of protoc plugins to be applied to the project.
*/
- public val protocPlugins: NamedDomainObjectContainer
+ public val plugins: NamedDomainObjectContainer
/**
* Configures the protoc plugins.
*/
- public fun protocPlugins(action: Action>)
+ public fun plugins(action: Action>)
/**
* Configuration for the Buf build tool.
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProtocPlugin.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt
similarity index 82%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProtocPlugin.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt
index 496169fe7..c5f0c7774 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/ProtocPlugin.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtocPlugin.kt
@@ -2,9 +2,11 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.proto
+package kotlinx.rpc.protoc
-import kotlinx.rpc.proto.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM
+import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM
+import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM
+import kotlinx.rpc.buf.tasks.BufGenerateTask
import kotlinx.rpc.util.OS
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
@@ -17,6 +19,7 @@ import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.mapProperty
import org.gradle.kotlin.dsl.property
+import java.io.File
/**
* Access to the `kotlin-multiplatform` protoc plugin.
@@ -31,6 +34,19 @@ public fun NamedDomainObjectContainer.kotlinMultiplatform(action:
kotlinMultiplatform.configure(action)
}
+/**
+ * Access to the `grpc-kotlin-multiplatform` protoc plugin.
+ */
+public val NamedDomainObjectContainer.grpcKotlinMultiplatform: NamedDomainObjectProvider
+ get() = named(GRPC_KOTLIN_MULTIPLATFORM)
+
+/**
+ * Configures the `grpc-kotlin-multiplatform` protoc plugin.
+ */
+public fun NamedDomainObjectContainer.grpcKotlinMultiplatform(action: Action) {
+ grpcKotlinMultiplatform.configure(action)
+}
+
/**
* Access to a specific protoc plugin.
*/
@@ -151,6 +167,13 @@ public open class ProtocPlugin(
* @see [kotlinMultiplatform]
*/
public const val KOTLIN_MULTIPLATFORM: String = "kotlin-multiplatform"
+
+ /**
+ * The name of the `grpc-kotlin-multiplatform` protoc plugin.
+ *
+ * @see [grpcKotlinMultiplatform]
+ */
+ public const val GRPC_KOTLIN_MULTIPLATFORM: String = "grpc-kotlin-multiplatform"
}
/**
@@ -181,6 +204,20 @@ public open class ProtocPlugin(
*/
public val executor: ListProperty = project.objects.listProperty()
+ /**
+ * Specifies the files that will be used during execution by the plugin.
+ *
+ * Useful to properly handle caching in [BufGenerateTask].
+ *
+ * Automatically includes the plugin's .jar file if [javaJar] was used.
+ *
+ * Optional.
+ *
+ * @see [BufGenerateTask.executableFiles]
+ */
+ public val executableFiles: ListProperty = project.objects.listProperty()
+ .convention(emptyList())
+
/**
* Command-line arguments that execute the plugin.
*/
@@ -201,6 +238,8 @@ public open class ProtocPlugin(
* If [executablePath] is not specified, the jar will be executed with Java used for the Gradle build.
*/
public fun javaJar(jarPath: Provider, executablePath: Provider? = null) {
+ executableFiles.add(jarPath.map { project.file(it) })
+
if (executablePath == null) {
executor(jarPath.map { listOf(OS.javaExePath, "-jar", it) })
return
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/consts.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt
similarity index 85%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/consts.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt
index dd9dfb093..5448e8156 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/consts.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/consts.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.proto
+package kotlinx.rpc.protoc
import org.gradle.api.artifacts.Configuration
@@ -57,3 +57,10 @@ public const val PROTO_FILES_IMPORT_DIR: String = "import"
* MUST be a single file.
*/
public const val PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION: String = "protocGenKotlinMultiplatformJar"
+
+/**
+ * [Configuration] name for the `protoc-gen-grpc-kotlin-multiplatform` protoc plugin artifact.
+ *
+ * MUST be a single file.
+ */
+public const val PROTOC_GEN_GRPC_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION: String = "protocGenGrpcKotlinMultiplatformJar"
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/directory.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt
similarity index 96%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/directory.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt
index d3231e6b5..02fb6a789 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/proto/directory.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/directory.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.proto
+package kotlinx.rpc.protoc
import org.gradle.api.Project
import java.io.File
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/pluginJars.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/pluginJars.kt
new file mode 100644
index 000000000..f12033e8e
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/pluginJars.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc
+
+import kotlinx.rpc.LIBRARY_VERSION
+import org.gradle.api.Project
+import org.gradle.api.provider.Provider
+
+/**
+ * Absolute path to the `protoc-gen-kotlin-multiplatform` jar.
+ *
+ * Can be used to customize the java executable path:
+ * ```kotlin
+ * rpc.protoc.plugins.kotlinMultiplatform {
+ * local {
+ * javaJar(kotlinMultiplatformProtocPluginJarPath, provider { "my-path-to-java" })
+ * }
+ * }
+ * ```
+ */
+public val Project.kotlinMultiplatformProtocPluginJarPath: Provider
+ get() = jarPathAccessor(PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION)
+
+internal fun Project.configureKotlinMultiplatformPluginJarConfiguration() {
+ configureJarConfiguration(
+ configurationName = PROTOC_GEN_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION,
+ pluginArtifactName = "protoc-gen-kotlin-multiplatform",
+ )
+}
+
+/**
+ * Absolute path to the `protoc-gen-grpc-kotlin-multiplatform` jar.
+ *
+ * Can be used to customize the java executable path:
+ * ```kotlin
+ * rpc.protoc.plugins.grpcKotlinMultiplatform {
+ * local {
+ * javaJar(grpcKotlinMultiplatformProtocPluginJarPath, provider { "my-path-to-java" })
+ * }
+ * }
+ * ```
+ */
+public val Project.grpcKotlinMultiplatformProtocPluginJarPath: Provider
+ get() = jarPathAccessor(PROTOC_GEN_GRPC_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION)
+
+internal fun Project.configureGrpcKotlinMultiplatformPluginJarConfiguration() {
+ configureJarConfiguration(
+ configurationName = PROTOC_GEN_GRPC_KOTLIN_MULTIPLATFORM_JAR_CONFIGURATION,
+ pluginArtifactName = "protoc-gen-grpc-kotlin-multiplatform",
+ )
+}
+
+private fun Project.jarPathAccessor(name: String): Provider =
+ project.configurations.named(name)
+ .map { it.singleFile.absolutePath }
+
+private fun Project.configureJarConfiguration(configurationName: String, pluginArtifactName: String) {
+ configurations.create(configurationName)
+
+ dependencies.add(
+ configurationName,
+ mapOf(
+ "group" to "org.jetbrains.kotlinx",
+ "name" to pluginArtifactName,
+ "version" to LIBRARY_VERSION,
+ "classifier" to "all",
+ "ext" to "jar",
+ ),
+ )
+}
diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/protections.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/protections.kt
similarity index 95%
rename from gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/protections.kt
rename to gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/protections.kt
index cea7ccae7..10067bdec 100644
--- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/grpc/protections.kt
+++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/protections.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc
+package kotlinx.rpc.protoc
import kotlinx.rpc.rpcExtension
import org.gradle.api.GradleException
@@ -20,7 +20,7 @@ internal fun Project.configurePluginProtections() {
pluginManager.withPlugin(PROTOBUF_PLUGIN_ID) { isProtobufPluginApplied = true }
afterEvaluate {
- if (!rpcExtension().grpcApplied.get()) {
+ if (!rpcExtension().protocApplied.get()) {
return@afterEvaluate
}
diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt
index a4977b2d2..66d55d109 100644
--- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt
+++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcJvmProjectTest.kt
@@ -139,7 +139,11 @@ plugins:
out: kotlin-multiplatform
opt:
- debugOutput=protoc-gen-kotlin-multiplatform.log
- - messageMode=interface
+ - explicitApiModeEnabled=true
+ - local: [protoc-gen-grpc-kotlin-multiplatform]
+ out: grpc-kotlin-multiplatform
+ opt:
+ - debugOutput=protoc-gen-grpc-kotlin-multiplatform.log
- explicitApiModeEnabled=true
- local: [path, to, protoc-gen-myplugin.exe]
out: myPlugin
diff --git a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt
index bc9836ac5..bd0dafedd 100644
--- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt
+++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/GrpcKmpProjectTest.kt
@@ -95,7 +95,6 @@ class GrpcKmpProjectTest : GrpcBaseTest() {
}
@Test
- // todo remove after KRPC-180
fun `No JVM Targets`() = runGrpcTest {
runNonExistentTask(bufGenerateMain)
runNonExistentTask(bufGenerateTest)
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 15e73d7a4..89ca6c01a 100644
--- a/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt
+++ b/gradle-plugin/src/test/kotlin/kotlinx/rpc/base/GrpcBaseTest.kt
@@ -101,10 +101,16 @@ abstract class GrpcBaseTest : BaseTest() {
}
val fileContent = file.readLines().joinToString("\n") {
- if (it.contains("protoc-gen-kotlin-multiplatform")) {
- it.replace(localPluginExecRegex, "[protoc-gen-kotlin-multiplatform]")
- } else {
- it
+ when {
+ 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
+ }
}
}
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 526a93fd8..437f5e141 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
@@ -3,7 +3,7 @@
*/
import org.gradle.kotlin.dsl.version
-import kotlinx.rpc.proto.*
+import kotlinx.rpc.protoc.*
plugins {
kotlin("jvm") version ""
@@ -11,8 +11,8 @@ plugins {
}
rpc {
- grpc {
- protocPlugins {
+ protoc {
+ plugins {
create("myPlugin") {
local {
executor("path", "to", "protoc-gen-myplugin.exe")
@@ -38,7 +38,7 @@ rpc {
protoSourceSets {
main {
- protocPlugin(rpc.grpc.protocPlugins.named("myPlugin"))
- protocPlugin(rpc.grpc.protocPlugins.named("myRemotePlugin"))
+ protocPlugin(rpc.protoc.plugins.named("myPlugin"))
+ protocPlugin(rpc.protoc.plugins.named("myRemotePlugin"))
}
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts
index f3e6962e7..aa739abe1 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/custom_buf_cli_flags/build.gradle.kts
@@ -3,7 +3,7 @@
*/
import org.gradle.kotlin.dsl.version
-import kotlinx.rpc.proto.*
+import kotlinx.rpc.protoc.*
import kotlinx.rpc.buf.*
import java.io.File
import kotlin.time.Duration.Companion.seconds
@@ -14,7 +14,7 @@ plugins {
}
rpc {
- grpc {
+ protoc {
buf {
configFile = File("some.buf.yaml")
logFormat = BufExtension.LogFormat.Json
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 6acb669c7..beec798d6 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
@@ -3,7 +3,7 @@
*/
import org.gradle.kotlin.dsl.version
-import kotlinx.rpc.proto.*
+import kotlinx.rpc.protoc.*
plugins {
kotlin("jvm") version ""
@@ -11,8 +11,8 @@ plugins {
}
rpc {
- grpc {
- protocPlugins {
+ protoc {
+ plugins {
create("myPlugin") {}
}
}
@@ -20,6 +20,6 @@ rpc {
protoSourceSets {
main {
- protocPlugin(rpc.grpc.protocPlugins.named("myPlugin"))
+ protocPlugin(rpc.protoc.plugins.named("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_protosourcesets/build.gradle.kts
index aafd2083f..60d6d646d 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_protosourcesets/build.gradle.kts
@@ -27,5 +27,5 @@ protoSourceSets {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts
index e9b01b47d..f1d4dc607 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/main_and_test_mixed_sources/build.gradle.kts
@@ -10,5 +10,5 @@ plugins {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts
index e9b01b47d..f1d4dc607 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/minimal_grpc_configuration/build.gradle.kts
@@ -10,5 +10,5 @@ plugins {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts
index 3b6317883..56ee2e979 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/proto_tasks_are_cached_properly/build.gradle.kts
@@ -3,7 +3,7 @@
*/
import org.gradle.kotlin.dsl.version
-import kotlinx.rpc.proto.kotlinMultiplatform
+import kotlinx.rpc.protoc.kotlinMultiplatform
plugins {
kotlin("jvm") version ""
@@ -11,5 +11,5 @@ plugins {
}
rpc {
- grpc()
+ protoc()
}
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 4ce1593cb..9d75c4634 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
@@ -19,5 +19,5 @@ protoSourceSets {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts
index e9b01b47d..f1d4dc607 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcJvmProjectTest/test_only_sources/build.gradle.kts
@@ -10,5 +10,5 @@ plugins {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts
index cbd88cc2c..a4500a1c1 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/main_and_test_mixed_sources/build.gradle.kts
@@ -14,5 +14,5 @@ kotlin {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts
index cbd88cc2c..a4500a1c1 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/minimal_grpc_configuration/build.gradle.kts
@@ -14,5 +14,5 @@ kotlin {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts
index cbd88cc2c..a4500a1c1 100644
--- a/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts
+++ b/gradle-plugin/src/test/resources/projects/GrpcKmpProjectTest/test_only_sources/build.gradle.kts
@@ -14,5 +14,5 @@ kotlin {
}
rpc {
- grpc()
+ protoc()
}
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 adf702591..1a8ba4661 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
@@ -21,5 +21,5 @@ protoSourceSets {
}
rpc {
- grpc()
+ protoc()
}
diff --git a/grpc/grpc-core/build.gradle.kts b/grpc/grpc-core/build.gradle.kts
index 5fae426b6..7fcd3dad7 100644
--- a/grpc/grpc-core/build.gradle.kts
+++ b/grpc/grpc-core/build.gradle.kts
@@ -2,12 +2,11 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-import kotlinx.rpc.buf.tasks.BufGenerateTask
-import kotlinx.rpc.proto.kotlinMultiplatform
-import org.gradle.internal.extensions.stdlib.capitalized
-import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
-import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+@file:OptIn(InternalRpcApi::class)
+
+import kotlinx.rpc.internal.InternalRpcApi
+import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency
+import util.configureCLibCInterop
plugins {
alias(libs.plugins.conventions.kmp)
@@ -31,7 +30,6 @@ kotlin {
implementation(libs.atomicfu)
implementation(libs.kotlinx.io.core)
- implementation(libs.kotlinx.collections.immutable)
}
}
@@ -52,89 +50,28 @@ kotlin {
api(libs.grpc.util)
api(libs.grpc.stub)
api(libs.grpc.protobuf)
- api("io.grpc:grpc-protobuf-lite:${libs.versions.grpc.asProvider().get()}")
+ api(libs.grpc.protobuf.lite)
implementation(libs.grpc.kotlin.stub) // causes problems to jpms if api
- api(libs.protobuf.java.util)
- implementation(libs.protobuf.kotlin)
}
}
jvmTest {
dependencies {
- implementation(projects.grpc.grpcCore)
- implementation(libs.coroutines.core)
- implementation(libs.coroutines.test)
-
- implementation(libs.grpc.stub)
implementation(libs.grpc.netty)
- implementation(libs.grpc.protobuf)
- implementation(libs.grpc.kotlin.stub)
- implementation(libs.protobuf.java.util)
- implementation(libs.protobuf.kotlin)
- }
- }
- }
-
- val grpcppCLib = projectDir.resolve("../grpcpp-c")
-
- // TODO: Replace function implementation, so it does not use an internal API
- fun findProgram(name: String) = org.gradle.internal.os.OperatingSystem.current().findInPath(name)
- val checkBazel by tasks.registering {
- doLast {
- val bazelPath = findProgram("bazel")
- if (bazelPath != null) {
- logger.debug("bazel: {}", bazelPath)
- } else {
- throw GradleException("'bazel' not found on PATH. Please install Bazel (https://bazel.build/).")
}
}
}
- val buildGrpcppCLib = tasks.register("buildGrpcppCLib") {
- group = "build"
- workingDir = grpcppCLib
- commandLine("bash", "-c", "bazel build :grpcpp_c_static :protowire_static --config=release")
- inputs.files(fileTree(grpcppCLib) { exclude("bazel-*/**") })
- outputs.dir(grpcppCLib.resolve("bazel-bin"))
-
- dependsOn(checkBazel)
- }
-
-
- targets.filterIsInstance().forEach {
- it.compilations.getByName("main") {
- cinterops {
- val libgrpcpp_c by creating {
- includeDirs(
- grpcppCLib.resolve("include"),
- grpcppCLib.resolve("bazel-grpcpp-c/external/grpc+/include")
- )
- extraOpts(
- "-libraryPath", "${grpcppCLib.resolve("bazel-out/darwin_arm64-opt/bin")}",
- )
- }
-
- val interopTask = "cinterop${libgrpcpp_c.name.capitalized()}${it.targetName.capitalized()}"
- tasks.named(interopTask, CInteropProcess::class) {
- dependsOn(buildGrpcppCLib)
- }
-
-
- val libprotowire by creating {
- includeDirs(
- grpcppCLib.resolve("include")
- )
- extraOpts(
- "-libraryPath", "${grpcppCLib.resolve("bazel-out/darwin_arm64-opt/bin")}",
- )
- }
-
- val libProtowireTask = "cinterop${libprotowire.name.capitalized()}${it.targetName.capitalized()}"
- tasks.named(libProtowireTask, CInteropProcess::class) {
- dependsOn(buildGrpcppCLib)
- }
-
- }
+ configureCLibCInterop(project, ":grpcpp_c_static") { cinteropCLib ->
+ @Suppress("unused")
+ val libgrpcpp_c by creating {
+ includeDirs(
+ cinteropCLib.resolve("include"),
+ cinteropCLib.resolve("bazel-cinterop-c/external/grpc+/include"),
+ )
+ extraOpts(
+ "-libraryPath", "${cinteropCLib.resolve("bazel-out/darwin_arm64-opt/bin")}",
+ )
}
}
}
@@ -147,28 +84,4 @@ protoSourceSets {
}
}
-
-rpc {
- grpc {
- val globalRootDir: String by extra
-
- protocPlugins.kotlinMultiplatform {
- local {
- javaJar("$globalRootDir/protoc-gen/build/libs/protoc-gen-$version-all.jar")
- }
- }
-
- project.tasks.withType().configureEach {
- if (name.endsWith("Test")) {
- dependsOn(gradle.includedBuild("protoc-gen").task(":jar"))
- }
- }
-
- // generate protos before compiling tests
- project.tasks.withType().configureEach {
- if (name.startsWith("compileTest")) {
- dependsOn(project.tasks.withType())
- }
- }
- }
-}
+configureLocalProtocGenDevelopmentDependency()
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/BaseGrpcServiceTest.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/BaseGrpcServiceTest.kt
similarity index 98%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/BaseGrpcServiceTest.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/BaseGrpcServiceTest.kt
index 04e08f276..08ed33cd4 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/BaseGrpcServiceTest.kt
+++ b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/BaseGrpcServiceTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.service
+package kotlinx.rpc.grpc.test
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/CustomResolverGrpcServiceTest.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/CustomResolverGrpcServiceTest.kt
similarity index 99%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/CustomResolverGrpcServiceTest.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/CustomResolverGrpcServiceTest.kt
index e68c002aa..eb8ee4d63 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/CustomResolverGrpcServiceTest.kt
+++ b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/CustomResolverGrpcServiceTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.service
+package kotlinx.rpc.grpc.test
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/RawClientServerTest.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/RawClientServerTest.kt
similarity index 95%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/RawClientServerTest.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/RawClientServerTest.kt
index 98840b0a3..4d5257274 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/RawClientServerTest.kt
+++ b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/RawClientServerTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc
+package kotlinx.rpc.grpc.test
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -14,6 +14,10 @@ import kotlinx.io.Buffer
import kotlinx.io.Source
import kotlinx.io.readString
import kotlinx.io.writeString
+import kotlinx.rpc.grpc.ManagedChannelBuilder
+import kotlinx.rpc.grpc.Server
+import kotlinx.rpc.grpc.ServerBuilder
+import kotlinx.rpc.grpc.buildChannel
import kotlinx.rpc.grpc.codec.MessageCodec
import kotlinx.rpc.grpc.internal.GrpcChannel
import kotlinx.rpc.grpc.internal.MethodDescriptor
@@ -29,6 +33,7 @@ import kotlinx.rpc.grpc.internal.serverStreamingServerMethodDefinition
import kotlinx.rpc.grpc.internal.serviceDescriptor
import kotlinx.rpc.grpc.internal.unaryRpc
import kotlinx.rpc.grpc.internal.unaryServerMethodDefinition
+import kotlinx.rpc.grpc.serverServiceDefinition
import kotlin.reflect.typeOf
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/SerializationGrpcServiceTest.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/SerializationGrpcServiceTest.kt
similarity index 98%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/SerializationGrpcServiceTest.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/SerializationGrpcServiceTest.kt
index 0ac1e2905..5c3467bf3 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/service/SerializationGrpcServiceTest.kt
+++ b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/SerializationGrpcServiceTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.service
+package kotlinx.rpc.grpc.test
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/GrpcServerTest.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcServerTest.kt
similarity index 100%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/GrpcServerTest.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcServerTest.kt
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/StreamingTest.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/StreamingTest.kt
similarity index 100%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/StreamingTest.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/StreamingTest.kt
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/TestPrimitiveService.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/TestPrimitiveService.kt
similarity index 100%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/TestPrimitiveService.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/TestPrimitiveService.kt
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/TestReferenceService.kt b/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/TestReferenceService.kt
similarity index 100%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/core/test/TestReferenceService.kt
rename to grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/TestReferenceService.kt
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.js.kt
deleted file mode 100644
index 2218a5078..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.js.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-
-package kotlinx.rpc.grpc
-
-/**
- * Same as [ManagedChannel], but is platform-exposed.
- */
-public actual abstract class ManagedChannelPlatform
-
-/**
- * Builder class for [ManagedChannel].
- */
-public actual abstract class ManagedChannelBuilder>
-
-internal actual fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel {
- error("JS target is not supported in gRPC")
-}
-
-internal actual fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*> {
- error("JS target is not supported in gRPC")
-}
-
-internal actual fun ManagedChannelBuilder(target: String): ManagedChannelBuilder<*> {
- error("JS target is not supported in gRPC")
-}
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/MutableHandlerRegistry.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/MutableHandlerRegistry.js.kt
deleted file mode 100644
index aa6ba04a1..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/MutableHandlerRegistry.js.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-
-package kotlinx.rpc.grpc
-
-/**
- * Registry of services and their methods used by servers to dispatching incoming calls.
- */
-public actual abstract class HandlerRegistry
-
-internal actual class MutableHandlerRegistry : HandlerRegistry() {
- actual fun addService(service: ServerServiceDefinition): ServerServiceDefinition? {
- error("JS target is not supported in gRPC")
- }
-
- actual fun removeService(service: ServerServiceDefinition): Boolean {
- error("JS target is not supported in gRPC")
- }
-}
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/Server.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/Server.js.kt
deleted file mode 100644
index c09084882..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/Server.js.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc
-
-/**
- * Platform-specific gRPC server builder.
- */
-public actual abstract class ServerBuilder> {
- public actual abstract fun addService(service: ServerServiceDefinition): T
-
- public actual abstract fun fallbackHandlerRegistry(registry: HandlerRegistry?): T
-}
-
-internal actual fun ServerBuilder(port: Int): ServerBuilder<*> {
- error("JS target is not supported in gRPC")
-}
-
-internal actual fun Server(builder: ServerBuilder<*>): Server {
- error("JS target is not supported in gRPC")
-}
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/ServerServiceDefinition.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/ServerServiceDefinition.js.kt
deleted file mode 100644
index 0ac0a6e29..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/ServerServiceDefinition.js.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-
-package kotlinx.rpc.grpc
-
-/**
- * Definition of a service to be exposed via a Server.
- */
-public actual class ServerServiceDefinition
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.js.kt
deleted file mode 100644
index c030ce564..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.js.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.internal
-
-internal actual fun WireDecoder.pushLimit(byteLen: Int): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireDecoder.popLimit(limit: Int) {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireDecoder.bytesUntilLimit(): Int {
- TODO("Not yet implemented")
-}
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.js.kt
deleted file mode 100644
index 4cff6ef95..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.js.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.pb
-
-import kotlinx.io.Buffer
-
-internal actual fun WireDecoder(source: Buffer): WireDecoder {
- TODO("Not yet implemented")
-}
\ No newline at end of file
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.js.kt
deleted file mode 100644
index 733950eba..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.js.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.pb
-
-import kotlinx.io.Sink
-
-internal actual fun WireEncoder(sink: Sink): WireEncoder {
- TODO("Not yet implemented")
-}
\ No newline at end of file
diff --git a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.js.kt b/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.js.kt
deleted file mode 100644
index 041674e94..000000000
--- a/grpc/grpc-core/src/jsMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.js.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.pb
-
-internal actual fun WireSize.int32(value: Int): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.int64(value: Long): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.uInt32(value: UInt): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.uInt64(value: ULong): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.sInt32(value: Int): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.sInt64(value: Long): Int {
- TODO("Not yet implemented")
-}
\ No newline at end of file
diff --git a/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/BridgeTest.kt b/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/test/BridgeTest.kt
similarity index 97%
rename from grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/BridgeTest.kt
rename to grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/test/BridgeTest.kt
index 5c3b8b97a..e0ef885b2 100644
--- a/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/BridgeTest.kt
+++ b/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/test/BridgeTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
+package kotlinx.rpc.grpc.test
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.coroutines.runBlocking
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.wasmJs.kt
deleted file mode 100644
index 8e9101a03..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.wasmJs.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-
-package kotlinx.rpc.grpc
-
-/**
- * Same as [ManagedChannel], but is platform-exposed.
- */
-public actual abstract class ManagedChannelPlatform
-
-/**
- * Builder class for [ManagedChannel].
- */
-public actual abstract class ManagedChannelBuilder>
-
-internal actual fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel {
- error("WasmJS target is not supported in gRPC")
-}
-
-internal actual fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*> {
- error("WasmJS target is not supported in gRPC")
-}
-
-internal actual fun ManagedChannelBuilder(target: String): ManagedChannelBuilder<*> {
- error("WasmJS target is not supported in gRPC")
-}
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/MutableHandlerRegistry.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/MutableHandlerRegistry.wasmJs.kt
deleted file mode 100644
index f3b50fc10..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/MutableHandlerRegistry.wasmJs.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-
-package kotlinx.rpc.grpc
-
-/**
- * Registry of services and their methods used by servers to dispatching incoming calls.
- */
-public actual abstract class HandlerRegistry
-
-internal actual class MutableHandlerRegistry : HandlerRegistry() {
- actual fun addService(service: ServerServiceDefinition): ServerServiceDefinition? {
- error("WasmJS target is not supported in gRPC")
- }
-
- actual fun removeService(service: ServerServiceDefinition): Boolean {
- error("WasmJS target is not supported in gRPC")
- }
-}
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/Server.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/Server.wasmJs.kt
deleted file mode 100644
index 698a08f9a..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/Server.wasmJs.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc
-
-/**
- * Platform-specific gRPC server builder.
- */
-
-public actual abstract class ServerBuilder> {
- public actual abstract fun addService(service: ServerServiceDefinition): T
-
- public actual abstract fun fallbackHandlerRegistry(registry: HandlerRegistry?): T
-}
-
-internal actual fun ServerBuilder(port: Int): ServerBuilder<*> {
- error("WasmJS target is not supported in gRPC")
-}
-
-internal actual fun Server(builder: ServerBuilder<*>): Server {
- error("WasmJS target is not supported in gRPC")
-}
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/ServerServiceDefinition.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/ServerServiceDefinition.wasmJs.kt
deleted file mode 100644
index 0ac0a6e29..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/ServerServiceDefinition.wasmJs.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
-
-package kotlinx.rpc.grpc
-
-/**
- * Definition of a service to be exposed via a Server.
- */
-public actual class ServerServiceDefinition
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.wasmJs.kt
deleted file mode 100644
index c030ce564..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.wasmJs.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.internal
-
-internal actual fun WireDecoder.pushLimit(byteLen: Int): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireDecoder.popLimit(limit: Int) {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireDecoder.bytesUntilLimit(): Int {
- TODO("Not yet implemented")
-}
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.wasmJs.kt
deleted file mode 100644
index 4cff6ef95..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.wasmJs.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.pb
-
-import kotlinx.io.Buffer
-
-internal actual fun WireDecoder(source: Buffer): WireDecoder {
- TODO("Not yet implemented")
-}
\ No newline at end of file
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.wasmJs.kt
deleted file mode 100644
index 733950eba..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.wasmJs.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.pb
-
-import kotlinx.io.Sink
-
-internal actual fun WireEncoder(sink: Sink): WireEncoder {
- TODO("Not yet implemented")
-}
\ No newline at end of file
diff --git a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.wasmJs.kt b/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.wasmJs.kt
deleted file mode 100644
index 041674e94..000000000
--- a/grpc/grpc-core/src/wasmJsMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.wasmJs.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.rpc.grpc.pb
-
-internal actual fun WireSize.int32(value: Int): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.int64(value: Long): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.uInt32(value: UInt): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.uInt64(value: ULong): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.sInt32(value: Int): Int {
- TODO("Not yet implemented")
-}
-
-internal actual fun WireSize.sInt64(value: Long): Int {
- TODO("Not yet implemented")
-}
\ No newline at end of file
diff --git a/grpc/grpc-ktor-server/build.gradle.kts b/grpc/grpc-ktor-server/build.gradle.kts
index 8fa4115d7..9084429c2 100644
--- a/grpc/grpc-ktor-server/build.gradle.kts
+++ b/grpc/grpc-ktor-server/build.gradle.kts
@@ -2,10 +2,11 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-import kotlinx.rpc.buf.tasks.BufGenerateTask
-import kotlinx.rpc.proto.kotlinMultiplatform
+@file:OptIn(InternalRpcApi::class)
+
+import kotlinx.rpc.internal.InternalRpcApi
+import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency
import org.gradle.kotlin.dsl.kotlin
-import org.gradle.kotlin.dsl.withType
plugins {
alias(libs.plugins.conventions.kmp)
@@ -40,20 +41,4 @@ kotlin {
}
}
-rpc {
- grpc {
- val globalRootDir: String by extra
-
- protocPlugins.kotlinMultiplatform {
- local {
- javaJar("$globalRootDir/protoc-gen/build/libs/protoc-gen-$version-all.jar")
- }
- }
-
- project.tasks.withType().configureEach {
- if (name.endsWith("Test")) {
- dependsOn(gradle.includedBuild("protoc-gen").task(":jar"))
- }
- }
- }
-}
+configureLocalProtocGenDevelopmentDependency()
diff --git a/protobuf/protobuf-core/build.gradle.kts b/protobuf/protobuf-core/build.gradle.kts
new file mode 100644
index 000000000..fdc7b1592
--- /dev/null
+++ b/protobuf/protobuf-core/build.gradle.kts
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(InternalRpcApi::class)
+
+import kotlinx.rpc.internal.InternalRpcApi
+import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency
+import util.configureCLibCInterop
+
+plugins {
+ alias(libs.plugins.conventions.kmp)
+ alias(libs.plugins.kotlinx.rpc)
+}
+
+kotlin {
+ sourceSets {
+ commonMain {
+ dependencies {
+ api(projects.utils)
+ api(libs.kotlinx.io.core)
+ }
+ }
+
+ commonTest {
+ dependencies {
+ implementation(projects.grpc.grpcCodec)
+
+ implementation(libs.kotlin.test)
+ implementation(libs.coroutines.test)
+ }
+ }
+
+ jvmMain {
+ dependencies {
+ api(libs.protobuf.java.util)
+ implementation(libs.protobuf.kotlin)
+ }
+ }
+
+ nativeMain {
+ dependencies {
+ implementation(libs.kotlinx.collections.immutable)
+ }
+ }
+ }
+
+ configureCLibCInterop(project, ":protowire_static") { cinteropCLib ->
+ @Suppress("unused")
+ val libprotowire by creating {
+ includeDirs(
+ cinteropCLib.resolve("include")
+ )
+ extraOpts(
+ "-libraryPath", "${cinteropCLib.resolve("bazel-out/darwin_arm64-opt/bin")}",
+ )
+ }
+ }
+}
+
+protoSourceSets {
+ commonTest {
+ proto {
+ exclude("exclude/**")
+ }
+ }
+}
+
+configureLocalProtocGenDevelopmentDependency()
diff --git a/protobuf/protobuf-core/gradle.properties b/protobuf/protobuf-core/gradle.properties
new file mode 100644
index 000000000..4ae0c96a1
--- /dev/null
+++ b/protobuf/protobuf-core/gradle.properties
@@ -0,0 +1,21 @@
+#
+# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+#
+kotlinx.rpc.exclude.wasmWasi=true
+kotlinx.rpc.exclude.js=true
+kotlinx.rpc.exclude.wasmJs=true
+kotlinx.rpc.exclude.iosArm64=true
+kotlinx.rpc.exclude.iosX64=true
+kotlinx.rpc.exclude.iosSimulatorArm64=true
+kotlinx.rpc.exclude.linuxArm64=true
+kotlinx.rpc.exclude.linuxX64=true
+kotlinx.rpc.exclude.macosX64=true
+kotlinx.rpc.exclude.mingwX64=true
+kotlinx.rpc.exclude.tvosArm64=true
+kotlinx.rpc.exclude.tvosSimulatorArm64=true
+kotlinx.rpc.exclude.tvosX64=true
+kotlinx.rpc.exclude.watchosArm32=true
+kotlinx.rpc.exclude.watchosArm64=true
+kotlinx.rpc.exclude.watchosDeviceArm64=true
+kotlinx.rpc.exclude.watchosSimulatorArm64=true
+kotlinx.rpc.exclude.watchosX64=true
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/utils/BitSet.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/BitSet.kt
similarity index 88%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/utils/BitSet.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/BitSet.kt
index 85520ca04..e8fe19bb7 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/utils/BitSet.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/BitSet.kt
@@ -2,9 +2,10 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.utils
+package kotlinx.rpc.protobuf.internal
import kotlinx.rpc.internal.utils.InternalRpcApi
+import kotlin.countOneBits
/**
* A fixed-sized vector of bits, allowing one to set/clear/read bits from it by a bit index.
@@ -24,14 +25,14 @@ public class BitSet(public val size: Int) {
/** Clears the bit at [index] (sets to 0). */
public fun clear(index: Int) {
- require(index >= 0 && index < size) { "Index $index out of bounds for length $size" }
+ require(index in 0..= 0 && index < size) { "Index $index out of bounds for length $size" }
+ require(index in 0..(
this@MsgFieldDelegate.value = value
valueSet = true
}
-}
\ No newline at end of file
+}
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/KTag.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/KTag.kt
similarity index 85%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/KTag.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/KTag.kt
index c6b9ffe2e..2e6510c34 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/KTag.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/KTag.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import kotlinx.rpc.internal.utils.InternalRpcApi
@@ -25,7 +25,7 @@ public data class KTag(val fieldNr: Int, val wireType: WireType) {
internal companion object {
// Number of bits in a tag which identify the wire type.
- const val K_TAG_TYPE_BITS: Int = 3;
+ const val K_TAG_TYPE_BITS: Int = 3
// Mask for those bits. (just 0b111)
val K_TAG_TYPE_MASK: UInt = (1u shl K_TAG_TYPE_BITS) - 1u
@@ -33,7 +33,7 @@ public data class KTag(val fieldNr: Int, val wireType: WireType) {
}
internal fun KTag.toRawKTag(): UInt {
- return (fieldNr.toUInt() shl KTag.Companion.K_TAG_TYPE_BITS) or wireType.ordinal.toUInt()
+ return (fieldNr.toUInt() shl KTag.K_TAG_TYPE_BITS) or wireType.ordinal.toUInt()
}
internal fun KTag.Companion.from(rawKTag: UInt): KTag {
@@ -49,5 +49,5 @@ internal fun KTag.Companion.from(rawKTag: UInt): KTag {
}
internal fun KTag.Companion.isValidFieldNr(fieldNr: Int): Boolean {
- return 1 <= fieldNr && fieldNr <= 536_870_911
-}
\ No newline at end of file
+ return fieldNr in 1..536_870_911
+}
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/ProtobufException.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/ProtobufException.kt
similarity index 97%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/ProtobufException.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/ProtobufException.kt
index 3830c6f66..39c27a42f 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/ProtobufException.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/ProtobufException.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
public sealed class ProtobufException : RuntimeException {
protected constructor(message: String, cause: Throwable? = null) : super(message, cause)
@@ -37,4 +37,4 @@ public class ProtobufDecodingException : ProtobufException {
public class ProtobufEncodingException : ProtobufException {
public constructor(message: String, cause: Throwable? = null) : super(message, cause)
-}
\ No newline at end of file
+}
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.kt
similarity index 96%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.kt
index 2961558c9..6b5d6fd4c 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.kt
@@ -2,11 +2,9 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import kotlinx.io.Buffer
-import kotlinx.rpc.grpc.internal.popLimit
-import kotlinx.rpc.grpc.internal.pushLimit
import kotlinx.rpc.internal.utils.InternalRpcApi
// TODO: Evaluate if this buffer size is suitable for all targets (KRPC-186)
@@ -118,4 +116,4 @@ public expect inline fun checkForPlatformDecodeException(block: () -> Unit)
* @param source The buffer containing the encoded wire-format data.
*/
@InternalRpcApi
-public expect fun WireDecoder(source: Buffer): WireDecoder
\ No newline at end of file
+public expect fun WireDecoder(source: Buffer): WireDecoder
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.kt
similarity index 97%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.kt
index dc6177d8a..856550539 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import kotlinx.io.Sink
import kotlinx.rpc.internal.utils.InternalRpcApi
@@ -69,4 +69,4 @@ public interface WireEncoder {
public expect inline fun checkForPlatformEncodeException(block: () -> Unit)
@InternalRpcApi
-public expect fun WireEncoder(sink: Sink): WireEncoder
\ No newline at end of file
+public expect fun WireEncoder(sink: Sink): WireEncoder
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.kt
similarity index 96%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.kt
index dbd2b5bc5..c1193697a 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.kt
@@ -2,7 +2,9 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+@file:Suppress("UnusedReceiverParameter")
+
+package kotlinx.rpc.protobuf.internal
import kotlinx.io.bytestring.encodeToByteString
import kotlinx.rpc.internal.utils.InternalRpcApi
@@ -10,6 +12,7 @@ import kotlinx.rpc.internal.utils.InternalRpcApi
@InternalRpcApi
public object WireSize
+@InternalRpcApi
public fun WireSize.tag(fieldNumber: Int, wireType: WireType): Int =
uInt32(KTag(fieldNumber, wireType).toRawKTag())
diff --git a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.kt b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.kt
similarity index 88%
rename from grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.kt
rename to protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.kt
index c20bd0e79..2e0377405 100644
--- a/grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.kt
+++ b/protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.kt
@@ -2,10 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
-
-import kotlinx.rpc.grpc.pb.ProtobufDecodingException
-import kotlinx.rpc.grpc.pb.WireDecoder
+package kotlinx.rpc.protobuf.internal
internal expect fun WireDecoder.pushLimit(byteLen: Int): Int
internal expect fun WireDecoder.popLimit(limit: Int)
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/internal/BitSetTest.kt b/protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/BitSetTest.kt
similarity index 97%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/internal/BitSetTest.kt
rename to protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/BitSetTest.kt
index 0644f91c6..707a05a03 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/internal/BitSetTest.kt
+++ b/protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/BitSetTest.kt
@@ -2,10 +2,14 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
+package kotlinx.rpc.protobuf.test
-import kotlinx.rpc.grpc.utils.BitSet
-import kotlin.test.*
+import kotlinx.rpc.protobuf.internal.BitSet
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
class BitSetTest {
@@ -302,4 +306,4 @@ class BitSetTest {
// Verify cardinality
assertEquals(size, bitSet.cardinality(), "Cardinality should equal size when all bits are set")
}
-}
\ No newline at end of file
+}
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/ProtosTest.kt b/protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/ProtosTest.kt
similarity index 97%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/ProtosTest.kt
rename to protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/ProtosTest.kt
index 49c349ebc..1c26009ae 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/ProtosTest.kt
+++ b/protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/ProtosTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.test
import OneOfMsg
import OneOfMsgInternal
@@ -16,12 +16,19 @@ import kotlinx.io.Buffer
import kotlinx.rpc.grpc.codec.MessageCodec
import kotlinx.rpc.grpc.test.*
import kotlinx.rpc.grpc.test.common.*
+import kotlinx.rpc.grpc.test.common.invoke
+import kotlinx.rpc.grpc.test.invoke
+import kotlinx.rpc.protobuf.internal.ProtobufDecodingException
+import kotlinx.rpc.protobuf.internal.WireEncoder
import test.nested.*
+import test.nested.invoke
import test.recursive.Recursive
import test.recursive.RecursiveInternal
import test.recursive.RecursiveReq
import test.recursive.invoke
import test.submsg.*
+import test.submsg.invoke
+import kotlin.collections.iterator
import kotlin.test.*
class ProtosTest {
diff --git a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.kt b/protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.kt
similarity index 96%
rename from grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.kt
rename to protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.kt
index e3a41b112..aaeacbe0d 100644
--- a/grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.kt
+++ b/protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.kt
@@ -2,9 +2,22 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.test
import kotlinx.io.Buffer
+import kotlinx.rpc.protobuf.internal.ProtobufDecodingException
+import kotlinx.rpc.protobuf.internal.WireDecoder
+import kotlinx.rpc.protobuf.internal.WireEncoder
+import kotlinx.rpc.protobuf.internal.WireSize
+import kotlinx.rpc.protobuf.internal.WireType
+import kotlinx.rpc.protobuf.internal.checkForPlatformDecodeException
+import kotlinx.rpc.protobuf.internal.packedEnum
+import kotlinx.rpc.protobuf.internal.packedInt32
+import kotlinx.rpc.protobuf.internal.packedInt64
+import kotlinx.rpc.protobuf.internal.packedSInt32
+import kotlinx.rpc.protobuf.internal.packedSInt64
+import kotlinx.rpc.protobuf.internal.packedUInt32
+import kotlinx.rpc.protobuf.internal.packedUInt64
import kotlin.test.*
enum class TestPlatform {
diff --git a/grpc/grpc-core/src/commonTest/proto/all_primitives.proto b/protobuf/protobuf-core/src/commonTest/proto/all_primitives.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/all_primitives.proto
rename to protobuf/protobuf-core/src/commonTest/proto/all_primitives.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/enum.proto b/protobuf/protobuf-core/src/commonTest/proto/enum.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/enum.proto
rename to protobuf/protobuf-core/src/commonTest/proto/enum.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/nested.proto b/protobuf/protobuf-core/src/commonTest/proto/nested.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/nested.proto
rename to protobuf/protobuf-core/src/commonTest/proto/nested.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/oneof.proto b/protobuf/protobuf-core/src/commonTest/proto/oneof.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/oneof.proto
rename to protobuf/protobuf-core/src/commonTest/proto/oneof.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/presence_check.proto b/protobuf/protobuf-core/src/commonTest/proto/presence_check.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/presence_check.proto
rename to protobuf/protobuf-core/src/commonTest/proto/presence_check.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/recursive.proto b/protobuf/protobuf-core/src/commonTest/proto/recursive.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/recursive.proto
rename to protobuf/protobuf-core/src/commonTest/proto/recursive.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/repeated.proto b/protobuf/protobuf-core/src/commonTest/proto/repeated.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/repeated.proto
rename to protobuf/protobuf-core/src/commonTest/proto/repeated.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/sub_message.proto b/protobuf/protobuf-core/src/commonTest/proto/sub_message.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/sub_message.proto
rename to protobuf/protobuf-core/src/commonTest/proto/sub_message.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/submsg.proto b/protobuf/protobuf-core/src/commonTest/proto/submsg.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/submsg.proto
rename to protobuf/protobuf-core/src/commonTest/proto/submsg.proto
diff --git a/grpc/grpc-core/src/commonTest/proto/test_map.proto b/protobuf/protobuf-core/src/commonTest/proto/test_map.proto
similarity index 100%
rename from grpc/grpc-core/src/commonTest/proto/test_map.proto
rename to protobuf/protobuf-core/src/commonTest/proto/test_map.proto
diff --git a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.jvm.kt b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.jvm.kt
similarity index 97%
rename from grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.jvm.kt
rename to protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.jvm.kt
index 77095c84d..a899713ab 100644
--- a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.jvm.kt
+++ b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.jvm.kt
@@ -2,13 +2,12 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import com.google.protobuf.CodedInputStream
import com.google.protobuf.InvalidProtocolBufferException
import kotlinx.io.Buffer
import kotlinx.io.asInputStream
-import kotlinx.rpc.grpc.internal.readPackedVarInternal
internal class WireDecoderJvm(source: Buffer) : WireDecoder {
// there is no way to omit coping here
diff --git a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.jvm.kt b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.jvm.kt
similarity index 99%
rename from grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.jvm.kt
rename to protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.jvm.kt
index ffa4b2f3c..a0461a899 100644
--- a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.jvm.kt
+++ b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.jvm.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import com.google.protobuf.CodedOutputStream
import kotlinx.io.IOException
diff --git a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.jvm.kt b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.jvm.kt
similarity index 96%
rename from grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.jvm.kt
rename to protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.jvm.kt
index 71896a28e..038bb1f9b 100644
--- a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.jvm.kt
+++ b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.jvm.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import com.google.protobuf.CodedOutputStream.*
import kotlinx.rpc.internal.utils.InternalRpcApi
diff --git a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.jvm.kt b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.jvm.kt
similarity index 81%
rename from grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.jvm.kt
rename to protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.jvm.kt
index c8501d4f9..79ea1da70 100644
--- a/grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.jvm.kt
+++ b/protobuf/protobuf-core/src/jvmMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.jvm.kt
@@ -2,10 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
-
-import kotlinx.rpc.grpc.pb.WireDecoder
-import kotlinx.rpc.grpc.pb.WireDecoderJvm
+package kotlinx.rpc.protobuf.internal
internal actual fun WireDecoder.pushLimit(byteLen: Int): Int {
return (this as WireDecoderJvm).codedInputStream.pushLimit(byteLen)
diff --git a/protobuf/protobuf-core/src/jvmTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.jvm.kt b/protobuf/protobuf-core/src/jvmTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.jvm.kt
new file mode 100644
index 000000000..1199a0e5c
--- /dev/null
+++ b/protobuf/protobuf-core/src/jvmTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.jvm.kt
@@ -0,0 +1,7 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protobuf.test
+
+actual val testPlatform: TestPlatform = TestPlatform.Jvm
diff --git a/grpc/grpc-core/src/nativeInterop/cinterop/libprotowire.def b/protobuf/protobuf-core/src/nativeInterop/cinterop/libprotowire.def
similarity index 100%
rename from grpc/grpc-core/src/nativeInterop/cinterop/libprotowire.def
rename to protobuf/protobuf-core/src/nativeInterop/cinterop/libprotowire.def
diff --git a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.native.kt b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.native.kt
similarity index 98%
rename from grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.native.kt
rename to protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.native.kt
index f26b9da9a..55d161dc6 100644
--- a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireDecoder.native.kt
+++ b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireDecoder.native.kt
@@ -2,13 +2,11 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import kotlinx.cinterop.*
import kotlinx.collections.immutable.persistentListOf
import kotlinx.io.Buffer
-import kotlinx.rpc.grpc.internal.ZeroCopyInputSource
-import kotlinx.rpc.grpc.internal.readPackedVarInternal
import libprotowire.*
import kotlin.experimental.ExperimentalNativeApi
import kotlin.math.min
@@ -304,4 +302,4 @@ public actual fun WireDecoder(source: Buffer): WireDecoder = WireDecoderNative(s
public actual inline fun checkForPlatformDecodeException(block: () -> Unit) {
block()
-}
\ No newline at end of file
+}
diff --git a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.native.kt b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.native.kt
similarity index 98%
rename from grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.native.kt
rename to protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.native.kt
index de5266f30..e0321b2db 100644
--- a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireEncoder.native.kt
+++ b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireEncoder.native.kt
@@ -2,11 +2,10 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import kotlinx.cinterop.*
import kotlinx.io.Sink
-import kotlinx.rpc.grpc.internal.writeFully
import libprotowire.*
import kotlin.experimental.ExperimentalNativeApi
import kotlin.native.ref.createCleaner
@@ -40,9 +39,12 @@ internal class WireEncoderNative(private val sink: Sink) : WireEncoder {
}) ?: error("Failed to create proto wire encoder")
}
+ @Suppress("unused")
private val contextCleaner = createCleaner(context) {
it.dispose()
}
+
+ @Suppress("unused")
private val rawCleaner = createCleaner(raw) {
pw_encoder_delete(it)
}
diff --git a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.native.kt b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.native.kt
similarity index 95%
rename from grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.native.kt
rename to protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.native.kt
index 72ccaddd6..81adfae42 100644
--- a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/pb/WireSize.native.kt
+++ b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/WireSize.native.kt
@@ -4,7 +4,7 @@
@file:OptIn(ExperimentalForeignApi::class)
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.internal
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.rpc.internal.utils.InternalRpcApi
diff --git a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/ZeroCopyInputSource.kt b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/ZeroCopyInputSource.kt
similarity index 95%
rename from grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/ZeroCopyInputSource.kt
rename to protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/ZeroCopyInputSource.kt
index e20df0607..c5b3781d5 100644
--- a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/ZeroCopyInputSource.kt
+++ b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/ZeroCopyInputSource.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
+package kotlinx.rpc.protobuf.internal
import kotlinx.cinterop.*
import kotlinx.io.Buffer
@@ -11,7 +11,6 @@ import kotlinx.io.InternalIoApi
import kotlinx.io.UnsafeIoApi
import kotlinx.io.unsafe.UnsafeBufferOperations
-
/**
* Handles (almost) zero-copy input operations on a [Buffer], allowing efficient transfer of data
* without creating intermediate copies. This class provides mechanisms to iterate over
@@ -54,7 +53,7 @@ internal class ZeroCopyInputSource(private val inner: Buffer) : AutoCloseable {
// released by the buffer. this is done by releaseLatestReadSegement
// which releases the segment in the inner buffer.
private var latestReadSegementArray: Pinned? = null
- private var closed = false;
+ private var closed = false
/**
* Get access to a segment of continuous bytes in the underlying [Buffer].
@@ -83,15 +82,15 @@ internal class ZeroCopyInputSource(private val inner: Buffer) : AutoCloseable {
val segmentSize = end - start
outData.pointed.value = latestReadSegementArray!!.addressOf(start)
- outSize.pointed.value = segmentSize;
+ outSize.pointed.value = segmentSize
- byteCount += segmentSize;
+ byteCount += segmentSize
// we are not yet advancing the inner buffer head.
// this ensures that the segment array is not released by the buffer and remains valid
0
}
- return true;
+ return true
}
/**
@@ -115,7 +114,7 @@ internal class ZeroCopyInputSource(private val inner: Buffer) : AutoCloseable {
check(latestReadSegementArray != null) { "next() must be immediately before backUp()" }
val readBytes = releaseLatestReadSegment(count)
check(readBytes >= 0) { "backUp() must not be called more than the number of bytes that were read in next()" }
- byteCount -= count;
+ byteCount -= count
}
/**
@@ -155,7 +154,7 @@ internal class ZeroCopyInputSource(private val inner: Buffer) : AutoCloseable {
if (latestReadSegementArray != null) {
releaseLatestReadSegment()
}
- closed = true;
+ closed = true
}
/**
@@ -175,8 +174,9 @@ internal class ZeroCopyInputSource(private val inner: Buffer) : AutoCloseable {
// the return value of the readFromHead defines the number of bytes that are getting released in the underlying
// buffer.
UnsafeBufferOperations.readFromHead(inner.buffer) { arr, start, end ->
- check(latestReadSegementArray?.get() == arr) {
- "array to advance must be the SAME as the currArr, was there some access to the underlying buffer?" }
+ check(latestReadSegementArray?.get() === arr) {
+ "array to advance must be the SAME as the currArr, was there some access to the underlying buffer?"
+ }
// release the whole segmentSize - the backup count.
readBytes = end - start - backUpCount
// prevent the value from being negative.
@@ -185,7 +185,7 @@ internal class ZeroCopyInputSource(private val inner: Buffer) : AutoCloseable {
}
// remove tracking of the released segment
latestReadSegementArray?.unpin()
- latestReadSegementArray = null;
+ latestReadSegementArray = null
return readBytes
}
-}
\ No newline at end of file
+}
diff --git a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/bufferUnsafeExtensions.kt b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/bufferUnsafeExtensions.kt
similarity index 95%
rename from grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/bufferUnsafeExtensions.kt
rename to protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/bufferUnsafeExtensions.kt
index f1e1495ae..822155c02 100644
--- a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/bufferUnsafeExtensions.kt
+++ b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/bufferUnsafeExtensions.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
+package kotlinx.rpc.protobuf.internal
import kotlinx.cinterop.*
import kotlinx.io.InternalIoApi
@@ -27,4 +27,4 @@ internal fun Sink.writeFully(buffer: CPointer, offset: Long, length: Lo
size.toInt()
}
}
-}
\ No newline at end of file
+}
diff --git a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.native.kt b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.native.kt
similarity index 86%
rename from grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.native.kt
rename to protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.native.kt
index a45418e62..94ddd5825 100644
--- a/grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/readPacked.native.kt
+++ b/protobuf/protobuf-core/src/nativeMain/kotlin/kotlinx/rpc/protobuf/internal/readPacked.native.kt
@@ -4,11 +4,9 @@
@file:OptIn(ExperimentalForeignApi::class)
-package kotlinx.rpc.grpc.internal
+package kotlinx.rpc.protobuf.internal
import kotlinx.cinterop.ExperimentalForeignApi
-import kotlinx.rpc.grpc.pb.WireDecoder
-import kotlinx.rpc.grpc.pb.WireDecoderNative
import libprotowire.pw_decoder_bytes_until_limit
import libprotowire.pw_decoder_pop_limit
import libprotowire.pw_decoder_push_limit
diff --git a/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.native.kt b/protobuf/protobuf-core/src/nativeTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.native.kt
similarity index 84%
rename from grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.native.kt
rename to protobuf/protobuf-core/src/nativeTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.native.kt
index 23fb4eae1..3d760283d 100644
--- a/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.native.kt
+++ b/protobuf/protobuf-core/src/nativeTest/kotlin/kotlinx/rpc/protobuf/test/WireCodecTest.native.kt
@@ -2,6 +2,6 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
+package kotlinx.rpc.protobuf.test
actual val testPlatform: TestPlatform = TestPlatform.Native
diff --git a/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/ZeroCopyInputSourceTest.kt b/protobuf/protobuf-core/src/nativeTest/kotlin/kotlinx/rpc/protobuf/test/ZeroCopyInputSourceTest.kt
similarity index 99%
rename from grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/ZeroCopyInputSourceTest.kt
rename to protobuf/protobuf-core/src/nativeTest/kotlin/kotlinx/rpc/protobuf/test/ZeroCopyInputSourceTest.kt
index 234d0941a..2941da507 100644
--- a/grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/ZeroCopyInputSourceTest.kt
+++ b/protobuf/protobuf-core/src/nativeTest/kotlin/kotlinx/rpc/protobuf/test/ZeroCopyInputSourceTest.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.internal
+package kotlinx.rpc.protobuf.test
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.CPointerVar
@@ -15,6 +15,7 @@ import kotlinx.cinterop.ptr
import kotlinx.cinterop.usePinned
import kotlinx.cinterop.value
import kotlinx.io.Buffer
+import kotlinx.rpc.protobuf.internal.ZeroCopyInputSource
import platform.posix.memcpy
import kotlin.experimental.ExperimentalNativeApi
import kotlin.test.Test
@@ -327,4 +328,4 @@ private fun ZeroCopyInputSource.nextIntoArray(): ByteArray = memScoped {
memcpy(it.addressOf(0), data.value, size.value.toULong())
}
result
-}
\ No newline at end of file
+}
diff --git a/protoc-gen/build.gradle.kts b/protoc-gen/build.gradle.kts
index fbef3d641..3a2714048 100644
--- a/protoc-gen/build.gradle.kts
+++ b/protoc-gen/build.gradle.kts
@@ -2,47 +2,13 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
-
plugins {
alias(libs.plugins.conventions.jvm)
}
-group = "org.jetbrains.kotlinx"
-version = rootProject.libs.versions.kotlinx.rpc.get()
-
-dependencies {
- implementation(libs.protobuf.java)
-
- implementation(libs.slf4j.api)
- implementation(libs.logback.classic)
-
- testImplementation(libs.kotlin.test)
-}
-
-tasks.jar {
- manifest {
- attributes["Main-Class"] = "kotlinx.rpc.protobuf.MainKt"
- }
-
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- archiveClassifier = "all"
-
- // Protoc plugins are all fat jars basically (the ones built on jvm)
- // be really careful of what you put in the classpath here
- from(
- configurations.runtimeClasspath.map { prop ->
- prop.map { if (it.isDirectory()) it else zipTree(it) }
- }
- )
-}
-
-kotlin {
- explicitApi = ExplicitApiMode.Disabled
-}
-
-tasks.test {
- useJUnitPlatform()
+allprojects {
+ group = "org.jetbrains.kotlinx"
+ version = rootProject.libs.versions.kotlinx.rpc.get()
}
-logger.lifecycle("[Protoc Plugin] kotlinx.rpc project version: $version")
+logger.lifecycle("[Protoc Gen] kotlinx.rpc project version: $version")
diff --git a/grpc/grpc-core/src/jvmTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.jvm.kt b/protoc-gen/common/build.gradle.kts
similarity index 60%
rename from grpc/grpc-core/src/jvmTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.jvm.kt
rename to protoc-gen/common/build.gradle.kts
index 88e83bb57..b460b64b0 100644
--- a/grpc/grpc-core/src/jvmTest/kotlin/kotlinx/rpc/grpc/pb/WireCodecTest.jvm.kt
+++ b/protoc-gen/common/build.gradle.kts
@@ -2,6 +2,6 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.grpc.pb
-
-actual val testPlatform: TestPlatform = TestPlatform.Jvm
\ No newline at end of file
+plugins {
+ alias(libs.plugins.conventions.protoc.gen)
+}
diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt
new file mode 100644
index 000000000..47d63bcd7
--- /dev/null
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc.gen.core
+
+import kotlinx.rpc.protoc.gen.core.model.FieldDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FieldType
+import kotlinx.rpc.protoc.gen.core.model.FileDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FqName
+import kotlinx.rpc.protoc.gen.core.model.MessageDeclaration
+import kotlinx.rpc.protoc.gen.core.model.Model
+import kotlinx.rpc.protoc.gen.core.model.fullName
+import org.slf4j.Logger
+
+const val RPC_INTERNAL_PACKAGE_SUFFIX = "_rpc_internal"
+const val MSG_INTERNAL_SUFFIX = "Internal"
+const val PB_PKG = "kotlinx.rpc.protobuf.internal"
+const val INTERNAL_RPC_API_ANNO = "kotlinx.rpc.internal.utils.InternalRpcApi"
+const val WITH_CODEC_ANNO = "kotlinx.rpc.grpc.codec.WithCodec"
+
+abstract class AModelToKotlinCommonGenerator(
+ protected val model: Model,
+ protected val logger: Logger,
+) {
+ protected abstract fun CodeGenerator.generatePublicDeclaredEntities(fileDeclaration: FileDeclaration)
+ protected abstract fun CodeGenerator.generateInternalDeclaredEntities(fileDeclaration: FileDeclaration)
+
+ protected abstract val hasPublicGeneratedFiles: Boolean
+ protected abstract val hasInternalGeneratedFiles: Boolean
+
+ protected var currentPackage: FqName = FqName.Package.Root
+ protected val additionalPublicImports = mutableSetOf()
+ protected val additionalInternalImports = mutableSetOf()
+
+ fun generateKotlinFiles(): List {
+ return model.files.flatMap { it.generateKotlinFiles() }
+ }
+
+ private fun FileDeclaration.generateKotlinFiles(): List {
+ additionalPublicImports.clear()
+ additionalInternalImports.clear()
+
+ return listOfNotNull(
+ if (hasPublicGeneratedFiles) generatePublicKotlinFile() else null,
+ if (hasInternalGeneratedFiles) generateInternalKotlinFile() else null,
+ )
+ }
+
+ private fun FileDeclaration.generatePublicKotlinFile(): FileGenerator {
+ currentPackage = packageName
+
+ return file(logger = logger) {
+ filename = this@generatePublicKotlinFile.name
+ packageName = this@generatePublicKotlinFile.packageName.safeFullName()
+ packagePath = this@generatePublicKotlinFile.packageName.safeFullName()
+
+ dependencies.forEach { dependency ->
+ importPackage(dependency.packageName.safeFullName())
+ }
+
+ fileOptIns = listOf("ExperimentalRpcApi::class", "InternalRpcApi::class")
+
+ generatePublicDeclaredEntities(this@generatePublicKotlinFile)
+
+ import("kotlinx.rpc.internal.utils.*")
+ import("kotlinx.coroutines.flow.*")
+
+ additionalPublicImports.forEach {
+ import(it)
+ }
+ }
+ }
+
+ private fun FileDeclaration.generateInternalKotlinFile(): FileGenerator {
+ currentPackage = packageName
+
+ return file(logger = logger) {
+ filename = this@generateInternalKotlinFile.name
+ packageName = this@generateInternalKotlinFile.packageName.safeFullName()
+ packagePath =
+ this@generateInternalKotlinFile.packageName.safeFullName()
+ .packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX)
+
+ fileOptIns = listOf("ExperimentalRpcApi::class", "$INTERNAL_RPC_API_ANNO::class")
+
+ dependencies.forEach { dependency ->
+ importPackage(dependency.packageName.safeFullName())
+ }
+
+ generateInternalDeclaredEntities(this@generateInternalKotlinFile)
+
+ import("$PB_PKG.*")
+ import("kotlinx.rpc.internal.utils.*")
+ import("kotlinx.coroutines.flow.*")
+
+ additionalInternalImports.forEach {
+ import(it)
+ }
+ }
+ }
+
+ protected fun FieldDeclaration.typeFqName(): String {
+ return when (type) {
+ is FieldType.Message -> {
+ type.dec.value.name.safeFullName()
+ }
+
+ is FieldType.Enum -> type.dec.name.safeFullName()
+
+ is FieldType.OneOf -> type.dec.name.safeFullName()
+
+ is FieldType.IntegralType -> {
+ type.fqName.simpleName
+ }
+
+ is FieldType.List -> {
+ val fqValue = when (val value = type.value) {
+ is FieldType.Message -> value.dec.value.name
+ is FieldType.IntegralType -> value.fqName
+ else -> error("Unsupported type: $value")
+ }
+
+ "List<${fqValue.safeFullName()}>"
+ }
+
+ is FieldType.Map -> {
+ val entry = type.entry
+
+ val fqKey = when (val key = entry.key) {
+ is FieldType.Message -> key.dec.value.name
+ is FieldType.IntegralType -> key.fqName
+ else -> error("Unsupported type: $key")
+ }
+
+ val fqValue = when (val value = entry.value) {
+ is FieldType.Message -> value.dec.value.name
+ is FieldType.IntegralType -> value.fqName
+ else -> error("Unsupported type: $value")
+ }
+
+ "Map<${fqKey.safeFullName()}, ${fqValue.safeFullName()}>"
+ }
+
+ }.withNullability(nullable)
+ }
+
+ protected fun String.wrapInFlowIf(condition: Boolean): String {
+ return if (condition) "Flow<$this>" else this
+ }
+
+ protected fun FqName.safeFullName(classSuffix: String = ""): String {
+ importRootDeclarationIfNeeded(this)
+
+ return fullName(classSuffix)
+ }
+
+ protected fun importRootDeclarationIfNeeded(
+ declaration: FqName,
+ nameToImport: String = declaration.simpleName,
+ internalOnly: Boolean = false,
+ ) {
+ if (declaration.parent == FqName.Package.Root && currentPackage != FqName.Package.Root && nameToImport.isNotBlank()) {
+ additionalInternalImports.add(nameToImport)
+ if (!internalOnly) {
+ additionalPublicImports.add(nameToImport)
+ }
+ }
+ }
+
+ protected fun FieldType.Message.internalConstructor() =
+ dec.value.internalClassFullName() + "()"
+
+ protected fun MessageDeclaration.internalClassFullName(): String {
+ return name.safeFullName(MSG_INTERNAL_SUFFIX)
+ }
+
+ protected fun MessageDeclaration.internalClassName(): String {
+ return name.simpleName + MSG_INTERNAL_SUFFIX
+ }
+
+ protected fun String.withNullability(nullable: Boolean): String {
+ return "$this${if (nullable) "?" else ""}"
+ }
+
+ protected fun String.packageNameSuffixed(suffix: String): String {
+ return if (isEmpty()) suffix else "$this.$suffix"
+ }
+}
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/CodeGenerator.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt
similarity index 98%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/CodeGenerator.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt
index cd8ddfbb5..ea43f0283 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/CodeGenerator.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf
+package kotlinx.rpc.protoc.gen.core
import org.slf4j.Logger
import org.slf4j.helpers.NOPLogger
@@ -67,7 +67,7 @@ open class CodeGenerator(
CodeGenerator("$indent$ONE_INDENT", builder, logger).block()
}
- internal fun scope(
+ fun scope(
prefix: String,
suffix: String = "",
nlAfterClosed: Boolean = true,
@@ -79,7 +79,7 @@ open class CodeGenerator(
scopeWithSuffix(suffix, openingBracket, nlAfterClosed, paramDecl, block)
}
- internal fun ifBranch(
+ fun ifBranch(
prefix: String = "",
condition: String,
ifBlock: (CodeGenerator.() -> Unit),
@@ -94,14 +94,14 @@ open class CodeGenerator(
scopeWithSuffix(block = elseBlock)
}
- internal fun whileBlock(
+ fun whileBlock(
condition: String,
block: (CodeGenerator.() -> Unit),
) {
scope("while ($condition)", block = block)
}
- internal fun whenBlock(
+ fun whenBlock(
condition: String? = null,
prefix: String = "",
block: (CodeGenerator.() -> Unit),
@@ -111,14 +111,13 @@ open class CodeGenerator(
scope("${pre}when$cond", block = block)
}
- internal fun whenCase(
+ fun whenCase(
condition: String,
block: (CodeGenerator.() -> Unit),
) {
scope("$condition ->", block = block)
}
-
private fun scopeWithSuffix(
suffix: String = "",
openingBracket: Boolean = true,
@@ -346,7 +345,7 @@ class FileGenerator(
newLine()
}
- var packageName = packageName
+ val packageName = packageName
if (packageName != null && packageName.isNotEmpty()) {
appendLine("package $packageName")
}
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/RpcProtobufPlugin.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt
similarity index 88%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/RpcProtobufPlugin.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt
index 4ff31adbf..8b3a2bc79 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/RpcProtobufPlugin.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf
+package kotlinx.rpc.protoc.gen.core
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.LoggerContext
@@ -12,12 +12,13 @@ import ch.qos.logback.core.FileAppender
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.Feature
+import kotlinx.rpc.protoc.gen.core.model.Model
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.slf4j.helpers.NOPLogger
import java.io.File
-class RpcProtobufPlugin {
+abstract class ProtocGenPlugin {
companion object {
private const val DEBUG_OUTPUT_OPTION = "debugOutput"
}
@@ -54,7 +55,7 @@ class RpcProtobufPlugin {
debugOutput = parameters[DEBUG_OUTPUT_OPTION]
- val files = input.generateKotlinCommonFiles()
+ val files = input.runGeneration()
.map { file ->
CodeGeneratorResponse.File.newBuilder()
.apply {
@@ -81,10 +82,9 @@ class RpcProtobufPlugin {
.build()
}
- private fun CodeGeneratorRequest.generateKotlinCommonFiles(): List {
- val model = this.toModel()
- val fileGenerator = ModelToKotlinCommonGenerator(model, logger)
- return fileGenerator.generateKotlinFiles()
+ private fun CodeGeneratorRequest.runGeneration(): List {
+ return generateKotlinByModel(this.toModel(), logger)
}
-}
+ protected abstract fun generateKotlinByModel(model: Model, logger: Logger): List
+}
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/codeRequestToModel.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt
similarity index 95%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/codeRequestToModel.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt
index 6f85829b8..1c15b453c 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/codeRequestToModel.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt
@@ -2,12 +2,21 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf
+package kotlinx.rpc.protoc.gen.core
import com.google.protobuf.DescriptorProtos
import com.google.protobuf.Descriptors
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest
-import kotlinx.rpc.protobuf.model.*
+import kotlinx.rpc.protoc.gen.core.model.EnumDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FieldDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FieldType
+import kotlinx.rpc.protoc.gen.core.model.FileDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FqName
+import kotlinx.rpc.protoc.gen.core.model.MessageDeclaration
+import kotlinx.rpc.protoc.gen.core.model.MethodDeclaration
+import kotlinx.rpc.protoc.gen.core.model.Model
+import kotlinx.rpc.protoc.gen.core.model.OneOfDeclaration
+import kotlinx.rpc.protoc.gen.core.model.ServiceDeclaration
private val nameCache = mutableMapOf()
private val modelCache = mutableMapOf()
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/FieldType.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt
similarity index 98%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/FieldType.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt
index c2de6b18d..296bf640b 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/FieldType.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf.model
+package kotlinx.rpc.protoc.gen.core.model
enum class WireType {
VARINT,
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/FqName.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FqName.kt
similarity index 96%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/FqName.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FqName.kt
index f6f470faf..c06e919a1 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/FqName.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FqName.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf.model
+package kotlinx.rpc.protoc.gen.core.model
sealed interface FqName {
val simpleName: String
@@ -53,7 +53,7 @@ sealed interface FqName {
}
}
-internal fun FqName.fullName(classSuffix: String = ""): String {
+fun FqName.fullName(classSuffix: String = ""): String {
val parentName = parent
val name = if (this is FqName.Declaration) "$simpleName$classSuffix" else simpleName
return when {
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/NameResolver.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/NameResolver.kt
similarity index 99%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/NameResolver.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/NameResolver.kt
index 8596245e8..481024ea6 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/NameResolver.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/NameResolver.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf.model
+package kotlinx.rpc.protoc.gen.core.model
import org.slf4j.Logger
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/model.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt
similarity index 98%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/model.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt
index dfe05671b..f60d463a9 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/model/model.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt
@@ -2,7 +2,7 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf.model
+package kotlinx.rpc.protoc.gen.core.model
import com.google.protobuf.Descriptors
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/Main.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/runGenerator.kt
similarity index 83%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/Main.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/runGenerator.kt
index 9671859cd..e75cacc43 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/Main.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/runGenerator.kt
@@ -2,14 +2,13 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf
+package kotlinx.rpc.protoc.gen.core
import com.google.protobuf.compiler.PluginProtos
-fun main() {
+fun runGenerator(plugin: ProtocGenPlugin) {
val inputBytes = System.`in`.readBytes()
val request = PluginProtos.CodeGeneratorRequest.parseFrom(inputBytes)
- val plugin = RpcProtobufPlugin()
val output: PluginProtos.CodeGeneratorResponse = plugin.run(request)
output.writeTo(System.out)
}
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/utils.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/utils.kt
similarity index 85%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/utils.kt
rename to protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/utils.kt
index 2b44fdcac..ebb718224 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/utils.kt
+++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/utils.kt
@@ -2,8 +2,8 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf
+package kotlinx.rpc.protoc.gen.core
internal fun String.decapitalize(): String {
return this.replaceFirstChar { it.lowercase() }
-}
\ No newline at end of file
+}
diff --git a/protoc-gen/src/test/kotlin/kotlinx/rpc/protobuf/test/FqNameTest.kt b/protoc-gen/common/src/test/kotlin/kotlinx/rpc/protoc/gen/test/FqNameTest.kt
similarity index 87%
rename from protoc-gen/src/test/kotlin/kotlinx/rpc/protobuf/test/FqNameTest.kt
rename to protoc-gen/common/src/test/kotlin/kotlinx/rpc/protoc/gen/test/FqNameTest.kt
index 4cc871c47..575c0dfab 100644
--- a/protoc-gen/src/test/kotlin/kotlinx/rpc/protobuf/test/FqNameTest.kt
+++ b/protoc-gen/common/src/test/kotlin/kotlinx/rpc/protoc/gen/test/FqNameTest.kt
@@ -2,11 +2,11 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf.test
+package kotlinx.rpc.protoc.gen.test
-import kotlinx.rpc.protobuf.model.FqName
-import kotlinx.rpc.protobuf.model.asParentsAndSimpleName
-import kotlinx.rpc.protobuf.model.fullName
+import kotlinx.rpc.protoc.gen.core.model.FqName
+import kotlinx.rpc.protoc.gen.core.model.asParentsAndSimpleName
+import kotlinx.rpc.protoc.gen.core.model.fullName
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/protoc-gen/src/test/kotlin/kotlinx/rpc/protobuf/test/NameResolverTest.kt b/protoc-gen/common/src/test/kotlin/kotlinx/rpc/protoc/gen/test/NameResolverTest.kt
similarity index 98%
rename from protoc-gen/src/test/kotlin/kotlinx/rpc/protobuf/test/NameResolverTest.kt
rename to protoc-gen/common/src/test/kotlin/kotlinx/rpc/protoc/gen/test/NameResolverTest.kt
index 8196c1ca5..93d2ea674 100644
--- a/protoc-gen/src/test/kotlin/kotlinx/rpc/protobuf/test/NameResolverTest.kt
+++ b/protoc-gen/common/src/test/kotlin/kotlinx/rpc/protoc/gen/test/NameResolverTest.kt
@@ -2,10 +2,10 @@
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
-package kotlinx.rpc.protobuf.test
+package kotlinx.rpc.protoc.gen.test
-import kotlinx.rpc.protobuf.model.FqName
-import kotlinx.rpc.protobuf.model.NameResolver
+import kotlinx.rpc.protoc.gen.core.model.FqName
+import kotlinx.rpc.protoc.gen.core.model.NameResolver
import org.junit.jupiter.api.assertThrows
import org.slf4j.helpers.NOPLogger
import kotlin.test.Test
diff --git a/protoc-gen/grpc/build.gradle.kts b/protoc-gen/grpc/build.gradle.kts
new file mode 100644
index 000000000..f4322aa09
--- /dev/null
+++ b/protoc-gen/grpc/build.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+ alias(libs.plugins.conventions.protoc.gen)
+}
+
+tasks.jar {
+ manifest {
+ attributes["Main-Class"] = "kotlinx.rpc.protoc.gen.grpc.MainKt"
+ }
+}
+
+dependencies {
+ implementation(projects.common)
+}
diff --git a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt
new file mode 100644
index 000000000..85dcb7274
--- /dev/null
+++ b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc.gen.grpc
+
+import kotlinx.rpc.protoc.gen.core.FileGenerator
+import kotlinx.rpc.protoc.gen.core.ProtocGenPlugin
+import kotlinx.rpc.protoc.gen.core.model.Model
+import org.slf4j.Logger
+
+object GrpcProtocGenPlugin : ProtocGenPlugin() {
+ override fun generateKotlinByModel(
+ model: Model,
+ logger: Logger,
+ ): List {
+ return ModelToGrpcKotlinCommonGenerator(model, logger).generateKotlinFiles()
+ }
+}
diff --git a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/Main.kt b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/Main.kt
new file mode 100644
index 000000000..5bdfedcae
--- /dev/null
+++ b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/Main.kt
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc.gen.grpc
+
+import kotlinx.rpc.protoc.gen.core.runGenerator
+
+fun main() {
+ runGenerator(GrpcProtocGenPlugin)
+}
diff --git a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt
new file mode 100644
index 000000000..e8d966404
--- /dev/null
+++ b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc.gen.grpc
+
+import kotlinx.rpc.protoc.gen.core.AModelToKotlinCommonGenerator
+import kotlinx.rpc.protoc.gen.core.CodeGenerator
+import kotlinx.rpc.protoc.gen.core.model.FileDeclaration
+import kotlinx.rpc.protoc.gen.core.model.Model
+import kotlinx.rpc.protoc.gen.core.model.ServiceDeclaration
+import org.slf4j.Logger
+
+class ModelToGrpcKotlinCommonGenerator(
+ model: Model,
+ logger: Logger,
+) : AModelToKotlinCommonGenerator(model, logger) {
+ override val hasPublicGeneratedFiles: Boolean = model.files.any { it.serviceDeclarations.isNotEmpty() }
+ override val hasInternalGeneratedFiles: Boolean = false
+
+ override fun CodeGenerator.generatePublicDeclaredEntities(fileDeclaration: FileDeclaration) {
+ fileDeclaration.serviceDeclarations.forEach { generatePublicService(it) }
+ }
+
+ override fun CodeGenerator.generateInternalDeclaredEntities(fileDeclaration: FileDeclaration) { }
+
+ @Suppress("detekt.LongMethod")
+ private fun CodeGenerator.generatePublicService(service: ServiceDeclaration) {
+ code("@kotlinx.rpc.grpc.annotations.Grpc")
+ clazz(service.name.simpleName, declarationType = CodeGenerator.DeclarationType.Interface) {
+ service.methods.forEach { method ->
+ val inputType = method.inputType
+ val outputType = method.outputType
+ function(
+ name = method.name,
+ modifiers = if (method.dec.isServerStreaming) "" else "suspend",
+ args = "message: ${inputType.name.safeFullName().wrapInFlowIf(method.dec.isClientStreaming)}",
+ returnType = outputType.name.safeFullName().wrapInFlowIf(method.dec.isServerStreaming),
+ )
+ }
+ }
+ }
+}
diff --git a/protoc-gen/protobuf/build.gradle.kts b/protoc-gen/protobuf/build.gradle.kts
new file mode 100644
index 000000000..0d6be072d
--- /dev/null
+++ b/protoc-gen/protobuf/build.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+ alias(libs.plugins.conventions.protoc.gen)
+}
+
+tasks.jar {
+ manifest {
+ attributes["Main-Class"] = "kotlinx.rpc.protoc.gen.MainKt"
+ }
+}
+
+dependencies {
+ implementation(projects.common)
+}
diff --git a/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/Main.kt b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/Main.kt
new file mode 100644
index 000000000..24d96f54a
--- /dev/null
+++ b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/Main.kt
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc.gen
+
+import kotlinx.rpc.protoc.gen.core.runGenerator
+
+fun main() {
+ runGenerator(ProtobufProtocGenPlugin)
+}
diff --git a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/ModelToKotlinCommonGenerator.kt b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt
similarity index 77%
rename from protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/ModelToKotlinCommonGenerator.kt
rename to protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt
index fb13b0873..e48b73d86 100644
--- a/protoc-gen/src/main/kotlin/kotlinx/rpc/protobuf/ModelToKotlinCommonGenerator.kt
+++ b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt
@@ -4,101 +4,39 @@
@file:Suppress("detekt.all")
-package kotlinx.rpc.protobuf
-
-import kotlinx.rpc.protobuf.CodeGenerator.DeclarationType
-import kotlinx.rpc.protobuf.model.*
+package kotlinx.rpc.protoc.gen
+
+import kotlinx.rpc.protoc.gen.core.AModelToKotlinCommonGenerator
+import kotlinx.rpc.protoc.gen.core.CodeGenerator
+import kotlinx.rpc.protoc.gen.core.INTERNAL_RPC_API_ANNO
+import kotlinx.rpc.protoc.gen.core.PB_PKG
+import kotlinx.rpc.protoc.gen.core.WITH_CODEC_ANNO
+import kotlinx.rpc.protoc.gen.core.model.EnumDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FieldDeclaration
+import kotlinx.rpc.protoc.gen.core.model.FieldType
+import kotlinx.rpc.protoc.gen.core.model.FileDeclaration
+import kotlinx.rpc.protoc.gen.core.model.MessageDeclaration
+import kotlinx.rpc.protoc.gen.core.model.Model
+import kotlinx.rpc.protoc.gen.core.model.OneOfDeclaration
+import kotlinx.rpc.protoc.gen.core.model.WireType
import org.slf4j.Logger
-private const val RPC_INTERNAL_PACKAGE_SUFFIX = "_rpc_internal"
-private const val MSG_INTERNAL_SUFFIX = "Internal"
-private const val PB_PKG = "kotlinx.rpc.grpc.pb"
-private const val INTERNAL_RPC_API_ANNO = "kotlinx.rpc.internal.utils.InternalRpcApi"
-private const val WITH_CODEC_ANNO = "kotlinx.rpc.grpc.codec.WithCodec"
-
-class ModelToKotlinCommonGenerator(
- private val model: Model,
- private val logger: Logger,
-) {
- fun generateKotlinFiles(): List {
- return model.files.flatMap { it.generateKotlinFiles() }
- }
-
- private fun FileDeclaration.generateKotlinFiles(): List {
- additionalPublicImports.clear()
- additionalInternalImports.clear()
-
- return listOf(
- generatePublicKotlinFile(),
- generateInternalKotlinFile(),
- )
+class ModelToProtobufKotlinCommonGenerator(
+ model: Model,
+ logger: Logger,
+) : AModelToKotlinCommonGenerator(model, logger) {
+ override val hasPublicGeneratedFiles: Boolean = model.files.any {
+ it.enumDeclarations.isNotEmpty() || it.messageDeclarations.isNotEmpty()
}
- private var currentPackage: FqName = FqName.Package.Root
-
- private fun FileDeclaration.generatePublicKotlinFile(): FileGenerator {
- currentPackage = packageName
-
- return file(logger = logger) {
- filename = this@generatePublicKotlinFile.name
- packageName = this@generatePublicKotlinFile.packageName.safeFullName()
- packagePath = this@generatePublicKotlinFile.packageName.safeFullName()
-
- dependencies.forEach { dependency ->
- importPackage(dependency.packageName.safeFullName())
- }
-
- fileOptIns = listOf("ExperimentalRpcApi::class", "InternalRpcApi::class")
-
- generatePublicDeclaredEntities(this@generatePublicKotlinFile)
-
- import("kotlinx.rpc.internal.utils.*")
- import("kotlinx.coroutines.flow.*")
-
- additionalPublicImports.forEach {
- import(it)
- }
- }
- }
-
- private fun FileDeclaration.generateInternalKotlinFile(): FileGenerator {
- currentPackage = packageName
-
- return file(logger = logger) {
- filename = this@generateInternalKotlinFile.name
- packageName = this@generateInternalKotlinFile.packageName.safeFullName()
- packagePath =
- this@generateInternalKotlinFile.packageName.safeFullName()
- .packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX)
-
- fileOptIns = listOf("ExperimentalRpcApi::class", "$INTERNAL_RPC_API_ANNO::class")
-
- dependencies.forEach { dependency ->
- importPackage(dependency.packageName.safeFullName())
- }
+ override val hasInternalGeneratedFiles: Boolean = hasPublicGeneratedFiles
- generateInternalDeclaredEntities(this@generateInternalKotlinFile)
-
- import("kotlinx.rpc.grpc.pb.*")
- import("kotlinx.rpc.internal.utils.*")
- import("kotlinx.coroutines.flow.*")
-
- additionalInternalImports.forEach {
- import(it)
- }
- }
- }
-
- private val additionalPublicImports = mutableSetOf()
- private val additionalInternalImports = mutableSetOf()
-
- private fun CodeGenerator.generatePublicDeclaredEntities(fileDeclaration: FileDeclaration) {
+ override fun CodeGenerator.generatePublicDeclaredEntities(fileDeclaration: FileDeclaration) {
fileDeclaration.messageDeclarations.forEach { generatePublicMessage(it) }
fileDeclaration.enumDeclarations.forEach { generatePublicEnum(it) }
- fileDeclaration.serviceDeclarations.forEach { generatePublicService(it) }
}
- private fun CodeGenerator.generateInternalDeclaredEntities(fileDeclaration: FileDeclaration) {
+ override fun CodeGenerator.generateInternalDeclaredEntities(fileDeclaration: FileDeclaration) {
generateInternalMessageEntities(fileDeclaration.messageDeclarations)
val allEnums =
@@ -136,7 +74,7 @@ class ModelToKotlinCommonGenerator(
clazz(
name = declaration.name.simpleName,
- declarationType = DeclarationType.Interface,
+ declarationType = CodeGenerator.DeclarationType.Interface,
annotations = listOf("@$WITH_CODEC_ANNO(${declaration.internalClassFullName()}.CODEC::class)")
) {
declaration.fields().forEach { (fieldDeclaration, _) ->
@@ -158,7 +96,7 @@ class ModelToKotlinCommonGenerator(
generatePublicEnum(enum)
}
- clazz("", modifiers = "companion", declarationType = DeclarationType.Object)
+ clazz("", modifiers = "companion", declarationType = CodeGenerator.DeclarationType.Object)
}
}
@@ -179,7 +117,7 @@ class ModelToKotlinCommonGenerator(
clazz(
name = internalClassName,
annotations = annotations,
- declarationType = DeclarationType.Class,
+ declarationType = CodeGenerator.DeclarationType.Class,
superTypes = superTypes,
) {
@@ -195,7 +133,9 @@ class ModelToKotlinCommonGenerator(
}
field.type is FieldType.Message ->
- "by MsgFieldDelegate(PresenceIndices.${field.name}) { ${field.type.dec.value.internalClassFullName()}() }"
+ "by MsgFieldDelegate(PresenceIndices.${field.name}) { " +
+ "${(field.type as FieldType.Message).dec.value.internalClassFullName()}() " +
+ "}"
else -> {
val fieldPresence = if (field.presenceIdx != null) "PresenceIndices.${field.name}" else ""
@@ -242,7 +182,7 @@ class ModelToKotlinCommonGenerator(
function("encode", modifiers = "override", args = "value: $msgFqName", returnType = sourceFqName) {
code("val buffer = $bufferFqName()")
code("val encoder = $PB_PKG.WireEncoder(buffer)")
- scope("$PB_PKG.checkForPlatformEncodeException", nlAfterClosed = false) {
+ scope("${PB_PKG}.checkForPlatformEncodeException", nlAfterClosed = false) {
code("value.asInternal().encodeWith(encoder)")
}
code("encoder.flush()")
@@ -252,7 +192,7 @@ class ModelToKotlinCommonGenerator(
function("decode", modifiers = "override", args = "stream: $sourceFqName", returnType = msgFqName) {
scope("$PB_PKG.WireDecoder(stream as $bufferFqName).use") {
code("val msg = ${declaration.internalClassFullName()}()")
- scope("$PB_PKG.checkForPlatformDecodeException", nlAfterClosed = false) {
+ scope("${PB_PKG}.checkForPlatformDecodeException", nlAfterClosed = false) {
code("${declaration.internalClassFullName()}.decodeWith(msg, it)")
}
code("msg.checkRequiredFields()")
@@ -334,12 +274,13 @@ class ModelToKotlinCommonGenerator(
val variantName = "${fieldType.dec.name.safeFullName()}.${variant.name}"
if (variant.type is FieldType.Message) {
// in case of a message, we must construct an empty message before reading the message
+ val message = variant.type as FieldType.Message
readMatchCase(
field = variant,
lvalue = "field.value",
beforeValueDecoding = {
beforeValueDecoding()
- scope("val field = ($lvalue as? $variantName) ?: $variantName(${variant.type.internalConstructor()}).also") {
+ scope("val field = ($lvalue as? $variantName) ?: $variantName(${message.internalConstructor()}).also") {
// write the constructed oneof variant to the field
code("$lvalue = it")
}
@@ -488,10 +429,10 @@ class ModelToKotlinCommonGenerator(
packedWithFixedSize: Boolean,
) {
var encFunc = type.decodeEncodeFuncName()
- when (val fieldType = type) {
+ when (type) {
is FieldType.IntegralType -> code("encoder.write${encFunc!!}(fieldNr = $number, value = $valueVar)")
is FieldType.List -> {
- encFunc = fieldType.value.decodeEncodeFuncName()
+ encFunc = type.value.decodeEncodeFuncName()
when {
isPacked && packedWithFixedSize ->
code("encoder.writePacked${encFunc!!}(fieldNr = $number, value = $valueVar)")
@@ -503,12 +444,12 @@ class ModelToKotlinCommonGenerator(
})"
)
- fieldType.value is FieldType.Message -> scope("$valueVar.forEach") {
+ type.value is FieldType.Message -> scope("$valueVar.forEach") {
code("encoder.writeMessage(fieldNr = ${number}, value = it.asInternal()) { encodeWith(it) }")
}
else -> {
- require(encFunc != null) { "No encode function for list type: $fieldType" }
+ require(encFunc != null) { "No encode function for list type: $type" }
scope("$valueVar.forEach") {
code("encoder.write${encFunc}($number, it)")
}
@@ -519,8 +460,8 @@ class ModelToKotlinCommonGenerator(
is FieldType.Enum -> code("encoder.write${encFunc!!}(fieldNr = $number, value = ${valueVar}.number)")
is FieldType.OneOf -> whenBlock("val value = $valueVar") {
- fieldType.dec.variants.forEach { variant ->
- whenCase("is ${fieldType.dec.name.safeFullName()}.${variant.name}") {
+ type.dec.variants.forEach { variant ->
+ whenCase("is ${type.dec.name.safeFullName()}.${variant.name}") {
generateEncodeFieldValue(variant, "value.value")
}
}
@@ -528,11 +469,11 @@ class ModelToKotlinCommonGenerator(
is FieldType.Map -> {
scope("$valueVar.forEach", paramDecl = "kEntry ->") {
- generateMapConstruction(fieldType, "kEntry.key", "kEntry.value")
+ generateMapConstruction(type, "kEntry.key", "kEntry.value")
scope(".also", paramDecl = "entry ->") {
generateEncodeFieldValue(
valueVar = "entry",
- type = FieldType.Message(lazy { fieldType.entry.dec }),
+ type = FieldType.Message(lazy { type.entry.dec }),
number = number, isPacked = false, packedWithFixedSize = false
)
}
@@ -584,7 +525,7 @@ class ModelToKotlinCommonGenerator(
requiredFields.forEach { field ->
ifBranch(condition = "!presenceMask[${field.presenceIdx}]", ifBlock = {
- code("throw $PB_PKG.ProtobufDecodingException.missingRequiredField(\"${declaration.name.simpleName}\", \"${field.name}\")")
+ code("throw ${PB_PKG}.ProtobufDecodingException.missingRequiredField(\"${declaration.name.simpleName}\", \"${field.name}\")")
})
}
@@ -604,7 +545,8 @@ class ModelToKotlinCommonGenerator(
scope("${field.name}?.also") {
whenBlock {
messageVariants.forEach { variant ->
- val variantClassName = "${field.type.dec.name.safeFullName()}.${variant.name}"
+ val variantClassName =
+ "${(field.type as FieldType.OneOf).dec.name.safeFullName()}.${variant.name}"
whenCase("it is $variantClassName") {
code("it.value.asInternal().checkRequiredFields()")
}
@@ -633,7 +575,6 @@ class ModelToKotlinCommonGenerator(
code("it.asInternal().checkRequiredFields()")
}
}
-
}
private fun CodeGenerator.generateInternalComputeSize(declaration: MessageDeclaration) {
@@ -685,8 +626,10 @@ class ModelToKotlinCommonGenerator(
}
}
-
- private fun CodeGenerator.generateFieldComputeSizeCall(field: FieldDeclaration, variable: String) {
+ private fun CodeGenerator.generateFieldComputeSizeCall(
+ field: FieldDeclaration,
+ variable: String,
+ ) {
val valueSize by lazy { field.type.valueSizeCall(variable, field.number, field.dec.isPacked) }
val tagSize = tagSizeCall(field.number, field.type.wireType)
@@ -704,14 +647,14 @@ class ModelToKotlinCommonGenerator(
is FieldType.Map -> {
scope("result += ${field.name}.entries.sumOf", paramDecl = "kEntry ->") {
- generateMapConstruction(field.type, "kEntry.key", "kEntry.value")
+ generateMapConstruction(field.type as FieldType.Map, "kEntry.key", "kEntry.value")
code("._size")
}
}
is FieldType.OneOf -> whenBlock("val value = $variable") {
- field.type.dec.variants.forEach { variant ->
- val variantName = "${field.type.dec.name.safeFullName()}.${variant.name}"
+ (field.type as FieldType.OneOf).dec.variants.forEach { variant ->
+ val variantName = "${(field.type as FieldType.OneOf).dec.name.safeFullName()}.${variant.name}"
whenCase("is $variantName") {
generateFieldComputeSizeCall(variant, "value.value")
}
@@ -736,7 +679,11 @@ class ModelToKotlinCommonGenerator(
}
}
- private fun CodeGenerator.generateMapConstruction(map: FieldType.Map, keyVar: String, valueVar: String) {
+ private fun CodeGenerator.generateMapConstruction(
+ map: FieldType.Map,
+ keyVar: String,
+ valueVar: String,
+ ) {
val entryClass = map.entry.dec.internalClassFullName()
scope("$entryClass().apply", nlAfterClosed = false) {
code("key = $keyVar")
@@ -745,7 +692,7 @@ class ModelToKotlinCommonGenerator(
}
private fun FieldType.valueSizeCall(variable: String, number: Int, isPacked: Boolean = false): String {
- val sizeFunName = decodeEncodeFuncName()?.decapitalize()
+ val sizeFunName = decodeEncodeFuncName()?.replaceFirstChar { it.lowercase() }
val sizeFunc = "$PB_PKG.WireSize.$sizeFunName($variable)"
return when (this) {
@@ -772,6 +719,7 @@ class ModelToKotlinCommonGenerator(
return "$PB_PKG.WireSize.tag($number, $PB_PKG.WireType.$wireType)"
}
+ @Suppress("SameParameterValue")
private fun int32SizeCall(number: String): String {
return "$PB_PKG.WireSize.int32($number)"
}
@@ -819,59 +767,10 @@ class ModelToKotlinCommonGenerator(
return "${name}: ${typeFqName()}"
}
- private fun FieldDeclaration.typeFqName(): String {
- return when (type) {
- is FieldType.Message -> {
- type.dec.value.name.safeFullName()
- }
-
- is FieldType.Enum -> type.dec.name.safeFullName()
-
- is FieldType.OneOf -> type.dec.name.safeFullName()
-
- is FieldType.IntegralType -> {
- type.fqName.simpleName
- }
-
- is FieldType.List -> {
- val fqValue = when (val value = type.value) {
- is FieldType.Message -> value.dec.value.name
- is FieldType.IntegralType -> value.fqName
- else -> error("Unsupported type: $value")
- }
-
- "List<${fqValue.safeFullName()}>"
- }
-
- is FieldType.Map -> {
- val entry = type.entry
-
- val fqKey = when (val key = entry.key) {
- is FieldType.Message -> key.dec.value.name
- is FieldType.IntegralType -> key.fqName
- else -> error("Unsupported type: $key")
- }
-
- val fqValue = when (val value = entry.value) {
- is FieldType.Message -> value.dec.value.name
- is FieldType.IntegralType -> value.fqName
- else -> error("Unsupported type: $value")
- }
-
- "Map<${fqKey.safeFullName()}, ${fqValue.safeFullName()}>"
- }
-
- }.withNullability(nullable)
- }
-
- private fun String.withNullability(nullable: Boolean): String {
- return "$this${if (nullable) "?" else ""}"
- }
-
private fun CodeGenerator.generateOneOfPublic(declaration: OneOfDeclaration) {
val interfaceName = declaration.name.simpleName
- clazz(interfaceName, "sealed", declarationType = DeclarationType.Interface) {
+ clazz(interfaceName, "sealed", declarationType = CodeGenerator.DeclarationType.Interface) {
declaration.variants.forEach { variant ->
clazz(
name = variant.name,
@@ -900,7 +799,7 @@ class ModelToKotlinCommonGenerator(
declaration.originalEntries.forEach { variant ->
clazz(
name = variant.name.simpleName,
- declarationType = DeclarationType.Object,
+ declarationType = CodeGenerator.DeclarationType.Object,
superTypes = listOf("$className(number = ${variant.dec.number})"),
)
}
@@ -915,7 +814,7 @@ class ModelToKotlinCommonGenerator(
newLine()
- clazz("", modifiers = "companion", declarationType = DeclarationType.Object) {
+ clazz("", modifiers = "companion", declarationType = CodeGenerator.DeclarationType.Object) {
declaration.aliases.forEach { alias: EnumDeclaration.Alias ->
code(
"val ${alias.name.simpleName}: $className " +
@@ -929,57 +828,6 @@ class ModelToKotlinCommonGenerator(
}
}
-
- @Suppress("detekt.LongMethod")
- private fun CodeGenerator.generatePublicService(service: ServiceDeclaration) {
- code("@kotlinx.rpc.grpc.annotations.Grpc")
- clazz(service.name.simpleName, declarationType = DeclarationType.Interface) {
- service.methods.forEach { method ->
- val inputType = method.inputType
- val outputType = method.outputType
- function(
- name = method.name,
- modifiers = if (method.dec.isServerStreaming) "" else "suspend",
- args = "message: ${inputType.name.safeFullName().wrapInFlowIf(method.dec.isClientStreaming)}",
- returnType = outputType.name.safeFullName().wrapInFlowIf(method.dec.isServerStreaming),
- )
- }
- }
- }
-
- private fun String.wrapInFlowIf(condition: Boolean): String {
- return if (condition) "Flow<$this>" else this
- }
-
- private fun FqName.safeFullName(classSuffix: String = ""): String {
- importRootDeclarationIfNeeded(this)
-
- return fullName(classSuffix)
- }
-
- private fun importRootDeclarationIfNeeded(
- declaration: FqName,
- nameToImport: String = declaration.simpleName,
- internalOnly: Boolean = false,
- ) {
- if (declaration.parent == FqName.Package.Root && currentPackage != FqName.Package.Root && nameToImport.isNotBlank()) {
- additionalInternalImports.add(nameToImport)
- if (!internalOnly) {
- additionalPublicImports.add(nameToImport)
- }
- }
- }
-
- private fun FieldType.Message.internalConstructor() =
- dec.value.internalClassFullName() + "()"
-
- private fun MessageDeclaration.internalClassFullName(): String {
- return name.safeFullName(MSG_INTERNAL_SUFFIX)
- }
-
- private fun MessageDeclaration.internalClassName(): String {
- return name.simpleName + MSG_INTERNAL_SUFFIX
- }
}
private fun MessageDeclaration.allEnumsRecursively(): List =
@@ -988,6 +836,3 @@ private fun MessageDeclaration.allEnumsRecursively(): List =
private fun MessageDeclaration.allNestedRecursively(): List =
nestedDeclarations + nestedDeclarations.flatMap(MessageDeclaration::allNestedRecursively)
-private fun String.packageNameSuffixed(suffix: String): String {
- return if (isEmpty()) suffix else "$this.$suffix"
-}
diff --git a/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt
new file mode 100644
index 000000000..813a2516d
--- /dev/null
+++ b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.rpc.protoc.gen
+
+import kotlinx.rpc.protoc.gen.core.FileGenerator
+import kotlinx.rpc.protoc.gen.core.ProtocGenPlugin
+import kotlinx.rpc.protoc.gen.core.model.Model
+import org.slf4j.Logger
+
+object ProtobufProtocGenPlugin : ProtocGenPlugin() {
+ override fun generateKotlinByModel(
+ model: Model,
+ logger: Logger,
+ ): List {
+ return ModelToProtobufKotlinCommonGenerator(model, logger).generateKotlinFiles()
+ }
+}
diff --git a/protoc-gen/settings.gradle.kts b/protoc-gen/settings.gradle.kts
index 23bec0e46..f6e6ebe50 100644
--- a/protoc-gen/settings.gradle.kts
+++ b/protoc-gen/settings.gradle.kts
@@ -17,4 +17,6 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
-includeRootAsPublic()
+include(":common") // NOT PUBLIC, grpc and protobuf are fat jars
+includePublic(":grpc") // protoc-gen-grpc-kotlin-multiplatform
+includePublic(":protobuf") // protoc-gen-kotlin-multiplatform
diff --git a/settings.gradle.kts b/settings.gradle.kts
index ebde63547..7b86eba05 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -28,6 +28,9 @@ dependencyResolutionManagement {
includeBuild("protoc-gen")
}
+include(":protobuf")
+includePublic(":protobuf:protobuf-core")
+
include(":grpc")
includePublic(":grpc:grpc-core")
includePublic(":grpc:grpc-ktor-server")
diff --git a/versions-root/libs.versions.toml b/versions-root/libs.versions.toml
index 654b82282..a05c9ed7c 100644
--- a/versions-root/libs.versions.toml
+++ b/versions-root/libs.versions.toml
@@ -113,6 +113,7 @@ grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" }
grpc-util = { module = "io.grpc:grpc-util", version.ref = "grpc" }
grpc-netty = { module = "io.grpc:grpc-netty", version.ref = "grpc" }
grpc-protobuf = { module = "io.grpc:grpc-protobuf", version.ref = "grpc" }
+grpc-protobuf-lite = { module = "io.grpc:grpc-protobuf-lite", version.ref = "grpc" }
grpc-kotlin-stub = { module = "io.grpc:grpc-kotlin-stub", version.ref = "grpc-kotlin" }
grpc-protoc-gen-java = { module = "io.grpc:protoc-gen-grpc-java", version.ref = "grpc" }
grpc-protoc-gen-kotlin = { module = "io.grpc:protoc-gen-grpc-kotlin", version.ref = "grpc-kotlin" }
@@ -160,6 +161,7 @@ conventions-root = { id = "conventions-root" }
conventions-dokka-spec = { id = "conventions-dokka-spec" }
conventions-dokka-public = { id = "conventions-dokka-public" }
conventions-no-psi-element = { id = "conventions-no-psi-element" }
+conventions-protoc-gen = { id = "conventions-protoc-gen" }
compiler-specific-module = { id = "compiler-specific-module" }
# gradle-plugin project