From 91bfa49804673bc337be002a537cb5d41b0c74ce Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Fri, 21 Jun 2019 18:39:34 +0200 Subject: Started integrating mill with Build Server Protocol --- .gitignore | 3 + contrib/bsp/.bsp/mill-bsp.json | 1 + .../bsp/src/mill/contrib/MainMillBuildServer.scala | 216 +++++++++++++++ .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 303 +++++++++++++++++++++ contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 129 +++++++++ 5 files changed, 652 insertions(+) create mode 100644 contrib/bsp/.bsp/mill-bsp.json create mode 100644 contrib/bsp/src/mill/contrib/MainMillBuildServer.scala create mode 100644 contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala create mode 100644 contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala diff --git a/.gitignore b/.gitignore index d79d325d..53cea62c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ output/ out/ /.bloop/ /.metals/ +contrib/bsp/mill-external-bs +contrib/bsp/mill-out-bs +mill.iml \ No newline at end of file diff --git a/contrib/bsp/.bsp/mill-bsp.json b/contrib/bsp/.bsp/mill-bsp.json new file mode 100644 index 00000000..880e7454 --- /dev/null +++ b/contrib/bsp/.bsp/mill-bsp.json @@ -0,0 +1 @@ +{"name":"mill-bsp","argv":["java","-DMILL_CLASSPATH=/usr/local/bin/mill","-DMILL_VERSION=0.4.0","-Djna.nosys=true","-cp","/usr/local/bin/mill","mill.MillMain mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala new file mode 100644 index 00000000..6a1594ba --- /dev/null +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -0,0 +1,216 @@ +package mill.contrib + +import java.io.{BufferedReader, File, InputStreamReader} + +import play.api.libs.json._ +import java.nio.file.FileAlreadyExistsException +import java.util.concurrent.{CompletableFuture, Executors, Future} + +import upickle.default._ +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient} +import mill._ +import mill.contrib.bsp.ModuleUtils +import mill.define.{Discover, ExternalModule, Target, Task} +import mill.eval.Evaluator +import mill.scalalib._ +import mill.util.DummyLogger +import org.eclipse.lsp4j.jsonrpc.{CompletableFutures, Launcher} +import requests.Compress.None +import upickle.default + +import scala.collection.JavaConverters._ + + +object MainMillBuildServer extends ExternalModule { + + implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() + + lazy val millDiscover: Discover[MainMillBuildServer.this.type] = Discover[this.type] + val version = "1.0.0" + val bspVersion = "2.0.0-M4" + val languages = List("scala", "java") + + // returns the mill installation path in the user's system + def whichMill(): String = { + import sys.process._ + "which mill"!! + } + + // 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 = whichMill().replaceAll("\n", "") + Json.toJson(new BspConnectionDetails("mill-bsp", + List("java","-DMILL_CLASSPATH=" + millPath, + "-DMILL_VERSION=0.4.0", "-Djna.nosys=true", "-cp", + millPath, + "mill.MillMain mill.contrib.MainMillBuildServer/startServer").asJava, + version, + bspVersion, + languages.asJava)) + } + + /** + * Installs the mill-bsp server. It creates a json file + * with connection details in the ./.bsp directory for + * a potential client to find. + * + * If a .bsp folder with a connection file already + * exists in the working directory, it will be + * overwritten and a corresponding message will be displayed + * in stdout. + * + * If the creation of the .bsp folder fails due to any other + * reason, the message and stacktrace of the exception will be + * printed to stdout. + * + */ + def installMillBsp(): Unit = { + val bspDirecotry = os.pwd / "contrib" / "bsp" / ".bsp" + + try { + os.makeDir(bspDirecotry) + os.write(bspDirecotry / "mill-bsp.json", Json.stringify(createBspConnectionJson())) + } catch { + case e: FileAlreadyExistsException => { + println("The bsp connection json file probably exists already - will be overwritten") + os.remove.all(bspDirecotry) + installMillBsp() + } + //TODO: Do I want to catch this or throw the exception? + case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + + " " + e.getStackTrace.toString) + } + + } + + /** + * Computes a mill task for resolving all JavaModules + * defined in the build.sc file of the project to build. + * This file should be in the working directory of the client. + * @param ev: Environment, used by mill to evaluate tasks + * @return: mill.Task which evaluates to a sequence of all + * the JavaModules defined for a project + */ + def modules(ev: Evaluator): Task[Seq[JavaModule]] = T.task{ + ev.rootModule.millInternal.segmentsToModules.values. + collect { + case m: scalalib.JavaModule => m + }.toSeq + } + + /** + * 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 + */ + def startServer(ev: Evaluator) = T.command { + + val millServer = new mill.contrib.bsp.MillBuildServer(modules(ev)(), ev, bspVersion, version, languages) + val executor = Executors.newCachedThreadPool() + + val stdin = System.in + val stdout = System.out + try { + val launcher = new Launcher.Builder[BuildClient]() + .setOutput(stdout) + .setInput(stdin) + .setLocalService(millServer) + .setRemoteInterface(classOf[BuildClient]) + .setExecutorService(executor) + .create() + millServer.onConnectWithClient(launcher.getRemoteProxy) + val listening = launcher.startListening() + millServer.cancelator = () => listening.cancel(true) + val voidFuture = listening.get() + } catch { + case e: Exception => + System.err.println("An exception occured while connecting to the client.") + System.err.println(e.getMessage) + e.printStackTrace() + } finally { + System.err.println("Shutting down executor") + executor.shutdown() + } + } + + def experiment: Unit = { + val index = foo.bar.test.millModuleSegments.parts.length + println((os.pwd / "out" / foo.bar.test.millModuleSegments.parts + / "compile" / "dest").toNIO.toAbsolutePath.toUri.toString ) + println(foo.bar.test.millModuleSegments.parts) + println(foo.bar.test.millSourcePath) + println(foo.bar.millOuterCtx.fileName) + } + + /** + * Allows minimal testing and installing the build server + * from the command line. + * @param args: can be - exp: executes code in the experiment + * method, helps with testing the server + * - install: installs the mill-bsp server in the + * working directory. + */ + def main(args: Array[String]) { + args(0) match { + case "exp" => experiment + case "install" => installMillBsp() //TODO: Do I want to make this a mill command instead? + case e: String => println("Wrong command, you can only use:\n " + + "install - creates the bsp connection json file\n") + } + + } +} + +object foo extends mill.define.ExternalModule { + + implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() + lazy val millDiscover: Discover[foo.this.type] = Discover[this.type] + + object bar extends ScalaModule { + def scalaVersion = "2.12.4" + override def ivyDeps = Agg(ivy"org.scalameta::metals:0.5.2") + override def moduleDeps = Seq(baz) + object test extends TestModule { + override def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.6.0") + def testFrameworks: Target[Seq[String]] = Seq("utest.runner.Framework") + } + } + + object baz extends scalajslib.ScalaJSModule { + def scalaJSVersion = "1.0.0-M8" + def scalaVersion = "2.12.8" + + } + + object mill_exercise extends ScalaModule { + def scalaVersion = "2.12.8" + + override def scalacPluginIvyDeps = Agg(ivy"org.scala-lang:scala-compiler:2.12.8") + override def ivyDeps = Agg( + ivy"org.scala-lang:scala-reflect:2.12.8", + ivy"org.scalameta::metals:0.5.2" + ) + + object test extends Tests { + override def ivyDeps = Agg(//ivy"org.scalameta::metals:0.5.2", + ivy"com.lihaoyi::utest:0.6.0", + ivy"org.scalactic::scalactic:3.0.5") + + def testFrameworks: Target[Seq[String]] = Seq("utest.runner.Framework") + } + } +} \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala new file mode 100644 index 00000000..4ae68f15 --- /dev/null +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -0,0 +1,303 @@ +package mill.contrib.bsp + +import java.util.{Calendar, Collections} +import java.util.concurrent.CompletableFuture + +import ch.epfl.scala.bsp4j._ +import mill._ +import mill.api.Strict +import mill.contrib.bsp.ModuleUtils._ +import mill.eval.Evaluator +import mill.scalalib._ +import mill.scalalib.api.CompilationResult + +import scala.collection.mutable.Map +import scala.collection.JavaConverters._ + + +class MillBuildServer(modules: Seq[JavaModule], + evaluator: Evaluator, + _bspVersion: String, + serverVersion:String, + languages: List[String]) extends BuildServer with ScalaBuildServer { + + val bspVersion: String = _bspVersion + val supportedLanguages: List[String] = languages + val millServerVersion: String = serverVersion + var cancelator: () => Unit = () => () + + var millModules: Seq[JavaModule] = modules + var client: BuildClient = _ + var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap(millModules) + var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) + var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = + ModuleUtils.millModulesToBspTargets(millModules, List("scala", "java")) + + var millEvaluator: Evaluator = evaluator + var clientInitialized = false + + override def onConnectWithClient(server: BuildClient): Unit = + client = server + + override def buildInitialize(params: InitializeBuildParams): CompletableFuture[InitializeBuildResult] = { + + val capabilities = new BuildServerCapabilities + capabilities.setCompileProvider(new CompileProvider(List("java", "scala").asJava)) + capabilities.setRunProvider(new RunProvider(List("java", "scala").asJava)) + capabilities.setTestProvider(new TestProvider(List("java", "scala").asJava)) + capabilities.setDependencySourcesProvider(true) + capabilities.setInverseSourcesProvider(true) + capabilities.setResourcesProvider(true) + capabilities.setBuildTargetChangedProvider(false) //TODO: for now it's false, but will try to support this later + val future = new CompletableFuture[InitializeBuildResult]() + future.complete(new InitializeBuildResult("mill-bsp", millServerVersion, bspVersion, capabilities)) + future + } + + override def onBuildInitialized(): Unit = { + clientInitialized = true + } + + override def buildShutdown(): CompletableFuture[Object] = { + clientInitialized match { + case true => val future = new CompletableFuture[AnyRef]() + future.complete("shut down this server") + future + case false => throw new Error("Can not send any other request before the initialize request") + } + + } + + override def onBuildExit(): Unit = { + cancelator() + } + + override def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = { + val future = new CompletableFuture[WorkspaceBuildTargetsResult]() + val result = new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava) + future.complete(result) + future + } + + private[this] def getSourceFiles(sources: Seq[os.Path]): Iterable[os.Path] = { + var files = Seq.empty[os.Path] + + for (source <- sources) { + if (os.exists(source)) (if (os.isDir(source)) os.walk(source) else Seq(source)) + .foreach(path => if (os.isFile(path) && List("scala", "java").contains(path.ext) && + !path.last.startsWith(".")) { + files ++= Seq(path) + }) + } + + files + } + + override def buildTargetSources(sourcesParams: SourcesParams): CompletableFuture[SourcesResult] = { + + def computeSourcesResult: SourcesResult = { + var items = List[SourcesItem]() + + for (targetId <- sourcesParams.getTargets.asScala) { + var itemSources = List[SourceItem]() + + val sources = evaluateInformativeTask(targetIdToModule(targetId).sources).left.get.map(pathRef => pathRef.path) + val generatedSources = evaluateInformativeTask(targetIdToModule(targetId).generatedSources).left.get + .map(pathRef => pathRef.path) + + for (file <- getSourceFiles(sources)) { + itemSources ++= List(new SourceItem(file.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.FILE, false)) + } + + for (file <- getSourceFiles(generatedSources)) { + itemSources ++= List(new SourceItem(file.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.FILE, true)) + } + + items ++= List(new SourcesItem(targetId, itemSources.asJava)) + } + + new SourcesResult(items.asJava) + } + + val future = new CompletableFuture[SourcesResult]() + future.complete(computeSourcesResult) + future + } + + override def buildTargetInverseSources(inverseSourcesParams: InverseSourcesParams): + CompletableFuture[InverseSourcesResult] = { + + def getInverseSourcesResult: InverseSourcesResult = { + val textDocument = inverseSourcesParams.getTextDocument + + val targets = (for (targetId <- targetIdToModule.keys + if buildTargetSources(new SourcesParams(Collections.singletonList(targetId))). + get.getItems.asScala.head.getSources.asScala. + exists(item => item.getUri.equals(textDocument.getUri))) + yield targetId).toList.asJava + new InverseSourcesResult(targets) + } + + val future = new CompletableFuture[InverseSourcesResult]() + future.complete(getInverseSourcesResult) + future + } + + override def buildTargetDependencySources(dependencySourcesParams: DependencySourcesParams): + CompletableFuture[DependencySourcesResult] = { + def getDependencySources: DependencySourcesResult = { + var items = List[DependencySourcesItem]() + + for (targetId <- dependencySourcesParams.getTargets.asScala) { + val millModule = targetIdToModule(targetId) + var sources = evaluateInformativeTask(millModule.resolveDeps(millModule.transitiveIvyDeps)). + left.get ++ + evaluateInformativeTask(millModule.resolveDeps(millModule.compileIvyDeps)). + left.get + millModule match { + case m: ScalaModule => sources ++= evaluateInformativeTask( + millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps)).left.get + case m: JavaModule => sources ++= List() + } + items ++= List(new DependencySourcesItem(targetId, sources. + map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString). + toList.asJava)) + } + + new DependencySourcesResult(items.asJava) + } + + val future = new CompletableFuture[DependencySourcesResult]() + future.complete(getDependencySources) + future + } + + override def buildTargetResources(resourcesParams: ResourcesParams): CompletableFuture[ResourcesResult] = { + + def getResources: ResourcesResult = { + var items = List[ResourcesItem]() + + for (targetId <- resourcesParams.getTargets.asScala) { + val millModule = targetIdToModule(targetId) + val resources = evaluateInformativeTask(millModule.resources).left.get. + flatMap(pathRef => os.walk(pathRef.path)). + map(path => path.toNIO.toAbsolutePath.toUri.toString). + toList.asJava + items ++= List(new ResourcesItem(targetId, resources)) + } + + new ResourcesResult(items.asJava) + } + + val future = new CompletableFuture[ResourcesResult]() + future.complete(getResources) + future + } + + //TODO: send task notifications - start, progress and finish + //TODO: if the client wants to give compilation arguments and the module + // already has some from the build file, what to do? + //TODO: Send notification if compilation fails + override def buildTargetCompile(compileParams: CompileParams): CompletableFuture[CompileResult] = { + + def getCompileResult: CompileResult = { + + var numFailures = 0 + var compileTime = 0 + for (targetId <- compileParams.getTargets.asScala) { + if (moduleToTarget(targetIdToModule(targetId)).getCapabilities.getCanCompile) { + var millModule = targetIdToModule(targetId) + val compileTask = millModule.compile + // send notification to client that compilation of this target started + val taskStartParams = new TaskStartParams(new TaskId(compileTask.hashCode().toString)) + taskStartParams.setEventTime(System.currentTimeMillis()) + taskStartParams.setMessage("Compiling target: " + targetId) + taskStartParams.setDataKind("compile-task") + taskStartParams.setData(new CompileTask(targetId)) + client.onBuildTaskStart(taskStartParams) + + val result = millEvaluator.evaluate(Strict.Agg(compileTask)) + val endTime = System.currentTimeMillis() + compileTime += result.timings.map(timingTuple => timingTuple._2).sum + var statusCode = StatusCode.OK + + if (result.failing.keyCount > 0) { + statusCode = StatusCode.ERROR + numFailures += result.failing.keyCount + } + + // send notification to client that compilation of this target ended => compilation report + val taskFinishParams = new TaskFinishParams(new TaskId(compileTask.hashCode().toString), statusCode) + taskFinishParams.setEventTime(endTime) + taskFinishParams.setMessage("Finished compiling target: " + + moduleToTarget(targetIdToModule(targetId)).getDisplayName) + taskFinishParams.setDataKind("compile-report") + val compileReport = new CompileReport(targetId, numFailures, 0) + compileReport.setOriginId(compileParams.getOriginId) + compileReport.setTime(compileTime) + taskFinishParams.setData(compileReport) + client.onBuildTaskFinish(taskFinishParams) + } + } + + var overallStatusCode = StatusCode.OK + if (numFailures > 0) { + overallStatusCode = StatusCode.ERROR + } + val compileResult = new CompileResult(overallStatusCode) + compileResult.setOriginId(compileParams.getOriginId) + compileResult //TODO: See what form IntelliJ expects data about products of compilation in order to set data field + } + + val future = new CompletableFuture[CompileResult]() + future.complete(getCompileResult) + future + } + + override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = ??? + + override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = ??? + + override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = ??? + + override def buildTargetScalacOptions(scalacOptionsParams: ScalacOptionsParams): + CompletableFuture[ScalacOptionsResult] = { + def getScalacOptionsResult: ScalacOptionsResult = { + var targetScalacOptions = List.empty[ScalacOptionsItem] + for (targetId <- scalacOptionsParams.getTargets.asScala) { + val module = targetIdToModule(targetId) + module match { + case m: ScalaModule => + val options = evaluateInformativeTask(m.scalacOptions).left.get.toList + val classpath = evaluateInformativeTask(m.compileClasspath).left.get. + map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString).toList + val index = m.millModuleSegments.parts.length + + val classDirectory = m.millOuterCtx.fileName//.toNIO.toAbsolutePath.toUri.toString + + targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) + case m: JavaModule => targetScalacOptions ++= List() + } + + } + new ScalacOptionsResult(targetScalacOptions.asJava) + } + + val future = new CompletableFuture[ScalacOptionsResult]() + future.complete(getScalacOptionsResult) + future + } + + override def buildTargetScalaMainClasses(scalaMainClassesParams: ScalaMainClassesParams): + CompletableFuture[ScalaMainClassesResult] = ??? + + override def buildTargetScalaTestClasses(scalaTestClassesParams: ScalaTestClassesParams): + CompletableFuture[ScalaTestClassesResult] = ??? + + + private[this] def targetToModule(moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier]): + Predef.Map[BuildTargetIdentifier, JavaModule] = { + moduleToTargetId.keys.map(mod => (moduleToTargetId(mod), mod)).toMap + + } +} diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala new file mode 100644 index 00000000..750d070e --- /dev/null +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -0,0 +1,129 @@ +package mill.contrib.bsp + +import java.util.Collections +import scala.collection.JavaConverters._ +import ch.epfl.scala.bsp4j._ +import mill.api.{Loose, Strict} +import mill.define.{Discover, Task} +import mill.eval +import mill.eval.Evaluator +import mill.scalajslib.ScalaJSModule +import mill.scalalib.api.Util +import mill.scalanativelib._ +import mill.scalalib.{JavaModule, ScalaModule, TestModule} +import mill.util.DummyLogger + +object ModuleUtils { + + object dummyModule extends mill.define.ExternalModule { + lazy val millDiscover: Discover[dummyModule.this.type] = Discover[this.type] + } + + val dummyEvalautor: Evaluator = new Evaluator(os.pwd / "contrib" / "bsp" / "mill-bs", + os.pwd / "contrib" / "bsp" / "mill-out-bs", + os.pwd / "contrib" / "bsp" / "mill-external-bs", + dummyModule, DummyLogger) + + def millModulesToBspTargets(modules: Seq[JavaModule], + supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { + + val moduleIdMap = getModuleTargetIdMap(modules) + var moduleToTarget = Predef.Map[JavaModule, BuildTarget]() + + for ( module <- modules ) { + val dataBuildTarget = computeScalaBuildTarget(module) + + val capabilities = getModuleCapabilities(module) + val buildTargetTag: String = module match { + case m: TestModule => BuildTargetTag.TEST + case m: JavaModule => "-" + } + + val dependencies = module match { + case m: JavaModule => m.moduleDeps.map(dep => moduleIdMap(dep)).toList.asJava + } + + val buildTarget = new BuildTarget(moduleIdMap(module), + Collections.singletonList(buildTargetTag), + supportedLanguages.asJava, + dependencies, + capabilities) + buildTarget.setData(dataBuildTarget) + buildTarget.setDisplayName(module.millModuleSegments.last.value.toList.head.pathSegments.head) + buildTarget.setBaseDirectory(module.millOuterCtx.fileName) + moduleToTarget ++= Map(module -> buildTarget) + + } + + moduleToTarget + } + + def getModuleCapabilities(module: JavaModule): BuildTargetCapabilities = { + val canRun = evaluateInformativeTask(module.finalMainClass, success = false) match { + case result: Left[String, eval.Result[String]] => true + case result: Right[String, eval.Result[String]] => false + } + val canTest = module match { + case module: TestModule => true + case module: JavaModule => false + } + + new BuildTargetCapabilities(true, canRun, canTest) + } + + //TODO: I think here I need to look at scalaLibraryIvyDeps, ivyDeps that contain + // "scala-compiler" and "scala-reflect" and at scalacPluginIvyDeps + def computeScalaBuildTarget(module: JavaModule): Any = { + module match { + case m: ScalaModule => + val scalaVersion = evaluateInformativeTask(m.scalaVersion).left.get + new ScalaBuildTarget( + evaluateInformativeTask(m.scalaOrganization).left.get, + scalaVersion, + Util.scalaBinaryVersion(scalaVersion), + getScalaTargetPlatform(m), + computeScalaLangDependencies(m). + map(pathRef => pathRef.path.toNIO.toAbsolutePath.toString). + toList.asJava) + + case m: JavaModule => "This is just a test or java target" + } + } + + def evaluateInformativeTask[T](task: Task[T], success: Boolean = true): Either[T, eval.Result[Any]] = { + if (success) { + Left(dummyEvalautor.evaluate(Strict.Agg(task)).results(task).asSuccess.get.value.asInstanceOf[T]) + } else { + Right(dummyEvalautor.evaluate(Strict.Agg(task)).results(task)) + } + + } + + def computeScalaLangDependencies(module: ScalaModule): Loose.Agg[eval.PathRef] = { + evaluateInformativeTask(module.scalacPluginClasspath).left.get ++ + evaluateInformativeTask(module.resolveDeps(module.ivyDeps)).left.get. + filter(pathRef => pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-compiler") || + pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-reflect") || + pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-library")) + } + + def getScalaTargetPlatform(module: ScalaModule): ScalaPlatform = { + module match { + case m: ScalaNativeModule => ScalaPlatform.NATIVE + case m: ScalaJSModule => ScalaPlatform.JS + case m: ScalaModule => ScalaPlatform.JVM + } + } + + def getModuleTargetIdMap(modules: Seq[JavaModule]): Predef.Map[JavaModule, BuildTargetIdentifier] = { + var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]() + + for ( module <- modules ) { + moduleToTarget ++= Map(module -> new BuildTargetIdentifier( + module.millSourcePath.toNIO.toAbsolutePath.toUri.toString + )) + } + + moduleToTarget + } +} -- cgit v1.2.3 From 74567bf7c09863193f9581658f76d14b9338191b Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 25 Jun 2019 11:06:02 +0200 Subject: Implemented MillBuildServer methods for retrieving the scala main classes and scala tets classes in a project. Also fixed bug in computing the target capabilities from modules. Started a simple implementation of buildTargetRun. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 17 +-- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 147 ++++++++++++++++++--- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 42 +++--- 3 files changed, 159 insertions(+), 47 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 6a1594ba..c606f834 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -147,13 +147,14 @@ object MainMillBuildServer extends ExternalModule { } } - def experiment: Unit = { - val index = foo.bar.test.millModuleSegments.parts.length - println((os.pwd / "out" / foo.bar.test.millModuleSegments.parts - / "compile" / "dest").toNIO.toAbsolutePath.toUri.toString ) - println(foo.bar.test.millModuleSegments.parts) - println(foo.bar.test.millSourcePath) - println(foo.bar.millOuterCtx.fileName) + def experiment(ev: Evaluator) = T.command { + val millServer = new mill.contrib.bsp.MillBuildServer(modules(ev)(), ev, bspVersion, version, languages) + val mods: Seq[JavaModule] = modules(ev)() + for (module <- mods) { + System.err.println("Module: " + module + "has capabilities: " + ModuleUtils.getModuleCapabilities(module, ev)) + System.err.println("Base directory: " + module.millOuterCtx.millSourcePath) + System.err.println("MIll source path: " + module.millSourcePath) + } } /** @@ -166,7 +167,7 @@ object MainMillBuildServer extends ExternalModule { */ def main(args: Array[String]) { args(0) match { - case "exp" => experiment + //case "exp" => experiment case "install" => installMillBsp() //TODO: Do I want to make this a mill command instead? case e: String => println("Wrong command, you can only use:\n " + "install - creates the bsp connection json file\n") diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 4ae68f15..bc8efc3e 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -1,8 +1,8 @@ package mill.contrib.bsp - +import sbt.testing._ import java.util.{Calendar, Collections} import java.util.concurrent.CompletableFuture - +import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import mill._ import mill.api.Strict @@ -10,16 +10,23 @@ import mill.contrib.bsp.ModuleUtils._ import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult - +import mill.scalalib.api.ZincWorkerApi import scala.collection.mutable.Map +import mill.api.Loose import scala.collection.JavaConverters._ +import mill.modules.Jvm +import mill.util.{PrintLogger, Ctx} +import mill.define.{Discover, ExternalModule, Target, Task} class MillBuildServer(modules: Seq[JavaModule], evaluator: Evaluator, _bspVersion: String, serverVersion:String, - languages: List[String]) extends BuildServer with ScalaBuildServer { + languages: List[String]) extends ExternalModule with BuildServer with ScalaBuildServer { + + implicit def millScoptEvaluatorReads[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 @@ -31,11 +38,16 @@ class MillBuildServer(modules: Seq[JavaModule], var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap(millModules) var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = - ModuleUtils.millModulesToBspTargets(millModules, List("scala", "java")) + ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) var millEvaluator: Evaluator = evaluator var clientInitialized = false + val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { + val log = mill.util.DummyLogger + val home = os.pwd + } + override def onConnectWithClient(server: BuildClient): Unit = client = server @@ -101,8 +113,8 @@ class MillBuildServer(modules: Seq[JavaModule], for (targetId <- sourcesParams.getTargets.asScala) { var itemSources = List[SourceItem]() - val sources = evaluateInformativeTask(targetIdToModule(targetId).sources).left.get.map(pathRef => pathRef.path) - val generatedSources = evaluateInformativeTask(targetIdToModule(targetId).generatedSources).left.get + val sources = evaluateInformativeTask(evaluator, targetIdToModule(targetId).sources).left.get.map(pathRef => pathRef.path) + val generatedSources = evaluateInformativeTask(evaluator, targetIdToModule(targetId).generatedSources).left.get .map(pathRef => pathRef.path) for (file <- getSourceFiles(sources)) { @@ -150,12 +162,12 @@ class MillBuildServer(modules: Seq[JavaModule], for (targetId <- dependencySourcesParams.getTargets.asScala) { val millModule = targetIdToModule(targetId) - var sources = evaluateInformativeTask(millModule.resolveDeps(millModule.transitiveIvyDeps)). + var sources = evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.transitiveIvyDeps)). left.get ++ - evaluateInformativeTask(millModule.resolveDeps(millModule.compileIvyDeps)). + evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.compileIvyDeps)). left.get millModule match { - case m: ScalaModule => sources ++= evaluateInformativeTask( + case m: ScalaModule => sources ++= evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps)).left.get case m: JavaModule => sources ++= List() } @@ -179,7 +191,7 @@ class MillBuildServer(modules: Seq[JavaModule], for (targetId <- resourcesParams.getTargets.asScala) { val millModule = targetIdToModule(targetId) - val resources = evaluateInformativeTask(millModule.resources).left.get. + val resources = evaluateInformativeTask(evaluator, millModule.resources).left.get. flatMap(pathRef => os.walk(pathRef.path)). map(path => path.toNIO.toAbsolutePath.toUri.toString). toList.asJava @@ -207,6 +219,7 @@ class MillBuildServer(modules: Seq[JavaModule], for (targetId <- compileParams.getTargets.asScala) { if (moduleToTarget(targetIdToModule(targetId)).getCapabilities.getCanCompile) { var millModule = targetIdToModule(targetId) + //millModule.javacOptions = compileParams.getArguments.asScala val compileTask = millModule.compile // send notification to client that compilation of this target started val taskStartParams = new TaskStartParams(new TaskId(compileTask.hashCode().toString)) @@ -254,7 +267,25 @@ class MillBuildServer(modules: Seq[JavaModule], future } - override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = ??? + override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = { + def getRunResult: RunResult = { + val module = targetIdToModule(runParams.getTarget) + val args = runParams.getArguments +// val runResult = runParams.getData() match { +// case d: ScalaMainClass => millEvaluator.evaluate(Strict.Agg(module.runMain(d.getClass, d.getArguments.asScala))) +// case default => millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" ")))) +// } + val runResult = millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" ")))) + if (runResult.failing.keyCount > 0) { + new RunResult(StatusCode.ERROR) + } else { + new RunResult(StatusCode.OK) + } + } + val future = new CompletableFuture[RunResult]() + future.complete(getRunResult) + future + } override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = ??? @@ -268,8 +299,8 @@ class MillBuildServer(modules: Seq[JavaModule], val module = targetIdToModule(targetId) module match { case m: ScalaModule => - val options = evaluateInformativeTask(m.scalacOptions).left.get.toList - val classpath = evaluateInformativeTask(m.compileClasspath).left.get. + val options = evaluateInformativeTask(evaluator, m.scalacOptions).left.get.toList + val classpath = evaluateInformativeTask(evaluator, m.compileClasspath).left.get. map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString).toList val index = m.millModuleSegments.parts.length @@ -278,7 +309,6 @@ class MillBuildServer(modules: Seq[JavaModule], targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) case m: JavaModule => targetScalacOptions ++= List() } - } new ScalacOptionsResult(targetScalacOptions.asJava) } @@ -288,12 +318,93 @@ class MillBuildServer(modules: Seq[JavaModule], future } +// private[this] def getSpecifiedMainClass(module: JavaModule): Either[Any, String] = { +// val mainClass = evaluateInformativeTask(module.finalMainClassOpt).left.get +// mainClass match { +// case main: Left[String, String] => Left(AnyRef) +// case main: Right[String, String] => Right(main.value) +// } +// } + override def buildTargetScalaMainClasses(scalaMainClassesParams: ScalaMainClassesParams): - CompletableFuture[ScalaMainClassesResult] = ??? + CompletableFuture[ScalaMainClassesResult] = { +// def getScalaMainClasses: ScalaMainClassesResult = { +// var items = List.empty[ScalaMainClassesItem] +// for (targetId <- scalaMainClassesParams.getTargets.asScala) { +// val module = targetIdToModule(targetId) +// var mainClasses = List.empty[ScalaMainClass] +// +// val specifiedMainClass = getSpecifiedMainClass(module) +// specifiedMainClass match { +// case main: Left[Any, String] => {} +// case main: Right[Any, String] => mainClasses ++= List(new ScalaMainClass(specifiedMainClass.getOrElse(""), +// evaluateInformativeTask(module.forkArgs).left.get.toList.asJava, +// List.empty[String].asJava)) +// } +// +// +// for (mainClass <- evaluateInformativeTask(module.zincWorker.worker).left.get. +// discoverMainClasses(evaluateInformativeTask(module.compile).left.get). +// filter(main => !main.equals(specifiedMainClass))) { +// mainClasses ++= List(new ScalaMainClass(mainClass, List.empty[String].asJava, List.empty[String].asJava)) +// } +// items ++= List(new ScalaMainClassesItem(targetId, mainClasses.asJava)) +// } +// new ScalaMainClassesResult(items.asJava) +// } + + def getScalaMainClasses: ScalaMainClassesResult = { + var items = List.empty[ScalaMainClassesItem] + for (targetId <- scalaMainClassesParams.getTargets.asScala) { + val module = targetIdToModule(targetId) + val specifiedMainClass = evaluateInformativeTask(evaluator, module.finalMainClassOpt).left.get + specifiedMainClass match { + case main: Left[String, String] => //TODO: Send a notification that main class could not be chosen + case main: Right[String, String] => + val item = new ScalaMainClassesItem(targetId, List(new ScalaMainClass(specifiedMainClass.getOrElse(""), + evaluateInformativeTask(evaluator, module.forkArgs).left.get.toList.asJava, + List.empty[String].asJava)).asJava) + items ++= List(item) + } + } - override def buildTargetScalaTestClasses(scalaTestClassesParams: ScalaTestClassesParams): - CompletableFuture[ScalaTestClassesResult] = ??? + new ScalaMainClassesResult(items.asJava) + } + val future = new CompletableFuture[ScalaMainClassesResult]() + future.complete(getScalaMainClasses) + future + } + + private[this] def getTestFrameworks(module: TestModule) (implicit ctx: Ctx.Home): Seq[String] = { + //val frameworkMap = TestRunner.frameworks(evaluateInformativeTask(module.testFrameworks).left.get) + val classFingerprint = Jvm.inprocess(evaluateInformativeTask(evaluator, module.runClasspath).left.get.map(_.path), + true, + true, + false, cl => { + val fs = TestRunner.frameworks(evaluateInformativeTask(evaluator, module.testFrameworks).left.get)(cl) + fs.flatMap(framework => + discoverTests(cl, framework, Agg(evaluateInformativeTask(evaluator, module.compile).left.get.classes.path))) + }) + classFingerprint.map(classF => classF._1.getName.stripSuffix("$")) + } + override def buildTargetScalaTestClasses(scalaTestClassesParams: ScalaTestClassesParams): + CompletableFuture[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, getTestFrameworks(module).toList.asJava)) + case module: JavaModule => //TODO: maybe send a notification that this target has no test classes + } + } + new ScalaTestClassesResult(items.asJava) + } + val future = new CompletableFuture[ScalaTestClassesResult]() + future.complete(getScalaTestClasses(ctx)) + future + } private[this] def targetToModule(moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier]): Predef.Map[BuildTargetIdentifier, JavaModule] = { diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 750d070e..e89fae3e 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -5,7 +5,7 @@ import scala.collection.JavaConverters._ import ch.epfl.scala.bsp4j._ import mill.api.{Loose, Strict} import mill.define.{Discover, Task} -import mill.eval +import mill.eval._ import mill.eval.Evaluator import mill.scalajslib.ScalaJSModule import mill.scalalib.api.Util @@ -25,15 +25,15 @@ object ModuleUtils { dummyModule, DummyLogger) def millModulesToBspTargets(modules: Seq[JavaModule], + evaluator: Evaluator, supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { val moduleIdMap = getModuleTargetIdMap(modules) var moduleToTarget = Predef.Map[JavaModule, BuildTarget]() for ( module <- modules ) { - val dataBuildTarget = computeScalaBuildTarget(module) - - val capabilities = getModuleCapabilities(module) + val dataBuildTarget = computeScalaBuildTarget(module, evaluator) + val capabilities = getModuleCapabilities(module, evaluator) val buildTargetTag: String = module match { case m: TestModule => BuildTargetTag.TEST case m: JavaModule => "-" @@ -50,7 +50,7 @@ object ModuleUtils { capabilities) buildTarget.setData(dataBuildTarget) buildTarget.setDisplayName(module.millModuleSegments.last.value.toList.head.pathSegments.head) - buildTarget.setBaseDirectory(module.millOuterCtx.fileName) + buildTarget.setBaseDirectory(module.millSourcePath.toNIO.toAbsolutePath.toUri.toString) moduleToTarget ++= Map(module -> buildTarget) } @@ -58,31 +58,31 @@ object ModuleUtils { moduleToTarget } - def getModuleCapabilities(module: JavaModule): BuildTargetCapabilities = { - val canRun = evaluateInformativeTask(module.finalMainClass, success = false) match { - case result: Left[String, eval.Result[String]] => true - case result: Right[String, eval.Result[String]] => false + def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { + val canRun = evaluateInformativeTask(evaluator, module.finalMainClass, success = false).right.get match { + case result: Result.Success[String] => true + case default => false } val canTest = module match { case module: TestModule => true - case module: JavaModule => false + case default => false } - new BuildTargetCapabilities(true, canRun, canTest) + new BuildTargetCapabilities(true, canTest, canRun) } //TODO: I think here I need to look at scalaLibraryIvyDeps, ivyDeps that contain // "scala-compiler" and "scala-reflect" and at scalacPluginIvyDeps - def computeScalaBuildTarget(module: JavaModule): Any = { + def computeScalaBuildTarget(module: JavaModule, evaluator: Evaluator): Any = { module match { case m: ScalaModule => - val scalaVersion = evaluateInformativeTask(m.scalaVersion).left.get + val scalaVersion = evaluateInformativeTask(evaluator, m.scalaVersion).left.get new ScalaBuildTarget( - evaluateInformativeTask(m.scalaOrganization).left.get, + evaluateInformativeTask(evaluator, m.scalaOrganization).left.get, scalaVersion, Util.scalaBinaryVersion(scalaVersion), getScalaTargetPlatform(m), - computeScalaLangDependencies(m). + computeScalaLangDependencies(m, evaluator). map(pathRef => pathRef.path.toNIO.toAbsolutePath.toString). toList.asJava) @@ -90,18 +90,18 @@ object ModuleUtils { } } - def evaluateInformativeTask[T](task: Task[T], success: Boolean = true): Either[T, eval.Result[Any]] = { + def evaluateInformativeTask[T](evaluator: Evaluator, task: Task[T], success: Boolean = true): Either[T, Result[Any]] = { if (success) { - Left(dummyEvalautor.evaluate(Strict.Agg(task)).results(task).asSuccess.get.value.asInstanceOf[T]) + Left(evaluator.evaluate(Strict.Agg(task)).results(task).asSuccess.get.value.asInstanceOf[T]) } else { - Right(dummyEvalautor.evaluate(Strict.Agg(task)).results(task)) + Right(evaluator.evaluate(Strict.Agg(task)).results(task)) } } - def computeScalaLangDependencies(module: ScalaModule): Loose.Agg[eval.PathRef] = { - evaluateInformativeTask(module.scalacPluginClasspath).left.get ++ - evaluateInformativeTask(module.resolveDeps(module.ivyDeps)).left.get. + def computeScalaLangDependencies(module: ScalaModule, evaluator: Evaluator): Loose.Agg[PathRef] = { + evaluateInformativeTask(evaluator, module.scalacPluginClasspath).left.get ++ + evaluateInformativeTask(evaluator, module.resolveDeps(module.ivyDeps)).left.get. filter(pathRef => pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-compiler") || pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-reflect") || pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-library")) -- cgit v1.2.3 From 623f91a83c49564ffef49a2ad0f6b7e3c7b29105 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 25 Jun 2019 14:17:46 +0200 Subject: Changed the mill bsp server installation command to put the .bsp folder in the working directory --- contrib/bsp/src/mill/contrib/MainMillBuildServer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index c606f834..56ee1419 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -75,7 +75,7 @@ object MainMillBuildServer extends ExternalModule { * */ def installMillBsp(): Unit = { - val bspDirecotry = os.pwd / "contrib" / "bsp" / ".bsp" + val bspDirecotry = os.pwd / ".bsp" try { os.makeDir(bspDirecotry) -- cgit v1.2.3 From 2319b109513ae7462d61fae69726546e3dc6eaf6 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 25 Jun 2019 17:29:51 +0200 Subject: Made install from MainMillBuildServer a mill command. Started simple implementation of buildTargetTest. Made sure targets are retrieved again everytime the workspaceBuildTargets request is sent --- .gitignore | 4 +- contrib/bsp/.bsp/mill-bsp.json | 19 ++- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 25 ++-- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 154 +++++++++++++++------ scratch/build.sc | 25 +++- 5 files changed, 172 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 53cea62c..1d1c261d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ out/ /.metals/ contrib/bsp/mill-external-bs contrib/bsp/mill-out-bs -mill.iml \ No newline at end of file +mill.iml +scratch +./bsp \ No newline at end of file diff --git a/contrib/bsp/.bsp/mill-bsp.json b/contrib/bsp/.bsp/mill-bsp.json index 880e7454..01b08e07 100644 --- a/contrib/bsp/.bsp/mill-bsp.json +++ b/contrib/bsp/.bsp/mill-bsp.json @@ -1 +1,18 @@ -{"name":"mill-bsp","argv":["java","-DMILL_CLASSPATH=/usr/local/bin/mill","-DMILL_VERSION=0.4.0","-Djna.nosys=true","-cp","/usr/local/bin/mill","mill.MillMain mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} \ No newline at end of file +{ + "name": "mill-bsp", + "argv": [ + "java", + "-DMILL_CLASSPATH=/usr/local/bin/mill", + "-DMILL_VERSION=0.4.0", + "-Djna.nosys=true", + "-cp", + "/usr/local/bin/mill", + "mill.MillMain mill.contrib.MainMillBuildServer/startServer" + ], + "version": "1.0.0", + "bspVersion": "2.0.0-M4", + "languages": [ + "scala", + "java" + ] +} \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 56ee1419..8905a622 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -10,7 +10,7 @@ import upickle.default._ import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient} import mill._ import mill.contrib.bsp.ModuleUtils -import mill.define.{Discover, ExternalModule, Target, Task} +import mill.define.{Command, Discover, ExternalModule, Target, Task} import mill.eval.Evaluator import mill.scalalib._ import mill.util.DummyLogger @@ -74,17 +74,18 @@ object MainMillBuildServer extends ExternalModule { * printed to stdout. * */ - def installMillBsp(): Unit = { - val bspDirecotry = os.pwd / ".bsp" + def install(ev: Evaluator): Command[Unit] = T.command{ + val bspDirectory = os.pwd / ".bsp" try { - os.makeDir(bspDirecotry) - os.write(bspDirecotry / "mill-bsp.json", Json.stringify(createBspConnectionJson())) + os.makeDir(bspDirectory) + os.write(bspDirectory / "mill-bsp.json", Json.stringify(createBspConnectionJson())) } catch { case e: FileAlreadyExistsException => { println("The bsp connection json file probably exists already - will be overwritten") - os.remove.all(bspDirecotry) - installMillBsp() + os.remove.all(bspDirectory) + install(ev) + () } //TODO: Do I want to catch this or throw the exception? case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + @@ -117,9 +118,9 @@ object MainMillBuildServer extends ExternalModule { * @return: mill.Command which executes the starting of the * server */ - def startServer(ev: Evaluator) = T.command { + def startServer(ev: Evaluator): Command[Unit] = T.command { - val millServer = new mill.contrib.bsp.MillBuildServer(modules(ev)(), ev, bspVersion, version, languages) + val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) val executor = Executors.newCachedThreadPool() val stdin = System.in @@ -147,8 +148,8 @@ object MainMillBuildServer extends ExternalModule { } } - def experiment(ev: Evaluator) = T.command { - val millServer = new mill.contrib.bsp.MillBuildServer(modules(ev)(), ev, bspVersion, version, languages) + def experiment(ev: Evaluator): Command[Unit] = T.command { + val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) val mods: Seq[JavaModule] = modules(ev)() for (module <- mods) { System.err.println("Module: " + module + "has capabilities: " + ModuleUtils.getModuleCapabilities(module, ev)) @@ -167,8 +168,6 @@ object MainMillBuildServer extends ExternalModule { */ def main(args: Array[String]) { args(0) match { - //case "exp" => experiment - case "install" => installMillBsp() //TODO: Do I want to make this a mill command instead? case e: String => println("Wrong command, you can only use:\n " + "install - creates the bsp connection json file\n") } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index bc8efc3e..9865a5a2 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -19,8 +19,7 @@ import mill.util.{PrintLogger, Ctx} import mill.define.{Discover, ExternalModule, Target, Task} -class MillBuildServer(modules: Seq[JavaModule], - evaluator: Evaluator, +class MillBuildServer(evaluator: Evaluator, _bspVersion: String, serverVersion:String, languages: List[String]) extends ExternalModule with BuildServer with ScalaBuildServer { @@ -32,15 +31,15 @@ class MillBuildServer(modules: Seq[JavaModule], val supportedLanguages: List[String] = languages val millServerVersion: String = serverVersion var cancelator: () => Unit = () => () - - var millModules: Seq[JavaModule] = modules + var millEvaluator: Evaluator = evaluator + var millModules: Seq[JavaModule] = getMillModules(millEvaluator) var client: BuildClient = _ var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap(millModules) var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) - var millEvaluator: Evaluator = evaluator + var clientInitialized = false val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { @@ -85,6 +84,7 @@ class MillBuildServer(modules: Seq[JavaModule], } override def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = { + recomputeTargets() val future = new CompletableFuture[WorkspaceBuildTargetsResult]() val result = new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava) future.complete(result) @@ -247,7 +247,7 @@ class MillBuildServer(modules: Seq[JavaModule], taskFinishParams.setDataKind("compile-report") val compileReport = new CompileReport(targetId, numFailures, 0) compileReport.setOriginId(compileParams.getOriginId) - compileReport.setTime(compileTime) + compileReport.setTime(compileTime.toLong) taskFinishParams.setData(compileReport) client.onBuildTaskFinish(taskFinishParams) } @@ -287,7 +287,101 @@ class MillBuildServer(modules: Seq[JavaModule], future } - override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = ??? + private[this] def getTestReport(targetId: BuildTargetIdentifier, results: Seq[TestRunner.Result]): TestReport = { + val testReport = new TestReport(targetId, 0, 0, 0, 0, 0) + testReport.setTime(results.map(r => r.duration).sum) + for (result <- results) { + result.status match { + case "Passed" => testReport.setPassed(testReport.getPassed + 1) + case "Failed" => testReport.setFailed(testReport.getFailed + 1) + case "Ignored" => testReport.setIgnored(testReport.getIgnored + 1) + case "Cancelled" => testReport.setCancelled(testReport.getCancelled + 1) + case "Skipped" => testReport.setSkipped(testReport.getSkipped + 1) + } + } + testReport + } + + private[this] def getStatusCode(results: Seq[TestRunner.Result]): StatusCode = { + if ( results.exists(res => res.status == "Failed") ) { + StatusCode.ERROR + } else if ( results.exists(res => res.status == "Cancelled") ) { + StatusCode.CANCELLED + }else { + StatusCode.OK + } + } + + override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = { + def getTestResult (implicit ctx: Ctx.Log with Ctx.Home ): TestResult = { + + val argsMap = testParams.getData match { + case scalaTestParams: ScalaTestParams => + (for (testItem <- scalaTestParams.getTestClasses.asScala) + yield (testItem.getTarget, testItem.getClasses.asScala.toSeq)).toMap + + case default => (for (targetId <- testParams.getTargets.asScala) yield (targetId, 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.test(argsMap(targetId).mkString(" ")) + val passed = 0; + val ignored = 0; + val skipped = 0; + val failed = 0; + val cancelled = 0 + // send notification to client that testing of this target started + val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) + taskStartParams.setEventTime(System.currentTimeMillis()) + taskStartParams.setMessage("Testing target: " + targetId) + taskStartParams.setDataKind("test-task") + taskStartParams.setData(new TestTask(targetId)) + client.onBuildTaskStart(taskStartParams) + + val (msg, results) = TestRunner.runTests( + TestRunner.frameworks(evaluateInformativeTask(millEvaluator, testModule.testFrameworks).left.get), + evaluateInformativeTask(millEvaluator, testModule.runClasspath).left.get.map(_.path), + Agg(evaluateInformativeTask(millEvaluator, testModule.compile).left.get.classes.path), + argsMap(targetId) + ) + + val endTime = System.currentTimeMillis() + // send notification to client that testing of this target ended => test report + val statusCode = getStatusCode(results) + val taskFinishParams = new TaskFinishParams( + new TaskId(testTask.hashCode().toString), + getStatusCode(results) + ) + taskFinishParams.setEventTime(endTime) + taskFinishParams.setMessage("Finished testing target: " + + moduleToTarget(targetIdToModule(targetId)).getDisplayName) + taskFinishParams.setDataKind("test-report") + taskFinishParams.setData(getTestReport(targetId, results)) + client.onBuildTaskFinish(taskFinishParams) + + statusCode match { + case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR + case default => + } + case default => + } + } + val testResult = new TestResult(overallStatusCode) + testParams.getOriginId match { + case id: String => + //TODO: Add the messages from mill to the data field? + testResult.setOriginId(id) + testResult + case default => testResult + } + } + val future = new CompletableFuture[TestResult]() + future.complete(getTestResult(ctx)) + future + } override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = ??? @@ -318,40 +412,8 @@ class MillBuildServer(modules: Seq[JavaModule], future } -// private[this] def getSpecifiedMainClass(module: JavaModule): Either[Any, String] = { -// val mainClass = evaluateInformativeTask(module.finalMainClassOpt).left.get -// mainClass match { -// case main: Left[String, String] => Left(AnyRef) -// case main: Right[String, String] => Right(main.value) -// } -// } - override def buildTargetScalaMainClasses(scalaMainClassesParams: ScalaMainClassesParams): CompletableFuture[ScalaMainClassesResult] = { -// def getScalaMainClasses: ScalaMainClassesResult = { -// var items = List.empty[ScalaMainClassesItem] -// for (targetId <- scalaMainClassesParams.getTargets.asScala) { -// val module = targetIdToModule(targetId) -// var mainClasses = List.empty[ScalaMainClass] -// -// val specifiedMainClass = getSpecifiedMainClass(module) -// specifiedMainClass match { -// case main: Left[Any, String] => {} -// case main: Right[Any, String] => mainClasses ++= List(new ScalaMainClass(specifiedMainClass.getOrElse(""), -// evaluateInformativeTask(module.forkArgs).left.get.toList.asJava, -// List.empty[String].asJava)) -// } -// -// -// for (mainClass <- evaluateInformativeTask(module.zincWorker.worker).left.get. -// discoverMainClasses(evaluateInformativeTask(module.compile).left.get). -// filter(main => !main.equals(specifiedMainClass))) { -// mainClasses ++= List(new ScalaMainClass(mainClass, List.empty[String].asJava, List.empty[String].asJava)) -// } -// items ++= List(new ScalaMainClassesItem(targetId, mainClasses.asJava)) -// } -// new ScalaMainClassesResult(items.asJava) -// } def getScalaMainClasses: ScalaMainClassesResult = { var items = List.empty[ScalaMainClassesItem] @@ -411,4 +473,18 @@ class MillBuildServer(modules: Seq[JavaModule], moduleToTargetId.keys.map(mod => (moduleToTargetId(mod), mod)).toMap } + + private[this] def getMillModules(ev: Evaluator): Seq[JavaModule] = { + ev.rootModule.millInternal.segmentsToModules.values. + collect { + case m: scalalib.JavaModule => m + }.toSeq + } + + private[this] def recomputeTargets(): Unit = { + millModules = getMillModules(millEvaluator) + moduleToTargetId = ModuleUtils.getModuleTargetIdMap(millModules) + targetIdToModule = targetToModule(moduleToTargetId) + moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) + } } diff --git a/scratch/build.sc b/scratch/build.sc index 804e1ca4..8c32d634 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,5 +1,6 @@ import mill.Agg import mill.scalalib._ +import ammonite.ops._ object core extends ScalaModule{ def scalaVersion = "2.12.8" @@ -9,4 +10,26 @@ object core extends ScalaModule{ ) } -def thingy = T{ Seq("hello", "world") } +object foo extends ScalaModule { + def scalaVersion = "2.12.8" + // def scalacPluginIvyDeps = Agg(ivy"org.scala-lang:scala-compiler:${scalaVersion()}") + def ivyDeps = Agg(ivy"org.scala-lang:scala-reflect:${scalaVersion()}", + ivy"org.scala-lang:scala-compiler:${scalaVersion()}") + object foo_test extends Tests { + def moduleDeps = Seq(foo) + def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.6.0") + def testFrameworks = Seq("utest.runner.Framework") + def generatedSources = T { + Seq(PathRef(os.pwd / "foo/test/src/Generated.scala")) + } + } +} + +object bar extends ScalaModule { + def scalaVersion = "2.12.8" + def moduleDeps = Seq(foo) + def mainClass = Some("BarMain") + def compileIvyDeps = Agg(ivy"org.scala-lang:scala-reflect:${scalaVersion()}") + def scalacOptions = Seq("-no-specialization") + def ivyDeps = Agg(ivy"ch.epfl.scala::bloop-config:1.2.5") +} -- cgit v1.2.3 From 5c17cf8cacd66743ae6b695f97541057796edb18 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 26 Jun 2019 18:02:22 +0200 Subject: Fixed bugs in ModuleUtils by converting every java path with toUri and using millSourcesPath. Chnaged mill task evaluation to account for cases when the result of an informative task is actually Failure. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 28 +++- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 179 +++++++++++++-------- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 41 +++-- scratch/build.sc | 51 +++--- 4 files changed, 181 insertions(+), 118 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 8905a622..7f1650d6 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -1,15 +1,16 @@ package mill.contrib -import java.io.{BufferedReader, File, InputStreamReader} +import java.io.{BufferedReader, File, InputStream, InputStreamReader, OutputStream} import play.api.libs.json._ import java.nio.file.FileAlreadyExistsException -import java.util.concurrent.{CompletableFuture, Executors, Future} +import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient} +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, ScalaTestClassesParams} import mill._ -import mill.contrib.bsp.ModuleUtils +import mill.api.Strict +import mill.contrib.bsp.{MillBuildServer, ModuleUtils} import mill.define.{Command, Discover, ExternalModule, Target, Task} import mill.eval.Evaluator import mill.scalalib._ @@ -81,13 +82,12 @@ object MainMillBuildServer extends ExternalModule { os.makeDir(bspDirectory) os.write(bspDirectory / "mill-bsp.json", Json.stringify(createBspConnectionJson())) } catch { - case e: FileAlreadyExistsException => { + case e: FileAlreadyExistsException => println("The bsp connection json file probably exists already - will be overwritten") os.remove.all(bspDirectory) install(ev) () - } - //TODO: Do I want to catch this or throw the exception? + //TODO: Do I want to catch this or throw the exception? case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + " " + e.getStackTrace.toString) } @@ -140,7 +140,9 @@ object MainMillBuildServer extends ExternalModule { } catch { case e: Exception => System.err.println("An exception occured while connecting to the client.") - System.err.println(e.getMessage) + System.err.println("Cause: " + e.getCause) + System.err.println("Message: " + e.getMessage) + System.err.println("Exception class: " + e.getClass) e.printStackTrace() } finally { System.err.println("Shutting down executor") @@ -152,6 +154,16 @@ object MainMillBuildServer extends ExternalModule { val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) val mods: Seq[JavaModule] = modules(ev)() for (module <- mods) { + val mainTask = ev.evaluate(Strict.Agg(module.finalMainClass)).results(module.finalMainClass) + println(ev.evaluate(Strict.Agg(module.runClasspath)).results(module.runClasspath)) + val id = millServer.moduleToTargetId(module) + module match { + case m: TestModule => + println("Test: " + millServer.buildTargetScalaTestClasses( + new ScalaTestClassesParams(List(id).asJava) + )) + case default => + } System.err.println("Module: " + module + "has capabilities: " + ModuleUtils.getModuleCapabilities(module, ev)) System.err.println("Base directory: " + module.millOuterCtx.millSourcePath) System.err.println("MIll source path: " + module.millSourcePath) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 9865a5a2..9658fdc5 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -2,20 +2,23 @@ package mill.contrib.bsp import sbt.testing._ import java.util.{Calendar, Collections} import java.util.concurrent.CompletableFuture + import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ -import mill._ -import mill.api.Strict +import mill.{scalalib, _} +import mill.api.{Loose, Result, Strict} import mill.contrib.bsp.ModuleUtils._ -import mill.eval.Evaluator +import mill.eval.{Evaluator} import mill.scalalib._ import mill.scalalib.api.CompilationResult import mill.scalalib.api.ZincWorkerApi + import scala.collection.mutable.Map -import mill.api.Loose +import mill.api.Result.{Failing, Success} + import scala.collection.JavaConverters._ import mill.modules.Jvm -import mill.util.{PrintLogger, Ctx} +import mill.util.{Ctx, PrintLogger} import mill.define.{Discover, ExternalModule, Target, Task} @@ -113,9 +116,12 @@ class MillBuildServer(evaluator: Evaluator, for (targetId <- sourcesParams.getTargets.asScala) { var itemSources = List[SourceItem]() - val sources = evaluateInformativeTask(evaluator, targetIdToModule(targetId).sources).left.get.map(pathRef => pathRef.path) - val generatedSources = evaluateInformativeTask(evaluator, targetIdToModule(targetId).generatedSources).left.get - .map(pathRef => pathRef.path) + val sources = evaluateInformativeTask(evaluator, targetIdToModule(targetId).sources, Agg.empty[PathRef]). + map(pathRef => pathRef.path).toSeq + val generatedSources = evaluateInformativeTask(evaluator, + targetIdToModule(targetId).generatedSources, + Agg.empty[PathRef]). + map(pathRef => pathRef.path).toSeq for (file <- getSourceFiles(sources)) { itemSources ++= List(new SourceItem(file.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.FILE, false)) @@ -162,13 +168,16 @@ class MillBuildServer(evaluator: Evaluator, for (targetId <- dependencySourcesParams.getTargets.asScala) { val millModule = targetIdToModule(targetId) - var sources = evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.transitiveIvyDeps)). - left.get ++ - evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.compileIvyDeps)). - left.get + var sources = evaluateInformativeTask(evaluator, + millModule.resolveDeps(millModule.transitiveIvyDeps), + Agg.empty[PathRef]) ++ + evaluateInformativeTask(evaluator, + millModule.resolveDeps(millModule.compileIvyDeps), + Agg.empty[PathRef]) millModule match { case m: ScalaModule => sources ++= evaluateInformativeTask(evaluator, - millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps)).left.get + millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps), + Agg.empty[PathRef]) case m: JavaModule => sources ++= List() } items ++= List(new DependencySourcesItem(targetId, sources. @@ -191,7 +200,7 @@ class MillBuildServer(evaluator: Evaluator, for (targetId <- resourcesParams.getTargets.asScala) { val millModule = targetIdToModule(targetId) - val resources = evaluateInformativeTask(evaluator, millModule.resources).left.get. + val resources = evaluateInformativeTask(evaluator, millModule.resources, Agg.empty[PathRef]). flatMap(pathRef => os.walk(pathRef.path)). map(path => path.toNIO.toAbsolutePath.toUri.toString). toList.asJava @@ -234,6 +243,12 @@ class MillBuildServer(evaluator: Evaluator, compileTime += result.timings.map(timingTuple => timingTuple._2).sum var statusCode = StatusCode.OK +// result.results(compileTask) match { +// case r: Failing[CompilationResult] => +// statusCode = StatusCode.ERROR +// numFailures += result.failing.keyCount +// case default => +// } if (result.failing.keyCount > 0) { statusCode = StatusCode.ERROR numFailures += result.failing.keyCount @@ -328,11 +343,7 @@ class MillBuildServer(evaluator: Evaluator, module match { case m: TestModule => val testModule = m.asInstanceOf[TestModule] val testTask = testModule.test(argsMap(targetId).mkString(" ")) - val passed = 0; - val ignored = 0; - val skipped = 0; - val failed = 0; - val cancelled = 0 + // send notification to client that testing of this target started val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) taskStartParams.setEventTime(System.currentTimeMillis()) @@ -341,31 +352,55 @@ class MillBuildServer(evaluator: Evaluator, taskStartParams.setData(new TestTask(targetId)) client.onBuildTaskStart(taskStartParams) - val (msg, results) = TestRunner.runTests( - TestRunner.frameworks(evaluateInformativeTask(millEvaluator, testModule.testFrameworks).left.get), - evaluateInformativeTask(millEvaluator, testModule.runClasspath).left.get.map(_.path), - Agg(evaluateInformativeTask(millEvaluator, testModule.compile).left.get.classes.path), - argsMap(targetId) - ) - - val endTime = System.currentTimeMillis() - // send notification to client that testing of this target ended => test report - val statusCode = getStatusCode(results) - val taskFinishParams = new TaskFinishParams( - new TaskId(testTask.hashCode().toString), - getStatusCode(results) - ) - taskFinishParams.setEventTime(endTime) - taskFinishParams.setMessage("Finished testing target: " + - moduleToTarget(targetIdToModule(targetId)).getDisplayName) - taskFinishParams.setDataKind("test-report") - taskFinishParams.setData(getTestReport(targetId, results)) - client.onBuildTaskFinish(taskFinishParams) - - statusCode match { - case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR - case default => + val runClasspath = getTaskResult(millEvaluator, testModule.runClasspath) + val frameworks = getTaskResult(millEvaluator, testModule.testFrameworks) + val compilationResult = getTaskResult(millEvaluator, testModule.compile) + + (runClasspath, frameworks, compilationResult) match { + case (Success(classpath), Success(testFrameworks), Success(compResult)) => + val (msg, results) = TestRunner.runTests( + TestRunner.frameworks(testFrameworks.asInstanceOf[Seq[String]]), + classpath.asInstanceOf[Agg[PathRef]].map(_.path), + Agg(compResult.asInstanceOf[scalalib.api.CompilationResult].classes.path), + argsMap(targetId) + ) + val endTime = System.currentTimeMillis() + // send notification to client that testing of this target ended => test report + val statusCode = getStatusCode(results) + val taskFinishParams = new TaskFinishParams( + new TaskId(testTask.hashCode().toString), + getStatusCode(results) + ) + taskFinishParams.setEventTime(endTime) + taskFinishParams.setMessage("Finished testing target: " + + moduleToTarget(targetIdToModule(targetId)).getDisplayName) + taskFinishParams.setDataKind("test-report") + taskFinishParams.setData(getTestReport(targetId, results)) + client.onBuildTaskFinish(taskFinishParams) + statusCode match { + case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR + case default => + } + case default => val endTime = System.currentTimeMillis() + val taskFinishParams = new TaskFinishParams( + new TaskId(testTask.hashCode().toString), + StatusCode.ERROR + ) + taskFinishParams.setEventTime(endTime) + taskFinishParams.setMessage("Testing target: " + + moduleToTarget(targetIdToModule(targetId)).getDisplayName + + "failed because one of the tasks it depended on failed. There might" + + "be compilation errors.") + taskFinishParams.setDataKind("test-report") + taskFinishParams.setData( + new TestReport(targetId, 0, 0, 0, 0, 0) + ) + overallStatusCode = StatusCode.ERROR + client.onBuildTaskFinish(taskFinishParams) + buildTargetCompile(new CompileParams(List(targetId).asJava)) } + + case default => } } @@ -393,12 +428,12 @@ class MillBuildServer(evaluator: Evaluator, val module = targetIdToModule(targetId) module match { case m: ScalaModule => - val options = evaluateInformativeTask(evaluator, m.scalacOptions).left.get.toList - val classpath = evaluateInformativeTask(evaluator, m.compileClasspath).left.get. + val options = evaluateInformativeTask(evaluator, m.scalacOptions, Seq.empty[String]).toList + val classpath = evaluateInformativeTask(evaluator, m.compileClasspath, Agg.empty[PathRef]). map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString).toList val index = m.millModuleSegments.parts.length - val classDirectory = m.millOuterCtx.fileName//.toNIO.toAbsolutePath.toUri.toString + val classDirectory = m.millSourcePath.toNIO.toAbsolutePath.toUri.toString targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) case m: JavaModule => targetScalacOptions ++= List() @@ -419,17 +454,21 @@ class MillBuildServer(evaluator: Evaluator, var items = List.empty[ScalaMainClassesItem] for (targetId <- scalaMainClassesParams.getTargets.asScala) { val module = targetIdToModule(targetId) - val specifiedMainClass = evaluateInformativeTask(evaluator, module.finalMainClassOpt).left.get - specifiedMainClass match { - case main: Left[String, String] => //TODO: Send a notification that main class could not be chosen - case main: Right[String, String] => - val item = new ScalaMainClassesItem(targetId, List(new ScalaMainClass(specifiedMainClass.getOrElse(""), - evaluateInformativeTask(evaluator, module.forkArgs).left.get.toList.asJava, - List.empty[String].asJava)).asJava) - items ++= List(item) + val scalaMainClasses = getTaskResult(millEvaluator, module.finalMainClassOpt) match { + case result: Result.Success[Any] => result.asSuccess.get.value match { + case mainClass: Right[String, String] => + List(new ScalaMainClass( + mainClass.value, + evaluateInformativeTask(evaluator, module.forkArgs, Seq.empty[String]). + toList.asJava, + List.empty[String].asJava)) + case msg: Left[String, String] => List.empty[ScalaMainClass] + } + case default => List.empty[ScalaMainClass] + } + val item = new ScalaMainClassesItem (targetId , scalaMainClasses.asJava) + items ++= List(item) } - } - new ScalaMainClassesResult(items.asJava) } val future = new CompletableFuture[ScalaMainClassesResult]() @@ -438,16 +477,24 @@ class MillBuildServer(evaluator: Evaluator, } private[this] def getTestFrameworks(module: TestModule) (implicit ctx: Ctx.Home): Seq[String] = { - //val frameworkMap = TestRunner.frameworks(evaluateInformativeTask(module.testFrameworks).left.get) - val classFingerprint = Jvm.inprocess(evaluateInformativeTask(evaluator, module.runClasspath).left.get.map(_.path), - true, - true, - false, cl => { - val fs = TestRunner.frameworks(evaluateInformativeTask(evaluator, module.testFrameworks).left.get)(cl) - fs.flatMap(framework => - discoverTests(cl, framework, Agg(evaluateInformativeTask(evaluator, module.compile).left.get.classes.path))) - }) - classFingerprint.map(classF => classF._1.getName.stripSuffix("$")) + 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), + true, + true, + 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 default => Seq.empty[String] //TODO: or send notification that something went wrong + } } override def buildTargetScalaTestClasses(scalaTestClassesParams: ScalaTestClassesParams): diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index e89fae3e..266d832a 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -1,8 +1,10 @@ package mill.contrib.bsp import java.util.Collections + import scala.collection.JavaConverters._ import ch.epfl.scala.bsp4j._ +import mill.api.Result.Success import mill.api.{Loose, Strict} import mill.define.{Discover, Task} import mill.eval._ @@ -13,6 +15,7 @@ import mill.scalanativelib._ import mill.scalalib.{JavaModule, ScalaModule, TestModule} import mill.util.DummyLogger + object ModuleUtils { object dummyModule extends mill.define.ExternalModule { @@ -59,8 +62,11 @@ object ModuleUtils { } def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { - val canRun = evaluateInformativeTask(evaluator, module.finalMainClass, success = false).right.get match { - case result: Result.Success[String] => true + val canRun = getTaskResult(evaluator, module.finalMainClassOpt) match { + case result: Result.Success[Any] => result.asSuccess.get.value match { + case _: Right[String, String] => true + case _: Left[String, String] => false + } case default => false } val canTest = module match { @@ -76,35 +82,38 @@ object ModuleUtils { def computeScalaBuildTarget(module: JavaModule, evaluator: Evaluator): Any = { module match { case m: ScalaModule => - val scalaVersion = evaluateInformativeTask(evaluator, m.scalaVersion).left.get + val scalaVersion = evaluateInformativeTask(evaluator, m.scalaVersion, "") new ScalaBuildTarget( - evaluateInformativeTask(evaluator, m.scalaOrganization).left.get, + evaluateInformativeTask(evaluator, m.scalaOrganization, ""), scalaVersion, Util.scalaBinaryVersion(scalaVersion), getScalaTargetPlatform(m), computeScalaLangDependencies(m, evaluator). - map(pathRef => pathRef.path.toNIO.toAbsolutePath.toString). + map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString). toList.asJava) case m: JavaModule => "This is just a test or java target" } } - def evaluateInformativeTask[T](evaluator: Evaluator, task: Task[T], success: Boolean = true): Either[T, Result[Any]] = { - if (success) { - Left(evaluator.evaluate(Strict.Agg(task)).results(task).asSuccess.get.value.asInstanceOf[T]) - } else { - Right(evaluator.evaluate(Strict.Agg(task)).results(task)) - } + def getTaskResult[T](evaluator: Evaluator, task: Task[T]): Result[Any] = { + evaluator.evaluate(Strict.Agg(task)).results(task) + } + 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 + } } def computeScalaLangDependencies(module: ScalaModule, evaluator: Evaluator): Loose.Agg[PathRef] = { - evaluateInformativeTask(evaluator, module.scalacPluginClasspath).left.get ++ - evaluateInformativeTask(evaluator, module.resolveDeps(module.ivyDeps)).left.get. - filter(pathRef => pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-compiler") || - pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-reflect") || - pathRef.path.toNIO.toAbsolutePath.toString.contains("scala-library")) + evaluateInformativeTask(evaluator, module.scalacPluginClasspath, Loose.Agg.empty[PathRef]) ++ + evaluateInformativeTask(evaluator, module.resolveDeps(module.ivyDeps), Loose.Agg.empty[PathRef]). + filter(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-compiler") || + pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-reflect") || + pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-library")) } def getScalaTargetPlatform(module: ScalaModule): ScalaPlatform = { diff --git a/scratch/build.sc b/scratch/build.sc index 8c32d634..817f68e4 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,35 +1,30 @@ -import mill.Agg -import mill.scalalib._ -import ammonite.ops._ +import mill._, scalalib._ +import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-7-be21ae-DIRTY1fe41d7a` -object core extends ScalaModule{ +object mill_exercise extends ScalaModule { def scalaVersion = "2.12.8" + def ivyDeps = Agg( - ivy"org.eclipse.jetty:jetty-websocket:8.1.16.v20140903", - ivy"org.eclipse.jetty:jetty-server:8.1.16.v20140903" + ivy"org.scalameta::metals:0.5.2", + ivy"org.scalameta::scalameta:4.1.9", + ivy"com.geirsson::coursier-small:1.3.3", + ivy"org.scala-lang:scala-reflect:2.12.8", + ivy"org.scala-lang:scala-compiler:2.12.8", + ivy"org.eclipse.lsp4j:org.eclipse.lsp4j:0.7.1", + ivy"ch.epfl.scala:bsp4j:2.0.0-M3", + ivy"com.google.code.gson:gson:2.3.1", + ivy"com.lihaoyi::ammonite-ops:1.6.7" ) -} -object foo extends ScalaModule { - def scalaVersion = "2.12.8" - // def scalacPluginIvyDeps = Agg(ivy"org.scala-lang:scala-compiler:${scalaVersion()}") - def ivyDeps = Agg(ivy"org.scala-lang:scala-reflect:${scalaVersion()}", - ivy"org.scala-lang:scala-compiler:${scalaVersion()}") - object foo_test extends Tests { - def moduleDeps = Seq(foo) - def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.6.0") - def testFrameworks = Seq("utest.runner.Framework") - def generatedSources = T { - Seq(PathRef(os.pwd / "foo/test/src/Generated.scala")) - } - } -} + object test extends Tests { + def ivyDeps = Agg(//ivy"org.scalameta::metals:0.5.2", + ivy"org.scalatest::scalatest:3.0.4", + ivy"org.scalactic::scalactic:3.0.5", + ivy"org.scalameta::testkit:4.1.9", + ivy"org.eclipse.lsp4j:org.eclipse.lsp4j:0.5.0", + ivy"ch.epfl.scala::bloop-config:1.2.5", + ivy"org.scala-lang.modules::scala-java8-compat:0.9.0") -object bar extends ScalaModule { - def scalaVersion = "2.12.8" - def moduleDeps = Seq(foo) - def mainClass = Some("BarMain") - def compileIvyDeps = Agg(ivy"org.scala-lang:scala-reflect:${scalaVersion()}") - def scalacOptions = Seq("-no-specialization") - def ivyDeps = Agg(ivy"ch.epfl.scala::bloop-config:1.2.5") + def testFrameworks = Seq("org.scalatest.tools.Framework") + } } -- cgit v1.2.3 From b71748b1b58da3b70ceb9290257ed688b71fbe21 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 27 Jun 2019 11:19:21 +0200 Subject: Implemented simple support for cleaning cache --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 15 ++--------- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 30 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 7f1650d6..a8508ab6 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -154,20 +154,9 @@ object MainMillBuildServer extends ExternalModule { val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) val mods: Seq[JavaModule] = modules(ev)() for (module <- mods) { - val mainTask = ev.evaluate(Strict.Agg(module.finalMainClass)).results(module.finalMainClass) - println(ev.evaluate(Strict.Agg(module.runClasspath)).results(module.runClasspath)) - val id = millServer.moduleToTargetId(module) - module match { - case m: TestModule => - println("Test: " + millServer.buildTargetScalaTestClasses( - new ScalaTestClassesParams(List(id).asJava) - )) - case default => + println(module.millModuleSegments.parts.mkString(".")) } - System.err.println("Module: " + module + "has capabilities: " + ModuleUtils.getModuleCapabilities(module, ev)) - System.err.println("Base directory: " + module.millOuterCtx.millSourcePath) - System.err.println("MIll source path: " + module.millSourcePath) - } + } /** diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 9658fdc5..91d9c4fd 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -8,7 +8,7 @@ import ch.epfl.scala.bsp4j._ import mill.{scalalib, _} import mill.api.{Loose, Result, Strict} import mill.contrib.bsp.ModuleUtils._ -import mill.eval.{Evaluator} +import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult import mill.scalalib.api.ZincWorkerApi @@ -21,6 +21,8 @@ import mill.modules.Jvm import mill.util.{Ctx, PrintLogger} import mill.define.{Discover, ExternalModule, Target, Task} +import scala.io.Source + class MillBuildServer(evaluator: Evaluator, _bspVersion: String, @@ -418,7 +420,31 @@ class MillBuildServer(evaluator: Evaluator, future } - override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = ??? + override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = { + def getCleanCacheResult: CleanCacheResult = { + var msg = "" + var cleaned = true + for (targetId <- cleanCacheParams.getTargets.asScala) { + val module = targetIdToModule(targetId) + val process = Runtime.getRuntime.exec(s"mill clean ${module.millModuleSegments.parts.mkString(".")}.compile") + + val processIn = process.getInputStream + val processErr = process.getErrorStream + + val errMessage = Source.fromInputStream(processErr).getLines().mkString("\n") + val message = Source.fromInputStream(processIn).getLines().mkString("\n") + msg += s"Cleaning cache for target ${targetId} produced the following message: ${message}, ${errMessage}" + if (msg.contains("failed")) { + cleaned = false + } + process.waitFor() + } + new CleanCacheResult(msg, cleaned) + } + val future = new CompletableFuture[CleanCacheResult]() + future.complete(getCleanCacheResult) + future + } override def buildTargetScalacOptions(scalacOptionsParams: ScalacOptionsParams): CompletableFuture[ScalacOptionsResult] = { -- cgit v1.2.3 From 0d53a6e057ae24ecf0bfd5bf0929310723c31282 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 10 Jul 2019 17:12:12 +0200 Subject: Implemented support for publishing compilation diagnostics through the custom BspLoggedReporter reporter. Patched the mill.api.Ctx data structure as well as the evaluate() method on mill's Evaluator in order to accept a potential reporter from the outside, or use a default value if none is given. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 30 +++- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 82 +++++++++++ .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 152 ++++++++++++++++++++- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 3 + main/api/src/mill/api/Ctx.scala | 5 +- main/core/src/eval/Evaluator.scala | 27 ++-- scalalib/api/src/ZincWorkerApi.scala | 8 +- scalalib/src/JavaModule.scala | 9 +- scalalib/src/ScalaModule.scala | 7 +- scalalib/worker/src/ZincWorkerImpl.scala | 43 +++--- scratch/build.sc | 4 +- 11 files changed, 323 insertions(+), 47 deletions(-) create mode 100644 contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index a8508ab6..7caaf5d3 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -7,10 +7,10 @@ import java.nio.file.FileAlreadyExistsException import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, ScalaTestClassesParams} +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, WorkspaceBuildTargetsResult} import mill._ import mill.api.Strict -import mill.contrib.bsp.{MillBuildServer, ModuleUtils} +import mill.contrib.bsp.{BspLoggedReporter, MillBuildServer, ModuleUtils} import mill.define.{Command, Discover, ExternalModule, Target, Task} import mill.eval.Evaluator import mill.scalalib._ @@ -20,6 +20,7 @@ import requests.Compress.None import upickle.default import scala.collection.JavaConverters._ +import scala.io.Source object MainMillBuildServer extends ExternalModule { @@ -152,11 +153,28 @@ object MainMillBuildServer extends ExternalModule { def experiment(ev: Evaluator): Command[Unit] = T.command { val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) - val mods: Seq[JavaModule] = modules(ev)() - for (module <- mods) { - println(module.millModuleSegments.parts.mkString(".")) + val client = new BuildClient { + var diagnostics = List.empty[PublishDiagnosticsParams] + override def onBuildShowMessage(params: ShowMessageParams): Unit = ??? + override def onBuildLogMessage(params: LogMessageParams): Unit = ??? + override def onBuildTaskStart(params: TaskStartParams): Unit = ??? + override def onBuildTaskProgress(params: TaskProgressParams): Unit = ??? + override def onBuildTaskFinish(params: TaskFinishParams): Unit = ??? + override def onBuildPublishDiagnostics( + params: PublishDiagnosticsParams + ): Unit = { + diagnostics ++= List(params) } - + override def onBuildTargetDidChange(params: DidChangeBuildTarget): Unit = + ??? + } + for (module <- millServer.millModules) { + ev.evaluate(Strict.Agg(module.compile), Option(new BspLoggedReporter(client, + millServer.moduleToTargetId(module), + Option.empty[String], + 10, millServer.getCompilationLogger))) + //println("Diagnostics: " + client.diagnostics) + } } /** diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala new file mode 100644 index 00000000..31900211 --- /dev/null +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -0,0 +1,82 @@ +package mill.contrib.bsp + +import java.io.File + +import ch.epfl.scala.{bsp4j => bsp} +import sbt.internal.inc.ManagedLoggedReporter +import sbt.internal.inc.schema.Position +import sbt.internal.util.ManagedLogger +import xsbti.{Problem, Severity} + +import scala.collection.JavaConverters._ +import scala.compat.java8.OptionConverters._ +import scala.io.Source + +class BspLoggedReporter(client: bsp.BuildClient, + targetId: bsp.BuildTargetIdentifier, + compilationOriginId: Option[String], + maxErrors: Int, + logger: ManagedLogger) extends ManagedLoggedReporter(maxErrors, logger) { + + override def logError(problem: Problem): Unit = { + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + println("Sent diagnostics to the client") + super.logError(problem) + println("Logged the error") + } + + override def logInfo(problem: Problem): Unit = { + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + super.logInfo(problem) + } + + override def logWarning(problem: Problem): Unit = { + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + super.logWarning(problem) + } + + def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): + bsp.PublishDiagnosticsParams = { + println("Problem: " + problem) + val sourceFile = problem.position().sourceFile().asScala + val start = new bsp.Position( + problem.position.startLine.asScala.getOrElse(0), + problem.position.startOffset.asScala.getOrElse(0)) + val end = new bsp.Position( + problem.position.endLine.asScala.getOrElse(0), + problem.position.endOffset.asScala.getOrElse(0)) + val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) + diagnostic.setCode(problem.position.lineContent) + diagnostic.setSource("compiler from mill") + diagnostic.setSeverity( problem.severity match { + case Severity.Info => bsp.DiagnosticSeverity.INFORMATION + case Severity.Error => bsp.DiagnosticSeverity.ERROR + case Severity.Warn => bsp.DiagnosticSeverity.WARNING + } + ) + + val params = new bsp.PublishDiagnosticsParams( + new bsp.TextDocumentIdentifier(sourceFile.getOrElse(new File(targetId.getUri)).toPath.toAbsolutePath.toUri.toString), + targetId, List(diagnostic).asJava, true) + + if (originId.nonEmpty) { params.setOriginId(originId.get) } + println("Diagnostics: " + params) + params + } + + private[this] def getErrorCode(file: Option[File], start: bsp.Position, end: bsp.Position, position: xsbti.Position): String = { + file match { + case None => position.lineContent + case f: Option[File] => + val source = Source.fromFile(f.get) + source.close() + val lines = source.getLines.toSeq + val code = lines(start.getLine).substring(start.getCharacter) + + lines.take(start.getLine - 1).takeRight(lines.length - end.getLine - 1).mkString("\n") + + lines(end.getLine).substring(0, end.getCharacter + 1) + code + } + + } + +} diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 91d9c4fd..5d79ee76 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -1,25 +1,34 @@ package mill.contrib.bsp +import java.io.File + import sbt.testing._ import java.util.{Calendar, Collections} import java.util.concurrent.CompletableFuture +import scala.compat.java8.OptionConverters._ import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ +import ch.epfl.scala.{bsp4j => bsp} import mill.{scalalib, _} import mill.api.{Loose, Result, Strict} import mill.contrib.bsp.ModuleUtils._ import mill.eval.Evaluator import mill.scalalib._ -import mill.scalalib.api.CompilationResult -import mill.scalalib.api.ZincWorkerApi +import mill.scalalib.api.{CompilationResult, ZincWorkerApi} +import sbt.internal.inc._ +import xsbti.{Position, Problem, Severity} +import xsbti.compile.{AnalysisContents, AnalysisStore, FileAnalysisStore} +import xsbti.compile.analysis.SourceInfo import scala.collection.mutable.Map -import mill.api.Result.{Failing, Success} +import mill.api.Result.{Failing, Failure, Success} import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.{Ctx, PrintLogger} import mill.define.{Discover, ExternalModule, Target, Task} +import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} +import sbt.util.LogExchange import scala.io.Source @@ -31,7 +40,6 @@ class MillBuildServer(evaluator: Evaluator, implicit def millScoptEvaluatorReads[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 @@ -44,7 +52,6 @@ class MillBuildServer(evaluator: Evaluator, var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) - var clientInitialized = false val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { @@ -217,6 +224,131 @@ class MillBuildServer(evaluator: Evaluator, future } + private[this] def getErrorCode(file: File, start: bsp.Position, end: bsp.Position): String = { + + val source = Source.fromFile(file) + source.close() + val lines = source.getLines.toSeq + val code = lines(start.getLine).substring(start.getCharacter) + + lines.take(start.getLine - 1).takeRight(lines.length - end.getLine - 1).mkString("\n") + + lines(end.getLine).substring(0, end.getCharacter + 1) + code + } + + def getDiagnosticsFromFile(analysisFile: os.Path, targetId: BuildTargetIdentifier, originId: Option[String]): + Seq[PublishDiagnosticsParams] = { + val analysisStore: AnalysisStore = FileAnalysisStore.getDefault(analysisFile.toIO) + analysisStore.get.asScala match { + case contents: AnalysisContents => + val sourceInfoMap: Map[File, SourceInfo] = contents.getAnalysis.readSourceInfos.getAllSourceInfos.asScala + var diagnosticsParams = Seq.empty[PublishDiagnosticsParams] + for ( (file, sourceInfo) <- sourceInfoMap) { + var diagnostics = List.empty[Diagnostic] + for (problem <- sourceInfo.getReportedProblems) { + val start = new bsp.Position( + problem.position.startLine.asScala.getOrElse(0), + problem.position.startOffset.asScala.getOrElse(0)) + val end = new bsp.Position( + problem.position.endLine.asScala.getOrElse(0), + problem.position.endOffset.asScala.getOrElse(0)) + val diagnostic = new Diagnostic(new Range(start, end), problem.message) + diagnostic.setCode(getErrorCode(file, start, end)) + diagnostic.setSource("compiler from mill") + + diagnostic.setSeverity( problem.severity match { + case Severity.Info => DiagnosticSeverity.INFORMATION + case Severity.Error => DiagnosticSeverity.ERROR + case Severity.Warn => DiagnosticSeverity.WARNING + } + ) + diagnostics ++= List(diagnostic) + } + val params = new PublishDiagnosticsParams(new TextDocumentIdentifier(file.toURI.toString), + targetId, diagnostics.asJava, true) + if (originId.nonEmpty) { params.setOriginId(originId.get) } + diagnosticsParams ++= Seq(params) + } + diagnosticsParams + case None => Seq.empty[PublishDiagnosticsParams] + } + } + + def getSourceFileCompileErrors(problems: Seq[Problem]): Map[File, Array[Problem]] = { + val problemsMap = Map.empty[File, Array[Problem]] + + for (problem <- problems) { + try { + val sourceFile = problem.position.sourceFile.get + if (problemsMap.contains(sourceFile)) { + problemsMap(sourceFile) = problemsMap(sourceFile) ++ Array(problem) + } else { + problemsMap(sourceFile) = Array(problem) + } + + } catch { + case e: Exception => + } + } + problemsMap + } + + def getDiagnostics(problems: Array[Problem], targetId: BuildTargetIdentifier, originId: Option[String]): + Seq[PublishDiagnosticsParams] = { + var diagnosticsParams = Seq.empty[PublishDiagnosticsParams] + for (( sourceFile, problems ) <- getSourceFileCompileErrors(problems)) { + var diagnostics = Seq.empty[Diagnostic] + for (problem <- problems) { + val start = new bsp.Position( + problem.position.startLine.asScala.getOrElse(0), + problem.position.startOffset.asScala.getOrElse(0)) + val end = new bsp.Position( + problem.position.endLine.asScala.getOrElse(0), + problem.position.endOffset.asScala.getOrElse(0)) + val diagnostic = new Diagnostic(new Range(start, end), problem.message) + diagnostic.setCode(getErrorCode(sourceFile, start, end)) + diagnostic.setSource("compiler from mill") + diagnostic.setSeverity( problem.severity match { + case Severity.Info => DiagnosticSeverity.INFORMATION + case Severity.Error => DiagnosticSeverity.ERROR + case Severity.Warn => DiagnosticSeverity.WARNING + } + ) + diagnostics ++= List(diagnostic) + } + val params = new PublishDiagnosticsParams(new TextDocumentIdentifier(sourceFile.toPath.toAbsolutePath.toUri.toString), + targetId, diagnostics.asJava, true) + + if (originId.nonEmpty) { params.setOriginId(originId.get) } + diagnosticsParams ++= Seq(params) + } + diagnosticsParams + } + + def getOriginId(params: CompileParams): Option[String] = { + try { + Option(params.getOriginId) + } catch { + case e: Exception => Option.empty[String] + } + } + + def sendCompilationDiagnostics(problems: Array[Problem], targetId: BuildTargetIdentifier, compileParams: CompileParams) = { + for (publishDiagnosticsParams <- + getDiagnostics(problems, targetId, getOriginId(compileParams))) { + client.onBuildPublishDiagnostics(publishDiagnosticsParams) + } + } + + def getCompilationLogger: ManagedLogger = { + val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( + mill.util.DummyLogger.outputStream + )) + val l = LogExchange.logger("Hello") + LogExchange.unbindLoggerAppenders("Hello") + LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Info) :: Nil) + l + } + //TODO: send task notifications - start, progress and finish //TODO: if the client wants to give compilation arguments and the module // already has some from the build file, what to do? @@ -229,9 +361,10 @@ class MillBuildServer(evaluator: Evaluator, var compileTime = 0 for (targetId <- compileParams.getTargets.asScala) { if (moduleToTarget(targetIdToModule(targetId)).getCapabilities.getCanCompile) { - var millModule = targetIdToModule(targetId) + val millModule = targetIdToModule(targetId) //millModule.javacOptions = compileParams.getArguments.asScala val compileTask = millModule.compile + // send notification to client that compilation of this target started val taskStartParams = new TaskStartParams(new TaskId(compileTask.hashCode().toString)) taskStartParams.setEventTime(System.currentTimeMillis()) @@ -240,8 +373,13 @@ class MillBuildServer(evaluator: Evaluator, taskStartParams.setData(new CompileTask(targetId)) client.onBuildTaskStart(taskStartParams) - val result = millEvaluator.evaluate(Strict.Agg(compileTask)) + val result = millEvaluator.evaluate(Strict.Agg(compileTask), + Option(new BspLoggedReporter(client, + targetId, + getOriginId(compileParams), + 10, getCompilationLogger))) val endTime = System.currentTimeMillis() + compileTime += result.timings.map(timingTuple => timingTuple._2).sum var statusCode = StatusCode.OK diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 266d832a..a3202aab 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -51,6 +51,9 @@ object ModuleUtils { supportedLanguages.asJava, dependencies, capabilities) + if (module.isInstanceOf[ScalaModule]) { + buildTarget.setDataKind("scala") + } buildTarget.setData(dataBuildTarget) buildTarget.setDisplayName(module.millModuleSegments.last.value.toList.head.pathSegments.head) buildTarget.setBaseDirectory(module.millSourcePath.toNIO.toAbsolutePath.toUri.toString) diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 69d01f7e..7d081d6a 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -2,8 +2,8 @@ package mill.api import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.language.implicitConversions - import os.Path +import sbt.internal.inc.ManagedLoggedReporter /** * Provides access to various resources in the context of a currently execution Target. @@ -60,7 +60,8 @@ class Ctx( dest0: () => os.Path, val log: Logger, val home: os.Path, - val env: Map[String, String] + val env: Map[String, String], + val reporter: Option[ManagedLoggedReporter] ) extends Ctx.Dest with Ctx.Log diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index f4ec8ff9..22132a8f 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -5,7 +5,6 @@ import java.net.URLClassLoader import scala.collection.JavaConverters._ import scala.collection.mutable import scala.util.control.NonFatal - import ammonite.runtime.SpecialClassLoader import mill.util.Router.EntryPoint import mill.define.{Ctx => _, _} @@ -13,11 +12,15 @@ import mill.api.Result.{Aborted, OuterStack, Success} import mill.util import mill.util._ import mill.api.Strict.Agg +import sbt.internal.inc.ManagedLoggedReporter +import sbt.internal.util.{ConsoleOut, MainAppender} +import sbt.util.LogExchange case class Labelled[T](task: NamedTask[T], segments: Segments){ def format = task match{ case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) + case t: PersistentArgs[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) case _ => None } def writer = task match{ @@ -40,7 +43,7 @@ case class Evaluator(home: os.Path, val classLoaderSignHash = classLoaderSig.hashCode() - def evaluate(goals: Agg[Task[_]]): Evaluator.Results = { + def evaluate(goals: Agg[Task[_]], reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter]): Evaluator.Results = { os.makeDir.all(outPath) val (sortedGroups, transitive) = Evaluator.plan(rootModule, goals) @@ -66,7 +69,8 @@ case class Evaluator(home: os.Path, terminal, group, results, - counterMsg + counterMsg, + reporter ) someTaskFailed = someTaskFailed || newResults.exists(task => !task._2.isInstanceOf[Success[_]]) @@ -111,7 +115,8 @@ case class Evaluator(home: os.Path, def evaluateGroupCached(terminal: Either[Task[_], Labelled[_]], group: Agg[Task[_]], results: collection.Map[Task[_], Result[(Any, Int)]], - counterMsg: String + counterMsg: String, + reporter: Option[ManagedLoggedReporter] ): (collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean) = { val externalInputsHash = scala.util.hashing.MurmurHash3.orderedHash( @@ -133,7 +138,8 @@ case class Evaluator(home: os.Path, inputsHash, paths = None, maybeTargetLabel = None, - counterMsg = counterMsg + counterMsg = counterMsg, + reporter ) (newResults, newEvaluated, false) case Right(labelledNamedTask) => @@ -185,7 +191,8 @@ case class Evaluator(home: os.Path, inputsHash, paths = Some(paths), maybeTargetLabel = Some(msgParts.mkString), - counterMsg = counterMsg + counterMsg = counterMsg, + reporter ) newResults(labelledNamedTask.task) match{ @@ -259,7 +266,8 @@ case class Evaluator(home: os.Path, inputsHash: Int, paths: Option[Evaluator.Paths], maybeTargetLabel: Option[String], - counterMsg: String): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { + counterMsg: String, + reporter: Option[ManagedLoggedReporter]): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { val newEvaluated = mutable.Buffer.empty[Task[_]] @@ -319,9 +327,10 @@ case class Evaluator(home: os.Path, }, multiLogger, home, - env + env, + reporter //new ManagedLoggedReporter(10, logger) ) - + println("Reporter: " + reporter) val out = System.out val in = System.in val err = System.err diff --git a/scalalib/api/src/ZincWorkerApi.scala b/scalalib/api/src/ZincWorkerApi.scala index 53a98c24..e80895d6 100644 --- a/scalalib/api/src/ZincWorkerApi.scala +++ b/scalalib/api/src/ZincWorkerApi.scala @@ -3,6 +3,8 @@ package mill.scalalib.api import mill.api.Loose.Agg import mill.api.PathRef import mill.api.JsonFormatters._ +import sbt.internal.inc._ + object ZincWorkerApi{ type Ctx = mill.api.Ctx.Dest with mill.api.Ctx.Log with mill.api.Ctx.Home } @@ -11,7 +13,8 @@ trait ZincWorkerApi { def compileJava(upstreamCompileOutput: Seq[CompilationResult], sources: Agg[os.Path], compileClasspath: Agg[os.Path], - javacOptions: Seq[String]) + javacOptions: Seq[String], + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] /** Compile a mixed Scala/Java or Scala-only project */ @@ -23,7 +26,8 @@ trait ZincWorkerApi { scalaOrganization: String, scalacOptions: Seq[String], compilerClasspath: Agg[os.Path], - scalacPluginClasspath: Agg[os.Path]) + scalacPluginClasspath: Agg[os.Path], + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] def discoverMainClasses(compilationResult: CompilationResult) diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index b9987ca1..e6c47324 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -13,6 +13,9 @@ import mill.modules.Jvm.{createAssembly, createJar} import Lib._ import mill.scalalib.publish.{Artifact, Scope} import mill.api.Loose.Agg +import sbt.internal.inc.ManagedLoggedReporter +import sbt.internal.util.{ConsoleOut, MainAppender} +import sbt.util.LogExchange /** * Core configuration required to compile a single Scala compilation target @@ -209,15 +212,17 @@ trait JavaModule extends mill.Module with TaskModule with GenIdeaModule { outer } yield PathRef(path) } + /** * Compiles the current module to generate compiled classfiles/bytecode */ - def compile: T[mill.scalalib.api.CompilationResult] = T.persistent{ + def compile: T[mill.scalalib.api.CompilationResult] = T.persistent { zincWorker.worker().compileJava( upstreamCompileOutput(), allSourceFiles().map(_.path), compileClasspath().map(_.path), - javacOptions() + javacOptions(), + T.ctx().reporter ) } diff --git a/scalalib/src/ScalaModule.scala b/scalalib/src/ScalaModule.scala index 0ebd5700..5c7a4c97 100644 --- a/scalalib/src/ScalaModule.scala +++ b/scalalib/src/ScalaModule.scala @@ -10,6 +10,9 @@ import mill.scalalib.api.Util.isDotty import Lib._ import mill.api.Loose.Agg import mill.api.DummyInputStream +import sbt.internal.inc.ManagedLoggedReporter +import sbt.internal.util.{ConsoleOut, MainAppender} +import sbt.util.LogExchange /** * Core configuration required to compile a single Scala compilation target @@ -128,7 +131,8 @@ trait ScalaModule extends JavaModule { outer => resolveDeps(T.task{runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})() } - override def compile: T[mill.scalalib.api.CompilationResult] = T.persistent{ + override def compile: T[mill.scalalib.api.CompilationResult] = T.persistent { + zincWorker.worker().compileMixed( upstreamCompileOutput(), allSourceFiles().map(_.path), @@ -139,6 +143,7 @@ trait ScalaModule extends JavaModule { outer => scalacOptions(), scalaCompilerClasspath().map(_.path), scalacPluginClasspath().map(_.path), + T.ctx().reporter ) } diff --git a/scalalib/worker/src/ZincWorkerImpl.scala b/scalalib/worker/src/ZincWorkerImpl.scala index cf37812c..bd61b440 100644 --- a/scalalib/worker/src/ZincWorkerImpl.scala +++ b/scalalib/worker/src/ZincWorkerImpl.scala @@ -4,15 +4,15 @@ import java.io.File import java.util.Optional import scala.ref.WeakReference - import mill.api.Loose.Agg -import mill.api.{KeyedLockedCache, PathRef} +import mill.api.{CompilationAnalysis, KeyedLockedCache, PathRef} import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} import mill.scalalib.api.Util.{grepJar, isDotty, scalaBinaryVersion} import sbt.internal.inc._ import sbt.internal.util.{ConsoleOut, MainAppender} import sbt.util.LogExchange import mill.scalalib.api.{CompilationResult, ZincWorkerApi} +import upickle.core.Visitor case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup { override def analysis(classpathEntry: File): Optional[CompileAnalysis] = am(classpathEntry) @@ -61,7 +61,7 @@ class ZincWorkerImpl(compilerBridge: Either[ scalaVersion, scalaOrganization, compilerClasspath, - scalacPluginClasspath, + scalacPluginClasspath ) { compilers: Compilers => val scaladocClass = compilers.scalac().scalaInstance().loader().loadClass("scala.tools.nsc.ScalaDoc") val scaladocMethod = scaladocClass.getMethod("process", classOf[Array[String]]) @@ -155,20 +155,23 @@ class ZincWorkerImpl(compilerBridge: Either[ def compileJava(upstreamCompileOutput: Seq[CompilationResult], sources: Agg[os.Path], compileClasspath: Agg[os.Path], - javacOptions: Seq[String]) + javacOptions: Seq[String], + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] = { for(res <- compileJava0( upstreamCompileOutput.map(c => (c.analysisFile, c.classes.path)), sources, compileClasspath, - javacOptions + javacOptions, + reporter )) yield CompilationResult(res._1, PathRef(res._2)) } def compileJava0(upstreamCompileOutput: Seq[(os.Path, os.Path)], sources: Agg[os.Path], compileClasspath: Agg[os.Path], - javacOptions: Seq[String]) + javacOptions: Seq[String], + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[(os.Path, os.Path)] = { compileInternal( upstreamCompileOutput, @@ -176,7 +179,8 @@ class ZincWorkerImpl(compilerBridge: Either[ compileClasspath, javacOptions, scalacOptions = Nil, - javaOnlyCompilers + javaOnlyCompilers, + reporter ) } @@ -188,7 +192,8 @@ class ZincWorkerImpl(compilerBridge: Either[ scalaOrganization: String, scalacOptions: Seq[String], compilerClasspath: Agg[os.Path], - scalacPluginClasspath: Agg[os.Path]) + scalacPluginClasspath: Agg[os.Path], + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] = { for (res <- compileMixed0( @@ -200,7 +205,8 @@ class ZincWorkerImpl(compilerBridge: Either[ scalaOrganization, scalacOptions, compilerClasspath, - scalacPluginClasspath + scalacPluginClasspath, + reporter )) yield CompilationResult(res._1, PathRef(res._2)) } @@ -212,13 +218,14 @@ class ZincWorkerImpl(compilerBridge: Either[ scalaOrganization: String, scalacOptions: Seq[String], compilerClasspath: Agg[os.Path], - scalacPluginClasspath: Agg[os.Path]) + scalacPluginClasspath: Agg[os.Path], + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[(os.Path, os.Path)] = { withCompilers( scalaVersion, scalaOrganization, compilerClasspath, - scalacPluginClasspath, + scalacPluginClasspath ) {compilers: Compilers => compileInternal( upstreamCompileOutput, @@ -226,7 +233,8 @@ class ZincWorkerImpl(compilerBridge: Either[ compileClasspath, javacOptions, scalacOptions = scalacPluginClasspath.map(jar => s"-Xplugin:$jar").toSeq ++ scalacOptions, - compilers + compilers, + reporter ) } } @@ -298,7 +306,8 @@ class ZincWorkerImpl(compilerBridge: Either[ compileClasspath: Agg[os.Path], javacOptions: Seq[String], scalacOptions: Seq[String], - compilers: Compilers) + compilers: Compilers, + reporter: Option[ManagedLoggedReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[(os.Path, os.Path)] = { os.makeDir.all(ctx.dest) @@ -312,7 +321,10 @@ class ZincWorkerImpl(compilerBridge: Either[ LogExchange.bindLoggerAppenders(id, (consoleAppender -> sbt.util.Level.Info) :: Nil) l } - + val newReporter = reporter match { + case None => new ManagedLoggedReporter(10, logger) + case r: Option[ManagedLoggedReporter] => r.get + } val analysisMap0 = upstreamCompileOutput.map(_.swap).toMap def analysisMap(f: File): Optional[CompileAnalysis] = { @@ -350,7 +362,7 @@ class ZincWorkerImpl(compilerBridge: Either[ zincIOFile, new FreshCompilerCache, IncOptions.of(), - new ManagedLoggedReporter(10, logger), + newReporter,//new ManagedLoggedReporter(10, logger), None, Array() ), @@ -372,7 +384,6 @@ class ZincWorkerImpl(compilerBridge: Either[ newResult.setup() ) ) - mill.api.Result.Success((zincFile, classesDir)) }catch{case e: CompileFailed => mill.api.Result.Failure(e.toString)} } diff --git a/scratch/build.sc b/scratch/build.sc index 817f68e4..c36b554f 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,9 +1,9 @@ import mill._, scalalib._ -import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-7-be21ae-DIRTY1fe41d7a` +//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-7-be21ae-DIRTY1fe41d7a` object mill_exercise extends ScalaModule { def scalaVersion = "2.12.8" - + def mainClass = Some("mill_exercise.Compiler") def ivyDeps = Agg( ivy"org.scalameta::metals:0.5.2", ivy"org.scalameta::scalameta:4.1.9", -- cgit v1.2.3 From 467aa455740bcffe16f8cd660f4f8683356e1bf8 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 10 Jul 2019 17:18:03 +0200 Subject: Fixed compilation error, removed print statements and unnecessary code. --- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 4 - .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 109 +-------------------- main/core/src/eval/Evaluator.scala | 2 - 3 files changed, 1 insertion(+), 114 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 31900211..4bc94b41 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -20,9 +20,7 @@ class BspLoggedReporter(client: bsp.BuildClient, override def logError(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) - println("Sent diagnostics to the client") super.logError(problem) - println("Logged the error") } override def logInfo(problem: Problem): Unit = { @@ -37,7 +35,6 @@ class BspLoggedReporter(client: bsp.BuildClient, def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): bsp.PublishDiagnosticsParams = { - println("Problem: " + problem) val sourceFile = problem.position().sourceFile().asScala val start = new bsp.Position( problem.position.startLine.asScala.getOrElse(0), @@ -60,7 +57,6 @@ class BspLoggedReporter(client: bsp.BuildClient, targetId, List(diagnostic).asJava, true) if (originId.nonEmpty) { params.setOriginId(originId.get) } - println("Diagnostics: " + params) params } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 5d79ee76..add5da5a 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -223,107 +223,7 @@ class MillBuildServer(evaluator: Evaluator, future.complete(getResources) future } - - private[this] def getErrorCode(file: File, start: bsp.Position, end: bsp.Position): String = { - - val source = Source.fromFile(file) - source.close() - val lines = source.getLines.toSeq - val code = lines(start.getLine).substring(start.getCharacter) + - lines.take(start.getLine - 1).takeRight(lines.length - end.getLine - 1).mkString("\n") + - lines(end.getLine).substring(0, end.getCharacter + 1) - code - } - - def getDiagnosticsFromFile(analysisFile: os.Path, targetId: BuildTargetIdentifier, originId: Option[String]): - Seq[PublishDiagnosticsParams] = { - val analysisStore: AnalysisStore = FileAnalysisStore.getDefault(analysisFile.toIO) - analysisStore.get.asScala match { - case contents: AnalysisContents => - val sourceInfoMap: Map[File, SourceInfo] = contents.getAnalysis.readSourceInfos.getAllSourceInfos.asScala - var diagnosticsParams = Seq.empty[PublishDiagnosticsParams] - for ( (file, sourceInfo) <- sourceInfoMap) { - var diagnostics = List.empty[Diagnostic] - for (problem <- sourceInfo.getReportedProblems) { - val start = new bsp.Position( - problem.position.startLine.asScala.getOrElse(0), - problem.position.startOffset.asScala.getOrElse(0)) - val end = new bsp.Position( - problem.position.endLine.asScala.getOrElse(0), - problem.position.endOffset.asScala.getOrElse(0)) - val diagnostic = new Diagnostic(new Range(start, end), problem.message) - diagnostic.setCode(getErrorCode(file, start, end)) - diagnostic.setSource("compiler from mill") - - diagnostic.setSeverity( problem.severity match { - case Severity.Info => DiagnosticSeverity.INFORMATION - case Severity.Error => DiagnosticSeverity.ERROR - case Severity.Warn => DiagnosticSeverity.WARNING - } - ) - diagnostics ++= List(diagnostic) - } - val params = new PublishDiagnosticsParams(new TextDocumentIdentifier(file.toURI.toString), - targetId, diagnostics.asJava, true) - if (originId.nonEmpty) { params.setOriginId(originId.get) } - diagnosticsParams ++= Seq(params) - } - diagnosticsParams - case None => Seq.empty[PublishDiagnosticsParams] - } - } - - def getSourceFileCompileErrors(problems: Seq[Problem]): Map[File, Array[Problem]] = { - val problemsMap = Map.empty[File, Array[Problem]] - - for (problem <- problems) { - try { - val sourceFile = problem.position.sourceFile.get - if (problemsMap.contains(sourceFile)) { - problemsMap(sourceFile) = problemsMap(sourceFile) ++ Array(problem) - } else { - problemsMap(sourceFile) = Array(problem) - } - - } catch { - case e: Exception => - } - } - problemsMap - } - - def getDiagnostics(problems: Array[Problem], targetId: BuildTargetIdentifier, originId: Option[String]): - Seq[PublishDiagnosticsParams] = { - var diagnosticsParams = Seq.empty[PublishDiagnosticsParams] - for (( sourceFile, problems ) <- getSourceFileCompileErrors(problems)) { - var diagnostics = Seq.empty[Diagnostic] - for (problem <- problems) { - val start = new bsp.Position( - problem.position.startLine.asScala.getOrElse(0), - problem.position.startOffset.asScala.getOrElse(0)) - val end = new bsp.Position( - problem.position.endLine.asScala.getOrElse(0), - problem.position.endOffset.asScala.getOrElse(0)) - val diagnostic = new Diagnostic(new Range(start, end), problem.message) - diagnostic.setCode(getErrorCode(sourceFile, start, end)) - diagnostic.setSource("compiler from mill") - diagnostic.setSeverity( problem.severity match { - case Severity.Info => DiagnosticSeverity.INFORMATION - case Severity.Error => DiagnosticSeverity.ERROR - case Severity.Warn => DiagnosticSeverity.WARNING - } - ) - diagnostics ++= List(diagnostic) - } - val params = new PublishDiagnosticsParams(new TextDocumentIdentifier(sourceFile.toPath.toAbsolutePath.toUri.toString), - targetId, diagnostics.asJava, true) - - if (originId.nonEmpty) { params.setOriginId(originId.get) } - diagnosticsParams ++= Seq(params) - } - diagnosticsParams - } - + def getOriginId(params: CompileParams): Option[String] = { try { Option(params.getOriginId) @@ -332,13 +232,6 @@ class MillBuildServer(evaluator: Evaluator, } } - def sendCompilationDiagnostics(problems: Array[Problem], targetId: BuildTargetIdentifier, compileParams: CompileParams) = { - for (publishDiagnosticsParams <- - getDiagnostics(problems, targetId, getOriginId(compileParams))) { - client.onBuildPublishDiagnostics(publishDiagnosticsParams) - } - } - def getCompilationLogger: ManagedLogger = { val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( mill.util.DummyLogger.outputStream diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 22132a8f..99befd0d 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -20,7 +20,6 @@ case class Labelled[T](task: NamedTask[T], segments: Segments){ def format = task match{ case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) - case t: PersistentArgs[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) case _ => None } def writer = task match{ @@ -330,7 +329,6 @@ case class Evaluator(home: os.Path, env, reporter //new ManagedLoggedReporter(10, logger) ) - println("Reporter: " + reporter) val out = System.out val in = System.in val err = System.err -- cgit v1.2.3 From 0735363de43b9acf671ebe8117d2d6a7eab3afe9 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 10 Jul 2019 17:36:27 +0200 Subject: Fixed issue with retrieving line and offset of errors found during compilation. --- contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala | 12 ++++++++---- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 10 +--------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 4bc94b41..5c7d5e5d 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -35,13 +35,17 @@ class BspLoggedReporter(client: bsp.BuildClient, def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): bsp.PublishDiagnosticsParams = { + println("Line: " + problem.position.line) + println("Offset: " + problem.position.offset) + println("pointer: " + problem.position.pointer) + println("pointer space: " + problem.position.pointerSpace) val sourceFile = problem.position().sourceFile().asScala val start = new bsp.Position( - problem.position.startLine.asScala.getOrElse(0), - problem.position.startOffset.asScala.getOrElse(0)) + problem.position.startLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), + problem.position.startOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) val end = new bsp.Position( - problem.position.endLine.asScala.getOrElse(0), - problem.position.endOffset.asScala.getOrElse(0)) + problem.position.endLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), + problem.position.endOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) diagnostic.setCode(problem.position.lineContent) diagnostic.setSource("compiler from mill") diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index add5da5a..b222a5f6 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -223,7 +223,7 @@ class MillBuildServer(evaluator: Evaluator, future.complete(getResources) future } - + def getOriginId(params: CompileParams): Option[String] = { try { Option(params.getOriginId) @@ -242,10 +242,8 @@ class MillBuildServer(evaluator: Evaluator, l } - //TODO: send task notifications - start, progress and finish //TODO: if the client wants to give compilation arguments and the module // already has some from the build file, what to do? - //TODO: Send notification if compilation fails override def buildTargetCompile(compileParams: CompileParams): CompletableFuture[CompileResult] = { def getCompileResult: CompileResult = { @@ -276,12 +274,6 @@ class MillBuildServer(evaluator: Evaluator, compileTime += result.timings.map(timingTuple => timingTuple._2).sum var statusCode = StatusCode.OK -// result.results(compileTask) match { -// case r: Failing[CompilationResult] => -// statusCode = StatusCode.ERROR -// numFailures += result.failing.keyCount -// case default => -// } if (result.failing.keyCount > 0) { statusCode = StatusCode.ERROR numFailures += result.failing.keyCount -- cgit v1.2.3 From 0096a4171bd0025ea390973a8ecd3079e8cf9895 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 10 Jul 2019 17:53:15 +0200 Subject: Added the custom bsp reporter to the evaluation of the run task in buildTargetRun. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index b222a5f6..59ac8e57 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -315,7 +315,11 @@ class MillBuildServer(evaluator: Evaluator, // case d: ScalaMainClass => millEvaluator.evaluate(Strict.Agg(module.runMain(d.getClass, d.getArguments.asScala))) // case default => millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" ")))) // } - val runResult = millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" ")))) + val runResult = millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" "))), + Option(new BspLoggedReporter(client, + runParams.getTarget, + Option.empty[String], + 10, getCompilationLogger))) if (runResult.failing.keyCount > 0) { new RunResult(StatusCode.ERROR) } else { -- cgit v1.2.3 From 8287563ea4ee743a483dc6c1e4f25dd219a11e59 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 11 Jul 2019 09:32:54 +0200 Subject: Fixed compilation error --- scalalib/worker/src/ZincWorkerImpl.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scalalib/worker/src/ZincWorkerImpl.scala b/scalalib/worker/src/ZincWorkerImpl.scala index bd61b440..7c696ccc 100644 --- a/scalalib/worker/src/ZincWorkerImpl.scala +++ b/scalalib/worker/src/ZincWorkerImpl.scala @@ -5,7 +5,7 @@ import java.util.Optional import scala.ref.WeakReference import mill.api.Loose.Agg -import mill.api.{CompilationAnalysis, KeyedLockedCache, PathRef} +import mill.api.{KeyedLockedCache, PathRef} import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} import mill.scalalib.api.Util.{grepJar, isDotty, scalaBinaryVersion} import sbt.internal.inc._ -- cgit v1.2.3 From cf2ddf2f9da40007847ca69dc521098727b98f6d Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 11 Jul 2019 13:18:12 +0200 Subject: Fixed bugs in getting target capabilities and dependencies. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 3 +++ contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 7 ++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 59ac8e57..3a694599 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -182,6 +182,9 @@ class MillBuildServer(evaluator: Evaluator, Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.compileIvyDeps), + Agg.empty[PathRef]) ++ + evaluateInformativeTask(evaluator, + millModule.unmanagedClasspath, Agg.empty[PathRef]) millModule match { case m: ScalaModule => sources ++= evaluateInformativeTask(evaluator, diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index a3202aab..1917f732 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -65,11 +65,8 @@ object ModuleUtils { } def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { - val canRun = getTaskResult(evaluator, module.finalMainClassOpt) match { - case result: Result.Success[Any] => result.asSuccess.get.value match { - case _: Right[String, String] => true - case _: Left[String, String] => false - } + val canRun = getTaskResult(evaluator, module.finalMainClass) match { + case result: Result.Success[String] => true case default => false } val canTest = module match { -- cgit v1.2.3 From 9c39dd6776944b15433f44c5c4fba994cdd7d152 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Fri, 12 Jul 2019 12:31:35 +0200 Subject: Added support for merging compile parameters from the mill build file with the compile parameters specified through bsp --- .bsp/mill-bsp.json | 1 + .../bsp/src/mill/contrib/MainMillBuildServer.scala | 40 +++++---- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 14 ++-- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 33 ++++---- .../bsp/src/mill/contrib/bsp/TaskParameters.scala | 97 ++++++++++++++++++++++ main/api/src/mill/api/Ctx.scala | 3 +- main/core/src/eval/Evaluator.scala | 26 ++++-- scalalib/src/JavaModule.scala | 2 +- scalalib/src/ScalaModule.scala | 2 +- scratch/build.sc | 14 +++- 10 files changed, 185 insertions(+), 47 deletions(-) create mode 100644 .bsp/mill-bsp.json create mode 100644 contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala diff --git a/.bsp/mill-bsp.json b/.bsp/mill-bsp.json new file mode 100644 index 00000000..0c4587f9 --- /dev/null +++ b/.bsp/mill-bsp.json @@ -0,0 +1 @@ +{"name":"mill-bsp","argv":["/home/alexandra/mill-release", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 7caaf5d3..b9e8ee8a 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -7,7 +7,7 @@ import java.nio.file.FileAlreadyExistsException import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, WorkspaceBuildTargetsResult} +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, CompileParams, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, WorkspaceBuildTargetsResult} import mill._ import mill.api.Strict import mill.contrib.bsp.{BspLoggedReporter, MillBuildServer, ModuleUtils} @@ -120,8 +120,9 @@ object MainMillBuildServer extends ExternalModule { * server */ def startServer(ev: Evaluator): Command[Unit] = T.command { - - val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) + val eval = new Evaluator(ev.home, ev.outPath, ev.externalOutPath, ev.rootModule, ev.log, ev.classLoaderSig, + ev.workerCache, ev.env, false) + val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) val executor = Executors.newCachedThreadPool() val stdin = System.in @@ -152,14 +153,26 @@ object MainMillBuildServer extends ExternalModule { } def experiment(ev: Evaluator): Command[Unit] = T.command { - val millServer = new mill.contrib.bsp.MillBuildServer(ev, bspVersion, version, languages) + val eval = new Evaluator(ev.home, ev.outPath, ev.externalOutPath, ev.rootModule, ev.log, ev.classLoaderSig, + ev.workerCache, ev.env, false) + val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) val client = new BuildClient { var diagnostics = List.empty[PublishDiagnosticsParams] - override def onBuildShowMessage(params: ShowMessageParams): Unit = ??? - override def onBuildLogMessage(params: LogMessageParams): Unit = ??? - override def onBuildTaskStart(params: TaskStartParams): Unit = ??? - override def onBuildTaskProgress(params: TaskProgressParams): Unit = ??? - override def onBuildTaskFinish(params: TaskFinishParams): Unit = ??? + override def onBuildShowMessage(params: ShowMessageParams): Unit = { + + } + override def onBuildLogMessage(params: LogMessageParams): Unit = { + + } + override def onBuildTaskStart(params: TaskStartParams): Unit = { + + } + override def onBuildTaskProgress(params: TaskProgressParams): Unit = { + + } + override def onBuildTaskFinish(params: TaskFinishParams): Unit = { + + } override def onBuildPublishDiagnostics( params: PublishDiagnosticsParams ): Unit = { @@ -168,12 +181,11 @@ object MainMillBuildServer extends ExternalModule { override def onBuildTargetDidChange(params: DidChangeBuildTarget): Unit = ??? } + millServer.client = client for (module <- millServer.millModules) { - ev.evaluate(Strict.Agg(module.compile), Option(new BspLoggedReporter(client, - millServer.moduleToTargetId(module), - Option.empty[String], - 10, millServer.getCompilationLogger))) - //println("Diagnostics: " + client.diagnostics) + if (millServer.moduleToTarget(module).getDisplayName == "random") { + println(millServer.buildTargetCompile(new CompileParams(List(millServer.moduleToTargetId(module)).asJava)).get) + } } } diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 5c7d5e5d..6d92cd16 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -24,6 +24,7 @@ class BspLoggedReporter(client: bsp.BuildClient, } override def logInfo(problem: Problem): Unit = { + logger.info("Problem: " + problem.toString) client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) super.logInfo(problem) } @@ -33,12 +34,10 @@ class BspLoggedReporter(client: bsp.BuildClient, super.logWarning(problem) } + //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 def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): bsp.PublishDiagnosticsParams = { - println("Line: " + problem.position.line) - println("Offset: " + problem.position.offset) - println("pointer: " + problem.position.pointer) - println("pointer space: " + problem.position.pointerSpace) val sourceFile = problem.position().sourceFile().asScala val start = new bsp.Position( problem.position.startLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), @@ -55,9 +54,12 @@ class BspLoggedReporter(client: bsp.BuildClient, case Severity.Warn => bsp.DiagnosticSeverity.WARNING } ) - + val textDocument = sourceFile.getOrElse(None) match { + case None => targetId.getUri + case f: File => f.toPath.toUri.toString + } val params = new bsp.PublishDiagnosticsParams( - new bsp.TextDocumentIdentifier(sourceFile.getOrElse(new File(targetId.getUri)).toPath.toAbsolutePath.toUri.toString), + new bsp.TextDocumentIdentifier(textDocument), targetId, List(diagnostic).asJava, true) if (originId.nonEmpty) { params.setOriginId(originId.get) } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 3a694599..b4635558 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -235,9 +235,17 @@ class MillBuildServer(evaluator: Evaluator, } } + def getArguments(params: CompileParams) : Option[Seq[String]] = { + try { + Option(params.getArguments.asScala) + } catch { + case e: Exception => Option.empty[Seq[String]] + } + } + def getCompilationLogger: ManagedLogger = { val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( - mill.util.DummyLogger.outputStream + System.out )) val l = LogExchange.logger("Hello") LogExchange.unbindLoggerAppenders("Hello") @@ -250,13 +258,12 @@ class MillBuildServer(evaluator: Evaluator, override def buildTargetCompile(compileParams: CompileParams): CompletableFuture[CompileResult] = { def getCompileResult: CompileResult = { - + val params = TaskParameters.fromCompileParams(compileParams) var numFailures = 0 var compileTime = 0 - for (targetId <- compileParams.getTargets.asScala) { + for (targetId <- params.getTargets) { if (moduleToTarget(targetIdToModule(targetId)).getCapabilities.getCanCompile) { val millModule = targetIdToModule(targetId) - //millModule.javacOptions = compileParams.getArguments.asScala val compileTask = millModule.compile // send notification to client that compilation of this target started @@ -271,7 +278,8 @@ class MillBuildServer(evaluator: Evaluator, Option(new BspLoggedReporter(client, targetId, getOriginId(compileParams), - 10, getCompilationLogger))) + 10, getCompilationLogger)), + params.getArguments.getOrElse(Seq.empty[String])) val endTime = System.currentTimeMillis() compileTime += result.timings.map(timingTuple => timingTuple._2).sum @@ -312,16 +320,13 @@ class MillBuildServer(evaluator: Evaluator, override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = { def getRunResult: RunResult = { - val module = targetIdToModule(runParams.getTarget) - val args = runParams.getArguments -// val runResult = runParams.getData() match { -// case d: ScalaMainClass => millEvaluator.evaluate(Strict.Agg(module.runMain(d.getClass, d.getArguments.asScala))) -// case default => millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" ")))) -// } - val runResult = millEvaluator.evaluate(Strict.Agg(module.run(args.asScala.mkString(" "))), + val params = TaskParameters.fromRunParams(runParams) + val module = targetIdToModule(params.getTargets.head) + val args = params.getArguments.getOrElse(Seq.empty[String]) + val runResult = millEvaluator.evaluate(Strict.Agg(module.run(args.mkString(" "))), Option(new BspLoggedReporter(client, - runParams.getTarget, - Option.empty[String], + params.getTargets.head, + params.getOriginId, 10, getCompilationLogger))) if (runResult.failing.keyCount > 0) { new RunResult(StatusCode.ERROR) diff --git a/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala b/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala new file mode 100644 index 00000000..b020b498 --- /dev/null +++ b/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala @@ -0,0 +1,97 @@ +package mill.contrib.bsp +import java.util + +import scala.collection.JavaConverters._ +import scala.compat.java8.OptionConverters._ +import ch.epfl.scala.bsp4j.{BuildTargetIdentifier, CompileParams, RunParams, TestParams} + +trait Parameters { + def getTargets: List[BuildTargetIdentifier] + + def getArguments: Option[Seq[String]] + + def getOriginId: Option[String] +} + +case class CParams(compileParams: CompileParams) extends Parameters { + + override def getTargets: List[BuildTargetIdentifier] = { + compileParams.getTargets.asScala.toList + } + + override def getArguments: Option[Seq[String]] = { + try { + Option(compileParams.getArguments.asScala) + }catch { + case e: Exception => Option.empty[Seq[String]] + } + } + + override def getOriginId: Option[String] = { + try { + Option(compileParams.getOriginId) + }catch { + case e: Exception => Option.empty[String] + } + } + +} +case class RParams(runParams: RunParams) extends Parameters { + + override def getTargets: List[BuildTargetIdentifier] = { + List(runParams.getTarget) + } + + override def getArguments: Option[Seq[String]] = { + try { + Option(runParams.getArguments.asScala) + }catch { + case e: Exception => Option.empty[Seq[String]] + } + } + + override def getOriginId: Option[String] = { + try { + Option(runParams.getOriginId) + }catch { + case e: Exception => Option.empty[String] + } + } + +} +case class TParams(testParams: TestParams) extends Parameters { + + override def getTargets: List[BuildTargetIdentifier] = { + testParams.getTargets.asScala.toList + } + + override def getArguments: Option[Seq[String]] = { + try { + Option(testParams.getArguments.asScala) + }catch { + case e: Exception => Option.empty[Seq[String]] + } + } + + override def getOriginId: Option[String] = { + try { + Option(testParams.getOriginId) + }catch { + case e: Exception => Option.empty[String] + } + } +} + +object TaskParameters { + def fromCompileParams(compileParams: CompileParams): Parameters = { + CParams(compileParams) + } + + def fromRunParams(runParams: RunParams): Parameters = { + RParams(runParams) + } + + def fromTestParams(testParams: TestParams): Parameters = { + TParams(testParams) + } +} \ No newline at end of file diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 7d081d6a..0799d887 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -61,7 +61,8 @@ class Ctx( val log: Logger, val home: os.Path, val env: Map[String, String], - val reporter: Option[ManagedLoggedReporter] + val reporter: Option[ManagedLoggedReporter], + val compileArguments: Seq[String] = Seq.empty[String] ) extends Ctx.Dest with Ctx.Log diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 99befd0d..55802546 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -12,7 +12,7 @@ import mill.api.Result.{Aborted, OuterStack, Success} import mill.util import mill.util._ import mill.api.Strict.Agg -import sbt.internal.inc.ManagedLoggedReporter +import sbt.internal.inc.{CompilerArguments, ManagedLoggedReporter} import sbt.internal.util.{ConsoleOut, MainAppender} import sbt.util.LogExchange @@ -42,9 +42,11 @@ case class Evaluator(home: os.Path, val classLoaderSignHash = classLoaderSig.hashCode() - def evaluate(goals: Agg[Task[_]], reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter]): Evaluator.Results = { + def evaluate(goals: Agg[Task[_]], + reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter], + compileArguments: Seq[String] = Seq.empty[String]): Evaluator.Results = { os.makeDir.all(outPath) - + println("Reporter: " + reporter) val (sortedGroups, transitive) = Evaluator.plan(rootModule, goals) val evaluated = new Agg.Mutable[Task[_]] @@ -69,7 +71,8 @@ case class Evaluator(home: os.Path, group, results, counterMsg, - reporter + reporter, + compileArguments ) someTaskFailed = someTaskFailed || newResults.exists(task => !task._2.isInstanceOf[Success[_]]) @@ -115,7 +118,8 @@ case class Evaluator(home: os.Path, group: Agg[Task[_]], results: collection.Map[Task[_], Result[(Any, Int)]], counterMsg: String, - reporter: Option[ManagedLoggedReporter] + reporter: Option[ManagedLoggedReporter], + compilerArguments: Seq[String] ): (collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean) = { val externalInputsHash = scala.util.hashing.MurmurHash3.orderedHash( @@ -138,7 +142,8 @@ case class Evaluator(home: os.Path, paths = None, maybeTargetLabel = None, counterMsg = counterMsg, - reporter + reporter, + compilerArguments ) (newResults, newEvaluated, false) case Right(labelledNamedTask) => @@ -191,7 +196,8 @@ case class Evaluator(home: os.Path, paths = Some(paths), maybeTargetLabel = Some(msgParts.mkString), counterMsg = counterMsg, - reporter + reporter, + compilerArguments ) newResults(labelledNamedTask.task) match{ @@ -266,7 +272,8 @@ case class Evaluator(home: os.Path, paths: Option[Evaluator.Paths], maybeTargetLabel: Option[String], counterMsg: String, - reporter: Option[ManagedLoggedReporter]): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { + reporter: Option[ManagedLoggedReporter], + compileArguments: Seq[String]): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { val newEvaluated = mutable.Buffer.empty[Task[_]] @@ -327,7 +334,8 @@ case class Evaluator(home: os.Path, multiLogger, home, env, - reporter //new ManagedLoggedReporter(10, logger) + reporter, + compileArguments//new ManagedLoggedReporter(10, logger) ) val out = System.out val in = System.in diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index e6c47324..941f82e7 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -221,7 +221,7 @@ trait JavaModule extends mill.Module with TaskModule with GenIdeaModule { outer upstreamCompileOutput(), allSourceFiles().map(_.path), compileClasspath().map(_.path), - javacOptions(), + javacOptions() ++ T.ctx.compileArguments, T.ctx().reporter ) } diff --git a/scalalib/src/ScalaModule.scala b/scalalib/src/ScalaModule.scala index 5c7a4c97..0c3684c6 100644 --- a/scalalib/src/ScalaModule.scala +++ b/scalalib/src/ScalaModule.scala @@ -140,7 +140,7 @@ trait ScalaModule extends JavaModule { outer => javacOptions(), scalaVersion(), scalaOrganization(), - scalacOptions(), + scalacOptions() ++ T.ctx.compileArguments, scalaCompilerClasspath().map(_.path), scalacPluginClasspath().map(_.path), T.ctx().reporter diff --git a/scratch/build.sc b/scratch/build.sc index c36b554f..85228a20 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,5 +1,5 @@ import mill._, scalalib._ -//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-7-be21ae-DIRTY1fe41d7a` +//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-16-c95bc4-DIRTYd5dc7fa5` object mill_exercise extends ScalaModule { def scalaVersion = "2.12.8" @@ -28,3 +28,15 @@ object mill_exercise extends ScalaModule { def testFrameworks = Seq("org.scalatest.tools.Framework") } } + +object random extends SbtModule { + + def scalacOptions = Seq( + //"-Ywarn-unused", + "-Ylog-classpath" + ) + + def scalaVersion = "2.12.8" + + def ivyDeps = Agg(ivy"ch.epfl.scala:bsp4j:2.0.0-M3") +} -- cgit v1.2.3 From 2be400138fb32a63a3bb05e6fdec31508673331b Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Fri, 12 Jul 2019 17:52:57 +0200 Subject: Partially fixed test request by implementing a test reporter, adding it to each task context and sending it to the test task. Implemented BspContext to hold information about the test reporter and the additional bsp compiler arguments. Made a common data structure for different bsp parameters - TaskParameters. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 48 +-------- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 1 + .../bsp/src/mill/contrib/bsp/BspTestReporter.scala | 98 ++++++++++++++++++ .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 113 ++++++++++----------- main/api/src/mill/api/BspCompileArguments.scala | 13 +++ main/api/src/mill/api/BspContext.scala | 16 +++ main/api/src/mill/api/Ctx.scala | 2 +- main/api/src/mill/api/TestReporter.scala | 21 ++++ main/core/src/eval/Evaluator.scala | 16 +-- scalajslib/src/ScalaJSModule.scala | 3 +- scalalib/src/JavaModule.scala | 5 +- scalalib/src/ScalaModule.scala | 2 +- scalalib/src/TestRunner.scala | 13 ++- scalanativelib/src/ScalaNativeModule.scala | 3 +- 14 files changed, 233 insertions(+), 121 deletions(-) create mode 100644 contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala create mode 100644 main/api/src/mill/api/BspCompileArguments.scala create mode 100644 main/api/src/mill/api/BspContext.scala create mode 100644 main/api/src/mill/api/TestReporter.scala diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index b9e8ee8a..9627bff9 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -7,7 +7,7 @@ import java.nio.file.FileAlreadyExistsException import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, CompileParams, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, WorkspaceBuildTargetsResult} +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, CompileParams, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, TestParams, WorkspaceBuildTargetsResult} import mill._ import mill.api.Strict import mill.contrib.bsp.{BspLoggedReporter, MillBuildServer, ModuleUtils} @@ -183,8 +183,10 @@ object MainMillBuildServer extends ExternalModule { } millServer.client = client for (module <- millServer.millModules) { - if (millServer.moduleToTarget(module).getDisplayName == "random") { - println(millServer.buildTargetCompile(new CompileParams(List(millServer.moduleToTargetId(module)).asJava)).get) + if (millServer.moduleToTarget(module).getDisplayName == "test") { + println(eval.evaluate(Strict.Agg(module.asInstanceOf[TestModule].test() + )).rawValues) + println(millServer.buildTargetTest(new TestParams(List(millServer.moduleToTargetId(module)).asJava)).get) } } } @@ -204,44 +206,4 @@ object MainMillBuildServer extends ExternalModule { } } -} - -object foo extends mill.define.ExternalModule { - - implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() - lazy val millDiscover: Discover[foo.this.type] = Discover[this.type] - - object bar extends ScalaModule { - def scalaVersion = "2.12.4" - override def ivyDeps = Agg(ivy"org.scalameta::metals:0.5.2") - override def moduleDeps = Seq(baz) - object test extends TestModule { - override def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.6.0") - def testFrameworks: Target[Seq[String]] = Seq("utest.runner.Framework") - } - } - - object baz extends scalajslib.ScalaJSModule { - def scalaJSVersion = "1.0.0-M8" - def scalaVersion = "2.12.8" - - } - - object mill_exercise extends ScalaModule { - def scalaVersion = "2.12.8" - - override def scalacPluginIvyDeps = Agg(ivy"org.scala-lang:scala-compiler:2.12.8") - override def ivyDeps = Agg( - ivy"org.scala-lang:scala-reflect:2.12.8", - ivy"org.scalameta::metals:0.5.2" - ) - - object test extends Tests { - override def ivyDeps = Agg(//ivy"org.scalameta::metals:0.5.2", - ivy"com.lihaoyi::utest:0.6.0", - ivy"org.scalactic::scalactic:3.0.5") - - def testFrameworks: Target[Seq[String]] = Seq("utest.runner.Framework") - } - } } \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 6d92cd16..eda25566 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -3,6 +3,7 @@ package mill.contrib.bsp import java.io.File import ch.epfl.scala.{bsp4j => bsp} +import mill.api.BspContext import sbt.internal.inc.ManagedLoggedReporter import sbt.internal.inc.schema.Position import sbt.internal.util.ManagedLogger diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala new file mode 100644 index 00000000..23cadfcb --- /dev/null +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -0,0 +1,98 @@ +package mill.contrib.bsp + +import ch.epfl.scala.bsp4j._ +import mill.api.{BspContext, TestReporter} +import sbt.testing._ + + +class BspTestReporter( + client: BuildClient, + targetId: BuildTargetIdentifier, + taskId: TaskId, + arguments: Seq[String]) extends BspContext { + + var passed = 0 + var failed = 0 + var cancelled = 0 + var ignored = 0 + var skipped = 0 + var totalTime: Long = 0 + + override def args: Seq[String] = arguments + + override def logStart(event: Event): Unit = { + val taskStartParams = new TaskStartParams(taskId) + taskStartParams.setEventTime(System.currentTimeMillis()) + taskStartParams.setDataKind("test-started") + taskStartParams.setData(new TestStart(getDisplayName(event))) + taskStartParams.setMessage("Starting running: " + getDisplayName(event)) + client.onBuildTaskStart(taskStartParams) + } + + 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 + }) + taskFinishParams.setDataKind("test-finished") + val testFinish = new TestFinish( + getDisplayName(event), + event.status match { + case sbt.testing.Status.Success => + passed += 1 + TestStatus.PASSED + case sbt.testing.Status.Canceled => + cancelled += 1 + TestStatus.CANCELLED + case sbt.testing.Status.Error => + failed += 1 + TestStatus.FAILED + case sbt.testing.Status.Failure => + failed += 1 + TestStatus.FAILED + case sbt.testing.Status.Ignored => + ignored += 1 + TestStatus.IGNORED + case sbt.testing.Status.Skipped => + skipped += 1 + TestStatus.SKIPPED + case sbt.testing.Status.Pending => + skipped += 1 + TestStatus.SKIPPED //TODO: what to do here + }) + taskFinishParams.setData(testFinish) + taskFinishParams.setEventTime(System.currentTimeMillis()) + taskFinishParams.setMessage("Finished running: " + getDisplayName(event)) + + if (event.throwable.isDefined) { + val exception = event.throwable.get + taskFinishParams.setData( // send data about any potential exceptions thrown during testing + TestException(exception.getStackTrace.toString, + exception.getMessage, + exception.getClass.toString)) + } + client.onBuildTaskFinish(taskFinishParams) + } + + 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() + } + } + + def getTestReport: TestReport = { + val report = new TestReport(targetId, passed, failed, ignored, cancelled, skipped) + report.setTime(totalTime) + report + } + +} + +case class TestException(stackTrace: String, message: String, exClass: String) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index b4635558..f3644060 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -10,7 +10,7 @@ import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import ch.epfl.scala.{bsp4j => bsp} import mill.{scalalib, _} -import mill.api.{Loose, Result, Strict} +import mill.api.{BspContext, Loose, Result, Strict} import mill.contrib.bsp.ModuleUtils._ import mill.eval.Evaluator import mill.scalalib._ @@ -276,10 +276,16 @@ class MillBuildServer(evaluator: Evaluator, val result = millEvaluator.evaluate(Strict.Agg(compileTask), Option(new BspLoggedReporter(client, - targetId, - getOriginId(compileParams), - 10, getCompilationLogger)), - params.getArguments.getOrElse(Seq.empty[String])) + targetId, + getOriginId(compileParams), + 10, getCompilationLogger)), + new BspContext { + override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) + override def logStart(event: Event): Unit = {} + + override def logFinish(event: Event): Unit = {} + } + ) val endTime = System.currentTimeMillis() compileTime += result.timings.map(timingTuple => timingTuple._2).sum @@ -354,19 +360,21 @@ class MillBuildServer(evaluator: Evaluator, testReport } - private[this] def getStatusCode(results: Seq[TestRunner.Result]): StatusCode = { - if ( results.exists(res => res.status == "Failed") ) { + private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { + if (results.rawValues.exists(r => r.isInstanceOf[Result.Failure[Any]])) { StatusCode.ERROR - } else if ( results.exists(res => res.status == "Cancelled") ) { + } + + if (results.rawValues.contains(Result.Skipped) || results.rawValues.contains(Result.Aborted)) { StatusCode.CANCELLED - }else { - StatusCode.OK } + + StatusCode.OK } override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = { def getTestResult (implicit ctx: Ctx.Log with Ctx.Home ): TestResult = { - + val params = TaskParameters.fromTestParams(testParams) val argsMap = testParams.getData match { case scalaTestParams: ScalaTestParams => (for (testItem <- scalaTestParams.getTestClasses.asScala) @@ -381,7 +389,7 @@ class MillBuildServer(evaluator: Evaluator, case m: TestModule => val testModule = m.asInstanceOf[TestModule] val testTask = testModule.test(argsMap(targetId).mkString(" ")) - // send notification to client that testing of this target started + // notifying the client that the testing of this build target started val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) taskStartParams.setEventTime(System.currentTimeMillis()) taskStartParams.setMessage("Testing target: " + targetId) @@ -389,65 +397,46 @@ class MillBuildServer(evaluator: Evaluator, taskStartParams.setData(new TestTask(targetId)) client.onBuildTaskStart(taskStartParams) - val runClasspath = getTaskResult(millEvaluator, testModule.runClasspath) - val frameworks = getTaskResult(millEvaluator, testModule.testFrameworks) - val compilationResult = getTaskResult(millEvaluator, testModule.compile) - - (runClasspath, frameworks, compilationResult) match { - case (Success(classpath), Success(testFrameworks), Success(compResult)) => - val (msg, results) = TestRunner.runTests( - TestRunner.frameworks(testFrameworks.asInstanceOf[Seq[String]]), - classpath.asInstanceOf[Agg[PathRef]].map(_.path), - Agg(compResult.asInstanceOf[scalalib.api.CompilationResult].classes.path), - argsMap(targetId) - ) - val endTime = System.currentTimeMillis() - // send notification to client that testing of this target ended => test report - val statusCode = getStatusCode(results) - val taskFinishParams = new TaskFinishParams( - new TaskId(testTask.hashCode().toString), - getStatusCode(results) - ) - taskFinishParams.setEventTime(endTime) - taskFinishParams.setMessage("Finished testing target: " + - moduleToTarget(targetIdToModule(targetId)).getDisplayName) - taskFinishParams.setDataKind("test-report") - taskFinishParams.setData(getTestReport(targetId, results)) - client.onBuildTaskFinish(taskFinishParams) - statusCode match { - case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR - case default => - } - case default => val endTime = System.currentTimeMillis() - val taskFinishParams = new TaskFinishParams( - new TaskId(testTask.hashCode().toString), - StatusCode.ERROR - ) - taskFinishParams.setEventTime(endTime) - taskFinishParams.setMessage("Testing target: " + - moduleToTarget(targetIdToModule(targetId)).getDisplayName + - "failed because one of the tasks it depended on failed. There might" + - "be compilation errors.") - taskFinishParams.setDataKind("test-report") - taskFinishParams.setData( - new TestReport(targetId, 0, 0, 0, 0, 0) - ) - overallStatusCode = StatusCode.ERROR - client.onBuildTaskFinish(taskFinishParams) - buildTargetCompile(new CompileParams(List(targetId).asJava)) + val bspContext = new BspTestReporter( + client, targetId, + new TaskId(testTask.hashCode().toString), + Seq.empty[String]) + + val results = millEvaluator.evaluate( + Strict.Agg(testTask), + Option(new BspLoggedReporter(client, + targetId, + params.getOriginId, + 10, getCompilationLogger)), + bspContext) + val endTime = System.currentTimeMillis() + val statusCode = getStatusCode(results) + statusCode match { + case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR + case default => } - + // notifying the client that the testing of this build target ended + val taskFinishParams = new TaskFinishParams( + new TaskId(testTask.hashCode().toString), + statusCode + ) + taskFinishParams.setEventTime(endTime) + taskFinishParams.setMessage("Finished testing target: " + + moduleToTarget(targetIdToModule(targetId)).getDisplayName) + taskFinishParams.setDataKind("test-report") + taskFinishParams.setData(bspContext.getTestReport) + client.onBuildTaskFinish(taskFinishParams) case default => } } val testResult = new TestResult(overallStatusCode) - testParams.getOriginId match { - case id: String => + params.getOriginId match { + case None => testResult + case Some(id) => //TODO: Add the messages from mill to the data field? testResult.setOriginId(id) testResult - case default => testResult } } val future = new CompletableFuture[TestResult]() diff --git a/main/api/src/mill/api/BspCompileArguments.scala b/main/api/src/mill/api/BspCompileArguments.scala new file mode 100644 index 00000000..9cfdd500 --- /dev/null +++ b/main/api/src/mill/api/BspCompileArguments.scala @@ -0,0 +1,13 @@ +package mill.api + +class BspCompileArguments { + var arguments: Seq[String] = Seq.empty[String] + + def args: Seq[String] = { + arguments + } + + def setArgs(args: Seq[String]): Unit = { + arguments = args + } +} diff --git a/main/api/src/mill/api/BspContext.scala b/main/api/src/mill/api/BspContext.scala new file mode 100644 index 00000000..c93fbca1 --- /dev/null +++ b/main/api/src/mill/api/BspContext.scala @@ -0,0 +1,16 @@ +package mill.api + +import sbt.testing.Event + +trait BspContext extends BspCompileArguments with TestReporter + +object DummyBspContext extends BspContext { + override def args = Seq.empty[String] + override def logStart(event: Event): Unit = { + + } + + override def logFinish(event: Event): Unit = { + + } +} \ No newline at end of file diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 0799d887..439f08d3 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -62,7 +62,7 @@ class Ctx( val home: os.Path, val env: Map[String, String], val reporter: Option[ManagedLoggedReporter], - val compileArguments: Seq[String] = Seq.empty[String] + val bsp: BspContext ) extends Ctx.Dest with Ctx.Log diff --git a/main/api/src/mill/api/TestReporter.scala b/main/api/src/mill/api/TestReporter.scala new file mode 100644 index 00000000..b3d0e432 --- /dev/null +++ b/main/api/src/mill/api/TestReporter.scala @@ -0,0 +1,21 @@ +package mill.api + +import sbt.testing._ + +trait TestReporter { + def logStart(event: Event): Unit + + def logFinish(event: Event): Unit + + +} + +object DummyReporter extends TestReporter { + override def logStart(event: Event): Unit = { + + } + + override def logFinish(event: Event): Unit = { + + } +} diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 55802546..92f00b25 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -6,6 +6,7 @@ import scala.collection.JavaConverters._ import scala.collection.mutable import scala.util.control.NonFatal import ammonite.runtime.SpecialClassLoader +import mill.api.{BspContext, DummyBspContext} import mill.util.Router.EntryPoint import mill.define.{Ctx => _, _} import mill.api.Result.{Aborted, OuterStack, Success} @@ -14,6 +15,7 @@ import mill.util._ import mill.api.Strict.Agg import sbt.internal.inc.{CompilerArguments, ManagedLoggedReporter} import sbt.internal.util.{ConsoleOut, MainAppender} +import sbt.testing.Event import sbt.util.LogExchange case class Labelled[T](task: NamedTask[T], @@ -44,7 +46,7 @@ case class Evaluator(home: os.Path, def evaluate(goals: Agg[Task[_]], reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter], - compileArguments: Seq[String] = Seq.empty[String]): Evaluator.Results = { + bspContext: BspContext = DummyBspContext): Evaluator.Results = { os.makeDir.all(outPath) println("Reporter: " + reporter) val (sortedGroups, transitive) = Evaluator.plan(rootModule, goals) @@ -72,7 +74,7 @@ case class Evaluator(home: os.Path, results, counterMsg, reporter, - compileArguments + bspContext ) someTaskFailed = someTaskFailed || newResults.exists(task => !task._2.isInstanceOf[Success[_]]) @@ -119,7 +121,7 @@ case class Evaluator(home: os.Path, results: collection.Map[Task[_], Result[(Any, Int)]], counterMsg: String, reporter: Option[ManagedLoggedReporter], - compilerArguments: Seq[String] + bspContext: BspContext ): (collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean) = { val externalInputsHash = scala.util.hashing.MurmurHash3.orderedHash( @@ -143,7 +145,7 @@ case class Evaluator(home: os.Path, maybeTargetLabel = None, counterMsg = counterMsg, reporter, - compilerArguments + bspContext ) (newResults, newEvaluated, false) case Right(labelledNamedTask) => @@ -197,7 +199,7 @@ case class Evaluator(home: os.Path, maybeTargetLabel = Some(msgParts.mkString), counterMsg = counterMsg, reporter, - compilerArguments + bspContext ) newResults(labelledNamedTask.task) match{ @@ -273,7 +275,7 @@ case class Evaluator(home: os.Path, maybeTargetLabel: Option[String], counterMsg: String, reporter: Option[ManagedLoggedReporter], - compileArguments: Seq[String]): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { + bspContext: BspContext): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { val newEvaluated = mutable.Buffer.empty[Task[_]] @@ -335,7 +337,7 @@ case class Evaluator(home: os.Path, home, env, reporter, - compileArguments//new ManagedLoggedReporter(10, logger) + bspContext//new ManagedLoggedReporter(10, logger) ) val out = System.out val in = System.in diff --git a/scalajslib/src/ScalaJSModule.scala b/scalajslib/src/ScalaJSModule.scala index 7dba4e72..d970cc11 100644 --- a/scalajslib/src/ScalaJSModule.scala +++ b/scalajslib/src/ScalaJSModule.scala @@ -195,7 +195,8 @@ trait TestScalaJSModule extends ScalaJSModule with TestModule { _ => Seq(framework), runClasspath().map(_.path), Agg(compile().classes.path), - args + args, + T.ctx.bsp ) val res = TestModule.handleResults(doneMsg, results) // Hack to try and let the Node.js subprocess finish streaming it's stdout diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index 941f82e7..d7a0eb2c 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -221,7 +221,7 @@ trait JavaModule extends mill.Module with TaskModule with GenIdeaModule { outer upstreamCompileOutput(), allSourceFiles().map(_.path), compileClasspath().map(_.path), - javacOptions() ++ T.ctx.compileArguments, + javacOptions() ++ T.ctx.bsp.args, T.ctx().reporter ) } @@ -612,7 +612,8 @@ trait TestModule extends JavaModule with TaskModule { TestRunner.frameworks(testFrameworks()), runClasspath().map(_.path), Agg(compile().classes.path), - args + args, + T.ctx.bsp ) TestModule.handleResults(doneMsg, results) diff --git a/scalalib/src/ScalaModule.scala b/scalalib/src/ScalaModule.scala index 0c3684c6..85f1b6db 100644 --- a/scalalib/src/ScalaModule.scala +++ b/scalalib/src/ScalaModule.scala @@ -140,7 +140,7 @@ trait ScalaModule extends JavaModule { outer => javacOptions(), scalaVersion(), scalaOrganization(), - scalacOptions() ++ T.ctx.compileArguments, + scalacOptions() ++ T.ctx.bsp.args, scalaCompilerClasspath().map(_.path), scalacPluginClasspath().map(_.path), T.ctx().reporter diff --git a/scalalib/src/TestRunner.scala b/scalalib/src/TestRunner.scala index 42e65d63..c2017c77 100644 --- a/scalalib/src/TestRunner.scala +++ b/scalalib/src/TestRunner.scala @@ -1,11 +1,13 @@ package mill.scalalib import ammonite.util.Colors import mill.Agg +import mill.api.{DummyReporter, TestReporter} import mill.modules.Jvm import mill.scalalib.Lib.discoverTests import mill.util.{Ctx, PrintLogger} import mill.util.JsonFormatters._ import sbt.testing._ +import mill.scalalib.api._ import scala.collection.mutable object TestRunner { @@ -45,7 +47,8 @@ object TestRunner { frameworkInstances = TestRunner.frameworks(frameworks), entireClasspath = Agg.from(classpath.map(os.Path(_))), testClassfilePath = Agg(os.Path(testCp)), - args = arguments + args = arguments, + DummyReporter )(ctx) // Clear interrupted state in case some badly-behaved test suite @@ -66,7 +69,8 @@ object TestRunner { def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework], entireClasspath: Agg[os.Path], testClassfilePath: Agg[os.Path], - args: Seq[String]) + args: Seq[String], + testReporter: TestReporter) (implicit ctx: Ctx.Log with Ctx.Home): (String, Seq[mill.scalalib.TestRunner.Result]) = { //Leave the context class loader set and open so that shutdown hooks can access it Jvm.inprocess(entireClasspath, classLoaderOverrideSbtTesting = true, isolated = true, closeContextClassLoaderWhenDone = false, cl => { @@ -88,7 +92,10 @@ object TestRunner { while (taskQueue.nonEmpty){ val next = taskQueue.dequeue().execute( new EventHandler { - def handle(event: Event) = events.append(event) + def handle(event: Event) = { + testReporter.logStart(event) + events.append(event) + } }, Array( new Logger { diff --git a/scalanativelib/src/ScalaNativeModule.scala b/scalanativelib/src/ScalaNativeModule.scala index d6fb66bd..933e35fc 100644 --- a/scalanativelib/src/ScalaNativeModule.scala +++ b/scalanativelib/src/ScalaNativeModule.scala @@ -188,7 +188,8 @@ trait TestScalaNativeModule extends ScalaNativeModule with TestModule { testOute nativeFrameworks, runClasspath().map(_.path), Agg(compile().classes.path), - args + args, + T.ctx.bsp ) TestModule.handleResults(doneMsg, results) -- cgit v1.2.3 From d7c7217b66322d740d28f42cc161d36c76b34d71 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 15 Jul 2019 13:56:30 +0200 Subject: Refactored MillBuildServer by putting the future completion at the end of each method in a separate function which now also throws exceptions if a request is made before initialize. Also gave all JavaModules the run capability. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 4 +- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 146 +++++++++++---------- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 6 +- main/core/src/eval/Evaluator.scala | 2 +- scalalib/src/TestRunner.scala | 15 ++- 5 files changed, 92 insertions(+), 81 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 9627bff9..90aa5610 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -184,8 +184,8 @@ object MainMillBuildServer extends ExternalModule { millServer.client = client for (module <- millServer.millModules) { if (millServer.moduleToTarget(module).getDisplayName == "test") { - println(eval.evaluate(Strict.Agg(module.asInstanceOf[TestModule].test() - )).rawValues) +// println(eval.evaluate(Strict.Agg(module.asInstanceOf[TestModule].testLocal() +// )).rawValues) println(millServer.buildTargetTest(new TestParams(List(millServer.moduleToTargetId(module)).asJava)).get) } } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index f3644060..3aeaa81a 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -8,7 +8,6 @@ import java.util.concurrent.CompletableFuture import scala.compat.java8.OptionConverters._ import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ -import ch.epfl.scala.{bsp4j => bsp} import mill.{scalalib, _} import mill.api.{BspContext, Loose, Result, Strict} import mill.contrib.bsp.ModuleUtils._ @@ -27,6 +26,8 @@ import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.{Ctx, PrintLogger} import mill.define.{Discover, ExternalModule, Target, Task} +import org.eclipse.lsp4j.InitializeError +import org.eclipse.lsp4j.jsonrpc.messages.{ResponseError, ResponseErrorCode} import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange @@ -52,6 +53,7 @@ class MillBuildServer(evaluator: Evaluator, var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) + var initialized = false var clientInitialized = false val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { @@ -74,6 +76,7 @@ class MillBuildServer(evaluator: Evaluator, capabilities.setBuildTargetChangedProvider(false) //TODO: for now it's false, but will try to support this later val future = new CompletableFuture[InitializeBuildResult]() future.complete(new InitializeBuildResult("mill-bsp", millServerVersion, bspVersion, capabilities)) + initialized = true future } @@ -82,13 +85,7 @@ class MillBuildServer(evaluator: Evaluator, } override def buildShutdown(): CompletableFuture[Object] = { - clientInitialized match { - case true => val future = new CompletableFuture[AnyRef]() - future.complete("shut down this server") - future - case false => throw new Error("Can not send any other request before the initialize request") - } - + handleExceptions[String, Object]((in) => "shut down this server".asInstanceOf[Object], "") } override def onBuildExit(): Unit = { @@ -96,11 +93,13 @@ class MillBuildServer(evaluator: Evaluator, } override def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = { - recomputeTargets() - val future = new CompletableFuture[WorkspaceBuildTargetsResult]() - val result = new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava) - future.complete(result) - future + recomputeTargets() + handleExceptions[String, WorkspaceBuildTargetsResult]( + (in) => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), "") +// val future = new CompletableFuture[WorkspaceBuildTargetsResult]() +// val result = new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava) +// future.complete(result) +// future } private[this] def getSourceFiles(sources: Seq[os.Path]): Iterable[os.Path] = { @@ -146,9 +145,10 @@ class MillBuildServer(evaluator: Evaluator, new SourcesResult(items.asJava) } - val future = new CompletableFuture[SourcesResult]() - future.complete(computeSourcesResult) - future +// val future = new CompletableFuture[SourcesResult]() +// future.complete(computeSourcesResult) +// future + handleExceptions[String, SourcesResult]((in) => computeSourcesResult, "") } override def buildTargetInverseSources(inverseSourcesParams: InverseSourcesParams): @@ -165,9 +165,10 @@ class MillBuildServer(evaluator: Evaluator, new InverseSourcesResult(targets) } - val future = new CompletableFuture[InverseSourcesResult]() - future.complete(getInverseSourcesResult) - future +// val future = new CompletableFuture[InverseSourcesResult]() +// future.complete(getInverseSourcesResult) +// future + handleExceptions[String, InverseSourcesResult]((in) => getInverseSourcesResult, "") } override def buildTargetDependencySources(dependencySourcesParams: DependencySourcesParams): @@ -200,9 +201,10 @@ class MillBuildServer(evaluator: Evaluator, new DependencySourcesResult(items.asJava) } - val future = new CompletableFuture[DependencySourcesResult]() - future.complete(getDependencySources) - future +// val future = new CompletableFuture[DependencySourcesResult]() +// future.complete(getDependencySources) +// future + handleExceptions[String, DependencySourcesResult]((in) => getDependencySources, "") } override def buildTargetResources(resourcesParams: ResourcesParams): CompletableFuture[ResourcesResult] = { @@ -222,9 +224,10 @@ class MillBuildServer(evaluator: Evaluator, new ResourcesResult(items.asJava) } - val future = new CompletableFuture[ResourcesResult]() - future.complete(getResources) - future +// val future = new CompletableFuture[ResourcesResult]() +// future.complete(getResources) +// future + handleExceptions[String, ResourcesResult]((in) => getResources, "") } def getOriginId(params: CompileParams): Option[String] = { @@ -319,9 +322,10 @@ class MillBuildServer(evaluator: Evaluator, compileResult //TODO: See what form IntelliJ expects data about products of compilation in order to set data field } - val future = new CompletableFuture[CompileResult]() - future.complete(getCompileResult) - future +// val future = new CompletableFuture[CompileResult]() +// future.complete(getCompileResult) +// future + handleExceptions[String, CompileResult]((in) => getCompileResult, "") } override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = { @@ -340,40 +344,27 @@ class MillBuildServer(evaluator: Evaluator, new RunResult(StatusCode.OK) } } - val future = new CompletableFuture[RunResult]() - future.complete(getRunResult) - future - } - - private[this] def getTestReport(targetId: BuildTargetIdentifier, results: Seq[TestRunner.Result]): TestReport = { - val testReport = new TestReport(targetId, 0, 0, 0, 0, 0) - testReport.setTime(results.map(r => r.duration).sum) - for (result <- results) { - result.status match { - case "Passed" => testReport.setPassed(testReport.getPassed + 1) - case "Failed" => testReport.setFailed(testReport.getFailed + 1) - case "Ignored" => testReport.setIgnored(testReport.getIgnored + 1) - case "Cancelled" => testReport.setCancelled(testReport.getCancelled + 1) - case "Skipped" => testReport.setSkipped(testReport.getSkipped + 1) - } - } - testReport +// val future = new CompletableFuture[RunResult]() +// future.complete(getRunResult) +// future + handleExceptions[String, RunResult]((in) => getRunResult, "") } private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { + System.err.println("Results: " + results.rawValues) if (results.rawValues.exists(r => r.isInstanceOf[Result.Failure[Any]])) { StatusCode.ERROR } - if (results.rawValues.contains(Result.Skipped) || results.rawValues.contains(Result.Aborted)) { + else if (results.rawValues.contains(Result.Skipped) || results.rawValues.contains(Result.Aborted)) { StatusCode.CANCELLED } - StatusCode.OK + else {StatusCode.OK} } override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = { - def getTestResult (implicit ctx: Ctx.Log with Ctx.Home ): TestResult = { + def getTestResult: TestResult = { val params = TaskParameters.fromTestParams(testParams) val argsMap = testParams.getData match { case scalaTestParams: ScalaTestParams => @@ -387,7 +378,7 @@ class MillBuildServer(evaluator: Evaluator, val module = targetIdToModule(targetId) module match { case m: TestModule => val testModule = m.asInstanceOf[TestModule] - val testTask = testModule.test(argsMap(targetId).mkString(" ")) + val testTask = testModule.testLocal(argsMap(targetId).mkString(" ")) // notifying the client that the testing of this build target started val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) @@ -413,7 +404,8 @@ class MillBuildServer(evaluator: Evaluator, val statusCode = getStatusCode(results) statusCode match { case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR - case default => + case StatusCode.CANCELLED => overallStatusCode = + if (overallStatusCode == StatusCode.ERROR) StatusCode.ERROR else StatusCode.CANCELLED } // notifying the client that the testing of this build target ended val taskFinishParams = new TaskFinishParams( @@ -439,9 +431,10 @@ class MillBuildServer(evaluator: Evaluator, testResult } } - val future = new CompletableFuture[TestResult]() - future.complete(getTestResult(ctx)) - future +// val future = new CompletableFuture[TestResult]() +// future.complete(getTestResult(ctx)) +// future + handleExceptions[String, TestResult]((in) => getTestResult, "") } override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = { @@ -465,9 +458,10 @@ class MillBuildServer(evaluator: Evaluator, } new CleanCacheResult(msg, cleaned) } - val future = new CompletableFuture[CleanCacheResult]() - future.complete(getCleanCacheResult) - future +// val future = new CompletableFuture[CleanCacheResult]() +// future.complete(getCleanCacheResult) +// future + handleExceptions[String, CleanCacheResult]((in) => getCleanCacheResult, "") } override def buildTargetScalacOptions(scalacOptionsParams: ScalacOptionsParams): @@ -492,9 +486,10 @@ class MillBuildServer(evaluator: Evaluator, new ScalacOptionsResult(targetScalacOptions.asJava) } - val future = new CompletableFuture[ScalacOptionsResult]() - future.complete(getScalacOptionsResult) - future +// val future = new CompletableFuture[ScalacOptionsResult]() +// future.complete(getScalacOptionsResult) +// future + handleExceptions[String, ScalacOptionsResult]((in) => getScalacOptionsResult, "") } override def buildTargetScalaMainClasses(scalaMainClassesParams: ScalaMainClassesParams): @@ -521,9 +516,10 @@ class MillBuildServer(evaluator: Evaluator, } new ScalaMainClassesResult(items.asJava) } - val future = new CompletableFuture[ScalaMainClassesResult]() - future.complete(getScalaMainClasses) - future +// val future = new CompletableFuture[ScalaMainClassesResult]() +// future.complete(getScalaMainClasses) +// future + handleExceptions[String, ScalaMainClassesResult]((in) => getScalaMainClasses, "") } private[this] def getTestFrameworks(module: TestModule) (implicit ctx: Ctx.Home): Seq[String] = { @@ -560,9 +556,10 @@ class MillBuildServer(evaluator: Evaluator, } new ScalaTestClassesResult(items.asJava) } - val future = new CompletableFuture[ScalaTestClassesResult]() - future.complete(getScalaTestClasses(ctx)) - future +// val future = new CompletableFuture[ScalaTestClassesResult]() +// future.complete(getScalaTestClasses(ctx)) +// future + handleExceptions[Ctx.Home, ScalaTestClassesResult]((c) => getScalaTestClasses(c), ctx) } private[this] def targetToModule(moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier]): @@ -584,4 +581,21 @@ class MillBuildServer(evaluator: Evaluator, targetIdToModule = targetToModule(moduleToTargetId) moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) } + + private[this] def handleExceptions[T, V](serverMethod: T => V, input: T): CompletableFuture[V] = { + val future = new CompletableFuture[V]() + initialized match { + case true => + try { + future.complete(serverMethod(input)) + } catch { + case e: Exception => future.completeExceptionally(e) + } + case false => + future.completeExceptionally( + new Exception("Can not respond to any request before receiving the `initialize` request.") + ) + } + future + } } diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 1917f732..e7c1ed22 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -65,16 +65,12 @@ object ModuleUtils { } def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { - val canRun = getTaskResult(evaluator, module.finalMainClass) match { - case result: Result.Success[String] => true - case default => false - } val canTest = module match { case module: TestModule => true case default => false } - new BuildTargetCapabilities(true, canTest, canRun) + new BuildTargetCapabilities(true, canTest, true) } //TODO: I think here I need to look at scalaLibraryIvyDeps, ivyDeps that contain diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 92f00b25..1243900c 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -337,7 +337,7 @@ case class Evaluator(home: os.Path, home, env, reporter, - bspContext//new ManagedLoggedReporter(10, logger) + bspContext //new ManagedLoggedReporter(10, logger) ) val out = System.out val in = System.in diff --git a/scalalib/src/TestRunner.scala b/scalalib/src/TestRunner.scala index c2017c77..2e6c2ba9 100644 --- a/scalalib/src/TestRunner.scala +++ b/scalalib/src/TestRunner.scala @@ -95,6 +95,7 @@ object TestRunner { def handle(event: Event) = { testReporter.logStart(event) events.append(event) + testReporter.logFinish(event) } }, Array( @@ -120,19 +121,19 @@ object TestRunner { val results = for(e <- events) yield { val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None mill.scalalib.TestRunner.Result( - e.fullyQualifiedName(), - e.selector() match{ + e.fullyQualifiedName(), + 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() }, - e.duration(), - e.status().toString, - ex.map(_.getClass.getName), - ex.map(_.getMessage), - ex.map(_.getStackTrace) + e.duration(), + e.status().toString, + ex.map(_.getClass.getName), + ex.map(_.getMessage), + ex.map(_.getStackTrace) ) } -- cgit v1.2.3 From a438a9a3906c3f53607638f593177142770e092d Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 15 Jul 2019 15:42:05 +0200 Subject: Removed commented code and fixed bug in assigned target ids. Now the uri of each BuildTarget will be the intelliJModulePath of the corresponsing mill module. --- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 43 ---------------------- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 5 +-- 2 files changed, 2 insertions(+), 46 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 3aeaa81a..ab7c8732 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -96,10 +96,6 @@ class MillBuildServer(evaluator: Evaluator, recomputeTargets() handleExceptions[String, WorkspaceBuildTargetsResult]( (in) => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), "") -// val future = new CompletableFuture[WorkspaceBuildTargetsResult]() -// val result = new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava) -// future.complete(result) -// future } private[this] def getSourceFiles(sources: Seq[os.Path]): Iterable[os.Path] = { @@ -144,10 +140,6 @@ class MillBuildServer(evaluator: Evaluator, new SourcesResult(items.asJava) } - -// val future = new CompletableFuture[SourcesResult]() -// future.complete(computeSourcesResult) -// future handleExceptions[String, SourcesResult]((in) => computeSourcesResult, "") } @@ -164,10 +156,6 @@ class MillBuildServer(evaluator: Evaluator, yield targetId).toList.asJava new InverseSourcesResult(targets) } - -// val future = new CompletableFuture[InverseSourcesResult]() -// future.complete(getInverseSourcesResult) -// future handleExceptions[String, InverseSourcesResult]((in) => getInverseSourcesResult, "") } @@ -200,10 +188,6 @@ class MillBuildServer(evaluator: Evaluator, new DependencySourcesResult(items.asJava) } - -// val future = new CompletableFuture[DependencySourcesResult]() -// future.complete(getDependencySources) -// future handleExceptions[String, DependencySourcesResult]((in) => getDependencySources, "") } @@ -223,10 +207,6 @@ class MillBuildServer(evaluator: Evaluator, new ResourcesResult(items.asJava) } - -// val future = new CompletableFuture[ResourcesResult]() -// future.complete(getResources) -// future handleExceptions[String, ResourcesResult]((in) => getResources, "") } @@ -321,10 +301,6 @@ class MillBuildServer(evaluator: Evaluator, compileResult.setOriginId(compileParams.getOriginId) compileResult //TODO: See what form IntelliJ expects data about products of compilation in order to set data field } - -// val future = new CompletableFuture[CompileResult]() -// future.complete(getCompileResult) -// future handleExceptions[String, CompileResult]((in) => getCompileResult, "") } @@ -344,9 +320,6 @@ class MillBuildServer(evaluator: Evaluator, new RunResult(StatusCode.OK) } } -// val future = new CompletableFuture[RunResult]() -// future.complete(getRunResult) -// future handleExceptions[String, RunResult]((in) => getRunResult, "") } @@ -431,9 +404,6 @@ class MillBuildServer(evaluator: Evaluator, testResult } } -// val future = new CompletableFuture[TestResult]() -// future.complete(getTestResult(ctx)) -// future handleExceptions[String, TestResult]((in) => getTestResult, "") } @@ -458,9 +428,6 @@ class MillBuildServer(evaluator: Evaluator, } new CleanCacheResult(msg, cleaned) } -// val future = new CompletableFuture[CleanCacheResult]() -// future.complete(getCleanCacheResult) -// future handleExceptions[String, CleanCacheResult]((in) => getCleanCacheResult, "") } @@ -485,10 +452,6 @@ class MillBuildServer(evaluator: Evaluator, } new ScalacOptionsResult(targetScalacOptions.asJava) } - -// val future = new CompletableFuture[ScalacOptionsResult]() -// future.complete(getScalacOptionsResult) -// future handleExceptions[String, ScalacOptionsResult]((in) => getScalacOptionsResult, "") } @@ -516,9 +479,6 @@ class MillBuildServer(evaluator: Evaluator, } new ScalaMainClassesResult(items.asJava) } -// val future = new CompletableFuture[ScalaMainClassesResult]() -// future.complete(getScalaMainClasses) -// future handleExceptions[String, ScalaMainClassesResult]((in) => getScalaMainClasses, "") } @@ -556,9 +516,6 @@ class MillBuildServer(evaluator: Evaluator, } new ScalaTestClassesResult(items.asJava) } -// val future = new CompletableFuture[ScalaTestClassesResult]() -// future.complete(getScalaTestClasses(ctx)) -// future handleExceptions[Ctx.Home, ScalaTestClassesResult]((c) => getScalaTestClasses(c), ctx) } diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index e7c1ed22..5413c02f 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -56,9 +56,8 @@ object ModuleUtils { } buildTarget.setData(dataBuildTarget) buildTarget.setDisplayName(module.millModuleSegments.last.value.toList.head.pathSegments.head) - buildTarget.setBaseDirectory(module.millSourcePath.toNIO.toAbsolutePath.toUri.toString) + buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) moduleToTarget ++= Map(module -> buildTarget) - } moduleToTarget @@ -125,7 +124,7 @@ object ModuleUtils { for ( module <- modules ) { moduleToTarget ++= Map(module -> new BuildTargetIdentifier( - module.millSourcePath.toNIO.toAbsolutePath.toUri.toString + module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString )) } -- cgit v1.2.3 From 7ca631c109f8e59a171cea6b15592b67a277bba5 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 15 Jul 2019 15:50:14 +0200 Subject: Fixed bug in computing the scala library dependencies of a ScalaBuildTarget. --- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 5413c02f..54137d25 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -104,6 +104,7 @@ object ModuleUtils { } def computeScalaLangDependencies(module: ScalaModule, evaluator: Evaluator): Loose.Agg[PathRef] = { + evaluateInformativeTask(evaluator, module.resolveDeps(module.scalaLibraryIvyDeps), Loose.Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, module.scalacPluginClasspath, Loose.Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, module.resolveDeps(module.ivyDeps), Loose.Agg.empty[PathRef]). filter(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-compiler") || -- cgit v1.2.3 From 3a1527b7cf11da8f154e96b4a0e193ea1f8a0896 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 15 Jul 2019 16:48:21 +0200 Subject: Added notification from server to client to show a message that either no main class was found or there are multiple and none was specified. --- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 6 +++- scratch/build.sc | 42 ---------------------- 2 files changed, 5 insertions(+), 43 deletions(-) delete mode 100644 scratch/build.sc diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index ab7c8732..df628049 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -470,7 +470,11 @@ class MillBuildServer(evaluator: Evaluator, evaluateInformativeTask(evaluator, module.forkArgs, Seq.empty[String]). toList.asJava, List.empty[String].asJava)) - case msg: Left[String, String] => List.empty[ScalaMainClass] + case msg: Left[String, String] => + val messageParams = new ShowMessageParams(MessageType.WARNING, msg.value) + messageParams.setOriginId(scalaMainClassesParams.getOriginId) + client.onBuildShowMessage(messageParams) // tell the client that no main class was found or specified + List.empty[ScalaMainClass] } case default => List.empty[ScalaMainClass] } diff --git a/scratch/build.sc b/scratch/build.sc deleted file mode 100644 index 85228a20..00000000 --- a/scratch/build.sc +++ /dev/null @@ -1,42 +0,0 @@ -import mill._, scalalib._ -//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-16-c95bc4-DIRTYd5dc7fa5` - -object mill_exercise extends ScalaModule { - def scalaVersion = "2.12.8" - def mainClass = Some("mill_exercise.Compiler") - def ivyDeps = Agg( - ivy"org.scalameta::metals:0.5.2", - ivy"org.scalameta::scalameta:4.1.9", - ivy"com.geirsson::coursier-small:1.3.3", - ivy"org.scala-lang:scala-reflect:2.12.8", - ivy"org.scala-lang:scala-compiler:2.12.8", - ivy"org.eclipse.lsp4j:org.eclipse.lsp4j:0.7.1", - ivy"ch.epfl.scala:bsp4j:2.0.0-M3", - ivy"com.google.code.gson:gson:2.3.1", - ivy"com.lihaoyi::ammonite-ops:1.6.7" - ) - - object test extends Tests { - def ivyDeps = Agg(//ivy"org.scalameta::metals:0.5.2", - ivy"org.scalatest::scalatest:3.0.4", - ivy"org.scalactic::scalactic:3.0.5", - ivy"org.scalameta::testkit:4.1.9", - ivy"org.eclipse.lsp4j:org.eclipse.lsp4j:0.5.0", - ivy"ch.epfl.scala::bloop-config:1.2.5", - ivy"org.scala-lang.modules::scala-java8-compat:0.9.0") - - def testFrameworks = Seq("org.scalatest.tools.Framework") - } -} - -object random extends SbtModule { - - def scalacOptions = Seq( - //"-Ywarn-unused", - "-Ylog-classpath" - ) - - def scalaVersion = "2.12.8" - - def ivyDeps = Agg(ivy"ch.epfl.scala:bsp4j:2.0.0-M3") -} -- cgit v1.2.3 From d7473780f2ab9ae7c5a3b77e482fd849438f631e Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 15 Jul 2019 17:47:18 +0200 Subject: Fixed bug in computing the testing arguments from bsp in buildTargetTest. --- contrib/bsp/src/mill/contrib/MainMillBuildServer.scala | 2 ++ contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala | 2 ++ contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 10 +++++++--- main/core/src/eval/Evaluator.scala | 2 +- scalalib/src/JavaModule.scala | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 90aa5610..d932fbcb 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -17,6 +17,7 @@ import mill.scalalib._ import mill.util.DummyLogger import org.eclipse.lsp4j.jsonrpc.{CompletableFutures, Launcher} import requests.Compress.None +import sbt.testing.Event import upickle.default import scala.collection.JavaConverters._ @@ -186,6 +187,7 @@ object MainMillBuildServer extends ExternalModule { if (millServer.moduleToTarget(module).getDisplayName == "test") { // println(eval.evaluate(Strict.Agg(module.asInstanceOf[TestModule].testLocal() // )).rawValues) + millServer.initialized = true println(millServer.buildTargetTest(new TestParams(List(millServer.moduleToTargetId(module)).asJava)).get) } } diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 23cadfcb..4027cea9 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -27,6 +27,7 @@ class BspTestReporter( taskStartParams.setData(new TestStart(getDisplayName(event))) taskStartParams.setMessage("Starting running: " + getDisplayName(event)) client.onBuildTaskStart(taskStartParams) + println("Logged start") } override def logFinish(event: Event): Unit = { @@ -75,6 +76,7 @@ class BspTestReporter( exception.getClass.toString)) } client.onBuildTaskFinish(taskFinishParams) + println("Logged finish") } def getDisplayName(e: Event): String = { diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index df628049..c1e0f989 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -351,7 +351,8 @@ class MillBuildServer(evaluator: Evaluator, val module = targetIdToModule(targetId) module match { case m: TestModule => val testModule = m.asInstanceOf[TestModule] - val testTask = testModule.testLocal(argsMap(targetId).mkString(" ")) + println("Arguments: " + argsMap(targetId)) + val testTask = testModule.testLocal(argsMap(targetId):_*) // notifying the client that the testing of this build target started val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) @@ -366,6 +367,8 @@ class MillBuildServer(evaluator: Evaluator, new TaskId(testTask.hashCode().toString), Seq.empty[String]) + println("BspContext: " + bspContext) + val results = millEvaluator.evaluate( Strict.Agg(testTask), Option(new BspLoggedReporter(client, @@ -379,6 +382,7 @@ class MillBuildServer(evaluator: Evaluator, case StatusCode.ERROR => overallStatusCode = StatusCode.ERROR case StatusCode.CANCELLED => overallStatusCode = if (overallStatusCode == StatusCode.ERROR) StatusCode.ERROR else StatusCode.CANCELLED + case StatusCode.OK => } // notifying the client that the testing of this build target ended val taskFinishParams = new TaskFinishParams( @@ -467,9 +471,9 @@ class MillBuildServer(evaluator: Evaluator, case mainClass: Right[String, String] => List(new ScalaMainClass( mainClass.value, + List.empty[String].asJava, evaluateInformativeTask(evaluator, module.forkArgs, Seq.empty[String]). - toList.asJava, - List.empty[String].asJava)) + toList.asJava)) case msg: Left[String, String] => val messageParams = new ShowMessageParams(MessageType.WARNING, msg.value) messageParams.setOriginId(scalaMainClassesParams.getOriginId) diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 1243900c..9a1bfb65 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -48,7 +48,6 @@ case class Evaluator(home: os.Path, reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter], bspContext: BspContext = DummyBspContext): Evaluator.Results = { os.makeDir.all(outPath) - println("Reporter: " + reporter) val (sortedGroups, transitive) = Evaluator.plan(rootModule, goals) val evaluated = new Agg.Mutable[Task[_]] @@ -308,6 +307,7 @@ case class Evaluator(home: os.Path, .map{x => newResults.getOrElse(x, results(x))} .collect{ case Result.Success((v, hashCode)) => v } + val res = if (targetInputValues.length != task.inputs.length) Result.Skipped else { diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index d7a0eb2c..cd467a3b 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -613,7 +613,7 @@ trait TestModule extends JavaModule with TaskModule { runClasspath().map(_.path), Agg(compile().classes.path), args, - T.ctx.bsp + T.ctx().bsp ) TestModule.handleResults(doneMsg, results) -- cgit v1.2.3 From e6b86afc7e765068441a1d0bb57eead199f55150 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 16 Jul 2019 21:25:50 +0200 Subject: Adding the scratch folder to git. --- .gitignore | 1 - .../bsp/src/mill/contrib/bsp/MillBspLogger.scala | 33 ++++++++++++++ scratch/.bsp/mill-bsp.json | 1 + .../src/main/scala/BenchmarkSource.scala | 14 ++++++ scratch/bsp/src/main/scala/BspSource.scala | 10 +++++ scratch/bsp/src/test/scala/BspTests.scala | 8 ++++ scratch/build.sc | 52 ++++++++++++++++++++++ scratch/core/src/main/scala/CommonObject.scala | 9 ++++ scratch/core/src/test/scala/CoreTests.scala | 9 ++++ scratch/test_build1.iml | 24 ++++++++++ 10 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala create mode 100644 scratch/.bsp/mill-bsp.json create mode 100644 scratch/benchmarks/src/main/scala/BenchmarkSource.scala create mode 100644 scratch/bsp/src/main/scala/BspSource.scala create mode 100644 scratch/bsp/src/test/scala/BspTests.scala create mode 100644 scratch/build.sc create mode 100644 scratch/core/src/main/scala/CommonObject.scala create mode 100644 scratch/core/src/test/scala/CoreTests.scala create mode 100644 scratch/test_build1.iml diff --git a/.gitignore b/.gitignore index 1d1c261d..b5fa3ece 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ out/ contrib/bsp/mill-external-bs contrib/bsp/mill-out-bs mill.iml -scratch ./bsp \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala new file mode 100644 index 00000000..c898926e --- /dev/null +++ b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala @@ -0,0 +1,33 @@ +package mill.contrib.bsp + +import java.io.{InputStream, PrintStream} + +import ch.epfl.scala.bsp4j.{BuildClient, TaskId, TaskProgressParams} +import mill.api.{BspContext, Logger} + +class MillBspLogger(client: BuildClient, taskId: Int) extends Logger { + + override def ticker(s: String): Unit = { + val progressString = s.split(" ")(0) + val progress = progressString.substring(1, progressString.length - 1).split("/") + val params = new TaskProgressParams(new TaskId(taskId.toString)) + params.setEventTime(System.currentTimeMillis()) + params.setMessage(s) + params.setUnit(s.split(" ")(1)) + params.setProgress(progress(0).toLong) + params.setTotal(progress(1).toLong) + client.onBuildTaskProgress(params) + } + + override def colored: Boolean = false + + override val errorStream: PrintStream + override val outputStream: PrintStream + override val inStream: InputStream + + override def info(s: String): Unit = {} + + override def error(s: String): Unit = {} + + override def debug(s: String): Unit = {} +} diff --git a/scratch/.bsp/mill-bsp.json b/scratch/.bsp/mill-bsp.json new file mode 100644 index 00000000..af709ea6 --- /dev/null +++ b/scratch/.bsp/mill-bsp.json @@ -0,0 +1 @@ +{"name":"mill-bsp","argv":["/home/alexandra/mill/out/dev/launcher/dest/run", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} diff --git a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala new file mode 100644 index 00000000..7ee88801 --- /dev/null +++ b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala @@ -0,0 +1,14 @@ +package main.scala + +import org.apache.commons.io.FileUtils +import java.io.File +//import com.williamfiset.fastjavaio.InputReader + +object BenchmarkSource { + //val reader = new InputReader() + + def main(args: Array[String]): Unit = { + val file = FileUtils.getFile("/home/alexandra/test_build1/build.sc") + println(file) + } +} diff --git a/scratch/bsp/src/main/scala/BspSource.scala b/scratch/bsp/src/main/scala/BspSource.scala new file mode 100644 index 00000000..3a2300ae --- /dev/null +++ b/scratch/bsp/src/main/scala/BspSource.scala @@ -0,0 +1,10 @@ +package main.scala +import ch.epfl.scala.bsp4j._ + +object BspSource { + val obj = CommonObject.strVal + + def main(args: Array[String]): Unit = { + println(new BuildTargetIdentifier("path")) + } +} diff --git a/scratch/bsp/src/test/scala/BspTests.scala b/scratch/bsp/src/test/scala/BspTests.scala new file mode 100644 index 00000000..596a1816 --- /dev/null +++ b/scratch/bsp/src/test/scala/BspTests.scala @@ -0,0 +1,8 @@ +package tests +import org.scalatest.FunSuite + +object BspTests extends FunSuite { + test("test 1") { + assert(CoreTests().coreValue > 0) + } +} diff --git a/scratch/build.sc b/scratch/build.sc new file mode 100644 index 00000000..97a7d1b4 --- /dev/null +++ b/scratch/build.sc @@ -0,0 +1,52 @@ +import mill.scalalib.{SbtModule, Dep, DepSyntax} +//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-21-fd34d3-DIRTY61e11e70` + +trait BetterFilesModule extends SbtModule{ + def scalaVersion = "2.12.4" + def scalacOptions = Seq( + "-deprecation", // Emit warning and location for usages of deprecated APIs. + "-encoding", "utf-8", // Specify character encoding used by source files. + "-explaintypes", // Explain type errors in more detail. + "-feature", // Emit warning and location for usages of features that should be imported explicitly. + "-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`. + "-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. + "-Ywarn-nullary-unit", // Warn when nullary methods return Unit. + "-Ywarn-numeric-widen", // Warn when numerics are widened. + "-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. + "-Ywarn-unused:imports", // Warn if an import selector is not referenced. + "-Ywarn-unused:locals", // Warn if a local definition is unused. + "-Ywarn-unused:params", // Warn if a value parameter is unused. + "-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. + "-Ywarn-unused:privates", // Warn if a private member is unused. + "-Ywarn-value-discard" // Warn when non-Unit expression results are unused. + ) + override def javacOptions = Seq("-source", "1.8", "-target", "1.8", "-Xlint") + + object test extends Tests{ + def moduleDeps = + if (this == core.test) super.moduleDeps + else super.moduleDeps ++ Seq(core.test) + def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.0.4") + def testFrameworks = Seq("org.scalatest.tools.Framework") + } +} + +object core extends BetterFilesModule + +object bsp extends BetterFilesModule{ + def moduleDeps = Seq(core) + def ivyDeps = Agg(ivy"ch.epfl.scala:bsp4j:2.0.0-M3") +} + +object benchmarks extends BetterFilesModule{ + def moduleDeps = Seq(core) + def ivyDeps = Agg( + ivy"commons-io:commons-io:2.5" + ) + def unmanagedClasspath = Agg( + mill.modules.Util.download( + "https://github.com/williamfiset/FastJavaIO/releases/download/v1.0/fastjavaio.jar", + "fastjavaio.jar" + ) + ) +} \ No newline at end of file diff --git a/scratch/core/src/main/scala/CommonObject.scala b/scratch/core/src/main/scala/CommonObject.scala new file mode 100644 index 00000000..e10bf3a0 --- /dev/null +++ b/scratch/core/src/main/scala/CommonObject.scala @@ -0,0 +1,9 @@ +package main.scala + +object CommonObject { + val strVal: String = "" + + def hasMethod: Boolean ={ + true + } +} diff --git a/scratch/core/src/test/scala/CoreTests.scala b/scratch/core/src/test/scala/CoreTests.scala new file mode 100644 index 00000000..6a25b4a4 --- /dev/null +++ b/scratch/core/src/test/scala/CoreTests.scala @@ -0,0 +1,9 @@ +package tests +import org.scalatest.FlatSpec + +class CoreTests extends FlatSpec { + val coreValue = 1 + "This simple addition" should "equal 4" in { + assert(2 + 2 == 4) + } +} diff --git a/scratch/test_build1.iml b/scratch/test_build1.iml new file mode 100644 index 00000000..294cb691 --- /dev/null +++ b/scratch/test_build1.iml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3 From 91086e5815a80dd2fd39901a689ad92b3db81487 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 10:26:08 +0200 Subject: Updating the start server commnand according to newest changes in mill version 0.5 --- scratch/.bsp/mill-bsp.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratch/.bsp/mill-bsp.json b/scratch/.bsp/mill-bsp.json index af709ea6..6540631f 100644 --- a/scratch/.bsp/mill-bsp.json +++ b/scratch/.bsp/mill-bsp.json @@ -1 +1 @@ -{"name":"mill-bsp","argv":["/home/alexandra/mill/out/dev/launcher/dest/run", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} +{"name":"mill-bsp","argv":["mill", "-i", "dev.run", "scratch", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} -- cgit v1.2.3 From 197e21c357c15d714ab9f026f9eec13015df5049 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 10:32:45 +0200 Subject: Added progress notifications during compilation --- .../bsp/src/mill/contrib/bsp/MillBspLogger.scala | 14 ++--------- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 3 ++- main/core/src/eval/Evaluator.scala | 28 +++++++++++++--------- scratch/build.sc | 2 +- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala index c898926e..d7dd62b1 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala @@ -4,8 +4,9 @@ import java.io.{InputStream, PrintStream} import ch.epfl.scala.bsp4j.{BuildClient, TaskId, TaskProgressParams} import mill.api.{BspContext, Logger} +import mill.util.ProxyLogger -class MillBspLogger(client: BuildClient, taskId: Int) extends Logger { +class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends ProxyLogger(logger) { override def ticker(s: String): Unit = { val progressString = s.split(" ")(0) @@ -19,15 +20,4 @@ class MillBspLogger(client: BuildClient, taskId: Int) extends Logger { client.onBuildTaskProgress(params) } - override def colored: Boolean = false - - override val errorStream: PrintStream - override val outputStream: PrintStream - override val inStream: InputStream - - override def info(s: String): Unit = {} - - override def error(s: String): Unit = {} - - override def debug(s: String): Unit = {} } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index c1e0f989..e1a5c361 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -267,7 +267,8 @@ class MillBuildServer(evaluator: Evaluator, override def logStart(event: Event): Unit = {} override def logFinish(event: Event): Unit = {} - } + }, + new MillBspLogger(client, compileTask.hashCode(), millEvaluator.log) ) val endTime = System.currentTimeMillis() diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 9a1bfb65..4dd45d14 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -46,7 +46,8 @@ case class Evaluator(home: os.Path, def evaluate(goals: Agg[Task[_]], reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter], - bspContext: BspContext = DummyBspContext): Evaluator.Results = { + bspContext: BspContext = DummyBspContext, + logger: Logger = log): Evaluator.Results = { os.makeDir.all(outPath) val (sortedGroups, transitive) = Evaluator.plan(rootModule, goals) @@ -73,7 +74,8 @@ case class Evaluator(home: os.Path, results, counterMsg, reporter, - bspContext + bspContext, + logger ) someTaskFailed = someTaskFailed || newResults.exists(task => !task._2.isInstanceOf[Success[_]]) @@ -120,7 +122,8 @@ case class Evaluator(home: os.Path, results: collection.Map[Task[_], Result[(Any, Int)]], counterMsg: String, reporter: Option[ManagedLoggedReporter], - bspContext: BspContext + bspContext: BspContext, + logger: Logger ): (collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean) = { val externalInputsHash = scala.util.hashing.MurmurHash3.orderedHash( @@ -144,7 +147,8 @@ case class Evaluator(home: os.Path, maybeTargetLabel = None, counterMsg = counterMsg, reporter, - bspContext + bspContext, + logger ) (newResults, newEvaluated, false) case Right(labelledNamedTask) => @@ -198,7 +202,8 @@ case class Evaluator(home: os.Path, maybeTargetLabel = Some(msgParts.mkString), counterMsg = counterMsg, reporter, - bspContext + bspContext, + logger ) newResults(labelledNamedTask.task) match{ @@ -274,7 +279,8 @@ case class Evaluator(home: os.Path, maybeTargetLabel: Option[String], counterMsg: String, reporter: Option[ManagedLoggedReporter], - bspContext: BspContext): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { + bspContext: BspContext, + logger: Logger): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { val newEvaluated = mutable.Buffer.empty[Task[_]] @@ -291,11 +297,11 @@ case class Evaluator(home: os.Path, val logRun = inputResults.forall(_.isInstanceOf[Result.Success[_]]) val prefix = s"[$counterMsg] $targetLabel " - if(logRun) log.ticker(prefix) + if(logRun) logger.ticker(prefix) prefix + "| " } - val multiLogger = new ProxyLogger(resolveLogger(paths.map(_.log))) { + val multiLogger = new ProxyLogger(resolveLogger(paths.map(_.log), logger)) { override def ticker(s: String): Unit = { super.ticker(tickerPrefix.getOrElse("")+s) } @@ -376,9 +382,9 @@ case class Evaluator(home: os.Path, (newResults, newEvaluated) } - def resolveLogger(logPath: Option[os.Path]): Logger = logPath match{ - case None => log - case Some(path) => MultiLogger(log.colored, log, FileLogger(log.colored, path, debugEnabled = true)) + def resolveLogger(logPath: Option[os.Path], logger: Logger): Logger = logPath match{ + case None => logger + case Some(path) => MultiLogger(logger.colored, log, FileLogger(logger.colored, path, debugEnabled = true)) } } diff --git a/scratch/build.sc b/scratch/build.sc index 97a7d1b4..98bdca27 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,5 +1,5 @@ import mill.scalalib.{SbtModule, Dep, DepSyntax} -//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.4.1-21-fd34d3-DIRTY61e11e70` +//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.5.0-28-53df48-DIRTYf21142f7` trait BetterFilesModule extends SbtModule{ def scalaVersion = "2.12.4" -- cgit v1.2.3 From 115a7dd7e90c056f174f6e29d0f4a8baef2f89e3 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 10:39:37 +0200 Subject: Added progress notifications during run and test tasks too. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index e1a5c361..bd06d29b 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -310,11 +310,13 @@ class MillBuildServer(evaluator: Evaluator, val params = TaskParameters.fromRunParams(runParams) val module = targetIdToModule(params.getTargets.head) val args = params.getArguments.getOrElse(Seq.empty[String]) - val runResult = millEvaluator.evaluate(Strict.Agg(module.run(args.mkString(" "))), + val runTask = module.run(args.mkString(" ")) + val runResult = millEvaluator.evaluate(Strict.Agg(runTask), Option(new BspLoggedReporter(client, params.getTargets.head, params.getOriginId, - 10, getCompilationLogger))) + 10, getCompilationLogger)), + logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) if (runResult.failing.keyCount > 0) { new RunResult(StatusCode.ERROR) } else { @@ -376,7 +378,8 @@ class MillBuildServer(evaluator: Evaluator, targetId, params.getOriginId, 10, getCompilationLogger)), - bspContext) + bspContext, + new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) val endTime = System.currentTimeMillis() val statusCode = getStatusCode(results) statusCode match { -- cgit v1.2.3 From 4fe042ea883af0a93b90bc7f00bfd55d05a44465 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 10:58:31 +0200 Subject: Imporved the way a target's display name is computed. --- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 54137d25..2ce3cb91 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -6,13 +6,13 @@ import scala.collection.JavaConverters._ import ch.epfl.scala.bsp4j._ import mill.api.Result.Success import mill.api.{Loose, Strict} -import mill.define.{Discover, Task} +import mill.define.{Discover, Segment, Segments, Task} import mill.eval._ import mill.eval.Evaluator import mill.scalajslib.ScalaJSModule import mill.scalalib.api.Util import mill.scalanativelib._ -import mill.scalalib.{JavaModule, ScalaModule, TestModule} +import mill.scalalib.{GenIdea, GenIdeaImpl, JavaModule, ScalaModule, TestModule} import mill.util.DummyLogger @@ -55,7 +55,7 @@ object ModuleUtils { buildTarget.setDataKind("scala") } buildTarget.setData(dataBuildTarget) - buildTarget.setDisplayName(module.millModuleSegments.last.value.toList.head.pathSegments.head) + buildTarget.setDisplayName(moduleName(module.millModuleSegments)) buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) moduleToTarget ++= Map(module -> buildTarget) } @@ -131,4 +131,12 @@ object ModuleUtils { moduleToTarget } + + // 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() } -- cgit v1.2.3 From 9a67570ae953b02f6562de31efaad4d1cba15983 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 13:54:08 +0200 Subject: Improved compilation by sending all requested targets to the mill evaluator to be compiled together rather than in different evaluation sessions. This avoids duplicated compilation results and side effects like diagnostics and task progress notifications. --- build.sc | 1 + .../bsp/src/mill/contrib/MainMillBuildServer.scala | 16 ++-- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 26 +++++- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 98 +++++++--------------- main/api/src/mill/api/Ctx.scala | 2 +- main/core/src/eval/Evaluator.scala | 10 ++- scalalib/src/JavaModule.scala | 2 +- scalalib/src/ScalaModule.scala | 2 +- 8 files changed, 70 insertions(+), 87 deletions(-) diff --git a/build.sc b/build.sc index abf9b9b7..222de575 100755 --- a/build.sc +++ b/build.sc @@ -125,6 +125,7 @@ object main extends MillModule { } } object api extends MillApiModule{ + //def moduleDeps = Seq(core) def ivyDeps = Agg( Deps.osLib, Deps.upickle diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index d932fbcb..bfe23444 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -7,7 +7,7 @@ import java.nio.file.FileAlreadyExistsException import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, CompileParams, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, TestParams, WorkspaceBuildTargetsResult} +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, CleanCacheParams, CompileParams, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, TestParams, WorkspaceBuildTargetsResult} import mill._ import mill.api.Strict import mill.contrib.bsp.{BspLoggedReporter, MillBuildServer, ModuleUtils} @@ -183,14 +183,12 @@ object MainMillBuildServer extends ExternalModule { ??? } millServer.client = client - for (module <- millServer.millModules) { - if (millServer.moduleToTarget(module).getDisplayName == "test") { -// println(eval.evaluate(Strict.Agg(module.asInstanceOf[TestModule].testLocal() -// )).rawValues) - millServer.initialized = true - println(millServer.buildTargetTest(new TestParams(List(millServer.moduleToTargetId(module)).asJava)).get) - } - } + millServer.initialized = true + println(millServer.buildTargetCleanCache( + new CleanCacheParams(millServer.millModules.map(m => millServer.moduleToTargetId(m)).asJava)).get) + println(millServer.buildTargetCompile( + new CompileParams(millServer.millModules.map(m => millServer.moduleToTargetId(m)).asJava)).get) + println("Diagnostics: " + client.diagnostics) } /** diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index eda25566..846ffd42 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -2,6 +2,7 @@ package mill.contrib.bsp import java.io.File +import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, InverseSourcesParams, ScalaBuildServer, TextDocumentIdentifier} import ch.epfl.scala.{bsp4j => bsp} import mill.api.BspContext import sbt.internal.inc.ManagedLoggedReporter @@ -14,24 +15,30 @@ import scala.compat.java8.OptionConverters._ import scala.io.Source class BspLoggedReporter(client: bsp.BuildClient, - targetId: bsp.BuildTargetIdentifier, + targetId: BuildTargetIdentifier, compilationOriginId: Option[String], maxErrors: Int, logger: ManagedLogger) extends ManagedLoggedReporter(maxErrors, logger) { + var errors = 0 + var warnings = 0 + var infos = 0 + override def logError(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + errors += 1 super.logError(problem) } override def logInfo(problem: Problem): Unit = { - logger.info("Problem: " + problem.toString) - client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + infos += 1 super.logInfo(problem) } override def logWarning(problem: Problem): Unit = { - client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + warnings += 1 super.logWarning(problem) } @@ -67,6 +74,17 @@ class BspLoggedReporter(client: bsp.BuildClient, params } + private[this] def getTragetId(problem: Problem, server: BuildServer with ScalaBuildServer): + Option[BuildTargetIdentifier] = { + problem.position().sourceFile().asScala match { + case Some(file) => Option( + server.buildTargetInverseSources( + new InverseSourcesParams(new TextDocumentIdentifier(file.toURI.toString)) + ).get.getTargets.asScala.head) + case None => Option.empty[BuildTargetIdentifier] + } + } + private[this] def getErrorCode(file: Option[File], start: bsp.Position, end: bsp.Position, position: xsbti.Position): String = { file match { case None => position.lineContent diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index bd06d29b..3937f1ab 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -52,6 +52,8 @@ class MillBuildServer(evaluator: Evaluator, var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) + var moduleCodeToTargetId: Predef.Map[Int, BuildTargetIdentifier] = + for ( (targetId, module) <- targetIdToModule ) yield (targetId, module.hashCode()).swap var initialized = false var clientInitialized = false @@ -236,72 +238,40 @@ class MillBuildServer(evaluator: Evaluator, l } + private[this] def getBspLoggedReporterPool(params: Parameters): Int => Option[ManagedLoggedReporter] = { + (int: Int) => + if (moduleCodeToTargetId.contains(int)) { + println("Module: " + int) + Option(new BspLoggedReporter(client, + moduleCodeToTargetId(int), + params.getOriginId, + 10, getCompilationLogger))} + else Option.empty[ManagedLoggedReporter] + } + //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] = { def getCompileResult: CompileResult = { val params = TaskParameters.fromCompileParams(compileParams) - var numFailures = 0 - var compileTime = 0 - for (targetId <- params.getTargets) { - if (moduleToTarget(targetIdToModule(targetId)).getCapabilities.getCanCompile) { - val millModule = targetIdToModule(targetId) - val compileTask = millModule.compile - - // send notification to client that compilation of this target started - val taskStartParams = new TaskStartParams(new TaskId(compileTask.hashCode().toString)) - taskStartParams.setEventTime(System.currentTimeMillis()) - taskStartParams.setMessage("Compiling target: " + targetId) - taskStartParams.setDataKind("compile-task") - taskStartParams.setData(new CompileTask(targetId)) - client.onBuildTaskStart(taskStartParams) - - val result = millEvaluator.evaluate(Strict.Agg(compileTask), - Option(new BspLoggedReporter(client, - targetId, - getOriginId(compileParams), - 10, getCompilationLogger)), - new BspContext { - override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) - override def logStart(event: Event): Unit = {} - - override def logFinish(event: Event): Unit = {} - }, - new MillBspLogger(client, compileTask.hashCode(), millEvaluator.log) - ) - val endTime = System.currentTimeMillis() - - compileTime += result.timings.map(timingTuple => timingTuple._2).sum - var statusCode = StatusCode.OK - - if (result.failing.keyCount > 0) { - statusCode = StatusCode.ERROR - numFailures += result.failing.keyCount - } - - // send notification to client that compilation of this target ended => compilation report - val taskFinishParams = new TaskFinishParams(new TaskId(compileTask.hashCode().toString), statusCode) - taskFinishParams.setEventTime(endTime) - taskFinishParams.setMessage("Finished compiling target: " + - moduleToTarget(targetIdToModule(targetId)).getDisplayName) - taskFinishParams.setDataKind("compile-report") - val compileReport = new CompileReport(targetId, numFailures, 0) - compileReport.setOriginId(compileParams.getOriginId) - compileReport.setTime(compileTime.toLong) - taskFinishParams.setData(compileReport) - client.onBuildTaskFinish(taskFinishParams) - } - } - - var overallStatusCode = StatusCode.OK - if (numFailures > 0) { - overallStatusCode = StatusCode.ERROR - } - val compileResult = new CompileResult(overallStatusCode) + val taskId = params.hashCode() + val compileTasks = Strict.Agg(params.getTargets.map(targetId => targetIdToModule(targetId).compile):_*) + + val result = millEvaluator.evaluate(compileTasks, + getBspLoggedReporterPool(params), + new BspContext { + override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) + override def logStart(event: Event): Unit = {} + + override def logFinish(event: Event): Unit = {} + }, + new MillBspLogger(client, taskId, millEvaluator.log) + ) + val compileResult = new CompileResult(getStatusCode(result)) compileResult.setOriginId(compileParams.getOriginId) compileResult //TODO: See what form IntelliJ expects data about products of compilation in order to set data field - } + } handleExceptions[String, CompileResult]((in) => getCompileResult, "") } @@ -312,10 +282,7 @@ class MillBuildServer(evaluator: Evaluator, val args = params.getArguments.getOrElse(Seq.empty[String]) val runTask = module.run(args.mkString(" ")) val runResult = millEvaluator.evaluate(Strict.Agg(runTask), - Option(new BspLoggedReporter(client, - params.getTargets.head, - params.getOriginId, - 10, getCompilationLogger)), + getBspLoggedReporterPool(params), logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) if (runResult.failing.keyCount > 0) { new RunResult(StatusCode.ERROR) @@ -328,7 +295,7 @@ class MillBuildServer(evaluator: Evaluator, private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { System.err.println("Results: " + results.rawValues) - if (results.rawValues.exists(r => r.isInstanceOf[Result.Failure[Any]])) { + if (results.failing.keyCount > 0) { StatusCode.ERROR } @@ -374,10 +341,7 @@ class MillBuildServer(evaluator: Evaluator, val results = millEvaluator.evaluate( Strict.Agg(testTask), - Option(new BspLoggedReporter(client, - targetId, - params.getOriginId, - 10, getCompilationLogger)), + getBspLoggedReporterPool(params), bspContext, new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) val endTime = System.currentTimeMillis() diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 439f08d3..02d50b22 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -61,7 +61,7 @@ class Ctx( val log: Logger, val home: os.Path, val env: Map[String, String], - val reporter: Option[ManagedLoggedReporter], + val reporter: Int => Option[ManagedLoggedReporter], val bsp: BspContext ) extends Ctx.Dest diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 4dd45d14..c6a1f52a 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -29,6 +29,8 @@ case class Labelled[T](task: NamedTask[T], case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) case _ => None } + +// override def hashCode(): Int = task.hashCode() } case class Evaluator(home: os.Path, @@ -45,7 +47,7 @@ case class Evaluator(home: os.Path, val classLoaderSignHash = classLoaderSig.hashCode() def evaluate(goals: Agg[Task[_]], - reporter: Option[ManagedLoggedReporter] = Option.empty[ManagedLoggedReporter], + reporter: Int => Option[ManagedLoggedReporter] = (int: Int) => Option.empty[ManagedLoggedReporter], bspContext: BspContext = DummyBspContext, logger: Logger = log): Evaluator.Results = { os.makeDir.all(outPath) @@ -121,7 +123,7 @@ case class Evaluator(home: os.Path, group: Agg[Task[_]], results: collection.Map[Task[_], Result[(Any, Int)]], counterMsg: String, - reporter: Option[ManagedLoggedReporter], + reporter: Int => Option[ManagedLoggedReporter], bspContext: BspContext, logger: Logger ): (collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean) = { @@ -278,7 +280,7 @@ case class Evaluator(home: os.Path, paths: Option[Evaluator.Paths], maybeTargetLabel: Option[String], counterMsg: String, - reporter: Option[ManagedLoggedReporter], + reporter: Int => Option[ManagedLoggedReporter], bspContext: BspContext, logger: Logger): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { @@ -313,7 +315,6 @@ case class Evaluator(home: os.Path, .map{x => newResults.getOrElse(x, results(x))} .collect{ case Result.Success((v, hashCode)) => v } - val res = if (targetInputValues.length != task.inputs.length) Result.Skipped else { @@ -345,6 +346,7 @@ case class Evaluator(home: os.Path, reporter, bspContext //new ManagedLoggedReporter(10, logger) ) + val out = System.out val in = System.in val err = System.err diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index cd467a3b..822d7985 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -222,7 +222,7 @@ trait JavaModule extends mill.Module with TaskModule with GenIdeaModule { outer allSourceFiles().map(_.path), compileClasspath().map(_.path), javacOptions() ++ T.ctx.bsp.args, - T.ctx().reporter + T.ctx().reporter(hashCode) ) } diff --git a/scalalib/src/ScalaModule.scala b/scalalib/src/ScalaModule.scala index 85f1b6db..5669ca8c 100644 --- a/scalalib/src/ScalaModule.scala +++ b/scalalib/src/ScalaModule.scala @@ -143,7 +143,7 @@ trait ScalaModule extends JavaModule { outer => scalacOptions() ++ T.ctx.bsp.args, scalaCompilerClasspath().map(_.path), scalacPluginClasspath().map(_.path), - T.ctx().reporter + T.ctx().reporter(hashCode) ) } -- cgit v1.2.3 From 30de346dbc3c0a5ed21e828c80a3176843e2a449 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 15:22:56 +0200 Subject: Added support for sending start/finish compilation notifications even when all targets are sent to the mill evaluator together. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 2 +- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 28 ++++++++++++++-------- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 25 ++++++++++++++----- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 8 +++---- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index bfe23444..24adaf1d 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -172,7 +172,7 @@ object MainMillBuildServer extends ExternalModule { } override def onBuildTaskFinish(params: TaskFinishParams): Unit = { - + println("Task Finish: " + params) } override def onBuildPublishDiagnostics( params: PublishDiagnosticsParams diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 846ffd42..8ab17d7e 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -2,7 +2,7 @@ package mill.contrib.bsp import java.io.File -import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, InverseSourcesParams, ScalaBuildServer, TextDocumentIdentifier} +import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, CompileReport, InverseSourcesParams, ScalaBuildServer, StatusCode, TaskFinishParams, TaskId, TextDocumentIdentifier} import ch.epfl.scala.{bsp4j => bsp} import mill.api.BspContext import sbt.internal.inc.ManagedLoggedReporter @@ -16,6 +16,7 @@ import scala.io.Source class BspLoggedReporter(client: bsp.BuildClient, targetId: BuildTargetIdentifier, + taskId: TaskId, compilationOriginId: Option[String], maxErrors: Int, logger: ManagedLogger) extends ManagedLoggedReporter(maxErrors, logger) { @@ -42,6 +43,20 @@ class BspLoggedReporter(client: bsp.BuildClient, super.logWarning(problem) } + 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, warnings) + compilationOriginId match { + case Some(id) => compileReport.setOriginId(id) + case None => + } + taskFinishParams.setData(compileReport) + client.onBuildTaskFinish(taskFinishParams) + } + //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 def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): @@ -74,15 +89,8 @@ class BspLoggedReporter(client: bsp.BuildClient, params } - private[this] def getTragetId(problem: Problem, server: BuildServer with ScalaBuildServer): - Option[BuildTargetIdentifier] = { - problem.position().sourceFile().asScala match { - case Some(file) => Option( - server.buildTargetInverseSources( - new InverseSourcesParams(new TextDocumentIdentifier(file.toURI.toString)) - ).get.getTargets.asScala.head) - case None => Option.empty[BuildTargetIdentifier] - } + private[this] def getStatusCode: StatusCode = { + if (errors > 0) StatusCode.ERROR else StatusCode.OK } private[this] def getErrorCode(file: Option[File], start: bsp.Position, end: bsp.Position, position: xsbti.Position): String = { diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 3937f1ab..e87768a6 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -238,12 +238,23 @@ class MillBuildServer(evaluator: Evaluator, l } - private[this] def getBspLoggedReporterPool(params: Parameters): Int => Option[ManagedLoggedReporter] = { + private[this] def getBspLoggedReporterPool(params: Parameters, taskStartMessage: String => String, + taskStartDataKind: String, taskStartData: BuildTargetIdentifier => Object): + Int => Option[ManagedLoggedReporter] = { (int: Int) => + val targetId = moduleCodeToTargetId(int) + val taskId = new TaskId(targetIdToModule(targetId).compile.hashCode.toString) if (moduleCodeToTargetId.contains(int)) { println("Module: " + int) + 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, - moduleCodeToTargetId(int), + targetId, + taskId, params.getOriginId, 10, getCompilationLogger))} else Option.empty[ManagedLoggedReporter] @@ -257,9 +268,9 @@ class MillBuildServer(evaluator: Evaluator, val params = TaskParameters.fromCompileParams(compileParams) val taskId = params.hashCode() val compileTasks = Strict.Agg(params.getTargets.map(targetId => targetIdToModule(targetId).compile):_*) - val result = millEvaluator.evaluate(compileTasks, - getBspLoggedReporterPool(params), + getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", + "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), new BspContext { override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) override def logStart(event: Event): Unit = {} @@ -282,7 +293,8 @@ class MillBuildServer(evaluator: Evaluator, val args = params.getArguments.getOrElse(Seq.empty[String]) val runTask = module.run(args.mkString(" ")) val runResult = millEvaluator.evaluate(Strict.Agg(runTask), - getBspLoggedReporterPool(params), + getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", + "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) if (runResult.failing.keyCount > 0) { new RunResult(StatusCode.ERROR) @@ -341,7 +353,8 @@ class MillBuildServer(evaluator: Evaluator, val results = millEvaluator.evaluate( Strict.Agg(testTask), - getBspLoggedReporterPool(params), + getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", + "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), bspContext, new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) val endTime = System.currentTimeMillis() diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 2ce3cb91..9a50f22f 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -37,9 +37,9 @@ object ModuleUtils { for ( module <- modules ) { val dataBuildTarget = computeScalaBuildTarget(module, evaluator) val capabilities = getModuleCapabilities(module, evaluator) - val buildTargetTag: String = module match { - case m: TestModule => BuildTargetTag.TEST - case m: JavaModule => "-" + val buildTargetTag: List[String] = module match { + case m: TestModule => List(BuildTargetTag.TEST) + case m: JavaModule => List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION) } val dependencies = module match { @@ -47,7 +47,7 @@ object ModuleUtils { } val buildTarget = new BuildTarget(moduleIdMap(module), - Collections.singletonList(buildTargetTag), + buildTargetTag.asJava, supportedLanguages.asJava, dependencies, capabilities) -- cgit v1.2.3 From 2a092dbb298065aeefc084463326ebfd194f82b5 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 17 Jul 2019 16:39:26 +0200 Subject: Fixed bug in computing the classDirectory for the scalac options request. Also changed the startServer command from the json connection file. --- .bsp/mill-bsp.json | 2 +- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 2 +- scratch/.bsp/mill-bsp.json | 2 +- scratch/benchmarks/src/main/scala/BenchmarkSource.scala | 2 +- scratch/bsp/src/test/scala/BspTests.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.bsp/mill-bsp.json b/.bsp/mill-bsp.json index 0c4587f9..fdc584d0 100644 --- a/.bsp/mill-bsp.json +++ b/.bsp/mill-bsp.json @@ -1 +1 @@ -{"name":"mill-bsp","argv":["/home/alexandra/mill-release", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} +{"name":"mill-bsp","argv":["mill", "-i", "dev.run", "scratch", "-i","mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index e87768a6..b191aed9 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -429,7 +429,7 @@ class MillBuildServer(evaluator: Evaluator, map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString).toList val index = m.millModuleSegments.parts.length - val classDirectory = m.millSourcePath.toNIO.toAbsolutePath.toUri.toString + val classDirectory = Evaluator.resolveDestPaths(os.pwd / "out", m.millModuleSegments).out.toIO.toURI.toString//m.millSourcePath.toNIO.toAbsolutePath.toUri.toString targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) case m: JavaModule => targetScalacOptions ++= List() diff --git a/scratch/.bsp/mill-bsp.json b/scratch/.bsp/mill-bsp.json index 6540631f..39d2d592 100644 --- a/scratch/.bsp/mill-bsp.json +++ b/scratch/.bsp/mill-bsp.json @@ -1 +1 @@ -{"name":"mill-bsp","argv":["mill", "-i", "dev.run", "scratch", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} +{"name":"mill-bsp","argv":["../out/dev/launcher/dest/run", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} diff --git a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala index 7ee88801..b690179c 100644 --- a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala +++ b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala @@ -1,8 +1,8 @@ package main.scala import org.apache.commons.io.FileUtils + import java.io.File -//import com.williamfiset.fastjavaio.InputReader object BenchmarkSource { //val reader = new InputReader() diff --git a/scratch/bsp/src/test/scala/BspTests.scala b/scratch/bsp/src/test/scala/BspTests.scala index 596a1816..21ba4bfe 100644 --- a/scratch/bsp/src/test/scala/BspTests.scala +++ b/scratch/bsp/src/test/scala/BspTests.scala @@ -3,6 +3,6 @@ import org.scalatest.FunSuite object BspTests extends FunSuite { test("test 1") { - assert(CoreTests().coreValue > 0) + assert(new CoreTests().coreValue > 0) } } -- cgit v1.2.3 From c6585259350412a5ecc1015755ad1f1108a03784 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 18 Jul 2019 12:30:04 +0200 Subject: Fixed bug in building the data field of a BuildTarget which represents a mill JavaModule. Until bsp updates specification on what to do with java modules, each mill JavaModule will translate into a bsp ScalaBuildTarget. --- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 9a50f22f..1d707350 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -72,9 +72,8 @@ object ModuleUtils { new BuildTargetCapabilities(true, canTest, true) } - //TODO: I think here I need to look at scalaLibraryIvyDeps, ivyDeps that contain - // "scala-compiler" and "scala-reflect" and at scalacPluginIvyDeps - def computeScalaBuildTarget(module: JavaModule, evaluator: Evaluator): Any = { + //TODO: Fix the data field for JavaModule when the bsp specification is updated + def computeScalaBuildTarget(module: JavaModule, evaluator: Evaluator): ScalaBuildTarget = { module match { case m: ScalaModule => val scalaVersion = evaluateInformativeTask(evaluator, m.scalaVersion, "") @@ -87,7 +86,14 @@ object ModuleUtils { map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString). toList.asJava) - case m: JavaModule => "This is just a test or java target" + case m: JavaModule => + val scalaVersion = "2.12.8" + new ScalaBuildTarget( + "or.scala-lang", + "2.12.8", + "2.12", + ScalaPlatform.JVM, + List.empty[String].asJava) } } -- cgit v1.2.3 From 2d03a6ca1706c9c34e4d29414e57031bf084ef21 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 18 Jul 2019 14:42:11 +0200 Subject: Fixed bug in computing the compilation output directory for the buildtTargetScalacOptions method. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index b191aed9..c3273e49 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -427,9 +427,8 @@ class MillBuildServer(evaluator: Evaluator, val options = evaluateInformativeTask(evaluator, m.scalacOptions, Seq.empty[String]).toList val classpath = evaluateInformativeTask(evaluator, m.compileClasspath, Agg.empty[PathRef]). map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString).toList - val index = m.millModuleSegments.parts.length - - val classDirectory = Evaluator.resolveDestPaths(os.pwd / "out", m.millModuleSegments).out.toIO.toURI.toString//m.millSourcePath.toNIO.toAbsolutePath.toUri.toString + val classDirectory = (Evaluator.resolveDestPaths(os.pwd / "out" , m.millModuleSegments). + dest / "classes").toIO.toURI.toString targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) case m: JavaModule => targetScalacOptions ++= List() -- cgit v1.2.3 From 90cc85577949416f5a367f86ba040a293915cb31 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 18 Jul 2019 17:05:54 +0200 Subject: Modified the sources and inversed sources requests to be compatible with returing directories of source files rather than individual source files. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index c3273e49..7684ba31 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -114,6 +114,7 @@ class MillBuildServer(evaluator: Evaluator, files } + //TODO: use mill's sources, same for resources override def buildTargetSources(sourcesParams: SourcesParams): CompletableFuture[SourcesResult] = { def computeSourcesResult: SourcesResult = { @@ -129,12 +130,14 @@ class MillBuildServer(evaluator: Evaluator, Agg.empty[PathRef]). map(pathRef => pathRef.path).toSeq - for (file <- getSourceFiles(sources)) { - itemSources ++= List(new SourceItem(file.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.FILE, false)) + for (source <- sources) { + itemSources ++= List( + new SourceItem(source.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.DIRECTORY, false)) } - for (file <- getSourceFiles(generatedSources)) { - itemSources ++= List(new SourceItem(file.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.FILE, true)) + for (genSource <- generatedSources) { + itemSources ++= List( + new SourceItem(genSource.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.DIRECTORY, true)) } items ++= List(new SourcesItem(targetId, itemSources.asJava)) @@ -154,7 +157,9 @@ class MillBuildServer(evaluator: Evaluator, val targets = (for (targetId <- targetIdToModule.keys if buildTargetSources(new SourcesParams(Collections.singletonList(targetId))). get.getItems.asScala.head.getSources.asScala. - exists(item => item.getUri.equals(textDocument.getUri))) + exists( + item => os.list(os.Path(item.getUri)). + map(dir => dir.toIO.toURI.toString).contains(textDocument.getUri))) yield targetId).toList.asJava new InverseSourcesResult(targets) } -- cgit v1.2.3 From 2e37532d915d54f61bc645faa6b0c73f29c5d9ea Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Fri, 19 Jul 2019 16:33:43 +0200 Subject: Added a root target for the mill base module of each project so that intellij can display the project correctly. --- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 14 ++++-- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 56 +++++++++++++++++++--- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 7684ba31..2c25a9fc 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -48,7 +48,10 @@ class MillBuildServer(evaluator: Evaluator, var millEvaluator: Evaluator = evaluator var millModules: Seq[JavaModule] = getMillModules(millEvaluator) var client: BuildClient = _ - var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap(millModules) + var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap( + millModules, + evaluator + ) var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) @@ -97,7 +100,8 @@ class MillBuildServer(evaluator: Evaluator, override def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = { recomputeTargets() handleExceptions[String, WorkspaceBuildTargetsResult]( - (in) => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), "") + (in) => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), + "") } private[this] def getSourceFiles(sources: Seq[os.Path]): Iterable[os.Path] = { @@ -114,7 +118,7 @@ class MillBuildServer(evaluator: Evaluator, files } - //TODO: use mill's sources, same for resources + override def buildTargetSources(sourcesParams: SourcesParams): CompletableFuture[SourcesResult] = { def computeSourcesResult: SourcesResult = { @@ -403,7 +407,7 @@ class MillBuildServer(evaluator: Evaluator, var cleaned = true for (targetId <- cleanCacheParams.getTargets.asScala) { val module = targetIdToModule(targetId) - val process = Runtime.getRuntime.exec(s"mill clean ${module.millModuleSegments.parts.mkString(".")}.compile") + val process = Runtime.getRuntime.exec(s"mill clean ${ModuleUtils.moduleName(module.millModuleSegments)}.compile") val processIn = process.getInputStream val processErr = process.getErrorStream @@ -527,7 +531,7 @@ class MillBuildServer(evaluator: Evaluator, private[this] def recomputeTargets(): Unit = { millModules = getMillModules(millEvaluator) - moduleToTargetId = ModuleUtils.getModuleTargetIdMap(millModules) + moduleToTargetId = ModuleUtils.getModuleTargetIdMap(millModules, millEvaluator) targetIdToModule = targetToModule(moduleToTargetId) moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) } diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 1d707350..d0db5fd0 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -1,12 +1,14 @@ package mill.contrib.bsp +import java.net.URI import java.util.Collections 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.{Discover, Segment, Segments, Task} +import mill.define.{BaseModule, Ctx, Discover, Module, Segment, Segments, Sources, Target, Task} import mill.eval._ import mill.eval.Evaluator import mill.scalajslib.ScalaJSModule @@ -14,6 +16,7 @@ import mill.scalalib.api.Util import mill.scalanativelib._ import mill.scalalib.{GenIdea, GenIdeaImpl, JavaModule, ScalaModule, TestModule} import mill.util.DummyLogger +import os.Path object ModuleUtils { @@ -31,8 +34,11 @@ object ModuleUtils { evaluator: Evaluator, supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { - val moduleIdMap = getModuleTargetIdMap(modules) - var moduleToTarget = Predef.Map[JavaModule, BuildTarget]() + val moduleIdMap = getModuleTargetIdMap(modules, evaluator) + val rootModule = getRootJavaModule(evaluator.rootModule) + var moduleToTarget = Predef.Map[JavaModule, BuildTarget]( + rootModule -> getRootTarget(rootModule, evaluator) + ) for ( module <- modules ) { val dataBuildTarget = computeScalaBuildTarget(module, evaluator) @@ -57,12 +63,47 @@ object ModuleUtils { buildTarget.setData(dataBuildTarget) buildTarget.setDisplayName(moduleName(module.millModuleSegments)) buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) - moduleToTarget ++= Map(module -> buildTarget) + + if (!moduleToTarget.contains(module)) moduleToTarget ++= Map(module -> buildTarget) } moduleToTarget } + def getRootJavaModule(rootBaseModule: BaseModule): JavaModule = { + implicit val ctx: Ctx = rootBaseModule.millOuterCtx + 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 sources: Sources = T.sources{millSourcePath} + override def generatedSources: Target[Seq[PathRef]] = T.sources{ + out() ++ target()} + } + } + + def getRootTarget(rootModule: JavaModule, evaluator: Evaluator): BuildTarget = { + + val rootTarget = new BuildTarget( + new BuildTargetIdentifier(rootModule.millSourcePath.toNIO.toAbsolutePath.toUri.toString), + List.empty[String].asJava, + List.empty[String].asJava, + List.empty[BuildTargetIdentifier].asJava, + new BuildTargetCapabilities(false, false, false)) + rootTarget.setBaseDirectory(rootModule.millSourcePath.toNIO.toAbsolutePath.toUri.toString) + rootTarget.setDataKind("scala") + rootTarget.setTags(List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION).asJava) + rootTarget.setData(computeScalaBuildTarget(rootModule, evaluator)) + val basePath = rootModule.millSourcePath.toIO.toPath + if (basePath.getNameCount >= 1) + rootTarget.setDisplayName(basePath.getName(basePath.getNameCount - 1) + "-root") + else rootTarget.setDisplayName("root") + rootTarget + } + def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { val canTest = module match { case module: TestModule => true @@ -126,8 +167,11 @@ object ModuleUtils { } } - def getModuleTargetIdMap(modules: Seq[JavaModule]): Predef.Map[JavaModule, BuildTargetIdentifier] = { - var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]() + def getModuleTargetIdMap(modules: Seq[JavaModule], evaluator:Evaluator): Predef.Map[JavaModule, BuildTargetIdentifier] = { + var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]( + getRootJavaModule(evaluator.rootModule) -> new BuildTargetIdentifier(evaluator.rootModule.millSourcePath. + toNIO.toAbsolutePath.toUri.toString) + ) for ( module <- modules ) { moduleToTarget ++= Map(module -> new BuildTargetIdentifier( -- cgit v1.2.3 From b5347266ee73d62659d01bdfe2d3444680a98747 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 22 Jul 2019 10:44:50 +0200 Subject: Modified the compilation diagnostics reporter to send not only the currently generated diagnostic, but also all of the past diagnostics discovered for a certain source file. This will be compatible with the idea that each set of diagnostics for a certain file invalidates all other diagnostics received in the past. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 22 ++++---- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 65 +++++++++++++++------- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 17 +++--- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 61 ++++++++++---------- scalalib/src/JavaModule.scala | 2 +- 5 files changed, 95 insertions(+), 72 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 24adaf1d..ba1dbcec 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -7,10 +7,11 @@ import java.nio.file.FileAlreadyExistsException import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, CleanCacheParams, CompileParams, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ShowMessageParams, TaskFinishParams, TaskProgressParams, TaskStartParams, TestParams, WorkspaceBuildTargetsResult} +import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, BuildTargetIdentifier, CleanCacheParams, CompileParams, CompileTask, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ScalacOptionsParams, ShowMessageParams, SourcesParams, TaskFinishParams, TaskProgressParams, TaskStartParams, TestParams, WorkspaceBuildTargetsResult} import mill._ -import mill.api.Strict -import mill.contrib.bsp.{BspLoggedReporter, MillBuildServer, ModuleUtils} +import mill.api.Result.{Aborted, Skipped} +import mill.api.{BspContext, Result, Strict} +import mill.contrib.bsp.{BspLoggedReporter, MillBspLogger, MillBuildServer, ModuleUtils, TaskParameters} import mill.define.{Command, Discover, ExternalModule, Target, Task} import mill.eval.Evaluator import mill.scalalib._ @@ -172,23 +173,24 @@ object MainMillBuildServer extends ExternalModule { } override def onBuildTaskFinish(params: TaskFinishParams): Unit = { - println("Task Finish: " + params) + //println("Task Finish: " + params) } override def onBuildPublishDiagnostics( params: PublishDiagnosticsParams ): Unit = { - diagnostics ++= List(params) + println("Diagnostics: " + params) } override def onBuildTargetDidChange(params: DidChangeBuildTarget): Unit = ??? } millServer.client = client millServer.initialized = true - println(millServer.buildTargetCleanCache( - new CleanCacheParams(millServer.millModules.map(m => millServer.moduleToTargetId(m)).asJava)).get) - println(millServer.buildTargetCompile( - new CompileParams(millServer.millModules.map(m => millServer.moduleToTargetId(m)).asJava)).get) - println("Diagnostics: " + client.diagnostics) + val compileParams = new CompileParams(millServer.moduleCodeToTargetId.values. + filter(t => millServer.targetIdToModule(t) != millServer.rootModule).toList.asJava) + val pool = millServer.getBspLoggedReporterPool(TaskParameters.fromCompileParams(compileParams), (t) => s"Started compiling target: $t", + "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)) + println(millServer.buildTargetCleanCache(new CleanCacheParams(millServer.moduleCodeToTargetId.values.toList.asJava)).get) + println(millServer.buildTargetCompile(compileParams).get) } /** diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 8ab17d7e..8e537fb8 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -1,16 +1,19 @@ package mill.contrib.bsp import java.io.File +import java.util.concurrent.{ConcurrentHashMap, ConcurrentMap} -import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, CompileReport, InverseSourcesParams, ScalaBuildServer, StatusCode, TaskFinishParams, TaskId, TextDocumentIdentifier} +import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, CompileReport, Diagnostic, InverseSourcesParams, ScalaBuildServer, StatusCode, TaskFinishParams, TaskId, TextDocumentIdentifier} import ch.epfl.scala.{bsp4j => bsp} import mill.api.BspContext +import org.eclipse.lsp4j.PublishDiagnosticsParams import sbt.internal.inc.ManagedLoggedReporter import sbt.internal.inc.schema.Position import sbt.internal.util.ManagedLogger import xsbti.{Problem, Severity} import scala.collection.JavaConverters._ +import scala.collection.concurrent import scala.compat.java8.OptionConverters._ import scala.io.Source @@ -24,6 +27,8 @@ class BspLoggedReporter(client: bsp.BuildClient, var errors = 0 var warnings = 0 var infos = 0 + var diagnosticMap: concurrent.Map[TextDocumentIdentifier, bsp.PublishDiagnosticsParams] = + new ConcurrentHashMap[TextDocumentIdentifier, bsp.PublishDiagnosticsParams]().asScala override def logError(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) @@ -61,31 +66,20 @@ class BspLoggedReporter(client: bsp.BuildClient, // associated to it, then the document field of the diagnostic is set to the uri of the target def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): bsp.PublishDiagnosticsParams = { + val diagnostic = getSingleDiagnostic(problem) val sourceFile = problem.position().sourceFile().asScala - val start = new bsp.Position( - problem.position.startLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), - problem.position.startOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) - val end = new bsp.Position( - problem.position.endLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), - problem.position.endOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) - val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) - diagnostic.setCode(problem.position.lineContent) - diagnostic.setSource("compiler from mill") - diagnostic.setSeverity( problem.severity match { - case Severity.Info => bsp.DiagnosticSeverity.INFORMATION - case Severity.Error => bsp.DiagnosticSeverity.ERROR - case Severity.Warn => bsp.DiagnosticSeverity.WARNING - } - ) - val textDocument = sourceFile.getOrElse(None) match { + val textDocument = new TextDocumentIdentifier( + sourceFile.getOrElse(None) match { case None => targetId.getUri case f: File => f.toPath.toUri.toString - } - val params = new bsp.PublishDiagnosticsParams( - new bsp.TextDocumentIdentifier(textDocument), - targetId, List(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 } @@ -93,6 +87,35 @@ class BspLoggedReporter(client: bsp.BuildClient, if (errors > 0) StatusCode.ERROR else StatusCode.OK } + private[this] def appendDiagnostics(textDocument: TextDocumentIdentifier, + currentDiagnostic: Diagnostic): List[Diagnostic] = { + diagnosticMap.getOrElse(textDocument, new bsp.PublishDiagnosticsParams( + textDocument, + targetId, + List.empty[Diagnostic].asJava, true)).getDiagnostics.asScala.toList ++ + List(currentDiagnostic) + } + + private[this] def getSingleDiagnostic(problem: Problem): Diagnostic ={ + + val start = new bsp.Position( + problem.position.startLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), + problem.position.startOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) + val end = new bsp.Position( + problem.position.endLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), + problem.position.endOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) + val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) + diagnostic.setCode(problem.position.lineContent) + diagnostic.setSource("compiler from mill") + diagnostic.setSeverity( problem.severity match { + case Severity.Info => bsp.DiagnosticSeverity.INFORMATION + case Severity.Error => bsp.DiagnosticSeverity.ERROR + case Severity.Warn => bsp.DiagnosticSeverity.WARNING + } + ) + diagnostic + } + private[this] def getErrorCode(file: Option[File], start: bsp.Position, end: bsp.Position, position: xsbti.Position): String = { file match { case None => position.lineContent diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 2c25a9fc..1666ed60 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -18,8 +18,6 @@ import sbt.internal.inc._ import xsbti.{Position, Problem, Severity} import xsbti.compile.{AnalysisContents, AnalysisStore, FileAnalysisStore} import xsbti.compile.analysis.SourceInfo - -import scala.collection.mutable.Map import mill.api.Result.{Failing, Failure, Success} import scala.collection.JavaConverters._ @@ -46,6 +44,7 @@ class MillBuildServer(evaluator: Evaluator, val millServerVersion: String = serverVersion var cancelator: () => Unit = () => () var millEvaluator: Evaluator = evaluator + var rootModule: JavaModule = ModuleUtils.getRootJavaModule(evaluator.rootModule) var millModules: Seq[JavaModule] = getMillModules(millEvaluator) var client: BuildClient = _ var moduleToTargetId: Predef.Map[JavaModule, BuildTargetIdentifier] = ModuleUtils.getModuleTargetIdMap( @@ -54,7 +53,7 @@ class MillBuildServer(evaluator: Evaluator, ) var targetIdToModule: Predef.Map[BuildTargetIdentifier, JavaModule] = targetToModule(moduleToTargetId) var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = - ModuleUtils.millModulesToBspTargets(millModules, evaluator, List("scala", "java")) + ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) var moduleCodeToTargetId: Predef.Map[Int, BuildTargetIdentifier] = for ( (targetId, module) <- targetIdToModule ) yield (targetId, module.hashCode()).swap @@ -247,14 +246,13 @@ class MillBuildServer(evaluator: Evaluator, l } - private[this] def getBspLoggedReporterPool(params: Parameters, taskStartMessage: String => String, + def getBspLoggedReporterPool(params: Parameters, taskStartMessage: String => String, taskStartDataKind: String, taskStartData: BuildTargetIdentifier => Object): Int => Option[ManagedLoggedReporter] = { (int: Int) => - val targetId = moduleCodeToTargetId(int) - val taskId = new TaskId(targetIdToModule(targetId).compile.hashCode.toString) if (moduleCodeToTargetId.contains(int)) { - println("Module: " + 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)) @@ -526,14 +524,15 @@ class MillBuildServer(evaluator: Evaluator, ev.rootModule.millInternal.segmentsToModules.values. collect { case m: scalalib.JavaModule => m - }.toSeq + }.toSeq ++ Seq(rootModule) } 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, evaluator, List("scala", "java")) + moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) } private[this] def handleExceptions[T, V](serverMethod: T => V, input: T): CompletableFuture[V] = { diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index d0db5fd0..36415c5f 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -31,40 +31,42 @@ object ModuleUtils { dummyModule, DummyLogger) def millModulesToBspTargets(modules: Seq[JavaModule], + rootModule: JavaModule, evaluator: Evaluator, supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { val moduleIdMap = getModuleTargetIdMap(modules, evaluator) - val rootModule = getRootJavaModule(evaluator.rootModule) - var moduleToTarget = Predef.Map[JavaModule, BuildTarget]( - rootModule -> getRootTarget(rootModule, evaluator) - ) + var moduleToTarget = Predef.Map[JavaModule, BuildTarget]() for ( module <- modules ) { - val dataBuildTarget = computeScalaBuildTarget(module, evaluator) - val capabilities = getModuleCapabilities(module, evaluator) - val buildTargetTag: List[String] = module match { - case m: TestModule => List(BuildTargetTag.TEST) - case m: JavaModule => List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION) + if (module == rootModule) { + moduleToTarget ++= Map(module -> getRootTarget(module, evaluator)) + } else { + val dataBuildTarget = computeScalaBuildTarget(module, evaluator) + val capabilities = getModuleCapabilities(module, evaluator) + val buildTargetTag: List[String] = module match { + case m: TestModule => List(BuildTargetTag.TEST) + case m: JavaModule => List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION) + } + + val dependencies = module match { + case m: JavaModule => m.moduleDeps.map(dep => moduleIdMap(dep)).toList.asJava + } + + val buildTarget = new BuildTarget(moduleIdMap(module), + buildTargetTag.asJava, + supportedLanguages.asJava, + dependencies, + capabilities) + if (module.isInstanceOf[ScalaModule]) { + buildTarget.setDataKind("scala") + } + buildTarget.setData(dataBuildTarget) + buildTarget.setDisplayName(moduleName(module.millModuleSegments)) + buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) + + if (!moduleToTarget.contains(module)) moduleToTarget ++= Map(module -> buildTarget) } - - val dependencies = module match { - case m: JavaModule => m.moduleDeps.map(dep => moduleIdMap(dep)).toList.asJava - } - - val buildTarget = new BuildTarget(moduleIdMap(module), - buildTargetTag.asJava, - supportedLanguages.asJava, - dependencies, - capabilities) - if (module.isInstanceOf[ScalaModule]) { - buildTarget.setDataKind("scala") - } - buildTarget.setData(dataBuildTarget) - buildTarget.setDisplayName(moduleName(module.millModuleSegments)) - buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) - - if (!moduleToTarget.contains(module)) moduleToTarget ++= Map(module -> buildTarget) } moduleToTarget @@ -168,10 +170,7 @@ object ModuleUtils { } def getModuleTargetIdMap(modules: Seq[JavaModule], evaluator:Evaluator): Predef.Map[JavaModule, BuildTargetIdentifier] = { - var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]( - getRootJavaModule(evaluator.rootModule) -> new BuildTargetIdentifier(evaluator.rootModule.millSourcePath. - toNIO.toAbsolutePath.toUri.toString) - ) + var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]() for ( module <- modules ) { moduleToTarget ++= Map(module -> new BuildTargetIdentifier( diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index 822d7985..5405828f 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -221,7 +221,7 @@ trait JavaModule extends mill.Module with TaskModule with GenIdeaModule { outer upstreamCompileOutput(), allSourceFiles().map(_.path), compileClasspath().map(_.path), - javacOptions() ++ T.ctx.bsp.args, + javacOptions() ++ T.ctx().bsp.args, T.ctx().reporter(hashCode) ) } -- cgit v1.2.3 From 5a49a273cf86b58b18395edd05f57c7c209f3fe7 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 22 Jul 2019 11:53:28 +0200 Subject: Made the variables that count erros and warnings in the compilation reporter atomic, such that their incrementation will be thread safe. --- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 8e537fb8..b4647fed 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -1,6 +1,7 @@ package mill.contrib.bsp import java.io.File +import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.{ConcurrentHashMap, ConcurrentMap} import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, CompileReport, Diagnostic, InverseSourcesParams, ScalaBuildServer, StatusCode, TaskFinishParams, TaskId, TextDocumentIdentifier} @@ -24,27 +25,27 @@ class BspLoggedReporter(client: bsp.BuildClient, maxErrors: Int, logger: ManagedLogger) extends ManagedLoggedReporter(maxErrors, logger) { - var errors = 0 - var warnings = 0 - var infos = 0 + var errors = new AtomicInteger(0) + var warnings = new AtomicInteger(0) + var infos = new AtomicInteger(0) var diagnosticMap: concurrent.Map[TextDocumentIdentifier, bsp.PublishDiagnosticsParams] = new ConcurrentHashMap[TextDocumentIdentifier, bsp.PublishDiagnosticsParams]().asScala override def logError(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) - errors += 1 + errors.incrementAndGet() super.logError(problem) } override def logInfo(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) - infos += 1 + infos.incrementAndGet() super.logInfo(problem) } override def logWarning(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) - warnings += 1 + warnings.incrementAndGet() super.logWarning(problem) } @@ -53,7 +54,7 @@ class BspLoggedReporter(client: bsp.BuildClient, taskFinishParams.setEventTime(System.currentTimeMillis()) taskFinishParams.setMessage("Finished compiling target: " + targetId.getUri) taskFinishParams.setDataKind("compile-report") - val compileReport = new CompileReport(targetId, errors, warnings) + val compileReport = new CompileReport(targetId, errors.get, warnings.get) compilationOriginId match { case Some(id) => compileReport.setOriginId(id) case None => @@ -84,16 +85,16 @@ class BspLoggedReporter(client: bsp.BuildClient, } private[this] def getStatusCode: StatusCode = { - if (errors > 0) StatusCode.ERROR else StatusCode.OK + if (errors.get > 0) StatusCode.ERROR else StatusCode.OK } private[this] def appendDiagnostics(textDocument: TextDocumentIdentifier, currentDiagnostic: Diagnostic): List[Diagnostic] = { - diagnosticMap.getOrElse(textDocument, new bsp.PublishDiagnosticsParams( + diagnosticMap.putIfAbsent(textDocument, new bsp.PublishDiagnosticsParams( textDocument, targetId, - List.empty[Diagnostic].asJava, true)).getDiagnostics.asScala.toList ++ - List(currentDiagnostic) + List.empty[Diagnostic].asJava, true)) + diagnosticMap(textDocument).getDiagnostics.asScala.toList ++ List(currentDiagnostic) } private[this] def getSingleDiagnostic(problem: Problem): Diagnostic ={ -- cgit v1.2.3 From 670698881ffbf632013b00dcd9340ff66c6024eb Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 22 Jul 2019 14:27:27 +0200 Subject: Modified the construction of the string ids for each BuildTargetIdentifier as there were collisions in case of cross modules. Also fixed bug in the clean cache method. Now, the command is using the rendering of millModuleSegments rather than the module name function. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 2 +- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 1666ed60..9c74f2f0 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -405,7 +405,7 @@ class MillBuildServer(evaluator: Evaluator, var cleaned = true for (targetId <- cleanCacheParams.getTargets.asScala) { val module = targetIdToModule(targetId) - val process = Runtime.getRuntime.exec(s"mill clean ${ModuleUtils.moduleName(module.millModuleSegments)}.compile") + val process = Runtime.getRuntime.exec(s"mill clean ${module.millModuleSegments.render}.compile") val processIn = process.getInputStream val processErr = process.getErrorStream diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 36415c5f..d593bceb 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -14,7 +14,7 @@ import mill.eval.Evaluator import mill.scalajslib.ScalaJSModule import mill.scalalib.api.Util import mill.scalanativelib._ -import mill.scalalib.{GenIdea, GenIdeaImpl, JavaModule, ScalaModule, TestModule} +import mill.scalalib.{CrossModuleBase, GenIdea, GenIdeaImpl, JavaModule, ScalaModule, TestModule} import mill.util.DummyLogger import os.Path @@ -36,11 +36,11 @@ object ModuleUtils { supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { val moduleIdMap = getModuleTargetIdMap(modules, evaluator) - var moduleToTarget = Predef.Map[JavaModule, BuildTarget]() + var moduleToTarget = Map.empty[JavaModule, BuildTarget] for ( module <- modules ) { if (module == rootModule) { - moduleToTarget ++= Map(module -> getRootTarget(module, evaluator)) + moduleToTarget ++= Map(module -> getRootTarget(module, evaluator, moduleIdMap(module))) } else { val dataBuildTarget = computeScalaBuildTarget(module, evaluator) val capabilities = getModuleCapabilities(module, evaluator) @@ -87,10 +87,10 @@ object ModuleUtils { } } - def getRootTarget(rootModule: JavaModule, evaluator: Evaluator): BuildTarget = { + def getRootTarget(rootModule: JavaModule, evaluator: Evaluator, targetId: BuildTargetIdentifier): BuildTarget = { val rootTarget = new BuildTarget( - new BuildTargetIdentifier(rootModule.millSourcePath.toNIO.toAbsolutePath.toUri.toString), + targetId, List.empty[String].asJava, List.empty[String].asJava, List.empty[BuildTargetIdentifier].asJava, @@ -173,8 +173,10 @@ object ModuleUtils { var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]() for ( module <- modules ) { + moduleToTarget ++= Map(module -> new BuildTargetIdentifier( - module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString + (module.millOuterCtx.millSourcePath / os.RelPath(moduleName(module.millModuleSegments))). + toNIO.toAbsolutePath.toUri.toString )) } -- cgit v1.2.3 From 88bed3805d01eebb9193684074a35f0298f124ce Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 22 Jul 2019 16:02:24 +0200 Subject: Renamed the main class as well as the method that starts the bsp server. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 31 ++++------------------ 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index ba1dbcec..5e9f7c42 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -25,11 +25,11 @@ import scala.collection.JavaConverters._ import scala.io.Source -object MainMillBuildServer extends ExternalModule { +object BSP extends ExternalModule { implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() - lazy val millDiscover: Discover[MainMillBuildServer.this.type] = Discover[this.type] + lazy val millDiscover: Discover[BSP.this.type] = Discover[this.type] val version = "1.0.0" val bspVersion = "2.0.0-M4" val languages = List("scala", "java") @@ -57,7 +57,7 @@ object MainMillBuildServer extends ExternalModule { List("java","-DMILL_CLASSPATH=" + millPath, "-DMILL_VERSION=0.4.0", "-Djna.nosys=true", "-cp", millPath, - "mill.MillMain mill.contrib.MainMillBuildServer/startServer").asJava, + "mill.MillMain mill.contrib.BSP/start").asJava, version, bspVersion, languages.asJava)) @@ -97,21 +97,6 @@ object MainMillBuildServer extends ExternalModule { } - /** - * Computes a mill task for resolving all JavaModules - * defined in the build.sc file of the project to build. - * This file should be in the working directory of the client. - * @param ev: Environment, used by mill to evaluate tasks - * @return: mill.Task which evaluates to a sequence of all - * the JavaModules defined for a project - */ - def modules(ev: Evaluator): Task[Seq[JavaModule]] = T.task{ - ev.rootModule.millInternal.segmentsToModules.values. - collect { - case m: scalalib.JavaModule => m - }.toSeq - } - /** * Computes a mill command which starts the mill-bsp * server and establishes connection to client. Waits @@ -121,7 +106,7 @@ object MainMillBuildServer extends ExternalModule { * @return: mill.Command which executes the starting of the * server */ - def startServer(ev: Evaluator): Command[Unit] = T.command { + 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) val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) @@ -184,13 +169,7 @@ object MainMillBuildServer extends ExternalModule { ??? } millServer.client = client - millServer.initialized = true - val compileParams = new CompileParams(millServer.moduleCodeToTargetId.values. - filter(t => millServer.targetIdToModule(t) != millServer.rootModule).toList.asJava) - val pool = millServer.getBspLoggedReporterPool(TaskParameters.fromCompileParams(compileParams), (t) => s"Started compiling target: $t", - "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)) - println(millServer.buildTargetCleanCache(new CleanCacheParams(millServer.moduleCodeToTargetId.values.toList.asJava)).get) - println(millServer.buildTargetCompile(compileParams).get) + } } /** -- cgit v1.2.3 From 5ea90651ba6be7c3ea86f1d3123fa6af5b2f9ca8 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 23 Jul 2019 10:39:26 +0200 Subject: Updated the clean cache method of the mill build server to use system properties in order to assemble the correct, OS-independent, mill clean command. --- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 9c74f2f0..e5319b77 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -405,7 +405,14 @@ class MillBuildServer(evaluator: Evaluator, var cleaned = true for (targetId <- cleanCacheParams.getTargets.asScala) { val module = targetIdToModule(targetId) - val process = Runtime.getRuntime.exec(s"mill clean ${module.millModuleSegments.render}.compile") + val cleanCommand = List("java", + s"-DMILL_CLASSPATH=${System.getProperty("MILL_CLASSPATH")}", + s"-DMILL_VERSION=${System.getProperty("MILL_VERSION")}", + "-Djna.nosys=true", "-cp", + System.getProperty("MILL_CLASSPATH"), + "mill.MillMain", "clean", + s"${module.millModuleSegments.render}.compile") + val process = Runtime.getRuntime.exec(cleanCommand.mkString(" ")) val processIn = process.getInputStream val processErr = process.getErrorStream -- cgit v1.2.3 From f0bcc011d7592db9b6e2fc19648c8fb3c7dd3c9d Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 23 Jul 2019 17:56:38 +0200 Subject: Addressed code review feedback. --- .gitignore | 3 +- contrib/bsp/readme.md | 38 ++++++++ .../bsp/src/mill/contrib/MainMillBuildServer.scala | 94 ++++-------------- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 29 +----- .../bsp/src/mill/contrib/bsp/BspTestReporter.scala | 51 +++++----- .../bsp/src/mill/contrib/bsp/MillBspLogger.scala | 22 +++-- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 40 +++----- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 108 ++++++++++----------- info.txt | 1 + main/api/src/mill/api/BspCompileArguments.scala | 4 - main/core/src/eval/Evaluator.scala | 4 +- scratch/.bsp/mill-bsp.json | 1 - .../src/main/scala/BenchmarkSource.scala | 2 + scratch/bsp/src/test/scala/BspTests.scala | 6 +- scratch/build.sc | 2 +- 15 files changed, 178 insertions(+), 227 deletions(-) create mode 100644 contrib/bsp/readme.md create mode 100644 info.txt delete mode 100644 scratch/.bsp/mill-bsp.json diff --git a/.gitignore b/.gitignore index b5fa3ece..53cea62c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,4 @@ out/ /.metals/ contrib/bsp/mill-external-bs contrib/bsp/mill-out-bs -mill.iml -./bsp \ No newline at end of file +mill.iml \ No newline at end of file diff --git a/contrib/bsp/readme.md b/contrib/bsp/readme.md new file mode 100644 index 00000000..c3846f66 --- /dev/null +++ b/contrib/bsp/readme.md @@ -0,0 +1,38 @@ +# Build Server Protocol support for mill + +The contrib.bsp module was created in order to integrate the Mill build tool +with IntelliJ IDEA via the Build Server Protocol (BSP). It implements most of +the server side functionality described in BSP, and can therefore connect to a +BSP client, including the one behind IntelliJ IDEA. This allows a lot of mill +tasks to be executed from the IDE. + +# Importing an existing mill project in IntelliJ via BSP + +1) Following the mill installation instructions +2) Add the following import statement in the build.sc +of your project: + + `import $ivy.com.lihaoyi::mill-contrib-bsp:$MILL_VERSION` + +3) Run the following command in the working directory of your project: + + `mill -i mill.contrib.BSP/install` + This should create a `.bsp/` directory inside your working directory, + containing a BSP connection file that clients can use to start the + BSP server for Mill. + + This command should be ran whenever you chnage the version of mill that + you use. + +4) Now you can use IntelliJ to import your project from existing sources +via bsp ( currently available in the nightly release ). Note: It might +take a few minutes to import a project the very first time. + +## Known Issues: + +- Sometimes build from IntelliJ might fail due to a NoClassDefFoundException +being thrown during the evaluation of tasks, a bug not easy to reproduce. +In this case it is recommended to refresh the bsp project. + +- Currently it's not possible ro run scala classes from intelliJ, but this +issue is being investigated diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index 5e9f7c42..b5b9748a 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -1,28 +1,19 @@ package mill.contrib -import java.io.{BufferedReader, File, InputStream, InputStreamReader, OutputStream} - import play.api.libs.json._ import java.nio.file.FileAlreadyExistsException -import java.util.concurrent.{CancellationException, CompletableFuture, ExecutorService, Executors, Future} +import java.util.concurrent.Executors import upickle.default._ -import ch.epfl.scala.bsp4j.{BspConnectionDetails, BuildClient, BuildTargetIdentifier, CleanCacheParams, CompileParams, CompileTask, DidChangeBuildTarget, LogMessageParams, PublishDiagnosticsParams, ScalaTestClassesParams, ScalacOptionsParams, ShowMessageParams, SourcesParams, TaskFinishParams, TaskProgressParams, TaskStartParams, TestParams, WorkspaceBuildTargetsResult} +import ch.epfl.scala.bsp4j._ import mill._ -import mill.api.Result.{Aborted, Skipped} -import mill.api.{BspContext, Result, Strict} -import mill.contrib.bsp.{BspLoggedReporter, MillBspLogger, MillBuildServer, ModuleUtils, TaskParameters} -import mill.define.{Command, Discover, ExternalModule, Target, Task} +import mill.contrib.bsp.ModuleUtils +import mill.define.{Command, Discover, ExternalModule} import mill.eval.Evaluator -import mill.scalalib._ -import mill.util.DummyLogger -import org.eclipse.lsp4j.jsonrpc.{CompletableFutures, Launcher} -import requests.Compress.None -import sbt.testing.Event -import upickle.default +import mill.scalalib.JavaModule +import org.eclipse.lsp4j.jsonrpc.Launcher import scala.collection.JavaConverters._ -import scala.io.Source object BSP extends ExternalModule { @@ -31,13 +22,11 @@ object BSP extends ExternalModule { lazy val millDiscover: Discover[BSP.this.type] = Discover[this.type] val version = "1.0.0" - val bspVersion = "2.0.0-M4" + val bspVersion = "2.0.0" val languages = List("scala", "java") - // returns the mill installation path in the user's system - def whichMill(): String = { - import sys.process._ - "which mill"!! + 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 @@ -52,12 +41,13 @@ object BSP extends ExternalModule { "languages" -> new JsArray(connection.getLanguages.asScala.map(string => JsString(string)).toIndexedSeq) ) } - val millPath = whichMill().replaceAll("\n", "") + val millPath = scala.sys.props("MILL_CLASSPATH") Json.toJson(new BspConnectionDetails("mill-bsp", - List("java","-DMILL_CLASSPATH=" + millPath, - "-DMILL_VERSION=0.4.0", "-Djna.nosys=true", "-cp", + 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, + "mill.MillMain", "mill.contrib.BSP/start").asJava, version, bspVersion, languages.asJava)) @@ -80,16 +70,14 @@ object BSP extends ExternalModule { */ def install(ev: Evaluator): Command[Unit] = T.command{ val bspDirectory = os.pwd / ".bsp" - + if (! os.exists(bspDirectory)) os.makeDir.all(bspDirectory) try { - os.makeDir(bspDirectory) - os.write(bspDirectory / "mill-bsp.json", Json.stringify(createBspConnectionJson())) + os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) } catch { case e: FileAlreadyExistsException => println("The bsp connection json file probably exists already - will be overwritten") - os.remove.all(bspDirectory) - install(ev) - () + os.remove(bspDirectory / "mill.json") + os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) //TODO: Do I want to catch this or throw the exception? case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + " " + e.getStackTrace.toString) @@ -132,7 +120,7 @@ object BSP extends ExternalModule { System.err.println("Cause: " + e.getCause) System.err.println("Message: " + e.getMessage) System.err.println("Exception class: " + e.getClass) - e.printStackTrace() + System.err.println("Stack Trace: " + e.getStackTrace) } finally { System.err.println("Shutting down executor") executor.shutdown() @@ -143,48 +131,8 @@ object BSP extends ExternalModule { val eval = new Evaluator(ev.home, ev.outPath, ev.externalOutPath, ev.rootModule, ev.log, ev.classLoaderSig, ev.workerCache, ev.env, false) val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) - val client = new BuildClient { - var diagnostics = List.empty[PublishDiagnosticsParams] - override def onBuildShowMessage(params: ShowMessageParams): Unit = { - - } - override def onBuildLogMessage(params: LogMessageParams): Unit = { - - } - override def onBuildTaskStart(params: TaskStartParams): Unit = { - - } - override def onBuildTaskProgress(params: TaskProgressParams): Unit = { - - } - override def onBuildTaskFinish(params: TaskFinishParams): Unit = { - //println("Task Finish: " + params) - } - override def onBuildPublishDiagnostics( - params: PublishDiagnosticsParams - ): Unit = { - println("Diagnostics: " + params) - } - override def onBuildTargetDidChange(params: DidChangeBuildTarget): Unit = - ??? - } - millServer.client = client - } - } - /** - * Allows minimal testing and installing the build server - * from the command line. - * @param args: can be - exp: executes code in the experiment - * method, helps with testing the server - * - install: installs the mill-bsp server in the - * working directory. - */ - def main(args: Array[String]) { - args(0) match { - case e: String => println("Wrong command, you can only use:\n " + - "install - creates the bsp connection json file\n") + millServer.initialized = true + println(millServer.workspaceBuildTargets().get) } - - } } \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index b4647fed..06c45b79 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -4,12 +4,9 @@ import java.io.File import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.{ConcurrentHashMap, ConcurrentMap} -import ch.epfl.scala.bsp4j.{BuildServer, BuildTargetIdentifier, CompileReport, Diagnostic, InverseSourcesParams, ScalaBuildServer, StatusCode, TaskFinishParams, TaskId, TextDocumentIdentifier} +import ch.epfl.scala.bsp4j._ import ch.epfl.scala.{bsp4j => bsp} -import mill.api.BspContext -import org.eclipse.lsp4j.PublishDiagnosticsParams import sbt.internal.inc.ManagedLoggedReporter -import sbt.internal.inc.schema.Position import sbt.internal.util.ManagedLogger import xsbti.{Problem, Severity} @@ -38,13 +35,13 @@ class BspLoggedReporter(client: bsp.BuildClient, } override def logInfo(problem: Problem): Unit = { - client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) infos.incrementAndGet() super.logInfo(problem) } override def logWarning(problem: Problem): Unit = { - client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) + client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) warnings.incrementAndGet() super.logWarning(problem) } @@ -76,8 +73,8 @@ class BspLoggedReporter(client: bsp.BuildClient, }) val params = new bsp.PublishDiagnosticsParams(textDocument, targetId, - appendDiagnostics(textDocument, diagnostic).asJava - , true) + appendDiagnostics(textDocument, diagnostic).asJava, + true) if (originId.nonEmpty) { params.setOriginId(originId.get) } diagnosticMap.put(textDocument, params) @@ -116,20 +113,4 @@ class BspLoggedReporter(client: bsp.BuildClient, ) diagnostic } - - private[this] def getErrorCode(file: Option[File], start: bsp.Position, end: bsp.Position, position: xsbti.Position): String = { - file match { - case None => position.lineContent - case f: Option[File] => - val source = Source.fromFile(f.get) - source.close() - val lines = source.getLines.toSeq - val code = lines(start.getLine).substring(start.getCharacter) + - lines.take(start.getLine - 1).takeRight(lines.length - end.getLine - 1).mkString("\n") + - lines(end.getLine).substring(0, end.getCharacter + 1) - code - } - - } - } diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 4027cea9..69471675 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -27,7 +27,6 @@ class BspTestReporter( taskStartParams.setData(new TestStart(getDisplayName(event))) taskStartParams.setMessage("Starting running: " + getDisplayName(event)) client.onBuildTaskStart(taskStartParams) - println("Logged start") } override def logFinish(event: Event): Unit = { @@ -39,31 +38,30 @@ class BspTestReporter( case default => StatusCode.OK }) taskFinishParams.setDataKind("test-finished") - val testFinish = new TestFinish( - getDisplayName(event), - event.status match { - case sbt.testing.Status.Success => - passed += 1 - TestStatus.PASSED - case sbt.testing.Status.Canceled => - cancelled += 1 - TestStatus.CANCELLED - case sbt.testing.Status.Error => - failed += 1 - TestStatus.FAILED - case sbt.testing.Status.Failure => - failed += 1 - TestStatus.FAILED - case sbt.testing.Status.Ignored => - ignored += 1 - TestStatus.IGNORED - case sbt.testing.Status.Skipped => - skipped += 1 - TestStatus.SKIPPED - case sbt.testing.Status.Pending => - skipped += 1 - TestStatus.SKIPPED //TODO: what to do here - }) + val status = event.status match { + case sbt.testing.Status.Success => + passed += 1 + TestStatus.PASSED + case sbt.testing.Status.Canceled => + cancelled += 1 + TestStatus.CANCELLED + case sbt.testing.Status.Error => + failed += 1 + TestStatus.FAILED + case sbt.testing.Status.Failure => + failed += 1 + TestStatus.FAILED + case sbt.testing.Status.Ignored => + ignored += 1 + TestStatus.IGNORED + case sbt.testing.Status.Skipped => + skipped += 1 + TestStatus.SKIPPED + case sbt.testing.Status.Pending => + skipped += 1 + TestStatus.SKIPPED //TODO: what to do here + } + val testFinish = new TestFinish(getDisplayName(event), status) taskFinishParams.setData(testFinish) taskFinishParams.setEventTime(System.currentTimeMillis()) taskFinishParams.setMessage("Finished running: " + getDisplayName(event)) @@ -76,7 +74,6 @@ class BspTestReporter( exception.getClass.toString)) } client.onBuildTaskFinish(taskFinishParams) - println("Logged finish") } def getDisplayName(e: Event): String = { diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala index d7dd62b1..811c898c 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala @@ -9,15 +9,19 @@ import mill.util.ProxyLogger class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends ProxyLogger(logger) { override def ticker(s: String): Unit = { - val progressString = s.split(" ")(0) - val progress = progressString.substring(1, progressString.length - 1).split("/") - val params = new TaskProgressParams(new TaskId(taskId.toString)) - params.setEventTime(System.currentTimeMillis()) - params.setMessage(s) - params.setUnit(s.split(" ")(1)) - params.setProgress(progress(0).toLong) - params.setTotal(progress(1).toLong) - client.onBuildTaskProgress(params) + try { + val progressString = s.split(" ")(0) + val progress = progressString.substring(1, progressString.length - 1).split("/") + val params = new TaskProgressParams(new TaskId(taskId.toString)) + params.setEventTime(System.currentTimeMillis()) + params.setMessage(s) + params.setUnit(s.split(" ")(1)) + params.setProgress(progress(0).toLong) + params.setTotal(progress(1).toLong) + client.onBuildTaskProgress(params) + } catch { + case e: Exception => + } } } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index e5319b77..b5ebf83e 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -1,11 +1,11 @@ package mill.contrib.bsp + import java.io.File import sbt.testing._ -import java.util.{Calendar, Collections} +import java.util.Collections import java.util.concurrent.CompletableFuture -import scala.compat.java8.OptionConverters._ import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import mill.{scalalib, _} @@ -13,19 +13,13 @@ import mill.api.{BspContext, Loose, Result, Strict} import mill.contrib.bsp.ModuleUtils._ import mill.eval.Evaluator import mill.scalalib._ -import mill.scalalib.api.{CompilationResult, ZincWorkerApi} +import mill.scalalib.api.CompilationResult import sbt.internal.inc._ -import xsbti.{Position, Problem, Severity} -import xsbti.compile.{AnalysisContents, AnalysisStore, FileAnalysisStore} -import xsbti.compile.analysis.SourceInfo -import mill.api.Result.{Failing, Failure, Success} import scala.collection.JavaConverters._ import mill.modules.Jvm -import mill.util.{Ctx, PrintLogger} -import mill.define.{Discover, ExternalModule, Target, Task} -import org.eclipse.lsp4j.InitializeError -import org.eclipse.lsp4j.jsonrpc.messages.{ResponseError, ResponseErrorCode} +import mill.util.Ctx +import mill.define.{Discover, ExternalModule} import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange @@ -43,7 +37,7 @@ class MillBuildServer(evaluator: Evaluator, val supportedLanguages: List[String] = languages val millServerVersion: String = serverVersion var cancelator: () => Unit = () => () - var millEvaluator: Evaluator = evaluator + val millEvaluator: Evaluator = evaluator var rootModule: JavaModule = ModuleUtils.getRootJavaModule(evaluator.rootModule) var millModules: Seq[JavaModule] = getMillModules(millEvaluator) var client: BuildClient = _ @@ -55,7 +49,7 @@ class MillBuildServer(evaluator: Evaluator, var moduleToTarget: Predef.Map[JavaModule, BuildTarget] = ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) var moduleCodeToTargetId: Predef.Map[Int, BuildTargetIdentifier] = - for ( (targetId, module) <- targetIdToModule ) yield (targetId, module.hashCode()).swap + for ( (targetId, module) <- targetIdToModule ) yield (module.hashCode(), targetId) var initialized = false var clientInitialized = false @@ -156,15 +150,12 @@ class MillBuildServer(evaluator: Evaluator, def getInverseSourcesResult: InverseSourcesResult = { val textDocument = inverseSourcesParams.getTextDocument - - val targets = (for (targetId <- targetIdToModule.keys - if buildTargetSources(new SourcesParams(Collections.singletonList(targetId))). - get.getItems.asScala.head.getSources.asScala. - exists( - item => os.list(os.Path(item.getUri)). - map(dir => dir.toIO.toURI.toString).contains(textDocument.getUri))) - yield targetId).toList.asJava - new InverseSourcesResult(targets) + 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)) + new InverseSourcesResult(targets.asJava) } handleExceptions[String, InverseSourcesResult]((in) => getInverseSourcesResult, "") } @@ -313,7 +304,7 @@ class MillBuildServer(evaluator: Evaluator, } private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { - System.err.println("Results: " + results.rawValues) + if (results.failing.keyCount > 0) { StatusCode.ERROR } @@ -340,7 +331,6 @@ class MillBuildServer(evaluator: Evaluator, val module = targetIdToModule(targetId) module match { case m: TestModule => val testModule = m.asInstanceOf[TestModule] - println("Arguments: " + argsMap(targetId)) val testTask = testModule.testLocal(argsMap(targetId):_*) // notifying the client that the testing of this build target started @@ -356,8 +346,6 @@ class MillBuildServer(evaluator: Evaluator, new TaskId(testTask.hashCode().toString), Seq.empty[String]) - println("BspContext: " + bspContext) - val results = millEvaluator.evaluate( Strict.Agg(testTask), getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index d593bceb..c5f203b5 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -21,55 +21,16 @@ import os.Path object ModuleUtils { - object dummyModule extends mill.define.ExternalModule { - lazy val millDiscover: Discover[dummyModule.this.type] = Discover[this.type] - } - - val dummyEvalautor: Evaluator = new Evaluator(os.pwd / "contrib" / "bsp" / "mill-bs", - os.pwd / "contrib" / "bsp" / "mill-out-bs", - os.pwd / "contrib" / "bsp" / "mill-external-bs", - dummyModule, DummyLogger) - def millModulesToBspTargets(modules: Seq[JavaModule], rootModule: JavaModule, evaluator: Evaluator, supportedLanguages: List[String]): Predef.Map[JavaModule, BuildTarget] = { val moduleIdMap = getModuleTargetIdMap(modules, evaluator) - var moduleToTarget = Map.empty[JavaModule, BuildTarget] - - for ( module <- modules ) { - if (module == rootModule) { - moduleToTarget ++= Map(module -> getRootTarget(module, evaluator, moduleIdMap(module))) - } else { - val dataBuildTarget = computeScalaBuildTarget(module, evaluator) - val capabilities = getModuleCapabilities(module, evaluator) - val buildTargetTag: List[String] = module match { - case m: TestModule => List(BuildTargetTag.TEST) - case m: JavaModule => List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION) - } - - val dependencies = module match { - case m: JavaModule => m.moduleDeps.map(dep => moduleIdMap(dep)).toList.asJava - } - - val buildTarget = new BuildTarget(moduleIdMap(module), - buildTargetTag.asJava, - supportedLanguages.asJava, - dependencies, - capabilities) - if (module.isInstanceOf[ScalaModule]) { - buildTarget.setDataKind("scala") - } - buildTarget.setData(dataBuildTarget) - buildTarget.setDisplayName(moduleName(module.millModuleSegments)) - buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) - - if (!moduleToTarget.contains(module)) moduleToTarget ++= Map(module -> buildTarget) - } - } - moduleToTarget + (for ( module <- modules ) + yield (module, getTarget(rootModule, module, evaluator, moduleIdMap))).toMap + } def getRootJavaModule(rootBaseModule: BaseModule): JavaModule = { @@ -87,10 +48,13 @@ object ModuleUtils { } } - def getRootTarget(rootModule: JavaModule, evaluator: Evaluator, targetId: BuildTargetIdentifier): BuildTarget = { + def getRootTarget( + rootModule: JavaModule, + evaluator: Evaluator, + moduleIdMap: Map[JavaModule, BuildTargetIdentifier]): BuildTarget = { val rootTarget = new BuildTarget( - targetId, + moduleIdMap(rootModule), List.empty[String].asJava, List.empty[String].asJava, List.empty[BuildTargetIdentifier].asJava, @@ -98,7 +62,7 @@ object ModuleUtils { rootTarget.setBaseDirectory(rootModule.millSourcePath.toNIO.toAbsolutePath.toUri.toString) rootTarget.setDataKind("scala") rootTarget.setTags(List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION).asJava) - rootTarget.setData(computeScalaBuildTarget(rootModule, evaluator)) + rootTarget.setData(computeBuildTargetData(rootModule, evaluator)) val basePath = rootModule.millSourcePath.toIO.toPath if (basePath.getNameCount >= 1) rootTarget.setDisplayName(basePath.getName(basePath.getNameCount - 1) + "-root") @@ -106,6 +70,46 @@ object ModuleUtils { rootTarget } + def getRegularTarget( + 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 { + case m: TestModule => List(BuildTargetTag.TEST) + case m: JavaModule => List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION) + } + + val dependencies = module match { + case m: JavaModule => m.moduleDeps.map(dep => moduleIdMap(dep)).toList.asJava + } + + val buildTarget = new BuildTarget(moduleIdMap(module), + buildTargetTag.asJava, + List("scala", "java").asJava, + dependencies, + capabilities) + if (module.isInstanceOf[ScalaModule]) { + buildTarget.setDataKind("scala") + } + buildTarget.setData(dataBuildTarget) + buildTarget.setDisplayName(moduleName(module.millModuleSegments)) + buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) + buildTarget + } + + def getTarget( rootModule: JavaModule, + module: JavaModule, + evaluator: Evaluator, + moduleIdMap: Map[JavaModule, BuildTargetIdentifier] + ): BuildTarget = { + if (module == rootModule) + getRootTarget(module, evaluator, moduleIdMap) + else + getRegularTarget(module, evaluator, moduleIdMap) + } + def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { val canTest = module match { case module: TestModule => true @@ -116,7 +120,7 @@ object ModuleUtils { } //TODO: Fix the data field for JavaModule when the bsp specification is updated - def computeScalaBuildTarget(module: JavaModule, evaluator: Evaluator): ScalaBuildTarget = { + def computeBuildTargetData(module: JavaModule, evaluator: Evaluator): ScalaBuildTarget = { module match { case m: ScalaModule => val scalaVersion = evaluateInformativeTask(evaluator, m.scalaVersion, "") @@ -170,17 +174,11 @@ object ModuleUtils { } def getModuleTargetIdMap(modules: Seq[JavaModule], evaluator:Evaluator): Predef.Map[JavaModule, BuildTargetIdentifier] = { - var moduleToTarget = Map[JavaModule, BuildTargetIdentifier]() - - for ( module <- modules ) { - moduleToTarget ++= Map(module -> new BuildTargetIdentifier( + (for ( module <- modules ) + yield (module, new BuildTargetIdentifier( (module.millOuterCtx.millSourcePath / os.RelPath(moduleName(module.millModuleSegments))). - toNIO.toAbsolutePath.toUri.toString - )) - } - - moduleToTarget + toNIO.toAbsolutePath.toUri.toString))).toMap } // this is taken from mill.scalalib GenIdeaImpl diff --git a/info.txt b/info.txt new file mode 100644 index 00000000..86fe32d4 --- /dev/null +++ b/info.txt @@ -0,0 +1 @@ +Build 2 diff --git a/main/api/src/mill/api/BspCompileArguments.scala b/main/api/src/mill/api/BspCompileArguments.scala index 9cfdd500..1af45a61 100644 --- a/main/api/src/mill/api/BspCompileArguments.scala +++ b/main/api/src/mill/api/BspCompileArguments.scala @@ -6,8 +6,4 @@ class BspCompileArguments { def args: Seq[String] = { arguments } - - def setArgs(args: Seq[String]): Unit = { - arguments = args - } } diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index c6a1f52a..75103398 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -29,8 +29,6 @@ case class Labelled[T](task: NamedTask[T], case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) case _ => None } - -// override def hashCode(): Int = task.hashCode() } case class Evaluator(home: os.Path, @@ -344,7 +342,7 @@ case class Evaluator(home: os.Path, home, env, reporter, - bspContext //new ManagedLoggedReporter(10, logger) + bspContext ) val out = System.out diff --git a/scratch/.bsp/mill-bsp.json b/scratch/.bsp/mill-bsp.json deleted file mode 100644 index 39d2d592..00000000 --- a/scratch/.bsp/mill-bsp.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"mill-bsp","argv":["../out/dev/launcher/dest/run", "-i", "mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} diff --git a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala index b690179c..fa3633a4 100644 --- a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala +++ b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala @@ -7,7 +7,9 @@ import java.io.File object BenchmarkSource { //val reader = new InputReader() + def main(args: Array[String]): Unit = { + val unusedValue = 3 val file = FileUtils.getFile("/home/alexandra/test_build1/build.sc") println(file) } diff --git a/scratch/bsp/src/test/scala/BspTests.scala b/scratch/bsp/src/test/scala/BspTests.scala index 21ba4bfe..f565217f 100644 --- a/scratch/bsp/src/test/scala/BspTests.scala +++ b/scratch/bsp/src/test/scala/BspTests.scala @@ -1,8 +1,10 @@ package tests import org.scalatest.FunSuite +import org.apache.commons.io.FileUtils -object BspTests extends FunSuite { +class BspTests extends FunSuite { + val wrongVal: String = 3 test("test 1") { - assert(new CoreTests().coreValue > 0) + assert(CoreTests().coreValue > 0) } } diff --git a/scratch/build.sc b/scratch/build.sc index 98bdca27..bdc731d3 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,5 +1,5 @@ import mill.scalalib.{SbtModule, Dep, DepSyntax} -//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.5.0-28-53df48-DIRTYf21142f7` +//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.5.0-40-278984-DIRTY7f118075` trait BetterFilesModule extends SbtModule{ def scalaVersion = "2.12.4" -- cgit v1.2.3 From fca29d647f2ae0b71c5b66b3ea4462987e08c911 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Tue, 23 Jul 2019 18:02:12 +0200 Subject: Removed unnecessary directories and reverted the scratch folder to how it was in the original mill repo. --- contrib/bsp/.bsp/mill-bsp.json | 18 ------- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 12 ----- .../src/main/scala/BenchmarkSource.scala | 16 ------- scratch/bsp/src/main/scala/BspSource.scala | 10 ---- scratch/bsp/src/test/scala/BspTests.scala | 10 ---- scratch/build.sc | 56 ++++------------------ scratch/core/src/main/scala/CommonObject.scala | 9 ---- scratch/core/src/test/scala/CoreTests.scala | 9 ---- scratch/test_build1.iml | 24 ---------- 9 files changed, 8 insertions(+), 156 deletions(-) delete mode 100644 contrib/bsp/.bsp/mill-bsp.json delete mode 100644 scratch/benchmarks/src/main/scala/BenchmarkSource.scala delete mode 100644 scratch/bsp/src/main/scala/BspSource.scala delete mode 100644 scratch/bsp/src/test/scala/BspTests.scala delete mode 100644 scratch/core/src/main/scala/CommonObject.scala delete mode 100644 scratch/core/src/test/scala/CoreTests.scala delete mode 100644 scratch/test_build1.iml diff --git a/contrib/bsp/.bsp/mill-bsp.json b/contrib/bsp/.bsp/mill-bsp.json deleted file mode 100644 index 01b08e07..00000000 --- a/contrib/bsp/.bsp/mill-bsp.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "mill-bsp", - "argv": [ - "java", - "-DMILL_CLASSPATH=/usr/local/bin/mill", - "-DMILL_VERSION=0.4.0", - "-Djna.nosys=true", - "-cp", - "/usr/local/bin/mill", - "mill.MillMain mill.contrib.MainMillBuildServer/startServer" - ], - "version": "1.0.0", - "bspVersion": "2.0.0-M4", - "languages": [ - "scala", - "java" - ] -} \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index b5b9748a..af5483a4 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -7,10 +7,8 @@ import java.util.concurrent.Executors import upickle.default._ import ch.epfl.scala.bsp4j._ import mill._ -import mill.contrib.bsp.ModuleUtils import mill.define.{Command, Discover, ExternalModule} import mill.eval.Evaluator -import mill.scalalib.JavaModule import org.eclipse.lsp4j.jsonrpc.Launcher import scala.collection.JavaConverters._ @@ -78,7 +76,6 @@ object BSP extends ExternalModule { println("The bsp connection json file probably exists already - will be overwritten") os.remove(bspDirectory / "mill.json") os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) - //TODO: Do I want to catch this or throw the exception? case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + " " + e.getStackTrace.toString) } @@ -126,13 +123,4 @@ object BSP extends ExternalModule { executor.shutdown() } } - - def experiment(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) - val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) - - millServer.initialized = true - println(millServer.workspaceBuildTargets().get) - } } \ No newline at end of file diff --git a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala b/scratch/benchmarks/src/main/scala/BenchmarkSource.scala deleted file mode 100644 index fa3633a4..00000000 --- a/scratch/benchmarks/src/main/scala/BenchmarkSource.scala +++ /dev/null @@ -1,16 +0,0 @@ -package main.scala - -import org.apache.commons.io.FileUtils - -import java.io.File - -object BenchmarkSource { - //val reader = new InputReader() - - - def main(args: Array[String]): Unit = { - val unusedValue = 3 - val file = FileUtils.getFile("/home/alexandra/test_build1/build.sc") - println(file) - } -} diff --git a/scratch/bsp/src/main/scala/BspSource.scala b/scratch/bsp/src/main/scala/BspSource.scala deleted file mode 100644 index 3a2300ae..00000000 --- a/scratch/bsp/src/main/scala/BspSource.scala +++ /dev/null @@ -1,10 +0,0 @@ -package main.scala -import ch.epfl.scala.bsp4j._ - -object BspSource { - val obj = CommonObject.strVal - - def main(args: Array[String]): Unit = { - println(new BuildTargetIdentifier("path")) - } -} diff --git a/scratch/bsp/src/test/scala/BspTests.scala b/scratch/bsp/src/test/scala/BspTests.scala deleted file mode 100644 index f565217f..00000000 --- a/scratch/bsp/src/test/scala/BspTests.scala +++ /dev/null @@ -1,10 +0,0 @@ -package tests -import org.scalatest.FunSuite -import org.apache.commons.io.FileUtils - -class BspTests extends FunSuite { - val wrongVal: String = 3 - test("test 1") { - assert(CoreTests().coreValue > 0) - } -} diff --git a/scratch/build.sc b/scratch/build.sc index bdc731d3..804e1ca4 100644 --- a/scratch/build.sc +++ b/scratch/build.sc @@ -1,52 +1,12 @@ -import mill.scalalib.{SbtModule, Dep, DepSyntax} -//import $ivy.`com.lihaoyi::mill-contrib-bsp:0.5.0-40-278984-DIRTY7f118075` +import mill.Agg +import mill.scalalib._ -trait BetterFilesModule extends SbtModule{ - def scalaVersion = "2.12.4" - def scalacOptions = Seq( - "-deprecation", // Emit warning and location for usages of deprecated APIs. - "-encoding", "utf-8", // Specify character encoding used by source files. - "-explaintypes", // Explain type errors in more detail. - "-feature", // Emit warning and location for usages of features that should be imported explicitly. - "-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`. - "-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. - "-Ywarn-nullary-unit", // Warn when nullary methods return Unit. - "-Ywarn-numeric-widen", // Warn when numerics are widened. - "-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. - "-Ywarn-unused:imports", // Warn if an import selector is not referenced. - "-Ywarn-unused:locals", // Warn if a local definition is unused. - "-Ywarn-unused:params", // Warn if a value parameter is unused. - "-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. - "-Ywarn-unused:privates", // Warn if a private member is unused. - "-Ywarn-value-discard" // Warn when non-Unit expression results are unused. +object core extends ScalaModule{ + def scalaVersion = "2.12.8" + def ivyDeps = Agg( + ivy"org.eclipse.jetty:jetty-websocket:8.1.16.v20140903", + ivy"org.eclipse.jetty:jetty-server:8.1.16.v20140903" ) - override def javacOptions = Seq("-source", "1.8", "-target", "1.8", "-Xlint") - - object test extends Tests{ - def moduleDeps = - if (this == core.test) super.moduleDeps - else super.moduleDeps ++ Seq(core.test) - def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.0.4") - def testFrameworks = Seq("org.scalatest.tools.Framework") - } -} - -object core extends BetterFilesModule - -object bsp extends BetterFilesModule{ - def moduleDeps = Seq(core) - def ivyDeps = Agg(ivy"ch.epfl.scala:bsp4j:2.0.0-M3") } -object benchmarks extends BetterFilesModule{ - def moduleDeps = Seq(core) - def ivyDeps = Agg( - ivy"commons-io:commons-io:2.5" - ) - def unmanagedClasspath = Agg( - mill.modules.Util.download( - "https://github.com/williamfiset/FastJavaIO/releases/download/v1.0/fastjavaio.jar", - "fastjavaio.jar" - ) - ) -} \ No newline at end of file +def thingy = T{ Seq("hello", "world") } diff --git a/scratch/core/src/main/scala/CommonObject.scala b/scratch/core/src/main/scala/CommonObject.scala deleted file mode 100644 index e10bf3a0..00000000 --- a/scratch/core/src/main/scala/CommonObject.scala +++ /dev/null @@ -1,9 +0,0 @@ -package main.scala - -object CommonObject { - val strVal: String = "" - - def hasMethod: Boolean ={ - true - } -} diff --git a/scratch/core/src/test/scala/CoreTests.scala b/scratch/core/src/test/scala/CoreTests.scala deleted file mode 100644 index 6a25b4a4..00000000 --- a/scratch/core/src/test/scala/CoreTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package tests -import org.scalatest.FlatSpec - -class CoreTests extends FlatSpec { - val coreValue = 1 - "This simple addition" should "equal 4" in { - assert(2 + 2 == 4) - } -} diff --git a/scratch/test_build1.iml b/scratch/test_build1.iml deleted file mode 100644 index 294cb691..00000000 --- a/scratch/test_build1.iml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file -- cgit v1.2.3 From 0b4bcbbfabe06d549d95efb427e75356287398fb Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 24 Jul 2019 14:03:48 +0200 Subject: Changed the clean cache method to usethe clean command form mill's MainModule rather than start the clean command as a separate subprocess. Removed dead code comment and added show message notifications to the MillBspLogger. --- .gitignore | 3 +- .../bsp/src/mill/contrib/bsp/MillBspLogger.scala | 21 ++++++++++--- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 36 ++++++++++------------ scalalib/worker/src/ZincWorkerImpl.scala | 2 +- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 53cea62c..84df7fe8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ out/ /.metals/ contrib/bsp/mill-external-bs contrib/bsp/mill-out-bs -mill.iml \ No newline at end of file +mill.iml +.bsp/ \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala index 811c898c..195d9aab 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala @@ -1,9 +1,7 @@ package mill.contrib.bsp -import java.io.{InputStream, PrintStream} - -import ch.epfl.scala.bsp4j.{BuildClient, TaskId, TaskProgressParams} -import mill.api.{BspContext, Logger} +import ch.epfl.scala.bsp4j._ +import mill.api.Logger import mill.util.ProxyLogger class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends ProxyLogger(logger) { @@ -24,4 +22,19 @@ class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends Pr } } + override def error(s: String): Unit = { + super.error(s) + client.onBuildShowMessage(new ShowMessageParams(MessageType.ERROR, s)) + } + + override def info(s: String): Unit = { + super.info(s) + client.onBuildShowMessage(new ShowMessageParams(MessageType.INFORMATION, s)) + } + + override def debug(s: String): Unit = { + super.debug(s) + client.onBuildShowMessage(new ShowMessageParams(MessageType.LOG, s)) + } + } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index b5ebf83e..f34571e4 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -20,6 +20,7 @@ import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.Ctx import mill.define.{Discover, ExternalModule} +import mill.main.MainModule import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange @@ -393,30 +394,25 @@ class MillBuildServer(evaluator: Evaluator, var cleaned = true for (targetId <- cleanCacheParams.getTargets.asScala) { val module = targetIdToModule(targetId) - val cleanCommand = List("java", - s"-DMILL_CLASSPATH=${System.getProperty("MILL_CLASSPATH")}", - s"-DMILL_VERSION=${System.getProperty("MILL_VERSION")}", - "-Djna.nosys=true", "-cp", - System.getProperty("MILL_CLASSPATH"), - "mill.MillMain", "clean", - s"${module.millModuleSegments.render}.compile") - val process = Runtime.getRuntime.exec(cleanCommand.mkString(" ")) - - val processIn = process.getInputStream - val processErr = process.getErrorStream - - val errMessage = Source.fromInputStream(processErr).getLines().mkString("\n") - val message = Source.fromInputStream(processIn).getLines().mkString("\n") - msg += s"Cleaning cache for target ${targetId} produced the following message: ${message}, ${errMessage}" - if (msg.contains("failed")) { + val mainModule = new MainModule { + override implicit def millDiscover: Discover[_] = { + Discover[this.type] + } + } + val cleanCommand = mainModule.clean(millEvaluator, List(s"${module.millModuleSegments.render}.compile"):_*) + val cleanResult = millEvaluator.evaluate( + Strict.Agg(cleanCommand), + logger = new MillBspLogger(client, cleanCommand.hashCode, millEvaluator.log) + ) + if (cleanResult.failing.keyCount > 0) { cleaned = false + msg += s" Target ${module.millModuleSegments.render} could not be cleaned." } - process.waitFor() } - new CleanCacheResult(msg, cleaned) + new CleanCacheResult(msg, cleaned) + } + handleExceptions[String, CleanCacheResult]((in) => getCleanCacheResult, "") } - handleExceptions[String, CleanCacheResult]((in) => getCleanCacheResult, "") - } override def buildTargetScalacOptions(scalacOptionsParams: ScalacOptionsParams): CompletableFuture[ScalacOptionsResult] = { diff --git a/scalalib/worker/src/ZincWorkerImpl.scala b/scalalib/worker/src/ZincWorkerImpl.scala index 7c696ccc..a1d632df 100644 --- a/scalalib/worker/src/ZincWorkerImpl.scala +++ b/scalalib/worker/src/ZincWorkerImpl.scala @@ -362,7 +362,7 @@ class ZincWorkerImpl(compilerBridge: Either[ zincIOFile, new FreshCompilerCache, IncOptions.of(), - newReporter,//new ManagedLoggedReporter(10, logger), + newReporter, None, Array() ), -- cgit v1.2.3 From 8138acf4911b668b3b15c19fd51c4f5e6aadc083 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 24 Jul 2019 16:57:44 +0200 Subject: Added docstrigs and comments. Also cleaned unused imports. Changd the printStream connected to the compilation logger sent to the mill evaluator from Bsp from System.out (might interfere with the lsp communication ) to the outputstream of the evaluator's logger. --- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 3 +- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 41 ++++-- .../bsp/src/mill/contrib/bsp/BspTestReporter.scala | 21 ++- .../bsp/src/mill/contrib/bsp/MillBspLogger.scala | 14 ++ .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 66 +++------ contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 148 +++++++++++++++++---- .../bsp/src/mill/contrib/bsp/TaskParameters.scala | 28 +++- main/api/src/mill/api/BspCompileArguments.scala | 9 ++ main/api/src/mill/api/BspContext.scala | 14 ++ main/api/src/mill/api/TestReporter.scala | 10 ++ 10 files changed, 266 insertions(+), 88 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index af5483a4..aa4cf211 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -3,14 +3,12 @@ package mill.contrib import play.api.libs.json._ 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 scala.collection.JavaConverters._ @@ -23,6 +21,7 @@ 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" } diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 06c45b79..a90b02a5 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -2,19 +2,35 @@ package mill.contrib.bsp import java.io.File import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.{ConcurrentHashMap, ConcurrentMap} - +import java.util.concurrent.ConcurrentHashMap import ch.epfl.scala.bsp4j._ import ch.epfl.scala.{bsp4j => bsp} import sbt.internal.inc.ManagedLoggedReporter import sbt.internal.util.ManagedLogger import xsbti.{Problem, Severity} - import scala.collection.JavaConverters._ import scala.collection.concurrent import scala.compat.java8.OptionConverters._ -import scala.io.Source + +/** + * Specialized reporter that sends compilation diagnostics + * 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 compilationOriginId optional origin id the client assigned to + * the compilation request. Needs to be sent + * back as part of the published diagnostics + * as well as compile report + * @param maxErrors The maximum number of errors to be logged during the + * compilation of targetId + * @param logger The logger that will log the messages for each Problem. + */ class BspLoggedReporter(client: bsp.BuildClient, targetId: BuildTargetIdentifier, taskId: TaskId, @@ -60,10 +76,14 @@ class BspLoggedReporter(client: bsp.BuildClient, 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 + // compile request ) //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 - def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): - bsp.PublishDiagnosticsParams = { + private[this] def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): + bsp.PublishDiagnosticsParams = { val diagnostic = getSingleDiagnostic(problem) val sourceFile = problem.position().sourceFile().asScala val textDocument = new TextDocumentIdentifier( @@ -81,10 +101,14 @@ class BspLoggedReporter(client: bsp.BuildClient, params } + // Compute the compilation status code private[this] def getStatusCode: StatusCode = { if (errors.get > 0) StatusCode.ERROR else StatusCode.OK } + // Update the published diagnostics for the fiven text file by + // adding the recently computed diagnostic to the list of + // all previous diagnostics generated for the same file. private[this] def appendDiagnostics(textDocument: TextDocumentIdentifier, currentDiagnostic: Diagnostic): List[Diagnostic] = { diagnosticMap.putIfAbsent(textDocument, new bsp.PublishDiagnosticsParams( @@ -94,14 +118,15 @@ class BspLoggedReporter(client: bsp.BuildClient, diagnosticMap(textDocument).getDiagnostics.asScala.toList ++ List(currentDiagnostic) } + // Computes the diagnostic related to the given Problem private[this] def getSingleDiagnostic(problem: Problem): Diagnostic ={ val start = new bsp.Position( problem.position.startLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), problem.position.startOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) val end = new bsp.Position( - problem.position.endLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), - problem.position.endOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) + problem.position.endLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(start.getLine)), + problem.position.endOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(start.getCharacter))) val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) diagnostic.setCode(problem.position.lineContent) diagnostic.setSource("compiler from mill") diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 69471675..51a2f2ba 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -1,10 +1,23 @@ package mill.contrib.bsp import ch.epfl.scala.bsp4j._ -import mill.api.{BspContext, TestReporter} +import mill.api.BspContext 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 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. + */ class BspTestReporter( client: BuildClient, targetId: BuildTargetIdentifier, @@ -76,7 +89,9 @@ class BspTestReporter( client.onBuildTaskFinish(taskFinishParams) } - def getDisplayName(e: Event): String = { + // 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() @@ -86,6 +101,8 @@ class BspTestReporter( } } + // Compute the test report data structure that will go into + // the task finish notification after all tests are ran. def getTestReport: TestReport = { val report = new TestReport(targetId, passed, failed, ignored, cancelled, skipped) report.setTime(totalTime) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala index 195d9aab..a17c4a40 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBspLogger.scala @@ -4,6 +4,19 @@ import ch.epfl.scala.bsp4j._ import mill.api.Logger import mill.util.ProxyLogger + +/** + * BSP-specialized logger class which sends `task-progress` + * 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 + * @param taskId unique ID of the task being evaluated + * @param logger the logger to which the messages received by this + * MillBspLogger are being redirected + */ class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends ProxyLogger(logger) { override def ticker(s: String): Unit = { @@ -17,6 +30,7 @@ class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends Pr params.setProgress(progress(0).toLong) params.setTotal(progress(1).toLong) client.onBuildTaskProgress(params) + super.ticker(s) } catch { case e: Exception => } diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index f34571e4..77e3ff38 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -1,21 +1,16 @@ package mill.contrib.bsp -import java.io.File - import sbt.testing._ -import java.util.Collections import java.util.concurrent.CompletableFuture - import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import mill.{scalalib, _} -import mill.api.{BspContext, Loose, Result, Strict} +import mill.api.{BspContext, Result, Strict} import mill.contrib.bsp.ModuleUtils._ import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult import sbt.internal.inc._ - import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.Ctx @@ -24,8 +19,6 @@ import mill.main.MainModule import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange -import scala.io.Source - class MillBuildServer(evaluator: Evaluator, _bspVersion: String, @@ -98,21 +91,6 @@ class MillBuildServer(evaluator: Evaluator, "") } - private[this] def getSourceFiles(sources: Seq[os.Path]): Iterable[os.Path] = { - var files = Seq.empty[os.Path] - - for (source <- sources) { - if (os.exists(source)) (if (os.isDir(source)) os.walk(source) else Seq(source)) - .foreach(path => if (os.isFile(path) && List("scala", "java").contains(path.ext) && - !path.last.startsWith(".")) { - files ++= Seq(path) - }) - } - - files - } - - override def buildTargetSources(sourcesParams: SourcesParams): CompletableFuture[SourcesResult] = { def computeSourcesResult: SourcesResult = { @@ -212,25 +190,10 @@ class MillBuildServer(evaluator: Evaluator, handleExceptions[String, ResourcesResult]((in) => getResources, "") } - def getOriginId(params: CompileParams): Option[String] = { - try { - Option(params.getOriginId) - } catch { - case e: Exception => Option.empty[String] - } - } - - def getArguments(params: CompileParams) : Option[Seq[String]] = { - try { - Option(params.getArguments.asScala) - } catch { - case e: Exception => Option.empty[Seq[String]] - } - } - - def getCompilationLogger: ManagedLogger = { + // construct the ManagedLogger that will go into the compilation problems reporter + private[this] def getCompilationLogger: ManagedLogger = { val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( - System.out + millEvaluator.log.outputStream )) val l = LogExchange.logger("Hello") LogExchange.unbindLoggerAppenders("Hello") @@ -238,7 +201,9 @@ class MillBuildServer(evaluator: Evaluator, l } - def getBspLoggedReporterPool(params: Parameters, taskStartMessage: String => String, + // 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[ManagedLoggedReporter] = { (int: Int) => @@ -280,7 +245,7 @@ class MillBuildServer(evaluator: Evaluator, ) val compileResult = new CompileResult(getStatusCode(result)) compileResult.setOriginId(compileParams.getOriginId) - compileResult //TODO: See what form IntelliJ expects data about products of compilation in order to set data field + compileResult //TODO: See in what form IntelliJ expects data about products of compilation in order to set data field } handleExceptions[String, CompileResult]((in) => getCompileResult, "") } @@ -304,6 +269,7 @@ class MillBuildServer(evaluator: Evaluator, handleExceptions[String, RunResult]((in) => getRunResult, "") } + // Get the execution status code given the results from Evaluator.evaluate private[this] def getStatusCode(results: Evaluator.Results): StatusCode = { if (results.failing.keyCount > 0) { @@ -468,7 +434,8 @@ class MillBuildServer(evaluator: Evaluator, handleExceptions[String, ScalaMainClassesResult]((in) => getScalaMainClasses, "") } - private[this] def getTestFrameworks(module: TestModule) (implicit ctx: Ctx.Home): Seq[String] = { + // 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) @@ -496,7 +463,7 @@ class MillBuildServer(evaluator: Evaluator, for (targetId <- scalaTestClassesParams.getTargets.asScala) { targetIdToModule(targetId) match { case module: TestModule => - items ++= List(new ScalaTestClassesItem(targetId, getTestFrameworks(module).toList.asJava)) + items ++= List(new ScalaTestClassesItem(targetId, getTestClasses(module).toList.asJava)) case module: JavaModule => //TODO: maybe send a notification that this target has no test classes } } @@ -505,12 +472,14 @@ class MillBuildServer(evaluator: Evaluator, 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 } + // Resolve all the mill modules contained in the project private[this] def getMillModules(ev: Evaluator): Seq[JavaModule] = { ev.rootModule.millInternal.segmentsToModules.values. collect { @@ -518,6 +487,8 @@ class MillBuildServer(evaluator: Evaluator, }.toSeq ++ Seq(rootModule) } + // 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) @@ -526,6 +497,11 @@ class MillBuildServer(evaluator: Evaluator, moduleToTarget = ModuleUtils.millModulesToBspTargets(millModules, rootModule, evaluator, List("scala", "java")) } + // 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]() initialized match { diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index c5f203b5..85a808a8 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -1,38 +1,87 @@ package mill.contrib.bsp -import java.net.URI -import java.util.Collections - 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, Discover, Module, Segment, Segments, Sources, Target, Task} +import mill.define.{BaseModule, Ctx, Segment, Segments, Target, Task} import mill.eval._ import mill.eval.Evaluator import mill.scalajslib.ScalaJSModule import mill.scalalib.api.Util import mill.scalanativelib._ -import mill.scalalib.{CrossModuleBase, GenIdea, GenIdeaImpl, JavaModule, ScalaModule, TestModule} -import mill.util.DummyLogger +import mill.scalalib.{JavaModule, ScalaModule, TestModule} import os.Path - +/** + * Utilities for translating the mill build into + * BSP information like BuildTargets and BuildTargetIdentifiers + */ object ModuleUtils { - def millModulesToBspTargets(modules: Seq[JavaModule], + /** + * Compute mapping between all the JavaModules contained in the + * 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 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] = { - val moduleIdMap = getModuleTargetIdMap(modules, evaluator) + val moduleIdMap = getModuleTargetIdMap(modules, evaluator) - (for ( module <- modules ) - yield (module, getTarget(rootModule, module, evaluator, moduleIdMap))).toMap + (for ( module <- modules ) + yield (module, getTarget(rootModule, module, evaluator, moduleIdMap))).toMap - } + } + /** + * Compute the BuildTarget associated with the given module, + * may or may not be identical to the root of the working + * directory ( rootModule ) + * + * @param rootModule mill JavaModule for the project root + * @param module mill JavaModule to compute the BuildTarget + * for + * @param evaluator mill Evaluator + * @param moduleIdMap mapping from each mill JavaModule + * contained in the working directory and + * a BuildTargetIdentifier associated + * with it. + * @return build target for `module` + */ + def getTarget( rootModule: JavaModule, + module: JavaModule, + evaluator: Evaluator, + moduleIdMap: Map[JavaModule, BuildTargetIdentifier] + ): BuildTarget = { + if (module == rootModule) + getRootTarget(module, evaluator, moduleIdMap) + else + getRegularTarget(module, evaluator, moduleIdMap) + } + + /** + * Given the BaseModule corresponding to the root + * of the working directory, compute a JavaModule that + * has the same millSourcePath. Set generated sources + * accoridng to the location of the compilation + * products + * @param rootBaseModule module for the root + * @return root JavaModule + */ def getRootJavaModule(rootBaseModule: BaseModule): JavaModule = { implicit val ctx: Ctx = rootBaseModule.millOuterCtx new JavaModule { @@ -42,12 +91,24 @@ object ModuleUtils { def out = T.sources{millSourcePath / "out"} def target = T.sources{millSourcePath / "target"} - //override def sources: Sources = T.sources{millSourcePath} 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 moduleIdMap mapping from each mill JavaModule + * contained in the working directory and + * a BuildTargetIdentifier associated + * with it. + * @return root BuildTarget + */ def getRootTarget( rootModule: JavaModule, evaluator: Evaluator, @@ -70,6 +131,19 @@ object ModuleUtils { rootTarget } + /** + * Compute the BuildTarget associated with the given mill + * JavaModule, which is any module present in the working + * directory, but it's not the root module itself. + * + * @param module any in-project mill module + * @param evaluator mill evaluator + * @param moduleIdMap mapping from each mill JavaModule + * contained in the working directory and + * a BuildTargetIdentifier associated + * with it. + * @return inner BuildTarget + */ def getRegularTarget( module: JavaModule, evaluator: Evaluator, @@ -99,28 +173,19 @@ object ModuleUtils { buildTarget } - def getTarget( rootModule: JavaModule, - module: JavaModule, - evaluator: Evaluator, - moduleIdMap: Map[JavaModule, BuildTargetIdentifier] - ): BuildTarget = { - if (module == rootModule) - getRootTarget(module, evaluator, moduleIdMap) - else - getRegularTarget(module, evaluator, moduleIdMap) - } - - def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { + // obtain the capabilities of the given module ( ex: canCompile, canRun, canTest ) + private[this] def getModuleCapabilities(module: JavaModule, evaluator: Evaluator): BuildTargetCapabilities = { val canTest = module match { - case module: TestModule => true + case _: TestModule => true case default => false } new BuildTargetCapabilities(true, canTest, true) } + // Compute the ScalaBuildTarget from information about the given JavaModule. //TODO: Fix the data field for JavaModule when the bsp specification is updated - def computeBuildTargetData(module: JavaModule, evaluator: Evaluator): ScalaBuildTarget = { + private[this] def computeBuildTargetData(module: JavaModule, evaluator: Evaluator): ScalaBuildTarget = { module match { case m: ScalaModule => val scalaVersion = evaluateInformativeTask(evaluator, m.scalaVersion, "") @@ -144,10 +209,25 @@ 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 { @@ -156,7 +236,9 @@ object ModuleUtils { } } - def computeScalaLangDependencies(module: ScalaModule, evaluator: Evaluator): Loose.Agg[PathRef] = { + // 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] = { evaluateInformativeTask(evaluator, module.resolveDeps(module.scalaLibraryIvyDeps), Loose.Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, module.scalacPluginClasspath, Loose.Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, module.resolveDeps(module.ivyDeps), Loose.Agg.empty[PathRef]). @@ -165,7 +247,8 @@ object ModuleUtils { pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-library")) } - def getScalaTargetPlatform(module: ScalaModule): ScalaPlatform = { + // Obtain the scala platform for `module` + private[this] def getScalaTargetPlatform(module: ScalaModule): ScalaPlatform = { module match { case m: ScalaNativeModule => ScalaPlatform.NATIVE case m: ScalaJSModule => ScalaPlatform.JS @@ -173,6 +256,13 @@ object ModuleUtils { } } + /** + * 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 ) diff --git a/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala b/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala index b020b498..29a07f17 100644 --- a/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala +++ b/contrib/bsp/src/mill/contrib/bsp/TaskParameters.scala @@ -1,10 +1,15 @@ package mill.contrib.bsp -import java.util import scala.collection.JavaConverters._ -import scala.compat.java8.OptionConverters._ import ch.epfl.scala.bsp4j.{BuildTargetIdentifier, CompileParams, RunParams, TestParams} + +/** + * Common trait to represent BSP request parameters that + * have a specific form: include one or more targetIds, + * arguments for the execution of the task, and an optional + * origin id generated by the client. + */ trait Parameters { def getTargets: List[BuildTargetIdentifier] @@ -83,14 +88,33 @@ case class TParams(testParams: TestParams) extends Parameters { } 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 + */ def fromCompileParams(compileParams: CompileParams): Parameters = { CParams(compileParams) } + /** + * Convert parameters specific to the run request + * to the common trait Parameters. + * @param runParams run request parameters + * @return general task parameters containing running info + */ def fromRunParams(runParams: RunParams): Parameters = { RParams(runParams) } + /** + * Convert parameters specific to the test request + * to the common trait Parameters. + * @param testParams compile request parameters + * @return general task parameters containing testing info + */ def fromTestParams(testParams: TestParams): Parameters = { TParams(testParams) } diff --git a/main/api/src/mill/api/BspCompileArguments.scala b/main/api/src/mill/api/BspCompileArguments.scala index 1af45a61..73586cc8 100644 --- a/main/api/src/mill/api/BspCompileArguments.scala +++ b/main/api/src/mill/api/BspCompileArguments.scala @@ -1,8 +1,17 @@ package mill.api +/** + * Data structure to represent Bsp client-specified + * compilation arguments + */ class BspCompileArguments { var arguments: Seq[String] = Seq.empty[String] + /** + * Return the compilation arguments specified by the + * Bsp client, which may or may not be found in the + * compiler options of any module from the build file. + */ def args: Seq[String] = { arguments } diff --git a/main/api/src/mill/api/BspContext.scala b/main/api/src/mill/api/BspContext.scala index c93fbca1..1281518d 100644 --- a/main/api/src/mill/api/BspContext.scala +++ b/main/api/src/mill/api/BspContext.scala @@ -2,10 +2,24 @@ package mill.api import sbt.testing.Event +/** + * Bsp Context with functionality for retrieving compile + * arguments provided by a Bsp client, as well as for logging + * the start and finish of a task triggered by the request of + * a Bsp client. Can be integrated into mill's Ctx to inject + * Bsp functionality into tasks like compile/run/test. + */ trait BspContext extends BspCompileArguments with TestReporter +/** + * Dummy Bsp Context that does nothing + * upon starting or finishing a task, and + * contains no client-specified compilation + * arguments + */ object DummyBspContext extends BspContext { override def args = Seq.empty[String] + override def logStart(event: Event): Unit = { } diff --git a/main/api/src/mill/api/TestReporter.scala b/main/api/src/mill/api/TestReporter.scala index b3d0e432..97dec761 100644 --- a/main/api/src/mill/api/TestReporter.scala +++ b/main/api/src/mill/api/TestReporter.scala @@ -2,6 +2,12 @@ package mill.api import sbt.testing._ +/** + * Test reporter class that can be + * injected into the test task and + * report information upon the start + * and the finish of testing events + */ trait TestReporter { def logStart(event: Event): Unit @@ -10,6 +16,10 @@ trait TestReporter { } +/** + * Dummy Test Reporter that doesn't report + * anything for any testing event. + */ object DummyReporter extends TestReporter { override def logStart(event: Event): Unit = { -- cgit v1.2.3 From c02b1bf61ea714b76b8c53759301bfb5be535fc9 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Thu, 25 Jul 2019 17:32:21 +0200 Subject: Changed the clean cache method back to a subprocess because this way it can wait for the output directories to actually be removed. Use IO instead of NIO for converting os.Path to URIs. Don't display the stack trace in the start method in case of CancelationException, which just means the server stopped. Also added support for tracing bsp messages inside a bsp.log file in the working directory of the project being built. --- .gitignore | 3 +- .../bsp/src/mill/contrib/MainMillBuildServer.scala | 9 +++- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 2 +- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 59 +++++++++++++--------- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 14 ++--- 5 files changed, 54 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 84df7fe8..af52d378 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ out/ contrib/bsp/mill-external-bs contrib/bsp/mill-out-bs mill.iml -.bsp/ \ No newline at end of file +.bsp/ +bsp.log \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala index aa4cf211..d31eb7f4 100644 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala @@ -1,15 +1,20 @@ package mill.contrib +import java.io.{File, PrintWriter} + import play.api.libs.json._ 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 scala.collection.JavaConverters._ +import scala.concurrent.CancellationException object BSP extends ExternalModule { @@ -103,7 +108,8 @@ object BSP extends ExternalModule { .setOutput(stdout) .setInput(stdin) .setLocalService(millServer) - .setRemoteInterface(classOf[BuildClient]) + .setRemoteInterface(classOf[BuildClient]). + traceMessages(new PrintWriter((os.pwd/ "bsp.log" ).toIO)) .setExecutorService(executor) .create() millServer.onConnectWithClient(launcher.getRemoteProxy) @@ -111,6 +117,7 @@ object BSP extends ExternalModule { millServer.cancelator = () => listening.cancel(true) val voidFuture = listening.get() } catch { + case _: CancellationException => System.err.println("The mill server was shut down.") case e: Exception => System.err.println("An exception occured while connecting to the client.") System.err.println("Cause: " + e.getCause) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index a90b02a5..ba42a67a 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -89,7 +89,7 @@ class BspLoggedReporter(client: bsp.BuildClient, val textDocument = new TextDocumentIdentifier( sourceFile.getOrElse(None) match { case None => targetId.getUri - case f: File => f.toPath.toUri.toString + case f: File => f.toURI.toString }) val params = new bsp.PublishDiagnosticsParams(textDocument, targetId, diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 77e3ff38..247f5ddb 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -2,6 +2,7 @@ package mill.contrib.bsp import sbt.testing._ import java.util.concurrent.CompletableFuture + import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import mill.{scalalib, _} @@ -11,6 +12,7 @@ import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult import sbt.internal.inc._ + import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.Ctx @@ -19,6 +21,8 @@ import mill.main.MainModule import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange +import scala.io.Source + class MillBuildServer(evaluator: Evaluator, _bspVersion: String, @@ -108,12 +112,12 @@ class MillBuildServer(evaluator: Evaluator, for (source <- sources) { itemSources ++= List( - new SourceItem(source.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.DIRECTORY, false)) + new SourceItem(source.toIO.toURI.toString, SourceItemKind.DIRECTORY, false)) } for (genSource <- generatedSources) { itemSources ++= List( - new SourceItem(genSource.toNIO.toAbsolutePath.toUri.toString, SourceItemKind.DIRECTORY, true)) + new SourceItem(genSource.toIO.toURI.toString, SourceItemKind.DIRECTORY, true)) } items ++= List(new SourcesItem(targetId, itemSources.asJava)) @@ -162,7 +166,7 @@ class MillBuildServer(evaluator: Evaluator, case m: JavaModule => sources ++= List() } items ++= List(new DependencySourcesItem(targetId, sources. - map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString). + map(pathRef => pathRef.path.toIO.toURI.toString). toList.asJava)) } @@ -180,7 +184,7 @@ class MillBuildServer(evaluator: Evaluator, val millModule = targetIdToModule(targetId) val resources = evaluateInformativeTask(evaluator, millModule.resources, Agg.empty[PathRef]). flatMap(pathRef => os.walk(pathRef.path)). - map(path => path.toNIO.toAbsolutePath.toUri.toString). + map(path => path.toIO.toURI.toString). toList.asJava items ++= List(new ResourcesItem(targetId, resources)) } @@ -355,26 +359,32 @@ class MillBuildServer(evaluator: Evaluator, } override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = { - 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] + + def getCleanCacheResult: CleanCacheResult = { + var msg = "" + var cleaned = true + for (targetId <- cleanCacheParams.getTargets.asScala) { + val module = targetIdToModule(targetId) + val cleanCommand = Array("java", + s"-DMILL_CLASSPATH=${System.getProperty("MILL_CLASSPATH")}", + s"-DMILL_VERSION=${System.getProperty("MILL_VERSION")}", + "-Djna.nosys=true", "-cp", + System.getProperty("MILL_CLASSPATH"), + "mill.MillMain", "clean", + s"${module.millModuleSegments.render}.compile") + val process = Runtime.getRuntime.exec(cleanCommand, null, os.pwd.toIO) + + val processIn = process.getInputStream + val processErr = process.getErrorStream + + val errMessage = Source.fromInputStream(processErr).getLines().mkString("\n") + val message = Source.fromInputStream(processIn).getLines().mkString("\n") + msg += s"Cleaning cache for target ${targetId} produced the following message: ${message}, ${errMessage}" + if (msg.contains("failed") || msg.contains("Error")) { + cleaned = false } + process.waitFor() } - val cleanCommand = mainModule.clean(millEvaluator, List(s"${module.millModuleSegments.render}.compile"):_*) - val cleanResult = millEvaluator.evaluate( - Strict.Agg(cleanCommand), - logger = new MillBspLogger(client, cleanCommand.hashCode, millEvaluator.log) - ) - if (cleanResult.failing.keyCount > 0) { - cleaned = false - msg += s" Target ${module.millModuleSegments.render} could not be cleaned." - } - } new CleanCacheResult(msg, cleaned) } handleExceptions[String, CleanCacheResult]((in) => getCleanCacheResult, "") @@ -390,7 +400,7 @@ class MillBuildServer(evaluator: Evaluator, case m: ScalaModule => val options = evaluateInformativeTask(evaluator, m.scalacOptions, Seq.empty[String]).toList val classpath = evaluateInformativeTask(evaluator, m.compileClasspath, Agg.empty[PathRef]). - map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString).toList + map(pathRef => pathRef.path.toIO.toURI.toString).toList val classDirectory = (Evaluator.resolveDestPaths(os.pwd / "out" , m.millModuleSegments). dest / "classes").toIO.toURI.toString @@ -403,6 +413,9 @@ class MillBuildServer(evaluator: Evaluator, handleExceptions[String, ScalacOptionsResult]((in) => getScalacOptionsResult, "") } + //TODO: In the case when mill fails to provide a main classes because multiple were + // 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] = { diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 85a808a8..ffe3f8d7 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -120,7 +120,7 @@ object ModuleUtils { List.empty[String].asJava, List.empty[BuildTargetIdentifier].asJava, new BuildTargetCapabilities(false, false, false)) - rootTarget.setBaseDirectory(rootModule.millSourcePath.toNIO.toAbsolutePath.toUri.toString) + rootTarget.setBaseDirectory(rootModule.millSourcePath.toIO.toURI.toString) rootTarget.setDataKind("scala") rootTarget.setTags(List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION).asJava) rootTarget.setData(computeBuildTargetData(rootModule, evaluator)) @@ -169,7 +169,7 @@ object ModuleUtils { } buildTarget.setData(dataBuildTarget) buildTarget.setDisplayName(moduleName(module.millModuleSegments)) - buildTarget.setBaseDirectory(module.intellijModulePath.toNIO.toAbsolutePath.toUri.toString) + buildTarget.setBaseDirectory(module.intellijModulePath.toIO.toURI.toString) buildTarget } @@ -195,7 +195,7 @@ object ModuleUtils { Util.scalaBinaryVersion(scalaVersion), getScalaTargetPlatform(m), computeScalaLangDependencies(m, evaluator). - map(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString). + map(pathRef => pathRef.path.toIO.toURI.toString). toList.asJava) case m: JavaModule => @@ -242,9 +242,9 @@ object ModuleUtils { evaluateInformativeTask(evaluator, module.resolveDeps(module.scalaLibraryIvyDeps), Loose.Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, module.scalacPluginClasspath, Loose.Agg.empty[PathRef]) ++ evaluateInformativeTask(evaluator, module.resolveDeps(module.ivyDeps), Loose.Agg.empty[PathRef]). - filter(pathRef => pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-compiler") || - pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-reflect") || - pathRef.path.toNIO.toAbsolutePath.toUri.toString.contains("scala-library")) + filter(pathRef => pathRef.path.toIO.toURI.toString.contains("scala-compiler") || + pathRef.path.toIO.toURI.toString.contains("scala-reflect") || + pathRef.path.toIO.toURI.toString.contains("scala-library")) } // Obtain the scala platform for `module` @@ -268,7 +268,7 @@ object ModuleUtils { (for ( module <- modules ) yield (module, new BuildTargetIdentifier( (module.millOuterCtx.millSourcePath / os.RelPath(moduleName(module.millModuleSegments))). - toNIO.toAbsolutePath.toUri.toString))).toMap + toIO.toURI.toString))).toMap } // this is taken from mill.scalalib GenIdeaImpl -- cgit v1.2.3 From db9f9dab89c37d9a804d7aeb7a01f705a87087ac Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Fri, 26 Jul 2019 17:36:34 +0200 Subject: Fixed bugs in passing the bsp specified run and test parameters to mill. --- contrib/bsp/readme.md | 11 +++- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 74 ++++++++++++++-------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/contrib/bsp/readme.md b/contrib/bsp/readme.md index c3846f66..8e1f38e0 100644 --- a/contrib/bsp/readme.md +++ b/contrib/bsp/readme.md @@ -9,10 +9,15 @@ tasks to be executed from the IDE. # Importing an existing mill project in IntelliJ via BSP 1) Following the mill installation instructions -2) Add the following import statement in the build.sc -of your project: +2) After this contrib module would be published, you should just have to add the +following import statement in the build.sc of your project: - `import $ivy.com.lihaoyi::mill-contrib-bsp:$MILL_VERSION` + `import $ivy.com.lihaoyi::mill-contrib-bsp:$OFFICIAL_MILL_VERSION` + +However, for now, you need to clone the mill repo, publish it locally by running +`ci/publish-local` and importing your local version in build.sc: + + `import $ivy.com.lihaoyi::mill-contrib-bsp:$LOCAL_MILL_VERSION` 3) Run the following command in the working directory of your project: diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 247f5ddb..648f44e6 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -5,6 +5,8 @@ import java.util.concurrent.CompletableFuture import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ +import com.google.gson.JsonObject +import mill.api.Result.{Skipped, Success} import mill.{scalalib, _} import mill.api.{BspContext, Result, Strict} import mill.contrib.bsp.ModuleUtils._ @@ -17,7 +19,6 @@ import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.Ctx import mill.define.{Discover, ExternalModule} -import mill.main.MainModule import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange @@ -257,52 +258,71 @@ class MillBuildServer(evaluator: Evaluator, override def buildTargetRun(runParams: RunParams): CompletableFuture[RunResult] = { 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.mkString(" ")) - val runResult = millEvaluator.evaluate(Strict.Agg(runTask), - getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", - "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), - logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) - if (runResult.failing.keyCount > 0) { - new RunResult(StatusCode.ERROR) - } else { - new RunResult(StatusCode.OK) - } + val module = targetIdToModule(params.getTargets.head) + val args = params.getArguments.getOrElse(Seq.empty[String]) + val runTask = module.run(args:_*) + val runResult = millEvaluator.evaluate(Strict.Agg(runTask), + getBspLoggedReporterPool( + params, + (t) => s"Started compiling target: $t", + "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) + } + params.getOriginId match { + case Some(id) => response.setOriginId(id) + case None => + } + response } handleExceptions[String, RunResult]((in) => 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 = { - if (results.failing.keyCount > 0) { + val statusCodes = results.results.keys.map(task => getStatusCodePerTask(results, task)).toSeq + if (statusCodes.contains(StatusCode.ERROR)) StatusCode.ERROR - } - - else if (results.rawValues.contains(Result.Skipped) || results.rawValues.contains(Result.Aborted)) { + else if (statusCodes.contains(StatusCode.CANCELLED)) StatusCode.CANCELLED - } - - else {StatusCode.OK} + else + StatusCode.OK } override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = { def getTestResult: TestResult = { val params = TaskParameters.fromTestParams(testParams) - val argsMap = testParams.getData match { - case scalaTestParams: ScalaTestParams => - (for (testItem <- scalaTestParams.getTestClasses.asScala) - yield (testItem.getTarget, testItem.getClasses.asScala.toSeq)).toMap + 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 e: Exception => (for (targetId <- testParams.getTargets.asScala) yield + (targetId.getUri, Seq.empty[String])).toMap + + } - case default => (for (targetId <- testParams.getTargets.asScala) yield (targetId, 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):_*) + 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)) -- cgit v1.2.3 From f42ef45af88ad3f352d21b45ba18bd4b6945bbc5 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 29 Jul 2019 09:36:35 +0200 Subject: Made corrections to the bsp module readme. --- contrib/bsp/readme.md | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/contrib/bsp/readme.md b/contrib/bsp/readme.md index 8e1f38e0..20b627c4 100644 --- a/contrib/bsp/readme.md +++ b/contrib/bsp/readme.md @@ -8,30 +8,33 @@ tasks to be executed from the IDE. # Importing an existing mill project in IntelliJ via BSP -1) Following the mill installation instructions -2) After this contrib module would be published, you should just have to add the -following import statement in the build.sc of your project: +1) Clone the mill git repo +2) Publish your mill version locally with `ci/publish-contrib` +3) Run the following command in the working directory of your project: + + `~/mill-release -i mill.contrib.BSP/install` + + This should create a `.bsp/` directory inside your working directory, + containing a BSP connection file that clients can use to start the + BSP server for Mill. + + This command should be ran whenever you change the version of mill that + you use. + + 4) Now you can use IntelliJ to import your project from existing sources + via bsp ( currently available in the nightly release ). Note: It might + take a few minutes to import a project the very first time. + +After the bsp support module would be published, it should be enough to: + +1) Install mill +2) Add the following import statement in the build.sc of your project: `import $ivy.com.lihaoyi::mill-contrib-bsp:$OFFICIAL_MILL_VERSION` -However, for now, you need to clone the mill repo, publish it locally by running -`ci/publish-local` and importing your local version in build.sc: - - `import $ivy.com.lihaoyi::mill-contrib-bsp:$LOCAL_MILL_VERSION` - 3) Run the following command in the working directory of your project: `mill -i mill.contrib.BSP/install` - This should create a `.bsp/` directory inside your working directory, - containing a BSP connection file that clients can use to start the - BSP server for Mill. - - This command should be ran whenever you chnage the version of mill that - you use. - -4) Now you can use IntelliJ to import your project from existing sources -via bsp ( currently available in the nightly release ). Note: It might -take a few minutes to import a project the very first time. ## Known Issues: -- cgit v1.2.3 From e442c892166dc440fe45c3f403e4cfa2a709d68f Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 29 Jul 2019 13:16:26 +0200 Subject: Fixed the compile request, if the root target is in the compile params, then don't include it in the compile task. Fixed some style issues suggested by intellij. Now, targets are recomputed at the beginning of each server request. --- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 116 +++++++++++---------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 648f44e6..e9f48a19 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -2,7 +2,6 @@ package mill.contrib.bsp import sbt.testing._ import java.util.concurrent.CompletableFuture - import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import com.google.gson.JsonObject @@ -14,14 +13,14 @@ import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult import sbt.internal.inc._ - import scala.collection.JavaConverters._ import mill.modules.Jvm -import mill.util.Ctx +import mill.util.{Ctx, DummyLogger} import mill.define.{Discover, ExternalModule} +import mill.main.EvaluatorScopt +import os.Path import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange - import scala.io.Source @@ -30,7 +29,7 @@ class MillBuildServer(evaluator: Evaluator, serverVersion:String, languages: List[String]) extends ExternalModule with BuildServer with ScalaBuildServer { - implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() + 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 @@ -54,8 +53,8 @@ class MillBuildServer(evaluator: Evaluator, var clientInitialized = false val ctx: Ctx.Log with Ctx.Home = new Ctx.Log with Ctx.Home { - val log = mill.util.DummyLogger - val home = os.pwd + val log: DummyLogger.type = mill.util.DummyLogger + val home: Path = os.pwd } override def onConnectWithClient(server: BuildClient): Unit = @@ -82,7 +81,7 @@ class MillBuildServer(evaluator: Evaluator, } override def buildShutdown(): CompletableFuture[Object] = { - handleExceptions[String, Object]((in) => "shut down this server".asInstanceOf[Object], "") + handleExceptions[String, Object](_ => "shut down this server".asInstanceOf[Object], "") } override def onBuildExit(): Unit = { @@ -92,12 +91,12 @@ class MillBuildServer(evaluator: Evaluator, override def workspaceBuildTargets(): CompletableFuture[WorkspaceBuildTargetsResult] = { recomputeTargets() handleExceptions[String, WorkspaceBuildTargetsResult]( - (in) => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), + _ => new WorkspaceBuildTargetsResult(moduleToTarget.values.toList.asJava), "") } override def buildTargetSources(sourcesParams: SourcesParams): CompletableFuture[SourcesResult] = { - + recomputeTargets() def computeSourcesResult: SourcesResult = { var items = List[SourcesItem]() @@ -126,11 +125,12 @@ class MillBuildServer(evaluator: Evaluator, new SourcesResult(items.asJava) } - handleExceptions[String, SourcesResult]((in) => computeSourcesResult, "") + handleExceptions[String, SourcesResult](_ => computeSourcesResult, "") } override def buildTargetInverseSources(inverseSourcesParams: InverseSourcesParams): - CompletableFuture[InverseSourcesResult] = { + CompletableFuture[InverseSourcesResult] = { + recomputeTargets() def getInverseSourcesResult: InverseSourcesResult = { val textDocument = inverseSourcesParams.getTextDocument @@ -141,11 +141,12 @@ class MillBuildServer(evaluator: Evaluator, map(m => moduleToTargetId(m)) new InverseSourcesResult(targets.asJava) } - handleExceptions[String, InverseSourcesResult]((in) => getInverseSourcesResult, "") + handleExceptions[String, InverseSourcesResult](_ => getInverseSourcesResult, "") } override def buildTargetDependencySources(dependencySourcesParams: DependencySourcesParams): - CompletableFuture[DependencySourcesResult] = { + CompletableFuture[DependencySourcesResult] = { + recomputeTargets() def getDependencySources: DependencySourcesResult = { var items = List[DependencySourcesItem]() @@ -161,10 +162,10 @@ class MillBuildServer(evaluator: Evaluator, millModule.unmanagedClasspath, Agg.empty[PathRef]) millModule match { - case m: ScalaModule => sources ++= evaluateInformativeTask(evaluator, + case _: ScalaModule => sources ++= evaluateInformativeTask(evaluator, millModule.resolveDeps(millModule.asInstanceOf[ScalaModule].scalaLibraryIvyDeps), Agg.empty[PathRef]) - case m: JavaModule => sources ++= List() + case _: JavaModule => sources ++= List() } items ++= List(new DependencySourcesItem(targetId, sources. map(pathRef => pathRef.path.toIO.toURI.toString). @@ -173,11 +174,11 @@ class MillBuildServer(evaluator: Evaluator, new DependencySourcesResult(items.asJava) } - handleExceptions[String, DependencySourcesResult]((in) => getDependencySources, "") + handleExceptions[String, DependencySourcesResult](_ => getDependencySources, "") } override def buildTargetResources(resourcesParams: ResourcesParams): CompletableFuture[ResourcesResult] = { - + recomputeTargets() def getResources: ResourcesResult = { var items = List[ResourcesItem]() @@ -192,7 +193,7 @@ class MillBuildServer(evaluator: Evaluator, new ResourcesResult(items.asJava) } - handleExceptions[String, ResourcesResult]((in) => getResources, "") + handleExceptions[String, ResourcesResult](_ => getResources, "") } // construct the ManagedLogger that will go into the compilation problems reporter @@ -211,7 +212,7 @@ class MillBuildServer(evaluator: Evaluator, private[this] def getBspLoggedReporterPool(params: Parameters, taskStartMessage: String => String, taskStartDataKind: String, taskStartData: BuildTargetIdentifier => Object): Int => Option[ManagedLoggedReporter] = { - (int: Int) => + int: Int => if (moduleCodeToTargetId.contains(int)) { val targetId = moduleCodeToTargetId(int) val taskId = new TaskId(targetIdToModule(targetId).compile.hashCode.toString) @@ -232,13 +233,15 @@ class MillBuildServer(evaluator: Evaluator, //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.map(targetId => targetIdToModule(targetId).compile):_*) + val compileTasks = Strict.Agg(params.getTargets. + filter(targetId => targetId != moduleToTarget(rootModule).getId). + map(targetId => targetIdToModule(targetId).compile):_*) val result = millEvaluator.evaluate(compileTasks, - getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", + getBspLoggedReporterPool(params, t => s"Started compiling target: $t", "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), new BspContext { override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) @@ -252,10 +255,11 @@ class MillBuildServer(evaluator: Evaluator, 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]((in) => getCompileResult, "") + 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) @@ -264,7 +268,7 @@ class MillBuildServer(evaluator: Evaluator, val runResult = millEvaluator.evaluate(Strict.Agg(runTask), getBspLoggedReporterPool( params, - (t) => s"Started compiling target: $t", + t => s"Started compiling target: $t", "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) @@ -278,7 +282,7 @@ class MillBuildServer(evaluator: Evaluator, } response } - handleExceptions[String, RunResult]((in) => getRunResult, "") + handleExceptions[String, RunResult](_ => getRunResult, "") } private[this] def getStatusCodePerTask(results: Evaluator.Results, task: mill.define.Task[_]): StatusCode = { @@ -302,6 +306,7 @@ class MillBuildServer(evaluator: Evaluator, } override def buildTargetTest(testParams: TestParams): CompletableFuture[TestResult] = { + recomputeTargets() def getTestResult: TestResult = { val params = TaskParameters.fromTestParams(testParams) val argsMap = try { @@ -312,7 +317,7 @@ class MillBuildServer(evaluator: Evaluator, testItem.getAsJsonObject.get("classes").getAsJsonArray .asScala.map(elem => elem.getAsString).toSeq)).toMap } catch { - case e: Exception => (for (targetId <- testParams.getTargets.asScala) yield + case _: Exception => (for (targetId <- testParams.getTargets.asScala) yield (targetId.getUri, Seq.empty[String])).toMap } @@ -339,7 +344,7 @@ class MillBuildServer(evaluator: Evaluator, val results = millEvaluator.evaluate( Strict.Agg(testTask), - getBspLoggedReporterPool(params, (t) => s"Started compiling target: $t", + getBspLoggedReporterPool(params, t => s"Started compiling target: $t", "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), bspContext, new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) @@ -363,7 +368,7 @@ class MillBuildServer(evaluator: Evaluator, taskFinishParams.setData(bspContext.getTestReport) client.onBuildTaskFinish(taskFinishParams) - case default => + case _ => } } val testResult = new TestResult(overallStatusCode) @@ -375,11 +380,11 @@ class MillBuildServer(evaluator: Evaluator, testResult } } - handleExceptions[String, TestResult]((in) => getTestResult, "") + handleExceptions[String, TestResult](_ => getTestResult, "") } override def buildTargetCleanCache(cleanCacheParams: CleanCacheParams): CompletableFuture[CleanCacheResult] = { - + recomputeTargets() def getCleanCacheResult: CleanCacheResult = { var msg = "" var cleaned = true @@ -399,7 +404,7 @@ class MillBuildServer(evaluator: Evaluator, val errMessage = Source.fromInputStream(processErr).getLines().mkString("\n") val message = Source.fromInputStream(processIn).getLines().mkString("\n") - msg += s"Cleaning cache for target ${targetId} produced the following message: ${message}, ${errMessage}" + msg += s"Cleaning cache for target $targetId produced the following message: $message, $errMessage" if (msg.contains("failed") || msg.contains("Error")) { cleaned = false } @@ -407,11 +412,12 @@ class MillBuildServer(evaluator: Evaluator, } new CleanCacheResult(msg, cleaned) } - handleExceptions[String, CleanCacheResult]((in) => getCleanCacheResult, "") + handleExceptions[String, CleanCacheResult](_ => getCleanCacheResult, "") } override def buildTargetScalacOptions(scalacOptionsParams: ScalacOptionsParams): CompletableFuture[ScalacOptionsResult] = { + recomputeTargets() def getScalacOptionsResult: ScalacOptionsResult = { var targetScalacOptions = List.empty[ScalacOptionsItem] for (targetId <- scalacOptionsParams.getTargets.asScala) { @@ -425,12 +431,12 @@ class MillBuildServer(evaluator: Evaluator, dest / "classes").toIO.toURI.toString targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) - case m: JavaModule => targetScalacOptions ++= List() + case _: JavaModule => targetScalacOptions ++= List() } } new ScalacOptionsResult(targetScalacOptions.asJava) } - handleExceptions[String, ScalacOptionsResult]((in) => getScalacOptionsResult, "") + handleExceptions[String, ScalacOptionsResult](_ => getScalacOptionsResult, "") } //TODO: In the case when mill fails to provide a main classes because multiple were @@ -438,7 +444,7 @@ class MillBuildServer(evaluator: Evaluator, // such that IntelliJ can run any of them override def buildTargetScalaMainClasses(scalaMainClassesParams: ScalaMainClassesParams): CompletableFuture[ScalaMainClassesResult] = { - + recomputeTargets() def getScalaMainClasses: ScalaMainClassesResult = { var items = List.empty[ScalaMainClassesItem] for (targetId <- scalaMainClassesParams.getTargets.asScala) { @@ -457,14 +463,14 @@ class MillBuildServer(evaluator: Evaluator, client.onBuildShowMessage(messageParams) // tell the client that no main class was found or specified List.empty[ScalaMainClass] } - case default => List.empty[ScalaMainClass] + case _ => List.empty[ScalaMainClass] } val item = new ScalaMainClassesItem (targetId , scalaMainClasses.asJava) items ++= List(item) } new ScalaMainClassesResult(items.asJava) } - handleExceptions[String, ScalaMainClassesResult]((in) => getScalaMainClasses, "") + handleExceptions[String, ScalaMainClassesResult](_ => getScalaMainClasses, "") } // Detect and return the test classes contained in the given TestModule @@ -476,33 +482,34 @@ class MillBuildServer(evaluator: Evaluator, (runClasspath, frameworks, compilationResult) match { case (Result.Success(classpath), Result.Success(testFrameworks), Result.Success(compResult)) => val classFingerprint = Jvm.inprocess(classpath.asInstanceOf[Seq[PathRef]].map(_.path), - true, - true, - false, cl => { + 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 default => Seq.empty[String] //TODO: or send notification that something went wrong + case _ => Seq.empty[String] //TODO: or send notification that something went wrong } } override def buildTargetScalaTestClasses(scalaTestClassesParams: ScalaTestClassesParams): CompletableFuture[ScalaTestClassesResult] = { + recomputeTargets() 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)) - case module: JavaModule => //TODO: maybe send a notification that this target has no test classes + 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) + handleExceptions[Ctx.Home, ScalaTestClassesResult](c => getScalaTestClasses(c), ctx) } // Given the mapping from modules to targetIds, construct the mapping from targetIds to modules @@ -537,17 +544,16 @@ class MillBuildServer(evaluator: Evaluator, // yet initialized. private[this] def handleExceptions[T, V](serverMethod: T => V, input: T): CompletableFuture[V] = { val future = new CompletableFuture[V]() - initialized match { - case true => - try { - future.complete(serverMethod(input)) - } catch { - case e: Exception => future.completeExceptionally(e) - } - case false => - future.completeExceptionally( - new Exception("Can not respond to any request before receiving the `initialize` request.") - ) + 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 } -- cgit v1.2.3 From 45036a5e3e4a7aa74f5364bba1ec557c6249bcfc Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Mon, 29 Jul 2019 17:28:48 +0200 Subject: Fixed the classpath for the scalac option request, so now main classes can be ran from intellij. --- .bsp/mill-bsp.json | 1 - contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 .bsp/mill-bsp.json diff --git a/.bsp/mill-bsp.json b/.bsp/mill-bsp.json deleted file mode 100644 index fdc584d0..00000000 --- a/.bsp/mill-bsp.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"mill-bsp","argv":["mill", "-i", "dev.run", "scratch", "-i","mill.contrib.MainMillBuildServer/startServer"],"version":"1.0.0","bspVersion":"2.0.0-M4","languages":["scala","java"]} diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index e9f48a19..7454a944 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -425,7 +425,7 @@ class MillBuildServer(evaluator: Evaluator, module match { case m: ScalaModule => val options = evaluateInformativeTask(evaluator, m.scalacOptions, Seq.empty[String]).toList - val classpath = evaluateInformativeTask(evaluator, m.compileClasspath, Agg.empty[PathRef]). + 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). dest / "classes").toIO.toURI.toString -- cgit v1.2.3 From 2471d68dd3d86331e0e456e9f81099d217004139 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 31 Jul 2019 09:06:11 +0200 Subject: Fixed bugs: If a module has no resource directory don't just throw error, just return empty list of resources. Include the name of the compile task in the segments that get converted tothe scalac options class directory. --- .gitignore | 3 +- contrib/bsp/src/mill/contrib/BSP.scala | 132 +++++++++++++++++++++ .../bsp/src/mill/contrib/MainMillBuildServer.scala | 132 --------------------- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 9 +- 4 files changed, 140 insertions(+), 136 deletions(-) create mode 100644 contrib/bsp/src/mill/contrib/BSP.scala delete mode 100644 contrib/bsp/src/mill/contrib/MainMillBuildServer.scala diff --git a/.gitignore b/.gitignore index af52d378..6c7f0baa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ contrib/bsp/mill-external-bs contrib/bsp/mill-out-bs mill.iml .bsp/ -bsp.log \ No newline at end of file +bsp.log +contrib/bsp/test/ \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala new file mode 100644 index 00000000..d31eb7f4 --- /dev/null +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -0,0 +1,132 @@ +package mill.contrib + +import java.io.{File, PrintWriter} + +import play.api.libs.json._ +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 scala.collection.JavaConverters._ +import scala.concurrent.CancellationException + + +object BSP extends ExternalModule { + + implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() + + lazy val millDiscover: Discover[BSP.this.type] = Discover[this.type] + val version = "1.0.0" + 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 + * a potential client to find. + * + * If a .bsp folder with a connection file already + * exists in the working directory, it will be + * overwritten and a corresponding message will be displayed + * in stdout. + * + * If the creation of the .bsp folder fails due to any other + * reason, the message and stacktrace of the exception will be + * printed to stdout. + * + */ + def install(ev: Evaluator): Command[Unit] = T.command{ + val bspDirectory = os.pwd / ".bsp" + if (! os.exists(bspDirectory)) os.makeDir.all(bspDirectory) + try { + os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) + } catch { + case e: FileAlreadyExistsException => + println("The bsp connection json file probably exists already - will be overwritten") + 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) + } + + } + + /** + * 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 + */ + 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) + val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) + val executor = Executors.newCachedThreadPool() + + val stdin = System.in + val stdout = System.out + try { + val launcher = new Launcher.Builder[BuildClient]() + .setOutput(stdout) + .setInput(stdin) + .setLocalService(millServer) + .setRemoteInterface(classOf[BuildClient]). + traceMessages(new PrintWriter((os.pwd/ "bsp.log" ).toIO)) + .setExecutorService(executor) + .create() + millServer.onConnectWithClient(launcher.getRemoteProxy) + val listening = launcher.startListening() + millServer.cancelator = () => listening.cancel(true) + val voidFuture = listening.get() + } catch { + case _: CancellationException => System.err.println("The mill server was shut down.") + case e: Exception => + System.err.println("An exception occured while connecting to the client.") + System.err.println("Cause: " + e.getCause) + System.err.println("Message: " + e.getMessage) + System.err.println("Exception class: " + e.getClass) + System.err.println("Stack Trace: " + e.getStackTrace) + } finally { + System.err.println("Shutting down executor") + executor.shutdown() + } + } +} \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala b/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala deleted file mode 100644 index d31eb7f4..00000000 --- a/contrib/bsp/src/mill/contrib/MainMillBuildServer.scala +++ /dev/null @@ -1,132 +0,0 @@ -package mill.contrib - -import java.io.{File, PrintWriter} - -import play.api.libs.json._ -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 scala.collection.JavaConverters._ -import scala.concurrent.CancellationException - - -object BSP extends ExternalModule { - - implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() - - lazy val millDiscover: Discover[BSP.this.type] = Discover[this.type] - val version = "1.0.0" - 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 - * a potential client to find. - * - * If a .bsp folder with a connection file already - * exists in the working directory, it will be - * overwritten and a corresponding message will be displayed - * in stdout. - * - * If the creation of the .bsp folder fails due to any other - * reason, the message and stacktrace of the exception will be - * printed to stdout. - * - */ - def install(ev: Evaluator): Command[Unit] = T.command{ - val bspDirectory = os.pwd / ".bsp" - if (! os.exists(bspDirectory)) os.makeDir.all(bspDirectory) - try { - os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) - } catch { - case e: FileAlreadyExistsException => - println("The bsp connection json file probably exists already - will be overwritten") - 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) - } - - } - - /** - * 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 - */ - 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) - val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) - val executor = Executors.newCachedThreadPool() - - val stdin = System.in - val stdout = System.out - try { - val launcher = new Launcher.Builder[BuildClient]() - .setOutput(stdout) - .setInput(stdin) - .setLocalService(millServer) - .setRemoteInterface(classOf[BuildClient]). - traceMessages(new PrintWriter((os.pwd/ "bsp.log" ).toIO)) - .setExecutorService(executor) - .create() - millServer.onConnectWithClient(launcher.getRemoteProxy) - val listening = launcher.startListening() - millServer.cancelator = () => listening.cancel(true) - val voidFuture = listening.get() - } catch { - case _: CancellationException => System.err.println("The mill server was shut down.") - case e: Exception => - System.err.println("An exception occured while connecting to the client.") - System.err.println("Cause: " + e.getCause) - System.err.println("Message: " + e.getMessage) - System.err.println("Exception class: " + e.getClass) - System.err.println("Stack Trace: " + e.getStackTrace) - } finally { - System.err.println("Shutting down executor") - executor.shutdown() - } - } -} \ No newline at end of file diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 7454a944..6e9cbc37 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -9,6 +9,7 @@ import mill.api.Result.{Skipped, Success} import mill.{scalalib, _} import mill.api.{BspContext, Result, Strict} import mill.contrib.bsp.ModuleUtils._ +import mill.define.Segment.Label import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult @@ -185,12 +186,12 @@ class MillBuildServer(evaluator: Evaluator, 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 items ++= List(new ResourcesItem(targetId, resources)) } - new ResourcesResult(items.asJava) } handleExceptions[String, ResourcesResult](_ => getResources, "") @@ -427,8 +428,10 @@ class MillBuildServer(evaluator: Evaluator, val options = evaluateInformativeTask(evaluator, m.scalacOptions, Seq.empty[String]).toList 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). - dest / "classes").toIO.toURI.toString + val classDirectory = (Evaluator.resolveDestPaths( + os.pwd / "out" , + m.millModuleSegments ++ Seq(Label("compile"))).dest / "classes" + ).toIO.toURI.toString targetScalacOptions ++= List(new ScalacOptionsItem(targetId, options.asJava, classpath.asJava, classDirectory)) case _: JavaModule => targetScalacOptions ++= List() -- cgit v1.2.3 From d215c582d75d114ebe7dd3f7a61326263ada1ab1 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 31 Jul 2019 15:25:33 +0200 Subject: CHanged the clean cache command to use the mill api again --- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 46 +++++++++++++--------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 6e9cbc37..423bab0b 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -2,6 +2,7 @@ package mill.contrib.bsp import sbt.testing._ import java.util.concurrent.CompletableFuture + import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import com.google.gson.JsonObject @@ -14,14 +15,16 @@ import mill.eval.Evaluator import mill.scalalib._ import mill.scalalib.api.CompilationResult import sbt.internal.inc._ + import scala.collection.JavaConverters._ import mill.modules.Jvm import mill.util.{Ctx, DummyLogger} import mill.define.{Discover, ExternalModule} -import mill.main.EvaluatorScopt +import mill.main.{EvaluatorScopt, MainModule} import os.Path import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange + import scala.io.Source @@ -391,25 +394,32 @@ class MillBuildServer(evaluator: Evaluator, var cleaned = true for (targetId <- cleanCacheParams.getTargets.asScala) { val module = targetIdToModule(targetId) - val cleanCommand = Array("java", - s"-DMILL_CLASSPATH=${System.getProperty("MILL_CLASSPATH")}", - s"-DMILL_VERSION=${System.getProperty("MILL_VERSION")}", - "-Djna.nosys=true", "-cp", - System.getProperty("MILL_CLASSPATH"), - "mill.MillMain", "clean", - s"${module.millModuleSegments.render}.compile") - val process = Runtime.getRuntime.exec(cleanCommand, null, os.pwd.toIO) - - val processIn = process.getInputStream - val processErr = process.getErrorStream - - val errMessage = Source.fromInputStream(processErr).getLines().mkString("\n") - val message = Source.fromInputStream(processIn).getLines().mkString("\n") - msg += s"Cleaning cache for target $targetId produced the following message: $message, $errMessage" - if (msg.contains("failed") || msg.contains("Error")) { + 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) + ) + 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) + } } - process.waitFor() } new CleanCacheResult(msg, cleaned) } -- cgit v1.2.3 From 33725730a0211cc437cf2158599b39aad86ac56d Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Sat, 10 Aug 2019 17:02:12 +0400 Subject: Fix typo in data kind field, use constants from bsp4j --- contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala | 4 ++-- contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 10 +++++----- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 51a2f2ba..2e981259 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -36,7 +36,7 @@ class BspTestReporter( override def logStart(event: Event): Unit = { val taskStartParams = new TaskStartParams(taskId) taskStartParams.setEventTime(System.currentTimeMillis()) - taskStartParams.setDataKind("test-started") + taskStartParams.setDataKind(TaskDataKind.TEST_START) taskStartParams.setData(new TestStart(getDisplayName(event))) taskStartParams.setMessage("Starting running: " + getDisplayName(event)) client.onBuildTaskStart(taskStartParams) @@ -50,7 +50,7 @@ class BspTestReporter( case sbt.testing.Status.Error => StatusCode.ERROR case default => StatusCode.OK }) - taskFinishParams.setDataKind("test-finished") + taskFinishParams.setDataKind(TaskDataKind.TEST_FINISH) val status = event.status match { case sbt.testing.Status.Success => passed += 1 diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 423bab0b..9557586e 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -246,7 +246,7 @@ class MillBuildServer(evaluator: Evaluator, map(targetId => targetIdToModule(targetId).compile):_*) val result = millEvaluator.evaluate(compileTasks, getBspLoggedReporterPool(params, t => s"Started compiling target: $t", - "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), + TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), new BspContext { override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) override def logStart(event: Event): Unit = {} @@ -273,7 +273,7 @@ class MillBuildServer(evaluator: Evaluator, getBspLoggedReporterPool( params, t => s"Started compiling target: $t", - "compile-task", + TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), logger = new MillBspLogger(client, runTask.hashCode(), millEvaluator.log)) val response = runResult.results(runTask) match { @@ -337,7 +337,7 @@ class MillBuildServer(evaluator: Evaluator, val taskStartParams = new TaskStartParams(new TaskId(testTask.hashCode().toString)) taskStartParams.setEventTime(System.currentTimeMillis()) taskStartParams.setMessage("Testing target: " + targetId) - taskStartParams.setDataKind("test-task") + taskStartParams.setDataKind(TaskDataKind.TEST_TASK) taskStartParams.setData(new TestTask(targetId)) client.onBuildTaskStart(taskStartParams) @@ -349,7 +349,7 @@ class MillBuildServer(evaluator: Evaluator, val results = millEvaluator.evaluate( Strict.Agg(testTask), getBspLoggedReporterPool(params, t => s"Started compiling target: $t", - "compile-task", (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), + TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), bspContext, new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) val endTime = System.currentTimeMillis() @@ -368,7 +368,7 @@ class MillBuildServer(evaluator: Evaluator, taskFinishParams.setEventTime(endTime) taskFinishParams.setMessage("Finished testing target: " + moduleToTarget(targetIdToModule(targetId)).getDisplayName) - taskFinishParams.setDataKind("test-report") + taskFinishParams.setDataKind(TaskDataKind.TEST_REPORT) taskFinishParams.setData(bspContext.getTestReport) client.onBuildTaskFinish(taskFinishParams) diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index ffe3f8d7..277091ed 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -121,7 +121,7 @@ object ModuleUtils { List.empty[BuildTargetIdentifier].asJava, new BuildTargetCapabilities(false, false, false)) rootTarget.setBaseDirectory(rootModule.millSourcePath.toIO.toURI.toString) - rootTarget.setDataKind("scala") + rootTarget.setDataKind(BuildTargetDataKind.SCALA) rootTarget.setTags(List(BuildTargetTag.LIBRARY, BuildTargetTag.APPLICATION).asJava) rootTarget.setData(computeBuildTargetData(rootModule, evaluator)) val basePath = rootModule.millSourcePath.toIO.toPath @@ -165,7 +165,7 @@ object ModuleUtils { dependencies, capabilities) if (module.isInstanceOf[ScalaModule]) { - buildTarget.setDataKind("scala") + buildTarget.setDataKind(BuildTargetDataKind.SCALA) } buildTarget.setData(dataBuildTarget) buildTarget.setDisplayName(moduleName(module.millModuleSegments)) -- cgit v1.2.3 From fcdfba1df24ff921d7bbb95e0f43c9b35e4a7b70 Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Mon, 12 Aug 2019 12:17:35 +0400 Subject: Fix failed test not being reported with appropriate status, add full stacktrace as message --- .../bsp/src/mill/contrib/bsp/BspTestReporter.scala | 29 +++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 2e981259..05e58ec7 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -1,5 +1,7 @@ package mill.contrib.bsp +import java.io.{PrintWriter, StringWriter} + import ch.epfl.scala.bsp4j._ import mill.api.BspContext import sbt.testing._ @@ -50,7 +52,6 @@ class BspTestReporter( case sbt.testing.Status.Error => StatusCode.ERROR case default => StatusCode.OK }) - taskFinishParams.setDataKind(TaskDataKind.TEST_FINISH) val status = event.status match { case sbt.testing.Status.Success => passed += 1 @@ -74,21 +75,25 @@ class BspTestReporter( skipped += 1 TestStatus.SKIPPED //TODO: what to do here } - val testFinish = new TestFinish(getDisplayName(event), status) - taskFinishParams.setData(testFinish) - taskFinishParams.setEventTime(System.currentTimeMillis()) - taskFinishParams.setMessage("Finished running: " + getDisplayName(event)) - if (event.throwable.isDefined) { - val exception = event.throwable.get - taskFinishParams.setData( // send data about any potential exceptions thrown during testing - TestException(exception.getStackTrace.toString, - exception.getMessage, - exception.getClass.toString)) - } + taskFinishParams.setDataKind(TaskDataKind.TEST_FINISH) + taskFinishParams.setData({ + val testFinish = new TestFinish(getDisplayName(event), status) + if (event.throwable.isDefined) + testFinish.setMessage(throwableToString(event.throwable().get())) + testFinish + }) + taskFinishParams.setEventTime(System.currentTimeMillis()) client.onBuildTaskFinish(taskFinishParams) } + private def throwableToString(t: Throwable): String = { + val sw = new StringWriter + val pw = new PrintWriter(sw) + t.printStackTrace(pw) + sw.toString + } + // Compute the display name of the test / test suite // to which the given event relates private[this] def getDisplayName(e: Event): String = { -- cgit v1.2.3 From 86d11edd422bc61639959f086282b4d3870fa053 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 28 Aug 2019 13:52:05 -0400 Subject: Update contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala Fix typo Co-Authored-By: Tobias Roeser --- contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala index 277091ed..8cccf7b3 100644 --- a/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala +++ b/contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala @@ -77,7 +77,7 @@ object ModuleUtils { * Given the BaseModule corresponding to the root * of the working directory, compute a JavaModule that * has the same millSourcePath. Set generated sources - * accoridng to the location of the compilation + * according to the location of the compilation * products * @param rootBaseModule module for the root * @return root JavaModule -- cgit v1.2.3 From 91cddda19106c468fbc0422b6aefc62618a4e128 Mon Sep 17 00:00:00 2001 From: Alexandra Dima Date: Wed, 28 Aug 2019 13:55:00 -0400 Subject: Updated documentation of contrib module since now it is possible to run scala classes directly from IntelliJ and removed file added to git by mistake. --- contrib/bsp/readme.md | 3 --- info.txt | 1 - 2 files changed, 4 deletions(-) delete mode 100644 info.txt diff --git a/contrib/bsp/readme.md b/contrib/bsp/readme.md index 20b627c4..c9557e3a 100644 --- a/contrib/bsp/readme.md +++ b/contrib/bsp/readme.md @@ -41,6 +41,3 @@ After the bsp support module would be published, it should be enough to: - Sometimes build from IntelliJ might fail due to a NoClassDefFoundException being thrown during the evaluation of tasks, a bug not easy to reproduce. In this case it is recommended to refresh the bsp project. - -- Currently it's not possible ro run scala classes from intelliJ, but this -issue is being investigated diff --git a/info.txt b/info.txt deleted file mode 100644 index 86fe32d4..00000000 --- a/info.txt +++ /dev/null @@ -1 +0,0 @@ -Build 2 -- cgit v1.2.3 From fc9b6e8b573fb2ad61801d0f335a05f07e3a2d83 Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Sun, 8 Sep 2019 22:13:11 +0200 Subject: Remove the dependency to bsp and zinc from core modules --- .../src/mill/contrib/bsp/BspLoggedReporter.scala | 43 +++++------- .../bsp/src/mill/contrib/bsp/BspTestReporter.scala | 6 +- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 38 ++++------- main/api/src/mill/api/BspCompileArguments.scala | 18 ----- main/api/src/mill/api/BspContext.scala | 30 --------- main/api/src/mill/api/Ctx.scala | 15 ++--- main/api/src/mill/api/TestReporter.scala | 60 ++++++++++++++++- main/core/src/eval/Evaluator.scala | 43 ++++++------ scalajslib/src/ScalaJSModule.scala | 2 +- scalalib/api/src/ZincWorkerApi.scala | 9 +-- scalalib/src/JavaModule.scala | 7 +- scalalib/src/ScalaModule.scala | 5 +- scalalib/src/TestRunner.scala | 4 +- scalalib/worker/src/ZincWorkerImpl.scala | 77 ++++++++++++++++++---- scalanativelib/src/ScalaNativeModule.scala | 2 +- 15 files changed, 197 insertions(+), 162 deletions(-) delete mode 100644 main/api/src/mill/api/BspCompileArguments.scala delete mode 100644 main/api/src/mill/api/BspContext.scala diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index ba42a67a..b4f0260d 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -1,17 +1,16 @@ package mill.contrib.bsp import java.io.File -import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicInteger + import ch.epfl.scala.bsp4j._ import ch.epfl.scala.{bsp4j => bsp} -import sbt.internal.inc.ManagedLoggedReporter -import sbt.internal.util.ManagedLogger -import xsbti.{Problem, Severity} +import mill.api.{Info, Problem, Warn, BuildProblemReporter} + import scala.collection.JavaConverters._ import scala.collection.concurrent -import scala.compat.java8.OptionConverters._ - +import scala.language.implicitConversions /** * Specialized reporter that sends compilation diagnostics @@ -27,16 +26,11 @@ import scala.compat.java8.OptionConverters._ * the compilation request. Needs to be sent * back as part of the published diagnostics * as well as compile report - * @param maxErrors The maximum number of errors to be logged during the - * compilation of targetId - * @param logger The logger that will log the messages for each Problem. */ class BspLoggedReporter(client: bsp.BuildClient, targetId: BuildTargetIdentifier, taskId: TaskId, - compilationOriginId: Option[String], - maxErrors: Int, - logger: ManagedLogger) extends ManagedLoggedReporter(maxErrors, logger) { + compilationOriginId: Option[String]) extends BuildProblemReporter { var errors = new AtomicInteger(0) var warnings = new AtomicInteger(0) @@ -47,19 +41,16 @@ class BspLoggedReporter(client: bsp.BuildClient, override def logError(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) errors.incrementAndGet() - super.logError(problem) } override def logInfo(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) infos.incrementAndGet() - super.logInfo(problem) } override def logWarning(problem: Problem): Unit = { client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId)) warnings.incrementAndGet() - super.logWarning(problem) } override def printSummary(): Unit = { @@ -85,7 +76,7 @@ class BspLoggedReporter(client: bsp.BuildClient, private[this] def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]): bsp.PublishDiagnosticsParams = { val diagnostic = getSingleDiagnostic(problem) - val sourceFile = problem.position().sourceFile().asScala + val sourceFile = problem.position.sourceFile val textDocument = new TextDocumentIdentifier( sourceFile.getOrElse(None) match { case None => targetId.getUri @@ -120,20 +111,22 @@ class BspLoggedReporter(client: bsp.BuildClient, // Computes the diagnostic related to the given Problem private[this] def getSingleDiagnostic(problem: Problem): Diagnostic ={ - + val pos = problem.position + val i: Integer = pos.startLine.orElse(pos.line).getOrElse[Int](0) + println(i) val start = new bsp.Position( - problem.position.startLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(0)), - problem.position.startOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(0))) + pos.startLine.orElse(pos.line).getOrElse[Int](0), + pos.startOffset.orElse(pos.offset).getOrElse[Int](0)) val end = new bsp.Position( - problem.position.endLine.asScala.getOrElse(problem.position.line.asScala.getOrElse(start.getLine)), - problem.position.endOffset.asScala.getOrElse(problem.position.offset.asScala.getOrElse(start.getCharacter))) + pos.endLine.orElse(pos.line).getOrElse[Int](start.getLine.intValue()), + pos.endOffset.orElse(pos.offset).getOrElse[Int](start.getCharacter.intValue())) val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message) - diagnostic.setCode(problem.position.lineContent) + diagnostic.setCode(pos.lineContent) diagnostic.setSource("compiler from mill") diagnostic.setSeverity( problem.severity match { - case Severity.Info => bsp.DiagnosticSeverity.INFORMATION - case Severity.Error => bsp.DiagnosticSeverity.ERROR - case Severity.Warn => bsp.DiagnosticSeverity.WARNING + case mill.api.Info => bsp.DiagnosticSeverity.INFORMATION + case mill.api.Error => bsp.DiagnosticSeverity.ERROR + case mill.api.Warn => bsp.DiagnosticSeverity.WARNING } ) diagnostic diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 05e58ec7..9681313a 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -3,7 +3,7 @@ package mill.contrib.bsp import java.io.{PrintWriter, StringWriter} import ch.epfl.scala.bsp4j._ -import mill.api.BspContext +import mill.api.TestReporter import sbt.testing._ @@ -24,7 +24,7 @@ class BspTestReporter( client: BuildClient, targetId: BuildTargetIdentifier, taskId: TaskId, - arguments: Seq[String]) extends BspContext { + arguments: Seq[String]) extends TestReporter { var passed = 0 var failed = 0 @@ -33,8 +33,6 @@ class BspTestReporter( var skipped = 0 var totalTime: Long = 0 - override def args: Seq[String] = arguments - override def logStart(event: Event): Unit = { val taskStartParams = new TaskStartParams(taskId) taskStartParams.setEventTime(System.currentTimeMillis()) diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 9557586e..40e98ef7 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -1,31 +1,27 @@ package mill.contrib.bsp -import sbt.testing._ import java.util.concurrent.CompletableFuture -import mill.scalalib.Lib.discoverTests import ch.epfl.scala.bsp4j._ import com.google.gson.JsonObject import mill.api.Result.{Skipped, Success} -import mill.{scalalib, _} -import mill.api.{BspContext, Result, Strict} +import mill.api.{DummyTestReporter, Result, Strict, BuildProblemReporter} import mill.contrib.bsp.ModuleUtils._ import mill.define.Segment.Label +import mill.define.{Discover, ExternalModule} import mill.eval.Evaluator +import mill.main.{EvaluatorScopt, MainModule} +import mill.modules.Jvm +import mill.scalalib.Lib.discoverTests import mill.scalalib._ import mill.scalalib.api.CompilationResult -import sbt.internal.inc._ - -import scala.collection.JavaConverters._ -import mill.modules.Jvm import mill.util.{Ctx, DummyLogger} -import mill.define.{Discover, ExternalModule} -import mill.main.{EvaluatorScopt, MainModule} +import mill.{scalalib, _} import os.Path import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} import sbt.util.LogExchange -import scala.io.Source +import scala.collection.JavaConverters._ class MillBuildServer(evaluator: Evaluator, @@ -215,7 +211,7 @@ class MillBuildServer(evaluator: Evaluator, // 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[ManagedLoggedReporter] = { + Int => Option[BuildProblemReporter] = { int: Int => if (moduleCodeToTargetId.contains(int)) { val targetId = moduleCodeToTargetId(int) @@ -229,9 +225,8 @@ class MillBuildServer(evaluator: Evaluator, Option(new BspLoggedReporter(client, targetId, taskId, - params.getOriginId, - 10, getCompilationLogger))} - else Option.empty[ManagedLoggedReporter] + params.getOriginId))} + else None } //TODO: if the client wants to give compilation arguments and the module @@ -247,12 +242,7 @@ class MillBuildServer(evaluator: Evaluator, val result = millEvaluator.evaluate(compileTasks, getBspLoggedReporterPool(params, t => s"Started compiling target: $t", TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), - new BspContext { - override def args: Seq[String] = params.getArguments.getOrElse(Seq.empty[String]) - override def logStart(event: Event): Unit = {} - - override def logFinish(event: Event): Unit = {} - }, + DummyTestReporter, new MillBspLogger(client, taskId, millEvaluator.log) ) val compileResult = new CompileResult(getStatusCode(result)) @@ -341,7 +331,7 @@ class MillBuildServer(evaluator: Evaluator, taskStartParams.setData(new TestTask(targetId)) client.onBuildTaskStart(taskStartParams) - val bspContext = new BspTestReporter( + val testReporter = new BspTestReporter( client, targetId, new TaskId(testTask.hashCode().toString), Seq.empty[String]) @@ -350,7 +340,7 @@ class MillBuildServer(evaluator: Evaluator, Strict.Agg(testTask), getBspLoggedReporterPool(params, t => s"Started compiling target: $t", TaskDataKind.COMPILE_TASK, (targetId: BuildTargetIdentifier) => new CompileTask(targetId)), - bspContext, + testReporter, new MillBspLogger(client, testTask.hashCode, millEvaluator.log)) val endTime = System.currentTimeMillis() val statusCode = getStatusCode(results) @@ -369,7 +359,7 @@ class MillBuildServer(evaluator: Evaluator, taskFinishParams.setMessage("Finished testing target: " + moduleToTarget(targetIdToModule(targetId)).getDisplayName) taskFinishParams.setDataKind(TaskDataKind.TEST_REPORT) - taskFinishParams.setData(bspContext.getTestReport) + taskFinishParams.setData(testReporter.getTestReport) client.onBuildTaskFinish(taskFinishParams) case _ => diff --git a/main/api/src/mill/api/BspCompileArguments.scala b/main/api/src/mill/api/BspCompileArguments.scala deleted file mode 100644 index 73586cc8..00000000 --- a/main/api/src/mill/api/BspCompileArguments.scala +++ /dev/null @@ -1,18 +0,0 @@ -package mill.api - -/** - * Data structure to represent Bsp client-specified - * compilation arguments - */ -class BspCompileArguments { - var arguments: Seq[String] = Seq.empty[String] - - /** - * Return the compilation arguments specified by the - * Bsp client, which may or may not be found in the - * compiler options of any module from the build file. - */ - def args: Seq[String] = { - arguments - } -} diff --git a/main/api/src/mill/api/BspContext.scala b/main/api/src/mill/api/BspContext.scala deleted file mode 100644 index 1281518d..00000000 --- a/main/api/src/mill/api/BspContext.scala +++ /dev/null @@ -1,30 +0,0 @@ -package mill.api - -import sbt.testing.Event - -/** - * Bsp Context with functionality for retrieving compile - * arguments provided by a Bsp client, as well as for logging - * the start and finish of a task triggered by the request of - * a Bsp client. Can be integrated into mill's Ctx to inject - * Bsp functionality into tasks like compile/run/test. - */ -trait BspContext extends BspCompileArguments with TestReporter - -/** - * Dummy Bsp Context that does nothing - * upon starting or finishing a task, and - * contains no client-specified compilation - * arguments - */ -object DummyBspContext extends BspContext { - override def args = Seq.empty[String] - - override def logStart(event: Event): Unit = { - - } - - override def logFinish(event: Event): Unit = { - - } -} \ No newline at end of file diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 02d50b22..96da84eb 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -3,7 +3,6 @@ package mill.api import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.language.implicitConversions import os.Path -import sbt.internal.inc.ManagedLoggedReporter /** * Provides access to various resources in the context of a currently execution Target. @@ -56,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[ManagedLoggedReporter], - val bsp: BspContext + 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 diff --git a/main/api/src/mill/api/TestReporter.scala b/main/api/src/mill/api/TestReporter.scala index 97dec761..8adea687 100644 --- a/main/api/src/mill/api/TestReporter.scala +++ b/main/api/src/mill/api/TestReporter.scala @@ -1,5 +1,7 @@ package mill.api +import java.io.File + import sbt.testing._ /** @@ -20,12 +22,66 @@ trait TestReporter { * Dummy Test Reporter that doesn't report * anything for any testing event. */ -object DummyReporter extends TestReporter { +object DummyTestReporter extends TestReporter { override def logStart(event: Event): Unit = { } - override def logFinish(event: Event): Unit = { } } + +trait BuildProblemReporter { + def logError(problem: Problem): Unit + + def logWarning(problem: Problem): Unit + + def logInfo(problem: Problem): Unit + + def printSummary(): Unit +} + +trait ProblemPosition { + def line: Option[Int] + + def lineContent: String + + def offset: Option[Int] + + def pointer: Option[Int] + + def pointerSpace: Option[String] + + def sourcePath: Option[String] + + def sourceFile: Option[File] + + def startOffset: Option[Int] = Option.empty + + def endOffset: Option[Int] = Option.empty + + def startLine: Option[Int] = Option.empty + + def startColumn: Option[Int] = Option.empty + + def endLine: Option[Int] = Option.empty + + def endColumn: Option[Int] = Option.empty +} + +sealed trait Severity +case object Info extends Severity +case object Error extends Severity +case object Warn extends Severity + +trait Problem { + def category: String + + def severity: Severity + + def message: String + + def position: ProblemPosition +} + + diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 75103398..37de69b3 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -2,21 +2,18 @@ package mill.eval import java.net.URLClassLoader -import scala.collection.JavaConverters._ -import scala.collection.mutable -import scala.util.control.NonFatal import ammonite.runtime.SpecialClassLoader -import mill.api.{BspContext, DummyBspContext} -import mill.util.Router.EntryPoint -import mill.define.{Ctx => _, _} import mill.api.Result.{Aborted, OuterStack, Success} +import mill.api.Strict.Agg +import mill.api.{DummyTestReporter, TestReporter, BuildProblemReporter} +import mill.define.{Ctx => _, _} import mill.util +import mill.util.Router.EntryPoint import mill.util._ -import mill.api.Strict.Agg -import sbt.internal.inc.{CompilerArguments, ManagedLoggedReporter} -import sbt.internal.util.{ConsoleOut, MainAppender} -import sbt.testing.Event -import sbt.util.LogExchange + +import scala.collection.JavaConverters._ +import scala.collection.mutable +import scala.util.control.NonFatal case class Labelled[T](task: NamedTask[T], segments: Segments){ @@ -45,8 +42,8 @@ case class Evaluator(home: os.Path, val classLoaderSignHash = classLoaderSig.hashCode() def evaluate(goals: Agg[Task[_]], - reporter: Int => Option[ManagedLoggedReporter] = (int: Int) => Option.empty[ManagedLoggedReporter], - bspContext: BspContext = DummyBspContext, + reporter: Int => Option[BuildProblemReporter] = (int: Int) => Option.empty[BuildProblemReporter], + testReporter: TestReporter = DummyTestReporter, logger: Logger = log): Evaluator.Results = { os.makeDir.all(outPath) val (sortedGroups, transitive) = Evaluator.plan(rootModule, goals) @@ -74,7 +71,7 @@ case class Evaluator(home: os.Path, results, counterMsg, reporter, - bspContext, + testReporter, logger ) someTaskFailed = someTaskFailed || newResults.exists(task => !task._2.isInstanceOf[Success[_]]) @@ -121,8 +118,8 @@ case class Evaluator(home: os.Path, group: Agg[Task[_]], results: collection.Map[Task[_], Result[(Any, Int)]], counterMsg: String, - reporter: Int => Option[ManagedLoggedReporter], - bspContext: BspContext, + zincProblemReporter: Int => Option[BuildProblemReporter], + testReporter: TestReporter, logger: Logger ): (collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean) = { @@ -146,8 +143,8 @@ case class Evaluator(home: os.Path, paths = None, maybeTargetLabel = None, counterMsg = counterMsg, - reporter, - bspContext, + zincProblemReporter, + testReporter, logger ) (newResults, newEvaluated, false) @@ -201,8 +198,8 @@ case class Evaluator(home: os.Path, paths = Some(paths), maybeTargetLabel = Some(msgParts.mkString), counterMsg = counterMsg, - reporter, - bspContext, + zincProblemReporter, + testReporter, logger ) @@ -278,8 +275,8 @@ case class Evaluator(home: os.Path, paths: Option[Evaluator.Paths], maybeTargetLabel: Option[String], counterMsg: String, - reporter: Int => Option[ManagedLoggedReporter], - bspContext: BspContext, + reporter: Int => Option[BuildProblemReporter], + testReporter: TestReporter, logger: Logger): (mutable.LinkedHashMap[Task[_], Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { @@ -342,7 +339,7 @@ case class Evaluator(home: os.Path, home, env, reporter, - bspContext + testReporter ) val out = System.out diff --git a/scalajslib/src/ScalaJSModule.scala b/scalajslib/src/ScalaJSModule.scala index d970cc11..741ab29e 100644 --- a/scalajslib/src/ScalaJSModule.scala +++ b/scalajslib/src/ScalaJSModule.scala @@ -196,7 +196,7 @@ trait TestScalaJSModule extends ScalaJSModule with TestModule { runClasspath().map(_.path), Agg(compile().classes.path), args, - T.ctx.bsp + T.ctx.testReporter ) val res = TestModule.handleResults(doneMsg, results) // Hack to try and let the Node.js subprocess finish streaming it's stdout diff --git a/scalalib/api/src/ZincWorkerApi.scala b/scalalib/api/src/ZincWorkerApi.scala index e80895d6..dfda44b8 100644 --- a/scalalib/api/src/ZincWorkerApi.scala +++ b/scalalib/api/src/ZincWorkerApi.scala @@ -1,9 +1,10 @@ package mill.scalalib.api import mill.api.Loose.Agg -import mill.api.PathRef +import mill.api.{PathRef, BuildProblemReporter} import mill.api.JsonFormatters._ -import sbt.internal.inc._ + + object ZincWorkerApi{ type Ctx = mill.api.Ctx.Dest with mill.api.Ctx.Log with mill.api.Ctx.Home @@ -14,7 +15,7 @@ trait ZincWorkerApi { sources: Agg[os.Path], compileClasspath: Agg[os.Path], javacOptions: Seq[String], - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] /** Compile a mixed Scala/Java or Scala-only project */ @@ -27,7 +28,7 @@ trait ZincWorkerApi { scalacOptions: Seq[String], compilerClasspath: Agg[os.Path], scalacPluginClasspath: Agg[os.Path], - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] def discoverMainClasses(compilationResult: CompilationResult) diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index 5405828f..051f6804 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -13,9 +13,6 @@ import mill.modules.Jvm.{createAssembly, createJar} import Lib._ import mill.scalalib.publish.{Artifact, Scope} import mill.api.Loose.Agg -import sbt.internal.inc.ManagedLoggedReporter -import sbt.internal.util.{ConsoleOut, MainAppender} -import sbt.util.LogExchange /** * Core configuration required to compile a single Scala compilation target @@ -221,7 +218,7 @@ trait JavaModule extends mill.Module with TaskModule with GenIdeaModule { outer upstreamCompileOutput(), allSourceFiles().map(_.path), compileClasspath().map(_.path), - javacOptions() ++ T.ctx().bsp.args, + javacOptions(), T.ctx().reporter(hashCode) ) } @@ -613,7 +610,7 @@ trait TestModule extends JavaModule with TaskModule { runClasspath().map(_.path), Agg(compile().classes.path), args, - T.ctx().bsp + T.ctx().testReporter ) TestModule.handleResults(doneMsg, results) diff --git a/scalalib/src/ScalaModule.scala b/scalalib/src/ScalaModule.scala index 5669ca8c..daf4adcc 100644 --- a/scalalib/src/ScalaModule.scala +++ b/scalalib/src/ScalaModule.scala @@ -10,9 +10,6 @@ import mill.scalalib.api.Util.isDotty import Lib._ import mill.api.Loose.Agg import mill.api.DummyInputStream -import sbt.internal.inc.ManagedLoggedReporter -import sbt.internal.util.{ConsoleOut, MainAppender} -import sbt.util.LogExchange /** * Core configuration required to compile a single Scala compilation target @@ -140,7 +137,7 @@ trait ScalaModule extends JavaModule { outer => javacOptions(), scalaVersion(), scalaOrganization(), - scalacOptions() ++ T.ctx.bsp.args, + scalacOptions(), scalaCompilerClasspath().map(_.path), scalacPluginClasspath().map(_.path), T.ctx().reporter(hashCode) diff --git a/scalalib/src/TestRunner.scala b/scalalib/src/TestRunner.scala index 2e6c2ba9..8d69723f 100644 --- a/scalalib/src/TestRunner.scala +++ b/scalalib/src/TestRunner.scala @@ -1,7 +1,7 @@ package mill.scalalib import ammonite.util.Colors import mill.Agg -import mill.api.{DummyReporter, TestReporter} +import mill.api.{DummyTestReporter, TestReporter} import mill.modules.Jvm import mill.scalalib.Lib.discoverTests import mill.util.{Ctx, PrintLogger} @@ -48,7 +48,7 @@ object TestRunner { entireClasspath = Agg.from(classpath.map(os.Path(_))), testClassfilePath = Agg(os.Path(testCp)), args = arguments, - DummyReporter + DummyTestReporter )(ctx) // Clear interrupted state in case some badly-behaved test suite diff --git a/scalalib/worker/src/ZincWorkerImpl.scala b/scalalib/worker/src/ZincWorkerImpl.scala index a1d632df..0f2cbf10 100644 --- a/scalalib/worker/src/ZincWorkerImpl.scala +++ b/scalalib/worker/src/ZincWorkerImpl.scala @@ -3,16 +3,17 @@ package mill.scalalib.worker import java.io.File import java.util.Optional -import scala.ref.WeakReference import mill.api.Loose.Agg -import mill.api.{KeyedLockedCache, PathRef} -import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} +import mill.api.{Info, KeyedLockedCache, PathRef, Problem, ProblemPosition, Severity, Warn, BuildProblemReporter} import mill.scalalib.api.Util.{grepJar, isDotty, scalaBinaryVersion} +import mill.scalalib.api.{CompilationResult, ZincWorkerApi} import sbt.internal.inc._ import sbt.internal.util.{ConsoleOut, MainAppender} import sbt.util.LogExchange -import mill.scalalib.api.{CompilationResult, ZincWorkerApi} -import upickle.core.Visitor +import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} + +import scala.ref.WeakReference + case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup { override def analysis(classpathEntry: File): Optional[CompileAnalysis] = am(classpathEntry) @@ -21,6 +22,45 @@ case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClassp Locate.definesClass(classpathEntry) } +class ZincProblem(base: xsbti.Problem) extends Problem { + override def category: String = base.category() + + override def severity: Severity = base.severity() match { + case xsbti.Severity.Info => mill.api.Info + case xsbti.Severity.Warn => mill.api.Warn + case xsbti.Severity.Error => mill.api.Error + } + + override def message: String = base.message() + + override def position: ProblemPosition = new ZincProblemPosition(base.position()) +} + +class ZincProblemPosition(base: xsbti.Position) extends ProblemPosition { + + object JavaOptionConverter { + implicit def convertInt(x: Optional[Integer]): Option[Int] = if (x.isEmpty) None else Some(x.get().intValue()) + implicit def convert[T](x: Optional[T]): Option[T] = if (x.isEmpty) None else Some(x.get()) + } + + import JavaOptionConverter._ + + override def line: Option[Int] = base.line() + + override def lineContent: String = base.lineContent() + + override def offset: Option[Int] = base.offset() + + override def pointer: Option[Int] = base.pointer() + + override def pointerSpace: Option[String] = base.pointerSpace() + + override def sourcePath: Option[String] = base.sourcePath() + + override def sourceFile: Option[File] = base.sourceFile() +} + + class ZincWorkerImpl(compilerBridge: Either[ (ZincWorkerApi.Ctx, (String, String) => (Option[Array[os.Path]], os.Path)), String => os.Path @@ -156,7 +196,7 @@ class ZincWorkerImpl(compilerBridge: Either[ sources: Agg[os.Path], compileClasspath: Agg[os.Path], javacOptions: Seq[String], - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] = { for(res <- compileJava0( @@ -171,7 +211,7 @@ class ZincWorkerImpl(compilerBridge: Either[ sources: Agg[os.Path], compileClasspath: Agg[os.Path], javacOptions: Seq[String], - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[(os.Path, os.Path)] = { compileInternal( upstreamCompileOutput, @@ -193,7 +233,7 @@ class ZincWorkerImpl(compilerBridge: Either[ scalacOptions: Seq[String], compilerClasspath: Agg[os.Path], scalacPluginClasspath: Agg[os.Path], - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[CompilationResult] = { for (res <- compileMixed0( @@ -219,7 +259,7 @@ class ZincWorkerImpl(compilerBridge: Either[ scalacOptions: Seq[String], compilerClasspath: Agg[os.Path], scalacPluginClasspath: Agg[os.Path], - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[(os.Path, os.Path)] = { withCompilers( scalaVersion, @@ -307,7 +347,7 @@ class ZincWorkerImpl(compilerBridge: Either[ javacOptions: Seq[String], scalacOptions: Seq[String], compilers: Compilers, - reporter: Option[ManagedLoggedReporter]) + reporter: Option[BuildProblemReporter]) (implicit ctx: ZincWorkerApi.Ctx): mill.api.Result[(os.Path, os.Path)] = { os.makeDir.all(ctx.dest) @@ -323,7 +363,22 @@ class ZincWorkerImpl(compilerBridge: Either[ } val newReporter = reporter match { case None => new ManagedLoggedReporter(10, logger) - case r: Option[ManagedLoggedReporter] => r.get + case Some(r) => new ManagedLoggedReporter(10, logger) { + override def logError(problem: xsbti.Problem): Unit = { + r.logError(new ZincProblem(problem)) + super.logError(problem) + } + + override def logWarning(problem: xsbti.Problem): Unit = { + r.logWarning(new ZincProblem(problem)) + super.logWarning(problem) + } + + override def logInfo(problem: xsbti.Problem): Unit = { + r.logInfo(new ZincProblem(problem)) + super.logInfo(problem) + } + } } val analysisMap0 = upstreamCompileOutput.map(_.swap).toMap diff --git a/scalanativelib/src/ScalaNativeModule.scala b/scalanativelib/src/ScalaNativeModule.scala index 933e35fc..389d1b6a 100644 --- a/scalanativelib/src/ScalaNativeModule.scala +++ b/scalanativelib/src/ScalaNativeModule.scala @@ -189,7 +189,7 @@ trait TestScalaNativeModule extends ScalaNativeModule with TestModule { testOute runClasspath().map(_.path), Agg(compile().classes.path), args, - T.ctx.bsp + T.ctx.testReporter ) TestModule.handleResults(doneMsg, results) -- cgit v1.2.3 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 From 53671b9cdfc53c1edcba256b367e268be0b7357d Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Tue, 8 Oct 2019 12:07:25 +0200 Subject: Add short comments about build reporters. --- main/api/src/mill/api/BuildReporter.scala | 97 +++++++++++++++++++++++++++++++ main/api/src/mill/api/TestReporter.scala | 87 --------------------------- 2 files changed, 97 insertions(+), 87 deletions(-) create mode 100644 main/api/src/mill/api/BuildReporter.scala delete mode 100644 main/api/src/mill/api/TestReporter.scala diff --git a/main/api/src/mill/api/BuildReporter.scala b/main/api/src/mill/api/BuildReporter.scala new file mode 100644 index 00000000..f8405984 --- /dev/null +++ b/main/api/src/mill/api/BuildReporter.scala @@ -0,0 +1,97 @@ +package mill.api + +import java.io.File + +import sbt.testing._ + +/** + * Test reporter class that can be + * injected into the test task and + * report information upon the start + * and the finish of testing events + */ +trait TestReporter { + def logStart(event: Event): Unit + + def logFinish(event: Event): Unit + + +} + +/** + * Dummy Test Reporter that doesn't report + * anything for any testing event. + */ +object DummyTestReporter extends TestReporter { + override def logStart(event: Event): Unit = { + + } + override def logFinish(event: Event): Unit = { + + } +} + +/** + * A listener trait for getting notified about + * build output like compiler warnings and errors + */ +trait BuildProblemReporter { + def logError(problem: Problem): Unit + + def logWarning(problem: Problem): Unit + + def logInfo(problem: Problem): Unit + + def printSummary(): Unit +} + +/** + * Contains general information about the build problem + */ +trait Problem { + def category: String + + def severity: Severity + + def message: String + + def position: ProblemPosition +} + +/** + * Indicates the exact location (source file, line, column) of the build problem + */ +trait ProblemPosition { + def line: Option[Int] + + def lineContent: String + + def offset: Option[Int] + + def pointer: Option[Int] + + def pointerSpace: Option[String] + + def sourcePath: Option[String] + + def sourceFile: Option[File] + + def startOffset: Option[Int] = Option.empty + + def endOffset: Option[Int] = Option.empty + + def startLine: Option[Int] = Option.empty + + def startColumn: Option[Int] = Option.empty + + def endLine: Option[Int] = Option.empty + + def endColumn: Option[Int] = Option.empty +} + +sealed trait Severity +case object Info extends Severity +case object Error extends Severity +case object Warn extends Severity + + diff --git a/main/api/src/mill/api/TestReporter.scala b/main/api/src/mill/api/TestReporter.scala deleted file mode 100644 index 8adea687..00000000 --- a/main/api/src/mill/api/TestReporter.scala +++ /dev/null @@ -1,87 +0,0 @@ -package mill.api - -import java.io.File - -import sbt.testing._ - -/** - * Test reporter class that can be - * injected into the test task and - * report information upon the start - * and the finish of testing events - */ -trait TestReporter { - def logStart(event: Event): Unit - - def logFinish(event: Event): Unit - - -} - -/** - * Dummy Test Reporter that doesn't report - * anything for any testing event. - */ -object DummyTestReporter extends TestReporter { - override def logStart(event: Event): Unit = { - - } - override def logFinish(event: Event): Unit = { - - } -} - -trait BuildProblemReporter { - def logError(problem: Problem): Unit - - def logWarning(problem: Problem): Unit - - def logInfo(problem: Problem): Unit - - def printSummary(): Unit -} - -trait ProblemPosition { - def line: Option[Int] - - def lineContent: String - - def offset: Option[Int] - - def pointer: Option[Int] - - def pointerSpace: Option[String] - - def sourcePath: Option[String] - - def sourceFile: Option[File] - - def startOffset: Option[Int] = Option.empty - - def endOffset: Option[Int] = Option.empty - - def startLine: Option[Int] = Option.empty - - def startColumn: Option[Int] = Option.empty - - def endLine: Option[Int] = Option.empty - - def endColumn: Option[Int] = Option.empty -} - -sealed trait Severity -case object Info extends Severity -case object Error extends Severity -case object Warn extends Severity - -trait Problem { - def category: String - - def severity: Severity - - def message: String - - def position: ProblemPosition -} - - -- cgit v1.2.3 From ebdb22edadd30f963abc48a178f09895cee412e3 Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Wed, 9 Oct 2019 18:02:00 +0200 Subject: Remove some unnecessary dependencies from contrib.bsp, use upickle instead of play-json --- contrib/bsp/src/mill/contrib/BSP.scala | 46 +++++++++++----------- .../bsp/src/mill/contrib/bsp/MillBuildServer.scala | 12 ------ 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala index ab432743..0758faf4 100644 --- a/contrib/bsp/src/mill/contrib/BSP.scala +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -9,12 +9,22 @@ 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 +case class BspConfigJson(name: String, + argv: Seq[String], + version: String, + bspVersion: String, + languages: Seq[String]) + extends BspConnectionDetails(name, argv.asJava, version, bspVersion, languages.asJava) { +} + +object BspConfigJson { + implicit val rw: ReadWriter[BspConfigJson] = macroRW +} object BSP extends ExternalModule { @@ -44,12 +54,12 @@ object BSP extends ExternalModule { val bspDirectory = os.pwd / ".bsp" if (!os.exists(bspDirectory)) os.makeDir.all(bspDirectory) try { - os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) + os.write(bspDirectory / "mill.json", createBspConnectionJson()) } catch { case e: FileAlreadyExistsException => println("The bsp connection json file probably exists already - will be overwritten") os.remove(bspDirectory / "mill.json") - os.write(bspDirectory / "mill.json", Json.stringify(createBspConnectionJson())) + os.write(bspDirectory / "mill.json", createBspConnectionJson()) case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + " " + e.getStackTrace.toString) } @@ -57,27 +67,17 @@ object BSP extends ExternalModule { } // 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) - ) - } + def createBspConnectionJson(): String = { 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)) + write(BspConfigJson("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"), + version, + bspVersion, + languages)) } // computes the path to the java executable diff --git a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala index 95eb43cd..6db85c2c 100644 --- a/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala +++ b/contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala @@ -18,8 +18,6 @@ import mill.scalalib.api.CompilationResult import mill.util.{Ctx, DummyLogger} import mill.{scalalib, _} import os.Path -import sbt.internal.util.{ConsoleOut, MainAppender, ManagedLogger} -import sbt.util.LogExchange import scala.collection.JavaConverters._ @@ -573,14 +571,4 @@ class MillBuildServer(evaluator: Evaluator, } } - // 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 - } } -- cgit v1.2.3 From d0070ee49aca7c9e8e757448d47ee51643d7a037 Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Sat, 12 Oct 2019 14:14:22 +0200 Subject: Fix formatting issues --- contrib/bsp/src/mill/contrib/BSP.scala | 14 +++++++++----- contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala | 6 ++++-- contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala | 9 ++++----- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala index 0758faf4..5334f509 100644 --- a/contrib/bsp/src/mill/contrib/BSP.scala +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -60,8 +60,9 @@ object BSP extends ExternalModule { println("The bsp connection json file probably exists already - will be overwritten") os.remove(bspDirectory / "mill.json") os.write(bspDirectory / "mill.json", createBspConnectionJson()) - case e: Exception => println("An exception occurred while installing mill-bsp: " + e.getMessage + - " " + e.getStackTrace.toString) + case e: Exception => + println("An exception occurred while installing mill-bsp") + e.printStackTrace() } } @@ -70,11 +71,14 @@ object BSP extends ExternalModule { def createBspConnectionJson(): String = { val millPath = scala.sys.props("MILL_CLASSPATH") write(BspConfigJson("mill-bsp", - List(whichJava, "-DMILL_CLASSPATH=" + millPath, + List(whichJava, + "-DMILL_CLASSPATH=" + millPath, s"-DMILL_VERSION=${scala.sys.props("MILL_VERSION")}", - "-Djna.nosys=true", "-cp", + "-Djna.nosys=true", + "-cp", millPath, - "mill.MillMain", "mill.contrib.BSP/start"), + "mill.MillMain", + "mill.contrib.BSP/start"), version, bspVersion, languages)) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala index 3e65b648..d1b9a66b 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala @@ -65,7 +65,8 @@ class BspLoggedReporter(client: bsp.BuildClient, }) val params = new bsp.PublishDiagnosticsParams(textDocument, targetId, - appendDiagnostics(textDocument, diagnostic).asJava, + appendDiagnostics(textDocument, + diagnostic).asJava, true) if (originId.nonEmpty) { @@ -94,7 +95,8 @@ class BspLoggedReporter(client: bsp.BuildClient, println(i) val start = new bsp.Position( pos.startLine.orElse(pos.line).getOrElse[Int](0), - pos.startOffset.orElse(pos.offset).getOrElse[Int](0)) + pos.startOffset.orElse(pos.offset).getOrElse[Int](0) + ) val end = new bsp.Position( pos.endLine.orElse(pos.line).getOrElse[Int](start.getLine.intValue()), pos.endOffset.orElse(pos.offset).getOrElse[Int](start.getCharacter.intValue())) diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala index 922e26eb..b49c0b7c 100644 --- a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala +++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala @@ -21,11 +21,10 @@ import sbt.testing._ * in case special arguments need to be passed to * the compiler before running the test task. */ -class BspTestReporter( - client: BuildClient, - targetId: BuildTargetIdentifier, - taskId: TaskId, - arguments: Seq[String]) extends TestReporter { +class BspTestReporter(client: BuildClient, + targetId: BuildTargetIdentifier, + taskId: TaskId, + arguments: Seq[String]) extends TestReporter { var passed = 0 var failed = 0 -- cgit v1.2.3 From 5f6da18596a0544c0270af8cf11478024220eab2 Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Sat, 12 Oct 2019 15:23:18 +0200 Subject: Use named versions in bsp module def --- build.sc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/build.sc b/build.sc index 222de575..e1405d30 100755 --- a/build.sc +++ b/build.sc @@ -53,6 +53,7 @@ object Deps { val upickle = ivy"com.lihaoyi::upickle:0.7.1" val utest = ivy"com.lihaoyi::utest:0.6.4" val zinc = ivy"org.scala-sbt::zinc:1.2.5" + val bsp = ivy"ch.epfl.scala:bsp4j:2.0.0-M4" } trait MillPublishModule extends PublishModule{ @@ -440,6 +441,17 @@ object contrib extends MillModule { ) def testArgs = T(scalanativelib.testArgs()) } + + object bsp extends MillModule { + + def moduleDeps = Seq(scalalib, scalajslib, main, scalanativelib) + def ivyDeps = Agg( + Deps.bsp, + Deps.ujsonCirce, + Deps.sbtTestInterface + ) + } + } @@ -562,7 +574,7 @@ def launcherScript(shellJvmArgs: Seq[String], } object dev extends MillModule{ - def moduleDeps = Seq(scalalib, scalajslib, scalanativelib, contrib.scalapblib, contrib.tut, contrib.scoverage) + def moduleDeps = Seq(scalalib, scalajslib, scalanativelib, contrib.scalapblib, contrib.tut, contrib.scoverage, contrib.bsp) def forkArgs = -- cgit v1.2.3 From bf9171d1ba35f03aa86af1a000669695e9821325 Mon Sep 17 00:00:00 2001 From: Jiuyang liu Date: Sun, 13 Oct 2019 05:27:30 +0000 Subject: bug fix for system vended mill, no `MILL_CLASSPATH` avalible. --- contrib/bsp/src/mill/contrib/BSP.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala index 5334f509..d928f4c2 100644 --- a/contrib/bsp/src/mill/contrib/BSP.scala +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -69,7 +69,7 @@ object BSP extends ExternalModule { // creates a Json with the BSP connection details def createBspConnectionJson(): String = { - val millPath = scala.sys.props("MILL_CLASSPATH") + val millPath = scala.sys.props.get("MILL_CLASSPATH").getOrElse(scala.sys.process.Process("which mill").lineStream_!.mkString) write(BspConfigJson("mill-bsp", List(whichJava, "-DMILL_CLASSPATH=" + millPath, @@ -133,4 +133,4 @@ object BSP extends ExternalModule { executor.shutdown() } } -} \ No newline at end of file +} -- cgit v1.2.3 From 8a632462d12000834bea9b0bd4fdb623cf41af38 Mon Sep 17 00:00:00 2001 From: Jiuyang liu Date: Mon, 14 Oct 2019 02:33:39 +0000 Subject: get mill classpath and version from system env or java properties. --- contrib/bsp/src/mill/contrib/BSP.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala index d928f4c2..e3166f25 100644 --- a/contrib/bsp/src/mill/contrib/BSP.scala +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -69,11 +69,12 @@ object BSP extends ExternalModule { // creates a Json with the BSP connection details def createBspConnectionJson(): String = { - val millPath = scala.sys.props.get("MILL_CLASSPATH").getOrElse(scala.sys.process.Process("which mill").lineStream_!.mkString) + val millPath = scala.sys.props.get("MILL_CLASSPATH").getOrElse(System.getProperty("MILL_CLASSPATH")) + val millVersion = scala.sys.props.get("MILL_VERSION").getOrElse(System.getProperty("MILL_VERSION")) write(BspConfigJson("mill-bsp", List(whichJava, - "-DMILL_CLASSPATH=" + millPath, - s"-DMILL_VERSION=${scala.sys.props("MILL_VERSION")}", + s"-DMILL_CLASSPATH=$millPath", + s"-DMILL_VERSION=$millVersion", "-Djna.nosys=true", "-cp", millPath, -- cgit v1.2.3 From 76a8dfe705cafc18836df122d45c51452d4c6de3 Mon Sep 17 00:00:00 2001 From: Samvel Abrahamyan Date: Tue, 15 Oct 2019 14:24:08 +0200 Subject: Fix review issues --- build.sc | 1 - contrib/bsp/src/mill/contrib/BSP.scala | 6 +++--- main/api/src/mill/api/BuildReporter.scala | 2 +- main/core/src/eval/Evaluator.scala | 5 +++++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build.sc b/build.sc index e1405d30..bd06c10f 100755 --- a/build.sc +++ b/build.sc @@ -126,7 +126,6 @@ object main extends MillModule { } } object api extends MillApiModule{ - //def moduleDeps = Seq(core) def ivyDeps = Agg( Deps.osLib, Deps.upickle diff --git a/contrib/bsp/src/mill/contrib/BSP.scala b/contrib/bsp/src/mill/contrib/BSP.scala index e3166f25..f214ed9a 100644 --- a/contrib/bsp/src/mill/contrib/BSP.scala +++ b/contrib/bsp/src/mill/contrib/BSP.scala @@ -32,7 +32,7 @@ object BSP extends ExternalModule { lazy val millDiscover: Discover[BSP.this.type] = Discover[this.type] val version = "1.0.0" - val bspVersion = "2.0.0" + val bspProtocolVersion = "2.0.0" val languages = List("scala", "java") /** @@ -81,7 +81,7 @@ object BSP extends ExternalModule { "mill.MillMain", "mill.contrib.BSP/start"), version, - bspVersion, + bspProtocolVersion, languages)) } @@ -103,7 +103,7 @@ object BSP extends ExternalModule { 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) - val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspVersion, version, languages) + val millServer = new mill.contrib.bsp.MillBuildServer(eval, bspProtocolVersion, version, languages) val executor = Executors.newCachedThreadPool() val stdin = System.in diff --git a/main/api/src/mill/api/BuildReporter.scala b/main/api/src/mill/api/BuildReporter.scala index f8405984..2b360a45 100644 --- a/main/api/src/mill/api/BuildReporter.scala +++ b/main/api/src/mill/api/BuildReporter.scala @@ -2,7 +2,7 @@ package mill.api import java.io.File -import sbt.testing._ +import sbt.testing.Event /** * Test reporter class that can be diff --git a/main/core/src/eval/Evaluator.scala b/main/core/src/eval/Evaluator.scala index 37de69b3..1b58660b 100644 --- a/main/core/src/eval/Evaluator.scala +++ b/main/core/src/eval/Evaluator.scala @@ -41,6 +41,11 @@ case class Evaluator(home: os.Path, val classLoaderSignHash = classLoaderSig.hashCode() + /** + * @param goals The tasks that need to be evaluated + * @param reporter A function that will accept a module id and provide a listener for build problems in that module + * @param testReporter Listener for test events like start, finish with success/error + */ def evaluate(goals: Agg[Task[_]], reporter: Int => Option[BuildProblemReporter] = (int: Int) => Option.empty[BuildProblemReporter], testReporter: TestReporter = DummyTestReporter, -- cgit v1.2.3