Skip to content

Commit

Permalink
Added BspModule.bspExtensions and load them via reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
lefou committed Jan 14, 2024
1 parent 4856a0b commit 5a6ec74
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 135 deletions.
46 changes: 39 additions & 7 deletions bsp/src/mill/bsp/BSP.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,44 @@ package mill.bsp

import mill.api.{Ctx, PathRef}
import mill.{Agg, T}
import mill.define.{Command, Discover, ExternalModule}
import mill.define.{Command, Discover, ExternalModule, Task}
import mill.main.BuildInfo
import mill.eval.Evaluator
import mill.util.Util.millProjectModule
import mill.scalalib.CoursierModule
import mill.scalalib.{BoundDep, CoursierModule, Dep, Lib}
import mill.scalalib.bsp.BspModule
import mill.scalalib.internal.JavaModuleUtils

object BSP extends ExternalModule with CoursierModule {

private def evaluator(): Evaluator =
Option(Evaluator.currentEvaluator.value)
.orElse(Option(Evaluator.allBootstrapEvaluators.value).flatMap(_.value.headOption))
.get

lazy val millDiscover: Discover[this.type] = Discover[this.type]

private def bspExtensions: T[(Seq[String], Seq[Dep])] = T.input {
// As this is a runtime value which can change, we need to be in an input target
val modules = JavaModuleUtils.transitiveModules(evaluator().rootModule)
.collect { case m: BspModule => m }
val extensions = modules.flatMap(m => m.bspExtensions).distinct
val classes = extensions.map(_.className).distinct
T.log.debug(s"BSP extensions: ${BspUtil.pretty(extensions)}")
val extensionIvyDeps = extensions.flatMap(_.ivyDeps)
(classes, extensionIvyDeps)
}

override def bindDependency: Task[Dep => BoundDep] = T.task { dep: Dep =>
Lib.depToBoundDep(dep, BuildInfo.scalaVersion, s"_mill${BuildInfo.millBinPlatform}")
}

private def bspWorkerLibs: T[Agg[PathRef]] = T {
millProjectModule("mill-bsp-worker", repositoriesTask())
millProjectModule(
"mill-bsp-worker",
repositoriesTask(),
extraDeps = bspExtensions()._2.map(bindDependency().andThen(_.dep))
)
}

/**
Expand All @@ -32,12 +58,17 @@ object BSP extends ExternalModule with CoursierModule {
*/
def install(jobs: Int = 1): Command[(PathRef, ujson.Value)] = T.command {
// we create a file containing the additional jars to load
val libUrls = bspWorkerLibs().map(_.path.toNIO.toUri.toURL).iterator.toSeq
// val libUrls = bspWorkerLibs().map(_.path.toNIO.toUri).iterator.toSeq

val bspServerConfig = BspServerConfig(
bspExtensions()._1,
bspWorkerLibs().toSeq
)
val cpFile =
T.workspace / Constants.bspDir / s"${Constants.serverName}-${BuildInfo.millVersion}.resources"
T.workspace / Constants.bspDir / s"${Constants.serverName}-${BuildInfo.millVersion}.conf"
os.write.over(
cpFile,
libUrls.mkString("\n"),
upickle.default.write(bspServerConfig, indent = 2),
createFolders = true
)
createBspConnection(jobs, Constants.serverName)
Expand Down Expand Up @@ -97,7 +128,8 @@ object BSP extends ExternalModule with CoursierModule {
millVersion = BuildInfo.millVersion,
bspVersion = Constants.bspProtocolVersion,
languages = Constants.languages
)
),
indent = 2
)
}

Expand Down
3 changes: 2 additions & 1 deletion bsp/src/mill/bsp/BspContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ private[mill] class BspContext(
streams,
logStream.getOrElse(streams.err),
home / Constants.bspDir,
canReload
canReload,
os.pwd
)
}
}
Expand Down
69 changes: 40 additions & 29 deletions bsp/src/mill/bsp/BspWorker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,69 @@ import mill.api.{Ctx, Logger, SystemStreams}
import os.Path

import java.io.PrintStream
import java.net.URL

private trait BspWorker {
def startBspServer(
streams: SystemStreams,
logStream: PrintStream,
logDir: os.Path,
canReload: Boolean
canReload: Boolean,
projectDir: os.Path
): Either[String, BspServerHandle]
}

private object BspWorker {

private[this] var worker: Option[BspWorker] = None

def readConfig(workspace: os.Path): Either[String, BspServerConfig] = {
val configFile =
workspace / Constants.bspDir / s"${Constants.serverName}-${mill.main.BuildInfo.millVersion}.conf"
if (!os.exists(configFile)) return Left(
s"""Could not find config file: ${configFile}
|You need to run `mill mill.bsp.BSP/install` before you can use the BSP server""".stripMargin
)

val config = upickle.default.read[BspServerConfig](
os.read(configFile)
)

// TODO: if outdated, we could regenerate the resource file and re-load the worker
Right(config)
}

def apply(
workspace: os.Path,
home0: os.Path,
log: Logger,
workerLibs: Option[Seq[URL]] = None
log: Logger
// workerLibs: Option[Seq[URL]] = None
): Either[String, BspWorker] = {
worker match {
case Some(x) => Right(x)
case None =>
val urls = workerLibs.map { urls =>
log.debug("Using direct submitted worker libs")
urls
}.getOrElse {
// load extra classpath entries from file
val cpFile =
workspace / Constants.bspDir / s"${Constants.serverName}-${mill.main.BuildInfo.millVersion}.resources"
if (!os.exists(cpFile)) return Left(
"You need to run `mill mill.bsp.BSP/install` before you can use the BSP server"
)
// val urls = workerLibs.map { urls =>
// log.debug("Using direct submitted worker libs")
// urls
// }.getOrElse {
// load extra config from file
readConfig(workspace).map { config =>
log.debug(s"BSP Server config: ${BspUtil.pretty(config)}")

// TODO: if outdated, we could regenerate the resource file and re-load the worker
val urls = config.classpath.map(_.path.toNIO.toUri.toURL)

// read the classpath from resource file
log.debug(s"Reading worker classpath from file: ${cpFile}")
os.read(cpFile).linesIterator.map(u => new URL(u)).toSeq
}

// create classloader with bsp.worker and deps
val cl = mill.api.ClassLoader.create(urls, getClass().getClassLoader())(
new Ctx.Home { override def home: Path = home0 }
)
// create classloader with bsp.worker and deps
val cl = mill.api.ClassLoader.create(urls, getClass().getClassLoader())(
new Ctx.Home {
override def home: Path = home0
}
)

val workerCls = cl.loadClass(Constants.bspWorkerImplClass)
val ctr = workerCls.getConstructor()
val workerImpl = ctr.newInstance().asInstanceOf[BspWorker]
worker = Some(workerImpl)
Right(workerImpl)
val workerCls = cl.loadClass(Constants.bspWorkerImplClass)
val ctr = workerCls.getConstructor()
val workerImpl = ctr.newInstance().asInstanceOf[BspWorker]
worker = Some(workerImpl)
workerImpl
}
}
}

Expand Down
Loading

0 comments on commit 5a6ec74

Please sign in to comment.