Skip to content

Commit

Permalink
refactor cli
Browse files Browse the repository at this point in the history
- log produced lines when running
- also return lines for further processing if wanted
- can now send data to stdin
  • Loading branch information
oyvindberg committed Aug 6, 2022
1 parent 383a4e0 commit 9bc21bd
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 38 deletions.
4 changes: 2 additions & 2 deletions bleep-cli-test/src/scala/bleep/IntegrationSnapshotTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class IntegrationSnapshotTests extends SnapshotTest {

test("bloop") {
// todo: cache result of looking up existing sources in a json file as well
cli(List("git", "submodule", "init"), logger, "git submodule init")(inFolder / "bloop")
cli(List("git", "submodule", "update"), logger, "git submodule update")(inFolder / "bloop")
cli(action = "git submodule init", cwd = inFolder / "bloop", cmd = List("git", "submodule", "init"), logger = logger)
cli("git submodule update", inFolder / "bloop", List("git", "submodule", "update"), logger)
testIn("bloop")
}

Expand Down
4 changes: 2 additions & 2 deletions bleep-cli-test/src/scala/bleep/SbtOutputParserTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class SbtOutputParserTest extends AnyFunSuite with TripleEqualsSupport {
|[info] jvm
|[info] micro""".stripMargin

val result = Import.parseProjectsOutput(input.split("\n").toList)
val result = Import.parseProjectsOutput(input.split("\n"))

val expected = Map(
Path.of("/home/foo/bleep/snapshot-tests-in/bloop") -> List("backend", "benchmarkBridge", "benchmarks", "bloop", "bloop4j", "bloopShared"),
Expand All @@ -98,7 +98,7 @@ class SbtOutputParserTest extends AnyFunSuite with TripleEqualsSupport {
|[info] List(2.12.16)
|[info] ...""".stripMargin

val result = Import.ScalaVersionOutput.parse(output.split("\n").toList).combined.toMap
val result = Import.ScalaVersionOutput.parse(output.split("\n")).combined
val expected =
Map(model.VersionScala("2.13.8") -> Set("treesNative"), model.VersionScala("2.12.16") -> Set("treesNative2", "treesNative", "binaryJVMProjects"))
assert(result === expected)
Expand Down
46 changes: 31 additions & 15 deletions bleep-cli/src/scala/bleep/commands/Import.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import java.nio.file.{Files, Path}
import scala.collection.immutable.SortedMap
import scala.collection.mutable
import scala.jdk.CollectionConverters._
import scala.sys.process.Process

object Import {
case class Options(
Expand Down Expand Up @@ -49,7 +48,7 @@ object Import {
.toList
.asScala

def parseProjectsOutput(lines: List[String]): Map[Path, List[String]] = {
def parseProjectsOutput(lines: Array[String]): Map[Path, List[String]] = {
var currentPath = Option.empty[Path]
val projectNames = List.newBuilder[String]
val b = Map.newBuilder[Path, List[String]]
Expand Down Expand Up @@ -107,7 +106,7 @@ object Import {
}
}
object ScalaVersionOutput {
def parse(lines: List[String]): ScalaVersionOutput = {
def parse(lines: Array[String]): ScalaVersionOutput = {
val scalaVersionsBuilder = mutable.Map.empty[model.VersionScala, mutable.Set[String]]
val crossVersionsBuilder = mutable.Map.empty[model.VersionScala, mutable.Set[String]]

Expand Down Expand Up @@ -198,14 +197,16 @@ case class Import(

// run this as a command to discover all projects, possibly across several builds, possibly including non-aggregated projects
val allProjectNamesByBuild: Map[Path, List[String]] = {
val cmd = List("sbt", "projects")
logger.info("Calling sbt to discover projects...")
logger.debug(cmd)
val output = Process(cmd, sbtBuildDir.toFile, sbtEnvs: _*)
.lazyLines(logger.processLogger("sbt discover projects"))
.toList
val output = cli(
action = "sbt discover projects",
cwd = sbtBuildDir,
cmd = List("sbt", "projects"),
logger = logger,
env = sbtEnvs
)

val result = Import.parseProjectsOutput(output)
val result = Import.parseProjectsOutput(output.stdout)

result.foreach { case (buildDir, projects) =>
logger.info(s"Discovered ${projects.length} in $buildDir")
Expand All @@ -231,9 +232,17 @@ addSbtPlugin("build.bleep" % "sbt-export-dependencies" % "0.2.0")
val cmd = List("sbt", "show " + projectNames.map(p => s"$p/scalaVersion $p/crossScalaVersions").mkString(" "))
logger.withContext(sbtBuildDir).info("Calling sbt to discover cross projects...")
logger.withContext(sbtBuildDir).debug(cmd)
val output = Process(cmd, sbtBuildDir.toFile, sbtEnvs: _*).lazyLines(logger.processLogger("sbt discover cross projects")).toList

val result = Import.ScalaVersionOutput.parse(output)
val output =
cli(
"sbt discover cross projects",
sbtBuildDir,
cmd,
logger,
env = sbtEnvs
)

val result = Import.ScalaVersionOutput.parse(output.stdout)

result.combined
.foldLeft(logger) { case (logger, (scala, projects)) => logger.withContext(scala.scalaVersion, projects.size) }
Expand Down Expand Up @@ -262,13 +271,20 @@ addSbtPlugin("build.bleep" % "sbt-export-dependencies" % "0.2.0")
}

scalaVersionOutput.scalaVersions.flatMap { case (scalaVersion, projects) => argsFor(scalaVersion, projects, switchScalaVersion = false) } ++
scalaVersionOutput.crossVersions.flatMap { case (scalaVersion, projects) => argsFor(scalaVersion, projects, switchScalaVersion = true) }
scalaVersionOutput.crossVersions.flatMap { case (scalaVersion, projects) => argsFor(scalaVersion, projects, switchScalaVersion = true) } ++
List("exit")
}

val cmd = "sbt" :: args.toList
logger.withContext(sbtBuildDir).info("Calling sbt to export cross projects...")
logger.withContext(sbtBuildDir).debug(cmd)
cli(cmd, logger, "sbt", env = sbtEnvs)(sbtBuildDir)
logger.withContext(sbtBuildDir).debug(args)
cli(
action = "sbt",
cwd = sbtBuildDir,
cmd = List("sbt export"),
logger = logger,
stdIn = cli.StdIn.Provided(args.mkString("\n").getBytes),
env = sbtEnvs
)
} finally Files.delete(tempAddBloopPlugin)
}
}
Expand Down
67 changes: 67 additions & 0 deletions bleep-core/src/scala/bleep/cli.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package bleep

import bleep.logging.Logger
import sourcecode.{Enclosing, File, Line}

import java.nio.file.Path
import scala.sys.process.{BasicIO, Process, ProcessIO}

object cli {
sealed trait WrittenLine
object WrittenLine {
case class StdErr(line: String) extends WrittenLine
case class StdOut(line: String) extends WrittenLine
}

case class WrittenLines(combined: Array[WrittenLine]) {
def stdout: Array[String] = combined.collect { case WrittenLine.StdOut(line) => line }
def stderr: Array[String] = combined.collect { case WrittenLine.StdErr(line) => line }
}

sealed trait StdIn
object StdIn {
case object No extends StdIn
case object Attach extends StdIn
case class Provided(data: Array[Byte]) extends StdIn
}

def apply(
action: String,
cwd: Path,
cmd: List[String],
logger: Logger,
stdIn: StdIn = StdIn.No,
env: List[(String, String)] = Nil
)(implicit l: Line, f: File, e: Enclosing): WrittenLines = {
val builder = Process(cmd, cwd = Some(cwd.toFile), env: _*)
val output = Array.newBuilder[WrittenLine]
val actionPrefix = if (action.isEmpty) "" else s"$action: "

val processIO = new ProcessIO(
writeInput = os =>
stdIn match {
case StdIn.No => ()
case StdIn.Attach => BasicIO.connectToIn(os)
case StdIn.Provided(data) => os.write(data)
},
processOutput = BasicIO.processFully { line =>
output += WrittenLine.StdOut(line)
logger.info(actionPrefix + line)(implicitly, l, f, e)
},
processError = BasicIO.processFully { line =>
output += WrittenLine.StdErr(line)
logger.warn(actionPrefix + line)(implicitly, l, f, e)
},
daemonizeThreads = false
)

val exitCode = builder.run(processIO).exitValue()

exitCode match {
case 0 => WrittenLines(output.result())
case n =>
logger.debug(s"Failed command with error code $n: ${cmd.mkString(" ")}")
throw new BleepException.Text(s"Failed external command '$action'")
}
}
}
7 changes: 6 additions & 1 deletion bleep-core/src/scala/bleep/commands/Clean.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ case class Clean(started: Started, projects: List[model.CrossProjectName]) exten
}
} else {
// fast path
cli(List(List("rm", "-Rf"), outDirectories.map(_.toString)).flatten, started.logger, "clean files")(FileUtils.TempDir)
cli(
action = "clean files",
cwd = FileUtils.TempDir,
cmd = List(List("rm", "-Rf"), outDirectories.map(_.toString)).flatten,
logger = started.logger
)
outDirectories.foreach(directory => started.logger.info(s"Deleted $directory"))
}
}
Expand Down
16 changes: 0 additions & 16 deletions bleep-core/src/scala/bleep/package.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import bleep.logging.Logger

import java.nio.file.Path
import scala.sys.process.Process

package object bleep {

Expand All @@ -18,17 +15,4 @@ package object bleep {
case Right(relPath) => path / relPath
}
}

def cli(cmd: List[String], logger: Logger, action: String, attachInput: Boolean = false, env: List[(String, String)] = Nil)(implicit cwd: Path): Unit = {
val builder = Process(cmd, cwd = Some(cwd.toFile), env: _*)
val processLogger = logger.processLogger(action)
val p = if (attachInput) builder.!<(processLogger) else builder.!(processLogger)

p match {
case 0 => ()
case n =>
logger.debug(s"Failed command with error code $n: ${cmd.mkString(" ")}")
throw new BleepException.Text(s"Failed external command '$action'")
}
}
}
2 changes: 1 addition & 1 deletion liberated/mdoc
2 changes: 1 addition & 1 deletion liberated/sbt-native-image

0 comments on commit 9bc21bd

Please sign in to comment.