Skip to content

Commit 205dc61

Browse files
authored
Merge pull request #292 from olafurpg/sbt-support
Add build tool support for sbt.
2 parents 0f38d55 + c217534 commit 205dc61

File tree

18 files changed

+341
-56
lines changed

18 files changed

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

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(

0 commit comments

Comments
 (0)