Skip to content

Commit 56bae4b

Browse files
vaslabssouvlakias
andauthored
KSP 2 support (#5727)
## Motivation: #5676 Also, language version 1.9 is deprecated since Kotlin 2.2.0. It turns out, that integration wise, it's much easier to run the symbol processor with KSP 2 via a CLI wrapper that it is for KSP 1 as a kotlin compiler plugin ## Details The documentation for the CLI wrapper is [here](https://github.com/google/ksp/blob/main/docs/ksp2cmdline.md) albeit a bit outdated. The key component that is missing, is a reference to the `-libraries` argument, which (as our trial and error shows) expects the module classpath. Micronaut had also a tricky bit, failing to resolve java sdk sources. Passing the `-jdk-home` resolves that. From there on, Dagger, Hilt and micronaut work without any major adaptations from the user's api perspective as we made an effort to consolidate `KSPModule` and `KSP2Module` input parameters We have also retested the pokemon module outside of this repo which also works, surprisingly without needing the `-friends` parameter ## Summary This PR gives mill the capability to build KSP 2 with language version and api version >= 2.0 , bringing a solution to the deprecated language version <=1.9 needed by KSP 1. Also, added an equivalent Micronaut example as the hello-micronaut in javalib/web, but with full KSP, no annotation processors needed ## Credits This PR was co-authored by @souvlakias --------- Co-authored-by: souvlakias <[email protected]>
1 parent 822df05 commit 56bae4b

File tree

14 files changed

+503
-116
lines changed

14 files changed

+503
-116
lines changed

example/kotlinlib/basic/7-dependency-injection/build.mill

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,39 @@
22

33
package build
44
import mill.*, kotlinlib.*
5-
import kotlinlib.ksp.KspModule
5+
import kotlinlib.ksp.Ksp2Module
66

7-
object dagger extends KspModule {
7+
object dagger extends Ksp2Module {
88

9-
def kotlinVersion = "2.1.0"
9+
def kotlinVersion = "2.2.0"
1010

11-
def kspVersion = "1.0.29"
11+
def kspVersion = "2.0.2"
1212

13-
def kotlinLanguageVersion = "1.9"
13+
def kspJvmTarget = "17"
1414

1515
def mainClass = Some("com.example.dagger.MainKt")
1616

1717
def kotlincOptions = super.kotlincOptions() ++ Seq("-no-reflect", "-verbose")
1818

19-
def mvnDeps = Seq(
20-
mvn"com.google.dagger:dagger-compiler:2.55"
19+
def mvnDeps = super.mvnDeps() ++ Seq(
20+
mvn"com.google.dagger:dagger:2.57"
2121
)
2222

2323
def kotlinSymbolProcessors = Seq(
24-
mvn"com.google.dagger:dagger-compiler:2.55"
24+
mvn"com.google.dagger:dagger-compiler:2.57"
2525
)
2626

27-
object test extends KspTests, TestModule.Junit5 {
27+
object test extends Ksp2Tests, TestModule.Junit5 {
2828

2929
def kotlincOptions = super.kotlincOptions() ++ Seq("-no-reflect", "-verbose")
3030

3131
def mvnDeps = super.mvnDeps() ++ Seq(
32-
mvn"com.google.dagger:dagger-compiler:2.55",
32+
mvn"com.google.dagger:dagger-compiler:2.57",
3333
mvn"io.kotest:kotest-runner-junit5:5.9.1"
3434
)
3535

3636
def kotlinSymbolProcessors = Seq(
37-
mvn"com.google.dagger:dagger-compiler:2.55"
37+
mvn"com.google.dagger:dagger-compiler:2.57"
3838
)
3939

4040
}
@@ -48,7 +48,7 @@ object dagger extends KspModule {
4848

4949
> ./mill show dagger.compile
5050

51-
> ls out/dagger/generatedSourcesWithKSP.dest/generated/ksp/main/java/com/example/dagger/
51+
> ls out/dagger/generatedSourcesWithKSP.dest/generated/java/com/example/dagger/
5252
DaggerNumberApp.java
5353
NumberService_Factory.java
5454
RandomNumberGenerator_Factory.java
@@ -77,7 +77,7 @@ Random number: ...
7777
}
7878
...
7979

80-
> ls out/dagger/test/generatedSourcesWithKSP.dest/generated/ksp/main/java/com/example/dagger/
80+
> ls out/dagger/test/generatedSourcesWithKSP.dest/generated/java/com/example/dagger/
8181
ConstantNumberGenerator_Factory.java
8282
DaggerDemoComponent.java
8383
DaggerTestApp.java
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package build
2+
3+
import coursier.{MavenRepository, Repository}
4+
5+
import mill.*
6+
import kotlinlib.*
7+
import kotlinlib.ksp.Ksp2Module
8+
9+
object micronaut extends MicronautModule {
10+
11+
def mvnDeps: Task.Simple[Seq[Dep]] = Seq(
12+
mvn"io.micronaut:micronaut-http-server-netty",
13+
mvn"io.micronaut.serde:micronaut-serde-jackson",
14+
mvn"io.micronaut:micronaut-jackson-databind",
15+
mvn"io.micronaut:micronaut-http-validation",
16+
mvn"ch.qos.logback:logback-classic",
17+
mvn"io.micronaut.kotlin:micronaut-kotlin-runtime",
18+
mvn"io.micronaut.data:micronaut-data-processor"
19+
)
20+
21+
object test extends MicronautTests, TestModule.Junit5 {
22+
23+
def mvnDeps = super.mvnDeps() ++ Seq(
24+
mvn"io.micronaut:micronaut-http-client",
25+
mvn"io.micronaut.test:micronaut-test-junit5"
26+
)
27+
28+
}
29+
30+
}
31+
32+
trait MicronautModule extends Ksp2Module { outer =>
33+
def kspVersion: T[String] = "2.0.2"
34+
def kotlinVersion = "2.2.0"
35+
def kspJvmTarget: T[String] = "11"
36+
37+
def bomMvnDeps: T[Seq[Dep]] = Seq(
38+
mvn"io.micronaut.platform:micronaut-platform:4.9.2"
39+
)
40+
41+
def kotlinSymbolProcessors = Seq(
42+
mvn"io.micronaut:micronaut-inject-kotlin:4.9.9"
43+
)
44+
45+
override def kspProcessorOptions: T[Map[String, String]] = Task {
46+
super.kspProcessorOptions() ++ Map(
47+
"micronaut.processing.module" -> moduleSegments.render,
48+
"micronaut.processing.incremental" -> "true"
49+
)
50+
}
51+
52+
override def resources: T[Seq[PathRef]] = Task {
53+
super.resources() ++ Seq(generatedSourcesWithKSP().resources)
54+
}
55+
56+
override def runClasspath: T[Seq[PathRef]] = Task {
57+
super.runClasspath() ++ Seq(generatedSourcesWithKSP().classes)
58+
}
59+
60+
trait MicronautTests extends MicronautModule, Ksp2Tests {
61+
62+
override def resources: T[Seq[PathRef]] = Task {
63+
super[Ksp2Tests].resources() ++ Seq(generatedSourcesWithKSP().resources)
64+
}
65+
66+
def kotlinSymbolProcessors = Seq(
67+
mvn"io.micronaut:micronaut-inject-kotlin-test:4.9.9"
68+
)
69+
70+
override def runClasspath: T[Seq[PathRef]] = Task {
71+
super.runClasspath() ++ Seq(
72+
generatedSourcesWithKSP().classes,
73+
outer.generatedSourcesWithKSP().classes
74+
)
75+
}
76+
77+
}
78+
79+
}
80+
81+
/** Usage
82+
83+
> ./mill show micronaut.test
84+
...
85+
{
86+
"msg": " ",
87+
"results": [
88+
{
89+
"fullyQualifiedName": "example.micronaut.HelloControllerTest",
90+
"selector": "testHello()",
91+
"duration": ...,
92+
"status": "Success"
93+
},
94+
{
95+
"fullyQualifiedName": "example.micronaut.FnrSearchServiceTest",
96+
"selector": "testItWorks()",
97+
"duration": ...,
98+
"status": "Success"
99+
}
100+
]
101+
}
102+
...
103+
104+
> ./mill show clean
105+
106+
*/
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
micronaut.application.name=default
2+
micronaut.server.port=9099
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<configuration>
2+
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<!-- encoders are assigned the type
5+
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
6+
<encoder>
7+
<pattern>%cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n</pattern>
8+
</encoder>
9+
</appender>
10+
11+
<root level="info">
12+
<appender-ref ref="STDOUT" />
13+
</root>
14+
</configuration>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package example.micronaut
2+
3+
import io.micronaut.http.MediaType
4+
import io.micronaut.http.annotation.Controller
5+
import io.micronaut.http.annotation.Get
6+
import io.micronaut.http.annotation.Produces
7+
8+
@Controller("/hello") // <1>
9+
class HelloController {
10+
@Get // <2>
11+
@Produces(MediaType.TEXT_PLAIN) // <3>
12+
fun index(): String = "Hello World" // <4>
13+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package example.micronaut
2+
3+
import io.micronaut.runtime.Micronaut.run
4+
fun main(args: Array<String>) {
5+
run(*args)
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
micronaut.server.port=-1
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package example.micronaut
2+
3+
import io.micronaut.runtime.EmbeddedApplication
4+
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
5+
import jakarta.inject.Inject
6+
import org.junit.jupiter.api.Assertions
7+
import org.junit.jupiter.api.Test
8+
9+
@MicronautTest
10+
class FnrSearchServiceTest {
11+
12+
@Inject
13+
lateinit var application: EmbeddedApplication<*>
14+
15+
@Test
16+
fun testItWorks() {
17+
Assertions.assertTrue(application.isRunning)
18+
}
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package example.micronaut
2+
3+
import io.micronaut.http.HttpRequest
4+
import io.micronaut.http.MediaType
5+
import io.micronaut.http.client.HttpClient
6+
import io.micronaut.http.client.annotation.Client
7+
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
8+
import jakarta.inject.Inject
9+
import org.junit.jupiter.api.Assertions.assertEquals
10+
import org.junit.jupiter.api.Assertions.assertNotNull
11+
import org.junit.jupiter.api.Test
12+
13+
@MicronautTest // <1>
14+
class HelloControllerTest {
15+
16+
@Inject
17+
@field:Client("/") // <2>
18+
lateinit var client: HttpClient
19+
20+
@Test
21+
fun testHello() {
22+
val request = HttpRequest.GET<Any>("/hello").accept(MediaType.TEXT_PLAIN) // <3>
23+
val body = client.toBlocking().retrieve(request)
24+
25+
assertNotNull(body)
26+
assertEquals("Hello World", body)
27+
}
28+
}

0 commit comments

Comments
 (0)