diff options
author | Alexandra Dima <alexandra.dima@jetbrains.com> | 2019-06-21 18:39:34 +0200 |
---|---|---|
committer | Samvel Abrahamyan <samvel1024@gmail.com> | 2019-10-12 14:32:36 +0200 |
commit | 91bfa49804673bc337be002a537cb5d41b0c74ce (patch) | |
tree | b6311674dc92c84d34c1a857e5aaea1a3f91c650 | |
parent | a6adf55cf37ff6aa05dd111a13595b7bd1b6cc92 (diff) | |
download | mill-91bfa49804673bc337be002a537cb5d41b0c74ce.tar.gz mill-91bfa49804673bc337be002a537cb5d41b0c74ce.tar.bz2 mill-91bfa49804673bc337be002a537cb5d41b0c74ce.zip |
Started integrating mill with Build Server Protocol
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | contrib/bsp/.bsp/mill-bsp.json | 1 | ||||
-rw-r--r-- | contrib/bsp/src/mill/contrib/MainMillBuildServer.scala | 216 | ||||
-rw-r--r-- | contrib/bsp/src/mill/contrib/bsp/MillBuildServer.scala | 303 | ||||
-rw-r--r-- | contrib/bsp/src/mill/contrib/bsp/ModuleUtils.scala | 129 |
5 files changed, 652 insertions, 0 deletions
@@ -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 + } +} |