Skip to content

Commit 78e6bb4

Browse files
hfmehmedKSP Auto Pick
authored andcommitted
Add test using new Variant apis to generated extras sources that ksp
will process (cherry picked from commit f9cfcb7)
1 parent 04f6d16 commit 78e6bb4

File tree

8 files changed

+312
-5
lines changed

8 files changed

+312
-5
lines changed

integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,26 @@ class GeneratedSrcsIncIT(useKSP2: Boolean) {
1919
val gradleRunner = GradleRunner.create().withProjectDir(project.root)
2020

2121
val expected = listOf(
22-
"w: [ksp] 1: [File: Bar.kt, File: Baz.kt]",
23-
"w: [ksp] 2: [File: Foo.kt]",
22+
"w: [ksp] 1: [File: A.kt, File: Bar.kt, File: Baz.kt, File: MyJavaClass.java, File: MyKotlinClass.kt]",
23+
"w: [ksp] 2: [File: Foo.kt, File: MyJavaClassBuilder.kt, File: MyKotlinClassBuilder.kt]",
2424
"w: [ksp] 3: [File: FooBar.kt, File: FooBaz.kt]"
2525
)
2626

2727
gradleRunner.withArguments("assemble").build().let { result ->
28-
val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
28+
val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }.distinct()
2929
Assert.assertEquals(expected, outputs)
3030
}
31+
32+
val expected2 = listOf(
33+
"w: [ksp] 1: [File: Bar.kt, File: Baz.kt, File: MyJavaClass.java, File: MyKotlinClass.kt]",
34+
"w: [ksp] 2: [File: Foo.kt, File: MyJavaClassBuilder.kt, File: MyKotlinClassBuilder.kt]",
35+
"w: [ksp] 3: [File: FooBar.kt, File: FooBaz.kt]"
36+
)
37+
3138
File(project.root, "workload/src/main/kotlin/com/example/Baz.kt").appendText(System.lineSeparator())
3239
gradleRunner.withArguments("assemble").build().let { result ->
33-
val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
34-
Assert.assertEquals(expected, outputs)
40+
val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }.distinct()
41+
Assert.assertEquals(expected2, outputs)
3542
}
3643
}
3744

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
buildscript {
2+
val testRepo: String by project
3+
4+
repositories {
5+
maven(testRepo)
6+
mavenCentral()
7+
maven("https://redirector.kotlinlang.org/maven/bootstrap/")
8+
google()
9+
}
10+
}
11+
12+
plugins {
13+
kotlin("android") apply false
14+
id("com.google.devtools.ksp") apply false
15+
id("com.android.library") apply false
16+
}
17+
18+
allprojects {
19+
val testRepo: String by project
20+
repositories {
21+
maven(testRepo)
22+
mavenCentral()
23+
maven("https://redirector.kotlinlang.org/maven/bootstrap/")
24+
google()
25+
}
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
pluginManagement {
2+
val kotlinVersion: String by settings
3+
val kspVersion: String by settings
4+
val testRepo: String by settings
5+
val agpVersion: String by settings
6+
plugins {
7+
id("com.google.devtools.ksp") version kspVersion apply false
8+
kotlin("jvm") version kotlinVersion apply false
9+
kotlin("android") version kotlinVersion apply false
10+
id("com.android.library") version agpVersion apply false
11+
}
12+
repositories {
13+
maven(testRepo)
14+
gradlePluginPortal()
15+
google()
16+
mavenCentral()
17+
maven("https://redirector.kotlinlang.org/maven/bootstrap/")
18+
}
19+
}
20+
21+
rootProject.name = "playground"
22+
23+
include(":workload")
24+
include(":test-processor")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.example.annotation
2+
3+
annotation class Builder
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import com.google.devtools.ksp.getConstructors
2+
import com.google.devtools.ksp.containingFile
3+
import com.google.devtools.ksp.processing.*
4+
import com.google.devtools.ksp.symbol.*
5+
import com.google.devtools.ksp.validate
6+
import java.io.OutputStream
7+
8+
fun OutputStream.appendText(str: String) {
9+
this.write(str.toByteArray())
10+
}
11+
12+
class BuilderProcessor : SymbolProcessor {
13+
lateinit var codeGenerator: CodeGenerator
14+
lateinit var logger: KSPLogger
15+
16+
fun init(
17+
options: Map<String, String>,
18+
kotlinVersion: KotlinVersion,
19+
codeGenerator: CodeGenerator,
20+
logger: KSPLogger,
21+
) {
22+
this.codeGenerator = codeGenerator
23+
this.logger = logger
24+
}
25+
26+
override fun process(resolver: Resolver): List<KSAnnotated> {
27+
val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
28+
val ret = symbols.filter { !it.validate() }
29+
symbols
30+
.filter { it.validate() }
31+
.forEach { it.accept(BuilderVisitor(), Unit) }
32+
return ret.toList()
33+
}
34+
35+
inner class BuilderVisitor : KSVisitorVoid() {
36+
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
37+
val constructor = classDeclaration.primaryConstructor
38+
?: classDeclaration.getConstructors().maxByOrNull { it.parameters.size }
39+
40+
if (constructor == null) {
41+
logger.error("No suitable constructor found for ${classDeclaration.qualifiedName?.asString()}", classDeclaration)
42+
return
43+
}
44+
45+
constructor.accept(this, data)
46+
}
47+
48+
override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
49+
val parent = function.parentDeclaration as KSClassDeclaration
50+
val packageName = parent.containingFile!!.packageName.asString()
51+
val className = "${parent.simpleName.asString()}Builder"
52+
53+
val file = codeGenerator.createNewFile(
54+
Dependencies(true, function.containingFile!!), packageName, className
55+
)
56+
file.appendText("package $packageName\n\n")
57+
file.appendText("class $className{\n")
58+
function.parameters.forEach {
59+
val name = it.name!!.asString()
60+
val typeName = StringBuilder(it.type.resolve().declaration.qualifiedName?.asString() ?: "<ERROR>")
61+
val typeArgs = it.type.element!!.typeArguments
62+
if (it.type.element!!.typeArguments.toList().isNotEmpty()) {
63+
typeName.append("<")
64+
typeName.append(
65+
typeArgs.map {
66+
val type = it.type?.resolve()
67+
"${it.variance.label} ${type?.declaration?.qualifiedName?.asString() ?: "ERROR"}" +
68+
if (type?.nullability == Nullability.NULLABLE) "?" else ""
69+
}.joinToString(", ")
70+
)
71+
typeName.append(">")
72+
}
73+
file.appendText(" private var $name: $typeName? = null\n")
74+
file.appendText(" internal fun with${name.capitalize()}($name: $typeName): $className {\n")
75+
file.appendText(" this.$name = $name\n")
76+
file.appendText(" return this\n")
77+
file.appendText(" }\n\n")
78+
}
79+
file.appendText(" internal fun build(): ${parent.qualifiedName!!.asString()} {\n")
80+
file.appendText(" return ${parent.qualifiedName!!.asString()}(")
81+
file.appendText(
82+
function.parameters.map {
83+
"${it.name!!.asString()}!!"
84+
}.joinToString(", ")
85+
)
86+
file.appendText(")\n")
87+
file.appendText(" }\n")
88+
file.appendText("}\n")
89+
file.close()
90+
}
91+
}
92+
}
93+
94+
class TestProcessorProvider2 : SymbolProcessorProvider {
95+
override fun create(
96+
env: SymbolProcessorEnvironment,
97+
): SymbolProcessor {
98+
return BuilderProcessor().apply {
99+
init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
100+
}
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
TestProcessorProvider
2+
TestProcessorProvider2
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
val testRepo: String by project
2+
3+
plugins {
4+
// DO NOT CHANGE THE ORDER.
5+
id("com.google.devtools.ksp")
6+
id("com.android.library")
7+
kotlin("android")
8+
}
9+
10+
version = "1.0-SNAPSHOT"
11+
12+
repositories {
13+
maven(testRepo)
14+
mavenCentral()
15+
maven("https://redirector.kotlinlang.org/maven/bootstrap/")
16+
}
17+
18+
dependencies {
19+
implementation(kotlin("stdlib"))
20+
implementation(project(":test-processor"))
21+
ksp(project(":test-processor"))
22+
}
23+
24+
android {
25+
namespace = "com.example.mylibrary"
26+
compileSdk = 34
27+
defaultConfig {
28+
minSdk = 34
29+
}
30+
lint {
31+
targetSdk = 34
32+
}
33+
34+
testOptions {
35+
targetSdk = 34
36+
}
37+
}
38+
39+
ksp {
40+
arg("option1", "value1")
41+
arg("option2", "value2")
42+
}
43+
44+
androidComponents.onVariants { variant ->
45+
val kotlinGenTaskProvider = project.tasks.register(
46+
"generate${variant.name}KotlinSources",
47+
KotlinSourceGeneratingTask::class.java
48+
)
49+
50+
kotlinGenTaskProvider.configure {
51+
this.packageName.set("com.kotlingen")
52+
this.sourceFiles.set(
53+
variant.sources.kotlin!!.static
54+
)
55+
}
56+
variant.sources.java!!.addGeneratedSourceDirectory(
57+
kotlinGenTaskProvider, KotlinSourceGeneratingTask::outputDir
58+
)
59+
60+
61+
val javaGenTaskProvider = project.tasks.register(
62+
"generate${variant.name}JavaSources",
63+
JavaSourceGeneratingTask::class.java
64+
)
65+
javaGenTaskProvider.configure {
66+
this.packageName.set("com.javagen")
67+
this.sourceFiles.set(
68+
variant.sources.java!!.static
69+
)
70+
}
71+
variant.sources.java!!.addGeneratedSourceDirectory(
72+
javaGenTaskProvider, JavaSourceGeneratingTask::outputDir
73+
)
74+
}
75+
76+
abstract class KotlinSourceGeneratingTask: DefaultTask() {
77+
@get:Input
78+
abstract val packageName: Property<String>
79+
@get:OutputDirectory
80+
abstract val outputDir: DirectoryProperty
81+
@get:InputFiles
82+
abstract val sourceFiles: ListProperty<Directory>
83+
@TaskAction
84+
fun generate() {
85+
val outputFolder = File(outputDir.get().asFile, packageName.get().replace('.', File.separatorChar))
86+
outputFolder.mkdirs()
87+
File(outputFolder, "MyKotlinClass.kt").writeText(
88+
"""
89+
package ${packageName.get()}
90+
import com.example.annotation.Builder
91+
92+
@Builder
93+
class MyKotlinClass {
94+
fun someFunctionUsingGeneratedAPIs() {
95+
System.err.println("Hello world !")
96+
}
97+
}
98+
""".trimIndent()
99+
)
100+
}
101+
}
102+
103+
abstract class JavaSourceGeneratingTask: DefaultTask() {
104+
@get:Input
105+
abstract val packageName: Property<String>
106+
@get:OutputDirectory
107+
abstract val outputDir: DirectoryProperty
108+
@get:InputFiles
109+
abstract val sourceFiles: ListProperty<Directory>
110+
@TaskAction
111+
fun generate() {
112+
val outputFolder = File(outputDir.get().asFile, packageName.get().replace('.', File.separatorChar))
113+
outputFolder.mkdirs()
114+
File(outputFolder, "MyJavaClass.java").writeText(
115+
"""
116+
package ${packageName.get()};
117+
import com.example.annotation.Builder;
118+
119+
@Builder
120+
public class MyJavaClass {
121+
public static void someFunctionUsingGeneratedAPIs() {
122+
System.err.println("Hello world !");
123+
}
124+
}
125+
""".trimIndent()
126+
)
127+
}
128+
}
129+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.example
2+
3+
import com.kotlingen.MyKotlinClassBuilder
4+
import com.javagen.MyJavaClassBuilder
5+
6+
fun main() {
7+
val builder = MyKotlinClassBuilder()
8+
val kotlinClass = builder.build()
9+
println(kotlinClass)
10+
11+
val builder2 = MyJavaClassBuilder()
12+
val javaClass = builder.build()
13+
println(javaClass)
14+
}

0 commit comments

Comments
 (0)