Skip to content

Commit 2e2adce

Browse files
Merge pull request #567 from alexarchambault/bsp-wrap-script
Generate semantic DBs for Java sources, pass wrapped sources details over BSP
2 parents 851ff7e + f130119 commit 2e2adce

32 files changed

+704
-76
lines changed

build.sc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ object stubs extends JavaModule with ScalaCliPublishModule {
4646
super.javacOptions() ++ Seq("-target", "8", "-source", "8")
4747
}
4848
}
49+
object `scala-cli-bsp` extends JavaModule with ScalaCliPublishModule {
50+
def ivyDeps = super.ivyDeps() ++ Seq(
51+
Deps.bsp4j
52+
)
53+
def javacOptions = T {
54+
super.javacOptions() ++ Seq("-target", "8", "-source", "8")
55+
}
56+
}
4957
object integration extends Module {
5058
object docker extends CliIntegrationDocker {
5159
object test extends Tests {
@@ -170,6 +178,7 @@ class Build(val crossScalaVersion: String)
170178
def moduleDeps = Seq(
171179
`bloop-rifle`(),
172180
`build-macros`(),
181+
`scala-cli-bsp`,
173182
`test-runner`(),
174183
`tasty-lib`()
175184
)
@@ -255,6 +264,10 @@ class Build(val crossScalaVersion: String)
255264
| def semanticDbPluginModuleName = "semanticdb-scalac"
256265
| def semanticDbPluginVersion = "${Deps.scalametaTrees.dep.version}"
257266
|
267+
| def semanticDbJavacPluginOrganization = "${Deps.semanticDbJavac.dep.module.organization.value}"
268+
| def semanticDbJavacPluginModuleName = "${Deps.semanticDbJavac.dep.module.name.value}"
269+
| def semanticDbJavacPluginVersion = "${Deps.semanticDbJavac.dep.version}"
270+
|
258271
| def localRepoResourcePath = "$localRepoResourcePath"
259272
|
260273
| def jmhVersion = "1.29"

modules/bloop-rifle/src/main/scala/scala/build/blooprifle/BloopRifle.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ object BloopRifle {
5454
bloopJava,
5555
config.javaOpts,
5656
cp.map(_.toPath),
57+
config.workingDir,
5758
scheduler,
5859
config.startCheckPeriod,
5960
config.startCheckTimeout,

modules/bloop-rifle/src/main/scala/scala/build/blooprifle/BloopRifleConfig.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ final case class BloopRifleConfig(
1212
javaPath: String,
1313
javaOpts: Seq[String],
1414
classPath: String => Either[Throwable, Seq[File]],
15+
workingDir: File,
1516
bspSocketOrPort: Option[() => BspConnectionAddress],
1617
bspStdin: Option[InputStream],
1718
bspStdout: Option[OutputStream],
@@ -116,13 +117,15 @@ object BloopRifleConfig {
116117

117118
def default(
118119
address: Address,
119-
bloopClassPath: String => Either[Throwable, Seq[File]]
120+
bloopClassPath: String => Either[Throwable, Seq[File]],
121+
workingDir: File
120122
): BloopRifleConfig =
121123
BloopRifleConfig(
122124
address = address,
123125
javaPath = "java",
124126
javaOpts = defaultJavaOpts,
125127
classPath = bloopClassPath,
128+
workingDir = workingDir,
126129
bspSocketOrPort = None,
127130
bspStdin = None,
128131
bspStdout = None,

modules/bloop-rifle/src/main/scala/scala/build/blooprifle/internal/Operations.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ object Operations {
102102
javaPath: String,
103103
javaOpts: Seq[String],
104104
classPath: Seq[Path],
105+
workingDir: File,
105106
scheduler: ScheduledExecutorService,
106107
waitInterval: FiniteDuration,
107108
timeout: Duration,
@@ -124,6 +125,7 @@ object Operations {
124125
) ++
125126
addressArgs
126127
val b = new ProcessBuilder(command: _*)
128+
b.directory(workingDir)
127129
b.redirectInput(ProcessBuilder.Redirect.PIPE)
128130

129131
// https://stackoverflow.com/questions/55628999/java-processbuilder-how-to-suppress-output-instead-of-redirecting-it/55629297#55629297

modules/build/src/main/scala/scala/build/Artifacts.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ final case class Artifacts(
2424
compilerDependencies: Seq[AnyDependency],
2525
compilerArtifacts: Seq[(String, Path)],
2626
compilerPlugins: Seq[(AnyDependency, String, Path)],
27+
javacPluginDependencies: Seq[(AnyDependency, String, Path)],
28+
extraJavacPlugins: Seq[Path],
2729
dependencies: Seq[AnyDependency],
2830
scalaNativeCli: Seq[Path],
2931
detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, Path)],
@@ -63,6 +65,8 @@ object Artifacts {
6365
def apply(
6466
params: ScalaParameters,
6567
compilerPlugins: Seq[Positioned[AnyDependency]],
68+
javacPluginDependencies: Seq[Positioned[AnyDependency]],
69+
extraJavacPlugins: Seq[Path],
6670
dependencies: Seq[Positioned[AnyDependency]],
6771
extraClassPath: Seq[Path],
6872
extraCompileOnlyJars: Seq[Path],
@@ -198,10 +202,23 @@ object Artifacts {
198202
.map(_.flatten)
199203
}
200204

205+
val javacPlugins0 = value {
206+
javacPluginDependencies
207+
.map { posDep =>
208+
artifacts(posDep.map(Seq(_)), allExtraRepositories, params, logger)
209+
.map(_.map { case (url, path) => (posDep.value, url, path) })
210+
}
211+
.sequence
212+
.left.map(CompositeBuildException(_))
213+
.map(_.flatten)
214+
}
215+
201216
Artifacts(
202217
compilerDependencies,
203218
compilerArtifacts,
204219
compilerPlugins0,
220+
javacPlugins0,
221+
extraJavacPlugins,
205222
updatedDependencies.map(_.value),
206223
scalaNativeCli,
207224
fetchRes.fullDetailedArtifacts.collect { case (d, p, a, Some(f)) => (d, p, a, f.toPath) },

modules/build/src/main/scala/scala/build/BloopBuildClient.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import ch.epfl.scala.bsp4j
44

55
import java.io.PrintStream
66

7+
import scala.build.options.Scope
8+
79
trait BloopBuildClient extends bsp4j.BuildClient {
810
def setProjectParams(newParams: Seq[String]): Unit
9-
def setGeneratedSources(newGeneratedSources: Seq[GeneratedSource]): Unit
11+
def setGeneratedSources(scope: Scope, newGeneratedSources: Seq[GeneratedSource]): Unit
1012
def diagnostics: Option[Seq[(Either[String, os.Path], bsp4j.Diagnostic)]]
1113
def clear(): Unit
1214
}

modules/build/src/main/scala/scala/build/Build.scala

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,10 @@ object Build {
559559
s"-Xplugin:${path.toAbsolutePath}"
560560
}
561561

562+
val generateSemanticDbs = options.scalaOptions.generateSemanticDbs.getOrElse(false)
563+
562564
val semanticDbScalacOptions =
563-
if (options.scalaOptions.generateSemanticDbs.getOrElse(false))
565+
if (generateSemanticDbs)
564566
if (params.scalaVersion.startsWith("2."))
565567
Seq(
566568
"-Yrangepos",
@@ -574,6 +576,29 @@ object Build {
574576
)
575577
else Nil
576578

579+
val semanticDbJavacOptions =
580+
// FIXME Should this be in scalaOptions, now that we use it for javac stuff too?
581+
if (generateSemanticDbs) {
582+
// from https://github.com/scalameta/metals/blob/04405c0401121b372ea1971c361e05108fb36193/metals/src/main/scala/scala/meta/internal/metals/JavaInteractiveSemanticdb.scala#L137-L146
583+
val compilerPackages = Seq(
584+
"com.sun.tools.javac.api",
585+
"com.sun.tools.javac.code",
586+
"com.sun.tools.javac.model",
587+
"com.sun.tools.javac.tree",
588+
"com.sun.tools.javac.util"
589+
)
590+
val exports = compilerPackages.flatMap { pkg =>
591+
Seq("-J--add-exports", s"-Jjdk.compiler/$pkg=ALL-UNNAMED")
592+
}
593+
594+
Seq(
595+
// does the path need to be escaped somehow?
596+
s"-Xplugin:semanticdb -sourceroot:${inputs.workspace} -targetroot:javac-classes-directory"
597+
) ++ exports
598+
}
599+
else
600+
Nil
601+
577602
val sourceRootScalacOptions =
578603
if (params.scalaVersion.startsWith("2.")) Nil
579604
else Seq("-sourceroot", inputs.workspace.toString)
@@ -601,6 +626,8 @@ object Build {
601626
compilerClassPath = artifacts.compilerClassPath
602627
)
603628

629+
val javacOptions = javacReleaseV ++ semanticDbJavacOptions ++ options.javaOptions.javacOptions
630+
604631
// `test` scope should contains class path to main scope
605632
val mainClassesPath =
606633
if (scope == Scope.Test)
@@ -609,6 +636,11 @@ object Build {
609636

610637
value(validate(logger, options))
611638

639+
val fullClassPath = artifacts.compileClassPath ++
640+
mainClassesPath ++
641+
artifacts.javacPluginDependencies.map(_._3) ++
642+
artifacts.extraJavacPlugins
643+
612644
val project = Project(
613645
directory = inputs.workspace / Constants.workspaceDirName,
614646
workspace = inputs.workspace,
@@ -622,13 +654,13 @@ object Build {
622654
Some(options.scalaNativeOptions.bloopConfig())
623655
else None,
624656
projectName = inputs.scopeProjectName(scope),
625-
classPath = artifacts.compileClassPath ++ mainClassesPath,
657+
classPath = fullClassPath,
626658
resolution = Some(Project.resolution(artifacts.detailedArtifacts)),
627659
sources = allSources,
628660
resourceDirs = sources.resourceDirs,
629661
scope = scope,
630662
javaHomeOpt = Option(options.javaHomeLocation().value),
631-
javacOptions = javacReleaseV
663+
javacOptions = javacOptions
632664
)
633665
project
634666
}
@@ -668,7 +700,7 @@ object Build {
668700
}
669701

670702
buildClient.clear()
671-
buildClient.setGeneratedSources(generatedSources)
703+
buildClient.setGeneratedSources(scope, generatedSources)
672704

673705
(classesDir0, params, artifacts, project, updatedBloopConfig)
674706
}
@@ -723,7 +755,7 @@ object Build {
723755
}
724756

725757
buildClient.clear()
726-
buildClient.setGeneratedSources(generatedSources)
758+
buildClient.setGeneratedSources(scope, generatedSources)
727759
val success = Bloop.compile(
728760
inputs.scopeProjectName(scope),
729761
bloopServer,

modules/build/src/main/scala/scala/build/ConsoleBloopBuildClient.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import java.net.URI
77
import java.nio.file.Paths
88

99
import scala.build.errors.Severity
10+
import scala.build.options.Scope
1011
import scala.collection.mutable
1112
import scala.jdk.CollectionConverters._
1213

1314
class ConsoleBloopBuildClient(
1415
logger: Logger,
1516
out: PrintStream,
1617
keepDiagnostics: Boolean = false,
17-
var generatedSources: Seq[GeneratedSource] = Nil
18+
generatedSources: mutable.Map[Scope, Seq[GeneratedSource]] = mutable.Map()
1819
) extends BloopBuildClient {
1920
import ConsoleBloopBuildClient._
2021
private var projectParams = Seq.empty[String]
@@ -29,8 +30,8 @@ class ConsoleBloopBuildClient(
2930

3031
private val diagnostics0 = new mutable.ListBuffer[(Either[String, os.Path], bsp4j.Diagnostic)]
3132

32-
def setGeneratedSources(newGeneratedSources: Seq[GeneratedSource]) =
33-
generatedSources = newGeneratedSources
33+
def setGeneratedSources(scope: Scope, newGeneratedSources: Seq[GeneratedSource]) =
34+
generatedSources(scope) = newGeneratedSources
3435
def setProjectParams(newParams: Seq[String]): Unit = {
3536
projectParams = newParams
3637
}
@@ -70,7 +71,8 @@ class ConsoleBloopBuildClient(
7071
logger.debug("Received onBuildPublishDiagnostics from bloop: " + params)
7172
for (diag <- params.getDiagnostics.asScala) {
7273

73-
val diagnosticMappings = generatedSources
74+
val diagnosticMappings = generatedSources.valuesIterator
75+
.flatMap(_.iterator)
7476
.map { source =>
7577
val lineShift = -os.read(source.generated)
7678
.take(source.topWrapperLen)
@@ -139,7 +141,7 @@ class ConsoleBloopBuildClient(
139141
override def onConnectWithServer(server: bsp4j.BuildServer): Unit = {}
140142

141143
def clear(): Unit = {
142-
generatedSources = Nil
144+
generatedSources.clear()
143145
diagnostics0.clear()
144146
printedStart = false
145147
}

modules/build/src/main/scala/scala/build/Directories.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ trait Directories {
1111
def virtualProjectsDir: os.Path
1212
def bspSocketDir: os.Path
1313
def bloopDaemonDir: os.Path
14+
def bloopWorkingDir: os.Path
1415
}
1516

1617
object Directories {
@@ -28,11 +29,13 @@ object Directories {
2829
// FIXME I would have preferred to use projDirs.dataLocalDir, but it seems named socket
2930
// support, or name sockets in general, aren't fine with it.
3031
os.Path(projDirs.cacheDir, Os.pwd) / "bsp-sockets"
31-
lazy val bloopDaemonDir: os.Path = {
32+
lazy val bloopDaemonDir: os.Path =
33+
bloopWorkingDir / "daemon"
34+
lazy val bloopWorkingDir: os.Path = {
3235
val baseDir =
3336
if (Properties.isMac) projDirs.cacheDir
3437
else projDirs.dataLocalDir
35-
os.Path(baseDir, Os.pwd) / "bloop" / "daemon"
38+
os.Path(baseDir, Os.pwd) / "bloop"
3639
}
3740
}
3841

@@ -48,7 +51,9 @@ object Directories {
4851
lazy val bspSocketDir: os.Path =
4952
dir / "data-local" / "bsp-sockets"
5053
lazy val bloopDaemonDir: os.Path =
51-
dir / "data-local" / "bloop" / "daemon"
54+
bloopWorkingDir / "daemon"
55+
lazy val bloopWorkingDir: os.Path =
56+
dir / "data-local" / "bloop"
5257
}
5358

5459
def default(): Directories = {

modules/build/src/main/scala/scala/build/ReplArtifacts.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ object ReplArtifacts {
6767
dependencies: Seq[AnyDependency],
6868
extraClassPath: Seq[Path],
6969
logger: Logger,
70-
directories: Directories,
7170
repositories: Seq[String]
7271
): Either[BuildException, ReplArtifacts] = either {
7372
val isScala2 = scalaParams.scalaVersion.startsWith("2.")

0 commit comments

Comments
 (0)