Skip to content

Commit 7a1048c

Browse files
committed
Use Kotlin 2.3's generatedKotlin API
1 parent b7e2f3e commit 7a1048c

7 files changed

Lines changed: 197 additions & 7 deletions

File tree

wire-gradle-plugin/src/main/kotlin/com/squareup/wire/gradle/kotlin/SourceRoots.kt

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ import com.squareup.wire.schema.ProtoTarget
2626
import com.squareup.wire.schema.Target
2727
import java.io.File
2828
import org.gradle.api.Project
29+
import org.gradle.api.file.Directory
2930
import org.gradle.api.file.SourceDirectorySet
31+
import org.gradle.api.provider.Provider
3032
import org.gradle.api.tasks.SourceSetContainer
3133
import org.gradle.api.tasks.TaskProvider
3234
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
3335
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
36+
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
3437

3538
internal fun forEachWireSource(
3639
project: Project,
@@ -63,12 +66,12 @@ internal fun forEachWireSource(
6366
hasKotlin -> {
6467
val kotlinSourceSets = project.extensions.findByType(KotlinProjectExtension::class.java)?.sourceSets
6568
val javaSourceSets = project.extensions.findByType(SourceSetContainer::class.java)
66-
val kotlinSourceDirectorySet = kotlinSourceSets?.findByName("main")?.kotlin
69+
val kotlinSourceSet = kotlinSourceSets?.findByName("main")
6770
val javaSourceDirectorySet = javaSourceSets?.findByName("main")?.java
6871
val source = JvmOrKmpSource(
6972
name = "main",
7073
sourceSets = listOf("main"),
71-
kotlinSourceDirectorySet = kotlinSourceDirectorySet,
74+
kotlinSourceSet = kotlinSourceSet,
7275
javaSourceDirectorySet = javaSourceDirectorySet,
7376
)
7477
sourceHandler(source)
@@ -79,7 +82,7 @@ internal fun forEachWireSource(
7982
val source = JvmOrKmpSource(
8083
name = "main",
8184
sourceSets = listOf("main"),
82-
kotlinSourceDirectorySet = null,
85+
kotlinSourceSet = null,
8386
javaSourceDirectorySet = javaSourceDirectorySet,
8487
)
8588
sourceHandler(source)
@@ -115,7 +118,7 @@ private fun KotlinMultiplatformExtension.sourceRoots(): List<WireSource> {
115118
return listOf(
116119
JvmOrKmpSource(
117120
name = "commonMain",
118-
kotlinSourceDirectorySet = sourceSets.getByName("commonMain").kotlin,
121+
kotlinSourceSet = sourceSets.getByName("commonMain"),
119122
javaSourceDirectorySet = null,
120123
sourceSets = listOf("commonMain"),
121124
),
@@ -125,7 +128,7 @@ private fun KotlinMultiplatformExtension.sourceRoots(): List<WireSource> {
125128
private class JvmOrKmpSource(
126129
name: String,
127130
sourceSets: List<String>,
128-
private val kotlinSourceDirectorySet: SourceDirectorySet?,
131+
private val kotlinSourceSet: KotlinSourceSet?,
129132
private val javaSourceDirectorySet: SourceDirectorySet?,
130133
) : WireSource(name, sourceSets) {
131134
override fun outputDir(project: Project): File {
@@ -144,12 +147,12 @@ private class JvmOrKmpSource(
144147
javaSourceDirectorySet?.srcDir(outputDirectory)
145148
}
146149
is KotlinTarget -> {
147-
kotlinSourceDirectorySet?.srcDir(outputDirectory)
150+
registerKotlinGeneratedSources(kotlinSourceSet, outputDirectory)
148151
}
149152
is CustomTarget -> {
150153
// Custom targets are wildcards, so we add all output directories.
151154
javaSourceDirectorySet?.srcDir(outputDirectory)
152-
kotlinSourceDirectorySet?.srcDir(outputDirectory)
155+
registerKotlinGeneratedSources(kotlinSourceSet, outputDirectory)
153156
}
154157
is ProtoTarget -> {
155158
// Do nothing
@@ -203,3 +206,28 @@ private class AndroidSource(
203206
}
204207
}
205208
}
209+
210+
/**
211+
* Registers [outputDirectory] as a generated Kotlin source directory on [kotlinSourceSet].
212+
*
213+
* On Kotlin 2.3+, uses the [KotlinSourceSet.generatedKotlin] API so that IDEs can distinguish
214+
* generated sources from hand-written ones. Falls back to [KotlinSourceSet.kotlin] on older
215+
* versions of the Kotlin Gradle Plugin where the API is not available.
216+
*/
217+
private fun registerKotlinGeneratedSources(
218+
kotlinSourceSet: KotlinSourceSet?,
219+
outputDirectory: Provider<Directory>,
220+
) {
221+
if (kotlinSourceSet == null) return
222+
// generatedKotlin was introduced experimentally in Kotlin 2.3. Detect it reflectively so that
223+
// Wire remains compatible with earlier Kotlin Gradle Plugin versions.
224+
val generatedKotlinMethod = runCatching {
225+
kotlinSourceSet.javaClass.getMethod("getGeneratedKotlin")
226+
}.getOrNull()
227+
if (generatedKotlinMethod != null) {
228+
val generatedKotlin = generatedKotlinMethod.invoke(kotlinSourceSet) as SourceDirectorySet
229+
generatedKotlin.srcDir(outputDirectory)
230+
} else {
231+
kotlinSourceSet.kotlin.srcDir(outputDirectory)
232+
}
233+
}

wire-gradle-plugin/src/test/kotlin/com/squareup/wire/gradle/WirePluginTest.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,38 @@ class WirePluginTest {
680680
assertThat(File(outputRoot, "com/squareup/geology/Period.kt")).exists()
681681
}
682682

683+
/**
684+
* Verifies that generated Kotlin sources are registered on the Kotlin source set so that
685+
* `compileKotlin` picks them up. This exercises the fallback path in
686+
* [com.squareup.wire.gradle.kotlin.registerKotlinGeneratedSources] for KGP < 2.3, where
687+
* `KotlinSourceSet.generatedKotlin` is not available and we fall back to `kotlin.srcDir()`.
688+
*/
689+
@Test
690+
fun kotlinProjectKotlinProtosCompiles() {
691+
val fixtureRoot = File("src/test/projects/kotlin-project-kotlin-protos")
692+
693+
val result = fixtureGradleRunner(fixtureRoot, "clean", "build").build()
694+
695+
assertThat(result.task(":generateMainProtos")).isNotNull()
696+
assertThat(result.task(":compileKotlin")).isNotNull()
697+
}
698+
699+
/**
700+
* Verifies that generated Kotlin sources are registered via `KotlinSourceSet.generatedKotlin`
701+
* (the API introduced experimentally in Kotlin 2.3) and that `compileKotlin` picks them up.
702+
* The fixture uses a hardcoded KGP 2.3.x dependency so that the new code path in
703+
* [com.squareup.wire.gradle.kotlin.registerKotlinGeneratedSources] is exercised.
704+
*/
705+
@Test
706+
fun kotlinProjectKotlinProtosWithGeneratedKotlinApi() {
707+
val fixtureRoot = File("src/test/projects/kotlin-project-kotlin-protos-kgp23")
708+
709+
val result = fixtureGradleRunner(fixtureRoot, "clean", "build").build()
710+
711+
assertThat(result.task(":generateMainProtos")).isNotNull()
712+
assertThat(result.task(":compileKotlin")).isNotNull()
713+
}
714+
683715
@Test
684716
fun protoLibrary() {
685717
val fixtureRoot = File("src/test/projects/proto-library")
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
3+
buildscript {
4+
dependencies {
5+
classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion"
6+
// Hardcoded to test the KotlinSourceSet.generatedKotlin API introduced in Kotlin 2.3.
7+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.20"
8+
}
9+
10+
repositories {
11+
maven {
12+
url new File(rootDir, "../../../../../build/localMaven").toURI().toString()
13+
}
14+
mavenCentral()
15+
google()
16+
}
17+
}
18+
19+
apply plugin: 'application'
20+
apply plugin: 'org.jetbrains.kotlin.jvm'
21+
apply plugin: 'com.squareup.wire'
22+
23+
application.mainClass = "com.squareup.dinosaurs.Sample"
24+
25+
repositories {
26+
maven {
27+
url new File(rootDir, "../../../../../build/localMaven").toURI().toString()
28+
}
29+
mavenCentral()
30+
}
31+
32+
dependencies {
33+
implementation "com.squareup.wire:wire-runtime:$wireVersion"
34+
}
35+
36+
wire {
37+
kotlin {}
38+
}
39+
40+
tasks.withType(JavaCompile).configureEach {
41+
sourceCompatibility = JavaVersion.VERSION_11.toString()
42+
targetCompatibility = JavaVersion.VERSION_11.toString()
43+
}
44+
45+
tasks.withType(KotlinCompile).configureEach {
46+
kotlinOptions {
47+
jvmTarget = "11"
48+
}
49+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include ':'
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (C) 2023 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.squareup.dinosaurs
17+
18+
import com.squareup.geology.Period
19+
import java.io.IOException
20+
import okio.ByteString.Companion.toByteString
21+
22+
class Sample {
23+
@Throws(IOException::class)
24+
fun run() {
25+
// Create an immutable value object with the Builder API.
26+
val stegosaurus = Dinosaur(
27+
name = "Stegosaurus",
28+
period = Period.JURASSIC,
29+
length_meters = 9.0,
30+
mass_kilograms = 5_000.0,
31+
picture_urls = listOf("http://goo.gl/LD5KY5", "http://goo.gl/VYRM67"),
32+
)
33+
// Encode that value to bytes, and print that as base64.
34+
val stegosaurusEncoded = Dinosaur.ADAPTER.encode(stegosaurus)
35+
println(stegosaurusEncoded.toByteString().base64())
36+
}
37+
38+
companion object {
39+
@Throws(IOException::class)
40+
@JvmStatic
41+
fun main(args: Array<String>) {
42+
Sample().run()
43+
}
44+
}
45+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
syntax = "proto2";
2+
3+
package squareup.dinosaurs;
4+
5+
option java_package = "com.squareup.dinosaurs";
6+
7+
import "squareup/geology/period.proto";
8+
9+
message Dinosaur {
10+
/** Common name of this dinosaur, like "Stegosaurus". */
11+
optional string name = 1;
12+
13+
/** URLs with images of this dinosaur. */
14+
repeated string picture_urls = 2;
15+
16+
optional double length_meters = 3;
17+
optional double mass_kilograms = 4;
18+
optional squareup.geology.Period period = 5;
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
syntax = "proto2";
2+
3+
package squareup.geology;
4+
5+
option java_package = "com.squareup.geology";
6+
7+
enum Period {
8+
/** 145.5 million years ago — 66.0 million years ago. */
9+
CRETACEOUS = 1;
10+
11+
/** 201.3 million years ago — 145.0 million years ago. */
12+
JURASSIC = 2;
13+
14+
/** 252.17 million years ago — 201.3 million years ago. */
15+
TRIASSIC = 3;
16+
}

0 commit comments

Comments
 (0)