Skip to content
Draft
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
20 changes: 15 additions & 5 deletions gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ tasks.withType<KotlinCompile>().configureEach {
}

dependencies {
implementation(libs.kotlin.gradle.plugin)
compileOnly(libs.kotlin.gradle.plugin)
compileOnly(libs.android.gradle.plugin)
compileOnly(libs.android.gradle.plugin.api)

testImplementation(libs.kotlin.gradle.plugin)
testImplementation(gradleTestKit())
Expand All @@ -50,9 +52,17 @@ tasks.test {

useJUnitPlatform()

val includedBuild = gradle.includedBuild("protoc-gen")
dependsOn(includedBuild.task(":grpc:publishAllPublicationsToBuildRepoRepository"))
dependsOn(includedBuild.task(":protobuf:publishAllPublicationsToBuildRepoRepository"))
val protocGen = gradle.includedBuild("protoc-gen")
dependsOn(protocGen.task(":grpc:publishAllPublicationsToBuildRepoRepository"))
dependsOn(protocGen.task(":protobuf:publishAllPublicationsToBuildRepoRepository"))

val compilerPlugin = gradle.includedBuild("compiler-plugin")
dependsOn(compilerPlugin.task(":compiler-plugin-cli:publishAllPublicationsToBuildRepoRepository"))
dependsOn(compilerPlugin.task(":compiler-plugin-k2:publishAllPublicationsToBuildRepoRepository"))
dependsOn(compilerPlugin.task(":compiler-plugin-backend:publishAllPublicationsToBuildRepoRepository"))
dependsOn(compilerPlugin.task(":compiler-plugin-common:publishAllPublicationsToBuildRepoRepository"))

dependsOn(":publishAllPublicationsToBuildRepoRepository")
}

// This block is needed to show plugin tasks on --dry-run
Expand Down Expand Up @@ -103,7 +113,7 @@ generateSource(
text = """
package kotlinx.rpc

const val KOTLIN_VERSION: String = "${libs.versions.kotlin.lang.get()}"
const val RPC_VERSION: String = "${libs.versions.kotlinx.rpc.get()}"

const val BUILD_REPO: String = "${File(globalRootDir).resolve("build/repo").absolutePath}"
""".trimIndent(),
Expand Down
11 changes: 5 additions & 6 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,13 @@ public open class RpcExtension @Inject internal constructor(objects: ObjectFacto
internal val protocApplied = AtomicBoolean(false)

internal val protocInternal by lazy {
if (protocApplied.get()) {
if (protocApplied.compareAndSet(false, true)) {
objects.newInstance<DefaultProtocExtension>().apply {
callbacks.forEach { it.execute(this) }
}
} else {
error("Illegal access to protoc extension during DefaultProtocExtension.init")
}

protocApplied.set(true)
objects.newInstance<DefaultProtocExtension>().apply {
callbacks.forEach { it.execute(this) }
}
}

private val callbacks = mutableListOf<Action<ProtocExtension>>()
Expand Down
22 changes: 22 additions & 0 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcGradlePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ package kotlinx.rpc

import kotlinx.rpc.protoc.configurePluginProtections
import kotlinx.rpc.protoc.createProtoExtensions
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.create

@Suppress("unused")
public class RpcGradlePlugin : Plugin<Project> {
override fun apply(target: Project) {
checkKGPIsInTheClasspath()

target.extensions.create<RpcExtension>("rpc")

applyCompilerPlugin(target)
Expand All @@ -28,3 +31,22 @@ public class RpcGradlePlugin : Plugin<Project> {
target.plugins.apply(CompilerPluginCli::class.java)
}
}

private fun checkKGPIsInTheClasspath() {
try {
Class.forName("org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin")
} catch (_: ClassNotFoundException) {
throw GradleException(
"""
Kotlin Gradle plugin is not applied to the project.
Please ensure that Kotlin Gradle plugin is applied to the project in the plugins block:

plugins {
kotlin("jvm") version "..." // or
kotlin("multiplatform") version "..." // or
kotlin("android") version "..."
}
""".trimIndent()
)
}
}
52 changes: 43 additions & 9 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/tasks/BufExecTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,31 @@ import kotlinx.rpc.buf.BufTasksExtension
import org.gradle.api.logging.LogLevel
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.SkipWhenEmpty
import javax.inject.Inject

/**
* Abstract base class for `buf` tasks.
*/
public abstract class BufExecTask @Inject constructor(
@Internal
public val properties: Properties
public val properties: Provider<Properties>,
) : DefaultTask() {
init {
group = PROTO_GROUP
}

// unsued, but required for Gradle to properly recognise inputs
@get:InputFiles
@get:SkipWhenEmpty
internal abstract val protoFiles: ListProperty<File>

// unsued, but required for Gradle to properly recognise inputs
@get:InputFiles
internal abstract val importProtoFiles: ListProperty<File>

// list of buf task dependencies of the same type
@get:Internal
internal abstract val bufTaskDependencies: SetProperty<String>
Expand Down Expand Up @@ -77,18 +88,41 @@ public abstract class BufExecTask @Inject constructor(
public class AndroidProperties internal constructor(
isTest: Boolean,
sourceSetName: String,

/**
* Name of the android flavor this task is associated with.
* Name of the android flavors this task is associated with.
*
* Can be empty for 'com.android.kotlin.multiplatform.library' source sets.
*
* @see com.android.build.api.variant.Variant.productFlavors
*/
public val flavor: String,
public val flavors: List<String>,

/**
* Name of the android build type this task is associated with.
*
* @see com.android.build.api.variant.Variant.buildType
*/
public val buildType: String,
public val buildType: String?,

/**
* Name of the android variant this task is associated with.
*
* Can be `null` for 'com.android.kotlin.multiplatform.library' source sets.
*
* @see com.android.build.api.variant.Variant.name
*/
public val variant: String?,

/**
* Whether the task is for instrumentation tests.
*/
public val isInstrumentedTest: Boolean,

/**
* Whether the task is for unit tests.
*/
public val variant: String,
public val isUnitTest: Boolean,
) : Properties(isTest, sourceSetName)

/**
Expand Down Expand Up @@ -151,7 +185,7 @@ public abstract class BufExecTask @Inject constructor(
public inline fun <reified T : BufExecTask> Project.registerBufExecTask(
name: String,
workingDir: Provider<File>,
properties: BufExecTask.Properties,
properties: Provider<BufExecTask.Properties>,
noinline configuration: T.() -> Unit,
): TaskProvider<T> = registerBufExecTask(T::class, name, workingDir, properties, configuration)

Expand All @@ -160,12 +194,12 @@ internal fun <T : BufExecTask> Project.registerBufExecTask(
clazz: KClass<T>,
name: String,
workingDir: Provider<File>,
properties: BufExecTask.Properties,
properties: Provider<BufExecTask.Properties>,
configuration: T.() -> Unit = {},
): TaskProvider<T> = tasks.register(name, clazz, properties).apply {
configure {
val executableConfiguration = configurations.getByName(BUF_EXECUTABLE_CONFIGURATION)
bufExecutable.set(executableConfiguration.singleFile)
val executableConfiguration = configurations.named(BUF_EXECUTABLE_CONFIGURATION)
bufExecutable.set(project.provider { executableConfiguration.get().singleFile })
this.workingDir.set(workingDir)

val buf = provider { rpcExtension().protoc.get().buf }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,29 @@ import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskProvider
import java.io.File
import kotlinx.rpc.buf.BufGenerateExtension
import org.gradle.api.tasks.InputDirectory
import kotlinx.rpc.protoc.DefaultProtoSourceSet
import kotlinx.rpc.protoc.bufExecProperties
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectories
import org.gradle.kotlin.dsl.listProperty
import javax.inject.Inject

/**
* Buf `generate` command.
*
* @see <a href="https://buf.build/docs/reference/cli/buf/generate/">buf generate</a>
*/
public abstract class BufGenerateTask @Inject internal constructor(properties: Properties) : BufExecTask(properties) {
// unsued, but required for Gradle to properly recognise inputs
@get:InputDirectory
internal abstract val protoFilesDir: Property<File>

// unsued, but required for Gradle to properly recognise inputs
@get:InputDirectory
internal abstract val importFilesDir: Property<File>
public abstract class BufGenerateTask @Inject internal constructor(
properties: Provider<Properties>,
) : BufExecTask(properties) {
// used to properly calculate output directories
@get:Input
internal abstract val pluginNames: ListProperty<String>

/**
* List of files used during `buf generate` command execution.
* List of executable files used during `buf generate` command execution.
*
* @see [ProtocPlugin.Artifact.Local.executableFiles]
*/
Expand Down Expand Up @@ -82,11 +85,22 @@ public abstract class BufGenerateTask @Inject internal constructor(properties: P
public abstract val additionalArgs: ListProperty<String>

/**
* The directory to output generated files.
* The directory to output generated files to, used as a `buf generate --output` argument,
* not the directory for sources. For that see [outputSourceDirectories].
*/
@get:OutputDirectory
public abstract val outputDirectory: Property<File>

private val outputSourceDirectoriesInternal: ListProperty<File> = project.objects.listProperty()

/**
* Generated source directories by plugin name.
*
* Can be used in [SourceDirectorySet.srcDirs].
*/
@get:OutputDirectories
public val outputSourceDirectories: Provider<List<File>> = outputSourceDirectoriesInternal

init {
command.set("generate")

Expand All @@ -110,6 +124,11 @@ public abstract class BufGenerateTask @Inject internal constructor(properties: P
}

this.args.set(args)

outputSourceDirectoriesInternal.set(pluginNames.map { plugins ->
val out = outputDirectory.get()
plugins.map { out.resolve(it) }
})
}

internal companion object {
Expand All @@ -118,20 +137,16 @@ public abstract class BufGenerateTask @Inject internal constructor(properties: P
}

internal fun Project.registerBufGenerateTask(
sourceSetName: String,
protoSourceSet: DefaultProtoSourceSet,
workingDir: File,
outputDirectory: File,
protoFilesDir: File,
importFilesDir: File,
includedPlugins: Provider<Set<ProtocPlugin>>,
configure: BufGenerateTask.() -> Unit = {},
): TaskProvider<BufGenerateTask> {
val capitalName = sourceSetName.replaceFirstChar { it.uppercase() }
val capitalName = protoSourceSet.name.replaceFirstChar { it.uppercase() }
val bufGenerateTaskName = "${BufGenerateTask.NAME_PREFIX}$capitalName"

val properties = BufExecTask.Properties(
isTest = sourceSetName.lowercase().endsWith("test"),
sourceSetName = sourceSetName,
)
val properties = protoSourceSet.bufExecProperties()

return registerBufExecTask<BufGenerateTask>(bufGenerateTaskName, provider { workingDir }, properties) {
group = PROTO_GROUP
Expand All @@ -145,8 +160,7 @@ internal fun Project.registerBufGenerateTask(

this.outputDirectory.set(outputDirectory)

this.protoFilesDir.set(protoFilesDir)
this.importFilesDir.set(importFilesDir)
this.pluginNames.set(includedPlugins.map { it.map { plugin -> plugin.name } })

configure()
}
Expand Down
Loading
Loading