From f834a3c4c1bb9940b14fa7393a14ff24ba92a896 Mon Sep 17 00:00:00 2001 From: James Ward Date: Tue, 12 Oct 2021 21:06:03 -0600 Subject: [PATCH] kotlin protos (#266) * kotlin protos * kt_jvm_proto_library proof of concept * Document kt_jvm_proto_library * Apply Bazel Kotlin proto rules to all examples * kotlin protos * add kotlin proto info * version bumps and cleanup Co-authored-by: Peter Schmitt --- WORKSPACE | 10 +- compiler/README.md | 76 +++++--- examples/android/build.gradle.kts | 8 +- .../grpc/examples/helloworld/MainActivity.kt | 6 +- examples/build.gradle.kts | 11 +- examples/client/build.gradle.kts | 12 +- .../io/grpc/examples/animals/AnimalsClient.kt | 6 +- .../io/grpc/examples/animals/BUILD.bazel | 1 + .../io/grpc/examples/helloworld/BUILD.bazel | 1 + .../examples/helloworld/HelloWorldClient.kt | 2 +- .../io/grpc/examples/routeguide/BUILD.bazel | 1 + .../examples/routeguide/RouteGuideClient.kt | 64 +++---- examples/native-client/build.gradle.kts | 2 +- .../examples/helloworld/HelloWorldClient.kt | 2 +- .../io/grpc/examples/animals/BUILD.bazel | 7 +- .../io/grpc/examples/helloworld/BUILD.bazel | 12 +- .../io/grpc/examples/routeguide/BUILD.bazel | 7 +- examples/server/build.gradle.kts | 12 +- .../io/grpc/examples/animals/AnimalsServer.kt | 41 ++--- .../io/grpc/examples/animals/BUILD.bazel | 1 + .../io/grpc/examples/helloworld/BUILD.bazel | 1 + .../examples/helloworld/HelloWorldServer.kt | 23 ++- .../io/grpc/examples/routeguide/BUILD.bazel | 2 +- .../examples/routeguide/RouteGuideServer.kt | 26 +-- examples/stub-android/build.gradle.kts | 20 +- examples/stub-lite/build.gradle.kts | 15 +- examples/stub/build.gradle.kts | 14 +- kt_jvm_grpc.bzl | 173 ++++++++++++++++++ repositories.bzl | 18 +- 29 files changed, 406 insertions(+), 168 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index c79bae4a..5b2787cc 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -34,6 +34,7 @@ maven_install( artifacts = [ "com.google.jimfs:jimfs:1.1", "com.google.truth.extensions:truth-proto-extension:1.0.1", + "com.google.protobuf:protobuf-kotlin:3.18.0", ] + IO_GRPC_GRPC_KOTLIN_ARTIFACTS + IO_GRPC_GRPC_JAVA_ARTIFACTS, generate_compat_repositories = True, override_targets = dict( @@ -56,16 +57,11 @@ grpc_java_repositories() # Protocol Buffers load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") - protobuf_deps() # Kotlin -load( - "@io_bazel_rules_kotlin//kotlin:kotlin.bzl", - "kotlin_repositories", - "kt_register_toolchains", -) - +load("@io_bazel_rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") kotlin_repositories() +load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") kt_register_toolchains() diff --git a/compiler/README.md b/compiler/README.md index 14794b59..82418aaf 100644 --- a/compiler/README.md +++ b/compiler/README.md @@ -1,8 +1,8 @@ gRPC Kotlin Codegen Plugin for Protobuf Compiler ================================================ -This generates the Kotlin interfaces out of the service definition from a -`.proto` file. It works with the Protobuf Compiler (`protoc`). +This generates the Kotlin interfaces out of the service definition from a`.proto` file. It works with the Protobuf Compiler (`protoc`) and uses a `protoc` plugin to generate Kotlin wrappers for the generated Java classes. +> Note: You can use the gRPC Kotlin compiler without using the protoc Kotlin compiler, but these instructions assume you want to use both together. ### Build Tool Plugins @@ -11,15 +11,16 @@ Usually this compiler is used via a build tool plugin, like in Gradle, Maven, et For Gradle, include the [protobuf plugin](https://github.com/google/protobuf-gradle-plugin) with at least version `0.8.13`, like: ``` plugins { - id("com.google.protobuf") version "SOME_VERSION" + id("com.google.protobuf") version "YOUR_PROTOBUF_PLUGIN_VERSION" } ``` -Add dependencies on `grpc-kotlin-stub` and a protobuf library like: +Add dependencies on `grpc-kotlin-stub` and protobuf libraries like: ``` dependencies { - implementation("io.grpc:grpc-kotlin-stub:SOME_VERSION") - implementation("io.grpc:grpc-protobuf:SOME_VERSION") + implementation("io.grpc:grpc-kotlin-stub:YOUR_GRPC_KOTLIN_VERSION") + implementation("io.grpc:grpc-protobuf:YOUR_GRPC_VERSION") + implementation("com.google.protobuf:protobuf-kotlin:YOUR_PROTOBUF_VERSION") } ``` @@ -27,22 +28,25 @@ Finally, setup the protobuf plugin: ``` protobuf { protoc { - artifact = "com.google.protobuf:protoc:SOME_VERSION" + artifact = "com.google.protobuf:protoc:YOUR_PROTOBUF_VERSION" } plugins { id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:SOME_VERSION" + artifact = "io.grpc:protoc-gen-grpc-java:YOUR_GRPC_VERSION" } id("grpckt") { - artifact = "io.grpc:protoc-gen-grpc-kotlin:SOME_VERSION:jdk7@jar" + artifact = "io.grpc:protoc-gen-grpc-kotlin:YOUR_GRPC_KOTLIN_VERSION:jdk7@jar" } } generateProtoTasks { - ofSourceSet("main").forEach { + all().forEach { it.plugins { id("grpc") id("grpckt") } + it.builtins { + id("kotlin") + } } } } @@ -55,21 +59,6 @@ For Maven, include the [protobuf plugin](https://www.xolstice.org/protobuf-maven org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 - - com.google.protobuf:protoc:SOME_VERSION:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:SOME_VERSION:exe:${os.detected.classifier} - - - grpc-kotlin - io.grpc - protoc-gen-grpc-kotlin - SOME_VERSION - jdk7 - io.grpc.kotlin.generator.GeneratorRunner - - - compile @@ -77,22 +66,53 @@ For Maven, include the [protobuf plugin](https://www.xolstice.org/protobuf-maven compile compile-custom + + com.google.protobuf:protoc:YOUR_PROTOBUF_VERSION:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:YOUR_GRPC_VERSION:exe:${os.detected.classifier} + + + grpc-kotlin + io.grpc + protoc-gen-grpc-kotlin + YOUR_GRPC_KOTLIN_VERSION + jdk7 + io.grpc.kotlin.generator.GeneratorRunner + + + + + + compile-kt + + compile-custom + + + com.google.protobuf:protoc:YOUR_PROTOBUF_VERSION:exe:${os.detected.classifier} + ${project.build.directory}/generated-sources/protobuf/kotlin + kotlin + ``` -Make sure you include a dependency on `stub` and a protobuf library like: +Make sure you include a dependency on `stub` and protobuf libraries like: ``` io.grpc grpc-kotlin-stub - SOME_VERSION + YOUR_GRPC_KOTLIN_VERSION io.grpc grpc-protobuf - SOME_VERSION + YOUR_PROTOBUF_VERSION + + + com.google.protobuf + protobuf-kotlin + YOUR_PROTOBUF_VERSION ``` diff --git a/examples/android/build.gradle.kts b/examples/android/build.gradle.kts index 0fd63602..07b9fc5a 100644 --- a/examples/android/build.gradle.kts +++ b/examples/android/build.gradle.kts @@ -5,7 +5,7 @@ plugins { dependencies { implementation(project(":stub-android")) - implementation("androidx.appcompat:appcompat:1.2.0") + implementation("androidx.appcompat:appcompat:1.3.1") runtimeOnly("io.grpc:grpc-okhttp:${rootProject.ext["grpcVersion"]}") } @@ -15,7 +15,7 @@ android { defaultConfig { applicationId = "io.grpc.examples.hello" - minSdkVersion(23) + minSdkVersion(26) targetSdkVersion(30) versionCode = 1 versionName = "1.0" @@ -31,7 +31,7 @@ android { sourceSets["main"].java.srcDir("src/main/kotlin") compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_7 - targetCompatibility = JavaVersion.VERSION_1_7 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } } diff --git a/examples/android/src/main/kotlin/io/grpc/examples/helloworld/MainActivity.kt b/examples/android/src/main/kotlin/io/grpc/examples/helloworld/MainActivity.kt index 13761771..ea9a35e6 100644 --- a/examples/android/src/main/kotlin/io/grpc/examples/helloworld/MainActivity.kt +++ b/examples/android/src/main/kotlin/io/grpc/examples/helloworld/MainActivity.kt @@ -10,11 +10,11 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder -import java.net.URL -import java.util.logging.Logger import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.runBlocking +import java.net.URL +import java.util.logging.Logger // todo: suspend funs class MainActivity : AppCompatActivity() { @@ -58,7 +58,7 @@ class MainActivity : AppCompatActivity() { fun sendReq() = runBlocking { try { - val request = HelloRequest.newBuilder().setName(nameText.text.toString()).build() + val request = helloRequest { name = nameText.text.toString() } val response = greeter.sayHello(request) responseText.text = response.message } catch (e: Exception) { diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 07e3277c..9fbcc247 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -1,14 +1,15 @@ plugins { id("com.android.application") version "4.1.1" apply false - id("com.google.protobuf") version "0.8.15" apply false - kotlin("jvm") version "1.4.32" apply false - id("org.jlleitschuh.gradle.ktlint") version "9.2.1" + id("com.google.protobuf") version "0.8.17" apply false + kotlin("jvm") version "1.5.31" apply false + id("org.jlleitschuh.gradle.ktlint") version "10.2.0" } // todo: move to subprojects, but how? -ext["grpcVersion"] = "1.37.0" +ext["grpcVersion"] = "1.39.0" // need to wait for grpc kotlin to move past this ext["grpcKotlinVersion"] = "1.1.0" // CURRENT_GRPC_KOTLIN_VERSION -ext["protobufVersion"] = "3.15.8" +ext["protobufVersion"] = "3.18.1" +ext["coroutinesVersion"] = "1.5.2" allprojects { repositories { diff --git a/examples/client/build.gradle.kts b/examples/client/build.gradle.kts index b045d9cd..a1a4534f 100644 --- a/examples/client/build.gradle.kts +++ b/examples/client/build.gradle.kts @@ -11,37 +11,37 @@ dependencies { tasks.register("HelloWorldClient") { dependsOn("classes") classpath = sourceSets["main"].runtimeClasspath - main = "io.grpc.examples.helloworld.HelloWorldClientKt" + mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") } tasks.register("RouteGuideClient") { dependsOn("classes") classpath = sourceSets["main"].runtimeClasspath - main = "io.grpc.examples.routeguide.RouteGuideClientKt" + mainClass.set("io.grpc.examples.routeguide.RouteGuideClientKt") } tasks.register("AnimalsClient") { dependsOn("classes") classpath = sourceSets["main"].runtimeClasspath - main = "io.grpc.examples.animals.AnimalsClientKt" + mainClass.set("io.grpc.examples.animals.AnimalsClientKt") } val helloWorldClientStartScripts = tasks.register("helloWorldClientStartScripts") { - mainClassName = "io.grpc.examples.helloworld.HelloWorldClientKt" + mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") applicationName = "hello-world-client" outputDir = tasks.named("startScripts").get().outputDir classpath = tasks.named("startScripts").get().classpath } val routeGuideClientStartScripts = tasks.register("routeGuideClientStartScripts") { - mainClassName = "io.grpc.examples.routeguide.RouteGuideClientKt" + mainClass.set("io.grpc.examples.routeguide.RouteGuideClientKt") applicationName = "route-guide-client" outputDir = tasks.named("startScripts").get().outputDir classpath = tasks.named("startScripts").get().classpath } val animalsClientStartScripts = tasks.register("animalsClientStartScripts") { - mainClassName = "io.grpc.examples.animals.AnimalsClientKt" + mainClass.set("io.grpc.examples.animals.AnimalsClientKt") applicationName = "animals-client" outputDir = tasks.named("startScripts").get().outputDir classpath = tasks.named("startScripts").get().classpath diff --git a/examples/client/src/main/kotlin/io/grpc/examples/animals/AnimalsClient.kt b/examples/client/src/main/kotlin/io/grpc/examples/animals/AnimalsClient.kt index 4782adbb..0855f8e6 100644 --- a/examples/client/src/main/kotlin/io/grpc/examples/animals/AnimalsClient.kt +++ b/examples/client/src/main/kotlin/io/grpc/examples/animals/AnimalsClient.kt @@ -27,19 +27,19 @@ class AnimalsClient(private val channel: ManagedChannel) : Closeable { private val sheepStub: SheepGrpcKt.SheepCoroutineStub by lazy { SheepGrpcKt.SheepCoroutineStub(channel) } suspend fun bark() { - val request = BarkRequest.getDefaultInstance() + val request = barkRequest {} val response = dogStub.bark(request) println("Received: ${response.message}") } suspend fun oink() { - val request = OinkRequest.getDefaultInstance() + val request = oinkRequest {} val response = pigStub.oink(request) println("Received: ${response.message}") } suspend fun baa() { - val request = BaaRequest.getDefaultInstance() + val request = baaRequest {} val response = sheepStub.baa(request) println("Received: ${response.message}") } diff --git a/examples/client/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel b/examples/client/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel index 0ee37866..b5e5a309 100644 --- a/examples/client/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel +++ b/examples/client/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel @@ -10,6 +10,7 @@ kt_jvm_binary( main_class = "io.grpc.examples.animals.AnimalsClientKt", deps = [ "//examples/protos/src/main/proto/io/grpc/examples/animals:animals_kt_grpc", + "//examples/protos/src/main/proto/io/grpc/examples/animals:animals_kt_proto", "@com_google_protobuf//:protobuf_java_util", "@io_grpc_grpc_java//netty", ], diff --git a/examples/client/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel b/examples/client/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel index 1c6ff3f6..7230ea90 100644 --- a/examples/client/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel +++ b/examples/client/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel @@ -10,6 +10,7 @@ kt_jvm_binary( main_class = "io.grpc.examples.helloworld.HelloWorldClientKt", deps = [ "//examples/protos/src/main/proto/io/grpc/examples/helloworld:hello_world_kt_grpc", + "//examples/protos/src/main/proto/io/grpc/examples/helloworld:hello_world_kt_proto", "@io_grpc_grpc_java//netty", ], ) diff --git a/examples/client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt b/examples/client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt index 6da59722..b3a8cf41 100644 --- a/examples/client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt +++ b/examples/client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt @@ -26,7 +26,7 @@ class HelloWorldClient(private val channel: ManagedChannel) : Closeable { private val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) suspend fun greet(name: String) { - val request = HelloRequest.newBuilder().setName(name).build() + val request = helloRequest { this.name = name } val response = stub.sayHello(request) println("Received: ${response.message}") } diff --git a/examples/client/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel b/examples/client/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel index f6ce08d7..65b0fcbc 100644 --- a/examples/client/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel +++ b/examples/client/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel @@ -11,6 +11,7 @@ kt_jvm_binary( resources = ["//examples/stub/src/main/resources/io/grpc/examples/routeguide:route_guide_db"], deps = [ "//examples/protos/src/main/proto/io/grpc/examples/routeguide:route_guide_kt_grpc", + "//examples/protos/src/main/proto/io/grpc/examples/routeguide:route_guide_kt_proto", "//examples/stub/src/main/kotlin/io/grpc/examples/routeguide:route_guide_stub", "@com_google_protobuf//:protobuf_java_util", "@io_grpc_grpc_java//netty", diff --git a/examples/client/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideClient.kt b/examples/client/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideClient.kt index 22712556..b4f6c999 100644 --- a/examples/client/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideClient.kt +++ b/examples/client/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideClient.kt @@ -19,14 +19,14 @@ package io.grpc.examples.routeguide import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder import io.grpc.examples.routeguide.RouteGuideGrpcKt.RouteGuideCoroutineStub -import java.io.Closeable -import java.util.concurrent.TimeUnit -import kotlin.random.Random -import kotlin.random.nextLong import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flow +import java.io.Closeable +import java.util.concurrent.TimeUnit +import kotlin.random.Random +import kotlin.random.nextLong class RouteGuideClient(private val channel: ManagedChannel) : Closeable { private val random = Random(314159) @@ -52,10 +52,10 @@ class RouteGuideClient(private val channel: ManagedChannel) : Closeable { suspend fun listFeatures(lowLat: Int, lowLon: Int, hiLat: Int, hiLon: Int) { println("*** ListFeatures: lowLat=$lowLat lowLon=$lowLon hiLat=$hiLat liLon=$hiLon") - val request = Rectangle.newBuilder() - .setLo(point(lowLat, lowLon)) - .setHi(point(hiLat, hiLon)) - .build() + val request = rectangle { + lo = point(lowLat, lowLon) + hi = point(hiLat, hiLon) + } var i = 1 stub.listFeatures(request).collect { feature -> println("Result #${i++}: $feature") @@ -92,26 +92,26 @@ class RouteGuideClient(private val channel: ManagedChannel) : Closeable { private fun generateOutgoingNotes(): Flow = flow { val notes = listOf( - RouteNote.newBuilder().apply { - message = "First message" - location = point(0, 0) - }.build(), - RouteNote.newBuilder().apply { - message = "Second message" - location = point(0, 0) - }.build(), - RouteNote.newBuilder().apply { - message = "Third message" - location = point(10000000, 0) - }.build(), - RouteNote.newBuilder().apply { - message = "Fourth message" - location = point(10000000, 10000000) - }.build(), - RouteNote.newBuilder().apply { - message = "Last message" - location = point(0, 0) - }.build() + routeNote { + message = "First message" + location = point(0, 0) + }, + routeNote { + message = "Second message" + location = point(0, 0) + }, + routeNote { + message = "Third message" + location = point(10000000, 0) + }, + routeNote { + message = "Fourth message" + location = point(10000000, 10000000) + }, + routeNote { + message = "Last message" + location = point(0, 0) + }, ) for (note in notes) { println("Sending message \"${note.message}\" at ${note.location.toStr()}") @@ -135,7 +135,7 @@ suspend fun main() { } } -private fun point(lat: Int, lon: Int): Point = Point.newBuilder() - .setLatitude(lat) - .setLongitude(lon) - .build() +private fun point(lat: Int, lon: Int): Point = point { + latitude = lat + longitude = lon +} diff --git a/examples/native-client/build.gradle.kts b/examples/native-client/build.gradle.kts index 73fa77a1..0dcb64fb 100644 --- a/examples/native-client/build.gradle.kts +++ b/examples/native-client/build.gradle.kts @@ -1,7 +1,7 @@ plugins { application kotlin("jvm") - id("com.palantir.graal") version "0.7.1" + id("com.palantir.graal") version "0.7.2" } dependencies { diff --git a/examples/native-client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt b/examples/native-client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt index b875ecac..2242584c 100644 --- a/examples/native-client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt +++ b/examples/native-client/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt @@ -26,7 +26,7 @@ class HelloWorldClient(private val channel: ManagedChannel) : Closeable { private val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) suspend fun greet(name: String) { - val request = HelloRequest.newBuilder().setName(name).build() + val request = helloRequest { this.name = name } val response = stub.sayHello(request) println("Received: ${response.message}") } diff --git a/examples/protos/src/main/proto/io/grpc/examples/animals/BUILD.bazel b/examples/protos/src/main/proto/io/grpc/examples/animals/BUILD.bazel index b26ee0a5..10a245b1 100644 --- a/examples/protos/src/main/proto/io/grpc/examples/animals/BUILD.bazel +++ b/examples/protos/src/main/proto/io/grpc/examples/animals/BUILD.bazel @@ -1,6 +1,6 @@ load("@rules_proto//proto:defs.bzl", "proto_library") load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library") -load("//:kt_jvm_grpc.bzl", "kt_jvm_grpc_library") +load("//:kt_jvm_grpc.bzl", "kt_jvm_grpc_library", "kt_jvm_proto_library") licenses(["notice"]) @@ -25,6 +25,11 @@ java_lite_proto_library( deps = [":animals_proto"], ) +kt_jvm_proto_library( + name = "animals_kt_proto", + deps = [":animals_proto"], +) + kt_jvm_grpc_library( name = "animals_kt_grpc", srcs = [":animals_proto"], diff --git a/examples/protos/src/main/proto/io/grpc/examples/helloworld/BUILD.bazel b/examples/protos/src/main/proto/io/grpc/examples/helloworld/BUILD.bazel index accbef98..e9dada43 100644 --- a/examples/protos/src/main/proto/io/grpc/examples/helloworld/BUILD.bazel +++ b/examples/protos/src/main/proto/io/grpc/examples/helloworld/BUILD.bazel @@ -1,6 +1,6 @@ load("@rules_proto//proto:defs.bzl", "proto_library") load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library") -load("//:kt_jvm_grpc.bzl", "kt_jvm_grpc_library") +load("//:kt_jvm_grpc.bzl", "kt_jvm_grpc_library", "kt_jvm_proto_library") licenses(["notice"]) @@ -11,20 +11,20 @@ proto_library( srcs = ["hello_world.proto"], ) -java_proto_library( - name = "hello_world_java_proto", +java_lite_proto_library( + name = "hello_world_java_proto_lite", deps = [":hello_world_proto"], ) -java_lite_proto_library( - name = "hello_world_java_proto_lite", +kt_jvm_proto_library( + name = "hello_world_kt_proto", deps = [":hello_world_proto"], ) kt_jvm_grpc_library( name = "hello_world_kt_grpc", srcs = [":hello_world_proto"], - deps = [":hello_world_java_proto"], + deps = [":hello_world_kt_proto"], ) kt_jvm_grpc_library( diff --git a/examples/protos/src/main/proto/io/grpc/examples/routeguide/BUILD.bazel b/examples/protos/src/main/proto/io/grpc/examples/routeguide/BUILD.bazel index 863e46a5..c3ad02db 100644 --- a/examples/protos/src/main/proto/io/grpc/examples/routeguide/BUILD.bazel +++ b/examples/protos/src/main/proto/io/grpc/examples/routeguide/BUILD.bazel @@ -1,6 +1,6 @@ load("@rules_proto//proto:defs.bzl", "proto_library") load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library") -load("//:kt_jvm_grpc.bzl", "kt_jvm_grpc_library") +load("//:kt_jvm_grpc.bzl", "kt_jvm_grpc_library", "kt_jvm_proto_library") licenses(["notice"]) @@ -22,6 +22,11 @@ java_lite_proto_library( deps = [":route_guide_proto"], ) +kt_jvm_proto_library( + name = "route_guide_kt_proto", + deps = [":route_guide_proto"], +) + kt_jvm_grpc_library( name = "route_guide_kt_grpc", srcs = [":route_guide_proto"], diff --git a/examples/server/build.gradle.kts b/examples/server/build.gradle.kts index f3e53f96..741c2015 100644 --- a/examples/server/build.gradle.kts +++ b/examples/server/build.gradle.kts @@ -11,37 +11,37 @@ dependencies { tasks.register("HelloWorldServer") { dependsOn("classes") classpath = sourceSets["main"].runtimeClasspath - main = "io.grpc.examples.helloworld.HelloWorldServerKt" + mainClass.set("io.grpc.examples.helloworld.HelloWorldServerKt") } tasks.register("RouteGuideServer") { dependsOn("classes") classpath = sourceSets["main"].runtimeClasspath - main = "io.grpc.examples.routeguide.RouteGuideServerKt" + mainClass.set("io.grpc.examples.routeguide.RouteGuideServerKt") } tasks.register("AnimalsServer") { dependsOn("classes") classpath = sourceSets["main"].runtimeClasspath - main = "io.grpc.examples.animals.AnimalsServerKt" + mainClass.set("io.grpc.examples.animals.AnimalsServerKt") } val helloWorldServerStartScripts = tasks.register("helloWorldServerStartScripts") { - mainClassName = "io.grpc.examples.helloworld.HelloWorldServerKt" + mainClass.set("io.grpc.examples.helloworld.HelloWorldServerKt") applicationName = "hello-world-server" outputDir = tasks.named("startScripts").get().outputDir classpath = tasks.named("startScripts").get().classpath } val routeGuideServerStartScripts = tasks.register("routeGuideServerStartScripts") { - mainClassName = "io.grpc.examples.routeguide.RouteGuideServerKt" + mainClass.set("io.grpc.examples.routeguide.RouteGuideServerKt") applicationName = "route-guide-server" outputDir = tasks.named("startScripts").get().outputDir classpath = tasks.named("startScripts").get().classpath } val animalsServerStartScripts = tasks.register("animalsServerStartScripts") { - mainClassName = "io.grpc.examples.animals.AnimalsServerKt" + mainClass.set("io.grpc.examples.animals.AnimalsServerKt") applicationName = "animals-server" outputDir = tasks.named("startScripts").get().outputDir classpath = tasks.named("startScripts").get().classpath diff --git a/examples/server/src/main/kotlin/io/grpc/examples/animals/AnimalsServer.kt b/examples/server/src/main/kotlin/io/grpc/examples/animals/AnimalsServer.kt index 3a5fc185..c4188d75 100644 --- a/examples/server/src/main/kotlin/io/grpc/examples/animals/AnimalsServer.kt +++ b/examples/server/src/main/kotlin/io/grpc/examples/animals/AnimalsServer.kt @@ -21,21 +21,21 @@ import io.grpc.ServerBuilder class AnimalsServer constructor(private val port: Int) { val server: Server = ServerBuilder - .forPort(port) - .addService(DogService()) - .addService(PigService()) - .addService(SheepService()) - .build() + .forPort(port) + .addService(DogService()) + .addService(PigService()) + .addService(SheepService()) + .build() fun start() { server.start() println("Server started, listening on $port") Runtime.getRuntime().addShutdownHook( - Thread { - println("*** shutting down gRPC server since JVM is shutting down") - this@AnimalsServer.stop() - println("*** server shut down") - } + Thread { + println("*** shutting down gRPC server since JVM is shutting down") + this@AnimalsServer.stop() + println("*** server shut down") + } ) } @@ -48,24 +48,21 @@ class AnimalsServer constructor(private val port: Int) { } private class DogService : DogGrpcKt.DogCoroutineImplBase() { - override suspend fun bark(request: BarkRequest) = BarkReply - .newBuilder() - .setMessage("Bark!") - .build() + override suspend fun bark(request: BarkRequest) = barkReply { + message = "Bark!" + } } private class PigService : PigGrpcKt.PigCoroutineImplBase() { - override suspend fun oink(request: OinkRequest) = OinkReply - .newBuilder() - .setMessage("Oink!") - .build() + override suspend fun oink(request: OinkRequest) = oinkReply { + message = "Oink!" + } } private class SheepService : SheepGrpcKt.SheepCoroutineImplBase() { - override suspend fun baa(request: BaaRequest) = BaaReply - .newBuilder() - .setMessage("Baa!") - .build() + override suspend fun baa(request: BaaRequest) = baaReply { + message = "Baa!" + } } } diff --git a/examples/server/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel b/examples/server/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel index 06887825..989a537b 100644 --- a/examples/server/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel +++ b/examples/server/src/main/kotlin/io/grpc/examples/animals/BUILD.bazel @@ -10,6 +10,7 @@ kt_jvm_binary( main_class = "io.grpc.examples.animals.AnimalsServerKt", deps = [ "//examples/protos/src/main/proto/io/grpc/examples/animals:animals_kt_grpc", + "//examples/protos/src/main/proto/io/grpc/examples/animals:animals_kt_proto", "@com_google_protobuf//:protobuf_java_util", "@io_grpc_grpc_java//netty", ], diff --git a/examples/server/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel b/examples/server/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel index ec3feda5..c332544a 100644 --- a/examples/server/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel +++ b/examples/server/src/main/kotlin/io/grpc/examples/helloworld/BUILD.bazel @@ -10,6 +10,7 @@ kt_jvm_binary( main_class = "io.grpc.examples.helloworld.HelloWorldServerKt", deps = [ "//examples/protos/src/main/proto/io/grpc/examples/helloworld:hello_world_kt_grpc", + "//examples/protos/src/main/proto/io/grpc/examples/helloworld:hello_world_kt_proto", "@io_grpc_grpc_java//netty", ], ) diff --git a/examples/server/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt b/examples/server/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt index ae3224b3..b7bb42ea 100644 --- a/examples/server/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt +++ b/examples/server/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt @@ -21,19 +21,19 @@ import io.grpc.ServerBuilder class HelloWorldServer(private val port: Int) { val server: Server = ServerBuilder - .forPort(port) - .addService(HelloWorldService()) - .build() + .forPort(port) + .addService(HelloWorldService()) + .build() fun start() { server.start() println("Server started, listening on $port") Runtime.getRuntime().addShutdownHook( - Thread { - println("*** shutting down gRPC server since JVM is shutting down") - this@HelloWorldServer.stop() - println("*** server shut down") - } + Thread { + println("*** shutting down gRPC server since JVM is shutting down") + this@HelloWorldServer.stop() + println("*** server shut down") + } ) } @@ -46,10 +46,9 @@ class HelloWorldServer(private val port: Int) { } private class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() { - override suspend fun sayHello(request: HelloRequest) = HelloReply - .newBuilder() - .setMessage("Hello ${request.name}") - .build() + override suspend fun sayHello(request: HelloRequest) = helloReply { + message = "Hello ${request.name}" + } } } diff --git a/examples/server/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel b/examples/server/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel index 7b463099..d1019977 100644 --- a/examples/server/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel +++ b/examples/server/src/main/kotlin/io/grpc/examples/routeguide/BUILD.bazel @@ -10,8 +10,8 @@ kt_jvm_binary( main_class = "io.grpc.examples.routeguide.RouteGuideServerKt", resources = ["//examples/stub/src/main/resources/io/grpc/examples/routeguide:route_guide_db"], deps = [ - "//examples/protos/src/main/proto/io/grpc/examples/routeguide:route_guide_java_proto", "//examples/protos/src/main/proto/io/grpc/examples/routeguide:route_guide_kt_grpc", + "//examples/protos/src/main/proto/io/grpc/examples/routeguide:route_guide_kt_proto", "//examples/stub/src/main/kotlin/io/grpc/examples/routeguide:route_guide_stub", "@com_google_protobuf//:protobuf_java_util", "@io_grpc_grpc_java//netty", diff --git a/examples/server/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideServer.kt b/examples/server/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideServer.kt index 572d887e..8d461bfe 100644 --- a/examples/server/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideServer.kt +++ b/examples/server/src/main/kotlin/io/grpc/examples/routeguide/RouteGuideServer.kt @@ -21,14 +21,14 @@ import com.google.common.base.Ticker import com.google.protobuf.util.Durations import io.grpc.Server import io.grpc.ServerBuilder -import java.util.Collections -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow +import java.util.Collections +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit /** * Kotlin adaptation of RouteGuideServer from the Java gRPC example. @@ -43,11 +43,11 @@ class RouteGuideServer( server.start() println("Server started, listening on $port") Runtime.getRuntime().addShutdownHook( - Thread { - println("*** shutting down gRPC server since JVM is shutting down") - this@RouteGuideServer.stop() - println("*** server shut down") - } + Thread { + println("*** shutting down gRPC server since JVM is shutting down") + this@RouteGuideServer.stop() + println("*** server shut down") + } ) } @@ -66,11 +66,11 @@ class RouteGuideServer( private val routeNotes = ConcurrentHashMap>() override suspend fun getFeature(request: Point): Feature = - // No feature was found, return an unnamed feature. - features.find { it.location == request } ?: Feature.newBuilder().apply { location = request }.build() + // No feature was found, return an unnamed feature. + features.find { it.location == request } ?: feature { location = request } override fun listFeatures(request: Rectangle): Flow = - features.asFlow().filter { it.exists() && it.location in request } + features.asFlow().filter { it.exists() && it.location in request } override suspend fun recordRoute(requests: Flow): RouteSummary { var pointCount = 0 @@ -89,12 +89,12 @@ class RouteGuideServer( } previous = request } - return RouteSummary.newBuilder().apply { + return routeSummary { this.pointCount = pointCount this.featureCount = featureCount this.distance = distance this.elapsedTime = Durations.fromMicros(stopwatch.elapsed(TimeUnit.MICROSECONDS)) - }.build() + } } override fun routeChat(requests: Flow): Flow = flow { diff --git a/examples/stub-android/build.gradle.kts b/examples/stub-android/build.gradle.kts index 214d1ec7..97602427 100644 --- a/examples/stub-android/build.gradle.kts +++ b/examples/stub-android/build.gradle.kts @@ -14,16 +14,27 @@ dependencies { protobuf(project(":protos")) api(kotlin("stdlib")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3") + api("org.jetbrains.kotlinx:kotlinx-coroutines-android:${rootProject.ext["coroutinesVersion"]}") api("io.grpc:grpc-protobuf-lite:${rootProject.ext["grpcVersion"]}") api("io.grpc:grpc-kotlin-stub:${rootProject.ext["grpcKotlinVersion"]}") - api("com.google.protobuf:protobuf-javalite:${rootProject.ext["protobufVersion"]}") + api("com.google.protobuf:protobuf-kotlin-lite:${rootProject.ext["protobufVersion"]}") } android { compileSdkVersion(30) buildToolsVersion = "30.0.2" + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} + +tasks.withType().all { + kotlinOptions { + freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") + } } protobuf { @@ -54,6 +65,11 @@ protobuf { option("lite") } } + it.builtins { + id("kotlin") { + option("lite") + } + } } } } diff --git a/examples/stub-lite/build.gradle.kts b/examples/stub-lite/build.gradle.kts index 400dbad4..05a79faa 100644 --- a/examples/stub-lite/build.gradle.kts +++ b/examples/stub-lite/build.gradle.kts @@ -12,15 +12,21 @@ plugins { dependencies { protobuf(project(":protos")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${rootProject.ext["coroutinesVersion"]}") api("io.grpc:grpc-protobuf-lite:${rootProject.ext["grpcVersion"]}") api("io.grpc:grpc-kotlin-stub:${rootProject.ext["grpcKotlinVersion"]}") - api("com.google.protobuf:protobuf-javalite:${rootProject.ext["protobufVersion"]}") + api("com.google.protobuf:protobuf-kotlin-lite:${rootProject.ext["protobufVersion"]}") } java { - sourceCompatibility = JavaVersion.VERSION_1_7 + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().all { + kotlinOptions { + freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") + } } protobuf { @@ -41,6 +47,9 @@ protobuf { named("java") { option("lite") } + id("kotlin") { + option("lite") + } } it.plugins { id("grpc") { diff --git a/examples/stub/build.gradle.kts b/examples/stub/build.gradle.kts index 663c958b..714279da 100644 --- a/examples/stub/build.gradle.kts +++ b/examples/stub/build.gradle.kts @@ -13,15 +13,22 @@ dependencies { protobuf(project(":protos")) api(kotlin("stdlib")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${rootProject.ext["coroutinesVersion"]}") api("io.grpc:grpc-protobuf:${rootProject.ext["grpcVersion"]}") api("com.google.protobuf:protobuf-java-util:${rootProject.ext["protobufVersion"]}") + api("com.google.protobuf:protobuf-kotlin:${rootProject.ext["protobufVersion"]}") api("io.grpc:grpc-kotlin-stub:${rootProject.ext["grpcKotlinVersion"]}") } java { - sourceCompatibility = JavaVersion.VERSION_1_7 + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().all { + kotlinOptions { + freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") + } } protobuf { @@ -42,6 +49,9 @@ protobuf { id("grpc") id("grpckt") } + it.builtins { + id("kotlin") + } } } } diff --git a/kt_jvm_grpc.bzl b/kt_jvm_grpc.bzl index 71f7c95c..c00d1671 100644 --- a/kt_jvm_grpc.bzl +++ b/kt_jvm_grpc.bzl @@ -159,3 +159,176 @@ def kt_jvm_grpc_library( deprecation = deprecation, features = features, ) + +def _get_real_short_path(file): + """Returns the correct short path file name to be used by protoc.""" + short_path = file.short_path + if short_path.startswith("../"): + second_slash = short_path.index("/", 3) + short_path = short_path[second_slash + 1:] + + virtual_imports = "_virtual_imports/" + if virtual_imports in short_path: + short_path = short_path.split(virtual_imports)[1].split("/", 1)[1] + return short_path + +def _kt_jvm_proto_library_helper_impl(ctx): + transitive_set = depset( + transitive = + [dep[ProtoInfo].transitive_descriptor_sets for dep in ctx.attr.proto_deps], + ) + proto_sources = [] + for dep in ctx.attr.proto_deps: + for file in dep[ProtoInfo].direct_sources: + proto_sources.append(_get_real_short_path(file)) + + gen_src_dir = ctx.actions.declare_directory(ctx.label.name + "/ktproto") + + protoc_args = ctx.actions.args() + protoc_args.set_param_file_format("multiline") + protoc_args.use_param_file("@%s") + protoc_args.add("--kotlin_out=" + gen_src_dir.path) + protoc_args.add_joined( + transitive_set, + join_with = ctx.configuration.host_path_separator, + format_joined = "--descriptor_set_in=%s", + ) + protoc_args.add_all(proto_sources) + + ctx.actions.run( + inputs = depset(transitive = [transitive_set]), + outputs = [gen_src_dir], + executable = ctx.executable._protoc, + arguments = [protoc_args], + progress_message = "Generating kotlin proto extensions for " + + ", ".join([ + str(dep.label) + for dep in ctx.attr.proto_deps + ]), + ) + + # Because protoc outputs an unknown number of files we need to zip them into a srcjar. + args = ctx.actions.args() + args.add("c") + args.add(ctx.outputs.srcjar) + args.add_all([gen_src_dir]) + ctx.actions.run( + arguments = [args], + executable = ctx.executable._zip, + inputs = [gen_src_dir], + outputs = [ctx.outputs.srcjar], + ) + + +_kt_jvm_proto_library_helper = rule( + attrs = dict( + proto_deps = attr.label_list( + providers = [ProtoInfo] + ), + deps = attr.label_list( + providers = [JavaInfo], + ), + exports = attr.label_list( + allow_rules = ["java_proto_library"], + ), + _protoc = attr.label( + default = Label("@com_google_protobuf//:protoc"), + cfg = "host", + executable = True, + ), + _zip = attr.label( + default = Label("@bazel_tools//tools/zip:zipper"), + cfg = "host", + executable=True + ), + ), + implementation = _kt_jvm_proto_library_helper_impl, + outputs = dict( + srcjar = "%{name}.srcjar", + ) +) + + +def kt_jvm_proto_library( + name, + deps = None, + tags = None, + testonly = None, + compatible_with = None, + restricted_to = None, + visibility = None, + deprecation = None, + features = []): + """ + This rule accepts any number of proto_library targets in "deps", translates them to Kotlin and + returns the compiled Kotlin. + + See also https://developers.google.com/protocol-buffers/docs/kotlintutorial for how to interact + with the generated Kotlin representation. + + Note that the rule will also export the java version of the same protos as Kotlin protos depend + on the java version under the hood. + + For standard attributes, see: + https://docs.bazel.build/versions/master/be/common-definitions.html#common-attributes + + Args: + name: A name for the target + deps: One or more proto_library targets to turn into Kotlin. + tags: Standard attribute + testonly: Standard attribute + compatible_with: Standard attribute + restricted_to: Standard attribute + visibility: Standard attribute + deprecation: Standard attribute + features: Standard attribute + """ + java_proto_target = ":%s_DO_NOT_DEPEND_java_proto" % name + helper_target = ":%s_DO_NOT_DEPEND_kt_proto" % name + + native.java_proto_library( + name = java_proto_target[1:], + deps = deps, + testonly = testonly, + compatible_with = compatible_with, + visibility = ["//visibility:private"], + restricted_to = restricted_to, + tags = tags, + deprecation = deprecation, + features = features, + ) + + _kt_jvm_proto_library_helper( + name = helper_target[1:], + proto_deps = deps, + deps = [ + java_proto_target, + ], + testonly = testonly, + compatible_with = compatible_with, + visibility = ["//visibility:private"], + restricted_to = restricted_to, + tags = tags, + deprecation = deprecation, + features = features, + ) + + kt_jvm_library( + name = name, + srcs = [helper_target + ".srcjar"], + deps = [ + "@com_google_protobuf_protobuf_kotlin//jar", + java_proto_target + ], + exports = [ + java_proto_target + ], + testonly = testonly, + compatible_with = compatible_with, + visibility = visibility, + restricted_to = restricted_to, + tags = tags, + deprecation = deprecation, + features = features, + ) + diff --git a/repositories.bzl b/repositories.bzl index c7b4abe1..425bb0ef 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -35,21 +35,23 @@ def grpc_kt_repositories(): io_grpc_grpc_java() def io_bazel_rules_kotlin(): - rules_kotlin_version = "b40d920c5a5e044c541513f0d5e9260d0a4579c0" + rules_kotlin_version = "v1.5.0-beta-3" + rules_kotlin_sha = "58edd86f0f3c5b959c54e656b8e7eb0b0becabd412465c37a2078693c2571f7f" http_archive( name = "io_bazel_rules_kotlin", - urls = ["https://github.com/bazelbuild/rules_kotlin/archive/%s.zip" % rules_kotlin_version], - sha256 = "3dadd0ad7272be6b1ed1274f62cadd4a1293c89990bcd7b4af32637a70ada63e", - type = "zip", - strip_prefix = "rules_kotlin-%s" % rules_kotlin_version, + urls = ["https://github.com/bazelbuild/rules_kotlin/releases/download/%s/rules_kotlin_release.tgz" % rules_kotlin_version], + sha256 = rules_kotlin_sha, ) def com_google_protobuf(): + protobuf_version = "3.17.3" + protobuf_sha = "77ad26d3f65222fd96ccc18b055632b0bfedf295cb748b712a98ba1ac0b704b2" + http_archive( name = "com_google_protobuf", - sha256 = "b37e96e81842af659605908a421960a5dc809acbc888f6b947bc320f8628e5b1", - strip_prefix = "protobuf-3.12.0", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.12.0.zip"], + sha256 = protobuf_sha, + strip_prefix = "protobuf-%s" % protobuf_version, + urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v%s/protobuf-all-%s.tar.gz" % (protobuf_version, protobuf_version)], ) def io_grpc_grpc_java():