Skip to content

Commit 7436772

Browse files
committed
Add build tool support for sbt.
Previously, the `lsif-java index` command only worked with Gradle and Maven builds. This commits adds support for sbt as well. The Scala community mostly uses sbt so this new feature is a significant milestone towards supporting Scala as well.
1 parent 0f38d55 commit 7436772

File tree

16 files changed

+294
-55
lines changed

16 files changed

+294
-55
lines changed

build.sbt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ lazy val V =
1717
def scala211 = "2.11.12"
1818
def scala3 = "3.0.1"
1919
def metals = "0.10.6-M1"
20-
def scalameta = "4.4.25"
20+
def scalameta = "4.4.26"
2121
def testcontainers = "0.39.3"
2222
def requests = "0.6.5"
2323
}
@@ -145,8 +145,12 @@ lazy val cli = project
145145
buildInfoKeys :=
146146
Seq[BuildInfoKey](
147147
version,
148+
sbtVersion,
148149
scalaVersion,
149-
"mtags" -> V.metals,
150+
"sbtSourcegraphVersion" ->
151+
com.sourcegraph.sbtsourcegraph.BuildInfo.version,
152+
"semanticdbVersion" -> V.scalameta,
153+
"mtagsVersion" -> V.metals,
150154
"scala211" -> V.scala211,
151155
"scala212" -> V.scala212,
152156
"scala213" -> V.scala213,
@@ -268,6 +272,7 @@ lazy val minimizedSettings = List[Def.Setting[_]](
268272
s"-Arandomtimestamp=${System.nanoTime()}",
269273
List(
270274
s"-Xplugin:semanticdb",
275+
s"-build-tool:sbt",
271276
s"-text:on",
272277
s"-verbose",
273278
s"-sourceroot:${(ThisBuild / baseDirectory).value}",
@@ -290,7 +295,11 @@ lazy val minimized8 = project
290295

291296
lazy val minimized15 = project
292297
.in(file("tests/minimized/.j15"))
293-
.settings(minimizedSettings, javaToolchainVersion := "15")
298+
.settings(
299+
minimizedSettings,
300+
javaToolchainVersion := "15",
301+
Compile / javaHome := None
302+
)
294303
.dependsOn(agent, plugin)
295304
.disablePlugins(JavaFormatterPlugin)
296305

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,53 @@
11
package com.sourcegraph.lsif_java.buildtools
22

3+
import java.nio.file.Files
34
import java.nio.file.Path
45

5-
import com.sourcegraph.io.AbsolutePath
66
import com.sourcegraph.lsif_java.commands.IndexCommand
7+
import com.sourcegraph.lsif_java.commands.IndexSemanticdbCommand
8+
import os.CommandResult
79

810
/**
911
* A build tool such as Gradle, Maven or Bazel.
1012
*/
1113
abstract class BuildTool(val name: String, index: IndexCommand) {
12-
13-
protected def defaultTargetroot: Path
14-
1514
def isHidden: Boolean = false
16-
def buildKind: String = ""
17-
1815
final def sourceroot: Path = index.workingDirectory
19-
final def targetroot: Path =
20-
AbsolutePath
21-
.of(index.targetroot.getOrElse(defaultTargetroot), index.workingDirectory)
22-
2316
def usedInCurrentDirectory(): Boolean
24-
25-
def generateSemanticdb(): os.CommandResult
26-
17+
def generateLsif(): Int
2718
}
2819

2920
object BuildTool {
3021
def all(index: IndexCommand): List[BuildTool] =
3122
List(
3223
new GradleBuildTool(index),
3324
new MavenBuildTool(index),
34-
new LsifBuildTool(index)
25+
new LsifBuildTool(index),
26+
new SbtBuildTool(index)
3527
)
3628
def allNames: String =
3729
all(IndexCommand()).filterNot(_.isHidden).map(_.name).mkString(", ")
30+
31+
def generateLsifFromTargetroot(
32+
generateSemanticdbResult: CommandResult,
33+
targetroot: Path,
34+
index: IndexCommand,
35+
buildKind: String = ""
36+
): Int = {
37+
if (!Files.isDirectory(targetroot)) {
38+
generateSemanticdbResult.exitCode
39+
} else if (index.app.reporter.hasErrors()) {
40+
index.app.reporter.exitCode()
41+
} else if (generateSemanticdbResult.exitCode != 0) {
42+
generateSemanticdbResult.exitCode
43+
} else {
44+
IndexSemanticdbCommand(
45+
output = index.finalOutput,
46+
targetroot = List(targetroot),
47+
packagehub = index.packagehub,
48+
buildKind = buildKind,
49+
app = index.app
50+
).run()
51+
}
52+
}
3853
}

lsif-java/src/main/scala/com/sourcegraph/lsif_java/buildtools/GradleBuildTool.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@ import os.CommandResult
1212

1313
class GradleBuildTool(index: IndexCommand) extends BuildTool("Gradle", index) {
1414

15-
override def defaultTargetroot: Path =
16-
Paths.get("build", "semanticdb-targetroot")
17-
1815
override def usedInCurrentDirectory(): Boolean = {
1916
Files.isRegularFile(index.workingDirectory.resolve("settings.gradle")) ||
2017
Files.isRegularFile(index.workingDirectory.resolve("gradlew")) ||
2118
Files.isRegularFile(index.workingDirectory.resolve("build.gradle")) ||
2219
Files.isRegularFile(index.workingDirectory.resolve("build.gradle.kts"))
2320
}
2421

25-
override def generateSemanticdb(): CommandResult = {
22+
override def generateLsif(): Int = {
23+
BuildTool
24+
.generateLsifFromTargetroot(generateSemanticdb(), targetroot, index)
25+
}
26+
27+
def targetroot: Path = index.finalTargetroot(defaultTargetroot)
28+
29+
private def defaultTargetroot: Path =
30+
Paths.get("build", "semanticdb-targetroot")
31+
private def generateSemanticdb(): CommandResult = {
2632
val gradleWrapper: Path = index
2733
.workingDirectory
2834
.resolve(

lsif-java/src/main/scala/com/sourcegraph/lsif_java/buildtools/LsifBuildTool.scala

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,25 @@ class LsifBuildTool(index: IndexCommand) extends BuildTool("LSIF", index) {
7676
.getDefault
7777
.getPathMatcher("glob:**.{java,scala}")
7878
private val moduleInfo = Paths.get("module-info.java")
79-
protected def defaultTargetroot: Path = Paths.get("target")
79+
80+
override def usedInCurrentDirectory(): Boolean =
81+
Files.isRegularFile(configFile)
82+
override def isHidden: Boolean = true
83+
override def generateLsif(): Int = {
84+
BuildTool.generateLsifFromTargetroot(
85+
generateSemanticdb(),
86+
index.finalTargetroot(defaultTargetroot),
87+
index,
88+
buildKind
89+
)
90+
}
91+
92+
private def targetroot: Path = index.finalTargetroot(defaultTargetroot)
93+
private def defaultTargetroot: Path = Paths.get("target")
8094
private def configFile =
8195
index.workingDirectory.resolve(LsifBuildTool.ConfigFileName)
82-
def usedInCurrentDirectory(): Boolean = Files.isRegularFile(configFile)
83-
override def isHidden: Boolean = true
84-
override def buildKind: String =
85-
parsedConfig.fold(_.kind, _ => super.buildKind)
86-
def generateSemanticdb(): CommandResult = {
96+
private def buildKind: String = parsedConfig.fold(_.kind, _ => "")
97+
private def generateSemanticdb(): CommandResult = {
8798
parsedConfig match {
8899
case ValueResult(value) =>
89100
clean()
@@ -224,7 +235,7 @@ class LsifBuildTool(index: IndexCommand) extends BuildTool("LSIF", index) {
224235
)
225236
}
226237
val mtags = Dependencies.resolveDependencies(
227-
List(s"org.scalameta:mtags_${scalaVersion}:${BuildInfo.mtags}")
238+
List(s"org.scalameta:mtags_${scalaVersion}:${BuildInfo.mtagsVersion}")
228239
)
229240
val scalaLibrary = mtags
230241
.classpath

lsif-java/src/main/scala/com/sourcegraph/lsif_java/buildtools/MavenBuildTool.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@ import com.sourcegraph.lsif_java.commands.IndexCommand
1111
import os.CommandResult
1212

1313
class MavenBuildTool(index: IndexCommand) extends BuildTool("Maven", index) {
14-
override def defaultTargetroot: Path =
15-
Paths.get("target", "semanticdb-targetroot")
14+
1615
override def usedInCurrentDirectory(): Boolean =
1716
Files.isRegularFile(index.workingDirectory.resolve("pom.xml"))
1817

19-
override def generateSemanticdb(): CommandResult = {
18+
override def generateLsif(): Int = {
19+
BuildTool.generateLsifFromTargetroot(
20+
generateSemanticdb(),
21+
index.finalTargetroot(defaultTargetroot),
22+
index
23+
)
24+
}
25+
26+
private def defaultTargetroot: Path =
27+
Paths.get("target", "semanticdb-targetroot")
28+
29+
private def generateSemanticdb(): CommandResult = {
2030
TemporaryFiles.withDirectory(index) { tmp =>
2131
val mvnw = index.workingDirectory.resolve("mvnw")
2232
val mavenScript =
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.sourcegraph.lsif_java.buildtools
2+
3+
import java.nio.file.Files
4+
import java.nio.file.StandardCopyOption
5+
import java.util.Properties
6+
7+
import scala.util.Using
8+
9+
import com.sourcegraph.lsif_java.BuildInfo
10+
import com.sourcegraph.lsif_java.commands.IndexCommand
11+
12+
class SbtBuildTool(index: IndexCommand) extends BuildTool("sbt", index) {
13+
override def usedInCurrentDirectory(): Boolean = {
14+
Files.isRegularFile(index.workingDirectory.resolve("build.sbt")) ||
15+
sbtVersion().isDefined
16+
}
17+
18+
override def generateLsif(): Int = {
19+
sbtVersion() match {
20+
case Some(version) =>
21+
if (isSupportedSbtVersion(version)) {
22+
unconditionallyGenerateLsif()
23+
} else {
24+
failFast(
25+
s"Unsupported sbt version '$version'. " +
26+
s"To fix this problem, upgrade to sbt ${BuildInfo.sbtVersion} and try again."
27+
)
28+
}
29+
case None =>
30+
failFast(
31+
s"No sbt version detected. " +
32+
s"To fix this problem, run the following command and try again: " +
33+
s"echo 'sbt.version=${BuildInfo.sbtVersion}' >> project/build.properties"
34+
)
35+
}
36+
}
37+
38+
private def failFast(message: String): Int = {
39+
index.app.error(message)
40+
1
41+
}
42+
43+
private def unconditionallyGenerateLsif(): Int =
44+
Using.resource(sourcegraphSbtPluginFile()) { _ =>
45+
val sourcegraphLsif = index
46+
.process(List("sbt", "sourcegraphEnable", "sourcegraphLsif"))
47+
val inputDump = index
48+
.workingDirectory
49+
.resolve("target")
50+
.resolve("sbt-sourcegraph")
51+
.resolve("dump.lsif")
52+
if (sourcegraphLsif.exitCode == 0 && Files.isRegularFile(inputDump)) {
53+
val outputDump = index.workingDirectory.resolve("dump.lsif")
54+
Files.copy(inputDump, outputDump, StandardCopyOption.REPLACE_EXISTING)
55+
index.app.info(outputDump.toString)
56+
}
57+
sourcegraphLsif.exitCode
58+
}
59+
60+
private def isSupportedSbtVersion(version: String): Boolean = {
61+
(!version.startsWith("0.13") || version.startsWith("0.13.17")) &&
62+
!version.startsWith("1.0") && !version.startsWith("1.1")
63+
}
64+
65+
private def sbtVersion(): Option[String] = {
66+
val buildProperties = index
67+
.workingDirectory
68+
.resolve("project")
69+
.resolve("build.properties")
70+
if (Files.isRegularFile(buildProperties)) {
71+
val props = new Properties()
72+
val in = Files.newInputStream(buildProperties)
73+
try props.load(in)
74+
finally in.close()
75+
Option(props.getProperty("sbt.version"))
76+
} else {
77+
None
78+
}
79+
}
80+
81+
private def sourcegraphSbtPluginFile(): AutoDeletedFile = {
82+
val addSbtPluginFile = index
83+
.workingDirectory
84+
.resolve("project")
85+
.resolve("sourcegraph_generated.sbt")
86+
val version = BuildInfo.sbtSourcegraphVersion
87+
AutoDeletedFile.fromPath(
88+
addSbtPluginFile,
89+
s"""addSbtPlugin("com.sourcegraph" % "sbt-sourcegraph" % "$version")
90+
|""".stripMargin
91+
)
92+
}
93+
}

lsif-java/src/main/scala/com/sourcegraph/lsif_java/commands/IndexCommand.scala

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,16 @@ case class IndexCommand(
6666
shellable: Shellable,
6767
env: Map[String, String] = Map.empty
6868
): CommandResult = {
69-
app.out.println(Color.DarkGray(shellable.value.mkString("$ ", " ", "")))
69+
val commandSyntax = shellable
70+
.value
71+
.map { line =>
72+
if (line.contains(" "))
73+
s"""'$line'"""
74+
else
75+
line
76+
}
77+
.mkString("$ ", " ", "")
78+
app.out.println(Color.DarkGray(commandSyntax))
7079
app
7180
.process(shellable)
7281
.call(
@@ -149,22 +158,7 @@ case class IndexCommand(
149158
}
150159
1
151160
case tool :: Nil =>
152-
val generateSemanticdbResult = tool.generateSemanticdb()
153-
if (!Files.isDirectory(tool.targetroot)) {
154-
generateSemanticdbResult.exitCode
155-
} else if (app.reporter.hasErrors()) {
156-
app.reporter.exitCode()
157-
} else if (generateSemanticdbResult.exitCode != 0) {
158-
generateSemanticdbResult.exitCode
159-
} else {
160-
IndexSemanticdbCommand(
161-
output = finalOutput,
162-
targetroot = List(tool.targetroot),
163-
packagehub = packagehub,
164-
buildKind = tool.buildKind,
165-
app = app
166-
).run()
167-
}
161+
tool.generateLsif()
168162
case many =>
169163
val names = many.map(_.name).mkString(", ")
170164
app.error(

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifSemanticdb.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.sourcegraph.lsif_semanticdb;
22

3+
import com.google.protobuf.CodedInputStream;
34
import com.sourcegraph.lsif_protocol.MarkupKind;
45
import com.sourcegraph.semanticdb_javac.Semanticdb;
56
import com.sourcegraph.semanticdb_javac.Semanticdb.SymbolInformation;
@@ -188,11 +189,13 @@ private Integer processDocumentUnsafe(
188189

189190
private Stream<LsifTextDocument> parseTextDocument(Path semanticdbPath) {
190191
try {
191-
return Semanticdb.TextDocuments.parseFrom(Files.readAllBytes(semanticdbPath))
192-
.getDocumentsList().stream()
192+
CodedInputStream in = CodedInputStream.newInstance(Files.readAllBytes(semanticdbPath));
193+
in.setRecursionLimit(1000);
194+
return Semanticdb.TextDocuments.parseFrom(in).getDocumentsList().stream()
193195
.filter(sdb -> !sdb.getOccurrencesList().isEmpty())
194196
.map(sdb -> new LsifTextDocument(semanticdbPath, sdb, options.sourceroot));
195197
} catch (IOException e) {
198+
options.reporter.error("invalid protobuf: " + semanticdbPath);
196199
options.reporter.error(e);
197200
return Stream.empty();
198201
}

0 commit comments

Comments
 (0)