Skip to content

Added support for Well-Known Types #449

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

Merged
merged 3 commits into from
Aug 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/protobuf-well-known-types.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Verify Protobuf Well-Known Types are Up-to-Date

on:
pull_request:

permissions:
contents: read

jobs:
verify-platforms-table:
name: Run Verification
runs-on: ubuntu-latest
steps:
- name: Checkout Sources
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Run Protobuf Conformance Test Generation
run: ./gradlew :protobuf:protobuf-core:bufGenerateCommonMain --info --stacktrace
- name: Check if Well-Known Types are up-to-date
run: |
if [[ -n "$(git status --porcelain | grep protobuf/protobuf-core/)" ]]; then
echo "Well-Known Types are not up-to-date. Please run './gradlew :protobuf:protobuf-core:bufGenerateCommonMain' and commit changes"
exit 1
fi
10 changes: 2 additions & 8 deletions gradle-plugin/api/gradle-plugin.api
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ public class kotlinx/rpc/RpcStrictModeExtension {

public final class kotlinx/rpc/VersionsKt {
public static final field BUF_TOOL_VERSION Ljava/lang/String;
public static final field GRPC_KOTLIN_VERSION Ljava/lang/String;
public static final field GRPC_VERSION Ljava/lang/String;
public static final field LIBRARY_VERSION Ljava/lang/String;
public static final field PLUGIN_VERSION Ljava/lang/String;
public static final field PROTOBUF_VERSION Ljava/lang/String;
}

public class kotlinx/rpc/buf/BufExtension {
Expand Down Expand Up @@ -179,17 +175,15 @@ public final class kotlinx/rpc/protoc/ProcessProtoFilesKt$inlined$sam$i$org_grad

public abstract interface class kotlinx/rpc/protoc/ProtoSourceSet {
public abstract fun getName ()Ljava/lang/String;
public abstract fun getPlugins ()Lorg/gradle/api/NamedDomainObjectContainer;
public abstract fun getProto ()Lorg/gradle/api/file/SourceDirectorySet;
public abstract fun plugins (Lorg/gradle/api/Action;)V
public fun proto (Lorg/gradle/api/Action;)V
public abstract fun protocPlugin (Lkotlinx/rpc/protoc/ProtocPlugin;)V
public abstract fun protocPlugin (Lorg/gradle/api/NamedDomainObjectProvider;)V
}

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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public abstract class BufGenerateTask : BufExecTask() {
/**
* Whether to include Well-Known Types.
*
* Automatically sets [includeImports] to `true`.
*
* @see <a href="https://buf.build/docs/reference/cli/buf/generate/#include-wkt">buf generate --include-wkt</a>
* @see [BufGenerateExtension.includeWkt]
*/
Expand Down Expand Up @@ -91,7 +93,7 @@ public abstract class BufGenerateTask : BufExecTask() {
buildList {
add("--output"); add(outputDirectory.get().absolutePath)

if (includeImports.get()) {
if (includeImports.get() || includeWkt.get()) {
add("--include-imports")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,40 @@ package kotlinx.rpc.internal
import kotlinx.rpc.buf.tasks.BufGenerateTask
import kotlinx.rpc.protoc.grpcKotlinMultiplatform
import kotlinx.rpc.protoc.kotlinMultiplatform
import kotlinx.rpc.protoc.protoSourceSets
import kotlinx.rpc.rpcExtension
import org.gradle.api.Project
import org.gradle.internal.extensions.core.extra
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.withType

@InternalRpcApi
public fun Project.configureLocalProtocGenDevelopmentDependency() {
public fun Project.configureLocalProtocGenDevelopmentDependency(
vararg sourceSetSuffix: String = arrayOf("Test"),
) {
val globalRootDir: String by extra

rpcExtension().protoc.plugins {
kotlinMultiplatform {
local {
javaJar("$globalRootDir/protoc-gen/protobuf/build/libs/protobuf-$version-all.jar")
// init
rpcExtension().protoc()

protoSourceSets.all {
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")
grpcKotlinMultiplatform {
local {
javaJar("$globalRootDir/protoc-gen/grpc/build/libs/grpc-$version-all.jar")
}
}
}
}

tasks.withType<BufGenerateTask>().configureEach {
if (name.endsWith("Test")) {
if (sourceSetSuffix.any { name.endsWith(it) }) {
val includedBuild = gradle.includedBuild("protoc-gen")
dependsOn(includedBuild.task(":grpc:jar"))
dependsOn(includedBuild.task(":protobuf:jar"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package kotlinx.rpc.protoc

import kotlinx.rpc.buf.tasks.BufGenerateTask
import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM
import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM
import kotlinx.rpc.util.findOrCreate
import kotlinx.rpc.util.withKotlinJvmExtension
import kotlinx.rpc.util.withKotlinKmpExtension
Expand All @@ -17,6 +19,9 @@ import org.gradle.api.tasks.SourceSetContainer
import org.gradle.kotlin.dsl.add
import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.property
import org.gradle.kotlin.dsl.the
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
import javax.inject.Inject

@Suppress("UNCHECKED_CAST")
Expand All @@ -34,18 +39,48 @@ internal open class DefaultProtoSourceSet @Inject constructor(
internal val project: Project,
override val name: String,
) : ProtoSourceSet {
val languageSourceSets: ListProperty<Any> = project.objects.listProperty<Any>()
val protocPlugins: ListProperty<String> = project.objects.listProperty<String>().convention(emptyList())
val generateTask: Property<BufGenerateTask> = project.objects.property<BufGenerateTask>()
override val plugins: NamedDomainObjectContainer<ProtocPlugin> =
project.objects.domainObjectContainer(ProtocPlugin::class.java) { name ->
ProtocPlugin(name, project)
}

override fun protocPlugin(plugin: NamedDomainObjectProvider<ProtocPlugin>) {
protocPlugins.add(plugin.name)
override fun plugins(action: Action<NamedDomainObjectContainer<ProtocPlugin>>) {
action.execute(plugins)
}

override fun protocPlugin(plugin: ProtocPlugin) {
protocPlugins.add(plugin.name)
init {
val explicitApiModeEnabled = project.provider {
project.the<KotlinBaseExtension>().explicitApi != ExplicitApiMode.Disabled
}

plugins.create(KOTLIN_MULTIPLATFORM) {
local {
javaJar(project.kotlinMultiplatformProtocPluginJarPath)
}

options.put("debugOutput", "protoc-gen-kotlin-multiplatform.log")

if ([email protected]().endsWith("main")) {
options.put("explicitApiModeEnabled", explicitApiModeEnabled)
}
}

plugins.create(GRPC_KOTLIN_MULTIPLATFORM) {
local {
javaJar(project.grpcKotlinMultiplatformProtocPluginJarPath)
}

options.put("debugOutput", "protoc-gen-grpc-kotlin-multiplatform.log")

if ([email protected]().endsWith("main")) {
options.put("explicitApiModeEnabled", explicitApiModeEnabled)
}
}
}

val languageSourceSets: ListProperty<Any> = project.objects.listProperty<Any>()
val generateTask: Property<BufGenerateTask> = project.objects.property<BufGenerateTask>()

override val proto: SourceDirectorySet = project.objects.sourceDirectorySet(
PROTO_SOURCE_DIRECTORY_NAME,
"Proto sources",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ 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.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
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.model.ObjectFactory
Expand All @@ -41,15 +38,6 @@ internal open class DefaultProtocExtension @Inject constructor(
objects: ObjectFactory,
private val project: Project,
) : ProtocExtension {
override val plugins: NamedDomainObjectContainer<ProtocPlugin> =
objects.domainObjectContainer(ProtocPlugin::class.java) { name ->
ProtocPlugin(name, project)
}

override fun plugins(action: Action<NamedDomainObjectContainer<ProtocPlugin>>) {
action.execute(plugins)
}

override val buf: BufExtension = project.objects.newInstance<BufExtension>()
override fun buf(action: Action<BufExtension>) {
action.execute(buf)
Expand All @@ -60,13 +48,10 @@ internal open class DefaultProtocExtension @Inject constructor(
project.configureKotlinMultiplatformPluginJarConfiguration()
project.configureGrpcKotlinMultiplatformPluginJarConfiguration()

createDefaultProtocPlugins()

project.protoSourceSets.all {
protocPlugin(plugins.kotlinMultiplatform)
protocPlugin(plugins.grpcKotlinMultiplatform)
}

// 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")

project.protoSourceSets.all {
if (this !is DefaultProtoSourceSet) {
Expand All @@ -92,16 +77,8 @@ internal open class DefaultProtocExtension @Inject constructor(

val pairSourceSet = protoSourceSet.correspondingMainSourceSetOrNull()

val mainProtocPlugins = pairSourceSet?.protocPlugins ?: provider { emptyList() }
val protocPluginNames = protoSourceSet.protocPlugins
.zip(mainProtocPlugins) { left, right -> left + right }
.map { it.distinct() }

val includedProtocPlugins = protocPluginNames.map { list ->
list.map { pluginName ->
[email protected](pluginName)
?: throw GradleException("Protoc plugin $pluginName not found")
}
val includedProtocPlugins = provider {
protoSourceSet.plugins.distinct()
}

val protoFiles = protoSourceSet.proto
Expand Down Expand Up @@ -303,35 +280,6 @@ internal open class DefaultProtocExtension @Inject constructor(
}
}

private fun createDefaultProtocPlugins() {
val explicitApiModeEnabled = project.provider {
project.the<KotlinBaseExtension>().explicitApi != ExplicitApiMode.Disabled
}

plugins.create(KOTLIN_MULTIPLATFORM) {
local {
javaJar(project.kotlinMultiplatformProtocPluginJarPath)
}

options.put("debugOutput", "protoc-gen-kotlin-multiplatform.log")
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")
}

private fun DefaultProtoSourceSet.correspondingMainSourceSetOrNull(): DefaultProtoSourceSet? {
return when {
name.lowercase().endsWith("main") -> {
Expand Down
18 changes: 4 additions & 14 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/ProtoSourceSet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,14 @@ public interface ProtoSourceSet {
public val name: String

/**
* Adds a new protoc plugin.
*
* Example:
* ```kotlin
* protocPlugin(rpc.protoc.plugins.myPlugin)
* ```
* Container of protoc plugins to be applied to the source set.
*/
public fun protocPlugin(plugin: NamedDomainObjectProvider<ProtocPlugin>)
public val plugins: NamedDomainObjectContainer<ProtocPlugin>

/**
* Adds a new protoc plugin.
*
* Example:
* ```kotlin
* protocPlugin(rpc.protoc.plugins.myPlugin.get())
* ```
* Configures the protoc plugins.
*/
public fun protocPlugin(plugin: ProtocPlugin)
public fun plugins(action: Action<NamedDomainObjectContainer<ProtocPlugin>>)

/**
* Default [SourceDirectorySet] for proto files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,18 @@ package kotlinx.rpc.protoc

import kotlinx.rpc.buf.BufExtension
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer

/**
* Configuration for the gRPC capabilities.
* Configuration for the Protoc capabilities.
*
* To enable the gRPC capabilities, add the following minimal code to your `build.gradle.kts`:
* To enable the Protoc capabilities, add the following minimal code to your `build.gradle.kts`:
* ```kotlin
* rpc {
* protoc()
* }
* ```
*/
public interface ProtocExtension {
/**
* List of protoc plugins to be applied to the project.
*/
public val plugins: NamedDomainObjectContainer<ProtocPlugin>

/**
* Configures the protoc plugins.
*/
public fun plugins(action: Action<NamedDomainObjectContainer<ProtocPlugin>>)

/**
* Configuration for the Buf build tool.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ internal fun Project.configureGrpcKotlinMultiplatformPluginJarConfiguration() {
)
}

private fun Project.jarPathAccessor(name: String): Provider<String> =
project.configurations.named(name)
.map { it.singleFile.absolutePath }
private fun Project.jarPathAccessor(name: String): Provider<String> = project.provider {
configurations.named(name).get().singleFile.absolutePath
}

private fun Project.configureJarConfiguration(configurationName: String, pluginArtifactName: String) {
configurations.create(configurationName)
Expand Down
Loading
Loading