scalacenter / bloop

Bloop is a build server and CLI tool to compile, test and run Scala fast from any editor or build tool.
https://scalacenter.github.io/bloop/
Apache License 2.0
907 stars 202 forks source link

Allow hot class reloading with DCEVM #1242

Open PhilAndrew opened 4 years ago

PhilAndrew commented 4 years ago

I would like to have hot class reloading with DCEVM. http://dcevm.github.io/ Bloop is used with the Seed Scala build tool. In this issue is described the problem found with attempting to get DCEVM to work. https://github.com/tindzk/seed/issues/76

The main problem as described is boop writing class files to different directories, as it says.

The first command wrote the class files to /tmp/build-dcevm/bloop/bloop-internal-classes/example-6P2vxtl9QjGGuCgBFSQ8wg==, whereas the second one used a different path: /tmp/build-dcevm/bloop/bloop-bsp-clients-classes/example-bloop-21r1tSAdRkONeSDCaa_UqA==/

If these problems could be addressed then hot class reloading could work potentially.

jvican commented 4 years ago

Thanks for opening the ticket @PhilAndrew.

Bloop has complete control over how class files are managed internally. For example, you have referenced some of those internal directories on your ticket. But Bloop still keeps the invariant that class files are written to a stable directory for the duration of a session.

So, Bloop will write the updated class files to the external directory of your client. If you're using the CLI and there's only one CLI client, that is usually .bloop/project-foo/bloop-bsp-clients-classes/classes-bloop-cli/ which is stable across runs. When you use seed, seed connects to bloop via BSP, so it will write the class files to a stable directory that is created when the seed client connects and deleted when it exits.

I don't know the specifics of how seed manages its bloop creation but integrating with DCEVM should work. The internal BSP classes dirs are private to bloop and shouldn't be used by external tools. /cc @tindzk

tindzk commented 4 years ago

Presently, there are two limitations in Bloop which make DCEVM support slightly inconvenient:

  1. The BSP protocol does not allow users to specify environment variables. This would be useful to selectively run a module with the DCEVM agent via JAVA_TOOL_OPTIONS (see example below).

  2. Seed does not support running commands in parallel over a single BSP connection yet (as discussed here). For now, it should be possible to use two terminals:

# Terminal 1
$ seed build --watch example  # uses BSP connection

# Terminal 2
$ seed run example  # uses BSP connection, but requires hard-coding the JVM options in the JSON configuration file
# or
$ JAVA_TOOL_OPTIONS="-XXaltjvm=dcevm -javaagent:/path/to/hotswap-agent.jar" bloop run example

Bloop uses different class paths for all these commands, so any changes made to the compiled classes will not be detected by the DCEVM agent.

imcharsi commented 4 years ago

Hi.

I'm trying to use VS Code + Metals + Bloop + DCEVM and I think that I found some problem.

Let me explain about it.

At first, Below is example bsp.trace.json when DCEVM was used.

[Trace - 02:38:05 오후] Received notification 'build/logMessage'
Params: {
  "type": 3,
  "message": "Starting HotswapAgent \u0027C:\\Users\\i\\Downloads\\java11-openjdk-dcevm-windows\\dcevm-11.0.7\\lib\\hotswap\\hotswap-agent.jar\u0027\nListening for transport dt_socket at address: 57071"
}

And Below is another bsp.trace.json when Oracle JVM was used.

[Trace - 02:48:07 오후] Received notification 'build/logMessage'
Params: {
  "type": 3,
  "message": "Listening for transport dt_socket at address: 57129"
}

And here is somewhere I guess that it may be related to this.

https://github.com/scalacenter/bloop/blob/681434ff9bb4153873050d601475e99fbfb056da/frontend/src/main/scala/bloop/dap/DebugSessionLogger.scala#L57

Key point is that there is a non-standard output(Starting HotswapAgent…) when DCEVM is used and Bloop can't parse the listening port because of such output.

So, My suggestion is adding some exceptional logic about ignoring such non-standard output like below.

...
    val from = msg.indexOf(JDINotificationPrefix) // appended line
    if (0 <= from) {  // appended line
    // if (msg.startsWith(JDINotificationPrefix)) { // removed line
      if (initialized.compareAndSet(false, true)) {
        val port = Integer.parseInt(msg.drop(from).drop(JDINotificationPrefix.length)) // appended line
        // val port = Integer.parseInt(msg.drop(JDINotificationPrefix.length)) // removed line
...

It is just my opinion.

Thanks for reading.

imcharsi commented 4 years ago

Hi.

I tried this in Ubuntu and there is no problem.

So, I opened the bsp.trace.json file and the related parts are below.

[Trace - 00:23:40 PM] Received notification 'build/logMessage'
Params: {
  "type": 3,
  "message": "Starting HotswapAgent \u0027/home/i/dcevm-11.0.7+1/lib/hotswap/hotswap-agent.jar\u0027"
}

[Trace - 00:23:40 PM] Received notification 'build/logMessage'
Params: {
  "type": 3,
  "message": "Listening for transport dt_socket at address: 40147"
}

Maybe the reason is single LF(0x0A) instead of CR(0x13)+LF(0x0A) in case of Windows?