This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-grpc.git


The following commit(s) were added to refs/heads/main by this push:
     new bd2ec65d Generate Scala 3-friendly gRPC sources (#770)
bd2ec65d is described below

commit bd2ec65d8c3814d361352ca70d20785984dd6962
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Fri Jul 3 20:41:46 2026 +0800

    Generate Scala 3-friendly gRPC sources (#770)
    
    Motivation:
    Scala 3 generated sources still used Scala 2 implicit syntax when 
scala3_sources was enabled, and Gradle users had no direct scala3Sources option.
    
    Modification:
    Generate using/given syntax and Scala 3 wildcard imports from the Scala 
templates when scala3_sources is enabled. Add Gradle scala3Sources wiring for 
pekkoGrpc and scalapb, cover Maven generator setting parsing, and document the 
Gradle and Maven configuration. Use a VarHandle-based Gradle unit-test helper 
instead of reflective Method#setAccessible access.
    
    Result:
    Generated Scala sources can opt into Scala 3 syntax consistently across 
sbt, Gradle, and Maven configuration paths.
    
    Tests:
    - sbt "sbt-plugin/scripted scala3/02-scala3-sourcegen" / environment failed 
before publishing local Scala 3 runtime: missing 
org.apache.pekko:pekko-grpc-runtime_3 SNAPSHOT
    - sbt "codegen / Compile / compile" / passed
    - sbt "maven-plugin / Test / compile" / passed
    - sbt "maven-plugin / Test / testOnly 
org.apache.pekko.grpc.maven.ProtocSpec" / passed
    - cd gradle-plugin && ./gradlew test --tests 
unit.ApplySpecTest.shouldExposeScala3SourcesOption --tests 
unit.ApplySpecTest.shouldPassScala3SourcesToScalaGenerators / passed
    - cd gradle-plugin && ./gradlew test --tests 
unit.ApplySpecTest.shouldPassScala3SourcesToScalaGenerators / environment 
failed once while sbt javafmtCheckAll was running concurrently: sbt boot socket 
Address already in use
    - cd gradle-plugin && ./gradlew test --tests 
unit.ApplySpecTest.shouldPassScala3SourcesToScalaGenerators / passed after 
replacing reflection with VarHandle
    - sbt "++ 3.3.8 runtime/publishLocal" "sbt-plugin/scripted 
scala3/02-scala3-sourcegen" / passed
    - sbt docs/paradox / passed
    - sbt headerCreateAll / passed
    - sbt +headerCheckAll / passed
    - sbt checkCodeStyle / passed
    - scalafmt --mode diff-ref=origin/main / passed
    - scalafmt --list --mode diff-ref=origin/main / passed
    - JDK 17 sbt javafmtAll / passed
    - JDK 17 sbt javafmtCheckAll / passed
    - cd gradle-plugin && ./gradlew test / environment failed on Java 25 
because Gradle 7.6.4 cannot process class file major version 69
    - JDK 17 cd gradle-plugin && ./gradlew test / passed with 
pekkoGrpcTest.baselineVersion=2.0.0-M2-43-6b2d34c7-20260703-0040-SNAPSHOT
    - git diff --check / passed
    
    References:
    Fixes #766
---
 .../grpc/gen/scaladsl/ScalaCompatConstants.scala   |  5 ++-
 .../twirl/templates/ScalaClient/Client.scala.txt   |  8 ++--
 .../templates/ScalaCommon/Marshallers.scala.txt    | 10 ++---
 .../twirl/templates/ScalaServer/Handler.scala.txt  | 22 +++++-----
 docs/src/main/paradox/buildtools/gradle.md         | 15 +++++++
 docs/src/main/paradox/buildtools/maven.md          | 17 ++++++++
 .../pekko/grpc/gradle/PekkoGrpcPlugin.groovy       |  7 +++-
 .../grpc/gradle/PekkoGrpcPluginExtension.groovy    |  1 +
 .../src/test/java/unit/ApplySpecTest.java          | 49 ++++++++++++++++++++++
 .../org/apache/pekko/grpc/maven/ProtocSpec.scala   |  5 +++
 10 files changed, 117 insertions(+), 22 deletions(-)

diff --git 
a/codegen/src/main/scala/org/apache/pekko/grpc/gen/scaladsl/ScalaCompatConstants.scala
 
b/codegen/src/main/scala/org/apache/pekko/grpc/gen/scaladsl/ScalaCompatConstants.scala
index 847f96bc..d7f66128 100644
--- 
a/codegen/src/main/scala/org/apache/pekko/grpc/gen/scaladsl/ScalaCompatConstants.scala
+++ 
b/codegen/src/main/scala/org/apache/pekko/grpc/gen/scaladsl/ScalaCompatConstants.scala
@@ -18,8 +18,11 @@
 package org.apache.pekko.grpc.gen.scaladsl
 
 private[scaladsl] class ScalaCompatConstants(emitScala3Sources: Boolean = 
false) {
-  // val WildcardType: String = if (emitScala3Sources) "?" else "_"
+  val WildcardType: String = if (emitScala3Sources) "?" else "_"
   val WildcardImport: String = if (emitScala3Sources) "*" else "_"
 
   val ImplicitUsing: String = if (emitScala3Sources) "using " else ""
+  val ImplicitParameter: String = if (emitScala3Sources) "using" else 
"implicit"
+  val ImplicitVal: String = if (emitScala3Sources) "given" else "implicit val"
+  val ImplicitDef: String = if (emitScala3Sources) "given" else "implicit def"
 }
diff --git a/codegen/src/main/twirl/templates/ScalaClient/Client.scala.txt 
b/codegen/src/main/twirl/templates/ScalaClient/Client.scala.txt
index e60fc962..01a78aa9 100644
--- a/codegen/src/main/twirl/templates/ScalaClient/Client.scala.txt
+++ b/codegen/src/main/twirl/templates/ScalaClient/Client.scala.txt
@@ -53,15 +53,15 @@ trait @{service.name}Client extends @{service.name} with 
@{service.name}ClientPo
 
 @@PekkoGrpcGenerated
 object @{service.name}Client {
-  def apply(settings: GrpcClientSettings)(implicit sys: 
ClassicActorSystemProvider): @{service.name}Client =
+  def apply(settings: 
GrpcClientSettings)(@{service.scalaCompatConstants.ImplicitParameter} sys: 
ClassicActorSystemProvider): @{service.name}Client =
     new Default@{service.name}Client(GrpcChannel(settings), isChannelOwned = 
true)
-  def apply(channel: GrpcChannel)(implicit sys: ClassicActorSystemProvider): 
@{service.name}Client =
+  def apply(channel: 
GrpcChannel)(@{service.scalaCompatConstants.ImplicitParameter} sys: 
ClassicActorSystemProvider): @{service.name}Client =
     new Default@{service.name}Client(channel, isChannelOwned = false)
 
-  private class Default@{service.name}Client(channel: GrpcChannel, 
isChannelOwned: Boolean)(implicit sys: ClassicActorSystemProvider) extends 
@{service.name}Client {
+  private class Default@{service.name}Client(channel: GrpcChannel, 
isChannelOwned: Boolean)(@{service.scalaCompatConstants.ImplicitParameter} sys: 
ClassicActorSystemProvider) extends @{service.name}Client {
     import 
@{service.name}.MethodDescriptors.@{service.scalaCompatConstants.WildcardImport}
 
-    private implicit val ex: ExecutionContext = sys.classicSystem.dispatcher
+    private @{service.scalaCompatConstants.ImplicitVal} ex: ExecutionContext = 
sys.classicSystem.dispatcher
     private val settings = channel.settings
     private val options = NettyClientUtils.callOptions(settings)
 
diff --git a/codegen/src/main/twirl/templates/ScalaCommon/Marshallers.scala.txt 
b/codegen/src/main/twirl/templates/ScalaCommon/Marshallers.scala.txt
index b531649e..8c473fd4 100644
--- a/codegen/src/main/twirl/templates/ScalaCommon/Marshallers.scala.txt
+++ b/codegen/src/main/twirl/templates/ScalaCommon/Marshallers.scala.txt
@@ -36,18 +36,18 @@ import pekko.grpc.PekkoGrpcGenerated
 @@PekkoGrpcGenerated
 object @{service.name}Marshallers {
   @for(serializer <- service.serializers) {
-  implicit val @serializer.name: 
ScalapbProtobufSerializer[@{serializer.scalaType}] = 
@{service.packageName}.@{service.name}.Serializers.@{serializer.name}
+  @{service.scalaCompatConstants.ImplicitVal} @serializer.name: 
ScalapbProtobufSerializer[@{serializer.scalaType}] = 
@{service.packageName}.@{service.name}.Serializers.@{serializer.name}
   }
 
-  implicit def unmarshaller[T](implicit serializer: ProtobufSerializer[T], 
mat: Materializer): FromRequestUnmarshaller[T] =
+  @{service.scalaCompatConstants.ImplicitDef} 
unmarshaller[T](@{service.scalaCompatConstants.ImplicitParameter} serializer: 
ProtobufSerializer[T], mat: Materializer): FromRequestUnmarshaller[T] =
     Unmarshaller((_: ExecutionContext) => (req: HttpRequest) => 
GrpcMarshalling.unmarshal(req)(serializer, mat))
 
-  implicit def toSourceUnmarshaller[T](implicit serializer: 
ProtobufSerializer[T], mat: Materializer): 
FromRequestUnmarshaller[pekko.stream.scaladsl.Source[T, pekko.NotUsed]] =
+  @{service.scalaCompatConstants.ImplicitDef} 
toSourceUnmarshaller[T](@{service.scalaCompatConstants.ImplicitParameter} 
serializer: ProtobufSerializer[T], mat: Materializer): 
FromRequestUnmarshaller[pekko.stream.scaladsl.Source[T, pekko.NotUsed]] =
     Unmarshaller((_: ExecutionContext) => (req: HttpRequest) => 
GrpcMarshalling.unmarshalStream(req)(serializer, mat))
 
-  implicit def marshaller[T](implicit serializer: ProtobufSerializer[T], 
writer: GrpcProtocolWriter, system: ClassicActorSystemProvider): 
ToResponseMarshaller[T] =
+  @{service.scalaCompatConstants.ImplicitDef} 
marshaller[T](@{service.scalaCompatConstants.ImplicitParameter} serializer: 
ProtobufSerializer[T], writer: GrpcProtocolWriter, system: 
ClassicActorSystemProvider): ToResponseMarshaller[T] =
     Marshaller.opaque((response: T) => 
GrpcMarshalling.marshal(response)(serializer, writer, system))
 
-  implicit def fromSourceMarshaller[T](implicit serializer: 
ProtobufSerializer[T], writer: GrpcProtocolWriter, system: 
ClassicActorSystemProvider): 
ToResponseMarshaller[pekko.stream.scaladsl.Source[T, pekko.NotUsed]] =
+  @{service.scalaCompatConstants.ImplicitDef} 
fromSourceMarshaller[T](@{service.scalaCompatConstants.ImplicitParameter} 
serializer: ProtobufSerializer[T], writer: GrpcProtocolWriter, system: 
ClassicActorSystemProvider): 
ToResponseMarshaller[pekko.stream.scaladsl.Source[T, pekko.NotUsed]] =
     Marshaller.opaque((response: pekko.stream.scaladsl.Source[T, 
pekko.NotUsed]) => GrpcMarshalling.marshalStream(response)(serializer, writer, 
system))
 }
diff --git a/codegen/src/main/twirl/templates/ScalaServer/Handler.scala.txt 
b/codegen/src/main/twirl/templates/ScalaServer/Handler.scala.txt
index e8c6af53..5106b44a 100644
--- a/codegen/src/main/twirl/templates/ScalaServer/Handler.scala.txt
+++ b/codegen/src/main/twirl/templates/ScalaServer/Handler.scala.txt
@@ -54,7 +54,7 @@ object @{serviceName}Handler {
      * Use `org.apache.pekko.grpc.scaladsl.ServiceHandler.concatOrNotFound` 
with `@{service.name}Handler.partial` when combining
      * several services.
      */
-    def apply(implementation: @serviceName)(implicit system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
+    def apply(implementation: 
@serviceName)(@{service.scalaCompatConstants.ImplicitParameter} system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
       handler(implementation, @{service.name}.name, 
GrpcExceptionHandler.defaultMapper)
 
     /**
@@ -64,7 +64,7 @@ object @{serviceName}Handler {
      * Use `org.apache.pekko.grpc.scaladsl.ServiceHandler.concatOrNotFound` 
with `@{service.name}Handler.partial` when combining
      * several services.
      */
-    def apply(implementation: @serviceName, eHandler: ActorSystem => 
PartialFunction[Throwable, Trailers])(implicit system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
+    def apply(implementation: @serviceName, eHandler: ActorSystem => 
PartialFunction[Throwable, 
Trailers])(@{service.scalaCompatConstants.ImplicitParameter} system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
       handler(implementation, @{service.name}.name, eHandler)
 
     /**
@@ -76,7 +76,7 @@ object @{serviceName}Handler {
      *
      * Registering a gRPC service under a custom prefix is not widely 
supported and strongly discouraged by the specification.
      */
-    def apply(implementation: @serviceName, prefix: String)(implicit system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
+    def apply(implementation: @serviceName, prefix: 
String)(@{service.scalaCompatConstants.ImplicitParameter} system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
       handler(implementation, prefix, GrpcExceptionHandler.defaultMapper)
 
     /**
@@ -88,7 +88,7 @@ object @{serviceName}Handler {
      *
      * Registering a gRPC service under a custom prefix is not widely 
supported and strongly discouraged by the specification.
      */
-    def apply(implementation: @serviceName, prefix: String, eHandler: 
ActorSystem => PartialFunction[Throwable, Trailers])(implicit system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
+    def apply(implementation: @serviceName, prefix: String, eHandler: 
ActorSystem => PartialFunction[Throwable, 
Trailers])(@{service.scalaCompatConstants.ImplicitParameter} system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
       handler(implementation, prefix, eHandler)
 
 @if(serviceName != "ServerReflection") {
@@ -101,7 +101,7 @@ object @{serviceName}Handler {
      * Use `org.apache.pekko.grpc.scaladsl.ServiceHandler.concatOrNotFound` 
with `@{service.name}Handler.partial` when combining
      * several services.
      */
-    def withServerReflection(implementation: @serviceName)(implicit system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
+    def withServerReflection(implementation: 
@serviceName)(@{service.scalaCompatConstants.ImplicitParameter} system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] =
         pekko.grpc.scaladsl.ServiceHandler.concatOrNotFound(
           @{serviceName}Handler.partial(implementation),
           pekko.grpc.scaladsl.ServerReflection.partial(List(@{service.name})))
@@ -115,9 +115,9 @@ object @{serviceName}Handler {
           null
       }
 
-    private def handler(implementation: @serviceName, prefix: String, 
eHandler: ActorSystem => PartialFunction[Throwable, Trailers])(implicit system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] = {
-      implicit val mat: Materializer = SystemMaterializer(system).materializer
-      implicit val ec: ExecutionContext = mat.executionContext
+    private def handler(implementation: @serviceName, prefix: String, 
eHandler: ActorSystem => PartialFunction[Throwable, 
Trailers])(@{service.scalaCompatConstants.ImplicitParameter} system: 
ClassicActorSystemProvider): model.HttpRequest => 
scala.concurrent.Future[model.HttpResponse] = {
+      @{service.scalaCompatConstants.ImplicitVal} mat: Materializer = 
SystemMaterializer(system).materializer
+      @{service.scalaCompatConstants.ImplicitVal} ec: ExecutionContext = 
mat.executionContext
       val spi = TelemetryExtension(system).spi
 
       import 
@{service.name}.Serializers.@{service.scalaCompatConstants.WildcardImport}
@@ -158,9 +158,9 @@ object @{serviceName}Handler {
      *
      * Registering a gRPC service under a custom prefix is not widely 
supported and strongly discouraged by the specification.
      */
-    def partial(implementation: @serviceName, prefix: String = 
@{service.name}.name, eHandler: ActorSystem => PartialFunction[Throwable, 
Trailers] = GrpcExceptionHandler.defaultMapper)(implicit system: 
ClassicActorSystemProvider): PartialFunction[model.HttpRequest, 
scala.concurrent.Future[model.HttpResponse]] = {
-      implicit val mat: Materializer = SystemMaterializer(system).materializer
-      implicit val ec: ExecutionContext = mat.executionContext
+    def partial(implementation: @serviceName, prefix: String = 
@{service.name}.name, eHandler: ActorSystem => PartialFunction[Throwable, 
Trailers] = 
GrpcExceptionHandler.defaultMapper)(@{service.scalaCompatConstants.ImplicitParameter}
 system: ClassicActorSystemProvider): PartialFunction[model.HttpRequest, 
scala.concurrent.Future[model.HttpResponse]] = {
+      @{service.scalaCompatConstants.ImplicitVal} mat: Materializer = 
SystemMaterializer(system).materializer
+      @{service.scalaCompatConstants.ImplicitVal} ec: ExecutionContext = 
mat.executionContext
       val spi = TelemetryExtension(system).spi
 
       import 
@{service.name}.Serializers.@{service.scalaCompatConstants.WildcardImport}
diff --git a/docs/src/main/paradox/buildtools/gradle.md 
b/docs/src/main/paradox/buildtools/gradle.md
index 7afd4d46..dc1c8462 100644
--- a/docs/src/main/paradox/buildtools/gradle.md
+++ b/docs/src/main/paradox/buildtools/gradle.md
@@ -38,6 +38,7 @@ Names and default values are provided.
         generatePlay = false
         usePlayActions = false
         serverPowerApis = false
+        scala3Sources = false
         extraGenerators = []
     }
     ```
@@ -58,6 +59,20 @@ To additionally generate server "power APIs" that have 
access to request metadat
     ```
     @@@
 
+### Generating Scala 3 sources
+
+For Scala projects, set `scala3Sources` to generate Scala 3-friendly sources:
+
+`build.gradle`
+:   @@@vars
+    ```gradle
+    pekkoGrpc {
+      ...
+      scala3Sources = true
+    }
+    ```
+    @@@
+
 ## Protoc version
 
 The version and the location of `protoc` can be changed using 
`protobuf-gradle-plugin` 
[settings](https://github.com/google/protobuf-gradle-plugin#locate-external-executables).
diff --git a/docs/src/main/paradox/buildtools/maven.md 
b/docs/src/main/paradox/buildtools/maven.md
index 7d9354c2..52a34310 100644
--- a/docs/src/main/paradox/buildtools/maven.md
+++ b/docs/src/main/paradox/buildtools/maven.md
@@ -53,6 +53,23 @@ To additionally generate server "power APIs" that have 
access to request metadat
     </plugin>
     ```
 
+### Generating Scala 3 sources
+
+For Scala projects, set `scala3Sources` in `generatorSettings` to generate 
Scala 3-friendly sources:
+
+`pom.xml`
+:   ```xml
+    <plugin>
+        ...
+        <configuration>
+          ...
+          <generatorSettings>
+            <scala3Sources>true</scala3Sources>
+          </generatorSettings>
+        </configuration>
+    </plugin>
+    ```
+
 ## Proto source directory
 
 By default the plugin looks for `.proto`-files under `src/main/protobuf` (and 
`src/main/proto`). This can be changed with the `protoPaths` setting,
diff --git 
a/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPlugin.groovy
 
b/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPlugin.groovy
index df3a2a1a..090f5747 100644
--- 
a/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPlugin.groovy
+++ 
b/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPlugin.groovy
@@ -140,6 +140,9 @@ class PekkoGrpcPlugin implements Plugin<Project> {
                             if (pekkoGrpcExt.generatePlay) {
                                 option "generate_play=true"
                             }
+                            if (pekkoGrpcExt.scala && 
pekkoGrpcExt.scala3Sources) {
+                                option "scala3_sources=true"
+                            }
                             if (pekkoGrpcExt.scala) {
                                 option "flat_package"
                             }
@@ -147,6 +150,9 @@ class PekkoGrpcPlugin implements Plugin<Project> {
                         if (pekkoGrpcExt.scala) {
                             scalapb {
                                 option "flat_package"
+                                if (pekkoGrpcExt.scala3Sources) {
+                                    option "scala3_sources"
+                                }
                             }
                         }
                     }
@@ -202,4 +208,3 @@ class PekkoGrpcPlugin implements Plugin<Project> {
         scalaVersions.first()
     }
 }
-
diff --git 
a/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPluginExtension.groovy
 
b/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPluginExtension.groovy
index 7de24576..7a938b42 100644
--- 
a/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPluginExtension.groovy
+++ 
b/gradle-plugin/src/main/groovy/org/apache/pekko/grpc/gradle/PekkoGrpcPluginExtension.groovy
@@ -31,6 +31,7 @@ class PekkoGrpcPluginExtension {
     boolean serverPowerApis = false
     boolean usePlayActions = false
     boolean includeStdTypes = false
+    boolean scala3Sources = false
 
     List<String> extraGenerators = []
 
diff --git a/gradle-plugin/src/test/java/unit/ApplySpecTest.java 
b/gradle-plugin/src/test/java/unit/ApplySpecTest.java
index e2669e71..71134e3c 100644
--- a/gradle-plugin/src/test/java/unit/ApplySpecTest.java
+++ b/gradle-plugin/src/test/java/unit/ApplySpecTest.java
@@ -11,14 +11,19 @@ package unit;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.nio.file.Files;
+import java.util.Collection;
 import java.util.stream.Stream;
 
+import com.google.protobuf.gradle.GenerateProtoTask;
 import helper.BaseSpec;
 import helper.ScalaWrapperPlugin;
 import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension;
 import org.gradle.api.Project;
 import org.gradle.api.ProjectConfigurationException;
+import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.testfixtures.ProjectBuilder;
 import org.gradle.testkit.runner.BuildResult;
@@ -36,6 +41,8 @@ import static org.junit.jupiter.api.Assertions.*;
 
 class ApplySpecTest extends BaseSpec {
 
+    private static final VarHandle PLUGINS_HANDLE = pluginsHandle();
+
     private Project project;
 
     private File log;
@@ -127,6 +134,48 @@ class ApplySpecTest extends BaseSpec {
         assertTrue(pekkoGrpcExt.getScala());
     }
 
+    @Test
+    void shouldExposeScala3SourcesOption() throws IOException {
+        PekkoGrpcPluginExtension pekkoGrpcExt = sampleSetup(project);
+
+        assertFalse(pekkoGrpcExt.getScala3Sources());
+
+        pekkoGrpcExt.setScala3Sources(true);
+
+        assertTrue(pekkoGrpcExt.getScala3Sources());
+    }
+
+    @Test
+    void shouldPassScala3SourcesToScalaGenerators() throws Exception {
+        PekkoGrpcPluginExtension pekkoGrpcExt = sampleSetup(project);
+        pekkoGrpcExt.setScala3Sources(true);
+
+        ((ProjectInternal) project).evaluate();
+
+        GenerateProtoTask task = (GenerateProtoTask) 
project.getTasks().getByName("generateProto");
+        Collection<GenerateProtoTask.PluginOptions> plugins = 
pluginOptionsForCaching(task);
+        assertTrue(plugins.stream()
+                .filter(plugin -> "pekkoGrpc".equals(plugin.getName()))
+                .anyMatch(plugin -> 
plugin.getOptions().contains("scala3_sources=true")));
+        assertTrue(plugins.stream()
+                .filter(plugin -> "scalapb".equals(plugin.getName()))
+                .anyMatch(plugin -> 
plugin.getOptions().contains("scala3_sources")));
+    }
+
+    @SuppressWarnings("unchecked")
+    private Collection<GenerateProtoTask.PluginOptions> 
pluginOptionsForCaching(GenerateProtoTask task) {
+        return (NamedDomainObjectContainer<GenerateProtoTask.PluginOptions>) 
PLUGINS_HANDLE.get(task);
+    }
+
+    private static VarHandle pluginsHandle() {
+        try {
+            return MethodHandles.privateLookupIn(GenerateProtoTask.class, 
MethodHandles.lookup())
+                    .findVarHandle(GenerateProtoTask.class, "plugins", 
NamedDomainObjectContainer.class);
+        } catch (IllegalAccessException | NoSuchFieldException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
     @Test
     void shouldDisableCompileJavaIfNoJavaSourceFilesFound() throws IOException 
{
         sampleSetup(project);
diff --git 
a/maven-plugin/src/test/scala/org/apache/pekko/grpc/maven/ProtocSpec.scala 
b/maven-plugin/src/test/scala/org/apache/pekko/grpc/maven/ProtocSpec.scala
index 7a6798be..92b32a54 100644
--- a/maven-plugin/src/test/scala/org/apache/pekko/grpc/maven/ProtocSpec.scala
+++ b/maven-plugin/src/test/scala/org/apache/pekko/grpc/maven/ProtocSpec.scala
@@ -43,5 +43,10 @@ class ProtocSpec extends AnyWordSpec with Matchers {
       val settings = Map("flatPackage" -> "true", "serverPowerApis" -> "true")
       AbstractGenerateMojo.parseGeneratorSettings(settings.asJava) shouldBe 
Seq("flat_package", "server_power_apis")
     }
+
+    "support the scala3Sources setting" in {
+      val settings = Map("scala3Sources" -> "true")
+      AbstractGenerateMojo.parseGeneratorSettings(settings.asJava) shouldBe 
Seq("scala3_sources")
+    }
   }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to