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

feature: Switch to using Bloop Rifle and backport all improvements #2355

Merged
merged 6 commits into from
Jul 11, 2024
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
46 changes: 12 additions & 34 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- uses: graalvm/setup-graalvm@v1
with:
version: '22.3.0'
java-version: '11'
java-version: '17'
Copy link
Member

Choose a reason for hiding this comment

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

Is it not compatible with Java 11 anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need at least Java 16 for UnixDomainSocket to work, which was the biggest blocker here, but Metals already dropped JDK 8 and uses JDK 17 as default with correct release flags being set automatically. And Bloop also does add release flags to make sure the compilation is done with the correct version of Java. Plus we also download JDK automatically using coursier both in Metals and Bloop new (moved) cli project, so this should be smooth for most people especially since Scala CLI already does it.

github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Tests
Expand All @@ -58,7 +58,7 @@ jobs:
- uses: graalvm/setup-graalvm@v1
with:
version: '22.3.0'
java-version: '11'
java-version: '17'
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Tests
Expand All @@ -74,37 +74,13 @@ jobs:
sbt nativeBridge04/test
shell: bash

launcher:
name: Launcher tests
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macOS-12]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: graalvm/setup-graalvm@v1
with:
version: '22.3.0'
java-version: '11'
github-token: ${{ secrets.GITHUB_TOKEN }}
components: 'native-image'

- name: Tests
run: |
echo $JAVA_HOME
sbt "install; launcherTest/test"
shell: bash

test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macOS-12]
jdk: ["22.3.0", "11"]
jdk: ["22.3.0", "17"]

name: Test ${{ matrix.os }} -- ${{ matrix.jdk }}
env:
Expand All @@ -118,15 +94,15 @@ jobs:

- uses: coursier/setup-action@v1
with:
jvm: 'temurin:11.0.18'
if: matrix.jdk == '11'
jvm: 'temurin:17'
if: matrix.jdk == '17'

- uses: coursier/cache-action@v6

- uses: graalvm/setup-graalvm@v1
with:
version: ${{ matrix.jdk }}
java-version: '11'
java-version: '17'
if: matrix.jdk == '22.3.0'

- uses: actions/setup-node@v4
Expand All @@ -146,6 +122,7 @@ jobs:
"frontend/test:compile; \
backend/test; \
docs/compile; \
bloopRifle/test; \
frontend/testOnly bloop.ScalaVersionsSpec; \
frontend/testOnly -bloop.ScalaVersionsSpec; \
frontend/runMain bloop.util.CommandsDocGenerator --test; \
Expand Down Expand Up @@ -188,23 +165,24 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Publish GraalVM Native artifacts
run: sbt "bloopgun213/graalvm-native-image:packageBin"
run: sbt "cli/graalvm-native-image:packageBin"

- name: Copy artifacts (windows)
if: matrix.os == 'windows-latest'
run: |
echo $ARTIFACT_NAME
ls bloopgun/target/bloopgun-2.13/graalvm-native-image/bloopgun-core.exe
ls cli/target/graalvm-native-image/cli.exe
mkdir -p bloop-artifacts
cp bloopgun/target/bloopgun-2.13/graalvm-native-image/bloopgun-core.exe bloop-artifacts/$ARTIFACT_NAME
cp cli/target/graalvm-native-image/cli.exe bloop-artifacts/$ARTIFACT_NAME
shell: bash

- name: Copy artifacts (not windows)
if: matrix.os != 'windows-latest'
run: |
ls cli/target/graalvm-native-image/cli
echo $ARTIFACT_NAME
mkdir -p bloop-artifacts
cp bloopgun/target/bloopgun-2.13/graalvm-native-image/bloopgun-core bloop-artifacts/$ARTIFACT_NAME
cp cli/target/graalvm-native-image/cli bloop-artifacts/$ARTIFACT_NAME

- uses: actions/upload-artifact@v4
with:
Expand Down
186 changes: 186 additions & 0 deletions bloop-rifle/src/main/scala/bloop/rifle/BloopRifle.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package bloop.rifle

import bloop.rifle.internal.{Operations, Util}

import java.io.{ByteArrayOutputStream, OutputStream}
import java.nio.file.Path
import java.util.concurrent.ScheduledExecutorService

import scala.concurrent.Future

object BloopRifle {

/**
* Checks whether a bloop server is running at this host / port.
*
* @param host
* @param port
* @param logger
* @return
* Whether a server is running or not.
*/
def check(
config: BloopRifleConfig,
logger: BloopRifleLogger
): Boolean = {
def check() =
Operations.check(
config.address,
logger
)
check()
}

/**
* Starts a new bloop server.
*
* @param config
* @param scheduler
* @param logger
* @return
* A future, that gets completed when the server is done starting (and can thus be used).
*/
def startServer(
config: BloopRifleConfig,
scheduler: ScheduledExecutorService,
logger: BloopRifleLogger,
version: String,
bloopJava: String
): Future[Unit] =
config.classPath(version) match {
case Left(ex) => Future.failed(new Exception("Error getting Bloop class path", ex))
case Right(cp) =>
logger.info("Starting compilation server")
logger.debug(
s"Starting Bloop $version at ${config.address.render} using JVM $bloopJava"
)
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 =
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, 4, 20)
case _ =>
false
}

Operations.startServer(
config.address,
bloopJava,
config.javaOpts,
cp.map(_.toPath),
config.workingDir,
scheduler,
config.startCheckPeriod,
config.startCheckTimeout,
logger,
bloopServerSupportsFileTruncating = bloopServerSupportsFileTruncating
)
}

/**
* Opens a BSP connection to a running bloop server.
*
* Starts a thread to read output from the nailgun connection, and another one to pass input to
* it.
*
* @param logger
* @return
* A [[BspConnection]] object, that can be used to close the connection.
*/
def bsp(
config: BloopRifleConfig,
workingDir: Path,
logger: BloopRifleLogger
): BspConnection = {

val bspSocketOrPort = config.bspSocketOrPort.map(_()).getOrElse {
BspConnectionAddress.Tcp("127.0.0.1", Util.randomPort())
}

val inOpt = config.bspStdin

val out = config.bspStdout.getOrElse(OutputStream.nullOutputStream())
val err = config.bspStderr.getOrElse(OutputStream.nullOutputStream())

Operations.bsp(
config.address,
bspSocketOrPort,
workingDir,
inOpt,
out,
err,
logger
)
}

def exit(
config: BloopRifleConfig,
workingDir: Path,
logger: BloopRifleLogger
): Int = {

val out = config.bspStdout.getOrElse(OutputStream.nullOutputStream())
val err = config.bspStderr.getOrElse(OutputStream.nullOutputStream())

Operations.exit(
config.address,
workingDir,
out,
err,
logger
)
}

def getCurrentBloopVersion(
config: BloopRifleConfig,
logger: BloopRifleLogger,
workdir: Path,
scheduler: ScheduledExecutorService
): Either[BloopAboutFailure, BloopServerRuntimeInfo] = {
val isRunning = BloopRifle.check(config, logger)

if (isRunning) {
val bufferedOStream = new ByteArrayOutputStream
Operations.about(
config.address,
workdir,
bufferedOStream,
OutputStream.nullOutputStream(),
logger,
scheduler
)
val bloopAboutOutput = new String(bufferedOStream.toByteArray)
VersionUtil
.parseBloopAbout(bloopAboutOutput)
.toRight(ParsingFailed(bloopAboutOutput))
} else
Left(BloopNotRunning)
}

sealed abstract class BloopAboutFailure extends Product with Serializable {
def message: String
}
case object BloopNotRunning extends BloopAboutFailure {
def message = "not running"
}
final case class ParsingFailed(bloopAboutOutput: String) extends BloopAboutFailure {
def message = s"failed to parse output: '$bloopAboutOutput'"
}

final case class BloopServerRuntimeInfo(
bloopVersion: BloopVersion,
jvmVersion: Int,
javaHome: String
) {
def message: String =
s"version $bloopVersion, JVM $jvmVersion under $javaHome"
}
}
Loading
Loading