Skip to content

Commit

Permalink
Introduce a shared worker cache
Browse files Browse the repository at this point in the history
  • Loading branch information
lefou committed Nov 29, 2023
1 parent 267a406 commit ae2cbc5
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 22 deletions.
2 changes: 1 addition & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ trait MillKotlinModule extends PublishModule with ScoverageModule with Cross.Mod
else Seq("-source", "1.8", "-target", "1.8")
release ++ Seq("-encoding", "UTF-8", "-deprecation")
}
override def scalacOptions = Seq("-target:jvm-1.8", "-encoding", "UTF-8", "-deprecation")
override def scalacOptions = Seq("-release:8", "-encoding", "UTF-8", "-deprecation")

override def scoverageVersion = deps.scoverageVersion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ trait KotlinModulePlatform extends JavaModule {
protected type ModuleRef[T] = Function0[T]
protected def zincWorkerRef: ModuleRef[ZincWorkerModule] = () => zincWorker

protected def kotlinWorkerRef: ModuleRef[KotlinWorkerModule] = () => KotlinWorkerModule

def kotlinCompilerIvyDeps: T[Agg[Dep]]

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package de.tobiasroeser.mill.kotlin

import mill.{Agg, T}
import mill.api.{CompileProblemReporter, PathRef, Result}
import mill.api.{PathRef, Result}
import mill.define.{ModuleRef, Task}
import mill.scalalib.api.{CompilationResult, ZincWorkerApi}
import mill.scalalib.{Dep, JavaModule, ZincWorkerModule}
Expand All @@ -12,6 +12,8 @@ trait KotlinModulePlatform extends JavaModule {

protected def zincWorkerRef: ModuleRef[ZincWorkerModule] = zincWorker

protected def kotlinWorkerRef: ModuleRef[KotlinWorkerModule] = ModuleRef(KotlinWorkerModule)

def kotlinCompilerIvyDeps: T[Agg[Dep]]

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ trait KotlinModulePlatform extends JavaModule {
protected type ModuleRef[T] = Function0[T]
protected def zincWorkerRef: ModuleRef[ZincWorkerModule] = () => zincWorker

protected def kotlinWorkerRef: ModuleRef[KotlinWorkerModule] = () => KotlinWorkerModule

def kotlinCompilerIvyDeps: T[Agg[Dep]]

/**
Expand Down
28 changes: 8 additions & 20 deletions main/src/de/tobiasroeser/mill/kotlin/KotlinModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,13 @@ trait KotlinModule extends JavaModule with KotlinModulePlatform { outer =>
// ivy"org.jetbrains.kotlin:kotlin-scripting-common:${kotlinCompilerVersion()}",
}

def kotlinWorker: Worker[KotlinWorker] = T.worker {
val cl = new URLClassLoader(
kotlinCompilerClasspath().map(_.path.toIO.toURI().toURL()).toArray[URL],
getClass().getClassLoader()
)
val className =
classOf[KotlinWorker].getPackage().getName() + ".impl." + classOf[KotlinWorker].getSimpleName() + "Impl"
val impl = cl.loadClass(className)
val worker = impl.newInstance().asInstanceOf[KotlinWorker]
if (worker.getClass().getClassLoader() != cl) {
T.ctx().log.error(
"""Worker not loaded from worker classloader.
|You should not add the mill-kotlin-worker JAR to the mill build classpath""".stripMargin
)
}
if (worker.getClass().getClassLoader() == classOf[KotlinWorker].getClassLoader()) {
T.ctx().log.error("Worker classloader used to load interface and implementation")
}
worker
// @Deprecated("Use kotlinWorkerTask instead, as this does not need to be cached as Worker")
// def kotlinWorker: Worker[KotlinWorker] = T.worker {
// kotlinWorkerTask()
// }

def kotlinWorkerTask: Task[KotlinWorker] = T.task {
kotlinWorkerRef().kotlinWorkerManager().get(kotlinCompilerClasspath())
}

/**
Expand Down Expand Up @@ -176,7 +164,7 @@ trait KotlinModule extends JavaModule with KotlinModulePlatform { outer =>
(kotlinSourceFiles ++ javaSourceFiles).map(_.toIO.getAbsolutePath())
).flatten

val workerResult = kotlinWorker().compile(compilerArgs: _*)
val workerResult = kotlinWorkerTask().compile(compilerArgs: _*)

val analysisFile = dest / "kotlin.analysis.dummy"
os.write(target = analysisFile, data = "", createFolders = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.tobiasroeser.mill.kotlin

import mill.api.{Ctx, PathRef}

trait KotlinWorkerManager {
def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker
}
64 changes: 64 additions & 0 deletions main/src/de/tobiasroeser/mill/kotlin/KotlinWorkerManagerImpl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package de.tobiasroeser.mill.kotlin

import mill.PathRef
import mill.api.{Ctx, PathRef}

import java.io.PrintStream
import java.net.{URL, URLClassLoader}

class KotlinWorkerManagerImpl(ctx: Ctx) extends KotlinWorkerManager with AutoCloseable {

private[this] var workerCache: Map[Seq[PathRef], (KotlinWorker, Int)] = Map.empty

override def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker = {
val toolsCp = toolsClasspath.distinct
val (worker, count) = workerCache.get(toolsCp) match {
case Some((w, count)) =>
ctx.log.debug(s"Reusing existing AspectjWorker for classpath: ${toolsCp}")
w -> count
case None =>
ctx.log.debug(s"Creating Classloader with classpath: [${toolsCp}]")
val classLoader = new URLClassLoader(
toolsCp.map(_.path.toNIO.toUri().toURL()).toArray[URL],
getClass().getClassLoader()
)

val className =
classOf[KotlinWorker].getPackage().getName() + ".impl." + classOf[KotlinWorker].getSimpleName() + "Impl"
ctx.log.debug(s"Creating ${className} from classpath: ${toolsCp}")
val impl = classLoader.loadClass(className)
val worker = impl.getConstructor().newInstance().asInstanceOf[KotlinWorker]
if (worker.getClass().getClassLoader() != classLoader) {
ctx.log.error(
"""Worker not loaded from worker classloader.
|You should not add the mill-kotlin-worker JAR to the mill build classpath""".stripMargin
)
}
if (worker.getClass().getClassLoader() == classOf[KotlinWorker].getClassLoader()) {
ctx.log.error("Worker classloader used to load interface and implementation")
}
worker -> 0
}
workerCache += toolsCp -> (worker -> (1 + count))
ctx.log.debug(stats())
worker
}

def stats(): String = {
s"""Cache statistics of ${this.toString()}:
|${
workerCache.map { case (cp, (worker, count)) =>
s"""- worker: ${worker.toString()}
| used: ${count}
|""".stripMargin
}.mkString
}""".stripMargin
}

override def close(): Unit = {
ctx.log.debug(stats())

Check warning on line 59 in main/src/de/tobiasroeser/mill/kotlin/KotlinWorkerManagerImpl.scala

View check run for this annotation

Codecov / codecov/patch

main/src/de/tobiasroeser/mill/kotlin/KotlinWorkerManagerImpl.scala#L59

Added line #L59 was not covered by tests

// We drop cached worker instances
workerCache = Map.empty

Check warning on line 62 in main/src/de/tobiasroeser/mill/kotlin/KotlinWorkerManagerImpl.scala

View check run for this annotation

Codecov / codecov/patch

main/src/de/tobiasroeser/mill/kotlin/KotlinWorkerManagerImpl.scala#L62

Added line #L62 was not covered by tests
}
}
14 changes: 14 additions & 0 deletions main/src/de/tobiasroeser/mill/kotlin/KotlinWorkerModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.tobiasroeser.mill.kotlin

import mill.T
import mill.define.{Discover, ExternalModule, Module, Worker}

trait KotlinWorkerModule extends Module {
def kotlinWorkerManager: Worker[KotlinWorkerManager] = T.worker {
new KotlinWorkerManagerImpl(T.ctx())
}
}

object KotlinWorkerModule extends ExternalModule with KotlinWorkerModule {
override def millDiscover: Discover[this.type] = Discover[this.type]
}

0 comments on commit ae2cbc5

Please sign in to comment.