Skip to content
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

Send the Bloop server output to a file when possible #853

Merged
merged 5 commits into from
Apr 14, 2022
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
35 changes: 18 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,6 @@ concurrency:
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
migration-tests:
timeout-minutes: 120
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- uses: coursier/cache-action@v6.3
- uses: VirtusLab/scala-cli-setup@267af2f1ed4911180b4bb25619ca4a586753cbd1
with:
jvm: "temurin:17"
- name: Unit 3.x tests
run: |
./mill -i cli3.compile
./mill -i cli3.test
./mill -i _[3.1.1].test
jvm-tests:
timeout-minutes: 120
runs-on: ${{ matrix.OS }}
Expand Down Expand Up @@ -203,6 +186,24 @@ jobs:
- name: Check examples
run: bash ./scala-cli --jvm temurin:17 .github/scripts/check_examples.sc

migration-tests:
timeout-minutes: 120
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- uses: coursier/cache-action@v6.3
- uses: VirtusLab/scala-cli-setup@267af2f1ed4911180b4bb25619ca4a586753cbd1
with:
jvm: "temurin:17"
- name: Unit 3.x tests
run: |
./mill -i cli3.compile
./mill -i cli3.test
./mill -i _[3.1.1].test

checks:
timeout-minutes: 15
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,25 @@ object BloopRifle {
): Future[Unit] =
config.classPath(version) match {
case Left(ex) => Future.failed(new Exception("Error getting Bloop class path", ex))
case Right(cp) =>
case Right((cp, isScalaCliBloop)) =>
object IntValue {
def unapply(s: String): Option[Int] =
// no String.toIntOption in Scala 2.12.x
try Some(s.toInt)
catch {
case _: NumberFormatException => None
}
}
val bloopServerSupportsFileTruncating =
isScalaCliBloop && {
version.takeWhile(c => c.isDigit || c == '.').split('.') match {
case Array(IntValue(maj), IntValue(min), IntValue(patch)) =>
import scala.math.Ordering.Implicits._
Seq(maj, min, patch) >= Seq(1, 14, 20)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we do need to check the bloop version if we control the version of bloop ourselves? I think if we see too old version running (e.g. after update of scala-cli) we should restart it with newer version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's for if users manually force an earlier version to something else. Earlier Bloop versions can't truncate their output file, so redirecting the output this way would create a file that might grow too big.

case _ =>
false
}
}
Operations.startServer(
config.address,
bloopJava,
Expand All @@ -57,7 +75,8 @@ object BloopRifle {
scheduler,
config.startCheckPeriod,
config.startCheckTimeout,
logger
logger,
bloopServerSupportsFileTruncating = bloopServerSupportsFileTruncating
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final case class BloopRifleConfig(
address: BloopRifleConfig.Address,
javaPath: String,
javaOpts: Seq[String],
classPath: String => Either[Throwable, Seq[File]],
classPath: String => Either[Throwable, (Seq[File], Boolean)],
workingDir: File,
bspSocketOrPort: Option[() => BspConnectionAddress],
bspStdin: Option[InputStream],
Expand All @@ -38,6 +38,8 @@ object BloopRifleConfig {
}
final case class DomainSocket(path: Path) extends Address {
def render = path.toString
def outputPath: Path =
path.resolve("output")
}
}

Expand Down Expand Up @@ -87,8 +89,9 @@ object BloopRifleConfig {
.getOrElse(hardCodedDefaultJavaOpts)
}

def scalaCliBloopOrg = "io.github.alexarchambault.bleep"
def hardCodedDefaultModule: String =
"io.github.alexarchambault.bleep:bloop-frontend_2.12"
s"$scalaCliBloopOrg:bloop-frontend_2.12"
def hardCodedDefaultVersion: String =
Constants.bloopVersion
def hardCodedDefaultScalaVersion: String =
Expand Down Expand Up @@ -118,7 +121,7 @@ object BloopRifleConfig {

def default(
address: Address,
bloopClassPath: String => Either[Throwable, Seq[File]],
bloopClassPath: String => Either[Throwable, (Seq[File], Boolean)],
workingDir: File
): BloopRifleConfig =
BloopRifleConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,28 @@ object Operations {
scheduler: ScheduledExecutorService,
waitInterval: FiniteDuration,
timeout: Duration,
logger: BloopRifleLogger
logger: BloopRifleLogger,
bloopServerSupportsFileTruncating: Boolean
): Future[Unit] = {

val (addressArgs, mainClass) = address match {
val (addressArgs, mainClass, writeOutputToOpt) = address match {
case BloopRifleConfig.Address.Tcp(host, port) =>
(Seq(host, port.toString), "bloop.Server")
case BloopRifleConfig.Address.DomainSocket(path) =>
(Seq(s"daemon:$path"), "bloop.Bloop")
(Seq(host, port.toString), "bloop.Server", None)
case s: BloopRifleConfig.Address.DomainSocket =>
val writeOutputToOpt0 =
if (bloopServerSupportsFileTruncating) Some(s.outputPath)
else None
(Seq(s"daemon:${s.path}"), "bloop.Bloop", writeOutputToOpt0)
}

val extraJavaOpts =
writeOutputToOpt.toSeq.map { writeOutputTo =>
s"-Dbloop.truncate-output-file-periodically=${writeOutputTo.toAbsolutePath}"
}

val command =
Seq(javaPath) ++
extraJavaOpts ++
javaOpts ++
Seq(
"-cp",
Expand All @@ -117,15 +128,25 @@ object Operations {
b.directory(workingDir)
b.redirectInput(ProcessBuilder.Redirect.PIPE)

b.redirectOutput {
if (logger.bloopCliInheritStdout) ProcessBuilder.Redirect.INHERIT
else ProcessBuilder.Redirect.DISCARD
}
if (logger.bloopCliInheritStdout)
b.redirectOutput(ProcessBuilder.Redirect.INHERIT)
else
writeOutputToOpt match {
case Some(writeOutputTo) =>
b.redirectOutput(writeOutputTo.toFile)
case None =>
b.redirectOutput(ProcessBuilder.Redirect.DISCARD)
}

b.redirectError {
if (logger.bloopCliInheritStderr) ProcessBuilder.Redirect.INHERIT
else ProcessBuilder.Redirect.DISCARD
}
if (logger.bloopCliInheritStderr)
b.redirectError(ProcessBuilder.Redirect.INHERIT)
else
writeOutputToOpt match {
case Some(writeOutputTo) =>
b.redirectError(writeOutputTo.toFile)
case None =>
b.redirectError(ProcessBuilder.Redirect.DISCARD)
}

val p = b.start()
p.getOutputStream.close()
Expand Down
19 changes: 12 additions & 7 deletions modules/build/src/main/scala/scala/build/Bloop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,28 @@ object Bloop {
res.map(_._2.toIO)
}

def bloopClassPath(logger: Logger, cache: FileCache[Task]): Either[BuildException, Seq[File]] =
def bloopClassPath(
logger: Logger,
cache: FileCache[Task]
): Either[BuildException, (Seq[File], Boolean)] =
bloopClassPath(logger, cache, BloopRifleConfig.defaultVersion)

def bloopClassPath(
logger: Logger,
cache: FileCache[Task],
bloopVersion: String
): Either[BuildException, Seq[File]] = either {
): Either[BuildException, (Seq[File], Boolean)] = either {
val moduleStr = BloopRifleConfig.defaultModule
val mod = value {
ModuleParser.parse(moduleStr)
.left.map(err => new ModuleFormatError(moduleStr, err, Some("Bloop")))
}
val dep = DependencyLike(mod, bloopVersion)
val sv = BloopRifleConfig.defaultScalaVersion
val sbv = ScalaVersion.binary(sv)
val params = ScalaParameters(sv, sbv)
value(bloopClassPath(dep, params, logger, cache))
val dep = DependencyLike(mod, bloopVersion)
val sv = BloopRifleConfig.defaultScalaVersion
val sbv = ScalaVersion.binary(sv)
val params = ScalaParameters(sv, sbv)
val cp = value(bloopClassPath(dep, params, logger, cache))
val isScalaCliBloop = moduleStr.startsWith(BloopRifleConfig.scalaCliBloopOrg + ":")
(cp, isScalaCliBloop)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import coursier.cache.FileCache

import scala.build.{Bloop, Logger}
import scala.build.blooprifle.BloopRifleConfig
import scala.util.Properties

object BloopServer {

Expand All @@ -14,10 +13,7 @@ object BloopServer {
// Not sure how to properly shut it down or have it exit after a period
// of inactivity, so we keep using our default global Bloop for now.
private def bloopAddress =
if (Properties.isWin)
BloopRifleConfig.Address.Tcp(BloopRifleConfig.defaultHost, BloopRifleConfig.defaultPort)
else
BloopRifleConfig.Address.DomainSocket(directories.bloopDaemonDir.toNIO)
BloopRifleConfig.Address.DomainSocket(directories.bloopDaemonDir.toNIO)

val bloopConfig = BloopRifleConfig.default(
bloopAddress,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package scala.cli.commands.bloop

import caseapp._

import scala.cli.commands.{LoggingOptions, SharedCompilationServerOptions, SharedDirectoriesOptions}

// format: off
final case class BloopOutputOptions(
@Recurse
logging: LoggingOptions = LoggingOptions(),
@Recurse
compilationServer: SharedCompilationServerOptions = SharedCompilationServerOptions(),
@Recurse
directories: SharedDirectoriesOptions = SharedDirectoriesOptions()
)
// format: on

object BloopOutputOptions {
implicit lazy val parser: Parser[BloopOutputOptions] = Parser.derive
implicit lazy val help: Help[BloopOutputOptions] = Help.derive
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package scala.cli.commands.bloop

import caseapp.core.RemainingArgs

import scala.build.blooprifle.BloopRifleConfig
import scala.cli.CurrentParams
import scala.cli.commands.util.CommonOps._
import scala.cli.commands.util.SharedCompilationServerOptionsUtil._
import scala.cli.commands.{CoursierOptions, ScalaCommand}

object BloopOutput extends ScalaCommand[BloopOutputOptions] {
override def hidden = true
override def inSipScala = false
override def names: List[List[String]] = List(
List("bloop", "output")
)

def run(options: BloopOutputOptions, args: RemainingArgs): Unit = {
CurrentParams.verbosity = options.logging.verbosity
val logger = options.logging.logger
val bloopRifleConfig = options.compilationServer.bloopRifleConfig(
logger,
CoursierOptions().coursierCache(logger.coursierLogger("Downloading Bloop")), // unused here
options.logging.verbosity,
"unused-java", // unused here
options.directories.directories
)
val outputFile = bloopRifleConfig.address match {
case s: BloopRifleConfig.Address.DomainSocket =>
logger.debug(s"Bloop server directory: ${s.path}")
logger.debug(s"Bloop server output path: ${s.outputPath}")
os.Path(s.outputPath, os.pwd)
case tcp: BloopRifleConfig.Address.Tcp =>
if (options.logging.verbosity >= 0)
System.err.println(
s"Error: Bloop server is listening on TCP at ${tcp.render}, output not available."
)
sys.exit(1)
}
if (!os.isFile(outputFile)) {
if (options.logging.verbosity >= 0)
System.err.println(s"Error: $outputFile not found")
sys.exit(1)
}
val content = os.read.bytes(outputFile)
logger.debug(s"Read ${content.length} bytes from $outputFile")
System.out.write(content)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scala.cli.commands.pgp
import scala.cli.signing.commands.PgpCreateOptions

class PgpCreateExternal extends PgpExternalCommand {
override def hidden = true
def actualHelp = PgpCreateOptions.help
def externalCommand = Seq("pgp", "create")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scala.cli.commands.pgp
import scala.cli.signing.commands.PgpSignOptions

class PgpSignExternal extends PgpExternalCommand {
override def hidden = true
def actualHelp = PgpSignOptions.help
def externalCommand = Seq("pgp", "sign")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scala.cli.commands.pgp
import scala.cli.signing.commands.PgpVerifyOptions

class PgpVerifyExternal extends PgpExternalCommand {
override def hidden = true
def actualHelp = PgpVerifyOptions.help
def externalCommand = Seq("pgp", "verify")

Expand Down