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

Update jsonrpc4s (via a fork) #161

Merged
merged 3 commits into from Mar 15, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Expand Up @@ -84,11 +84,9 @@ jobs:
with:
jvm: "zulu:17"
- run: |
./mill -i __.publishLocal &&\
./mill -i integration.test.jvm
if: runner.os != 'Windows'
- run: |
@call ./mill.bat -i __.publishLocal
@call ./mill.bat -i integration.test.jvm
shell: cmd
if: runner.os == 'Windows'
Expand Down Expand Up @@ -127,11 +125,9 @@ jobs:
if-no-files-found: error
retention-days: 1
- run: |
./mill -i __.publishLocal &&\
./mill -i "integration.test.native"
if: github.event_name == 'push' && runner.os != 'Windows'
- run: |
./mill.bat -i __.publishLocal
./mill.bat -i integration.test.native
if: github.event_name == 'push' && runner.os == 'Windows'
shell: bash
Expand Down
30 changes: 26 additions & 4 deletions build.sc
Expand Up @@ -19,6 +19,8 @@ object Dependencies {

def scalaVersions = Seq(scala212, scala213)

def serverScalaVersion = scala212

def asmVersion = "9.6"
def coursierVersion = "2.1.0-M6-53-gb4f448130"
def graalvmVersion = "22.2.0"
Expand Down Expand Up @@ -46,6 +48,7 @@ object Dependencies {
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:$jsoniterVersion"
def jsoniterMacros =
ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:$jsoniterVersion"
def jsonrpc4s = ivy"io.github.alexarchambault.bleep::jsonrpc4s:0.1.1"
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the reason for the fork? Any chance to just include it in the library?

Btw. I just moved the fork to https://github.com/VirtusLab/bloop-core since I didn't want to bother you every time something broke (and release did actually break).

As a side note, I also plan to move most of the things back to original Bloop to get rid of the fork altogether, since we plan to switch it to JDK 17 soon. Would it be enough to just move bloop-rifle back? I don't see anything else that is hugely changed from the original Bloop

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What's the reason for the fork? Any chance to just include it in the library?

It's just easier for me to have the fix be released and usable from here (I tried to upstream some changes in snailgun some time ago, but they're still not released…)

Btw. I just moved the fork to https://github.com/VirtusLab/bloop-core since I didn't want to bother you every time something broke (and release did actually break).

I intend to cut a release right after merging this, so I'll probably fix that

As a side note, I also plan to move most of the things back to original Bloop to get rid of the fork altogether, since we plan to switch it to JDK 17 soon. Would it be enough to just move bloop-rifle back? I don't see anything else that is hugely changed from the original Bloop

Good that the changes in shared / backend / frontend are upstreamed. But bloop-rifle should be used by the Bloop CLI, and so should live alongside it in the Bloop repo IMO. It doesn't really need to live in the Scala CLI repo anyway I think.

def junit = ivy"com.github.sbt:junit-interface:0.13.3"
def libdaemonjvm = ivy"io.github.alexarchambault.libdaemon::libdaemon:0.0.11"
def libraryManagement = ivy"org.scala-sbt::librarymanagement-ivy:1.9.3"
Expand Down Expand Up @@ -156,7 +159,11 @@ class Shared(val crossScalaVersion: String) extends BloopCrossSbtModule with Pub
emptyZip()
}
def ivyDeps = super.ivyDeps() ++ Agg(
Dependencies.bsp4s.exclude(("com.github.plokhotnyuk.jsoniter-scala", "*")),
Dependencies.bsp4s
.exclude(("com.github.plokhotnyuk.jsoniter-scala", "*"))
.exclude(("me.vican.jorge", "jsonrpc4s_2.12"))
.exclude(("me.vican.jorge", "jsonrpc4s_2.13")),
Dependencies.jsonrpc4s,
Dependencies.coursierInterface,
Dependencies.jsoniterCore,
Dependencies.log4j,
Expand Down Expand Up @@ -532,8 +539,8 @@ object `bloop-rifle` extends BloopCliModule {
|
|/** Build-time constants. Generated by mill. */
|object Constants {
| def bloopVersion = "${frontend(Dependencies.scala212).publishVersion()}"
| def bloopScalaVersion = "${Dependencies.scala212}"
| def bloopVersion = "${frontend(Dependencies.serverScalaVersion).publishVersion()}"
| def bloopScalaVersion = "${Dependencies.serverScalaVersion}"
| def bspVersion = "${Dependencies.bsp4j.dep.version}"
|}
|""".stripMargin
Expand Down Expand Up @@ -607,6 +614,19 @@ object integration extends BloopCliModule {
Dependencies.pprint
)

private def repoRoot = os.pwd / "out" / "repo"
def localRepo = T {
val modules = Seq(
frontend(Dependencies.serverScalaVersion),
backend(Dependencies.serverScalaVersion),
shared(Dependencies.serverScalaVersion)
)
os.remove.all(repoRoot)
os.makeDir.all(repoRoot)
val tasks = modules.map(_.publishLocalNoFluff(repoRoot.toString))
define.Target.sequence(tasks)
}

def forkEnv = super.forkEnv() ++ Seq(
"BLOOP_CLI_TESTS_TMP_DIR" -> tmpDir()
)
Expand All @@ -615,8 +635,10 @@ object integration extends BloopCliModule {
def test(args: String*) = {
val argsTask = T.task {
val launcher = launcherTask().path
localRepo()
val extraArgs = Seq(
s"-Dtest.bloop-cli.path=$launcher"
s"-Dtest.bloop-cli.path=$launcher",
s"-Dtest.bloop-cli.repo=$repoRoot"
)
args ++ extraArgs
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/main/scala/bloop/bsp/BspServer.scala
Expand Up @@ -163,6 +163,8 @@ object BspServer {
case scala.util.Success(socket: ServerSocket) =>
listenToConnection(handle, socket).onErrorRecover {
case t =>
System.err.println("Exiting BSP server with:")
t.printStackTrace(System.err)
state.withError(s"Exiting BSP server with ${t.getMessage}", t)
}
case scala.util.Failure(t: Throwable) =>
Expand Down
Expand Up @@ -9,22 +9,21 @@ class BloopCliTests extends munit.FunSuite {
val dirArgs = Seq[os.Shellable]("--daemon-dir", root / "daemon")

os.proc(Launcher.launcher, "exit", dirArgs)
.call(cwd = root)
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
val statusCheck0 = os.proc(Launcher.launcher, "status", dirArgs)
.call(cwd = root)
.out.trim()
expect(statusCheck0 == "stopped")

val res = os.proc(Launcher.launcher, dirArgs, "about")
.call(cwd = root)
expect(res.out.text().startsWith("bloop v"))
os.proc(Launcher.launcher, dirArgs, "about")
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = Launcher.extraEnv)
val statusCheck1 = os.proc(Launcher.launcher, "status", dirArgs)
.call(cwd = root)
.out.trim()
expect(statusCheck1 == "running")

os.proc(Launcher.launcher, "exit", dirArgs)
.call(cwd = root)
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit)
val statusCheck2 = os.proc(Launcher.launcher, "status", dirArgs)
.call(cwd = root)
.out.trim()
Expand Down
134 changes: 134 additions & 0 deletions integration/test/src/bloop/cli/integration/BspTests.scala
@@ -0,0 +1,134 @@
package bloop.cli.integration

import java.net.{StandardProtocolFamily, UnixDomainSocketAddress}
import java.nio.channels.SocketChannel
import java.nio.charset.StandardCharsets
import java.nio.ByteBuffer

class BspTests extends munit.FunSuite {

test("no JSON junk in errors") {
TmpDir.fromTmpDir { root =>
val dirArgs = Seq[os.Shellable]("--daemon-dir", root / "daemon")
val bspFile = root / "bsp-socket"

val dummyMsg =
"""{
| "jsonrpc": "2.0",
| "method": "workspace/buildTargetz",
| "params": null,
| "id": 2
|}""".stripMargin

var bspProc: os.SubProcess = null
var socket: SocketChannel = null

try {
os.proc(Launcher.launcher, dirArgs, "about")
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = Launcher.extraEnv)

bspProc =
os.proc(Launcher.launcher, dirArgs, "bsp", "--protocol", "local", "--socket", bspFile)
.spawn(cwd = root, stdin = os.Inherit, stdout = os.Inherit)

val addr = UnixDomainSocketAddress.of(bspFile.toNIO)
var connected = false
var attemptCount = 0

while (!connected && bspProc.isAlive() && attemptCount < 10) {
if (attemptCount > 0)
Thread.sleep(1000L)
attemptCount += 1

if (os.exists(bspFile)) {
socket = SocketChannel.open(StandardProtocolFamily.UNIX)
socket.connect(addr)
socket.finishConnect()
connected = true
}
}

if (!connected)
sys.error("Not connected to Bloop server via BSP :|")

def sendMsg(msg: String): Unit = {
val bytes = msg.getBytes(StandardCharsets.UTF_8)

def doWrite(buf: ByteBuffer): Unit =
if (buf.position() < buf.limit()) {
val written = socket.write(buf)
if (written == 0)
Thread.sleep(100L)
doWrite(buf)
}

doWrite(ByteBuffer.wrap(
(s"Content-Length: ${bytes.length}" + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)
))
doWrite(ByteBuffer.wrap(bytes))
}

sendMsg(dummyMsg)

val arr = Array.ofDim[Byte](10 * 1024)
val buf = ByteBuffer.wrap(arr)

// seems to do the job…
def doRead(buf: ByteBuffer, count: Int): Int = {
val read = socket.read(buf)
if (read <= 0 && count < 100) {
Thread.sleep(100L)
doRead(buf, count + 1)
}
else
read
}

val read = doRead(buf, 0)
assert(read > 0)

val resp = new String(arr, 0, read, StandardCharsets.UTF_8)

def validateJson(content: String): Boolean = {
assert(content.startsWith("{"))
assert(content.endsWith("}"))
val chars = content.toCharArray
val objCount = Array.ofDim[Int](chars.length)
for (i <- 0 until chars.length) {
val previous = if (i == 0) 0 else objCount(i - 1)
val count = previous + (if (chars(i) == '{') 1 else if (chars(i) == '}') -1 else 0)
objCount(i) = count
}
objCount.dropRight(1).forall(_ > 0)
}

resp.linesWithSeparators.toVector match {
case Seq(cl, empty, other @ _*)
if cl.startsWith("Content-Length:") && empty.trim.isEmpty =>
val json = other.mkString
val validated = validateJson(json)
if (!validated)
pprint.err.log(json)
assert(validated, "Unexpected JSON response shape")
case _ =>
pprint.err.log(resp)
sys.error("Unexpected response shape")
}
}
finally {
if (socket != null)
socket.close()

if (bspProc != null) {
bspProc.waitFor(1000L)
if (bspProc.isAlive())
bspProc.close()

os.proc(Launcher.launcher, dirArgs, "exit")
.call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, check = false)
}
}
}
}

}
10 changes: 10 additions & 0 deletions integration/test/src/bloop/cli/integration/Launcher.scala
Expand Up @@ -7,4 +7,14 @@ object Launcher {
sys.error("test.bloop-cli.path not set")
)

lazy val repoRoot = os.Path(sys.props.getOrElse(
"test.bloop-cli.repo",
sys.error("test.bloop-cli.repo not set")
))

lazy val extraEnv =
Map(
"COURSIER_REPOSITORIES" -> s"ivy:${repoRoot.toNIO.toUri.toASCIIString}/[defaultPattern]|ivy2Local|central"
)

}