From ec0332a48c112255a0be0eaa3c1df815a1827f9c Mon Sep 17 00:00:00 2001 From: Joan Goyeau Date: Mon, 26 Oct 2020 12:25:20 -0700 Subject: [PATCH] Handle upcoming mill embedded BSP and fix mill BSP install --- .../importing/BspProjectResolver.scala | 4 ++ .../importing/MillProjectImportProvider.scala | 40 +++++++++++++++ .../project/importing/bspConfigSteps.scala | 11 ++-- .../bsp/project/importing/projectImport.scala | 5 +- .../importing/setup/MillConfigSetup.scala | 51 ------------------- .../bsp/protocol/BspCommunication.scala | 2 +- .../org/jetbrains/bsp/settings/settings.scala | 6 ++- .../project/MillProjectImportProvider.scala | 27 ---------- 8 files changed, 59 insertions(+), 87 deletions(-) create mode 100644 bsp/src/org/jetbrains/bsp/project/importing/MillProjectImportProvider.scala delete mode 100644 bsp/src/org/jetbrains/bsp/project/importing/setup/MillConfigSetup.scala delete mode 100644 scala/scala-impl/src/org/jetbrains/sbt/project/MillProjectImportProvider.scala diff --git a/bsp/src/org/jetbrains/bsp/project/importing/BspProjectResolver.scala b/bsp/src/org/jetbrains/bsp/project/importing/BspProjectResolver.scala index 4e7f076ab51..bceab8ab9ca 100644 --- a/bsp/src/org/jetbrains/bsp/project/importing/BspProjectResolver.scala +++ b/bsp/src/org/jetbrains/bsp/project/importing/BspProjectResolver.scala @@ -171,9 +171,13 @@ class BspProjectResolver extends ExternalSystemProjectResolver[BspExecutionSetti case BspProjectSettings.AutoPreImport => if (executionSettings.config == AutoConfig && bloopConfigDir(workspace).isDefined && isSbtProject) runBloopInstall(workspace) + else if (MillProjectImportProvider.canImport(workspace)) + MillProjectImportProvider.bspInstall(workspace) else emptySuccess case BspProjectSettings.BloopSbtPreImport => runBloopInstall(workspace) + case BspProjectSettings.MillBspPreImport => + MillProjectImportProvider.bspInstall(workspace) } } else emptySuccess } diff --git a/bsp/src/org/jetbrains/bsp/project/importing/MillProjectImportProvider.scala b/bsp/src/org/jetbrains/bsp/project/importing/MillProjectImportProvider.scala new file mode 100644 index 00000000000..aa19fd82d0a --- /dev/null +++ b/bsp/src/org/jetbrains/bsp/project/importing/MillProjectImportProvider.scala @@ -0,0 +1,40 @@ +package org.jetbrains.bsp.project.importing + +import java.io.File +import org.jetbrains.plugins.scala.build.BuildMessages +import scala.io.Source +import scala.sys.process._ +import scala.util.{Failure, Try, Using} + +object MillProjectImportProvider { + def canImport(workspace: File): Boolean = + Option(workspace) match { + case Some(directory) if directory.isDirectory => isBspCompatible(directory) || isLegacyBspCompatible(directory) + case _ => false + } + + def bspInstall(workspace: File): Try[BuildMessages] = { + val work = + if (isBspCompatible(workspace)) Try(Process("./mill mill.bsp.BSP/install", workspace).!!) + else if (isLegacyBspCompatible(workspace)) Try(Process("./mill -i mill.contrib.BSP/install", workspace).!!) + else Failure(new IllegalStateException("Unable to install BSP as this is not a Mill project")) + + work.transform( + _ => Try(BuildMessages.empty.status(BuildMessages.OK)), + e => Try(BuildMessages.empty.status(BuildMessages.Error).addError(e.getMessage)) + ) + } + + private def isBspCompatible(workspace: File) = workspace.listFiles().exists { buildScript => + !buildScript.isDirectory && buildScript.getName == "mill" && + Using.resource(Source.fromFile(buildScript))(_.getLines().exists(!_.matches("""^.*(0\.8\.0|0\.7.+|0\.6.+)$"""))) + } + + // Legacy Mill =< 0.8.0 + private def isLegacyBspCompatible(workspace: File) = workspace.listFiles().exists { buildScript => + !buildScript.isDirectory && buildScript.getName == "build.sc" && + Using.resource(Source.fromFile(buildScript))( + _.getLines().contains("import $ivy.`com.lihaoyi::mill-contrib-bsp:$MILL_VERSION`") + ) + } +} diff --git a/bsp/src/org/jetbrains/bsp/project/importing/bspConfigSteps.scala b/bsp/src/org/jetbrains/bsp/project/importing/bspConfigSteps.scala index 2b63c3c1f42..9f28adb7940 100644 --- a/bsp/src/org/jetbrains/bsp/project/importing/bspConfigSteps.scala +++ b/bsp/src/org/jetbrains/bsp/project/importing/bspConfigSteps.scala @@ -16,13 +16,13 @@ import org.jetbrains.annotations.Nls import org.jetbrains.bsp.{BspBundle, BspUtil} import org.jetbrains.bsp.project.importing.BspSetupConfigStep.ConfigSetupTask import org.jetbrains.bsp.project.importing.bspConfigSteps._ -import org.jetbrains.bsp.project.importing.setup.{BspConfigSetup, FastpassConfigSetup, MillConfigSetup, NoConfigSetup, SbtConfigSetup} +import org.jetbrains.bsp.project.importing.setup.{BspConfigSetup, FastpassConfigSetup, NoConfigSetup, SbtConfigSetup} import org.jetbrains.bsp.protocol.BspConnectionConfig import org.jetbrains.bsp.settings.BspProjectSettings._ import org.jetbrains.plugins.scala.build.IndicatorReporter import org.jetbrains.plugins.scala.project.Version import org.jetbrains.sbt.SbtUtil._ -import org.jetbrains.sbt.project.{MillProjectImportProvider, SbtProjectImportProvider} +import org.jetbrains.sbt.project.SbtProjectImportProvider object bspConfigSteps { @@ -104,8 +104,9 @@ object bspConfigSteps { // server config to be set in next step SbtConfigSetup(workspace) case bspConfigSteps.MillSetup => - builder.setPreImportConfig(NoPreImport) - MillConfigSetup(workspace) + builder.setPreImportConfig(MillBspPreImport) + builder.setServerConfig(AutoConfig) + NoConfigSetup case bspConfigSteps.FastpassSetup => builder.setPreImportConfig(NoPreImport) val bspWorkspace = FastpassConfigSetup.computeBspWorkspace(workspace) @@ -129,7 +130,7 @@ object bspConfigSteps { } else Nil val millChoice = - if (MillProjectImportProvider.canImport(vfile)) List(MillSetup) + if (MillProjectImportProvider.canImport(vfile.toNioPath.toFile)) List(MillSetup) else Nil val bloopChoice = diff --git a/bsp/src/org/jetbrains/bsp/project/importing/projectImport.scala b/bsp/src/org/jetbrains/bsp/project/importing/projectImport.scala index 73974ff9e86..11811330496 100644 --- a/bsp/src/org/jetbrains/bsp/project/importing/projectImport.scala +++ b/bsp/src/org/jetbrains/bsp/project/importing/projectImport.scala @@ -31,7 +31,7 @@ import org.jetbrains.bsp._ import org.jetbrains.bsp.protocol.BspConnectionConfig import org.jetbrains.bsp.settings.BspProjectSettings._ import org.jetbrains.bsp.settings._ -import org.jetbrains.sbt.project.{MillProjectImportProvider, SbtProjectImportProvider} +import org.jetbrains.sbt.project.SbtProjectImportProvider class BspProjectImportBuilder extends AbstractExternalProjectImportBuilder[BspImportControl]( @@ -251,7 +251,8 @@ object BspProjectOpenProcessor { // val sbtProject = SbtProjectImportProvider.canImport(workspace) // temporarily disable sbt importing via bloop from welcome screen (SCL-17359) val sbtProject = false - val millProject = MillProjectImportProvider.canImport(workspace) + + val millProject = MillProjectImportProvider.canImport(workspace.toNioPath.toFile) bspConnectionProtocolSupported || bloopProject || bspConnectionProtocolSupported || sbtProject || millProject } diff --git a/bsp/src/org/jetbrains/bsp/project/importing/setup/MillConfigSetup.scala b/bsp/src/org/jetbrains/bsp/project/importing/setup/MillConfigSetup.scala deleted file mode 100644 index 93a4a87c0ad..00000000000 --- a/bsp/src/org/jetbrains/bsp/project/importing/setup/MillConfigSetup.scala +++ /dev/null @@ -1,51 +0,0 @@ -package org.jetbrains.bsp.project.importing.setup - -import java.io.File -import java.util.concurrent.atomic.AtomicBoolean - -import org.jetbrains.plugins.scala.build.{BuildMessages, BuildReporter} - -import scala.jdk.CollectionConverters._ -import scala.concurrent.duration._ -import scala.util.{Failure, Success, Try} - -object MillConfigSetup { - private val MillProcessCheckTimeout = 100.millis - - def apply(baseDir: File): MillConfigSetup = { - val processBuilder = new ProcessBuilder(Seq("./mill", "-i", "mill.contrib.BSP/install").asJava) - processBuilder.directory(baseDir) - new MillConfigSetup(processBuilder) - } -} - -class MillConfigSetup(processBuilder: ProcessBuilder) extends BspConfigSetup { - import MillConfigSetup._ - - private val cancellationFlag: AtomicBoolean = new AtomicBoolean(false) - - def cancel(): Unit = cancellationFlag.set(true) - - private def waitFinish(process: Process): Option[BuildMessages] = - Iterator.continually(process.waitFor(MillProcessCheckTimeout.length, MillProcessCheckTimeout.unit)) - .map { end => - if (! end && cancellationFlag.get()) { - process.destroy() - true - } else end - } - .find(identity) - .map(_ => BuildMessages.empty.status(BuildMessages.OK)) - - override def run(implicit reporter: BuildReporter): Try[BuildMessages] = { - // TODO send process output to reporter / messages - reporter.start() - val process = processBuilder.start() - val result = Try(waitFinish(process).get) - result match { - case Failure(err) => reporter.finishWithFailure(err) - case Success(bm) => reporter.finish(bm) - } - result - } -} diff --git a/bsp/src/org/jetbrains/bsp/protocol/BspCommunication.scala b/bsp/src/org/jetbrains/bsp/protocol/BspCommunication.scala index 65235396a75..c253ca81d2b 100644 --- a/bsp/src/org/jetbrains/bsp/protocol/BspCommunication.scala +++ b/bsp/src/org/jetbrains/bsp/protocol/BspCommunication.scala @@ -34,7 +34,7 @@ class BspCommunication private[protocol](base: File, config: BspServerConfig) ex private val session: AtomicReference[Option[BspSession]] = new AtomicReference[Option[BspSession]](None) - val exitCommands: List[List[String]] = { + lazy val exitCommands: List[List[String]] = { val workspace = new File(base.getAbsolutePath) val files = BspConnectionConfig.workspaceBspConfigs(workspace) val argvExitCommands = files.flatMap { file => diff --git a/bsp/src/org/jetbrains/bsp/settings/settings.scala b/bsp/src/org/jetbrains/bsp/settings/settings.scala index f94b85d4190..9ecb2f97eb5 100644 --- a/bsp/src/org/jetbrains/bsp/settings/settings.scala +++ b/bsp/src/org/jetbrains/bsp/settings/settings.scala @@ -71,6 +71,8 @@ object BspProjectSettings { case object AutoPreImport extends PreImportConfig /** Preimport with Bloop from sbt project */ case object BloopSbtPreImport extends PreImportConfig + /** Preimport with BSP from Mill project */ + case object MillBspPreImport extends PreImportConfig class PreImportConfigConverter extends Converter[PreImportConfig] { override def fromString(value: String): PreImportConfig = @@ -78,6 +80,7 @@ object BspProjectSettings { case "NoPreImport" => NoPreImport case "AutoPreImport" => AutoPreImport case "BloopBspPreImport" => BloopSbtPreImport + case "MillBspPreImport" => MillBspPreImport } override def toString(value: PreImportConfig): String = @@ -85,6 +88,7 @@ object BspProjectSettings { case NoPreImport => "NoPreImport" case AutoPreImport => "AutoPreImport" case BloopSbtPreImport => "BloopBspPreImport" + case MillBspPreImport => "MillBspPreImport" } } @@ -101,7 +105,7 @@ object BspProjectSettings { override def toString(value: BspServerConfig): String = value match { case AutoConfig => "AutoConfig" - case BloopConfig =>"BloopConfig" + case BloopConfig => "BloopConfig" case BspConfigFile(path) => s"BspConfigFile:$path" } } diff --git a/scala/scala-impl/src/org/jetbrains/sbt/project/MillProjectImportProvider.scala b/scala/scala-impl/src/org/jetbrains/sbt/project/MillProjectImportProvider.scala deleted file mode 100644 index 2fc6e1c2310..00000000000 --- a/scala/scala-impl/src/org/jetbrains/sbt/project/MillProjectImportProvider.scala +++ /dev/null @@ -1,27 +0,0 @@ -package org.jetbrains.sbt.project - -import com.intellij.openapi.vfs.VirtualFile -import java.io.File -import scala.io.Source - -object MillProjectImportProvider { - private val buildFile = "build.sc" - - def canImport(vFile: VirtualFile): Boolean = vFile match { - case null => false - case directory if directory.isDirectory => - val buildWithBspPlugin = directory.getChildren.exists { vBuildScript => - vBuildScript.getName == "build.sc" && - Source.fromInputStream(vBuildScript.getInputStream) - .getLines() - .contains("import $ivy.`com.lihaoyi::mill-contrib-bsp:$MILL_VERSION`") - } - val bootstrap = directory.getChildren.exists { child => - val file = new File(child.getPath) - file.getName == "mill" && file.canExecute - } - - buildWithBspPlugin && bootstrap - case file => file.getName == buildFile - } -}