Skip to content

Commit 1058906

Browse files
committed
improvement: Try and find latest supported semanticdb version
Previously, we wouldn't produce semanticdb files for older version of Scala since we wouldn't find the correct artifact if we stopped publishing for a particular version. Now, we try and find the latest version that still supported that Scala version. This is similar to the change we did in Metals in regards to the presentation compiler interface, where we would fetch old mtags for unsupported scala version and this way always support a particular version.
1 parent cfd7303 commit 1058906

2 files changed

Lines changed: 88 additions & 27 deletions

File tree

frontend/src/main/scala/bloop/engine/caches/SemanticDBCache.scala

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,30 @@ package bloop.engine.caches
22

33
import java.nio.file.Path
44

5+
import scala.collection.JavaConverters._
6+
import scala.concurrent.Await
7+
import scala.concurrent.duration.FiniteDuration
58
import scala.util.Failure
69
import scala.util.Success
710
import scala.util.Try
811

912
import bloop.DependencyResolution
1013
import bloop.SemanticDBCacheLock
14+
import bloop.engine.ExecutionContext
1115
import bloop.io.AbsolutePath
1216
import bloop.io.Paths
1317
import bloop.logging.Logger
18+
import bloop.task.Task
1419

1520
import sbt.internal.inc.BloopComponentCompiler
1621
import sbt.internal.inc.BloopComponentManager
1722
import sbt.internal.inc.IfMissing
23+
import java.util.concurrent.ConcurrentHashMap
24+
import scala.concurrent.Future
1825

1926
object SemanticDBCache {
27+
// to avoid resolving the same fallback semanticdb version multiple times
28+
private val supportedFallbackSemanticdbVersions = new ConcurrentHashMap[String, String]()
2029
private def fetchPlugin(
2130
artifact: DependencyResolution.Artifact,
2231
logger: Logger
@@ -57,6 +66,41 @@ object SemanticDBCache {
5766
}
5867
}
5968

69+
private def fallbackSemanticdbFetch(
70+
scalaVersion: String,
71+
artifact: DependencyResolution.Artifact,
72+
logger: Logger
73+
): Either[String, AbsolutePath] = {
74+
// if scala version is no longer supported find the latest supported semanticdb version
75+
def versionFuture =
76+
Future {
77+
coursierapi.Complete
78+
.create()
79+
.withScalaVersion(scalaVersion)
80+
.withScalaBinaryVersion(scalaVersion.split('.').take(2).mkString("."))
81+
.withInput(s"org.scalameta:semanticdb-scalac_$scalaVersion:")
82+
.complete()
83+
.getCompletions()
84+
.asScala
85+
.lastOption
86+
}(ExecutionContext.ioScheduler)
87+
88+
def version =
89+
try
90+
Await.result(versionFuture, FiniteDuration(10, "s"))
91+
catch {
92+
case _: Throwable => None
93+
}
94+
95+
Option(supportedFallbackSemanticdbVersions.get(scalaVersion)).orElse(version) match {
96+
case None =>
97+
Left(s"After retry no existing semanticdb version found for scala version $scalaVersion")
98+
case Some(semanticdbVersion) =>
99+
supportedFallbackSemanticdbVersions.put(scalaVersion, semanticdbVersion)
100+
fetchPlugin(artifact.copy(version = semanticdbVersion), logger)
101+
}
102+
}
103+
60104
@volatile private var latestResolvedScalaSemanticDB: Path = null
61105
def fetchScalaPlugin(
62106
scalaVersion: String,
@@ -77,7 +121,16 @@ object SemanticDBCache {
77121
latestResolvedPlugin
78122
}
79123
}
80-
} else fetchPlugin(artifact, logger)
124+
} else {
125+
fetchPlugin(artifact, logger) match {
126+
case Right(plugin) => Right(plugin)
127+
case Left(error) =>
128+
fallbackSemanticdbFetch(scalaVersion, artifact, logger) match {
129+
case Left(newError) => Left(error + "\n" + newError)
130+
case Right(plugin) => Right(plugin)
131+
}
132+
}
133+
}
81134
}
82135

83136
@volatile private var latestResolvedJavaSemanticDB: Path = null

frontend/src/test/scala/bloop/bsp/BspMetalsClientSpec.scala

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -92,32 +92,6 @@ class BspMetalsClientSpec(
9292
}
9393
}
9494

95-
test("do not initialize metals client and save settings with unsupported scala version") {
96-
TestUtil.withinWorkspace { workspace =>
97-
val `A` = TestProject(workspace, "A", Nil, scalaVersion = Some("2.12.4"))
98-
val projects = List(`A`)
99-
val configDir = TestProject.populateWorkspace(workspace, projects)
100-
val logger = new RecordingLogger(ansiCodesSupported = false)
101-
val extraParams = BloopExtraBuildParams(
102-
ownsBuildFiles = None,
103-
clientClassesRootDir = None,
104-
semanticdbVersion = Some(semanticdbVersion), // Doesn't support 2.12.4
105-
supportedScalaVersions = Some(List(testedScalaVersion)),
106-
javaSemanticdbVersion = Some(javaSemanticdbVersion)
107-
)
108-
109-
loadBspState(workspace, projects, logger, "Metals", bloopExtraParams = extraParams) { state =>
110-
assertNoDiffInSettingsFile(
111-
configDir,
112-
expectedConfig
113-
)
114-
// Expect only range positions to be added, semanticdb is not supported
115-
assertScalacOptions(state, `A`, "-Yrangepos")
116-
assertNoDiff(logger.warnings.mkString(lineSeparator), "")
117-
}
118-
}
119-
}
120-
12195
test("initialize metals client in workspace with already enabled semanticdb") {
12296
TestUtil.withinWorkspace { workspace =>
12397
val pluginPath = s"-Xplugin:path-to-plugin/$semanticdbJar"
@@ -338,6 +312,40 @@ class BspMetalsClientSpec(
338312
}
339313
}
340314

315+
test("compile with old semanticDB") {
316+
TestUtil.withinWorkspace { workspace =>
317+
object JavacOptions {
318+
// This will cause to use the forked javac compiler, since addong any `-J` property causes it
319+
val A = List("-J-Xms48m")
320+
}
321+
322+
val `A` =
323+
TestProject(
324+
workspace,
325+
"A",
326+
dummyFooScalaAndBarJavaSources,
327+
javacOptions = JavacOptions.A,
328+
// this Scala version is not supported in the newest semanticdb
329+
scalaVersion = Some("2.12.8")
330+
)
331+
val projects = List(`A`)
332+
val configDir = TestProject.populateWorkspace(workspace, projects)
333+
val logger = new RecordingLogger(ansiCodesSupported = false)
334+
WorkspaceSettings.writeToFile(
335+
configDir,
336+
WorkspaceSettings
337+
.fromSemanticdbSettings("0.5.7", semanticdbVersion, List(testedScalaVersion)),
338+
logger
339+
)
340+
loadBspState(workspace, projects, logger) { state =>
341+
val compiledState = state.compile(`A`).toTestState
342+
assert(compiledState.status == ExitStatus.Ok)
343+
assertSemanticdbFileFor("Foo.scala", compiledState)
344+
assertSemanticdbFileFor("Bar.java", compiledState)
345+
}
346+
}
347+
}
348+
341349
test("compile with semanticDB using cached plugin") {
342350
TestUtil.withinWorkspace { workspace =>
343351
val `A` = TestProject(workspace, "A", dummyFooScalaAndBarJavaSources)

0 commit comments

Comments
 (0)