From 2269dbfca8b1118ec7f89abeb52e70a88376432c Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Tue, 8 Oct 2019 12:00:28 +0200 Subject: Reformat code --- contrib/bsp/src/mill/contrib/BSP.scala | 78 ++-- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 104 ++--- .../bsp/src/mill/contrib/bsp/BspTestReporter.scala | 47 +- .../bsp/src/mill/contrib/bsp/MillBspLogger.scala | 1 + .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 487 +++++++++++---------- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 186 ++++---- .../bsp/src/mill/contrib/bsp/TaskParameters.scala | 20 +- main/api/src/mill/api/Ctx.scala | 14 +- 8 files changed, 493 insertions(+), 444 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala index d31eb7f4..ab432743 100644 --- a/contrib/bsp/src/mill/contrib/BSP.scala +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -1,17 +1,16 @@ package mill.contrib -import java.io.{File, PrintWriter} - -import play.api.libs.json._ +import java.io.PrintWriter import java.nio.file.FileAlreadyExistsException import java.util.concurrent.Executors -import upickle.default._ import ch.epfl.scala.bsp4j._ import mill._ import mill.define.{Command, Discover, ExternalModule} import mill.eval.Evaluator import org.eclipse.lsp4j.jsonrpc.Launcher +import play.api.libs.json._ +import upickle.default._ import scala.collection.JavaConverters._ import scala.concurrent.CancellationException @@ -26,35 +25,6 @@ object BSP extends ExternalModule { val bspVersion = "2.0.0" val languages = List("scala", "java") - // computes the path to the java executable - def whichJava: String = { - if (scala.sys.props.contains("JAVA_HOME")) scala.sys.props("JAVA_HOME") else "java" - } - - // creates a Json with the BSP connection details - def createBspConnectionJson(): JsValue = { - - implicit val connectionWrites = new Writes[BspConnectionDetails] { - def writes(connection: BspConnectionDetails) = Json.obj( - "name" -> connection.getName, - "argv" -> new JsArray(connection.getArgv.asScala.map(string => JsString(string)).toIndexedSeq), - "version" -> connection.getVersion, - "bspVersion" -> connection.getBspVersion, - "languages" -> new JsArray(connection.getLanguages.asScala.map(string => JsString(string)).toIndexedSeq) - ) - } - val millPath = scala.sys.props("MILL_CLASSPATH") - Json.toJson(new BspConnectionDetails("mill-bsp", - List(whichJava,"-DMILL_CLASSPATH=" + millPath, - s"-DMILL_VERSION=${scala.sys.props("MILL_VERSION")}", - "-Djna.nosys=true", "-cp", - millPath, - "mill.MillMain", "mill.contrib.BSP/start").asJava, - version, - bspVersion, - languages.asJava)) - } - /** * Installs the mill-bsp server. It creates a json file * with connection details in the ./.bsp directory for @@ -70,9 +40,9 @@ object BSP extends ExternalModule { * printed to stdout. * */ - def install(ev: Evaluator): Command[Unit] = T.command{ + def install(ev: Evaluator): Command[Unit] = T.command { val bspDirectory = os.pwd / ".bsp" - if (! os.exists(bspDirectory)) os.makeDir.all(bspDirectory) + if (!os.exists(bspDirectory)) os.makeDir.all(bspDirectory) try { os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) } catch { @@ -81,23 +51,53 @@ object BSP extends ExternalModule { os.remove(bspDirectory / "mill.json") os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + - " " + e.getStackTrace.toString) + " " + e.getStackTrace.toString) } } + // creates a Json with the BSP connection details + def createBspConnectionJson(): JsValue = { + + implicit val connectionWrites = new Writes[BspConnectionDetails] { + def writes(connection: BspConnectionDetails) = Json.obj( + "name" -> connection.getName, + "argv" -> new JsArray(connection.getArgv.asScala.map(string => JsString(string)).toIndexedSeq), + "version" -> connection.getVersion, + "bspVersion" -> connection.getBspVersion, + "languages" -> new JsArray(connection.getLanguages.asScala.map(string => JsString(string)).toIndexedSeq) + ) + } + val millPath = scala.sys.props("MILL_CLASSPATH") + Json.toJson(new BspConnectionDetails("mill-bsp", + List(whichJava, "-DMILL_CLASSPATH=" + millPath, + s"-DMILL_VERSION=${scala.sys.props("MILL_VERSION")}", + "-Djna.nosys=true", "-cp", + millPath, + "mill.MillMain", "mill.contrib.BSP/start").asJava, + version, + bspVersion, + languages.asJava)) + } + + // computes the path to the java executable + def whichJava: String = { + if (scala.sys.props.contains("JAVA_HOME")) scala.sys.props("JAVA_HOME") else "java" + } + /** * Computes a mill command which starts the mill-bsp * server and establishes connection to client. Waits * until a client connects and ends the connection * after the client sent an "exit" notification + * * @param ev Environment, used by mill to evaluate commands * @return: mill.Command which executes the starting of the - * server + * server */ def start(ev: Evaluator): Command[Unit] = T.command { val eval = new Evaluator(ev.home, ev.outPath, ev.externalOutPath, ev.rootModule, ev.log, ev.classLoaderSig, - ev.workerCache, ev.env, false) + ev.workerCache, ev.env, false) val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) val executor = Executors.newCachedThreadPool() @@ -109,7 +109,7 @@ object BSP extends ExternalModule { .setInput(stdin) .setLocalService(millServer) .setRemoteInterface(classOf[BuildClient]). - traceMessages(new PrintWriter((os.pwd/ "bsp.log" ).toIO)) + traceMessages(new PrintWriter((os.pwd / "bsp.log").toIO)) .setExecutorService(executor) .create() millServer.onConnectWithClient(launcher.getRemoteProxy) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index b4f0260d..3e65b648 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -6,7 +6,7 @@ import java.util.concurrent.atomic.AtomicInteger import ch.epfl.scala.bsp4j._ import ch.epfl.scala.{bsp4j => bsp} -import mill.api.{Info, Problem, Warn, BuildProblemReporter} +import mill.api.{BuildProblemReporter, Problem} import scala.collection.JavaConverters._ import scala.collection.concurrent @@ -17,11 +17,11 @@ import scala.language.implicitConversions * for each problem it logs, either as information, warning or * error as well as task finish notifications of type `compile-report`. * - * @param client the client to send diagnostics to - * @param targetId the target id of the target whose compilation - * the diagnostics are related to - * @param taskId a unique id of the compilation task of the target - * specified by `targetId` + * @param client the client to send diagnostics to + * @param targetId the target id of the target whose compilation + * the diagnostics are related to + * @param taskId a unique id of the compilation task of the target + * specified by `targetId` * @param compilationOriginId optional origin id the client assigned to * the compilation request. Needs to be sent * back as part of the published diagnostics @@ -48,25 +48,6 @@ class BspLoggedReporter(client: bsp.BuildClient, infos.incrementAndGet() } - override def logWarning(problem: Problem): Unit = { - client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) - warnings.incrementAndGet() - } - - override def printSummary(): Unit = { - val taskFinishParams = new TaskFinishParams(taskId, getStatusCode) - taskFinishParams.setEventTime(System.currentTimeMillis()) - taskFinishParams.setMessage("Finished compiling target: " + targetId.getUri) - taskFinishParams.setDataKind("compile-report") - val compileReport = new CompileReport(targetId, errors.get, warnings.get) - compilationOriginId match { - case Some(id) => compileReport.setOriginId(id) - case None => - } - taskFinishParams.setData(compileReport) - client.onBuildTaskFinish(taskFinishParams) - } - // Obtains the parameters for sending diagnostics about the given Problem ( as well as // about all previous problems generated for the same text file ) related to the specified // targetId, incorporating the given optional originId ( generated by the client for the @@ -74,27 +55,24 @@ class BspLoggedReporter(client: bsp.BuildClient, //TODO: document that if the problem is a general information without a text document // associated to it, then the document field of the diagnostic is set to the uri of the target private[this] def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): - bsp.PublishDiagnosticsParams = { - val diagnostic = getSingleDiagnostic(problem) - val sourceFile = problem.position.sourceFile - val textDocument = new TextDocumentIdentifier( - sourceFile.getOrElse(None) match { + bsp.PublishDiagnosticsParams = { + val diagnostic = getSingleDiagnostic(problem) + val sourceFile = problem.position.sourceFile + val textDocument = new TextDocumentIdentifier( + sourceFile.getOrElse(None) match { case None => targetId.getUri case f: File => f.toURI.toString }) - val params = new bsp.PublishDiagnosticsParams(textDocument, - targetId, - appendDiagnostics(textDocument, diagnostic).asJava, - true) + val params = new bsp.PublishDiagnosticsParams(textDocument, + targetId, + appendDiagnostics(textDocument, diagnostic).asJava, + true) - if (originId.nonEmpty) { params.setOriginId(originId.get) } - diagnosticMap.put(textDocument, params) - params + if (originId.nonEmpty) { + params.setOriginId(originId.get) } - - // Compute the compilation status code - private[this] def getStatusCode: StatusCode = { - if (errors.get > 0) StatusCode.ERROR else StatusCode.OK + diagnosticMap.put(textDocument, params) + params } // Update the published diagnostics for the fiven text file by @@ -103,14 +81,14 @@ class BspLoggedReporter(client: bsp.BuildClient, private[this] def appendDiagnostics(textDocument: TextDocumentIdentifier, currentDiagnostic: Diagnostic): List[Diagnostic] = { diagnosticMap.putIfAbsent(textDocument, new bsp.PublishDiagnosticsParams( - textDocument, - targetId, - List.empty[Diagnostic].asJava, true)) + textDocument, + targetId, + List.empty[Diagnostic].asJava, true)) diagnosticMap(textDocument).getDiagnostics.asScala.toList ++ List(currentDiagnostic) } // Computes the diagnostic related to the given Problem - private[this] def getSingleDiagnostic(problem: Problem): Diagnostic ={ + private[this] def getSingleDiagnostic(problem: Problem): Diagnostic = { val pos = problem.position val i: Integer = pos.startLine.orElse(pos.line).getOrElse[Int](0) println(i) @@ -123,12 +101,36 @@ class BspLoggedReporter(client: bsp.BuildClient, val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) diagnostic.setCode(pos.lineContent) diagnostic.setSource("compiler from mill") - diagnostic.setSeverity( problem.severity match { - case mill.api.Info => bsp.DiagnosticSeverity.INFORMATION - case mill.api.Error => bsp.DiagnosticSeverity.ERROR - case mill.api.Warn => bsp.DiagnosticSeverity.WARNING - } - ) + diagnostic.setSeverity(problem.severity match { + case mill.api.Info => bsp.DiagnosticSeverity.INFORMATION + case mill.api.Error => bsp.DiagnosticSeverity.ERROR + case mill.api.Warn => bsp.DiagnosticSeverity.WARNING + } + ) diagnostic } + + override def logWarning(problem: Problem): Unit = { + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + warnings.incrementAndGet() + } + + override def printSummary(): Unit = { + val taskFinishParams = new TaskFinishParams(taskId, getStatusCode) + taskFinishParams.setEventTime(System.currentTimeMillis()) + taskFinishParams.setMessage("Finished compiling target: " + targetId.getUri) + taskFinishParams.setDataKind("compile-report") + val compileReport = new CompileReport(targetId, errors.get, warnings.get) + compilationOriginId match { + case Some(id) => compileReport.setOriginId(id) + case None => + } + taskFinishParams.setData(compileReport) + client.onBuildTaskFinish(taskFinishParams) + } + + // Compute the compilation status code + private[this] def getStatusCode: StatusCode = { + if (errors.get > 0) StatusCode.ERROR else StatusCode.OK + } } diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 9681313a..922e26eb 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -10,12 +10,13 @@ import sbt.testing._ /** * Context class for BSP, specialized for sending `task-start` and * `task-finish` notifications for every test being ran. - * @param client The client to send notifications to - * @param targetId The targetId of the BSP target for which - * the test request is being processed - * @param taskId The unique taskId associated with the - * test task that will trigger this reporter - * to log testing events. + * + * @param client The client to send notifications to + * @param targetId The targetId of the BSP target for which + * the test request is being processed + * @param taskId The unique taskId associated with the + * test task that will trigger this reporter + * to log testing events. * @param arguments compilation arguments as part of the BSP context, * in case special arguments need to be passed to * the compiler before running the test task. @@ -42,14 +43,26 @@ class BspTestReporter( client.onBuildTaskStart(taskStartParams) } + // Compute the display name of the test / test suite + // to which the given event relates + private[this] def getDisplayName(e: Event): String = { + e.selector() match { + case s: NestedSuiteSelector => s.suiteId() + case s: NestedTestSelector => s.suiteId() + "." + s.testName() + case s: SuiteSelector => s.toString + case s: TestSelector => s.testName() + case s: TestWildcardSelector => s.testWildcard() + } + } + override def logFinish(event: Event): Unit = { totalTime += event.duration() val taskFinishParams = new TaskFinishParams(taskId, - event.status() match { - case sbt.testing.Status.Canceled => StatusCode.CANCELLED - case sbt.testing.Status.Error => StatusCode.ERROR - case default => StatusCode.OK - }) + event.status() match { + case sbt.testing.Status.Canceled => StatusCode.CANCELLED + case sbt.testing.Status.Error => StatusCode.ERROR + case default => StatusCode.OK + }) val status = event.status match { case sbt.testing.Status.Success => passed += 1 @@ -92,18 +105,6 @@ class BspTestReporter( sw.toString } - // Compute the display name of the test / test suite - // to which the given event relates - private[this] def getDisplayName(e: Event): String = { - e.selector() match{ - case s: NestedSuiteSelector => s.suiteId() - case s: NestedTestSelector => s.suiteId() + "." + s.testName() - case s: SuiteSelector => s.toString - case s: TestSelector => s.testName() - case s: TestWildcardSelector => s.testWildcard() - } - } - // Compute the test report data structure that will go into // the task finish notification after all tests are ran. def getTestReport: TestReport = { diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala index a17c4a40..12638bed 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala @@ -10,6 +10,7 @@ import mill.util.ProxyLogger * notifications ( upon the invocation of the `ticker` method ) and * `show-message` notifications ( for each error or information * being logged ). + * * @param client the client to send notifications to, also the * client that initiated a request which triggered * a mill task evaluation diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 40e98ef7..95eb43cd 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -5,7 +5,7 @@ import java.util.concurrent.CompletableFuture import ch.epfl.scala.bsp4j._ import com.google.gson.JsonObject import mill.api.Result.{Skipped, Success} -import mill.api.{DummyTestReporter, Result, Strict, BuildProblemReporter} +import mill.api.{BuildProblemReporter, DummyTestReporter, Result, Strict} import mill.contrib.bsp.ModuleUtils._ import mill.define.Segment.Label import mill.define.{Discover, ExternalModule} @@ -26,37 +26,36 @@ import scala.collection.JavaConverters._ class MillBuildServer(evaluator: Evaluator, _bspVersion: String, - serverVersion:String, - languages: List[String]) extends ExternalModule with BuildServer with ScalaBuildServer { + serverVersion: String, + languages: List[String]) extends ExternalModule with BuildServer with ScalaBuildServer { implicit def millScoptEvaluatorReads[T]: EvaluatorScopt[T] = new mill.main.EvaluatorScopt[T]() + lazy val millDiscover: Discover[MillBuildServer.this.type] = Discover[this.type] val bspVersion: String = _bspVersion val supportedLanguages: List[String] = languages val millServerVersion: String = serverVersion - var cancelator: () => Unit = () => () val millEvaluator: Evaluator = evaluator + val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { + val log: DummyLogger.type = mill.util.DummyLogger + val home: Path = os.pwd + } + var cancelator: () => Unit = () => () var rootModule: JavaModule = ModuleUtils.getRootJavaModule(evaluator.rootModule) var millModules: Seq[JavaModule] = getMillModules(millEvaluator) var client: BuildClient = _ var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap( - millModules, - evaluator - ) + millModules, + evaluator + ) var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = - ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) + ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) var moduleCodeToTargetId: Predef.Map[Int, BuildTargetIdentifier] = - for ( (targetId, module) <- targetIdToModule ) yield (module.hashCode(), targetId) - + for ((targetId, module) <- targetIdToModule) yield (module.hashCode(), targetId) var initialized = false var clientInitialized = false - val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { - val log: DummyLogger.type = mill.util.DummyLogger - val home: Path = os.pwd - } - override def onConnectWithClient(server: BuildClient): Unit = client = server @@ -89,14 +88,15 @@ class MillBuildServer(evaluator: Evaluator, } override def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = { - recomputeTargets() - handleExceptions[String, WorkspaceBuildTargetsResult]( - _ => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), - "") + recomputeTargets() + handleExceptions[String, WorkspaceBuildTargetsResult]( + _ => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), + "") } override def buildTargetSources(sourcesParams: SourcesParams): CompletableFuture[SourcesResult] = { recomputeTargets() + def computeSourcesResult: SourcesResult = { var items = List[SourcesItem]() @@ -104,11 +104,11 @@ class MillBuildServer(evaluator: Evaluator, var itemSources = List[SourceItem]() val sources = evaluateInformativeTask(evaluator, targetIdToModule(targetId).sources, Agg.empty[PathRef]). - map(pathRef => pathRef.path).toSeq + map(pathRef => pathRef.path).toSeq val generatedSources = evaluateInformativeTask(evaluator, - targetIdToModule(targetId).generatedSources, - Agg.empty[PathRef]). - map(pathRef => pathRef.path).toSeq + targetIdToModule(targetId).generatedSources, + Agg.empty[PathRef]). + map(pathRef => pathRef.path).toSeq for (source <- sources) { itemSources ++= List( @@ -125,28 +125,31 @@ class MillBuildServer(evaluator: Evaluator, new SourcesResult(items.asJava) } + handleExceptions[String, SourcesResult](_ => computeSourcesResult, "") } override def buildTargetInverseSources(inverseSourcesParams: InverseSourcesParams): - CompletableFuture[InverseSourcesResult] = { + CompletableFuture[InverseSourcesResult] = { recomputeTargets() def getInverseSourcesResult: InverseSourcesResult = { val textDocument = inverseSourcesParams.getTextDocument val targets = millModules.filter(m => ModuleUtils.evaluateInformativeTask( - millEvaluator, m.allSourceFiles, Seq.empty[PathRef]). - map(pathRef => pathRef.path.toIO.toURI.toString). - contains(textDocument.getUri)). - map(m => moduleToTargetId(m)) + millEvaluator, m.allSourceFiles, Seq.empty[PathRef]). + map(pathRef => pathRef.path.toIO.toURI.toString). + contains(textDocument.getUri)). + map(m => moduleToTargetId(m)) new InverseSourcesResult(targets.asJava) } + handleExceptions[String, InverseSourcesResult](_ => getInverseSourcesResult, "") } override def buildTargetDependencySources(dependencySourcesParams: DependencySourcesParams): - CompletableFuture[DependencySourcesResult] = { + CompletableFuture[DependencySourcesResult] = { recomputeTargets() + def getDependencySources: DependencySourcesResult = { var items = List[DependencySourcesItem]() @@ -155,117 +158,136 @@ class MillBuildServer(evaluator: Evaluator, var sources = evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.transitiveIvyDeps), Agg.empty[PathRef]) ++ - evaluateInformativeTask(evaluator, - millModule.resolveDeps(millModule.compileIvyDeps), - Agg.empty[PathRef]) ++ - evaluateInformativeTask(evaluator, - millModule.unmanagedClasspath, - Agg.empty[PathRef]) + evaluateInformativeTask(evaluator, + millModule.resolveDeps(millModule.compileIvyDeps), + Agg.empty[PathRef]) ++ + evaluateInformativeTask(evaluator, + millModule.unmanagedClasspath, + Agg.empty[PathRef]) millModule match { case _: ScalaModule => sources ++= evaluateInformativeTask(evaluator, - millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps), - Agg.empty[PathRef]) + millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps), + Agg.empty[PathRef]) case _: JavaModule => sources ++= List() } items ++= List(new DependencySourcesItem(targetId, sources. - map(pathRef => pathRef.path.toIO.toURI.toString). - toList.asJava)) + map(pathRef => pathRef.path.toIO.toURI.toString). + toList.asJava)) } new DependencySourcesResult(items.asJava) } + handleExceptions[String, DependencySourcesResult](_ => getDependencySources, "") } + // Recompute the modules in the project in case any changes to the build took place + // and update all the mappings that depend on this info + private[this] def recomputeTargets(): Unit = { + rootModule = ModuleUtils.getRootJavaModule(millEvaluator.rootModule) + millModules = getMillModules(millEvaluator) + moduleToTargetId = ModuleUtils.getModuleTargetIdMap(millModules, millEvaluator) + targetIdToModule = targetToModule(moduleToTargetId) + moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) + } + + // Given the mapping from modules to targetIds, construct the mapping from targetIds to modules + private[this] def targetToModule(moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier]): + Predef.Map[BuildTargetIdentifier, JavaModule] = { + moduleToTargetId.keys.map(mod => (moduleToTargetId(mod), mod)).toMap + + } + + // Resolve all the mill modules contained in the project + private[this] def getMillModules(ev: Evaluator): Seq[JavaModule] = { + ev.rootModule.millInternal.segmentsToModules.values. + collect { + case m: scalalib.JavaModule => m + }.toSeq ++ Seq(rootModule) + } + + // Given a function that take input of type T and return output of type V, + // apply the function on the given inputs and return a completable future of + // the result. If the execution of the function raises an Exception, complete + // the future exceptionally. Also complete exceptionally if the server was not + // yet initialized. + private[this] def handleExceptions[T, V](serverMethod: T => V, input: T): CompletableFuture[V] = { + val future = new CompletableFuture[V]() + if (initialized) { + try { + future.complete(serverMethod(input)) + } catch { + case e: Exception => future.completeExceptionally(e) + } + } else { + future.completeExceptionally( + new Exception("Can not respond to any request before receiving the `initialize` request.") + ) + } + future + } + override def buildTargetResources(resourcesParams: ResourcesParams): CompletableFuture[ResourcesResult] = { recomputeTargets() + def getResources: ResourcesResult = { var items = List[ResourcesItem]() for (targetId <- resourcesParams.getTargets.asScala) { val millModule = targetIdToModule(targetId) val resources = evaluateInformativeTask(evaluator, millModule.resources, Agg.empty[PathRef]). - filter(pathRef => os.exists(pathRef.path)). - flatMap(pathRef => os.walk(pathRef.path)). - map(path => path.toIO.toURI.toString). - toList.asJava + filter(pathRef => os.exists(pathRef.path)). + flatMap(pathRef => os.walk(pathRef.path)). + map(path => path.toIO.toURI.toString). + toList.asJava items ++= List(new ResourcesItem(targetId, resources)) } new ResourcesResult(items.asJava) } - handleExceptions[String, ResourcesResult](_ => getResources, "") - } - // construct the ManagedLogger that will go into the compilation problems reporter - private[this] def getCompilationLogger: ManagedLogger = { - val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( - millEvaluator.log.outputStream - )) - val l = LogExchange.logger("Hello") - LogExchange.unbindLoggerAppenders("Hello") - LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Info) :: Nil) - l - } - - // define the function that spawns compilation reporter for each module based on the - // module's hash code TODO: find something more reliable than the hash code - private[this] def getBspLoggedReporterPool(params: Parameters, taskStartMessage: String => String, - taskStartDataKind: String, taskStartData: BuildTargetIdentifier => Object): - Int => Option[BuildProblemReporter] = { - int: Int => - if (moduleCodeToTargetId.contains(int)) { - val targetId = moduleCodeToTargetId(int) - val taskId = new TaskId(targetIdToModule(targetId).compile.hashCode.toString) - val taskStartParams = new TaskStartParams(taskId) - taskStartParams.setEventTime(System.currentTimeMillis()) - taskStartParams.setData(taskStartData(targetId)) - taskStartParams.setDataKind(taskStartDataKind) - taskStartParams.setMessage(taskStartMessage(moduleToTarget(targetIdToModule(targetId)).getDisplayName)) - client.onBuildTaskStart(taskStartParams) - Option(new BspLoggedReporter(client, - targetId, - taskId, - params.getOriginId))} - else None + handleExceptions[String, ResourcesResult](_ => getResources, "") } //TODO: if the client wants to give compilation arguments and the module // already has some from the build file, what to do? override def buildTargetCompile(compileParams: CompileParams): CompletableFuture[CompileResult] = { recomputeTargets() + def getCompileResult: CompileResult = { val params = TaskParameters.fromCompileParams(compileParams) val taskId = params.hashCode() val compileTasks = Strict.Agg(params.getTargets. - filter(targetId => targetId != moduleToTarget(rootModule).getId). - map(targetId => targetIdToModule(targetId).compile):_*) + filter(targetId => targetId != moduleToTarget(rootModule).getId). + map(targetId => targetIdToModule(targetId).compile): _*) val result = millEvaluator.evaluate(compileTasks, - getBspLoggedReporterPool(params, t => s"Started compiling target: $t", - TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), - DummyTestReporter, - new MillBspLogger(client, taskId, millEvaluator.log) - ) + getBspLoggedReporterPool(params, t => s"Started compiling target: $t", + TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), + DummyTestReporter, + new MillBspLogger(client, taskId, millEvaluator.log) + ) val compileResult = new CompileResult(getStatusCode(result)) compileResult.setOriginId(compileParams.getOriginId) compileResult //TODO: See in what form IntelliJ expects data about products of compilation in order to set data field } + handleExceptions[String, CompileResult](_ => getCompileResult, "") } override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = { recomputeTargets() + def getRunResult: RunResult = { val params = TaskParameters.fromRunParams(runParams) val module = targetIdToModule(params.getTargets.head) val args = params.getArguments.getOrElse(Seq.empty[String]) - val runTask = module.run(args:_*) + val runTask = module.run(args: _*) val runResult = millEvaluator.evaluate(Strict.Agg(runTask), - getBspLoggedReporterPool( - params, - t => s"Started compiling target: $t", - TaskDataKind.COMPILE_TASK, - (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), - logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) + getBspLoggedReporterPool( + params, + t => s"Started compiling target: $t", + TaskDataKind.COMPILE_TASK, + (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), + logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) val response = runResult.results(runTask) match { case _: Result.Success[Any] => new RunResult(StatusCode.OK) case _ => new RunResult(StatusCode.ERROR) @@ -276,52 +298,34 @@ class MillBuildServer(evaluator: Evaluator, } response } - handleExceptions[String, RunResult](_ => getRunResult, "") - } - - private[this] def getStatusCodePerTask(results: Evaluator.Results, task: mill.define.Task[_]): StatusCode = { - results.results(task) match { - case _: Success[_] => StatusCode.OK - case Skipped => StatusCode.CANCELLED - case _ => StatusCode.ERROR - } - } - - // Get the execution status code given the results from Evaluator.evaluate - private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { - val statusCodes = results.results.keys.map(task => getStatusCodePerTask(results, task)).toSeq - if (statusCodes.contains(StatusCode.ERROR)) - StatusCode.ERROR - else if (statusCodes.contains(StatusCode.CANCELLED)) - StatusCode.CANCELLED - else - StatusCode.OK + handleExceptions[String, RunResult](_ => getRunResult, "") } override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = { recomputeTargets() + def getTestResult: TestResult = { val params = TaskParameters.fromTestParams(testParams) val argsMap = try { - val scalaTestParams = testParams.getData.asInstanceOf[JsonObject] - (for (testItem <- scalaTestParams.get("testClasses").getAsJsonArray.asScala) - yield ( - testItem.getAsJsonObject.get("target").getAsJsonObject.get("uri").getAsString, - testItem.getAsJsonObject.get("classes").getAsJsonArray - .asScala.map(elem => elem.getAsString).toSeq)).toMap - } catch { - case _: Exception => (for (targetId <- testParams.getTargets.asScala) yield - (targetId.getUri, Seq.empty[String])).toMap - - } + val scalaTestParams = testParams.getData.asInstanceOf[JsonObject] + (for (testItem <- scalaTestParams.get("testClasses").getAsJsonArray.asScala) + yield ( + testItem.getAsJsonObject.get("target").getAsJsonObject.get("uri").getAsString, + testItem.getAsJsonObject.get("classes").getAsJsonArray + .asScala.map(elem => elem.getAsString).toSeq)).toMap + } catch { + case _: Exception => (for (targetId <- testParams.getTargets.asScala) yield + (targetId.getUri, Seq.empty[String])).toMap + + } var overallStatusCode = StatusCode.OK for (targetId <- testParams.getTargets.asScala) { val module = targetIdToModule(targetId) module match { case m: TestModule => val testModule = m.asInstanceOf[TestModule] - val testTask = testModule.testLocal(argsMap(targetId.getUri):_*) + val testTask = testModule.testLocal(argsMap(targetId.getUri): _*) // notifying the client that the testing of this build target started val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) @@ -339,7 +343,7 @@ class MillBuildServer(evaluator: Evaluator, val results = millEvaluator.evaluate( Strict.Agg(testTask), getBspLoggedReporterPool(params, t => s"Started compiling target: $t", - TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), + TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), testReporter, new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) val endTime = System.currentTimeMillis() @@ -354,10 +358,10 @@ class MillBuildServer(evaluator: Evaluator, val taskFinishParams = new TaskFinishParams( new TaskId(testTask.hashCode().toString), statusCode - ) + ) taskFinishParams.setEventTime(endTime) taskFinishParams.setMessage("Finished testing target: " + - moduleToTarget(targetIdToModule(targetId)).getDisplayName) + moduleToTarget(targetIdToModule(targetId)).getDisplayName) taskFinishParams.setDataKind(TaskDataKind.TEST_REPORT) taskFinishParams.setData(testReporter.getTestReport) client.onBuildTaskFinish(taskFinishParams) @@ -369,56 +373,105 @@ class MillBuildServer(evaluator: Evaluator, params.getOriginId match { case None => testResult case Some(id) => - //TODO: Add the messages from mill to the data field? + //TODO: Add the messages from mill to the data field? testResult.setOriginId(id) testResult } } + handleExceptions[String, TestResult](_ => getTestResult, "") } + // define the function that spawns compilation reporter for each module based on the + // module's hash code TODO: find something more reliable than the hash code + private[this] def getBspLoggedReporterPool(params: Parameters, + taskStartMessage: String => String, + taskStartDataKind: String, + taskStartData: BuildTargetIdentifier => Object): + Int => Option[BuildProblemReporter] = { + int: Int => + if (moduleCodeToTargetId.contains(int)) { + val targetId = moduleCodeToTargetId(int) + val taskId = new TaskId(targetIdToModule(targetId).compile.hashCode.toString) + val taskStartParams = new TaskStartParams(taskId) + taskStartParams.setEventTime(System.currentTimeMillis()) + taskStartParams.setData(taskStartData(targetId)) + taskStartParams.setDataKind(taskStartDataKind) + taskStartParams.setMessage(taskStartMessage(moduleToTarget(targetIdToModule(targetId)).getDisplayName)) + client.onBuildTaskStart(taskStartParams) + Option(new BspLoggedReporter(client, + targetId, + taskId, + params.getOriginId)) + } + else None + } + + // Get the execution status code given the results from Evaluator.evaluate + private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { + + val statusCodes = results.results.keys.map(task => getStatusCodePerTask(results, task)).toSeq + if (statusCodes.contains(StatusCode.ERROR)) + StatusCode.ERROR + else if (statusCodes.contains(StatusCode.CANCELLED)) + StatusCode.CANCELLED + else + StatusCode.OK + } + + private[this] def getStatusCodePerTask(results: Evaluator.Results, task: mill.define.Task[_]): StatusCode = { + results.results(task) match { + case _: Success[_] => StatusCode.OK + case Skipped => StatusCode.CANCELLED + case _ => StatusCode.ERROR + } + } + override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = { - recomputeTargets() - def getCleanCacheResult: CleanCacheResult = { - var msg = "" - var cleaned = true - for (targetId <- cleanCacheParams.getTargets.asScala) { - val module = targetIdToModule(targetId) - val mainModule = new MainModule { - override implicit def millDiscover: Discover[_] = { - Discover[this.type] - } + recomputeTargets() + + def getCleanCacheResult: CleanCacheResult = { + var msg = "" + var cleaned = true + for (targetId <- cleanCacheParams.getTargets.asScala) { + val module = targetIdToModule(targetId) + val mainModule = new MainModule { + override implicit def millDiscover: Discover[_] = { + Discover[this.type] } - val cleanTask = mainModule.clean(millEvaluator, List(s"${module.millModuleSegments.render}.compile"):_*) - val cleanResult = millEvaluator.evaluate( - Strict.Agg(cleanTask), - logger = new MillBspLogger(client, cleanTask.hashCode, millEvaluator.log) + } + val cleanTask = mainModule.clean(millEvaluator, List(s"${module.millModuleSegments.render}.compile"): _*) + val cleanResult = millEvaluator.evaluate( + Strict.Agg(cleanTask), + logger = new MillBspLogger(client, cleanTask.hashCode, millEvaluator.log) ) - if (cleanResult.failing.keyCount > 0) { - cleaned = false - msg += s" Target ${module.millModuleSegments.render} could not be cleaned. See message from mill: \n" - cleanResult.results(cleanTask) match { - case fail: Result.Failure[Any] => msg += fail.msg + "\n" - case _ => msg += "could not retrieve message" - } - } else { - msg += s"${module.millModuleSegments.render} cleaned \n" + if (cleanResult.failing.keyCount > 0) { + cleaned = false + msg += s" Target ${module.millModuleSegments.render} could not be cleaned. See message from mill: \n" + cleanResult.results(cleanTask) match { + case fail: Result.Failure[Any] => msg += fail.msg + "\n" + case _ => msg += "could not retrieve message" + } + } else { + msg += s"${module.millModuleSegments.render} cleaned \n" - val outDir = Evaluator.resolveDestPaths(os.pwd / "out", module.millModuleSegments ++ - Seq(Label("compile"))).out - while (os.exists(outDir)) { - Thread.sleep(10) - } + val outDir = Evaluator.resolveDestPaths(os.pwd / "out", module.millModuleSegments ++ + Seq(Label("compile"))).out + while (os.exists(outDir)) { + Thread.sleep(10) } } - new CleanCacheResult(msg, cleaned) } - handleExceptions[String, CleanCacheResult](_ => getCleanCacheResult, "") + new CleanCacheResult(msg, cleaned) } + handleExceptions[String, CleanCacheResult](_ => getCleanCacheResult, "") + } + override def buildTargetScalacOptions(scalacOptionsParams: ScalacOptionsParams): - CompletableFuture[ScalacOptionsResult] = { + CompletableFuture[ScalacOptionsResult] = { recomputeTargets() + def getScalacOptionsResult: ScalacOptionsResult = { var targetScalacOptions = List.empty[ScalacOptionsItem] for (targetId <- scalacOptionsParams.getTargets.asScala) { @@ -429,8 +482,8 @@ class MillBuildServer(evaluator: Evaluator, val classpath = evaluateInformativeTask(evaluator, m.runClasspath, Agg.empty[PathRef]). map(pathRef => pathRef.path.toIO.toURI.toString).toList val classDirectory = (Evaluator.resolveDestPaths( - os.pwd / "out" , - m.millModuleSegments ++ Seq(Label("compile"))).dest / "classes" + os.pwd / "out", + m.millModuleSegments ++ Seq(Label("compile"))).dest / "classes" ).toIO.toURI.toString targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) @@ -439,6 +492,7 @@ class MillBuildServer(evaluator: Evaluator, } new ScalacOptionsResult(targetScalacOptions.asJava) } + handleExceptions[String, ScalacOptionsResult](_ => getScalacOptionsResult, "") } @@ -446,8 +500,9 @@ class MillBuildServer(evaluator: Evaluator, // defined for the same module, do something so that those can still be detected // such that IntelliJ can run any of them override def buildTargetScalaMainClasses(scalaMainClassesParams: ScalaMainClassesParams): - CompletableFuture[ScalaMainClassesResult] = { + CompletableFuture[ScalaMainClassesResult] = { recomputeTargets() + def getScalaMainClasses: ScalaMainClassesResult = { var items = List.empty[ScalaMainClassesItem] for (targetId <- scalaMainClassesParams.getTargets.asScala) { @@ -456,10 +511,10 @@ class MillBuildServer(evaluator: Evaluator, case result: Result.Success[Any] => result.asSuccess.get.value match { case mainClass: Right[String, String] => List(new ScalaMainClass( - mainClass.value, - List.empty[String].asJava, - evaluateInformativeTask(evaluator, module.forkArgs, Seq.empty[String]). - toList.asJava)) + mainClass.value, + List.empty[String].asJava, + evaluateInformativeTask(evaluator, module.forkArgs, Seq.empty[String]). + toList.asJava)) case msg: Left[String, String] => val messageParams = new ShowMessageParams(MessageType.WARNING, msg.value) messageParams.setOriginId(scalaMainClassesParams.getOriginId) @@ -468,96 +523,64 @@ class MillBuildServer(evaluator: Evaluator, } case _ => List.empty[ScalaMainClass] } - val item = new ScalaMainClassesItem (targetId , scalaMainClasses.asJava) + val item = new ScalaMainClassesItem(targetId, scalaMainClasses.asJava) items ++= List(item) - } + } new ScalaMainClassesResult(items.asJava) } - handleExceptions[String, ScalaMainClassesResult](_ => getScalaMainClasses, "") - } - // Detect and return the test classes contained in the given TestModule - private[this] def getTestClasses(module: TestModule) (implicit ctx: Ctx.Home): Seq[String] = { - val runClasspath = getTaskResult(millEvaluator, module.runClasspath) - val frameworks = getTaskResult(millEvaluator, module.testFrameworks) - val compilationResult = getTaskResult(millEvaluator, module.compile) - - (runClasspath, frameworks, compilationResult) match { - case (Result.Success(classpath), Result.Success(testFrameworks), Result.Success(compResult)) => - val classFingerprint = Jvm.inprocess(classpath.asInstanceOf[Seq[PathRef]].map(_.path), - classLoaderOverrideSbtTesting = true, - isolated = true, - closeContextClassLoaderWhenDone = false, cl => { - val fs = TestRunner.frameworks(testFrameworks.asInstanceOf[Seq[String]])(cl) - fs.flatMap(framework => - discoverTests(cl, framework, Agg(compResult.asInstanceOf[CompilationResult]. - classes.path))) - }) - classFingerprint.map(classF => classF._1.getName.stripSuffix("$")) - case _ => Seq.empty[String] //TODO: or send notification that something went wrong - } + handleExceptions[String, ScalaMainClassesResult](_ => getScalaMainClasses, "") } override def buildTargetScalaTestClasses(scalaTestClassesParams: ScalaTestClassesParams): - CompletableFuture[ScalaTestClassesResult] = { + CompletableFuture[ScalaTestClassesResult] = { recomputeTargets() - def getScalaTestClasses (implicit ctx: Ctx.Home): ScalaTestClassesResult = { + + def getScalaTestClasses(implicit ctx: Ctx.Home): ScalaTestClassesResult = { var items = List.empty[ScalaTestClassesItem] for (targetId <- scalaTestClassesParams.getTargets.asScala) { targetIdToModule(targetId) match { case module: TestModule => - items ++= List(new ScalaTestClassesItem(targetId, getTestClasses(module).toList.asJava)) + items ++= List(new ScalaTestClassesItem(targetId, getTestClasses(module).toList.asJava)) case _: JavaModule => //TODO: maybe send a notification that this target has no test classes } } new ScalaTestClassesResult(items.asJava) } - handleExceptions[Ctx.Home, ScalaTestClassesResult](c => getScalaTestClasses(c), ctx) - } - - // Given the mapping from modules to targetIds, construct the mapping from targetIds to modules - private[this] def targetToModule(moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier]): - Predef.Map[BuildTargetIdentifier, JavaModule] = { - moduleToTargetId.keys.map(mod => (moduleToTargetId(mod), mod)).toMap + handleExceptions[Ctx.Home, ScalaTestClassesResult](c => getScalaTestClasses(c), ctx) } - // Resolve all the mill modules contained in the project - private[this] def getMillModules(ev: Evaluator): Seq[JavaModule] = { - ev.rootModule.millInternal.segmentsToModules.values. - collect { - case m: scalalib.JavaModule => m - }.toSeq ++ Seq(rootModule) - } + // Detect and return the test classes contained in the given TestModule + private[this] def getTestClasses(module: TestModule)(implicit ctx: Ctx.Home): Seq[String] = { + val runClasspath = getTaskResult(millEvaluator, module.runClasspath) + val frameworks = getTaskResult(millEvaluator, module.testFrameworks) + val compilationResult = getTaskResult(millEvaluator, module.compile) - // Recompute the modules in the project in case any changes to the build took place - // and update all the mappings that depend on this info - private[this] def recomputeTargets(): Unit = { - rootModule = ModuleUtils.getRootJavaModule(millEvaluator.rootModule) - millModules = getMillModules(millEvaluator) - moduleToTargetId = ModuleUtils.getModuleTargetIdMap(millModules, millEvaluator) - targetIdToModule = targetToModule(moduleToTargetId) - moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) + (runClasspath, frameworks, compilationResult) match { + case (Result.Success(classpath), Result.Success(testFrameworks), Result.Success(compResult)) => + val classFingerprint = Jvm.inprocess(classpath.asInstanceOf[Seq[PathRef]].map(_.path), + classLoaderOverrideSbtTesting = true, + isolated = true, + closeContextClassLoaderWhenDone = false, cl => { + val fs = TestRunner.frameworks(testFrameworks.asInstanceOf[Seq[String]])(cl) + fs.flatMap(framework => + discoverTests(cl, framework, Agg(compResult.asInstanceOf[CompilationResult]. + classes.path))) + }) + classFingerprint.map(classF => classF._1.getName.stripSuffix("$")) + case _ => Seq.empty[String] //TODO: or send notification that something went wrong + } } - // Given a function that take input of type T and return output of type V, - // apply the function on the given inputs and return a completable future of - // the result. If the execution of the function raises an Exception, complete - // the future exceptionally. Also complete exceptionally if the server was not - // yet initialized. - private[this] def handleExceptions[T, V](serverMethod: T => V, input: T): CompletableFuture[V] = { - val future = new CompletableFuture[V]() - if (initialized) { - try { - future.complete(serverMethod(input)) - } catch { - case e: Exception => future.completeExceptionally(e) - } - } else { - future.completeExceptionally( - new Exception("Can not respond to any request before receiving the `initialize` request.") - ) - } - future + // construct the ManagedLogger that will go into the compilation problems reporter + private[this] def getCompilationLogger: ManagedLogger = { + val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( + millEvaluator.log.outputStream + )) + val l = LogExchange.logger("Hello") + LogExchange.unbindLoggerAppenders("Hello") + LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Info) :: Nil) + l } } diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 8cccf7b3..1fd0f019 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -1,19 +1,19 @@ package mill.contrib.bsp -import scala.collection.JavaConverters._ import ch.epfl.scala.bsp4j._ import mill.T import mill.api.Result.Success import mill.api.{Loose, Strict} -import mill.define.{BaseModule, Ctx, Segment, Segments, Target, Task} -import mill.eval._ -import mill.eval.Evaluator +import mill.define._ +import mill.eval.{Evaluator, _} import mill.scalajslib.ScalaJSModule import mill.scalalib.api.Util -import mill.scalanativelib._ import mill.scalalib.{JavaModule, ScalaModule, TestModule} +import mill.scalanativelib._ import os.Path +import scala.collection.JavaConverters._ + /** * Utilities for translating the mill build into * BSP information like BuildTargets and BuildTargetIdentifiers @@ -25,24 +25,25 @@ object ModuleUtils { * working directory ( has to be a mill-based project ) and * BSP BuildTargets ( mill modules correspond one-to-one to * bsp build targets ). - * @param modules All JavaModules contained in the working - * directory of the mill project - * @param rootModule The root module ( corresponding to the root - * of the mill project ) - * @param evaluator The mill evaluator that can resolve information - * about the mill project + * + * @param modules All JavaModules contained in the working + * directory of the mill project + * @param rootModule The root module ( corresponding to the root + * of the mill project ) + * @param evaluator The mill evaluator that can resolve information + * about the mill project * @param supportedLanguages the languages supported by the modules * of the mill project * @return JavaModule -> BuildTarget mapping */ def millModulesToBspTargets(modules: Seq[JavaModule], - rootModule: JavaModule, - evaluator: Evaluator, - supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { + rootModule: JavaModule, + evaluator: Evaluator, + supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { val moduleIdMap = getModuleTargetIdMap(modules, evaluator) - (for ( module <- modules ) + (for (module <- modules) yield (module, getTarget(rootModule, module, evaluator, moduleIdMap))).toMap } @@ -62,10 +63,10 @@ object ModuleUtils { * with it. * @return build target for `module` */ - def getTarget( rootModule: JavaModule, - module: JavaModule, - evaluator: Evaluator, - moduleIdMap: Map[JavaModule, BuildTargetIdentifier] + def getTarget(rootModule: JavaModule, + module: JavaModule, + evaluator: Evaluator, + moduleIdMap: Map[JavaModule, BuildTargetIdentifier] ): BuildTarget = { if (module == rootModule) getRootTarget(module, evaluator, moduleIdMap) @@ -79,6 +80,7 @@ object ModuleUtils { * has the same millSourcePath. Set generated sources * according to the location of the compilation * products + * * @param rootBaseModule module for the root * @return root JavaModule */ @@ -87,22 +89,33 @@ object ModuleUtils { new JavaModule { override def millSourcePath: Path = rootBaseModule.millSourcePath - override def sources = T.sources{millSourcePath / "src"} - def out = T.sources{millSourcePath / "out"} - def target = T.sources{millSourcePath / "target"} - override def generatedSources: Target[Seq[PathRef]] = T.sources{ - out() ++ target()} + override def sources = T.sources { + millSourcePath / "src" + } + + def out = T.sources { + millSourcePath / "out" + } + + def target = T.sources { + millSourcePath / "target" + } + + override def generatedSources: Target[Seq[PathRef]] = T.sources { + out() ++ target() + } } } /** * Compute the BuildTarget associated with the root * directory of the mill project being built - * @param rootModule the root JavaModule extracted from - * the build file by a mill evalautor - * @param evaluator mill evaluator that can resolve - * build information + * + * @param rootModule the root JavaModule extracted from + * the build file by a mill evalautor + * @param evaluator mill evaluator that can resolve + * build information * @param moduleIdMap mapping from each mill JavaModule * contained in the working directory and * a BuildTargetIdentifier associated @@ -145,9 +158,9 @@ object ModuleUtils { * @return inner BuildTarget */ def getRegularTarget( - module: JavaModule, - evaluator: Evaluator, - moduleIdMap: Map[JavaModule, BuildTargetIdentifier]): BuildTarget = { + module: JavaModule, + evaluator: Evaluator, + moduleIdMap: Map[JavaModule, BuildTargetIdentifier]): BuildTarget = { val dataBuildTarget = computeBuildTargetData(module, evaluator) val capabilities = getModuleCapabilities(module, evaluator) val buildTargetTag: List[String] = module match { @@ -160,10 +173,10 @@ object ModuleUtils { } val buildTarget = new BuildTarget(moduleIdMap(module), - buildTargetTag.asJava, - List("scala", "java").asJava, - dependencies, - capabilities) + buildTargetTag.asJava, + List("scala", "java").asJava, + dependencies, + capabilities) if (module.isInstanceOf[ScalaModule]) { buildTarget.setDataKind(BuildTargetDataKind.SCALA) } @@ -173,6 +186,59 @@ object ModuleUtils { buildTarget } + /** + * Evaluate the given task using the given mill evaluator and return + * its result of type Result + * + * @param evaluator mill evalautor + * @param task task to evaluate + * @tparam T + */ + def getTaskResult[T](evaluator: Evaluator, task: Task[T]): Result[Any] = { + evaluator.evaluate(Strict.Agg(task)).results(task) + } + + /** + * Evaluate the given task using the given mill evaluator and return + * its result of type T, or the default value of the evaluation failed. + * + * @param evaluator mill evalautor + * @param task task to evaluate + * @param defaultValue default value to return in case of failure + * @tparam T + */ + def evaluateInformativeTask[T](evaluator: Evaluator, task: Task[T], defaultValue: T): T = { + val evaluated = evaluator.evaluate(Strict.Agg(task)).results(task) + evaluated match { + case Success(value) => evaluated.asSuccess.get.value.asInstanceOf[T] + case default => defaultValue + } + } + + /** + * Compute mapping between a mill JavaModule and the BuildTargetIdentifier + * associated with its corresponding bsp BuildTarget. + * + * @param modules mill modules inside the project ( including root ) + * @param evaluator mill evalautor to resolve build information + * @return JavaModule -> BuildTargetIdentifier mapping + */ + def getModuleTargetIdMap(modules: Seq[JavaModule], evaluator: Evaluator): Predef.Map[JavaModule, BuildTargetIdentifier] = { + + (for (module <- modules) + yield (module, new BuildTargetIdentifier( + (module.millOuterCtx.millSourcePath / os.RelPath(moduleName(module.millModuleSegments))). + toIO.toURI.toString))).toMap + } + + // this is taken from mill.scalalib GenIdeaImpl + def moduleName(p: Segments) = p.value.foldLeft(StringBuilder.newBuilder) { + case (sb, Segment.Label(s)) if sb.isEmpty => sb.append(s) + case (sb, Segment.Cross(s)) if sb.isEmpty => sb.append(s.mkString("-")) + case (sb, Segment.Label(s)) => sb.append(".").append(s) + case (sb, Segment.Cross(s)) => sb.append("-").append(s.mkString("-")) + }.mkString.toLowerCase() + // obtain the capabilities of the given module ( ex: canCompile, canRun, canTest ) private[this] def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { val canTest = module match { @@ -209,33 +275,6 @@ object ModuleUtils { } } - /** - * Evaluate the given task using the given mill evaluator and return - * its result of type Result - * @param evaluator mill evalautor - * @param task task to evaluate - * @tparam T - */ - def getTaskResult[T](evaluator: Evaluator, task: Task[T]): Result[Any] = { - evaluator.evaluate(Strict.Agg(task)).results(task) - } - - /** - * Evaluate the given task using the given mill evaluator and return - * its result of type T, or the default value of the evaluation failed. - * @param evaluator mill evalautor - * @param task task to evaluate - * @param defaultValue default value to return in case of failure - * @tparam T - */ - def evaluateInformativeTask[T](evaluator: Evaluator, task: Task[T], defaultValue: T): T = { - val evaluated = evaluator.evaluate(Strict.Agg(task)).results(task) - evaluated match { - case Success(value) => evaluated.asSuccess.get.value.asInstanceOf[T] - case default => defaultValue - } - } - // Compute all relevant scala dependencies of `module`, like scala-library, scala-compiler, // and scala-reflect private[this] def computeScalaLangDependencies(module: ScalaModule, evaluator: Evaluator): Loose.Agg[PathRef] = { @@ -255,27 +294,4 @@ object ModuleUtils { case m: ScalaModule => ScalaPlatform.JVM } } - - /** - * Compute mapping between a mill JavaModule and the BuildTargetIdentifier - * associated with its corresponding bsp BuildTarget. - * @param modules mill modules inside the project ( including root ) - * @param evaluator mill evalautor to resolve build information - * @return JavaModule -> BuildTargetIdentifier mapping - */ - def getModuleTargetIdMap(modules: Seq[JavaModule], evaluator:Evaluator): Predef.Map[JavaModule, BuildTargetIdentifier] = { - - (for ( module <- modules ) - yield (module, new BuildTargetIdentifier( - (module.millOuterCtx.millSourcePath / os.RelPath(moduleName(module.millModuleSegments))). - toIO.toURI.toString))).toMap - } - - // this is taken from mill.scalalib GenIdeaImpl - def moduleName(p: Segments) = p.value.foldLeft(StringBuilder.newBuilder) { - case (sb, Segment.Label(s)) if sb.isEmpty => sb.append(s) - case (sb, Segment.Cross(s)) if sb.isEmpty => sb.append(s.mkString("-")) - case (sb, Segment.Label(s)) => sb.append(".").append(s) - case (sb, Segment.Cross(s)) => sb.append("-").append(s.mkString("-")) - }.mkString.toLowerCase() } diff --git a/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala b/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala index 29a07f17..a235c922 100644 --- a/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala +++ b/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala @@ -1,8 +1,9 @@ package mill.contrib.bsp -import scala.collection.JavaConverters._ import ch.epfl.scala.bsp4j.{BuildTargetIdentifier, CompileParams, RunParams, TestParams} +import scala.collection.JavaConverters._ + /** * Common trait to represent BSP request parameters that @@ -27,7 +28,7 @@ case class CParams(compileParams: CompileParams) extends Parameters { override def getArguments: Option[Seq[String]] = { try { Option(compileParams.getArguments.asScala) - }catch { + } catch { case e: Exception => Option.empty[Seq[String]] } } @@ -35,12 +36,13 @@ case class CParams(compileParams: CompileParams) extends Parameters { override def getOriginId: Option[String] = { try { Option(compileParams.getOriginId) - }catch { + } catch { case e: Exception => Option.empty[String] } } } + case class RParams(runParams: RunParams) extends Parameters { override def getTargets: List[BuildTargetIdentifier] = { @@ -50,7 +52,7 @@ case class RParams(runParams: RunParams) extends Parameters { override def getArguments: Option[Seq[String]] = { try { Option(runParams.getArguments.asScala) - }catch { + } catch { case e: Exception => Option.empty[Seq[String]] } } @@ -58,12 +60,13 @@ case class RParams(runParams: RunParams) extends Parameters { override def getOriginId: Option[String] = { try { Option(runParams.getOriginId) - }catch { + } catch { case e: Exception => Option.empty[String] } } } + case class TParams(testParams: TestParams) extends Parameters { override def getTargets: List[BuildTargetIdentifier] = { @@ -73,7 +76,7 @@ case class TParams(testParams: TestParams) extends Parameters { override def getArguments: Option[Seq[String]] = { try { Option(testParams.getArguments.asScala) - }catch { + } catch { case e: Exception => Option.empty[Seq[String]] } } @@ -81,7 +84,7 @@ case class TParams(testParams: TestParams) extends Parameters { override def getOriginId: Option[String] = { try { Option(testParams.getOriginId) - }catch { + } catch { case e: Exception => Option.empty[String] } } @@ -92,6 +95,7 @@ object TaskParameters { /** * Convert parameters specific to the compile request * to the common trait Parameters. + * * @param compileParams compile request parameters * @return general task parameters containing compilation info */ @@ -102,6 +106,7 @@ object TaskParameters { /** * Convert parameters specific to the run request * to the common trait Parameters. + * * @param runParams run request parameters * @return general task parameters containing running info */ @@ -112,6 +117,7 @@ object TaskParameters { /** * Convert parameters specific to the test request * to the common trait Parameters. + * * @param testParams compile request parameters * @return general task parameters containing testing info */ diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 96da84eb..e86ad7f9 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -55,13 +55,13 @@ object Ctx { class Ctx( - val args: IndexedSeq[_], - dest0: () => os.Path, - val log: Logger, - val home: os.Path, - val env: Map[String, String], - val reporter: Int => Option[BuildProblemReporter], - val testReporter: TestReporter + val args: IndexedSeq[_], + dest0: () => os.Path, + val log: Logger, + val home: os.Path, + val env: Map[String, String], + val reporter: Int => Option[BuildProblemReporter], + val testReporter: TestReporter ) extends Ctx.Dest with Ctx.Log -- cgit v1.2.3