Skip to content

sbt-dotty: Make launchIDE work offline #5133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import xerial.sbt.pack.PackPlugin
import xerial.sbt.pack.PackPlugin.autoImport._

import dotty.tools.sbtplugin.DottyPlugin.autoImport._
import dotty.tools.sbtplugin.DottyIDEPlugin.{ prepareCommand, runProcess }
import dotty.tools.sbtplugin.DottyIDEPlugin.{ installCodeExtension, prepareCommand, runProcess }
import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._

import sbtbuildinfo.BuildInfoPlugin
Expand Down Expand Up @@ -56,7 +56,7 @@ object Build {

val sbtDottyName = "sbt-dotty"
val sbtDottyVersion = {
val base = "0.2.4"
val base = "0.2.5"
if (isRelease) base else base + "-SNAPSHOT"
}

Expand Down Expand Up @@ -988,27 +988,27 @@ object Build {
val coursier = workingDir / "out" / "coursier"
val packageJson = workingDir / "package.json"
if (!coursier.exists || packageJson.lastModified > coursier.lastModified)
runProcess(Seq("npm", "install"), wait = true, directory = workingDir)
runProcess(Seq("npm", "install"), wait = true, directory = Some(workingDir))
val tsc = workingDir / "node_modules" / ".bin" / "tsc"
runProcess(Seq(tsc.getAbsolutePath, "--pretty", "--project", workingDir.getAbsolutePath), wait = true)

// Currently, vscode-dotty depends on daltonjorge.scala for syntax highlighting,
// this is not automatically installed when starting the extension in development mode
// (--extensionDevelopmentPath=...)
runProcess(codeCommand.value ++ Seq("--install-extension", "daltonjorge.scala"), wait = true)
installCodeExtension(codeCommand.value, "daltonjorge.scala")

sbt.internal.inc.Analysis.Empty
}.dependsOn(managedResources in Compile).value,
sbt.Keys.`package`:= {
runProcess(Seq("vsce", "package"), wait = true, directory = baseDirectory.value)
runProcess(Seq("vsce", "package"), wait = true, directory = Some(baseDirectory.value))

baseDirectory.value / s"dotty-${version.value}.vsix"
},
unpublish := {
runProcess(Seq("vsce", "unpublish"), wait = true, directory = baseDirectory.value)
runProcess(Seq("vsce", "unpublish"), wait = true, directory = Some(baseDirectory.value))
},
publish := {
runProcess(Seq("vsce", "publish"), wait = true, directory = baseDirectory.value)
runProcess(Seq("vsce", "publish"), wait = true, directory = Some(baseDirectory.value))
},
run := Def.inputTask {
val inputArgs = spaceDelimited("<arg>").parsed
Expand Down
67 changes: 56 additions & 11 deletions sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sbt.Def.Initialize
import sbt.Keys._
import java.io._
import java.lang.ProcessBuilder
import java.lang.ProcessBuilder.Redirect
import scala.collection.mutable
import scala.util.Properties.{ isWin, isMac }

Expand Down Expand Up @@ -146,23 +147,66 @@ object DottyIDEPlugin extends AutoPlugin {
if (isWin) Seq("cmd.exe", "/C") ++ cmd
else cmd

/** Run `cmd`.
* @param wait If true, wait for `cmd` to return and throw an exception if the exit code is non-zero.
* @param directory If not null, run `cmd` in this directory.
/** Run the command `cmd`.
*
* @param wait If true, wait for the command to return and throw an exception if the exit code is non-zero.
* @param directory If not None, run the command in this directory.
* @param outputCallback If not None, pass the command output to this callback instead of writing it to stdout.
*/
def runProcess(cmd: Seq[String], wait: Boolean = false, directory: File = null): Unit = {
val pb = new ProcessBuilder(prepareCommand(cmd): _*).inheritIO()
if (directory != null) pb.directory(directory)
def runProcess(cmd: Seq[String], wait: Boolean = false, directory: Option[File] = None, outputCallback: Option[BufferedReader => Unit] = None): Unit = {
val pb = new ProcessBuilder(prepareCommand(cmd): _*)

directory match {
case Some(dir) =>
pb.directory(dir)
case None =>
}

pb.redirectInput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT)
.redirectOutput(
outputCallback match {
case Some(_) =>
Redirect.PIPE
case None =>
Redirect.INHERIT
})

val process = pb.start()
outputCallback match {
case Some(callback) =>
callback(new BufferedReader(new InputStreamReader(process.getInputStream)))
case None =>
}
if (wait) {
val exitCode = pb.start().waitFor()
val exitCode = process.waitFor()
if (exitCode != 0) {
val cmdString = cmd.mkString(" ")
val description = if (directory != null) s""" in directory "$directory"""" else ""
throw new MessageOnlyException(s"""Running command "${cmdString}"${description} failed.""")
}
}
else
pb.start()
}

/** Install or upgrade Code extension `name`.
*
* We start by trying to install or upgrade the extension. If this fails we
* check if an existing version of the extension exists. If this also fails
* we throw an exception. This ensures that we're always running the latest
* version of the extension but that we can still work offline.
*/
def installCodeExtension(codeCmd: Seq[String], name: String): Unit = {
try {
runProcess(codeCmd ++ Seq("--install-extension", name), wait = true)
} catch {
case e: Exception =>
var alreadyInstalled: Boolean = false
runProcess(codeCmd ++ Seq("--list-extensions"), wait = true, outputCallback = Some({ br =>
alreadyInstalled = br.lines.filter(_ == name).findFirst.isPresent
}))
if (!alreadyInstalled)
throw e
}
}

private val projectConfig = taskKey[Option[ProjectConfig]]("")
Expand Down Expand Up @@ -270,8 +314,9 @@ object DottyIDEPlugin extends AutoPlugin {

runCode := {
try {
runProcess(codeCommand.value ++ Seq("--install-extension", "lampepfl.dotty"), wait = true)
runProcess(codeCommand.value ++ Seq("."), directory = baseDirectory.value)
installCodeExtension(codeCommand.value, "lampepfl.dotty")

runProcess(codeCommand.value ++ Seq("."), directory = Some(baseDirectory.value))
} catch {
case ioex: IOException if ioex.getMessage.startsWith("""Cannot run program "code"""") =>
val log = streams.value.log
Expand Down