Skip to content

Commit f3c4db6

Browse files
authored
Use Kotlin build-tools-api to compile Kotlin code (#5785)
With Kotlin 2.2, there is a new beta build tools api (BT API) which should be used by build tools to integrate the Kotlin compiler. This PR adds support for this API and uses it by default if the module is using Kotlin 2.0+ for JVM. Related to #5788 Pull request: #5785
1 parent c3605ea commit f3c4db6

File tree

22 files changed

+274
-104
lines changed

22 files changed

+274
-104
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import kotlinlib.ksp.KspModule
77
object dagger extends KspModule {
88

99
def kotlinVersion = "2.2.0"
10-
1110
def kspVersion = "2.0.2"
12-
1311
def kspJvmTarget = "17"
1412

1513
def mainClass = Some("com.example.dagger.MainKt")
1614

1715
def kotlincOptions = super.kotlincOptions() ++ Seq("-no-reflect", "-verbose")
1816

17+
// override def kotlincUseBtApi = false
18+
1919
def mvnDeps = super.mvnDeps() ++ Seq(
2020
mvn"com.google.dagger:dagger:2.57"
2121
)
@@ -36,7 +36,6 @@ object dagger extends KspModule {
3636
def kotlinSymbolProcessors = Seq(
3737
mvn"com.google.dagger:dagger-compiler:2.57"
3838
)
39-
4039
}
4140
}
4241

@@ -46,13 +45,21 @@ object dagger extends KspModule {
4645

4746
/** Usage
4847

49-
> ./mill dagger.compile
48+
> ./mill show dagger.generatedSources
49+
[
50+
"ref:v0:.../out/dagger/generatedSourcesWithKsp2.dest/generated/java",
51+
"ref:v0:.../out/dagger/generatedSourcesWithKsp2.dest/generated/kotlin"
52+
]
5053

5154
> ls out/dagger/generatedSourcesWithKsp2.dest/generated/java/com/example/dagger/
5255
DaggerNumberApp.java
5356
NumberService_Factory.java
5457
RandomNumberGenerator_Factory.java
5558

59+
> ./mill dagger.compile
60+
Compiling 6 Kotlin sources and reading 3 Java sources to .../out/dagger/compile.dest/classes ...
61+
Compiling 3 Java sources to .../out/dagger/compile.dest/classes ...
62+
5663
> ./mill dagger.run
5764
Random number: ...
5865

example/thirdparty/arrow/build.mill

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ object `package` extends Module {
131131
def kotlinLanguageVersion = majorVersion(kotlinVersion())
132132
def kotlinApiVersion = majorVersion(kotlinVersion())
133133
def kotlinExplicitApi = ArrowMultiplatformModule.this.kotlinExplicitApi
134+
def kotlincUseBtApi = false
134135

135136
override def sources: T[Seq[PathRef]] = Task.Sources({
136137
val sourcesRootPath = moduleDir / "src"

libs/javalib/src/mill/javalib/JavaHomeModule.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import mill.api.{PathRef, Task}
77
* Common trait for modules that use either a custom or a globally shared [[JvmWorkerModule]].
88
*/
99
trait JavaHomeModule extends CoursierModule {
10+
1011
def jvmId: T[String] = ""
1112

1213
def jvmIndexVersion: T[String] = mill.javalib.api.Versions.coursierJvmIndexVersion

libs/javalib/src/mill/javalib/JavaModule.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ trait JavaModule
6868
() => genIdeaInternalExt()
6969

7070
override def jvmWorker: ModuleRef[JvmWorkerModule] = super.jvmWorker
71+
7172
trait JavaTests extends JavaModule with TestModule {
7273
// Run some consistence checks
7374
hierarchyChecks()

libs/javalib/src/mill/javalib/SemanticDbJavaModule.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import mill.javalib.api.internal.{JavaCompilerOptions, ZincCompileJava}
1515
@experimental
1616
trait SemanticDbJavaModule extends CoursierModule with SemanticDbJavaModuleApi
1717
with WithJvmWorkerModule {
18+
1819
def jvmWorker: ModuleRef[JvmWorkerModule]
1920

2021
def upstreamSemanticDbDatas: Task[Seq[SemanticDbJavaModule.SemanticDbData]] =

libs/kotlinlib/api/src/mill/kotlinlib/worker/api/KotlinWorker.scala

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
*/
66
package mill.kotlinlib.worker.api
77

8-
import mill.api.{TaskCtx}
9-
import mill.api.{Result}
8+
import mill.api.TaskCtx
9+
import mill.api.Result
1010

1111
trait KotlinWorker {
1212

13-
def compile(target: KotlinWorkerTarget, args: Seq[String])(implicit ctx: TaskCtx): Result[Unit]
14-
val x = 1
15-
}
13+
def compile(
14+
target: KotlinWorkerTarget,
15+
useBtApi: Boolean,
16+
args: Seq[String],
17+
sources: Seq[os.Path]
18+
)(implicit ctx: TaskCtx): Result[Unit]
1619

17-
sealed trait KotlinWorkerTarget
18-
object KotlinWorkerTarget {
19-
case object Jvm extends KotlinWorkerTarget
20-
case object Js extends KotlinWorkerTarget
2120
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package mill.kotlinlib.worker.api
2+
3+
sealed trait KotlinWorkerTarget
4+
5+
object KotlinWorkerTarget {
6+
case object Jvm extends KotlinWorkerTarget
7+
case object Js extends KotlinWorkerTarget
8+
}

libs/kotlinlib/package.mill

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,9 @@ object `package` extends MillStableScalaModule with BuildInfo {
3939
)
4040
)
4141

42-
trait MillKotlinModule extends MillPublishScalaModule {
43-
override def javacOptions = super.javacOptions() ++ {
44-
val release =
45-
if (scala.util.Properties.isJavaAtLeast(11)) Seq("-release", "8")
46-
else Seq("-source", "1.8", "-target", "1.8")
47-
release ++ Seq("-encoding", "UTF-8", "-deprecation")
48-
}
49-
}
42+
trait MillKotlinModule extends MillPublishScalaModule
5043

44+
// shared between kotlinlib and kotlinlib.worker
5145
object api extends MillKotlinModule {
5246
def moduleDeps = Seq(build.libs.javalib.testrunner)
5347

@@ -64,7 +58,8 @@ object `package` extends MillStableScalaModule with BuildInfo {
6458
override def compileMvnDeps: T[Seq[Dep]] =
6559
super.mandatoryMvnDeps() ++ Seq(
6660
Deps.osLib,
67-
Deps.kotlinCompiler
61+
Deps.kotlinCompiler,
62+
Deps.kotlinBuildToolsApi
6863
)
6964
}
7065

libs/kotlinlib/src/mill/kotlinlib/KotlinModule.scala

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import mill.api.Result
1212
import mill.api.ModuleRef
1313
import mill.kotlinlib.worker.api.KotlinWorkerTarget
1414
import mill.javalib.api.CompilationResult
15-
import mill.javalib.api.{JvmWorkerApi => PublicJvmWorkerApi}
15+
import mill.javalib.api.JvmWorkerApi as PublicJvmWorkerApi
1616
import mill.javalib.api.internal.JvmWorkerApi
1717
import mill.api.daemon.internal.{CompileProblemReporter, KotlinModuleApi, internal}
1818
import mill.javalib.{JavaModule, JvmWorkerModule, Lib}
19-
import mill.util.Jvm
19+
import mill.util.{Jvm, Version}
2020
import mill.*
2121

2222
import java.io.File
@@ -134,19 +134,27 @@ trait KotlinModule extends JavaModule with KotlinModuleApi { outer =>
134134
val isOldKotlin = Seq("1.0.", "1.1.", "1.2.0", "1.2.1", "1.2.2", "1.2.3", "1.2.4")
135135
.exists(prefix => kv.startsWith(prefix))
136136

137-
val compilerDep = if (useEmbeddable) {
138-
mvn"org.jetbrains.kotlin:kotlin-compiler-embeddable:${kv}"
139-
} else {
140-
mvn"org.jetbrains.kotlin:kotlin-compiler:${kv}"
141-
}
137+
val compilerDep =
138+
if (useEmbeddable) mvn"org.jetbrains.kotlin:kotlin-compiler-embeddable:${kv}"
139+
else mvn"org.jetbrains.kotlin:kotlin-compiler:${kv}"
142140

143-
val scriptCompilerDep = if (useEmbeddable) {
144-
mvn"org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:${kv}"
145-
} else {
146-
mvn"org.jetbrains.kotlin:kotlin-scripting-compiler:${kv}"
147-
}
141+
val btApiDeps = when(kotlincUseBtApi())(
142+
mvn"org.jetbrains.kotlin:kotlin-build-tools-api:$kv",
143+
mvn"org.jetbrains.kotlin:kotlin-build-tools-impl:$kv"
144+
)
145+
146+
val scriptCompilerDeps: Seq[Dep] = when(!isOldKotlin)(
147+
(if (useEmbeddable) Seq(
148+
mvn"org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:${kv}"
149+
)
150+
else Seq(
151+
mvn"org.jetbrains.kotlin:kotlin-scripting-compiler:${kv}",
152+
mvn"org.jetbrains.kotlin:kotlin-scripting-compiler-impl:${kv}",
153+
mvn"org.jetbrains.kotlin:kotlin-scripting-jvm:$kv"
154+
))*
155+
)
148156

149-
Seq(compilerDep) ++ when(!isOldKotlin)(scriptCompilerDep)
157+
Seq(compilerDep) ++ btApiDeps ++ scriptCompilerDeps
150158
}
151159

152160
/**
@@ -326,9 +334,11 @@ trait KotlinModule extends JavaModule with KotlinModuleApi { outer =>
326334
}
327335

328336
if (isMixed || isKotlin) {
337+
val extra = if (isJava) s"and reading ${javaSourceFiles.size} Java sources " else ""
329338
ctx.log.info(
330-
s"Compiling ${kotlinSourceFiles.size} Kotlin sources to ${classes} ..."
339+
s"Compiling ${kotlinSourceFiles.size} Kotlin sources ${extra}to ${classes} ..."
331340
)
341+
332342
val compilerArgs: Seq[String] = Seq(
333343
// destdir
334344
Seq("-d", classes.toString()),
@@ -344,14 +354,17 @@ trait KotlinModule extends JavaModule with KotlinModuleApi { outer =>
344354
"-Xexplicit-api=strict"
345355
),
346356
allKotlincOptions(),
347-
extraKotlinArgs,
348-
// parameters
349-
(kotlinSourceFiles ++ javaSourceFiles).map(_.toString())
357+
extraKotlinArgs
350358
).flatten
351359

352360
val workerResult =
353361
KotlinWorkerManager.kotlinWorker().withValue(kotlinCompilerClasspath()) {
354-
_.compile(KotlinWorkerTarget.Jvm, compilerArgs)
362+
_.compile(
363+
target = KotlinWorkerTarget.Jvm,
364+
useBtApi = kotlincUseBtApi(),
365+
args = compilerArgs,
366+
sources = kotlinSourceFiles ++ javaSourceFiles
367+
)
355368
}
356369

357370
val analysisFile = dest / "kotlin.analysis.dummy"
@@ -380,6 +393,15 @@ trait KotlinModule extends JavaModule with KotlinModuleApi { outer =>
380393
*/
381394
def kotlincOptions: T[Seq[String]] = Task { Seq.empty[String] }
382395

396+
/**
397+
* Enable use of new Kotlin Build API (Beta).
398+
* Enabled by default for Kotlin 2.x targetting the JVM.
399+
*/
400+
def kotlincUseBtApi: T[Boolean] = Task {
401+
Version.parse(kotlinVersion())
402+
.isNewerThan(Version.parse("2.0.0"))(using Version.IgnoreQualifierOrdering)
403+
}
404+
383405
/**
384406
* Mandatory command-line options to pass to the Kotlin compiler
385407
* that shouldn't be removed by overriding `scalacOptions`
@@ -469,6 +491,7 @@ trait KotlinModule extends JavaModule with KotlinModuleApi { outer =>
469491
}
470492
override def kotlinUseEmbeddableCompiler: Task[Boolean] =
471493
Task.Anon { outer.kotlinUseEmbeddableCompiler() }
494+
override def kotlincUseBtApi: Task.Simple[Boolean] = Task { outer.kotlincUseBtApi() }
472495
}
473496

474497
}

libs/kotlinlib/src/mill/kotlinlib/js/KotlinJsModule.scala

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -95,28 +95,8 @@ trait KotlinJsModule extends KotlinModule { outer =>
9595
* Compiles all the sources to the IR representation.
9696
*/
9797
override def compile: T[CompilationResult] = Task {
98-
KotlinWorkerManager.kotlinWorker().withValue(kotlinCompilerClasspath()) {
99-
kotlinWorker =>
100-
kotlinJsCompile(
101-
outputMode = OutputMode.KlibDir,
102-
irClasspath = None,
103-
allKotlinSourceFiles = allKotlinSourceFiles(),
104-
librariesClasspath = compileClasspath(),
105-
callMain = kotlinJsCallMain(),
106-
moduleKind = kotlinJsModuleKind(),
107-
produceSourceMaps = kotlinJsSourceMap(),
108-
sourceMapEmbedSourcesKind = kotlinJsSourceMapEmbedSources(),
109-
sourceMapNamesPolicy = kotlinJsSourceMapNamesPolicy(),
110-
splitPerModule = kotlinJsSplitPerModule(),
111-
esTarget = kotlinJsESTarget(),
112-
kotlinVersion = kotlinVersion(),
113-
destinationRoot = Task.dest,
114-
artifactId = artifactId(),
115-
explicitApi = kotlinExplicitApi(),
116-
extraKotlinArgs = allKotlincOptions(),
117-
worker = kotlinWorker
118-
)
119-
}
98+
// same as super, but kept for bin-compat
99+
kotlinCompileTask()()
120100
}
121101

122102
override def runLocal(args: Task[Args] = Task.Anon(Args())): Command[Unit] =
@@ -217,7 +197,8 @@ trait KotlinJsModule extends KotlinModule { outer =>
217197
artifactId = artifactId(),
218198
explicitApi = kotlinExplicitApi(),
219199
extraKotlinArgs = allKotlincOptions() ++ extraKotlinArgs,
220-
worker = kotlinWorker
200+
worker = kotlinWorker,
201+
useBtApi = kotlincUseBtApi()
221202
)
222203
}
223204
}
@@ -247,7 +228,8 @@ trait KotlinJsModule extends KotlinModule { outer =>
247228
artifactId = artifactId(),
248229
explicitApi = kotlinExplicitApi(),
249230
extraKotlinArgs = allKotlincOptions(),
250-
worker = kotlinWorker
231+
worker = kotlinWorker,
232+
useBtApi = kotlincUseBtApi()
251233
)
252234
}
253235
}
@@ -291,7 +273,8 @@ trait KotlinJsModule extends KotlinModule { outer =>
291273
artifactId: String,
292274
explicitApi: Boolean,
293275
extraKotlinArgs: Seq[String],
294-
worker: KotlinWorker
276+
worker: KotlinWorker,
277+
useBtApi: Boolean
295278
)(implicit ctx: mill.api.TaskCtx): Result[CompilationResult] = {
296279
val versionAllowed = kotlinVersion.split("\\.").map(_.toInt) match {
297280
case Array(1, 8, z) => z >= 20
@@ -308,10 +291,15 @@ trait KotlinJsModule extends KotlinModule { outer =>
308291
// * https://kotlinlang.org/docs/compiler-reference.html#kotlin-js-compiler-options
309292
// * https://github.com/JetBrains/kotlin/blob/v1.9.25/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt
310293

311-
val inputFiles = irClasspath match {
312-
case Some(x) => Seq(s"-Xinclude=${x.path.toIO.getAbsolutePath}")
313-
case None => allKotlinSourceFiles.map(_.path.toIO.getAbsolutePath)
314-
}
294+
// val (inputFiles, includeArgs) = irClasspath match {
295+
// case Some(x) =>
296+
// (Seq(), Seq(s"-Xinclude=${x.path.toIO.getAbsolutePath}"))
297+
// case None =>
298+
// (allKotlinSourceFiles.map(_.path.toIO.getAbsolutePath), Seq())
299+
// }
300+
301+
val includeArgs = irClasspath.map(p => s"-Xinclude=${p.path}").toSeq
302+
val inputFiles = irClasspath.fold(allKotlinSourceFiles.map(_.path))(_ => Seq())
315303

316304
val librariesCp = librariesClasspath.map(_.path)
317305
.filter(os.exists)
@@ -399,8 +387,7 @@ trait KotlinJsModule extends KotlinModule { outer =>
399387
val compilerArgs: Seq[String] = Seq(
400388
innerCompilerArgs.result(),
401389
extraKotlinArgs,
402-
// parameters
403-
inputFiles
390+
includeArgs
404391
).flatten
405392

406393
val compileDestination = os.Path(outputArgs.last)
@@ -411,7 +398,12 @@ trait KotlinJsModule extends KotlinModule { outer =>
411398
} else {
412399
Task.log.info(s"Linking IR to $compileDestination")
413400
}
414-
val workerResult = worker.compile(KotlinWorkerTarget.Js, compilerArgs)
401+
val workerResult = worker.compile(
402+
target = KotlinWorkerTarget.Js,
403+
useBtApi = useBtApi,
404+
args = compilerArgs,
405+
sources = inputFiles
406+
)
415407

416408
val analysisFile = Task.dest / "kotlin.analysis.dummy"
417409
if (!os.exists(analysisFile)) {
@@ -430,6 +422,11 @@ trait KotlinJsModule extends KotlinModule { outer =>
430422
}
431423
}
432424

425+
override def kotlincUseBtApi: Task.Simple[Boolean] = Task {
426+
// currently not implemented
427+
false
428+
}
429+
433430
private def binaryKindToOutputMode(binaryKind: Option[BinaryKind]): OutputMode =
434431
binaryKind match {
435432
// still produce IR classes, but they won't be yet linked

0 commit comments

Comments
 (0)