diff --git a/docs/src/main/asciidoc/grpc-generation-reference.adoc b/docs/src/main/asciidoc/grpc-generation-reference.adoc index 70c0a4cde0fb6..00c5f37c9aefe 100644 --- a/docs/src/main/asciidoc/grpc-generation-reference.adoc +++ b/docs/src/main/asciidoc/grpc-generation-reference.adoc @@ -165,6 +165,13 @@ quarkus.generate-code.grpc.scan-for-proto-exclude.":"=foo/p Note that `:` characters in the property keys must be escaped. +== Kotlin code generation + +`protoc` also supports https://protobuf.dev/reference/kotlin/kotlin-generated/[generating Kotlin code] in addition to the generated Java code. + +By default, the Kotlin code generation is enabled if the dependency `io.quarkus:quarkus-kotlin` is present. +To explicitly en-/disable this feature, set the `quarkus.generate-code.grpc.kotlin.generate` property in your `application.properties` file to `true` or `false`. + == Skipping code generation You can skip gRPC code generation using: diff --git a/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java b/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java index d4e67796e2420..581adc6d17f33 100644 --- a/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java +++ b/extensions/grpc/codegen/src/main/java/io/quarkus/grpc/deployment/GrpcCodeGen.java @@ -23,6 +23,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -66,8 +67,11 @@ public class GrpcCodeGen implements CodeGenProvider { private static final String USE_ARG_FILE = "quarkus.generate-code.grpc.use-arg-file"; + private static final String GENERATE_KOTLIN = "quarkus.generate-code.grpc.kotlin.generate"; + private Executables executables; private String input; + private boolean hasQuarkusKotlinDependency; @Override public String providerId() { @@ -95,6 +99,7 @@ public Path getInputDirectory() { @Override public void init(ApplicationModel model, Map properties) { this.input = properties.get("quarkus.grpc.codegen.proto-directory"); + this.hasQuarkusKotlinDependency = containsQuarkusKotlin(model.getDependencies()); } @Override @@ -162,6 +167,10 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { "--grpc_out=" + outDir, "--java_out=" + outDir)); + if (shouldGenerateKotlin(context.config())) { + command.add("--kotlin_out=" + outDir); + } + if (shouldGenerateDescriptorSet(context.config())) { command.add(String.format("--descriptor_set_out=%s", getDescriptorSetOutputFile(context))); } @@ -296,6 +305,11 @@ private boolean isGeneratingFromAppDependenciesEnabled(Config config) { .filter(value -> !"none".equals(value)).isPresent(); } + private boolean shouldGenerateKotlin(Config config) { + return config.getOptionalValue(GENERATE_KOTLIN, Boolean.class).orElse( + hasQuarkusKotlinDependency); + } + private boolean shouldGenerateDescriptorSet(Config config) { return config.getOptionalValue(GENERATE_DESCRIPTOR_SET, Boolean.class).orElse(FALSE); } @@ -526,6 +540,16 @@ private static void writePluginExeCmd(Path pluginPath, BufferedWriter writer) th writer.newLine(); } + private static boolean containsQuarkusKotlin(Collection dependencies) { + return dependencies.stream().anyMatch(new Predicate() { + @Override + public boolean test(ResolvedDependency rd) { + return rd.getGroupId().equalsIgnoreCase("io.quarkus") + && rd.getArtifactId().equalsIgnoreCase("quarkus-kotlin"); + } + }); + } + private static class Executables { final Path protoc; diff --git a/integration-tests/gradle/src/main/resources/kotlin-grpc-project/src/main/kotlin/org/acme/ExampleResource.kt b/integration-tests/gradle/src/main/resources/kotlin-grpc-project/src/main/kotlin/org/acme/ExampleResource.kt index e1ee93710f419..dcf11b93a86cd 100644 --- a/integration-tests/gradle/src/main/resources/kotlin-grpc-project/src/main/kotlin/org/acme/ExampleResource.kt +++ b/integration-tests/gradle/src/main/resources/kotlin-grpc-project/src/main/kotlin/org/acme/ExampleResource.kt @@ -6,11 +6,12 @@ import jakarta.ws.rs.Produces import jakarta.ws.rs.core.MediaType import io.quarkus.example.HelloMsg +import io.quarkus.example.helloMsg @Path("/hello") class ExampleResource { @GET @Produces(MediaType.TEXT_PLAIN) - fun hello() = "hello" + HelloMsg.Status.TEST_ONE.getNumber() + fun hello() = "hello" + helloMsg { status = HelloMsg.Status.TEST_ONE }.statusValue } \ No newline at end of file