Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support protoc Kotlin code generation #44552

Merged
merged 1 commit into from
Feb 28, 2025

Conversation

PhilKes
Copy link
Contributor

@PhilKes PhilKes commented Nov 17, 2024

Closes #39127

This PR adds support to generate Kotlin DSLs/Extensions for protoc via adding the kotlin_out parameter to the protoc command:

  • By default, the Kotlin code generation is enabled if the dependency io.quarkus:quarkus-kotlin is present
  • It can be explicetely en-/disabled via the quarkus.generate-code.grpc.kotlin.generate property

To test it I reused the kotlin-grpc-project integration-test to use the generated DSL for HelloMsg.
Now currently when running the KotlinGRPCProjectBuildTest, compileKotlin fails with:

e: file://.../quarkus/integration-tests/gradle/target/classes/kotlin-grpc-project/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:15:17 Annotation argument must be a compile-time constant.
e: file://.../quarkus/integration-tests/gradle/target/classes/kotlin-grpc-project/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:15:37 Unresolved reference 'kotlin'.
e: file://.../quarkus/integration-tests/gradle/target/classes/kotlin-grpc-project/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:16:24 Unresolved reference 'kotlin'.
HelloMsgKt.kt
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: hello.proto

// Generated files should ignore deprecation warnings
@file:Suppress("DEPRECATION")
package io.quarkus.example;

@kotlin.jvm.JvmName("-initializehelloMsg")
public inline fun helloMsg(block: io.quarkus.example.HelloMsgKt.Dsl.() -> kotlin.Unit): io.quarkus.example.HelloMsg =
io.quarkus.example.HelloMsgKt.Dsl._create(io.quarkus.example.HelloMsg.newBuilder()).apply { block() }._build()
/**
* Protobuf type `io.quarkus.example.HelloMsg`
*/
public object HelloMsgKt {
@kotlin.OptIn(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)
@com.google.protobuf.kotlin.ProtoDslMarker
public class Dsl private constructor(
  private val _builder: io.quarkus.example.HelloMsg.Builder
) {
  public companion object {
    @kotlin.jvm.JvmSynthetic
    @kotlin.PublishedApi
    internal fun _create(builder: io.quarkus.example.HelloMsg.Builder): Dsl = Dsl(builder)
  }

  @kotlin.jvm.JvmSynthetic
  @kotlin.PublishedApi
  internal fun _build(): io.quarkus.example.HelloMsg = _builder.build()

  /**
   * `string message = 1;`
   */
  public var message: kotlin.String
    @JvmName("getMessage")
    get() = _builder.getMessage()
    @JvmName("setMessage")
    set(value) {
      _builder.setMessage(value)
    }
  /**
   * `string message = 1;`
   */
  public fun clearMessage() {
    _builder.clearMessage()
  }

  /**
   * `.google.protobuf.Timestamp date_time = 2;`
   */
  public var dateTime: com.google.protobuf.Timestamp
    @JvmName("getDateTime")
    get() = _builder.getDateTime()
    @JvmName("setDateTime")
    set(value) {
      _builder.setDateTime(value)
    }
  /**
   * `.google.protobuf.Timestamp date_time = 2;`
   */
  public fun clearDateTime() {
    _builder.clearDateTime()
  }
  /**
   * `.google.protobuf.Timestamp date_time = 2;`
   * @return Whether the dateTime field is set.
   */
  public fun hasDateTime(): kotlin.Boolean {
    return _builder.hasDateTime()
  }

  /**
   * `.io.quarkus.example.HelloMsg.Status status = 3;`
   */
  public var status: io.quarkus.example.HelloMsg.Status
    @JvmName("getStatus")
    get() = _builder.getStatus()
    @JvmName("setStatus")
    set(value) {
      _builder.setStatus(value)
    }
  public var statusValue: kotlin.Int
    @JvmName("getStatusValue")
    get() = _builder.getStatusValue()
    @JvmName("setStatusValue")
    set(value) {
      _builder.setStatusValue(value)
    }
  /**
   * `.io.quarkus.example.HelloMsg.Status status = 3;`
   */
  public fun clearStatus() {
    _builder.clearStatus()
  }
}
}
@kotlin.jvm.JvmSynthetic
public inline fun io.quarkus.example.HelloMsg.copy(block: `io.quarkus.example`.HelloMsgKt.Dsl.() -> kotlin.Unit): io.quarkus.example.HelloMsg =
`io.quarkus.example`.HelloMsgKt.Dsl._create(this.toBuilder()).apply { block() }._build()

public val io.quarkus.example.HelloMsgOrBuilder.dateTimeOrNull: com.google.protobuf.Timestamp?
get() = if (hasDateTime()) getDateTime() else null

I am guessing this is because the generated kotlin code should not be in build/classes/java but rather build/classes/kotlin?
But if we provide a different output path for the kotlin_out than for java_out we would need a way to add this extra path also to the compile sources, which is done in CodeGenerator.init(), right?

Copy link

github-actions bot commented Nov 17, 2024

🙈 The PR is closed and the preview is expired.

This comment has been minimized.

This comment has been minimized.

Copy link
Member

@gsmet gsmet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks promising, thanks for the PR.

I suggested some minor doc changes and more importantly it seems some Gradle tests are failing and I think that's the important bit:

2024-11-17T17:13:44.2059374Z Options for KOTLIN DAEMON: IncrementalCompilationOptions(super=CompilationOptions(compilerMode=INCREMENTAL_COMPILER, targetPlatform=JVM, reportCategories=[0, 3], reportSeverity=2, requestedCompilationResults=[0], kotlinScriptExtensions=[]), areFileChangesKnown=false, modifiedFiles=null, deletedFiles=null, classpathChanges=NotAvailableForNonIncrementalRun, workingDir=/home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/cacheable, multiModuleICSettings=MultiModuleICSettings(buildHistoryFile=/home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/local-state/build-history.bin, useModuleDetection=false), usePreciseJavaTracking=true, icFeatures=IncrementalCompilationFeatures(withAbiSnapshot=false, preciseCompilationResultsBackup=true, keepIncrementalCompilationCachesInMemory=true, enableUnsafeIncrementalCompilationForMultiplatform=false), outputFiles=[/home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/kotlin/main, /home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/cacheable, /home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/local-state])
2024-11-17T17:13:44.2061194Z e: file:///home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:15:17 Annotation argument must be a compile-time constant.
2024-11-17T17:13:44.2063191Z e: file:///home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:15:37 Unresolved reference 'kotlin'.
2024-11-17T17:13:44.2065030Z e: file:///home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:16:24 Unresolved reference 'kotlin'.
2024-11-17T17:13:44.2065363Z Finished executing kotlin compiler using DAEMON strategy

@gsmet
Copy link
Member

gsmet commented Nov 18, 2024

@cdsap I'm not sure if it's related to this change but I have seen the following in the logs and it doesn't look like something we would want:

2024-11-17T17:13:44.2065823Z 2 problems were found storing the configuration cache.
2024-11-17T17:13:44.2068318Z - Task :quarkusDev of type io.quarkus.gradle.tasks.QuarkusDev: cannot serialize object of type 'org.gradle.api.internal.artifacts.configurations.DefaultUnlockedConfiguration', a subtype of 'org.gradle.api.artifacts.Configuration', as these are not supported with the configuration cache.
2024-11-17T17:13:44.2069077Z See https://docs.gradle.org/8.9/userguide/configuration_cache.html#config_cache:requirements:disallowed_types
2024-11-17T17:13:44.2071428Z - Task :quarkusDev of type io.quarkus.gradle.tasks.QuarkusDev: cannot serialize object of type 'org.gradle.api.internal.tasks.DefaultSourceSet', a subtype of 'org.gradle.api.tasks.SourceSet', as these are not supported with the configuration cache.
2024-11-17T17:13:44.2072168Z See https://docs.gradle.org/8.9/userguide/configuration_cache.html#config_cache:requirements:disallowed_types

@cdsap
Copy link
Contributor

cdsap commented Nov 19, 2024

@gsmet I tested with a version without the configuration cache changes (3.15.1), I'm observing the same log:
https://ge.solutions-team.gradle.com/s/g33okmapudxeg/console-log?page=1#L73

Note that I'm executing the build like: ./gradlew clean quarkusDev --configuration-cache --no-build-cache

Will confirm with the team

@PhilKes PhilKes force-pushed the grpc-generate-kotlin branch from 3227bb7 to 8c47257 Compare November 19, 2024 17:41
@PhilKes
Copy link
Contributor Author

PhilKes commented Nov 19, 2024

This looks promising, thanks for the PR.

I suggested some minor doc changes and more importantly it seems some Gradle tests are failing and I think that's the important bit:

2024-11-17T17:13:44.2059374Z Options for KOTLIN DAEMON: IncrementalCompilationOptions(super=CompilationOptions(compilerMode=INCREMENTAL_COMPILER, targetPlatform=JVM, reportCategories=[0, 3], reportSeverity=2, requestedCompilationResults=[0], kotlinScriptExtensions=[]), areFileChangesKnown=false, modifiedFiles=null, deletedFiles=null, classpathChanges=NotAvailableForNonIncrementalRun, workingDir=/home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/cacheable, multiModuleICSettings=MultiModuleICSettings(buildHistoryFile=/home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/local-state/build-history.bin, useModuleDetection=false), usePreciseJavaTracking=true, icFeatures=IncrementalCompilationFeatures(withAbiSnapshot=false, preciseCompilationResultsBackup=true, keepIncrementalCompilationCachesInMemory=true, enableUnsafeIncrementalCompilationForMultiplatform=false), outputFiles=[/home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/kotlin/main, /home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/cacheable, /home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/kotlin/compileKotlin/local-state])
2024-11-17T17:13:44.2061194Z e: file:///home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:15:17 Annotation argument must be a compile-time constant.
2024-11-17T17:13:44.2063191Z e: file:///home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:15:37 Unresolved reference 'kotlin'.
2024-11-17T17:13:44.2065030Z e: file:///home/runner/work/quarkus/quarkus/integration-tests/gradle/target/kotlin-grpc-project12669661681070306965/build/classes/java/quarkus-generated-sources/grpc/io/quarkus/example/HelloMsgKt.kt:16:24 Unresolved reference 'kotlin'.
2024-11-17T17:13:44.2065363Z Finished executing kotlin compiler using DAEMON strategy

Thanks for the feedback. I am aware of the failing tests, as I described in the PR description I am not sure what the problem is since I dont have much experienced with mixed java/kotlin generated sources. Do the generated kotlin sources have to be in another folder than the generated java code? (see my original PR description)

@PhilKes PhilKes requested a review from gsmet November 19, 2024 17:46

This comment has been minimized.

This comment has been minimized.

@PhilKes
Copy link
Contributor Author

PhilKes commented Dec 2, 2024

Any feedback on this, @geoand, @alesj, @cescoffier? 🙂

@cescoffier
Copy link
Member

The PR looks good, but the Gradle issue is problematic.

@cescoffier
Copy link
Member

@PhilKes did you look at the gradle issue?

@PhilKes
Copy link
Contributor Author

PhilKes commented Feb 19, 2025

@PhilKes did you look at the gradle issue?

@cescoffier I looked at it, the generated HelloMsgKt needs the additional dependency protobuf-kotlin for some annotations:

// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: hello.proto
//...
public object HelloMsgKt {
  @kotlin.OptIn(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)
  @com.google.protobuf.kotlin.ProtoDslMarker
  public class Dsl private constructor(
 //...

Now I am not sure how to proceed, should I add a check for the protobuf-kotlin dependency being present (like containsQuarkusKotlin()) or should protobuf-kotlin be added to quarkus-kotlin dependencies?

@cescoffier
Copy link
Member

Should protobuf-kotlin be added to quarkus-kotlin dependencies

Does it need to be available at runtime or only at build time? If it's only build time, we can "just" document that when using Gradle this dependency is necessary.

@PhilKes PhilKes force-pushed the grpc-generate-kotlin branch from 8c47257 to f10e6d2 Compare February 24, 2025 17:16

This comment has been minimized.

@PhilKes PhilKes force-pushed the grpc-generate-kotlin branch from f10e6d2 to ab286fd Compare February 24, 2025 19:00
@quarkus-bot quarkus-bot bot added the area/dependencies Pull requests that update a dependency file label Feb 24, 2025

This comment has been minimized.

@PhilKes
Copy link
Contributor Author

PhilKes commented Feb 24, 2025

Should protobuf-kotlin be added to quarkus-kotlin dependencies

Does it need to be available at runtime or only at build time? If it's only build time, we can "just" document that when using Gradle this dependency is necessary.

Indeed it's only needed at build-time so I added it as a compileOnly dependency and added a note in the documentation

This comment has been minimized.

Copy link
Member

@cescoffier cescoffier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor comment on the doc

@cescoffier cescoffier requested a review from alesj February 25, 2025 06:37
@PhilKes PhilKes force-pushed the grpc-generate-kotlin branch from ab286fd to 45c6907 Compare February 25, 2025 16:40

This comment has been minimized.

Copy link
Member

@cescoffier cescoffier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@cescoffier
Copy link
Member

Let's see what the CI says.

@PhilKes PhilKes force-pushed the grpc-generate-kotlin branch from 45c6907 to 4c047a3 Compare February 25, 2025 19:40

This comment has been minimized.

This comment has been minimized.

@PhilKes
Copy link
Contributor Author

PhilKes commented Feb 26, 2025

@cescoffier looks like there is something wrong with the build pipeline?

./mvnw: line 327: /c/hostedtoolcache/windows/Java_Temurin-Hotspot_jdk/17.0.14-7/x64/bin/java: Argument list too long
https://github.com/quarkusio/quarkus/actions/runs/13529572158/job/37808886665?pr=44552#step:17:36

@cescoffier
Copy link
Member

Hum, no, on Windows we need to use the file approach to workaround that issue. So, arguments are written in a file and passed as @file if I remember correctly

@PhilKes PhilKes force-pushed the grpc-generate-kotlin branch from 4c047a3 to 9199d38 Compare February 27, 2025 17:30
Copy link

quarkus-bot bot commented Feb 27, 2025

Status for workflow Quarkus Documentation CI

This is the status report for running Quarkus Documentation CI on commit 9199d38.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

Warning

There are other workflow runs running, you probably need to wait for their status before merging.

Copy link

quarkus-bot bot commented Feb 27, 2025

Status for workflow Quarkus CI

This is the status report for running Quarkus CI on commit 9199d38.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

You can consult the Develocity build scans.

@PhilKes
Copy link
Contributor Author

PhilKes commented Feb 27, 2025

Hum, no, on Windows we need to use the file approach to workaround that issue. So, arguments are written in a file and passed as @file if I remember correctly

Well the build succeeded now after an update via ebase

@cescoffier cescoffier merged commit 6b58d41 into quarkusio:main Feb 28, 2025
61 checks passed
@quarkus-bot quarkus-bot bot added this to the 3.21 - main milestone Feb 28, 2025
@quarkus-bot quarkus-bot bot added the kind/enhancement New feature or request label Feb 28, 2025
@cescoffier
Copy link
Member

Thanks!

@edeandrea
Copy link
Contributor

edeandrea commented Mar 7, 2025

Hello. I think this is the culprit of what we're seeing in #23612 (comment)

In the superheroes sample application, the grpc-locations application build is now failing since March 2.

Setting quarkus.generate-code.grpc.kotlin.generate=false does allow me to get around this problem, but there is definitely a problem here.

I've created #46675 to track this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[gRPC/protoc] Support for Kotlin code generation
6 participants