Skip to content

Commit

Permalink
Persist sbt metadata in project model
Browse files Browse the repository at this point in the history
Add the sbt metadata field in the project configuration file so that we
can present it to IDEs via BSP. A nice property of the approach taken in
this PR is that we automatically create a project file for the build
under the meta build `.bloop` directory. This is helpful for two
reasons:

1. Bloop can compile your build just as it can compile your project.
2. Bloop knows immediately any change that happens to your build.

The implementation here proposed is quite tricky because of some sbt
implementation details. We cannot get the `autoImports` from the build
of the build, so we need to roll out our own piece of code to do so with
the `pluginData` of the meta build. This repeats some work because it
needs to classload the build data for our project's build, but it's
negligible in terms of time spent (<500 ms in the cases I've tested).

Aside from implementing this feature, this commit also removes any cross
published module and instead creates a static module for it. This helps
development because we can compile the projects for 0.13 and 1.0
immediately without needing to use `+` and `^`, which are completely
broken in our build.
  • Loading branch information
jvican committed Jun 12, 2018
1 parent bff14ec commit b33640d
Show file tree
Hide file tree
Showing 25 changed files with 202 additions and 75 deletions.
5 changes: 3 additions & 2 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
matrix:
SBT_RUN:
- sbt "benchmarks/compile" \
"+jsonConfig/test" \
"jsonConfig210/test" \
"jsonConfig212/test" \
"frontend/integrationSetUpBloop" \
"backend/test" \
"nativeBridge/test" \
Expand All @@ -12,7 +13,7 @@ matrix:
"docs/makeSite"

SBT_RUN_BENCHMARKS_AND_SCRIPTED:
- sbt "sbtBloop/scripted" \
- sbt "sbtBloop10/scripted" \
"benchmarks/jmh:run .*HotBloopBenchmark.* -wi 0 -i 1 -f1 -t1 -p project=with-tests -p projectName=with-tests"

SBT_PUBLISH:
Expand Down
92 changes: 53 additions & 39 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -47,40 +47,52 @@ val backend = project
)
)

val jsonConfig210 = project
.in(file("config"))
.disablePlugins(ScriptedPlugin)
.settings(testSettings)
.settings(
name := "bloop-config",
target := file("config") / "target" / "json-config-2.10",
scalaVersion := "2.10.7",
// We compile in both so that the maven integration can be tested locally
publishLocal := publishLocal.dependsOn(publishM2).value,
libraryDependencies ++= {
List(
Dependencies.circeParser,
Dependencies.circeCore,
Dependencies.circeGeneric,
compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full),
Dependencies.scalacheck % Test,
)
}
)

// Needs to be called `jsonConfig` because of naming conflict with sbt universe...
val jsonConfig = project
val jsonConfig212 = project
.in(file("config"))
.disablePlugins(ScriptedPlugin)
.settings(testSettings)
.settings(
name := "bloop-config",
crossScalaVersions := List(Keys.scalaVersion.in(backend).value, "2.10.7"),
target := file("config") / "target" / "json-config-2.12",
scalaVersion := Keys.scalaVersion.in(backend).value,
// We compile in both so that the maven integration can be tested locally
publishLocal := publishLocal.dependsOn(publishM2).value,
libraryDependencies ++= {
if (scalaBinaryVersion.value == "2.12") {
List(
Dependencies.circeParser,
Dependencies.circeDerivation,
Dependencies.nuprocess,
Dependencies.scalacheck % Test,
)
} else {
List(
Dependencies.circeParser,
Dependencies.circeCore,
Dependencies.circeGeneric,
compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full),
Dependencies.scalacheck % Test,
)
}
List(
Dependencies.circeParser,
Dependencies.circeDerivation,
Dependencies.nuprocess,
Dependencies.scalacheck % Test,
)
}
)

import build.BuildImplementation.jvmOptions
// For the moment, the dependency is fixed
lazy val frontend: Project = project
.dependsOn(backend, backend % "test->test", jsonConfig)
.dependsOn(backend, backend % "test->test", jsonConfig212)
.disablePlugins(ScriptedPlugin)
.enablePlugins(BuildInfoPlugin)
.settings(testSettings, assemblySettings, releaseSettings, integrationTestSettings)
Expand Down Expand Up @@ -114,33 +126,32 @@ val benchmarks = project
skip in publish := true,
)

lazy val sbtBloop = project
lazy val sbtBloop10 = project
.enablePlugins(ScriptedPlugin)
.in(file("integrations") / "sbt-bloop")
.dependsOn(jsonConfig)
.settings(BuildDefaults.scriptedSettings)
.settings(
name := "sbt-bloop",
sbtPlugin := true,
scalaVersion := BuildDefaults.fixScalaVersionForSbtPlugin.value,
bintrayPackage := "sbt-bloop",
bintrayOrganization := Some("sbt"),
bintrayRepository := "sbt-plugin-releases",
publishMavenStyle := releaseEarlyWith.value == SonatypePublisher,
publishLocal := publishLocal.dependsOn(publishLocal in jsonConfig).value
)
.settings(sbtPluginSettings("1.1.4", jsonConfig212))
.dependsOn(jsonConfig212)

// Let's remove scripted for 0.13, we only test 1.0
lazy val sbtBloop013 = project
.disablePlugins(ScriptedPlugin)
.in(file("integrations") / "sbt-bloop")
.settings(scalaVersion := Keys.scalaVersion.in(jsonConfig210).value)
.settings(sbtPluginSettings("0.13.17", jsonConfig210))
.dependsOn(jsonConfig210)

val mavenBloop = project
.in(file("integrations") / "maven-bloop")
.disablePlugins(ScriptedPlugin)
.dependsOn(jsonConfig)
.dependsOn(jsonConfig212)
.settings(name := "maven-bloop")
.settings(BuildDefaults.mavenPluginBuildSettings)

val millBloop = project
.in(file("integrations") / "mill-bloop")
.disablePlugins(ScriptedPlugin)
.dependsOn(jsonConfig)
.dependsOn(jsonConfig212)
.settings(name := "mill-bloop")
.settings(BuildDefaults.millModuleBuildSettings)

Expand Down Expand Up @@ -188,7 +199,7 @@ lazy val nativeBridge = project
)

val allProjects =
Seq(backend, benchmarks, frontend, jsonConfig, sbtBloop, mavenBloop, millBloop, nativeBridge, jsBridge06, jsBridge10)
Seq(backend, benchmarks, frontend, jsonConfig210, jsonConfig212, sbtBloop013, sbtBloop10, mavenBloop, millBloop, nativeBridge, jsBridge06, jsBridge10)
val allProjectReferences = allProjects.map(p => LocalProject(p.id))
val bloop = project
.in(file("."))
Expand All @@ -210,8 +221,10 @@ val publishLocalCmd = Keys.publishLocal.key.label
addCommandAlias(
"install",
Seq(
s"+${jsonConfig.id}/$publishLocalCmd",
s"^${sbtBloop.id}/$publishLocalCmd",
s"${jsonConfig210.id}/$publishLocalCmd",
s"${jsonConfig212.id}/$publishLocalCmd",
s"${sbtBloop013.id}/$publishLocalCmd",
s"${sbtBloop10.id}/$publishLocalCmd",
s"${mavenBloop.id}/$publishLocalCmd",
s"${backend.id}/$publishLocalCmd",
s"${frontend.id}/$publishLocalCmd",
Expand All @@ -226,9 +239,10 @@ val releaseEarlyCmd = releaseEarly.key.label
val allBloopReleases = List(
s"${backend.id}/$releaseEarlyCmd",
s"${frontend.id}/$releaseEarlyCmd",
s"+${jsonConfig.id}/$publishLocalCmd", // Necessary because of a coursier bug?
s"+${jsonConfig.id}/$releaseEarlyCmd",
s"^${sbtBloop.id}/$releaseEarlyCmd",
s"${jsonConfig210.id}/$releaseEarlyCmd",
s"${jsonConfig212.id}/$releaseEarlyCmd",
s"${sbtBloop013.id}/$releaseEarlyCmd",
s"${sbtBloop10.id}/$releaseEarlyCmd",
s"${mavenBloop.id}/$releaseEarlyCmd",
s"${nativeBridge.id}/$releaseEarlyCmd",
s"${jsBridge06.id}/$releaseEarlyCmd",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ object ConfigEncoderDecoders {
implicit val scalaEncoder: ObjectEncoder[Scala] = deriveEncoder
implicit val scalaDecoder: Decoder[Scala] = deriveDecoder

implicit val sbtEncoder: ObjectEncoder[Sbt] = deriveEncoder
implicit val sbtDecoder: Decoder[Sbt] = deriveDecoder

implicit val projectEncoder: ObjectEncoder[Project] = deriveEncoder
implicit val projectDecoder: Decoder[Project] = deriveDecoder

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ object ConfigEncoderDecoders {
implicit val scalaEncoder: ObjectEncoder[Scala] = deriveEncoder
implicit val scalaDecoder: Decoder[Scala] = deriveDecoder

implicit val sbtEncoder: ObjectEncoder[Sbt] = deriveEncoder
implicit val sbtDecoder: Decoder[Sbt] = deriveDecoder

implicit val projectEncoder: ObjectEncoder[Project] = deriveEncoder
implicit val projectDecoder: Decoder[Project] = deriveDecoder

Expand Down
13 changes: 12 additions & 1 deletion config/src/main/scala/bloop/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ object Config {
case class Test(frameworks: Array[TestFramework], options: TestOptions)
object Test { private[bloop] val empty = Test(Array(), TestOptions.empty) }

case class Sbt(
sbtVersion: String,
autoImports: List[String]
)

object Sbt {
private[bloop] val empty = Sbt("", Nil)
}

sealed abstract class CompileOrder(val id: String)
case object Mixed extends CompileOrder("mixed")
case object JavaThenScala extends CompileOrder("java->scala")
Expand Down Expand Up @@ -180,6 +189,7 @@ object Config {
classesDir: Path,
`scala`: Scala,
java: Java,
sbt: Sbt,
test: Test,
platform: Platform,
compileSetup: CompileSetup,
Expand All @@ -188,7 +198,7 @@ object Config {

object Project {
// FORMAT: OFF
private[bloop] val empty: Project = Project("", emptyPath, Array(), Array(), Array(), emptyPath, emptyPath, emptyPath, Scala.empty, Java.empty, Test.empty, Platform.default, CompileSetup.empty, Resolution.empty)
private[bloop] val empty: Project = Project("", emptyPath, Array(), Array(), Array(), emptyPath, emptyPath, emptyPath, Scala.empty, Java.empty, Sbt.empty, Test.empty, Platform.default, CompileSetup.empty, Resolution.empty)
// FORMAT: ON

def analysisFileName(projectName: String) = s"$projectName-analysis.bin"
Expand Down Expand Up @@ -231,6 +241,7 @@ object Config {
outAnalysisFile,
Scala("org.scala-lang", "scala-compiler", "2.12.4", Array("-warn"), Array()),
Java(Array("-version")),
Sbt("1.1.0", Nil),
Test(Array(), TestOptions(Nil, Nil)),
platform,
CompileSetup.empty,
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/main/scala/bloop/Project.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ final case class Project(
analysisOut: AbsolutePath,
platform: Platform,
jsToolchain: Option[ScalaJsToolchain],
nativeToolchain: Option[ScalaNativeToolchain]
nativeToolchain: Option[ScalaNativeToolchain],
sbt: Option[Config.Sbt]
) {
override def toString: String = s"$name"
override val hashCode: Int = scala.util.hashing.MurmurHash3.productHash(this)
Expand Down Expand Up @@ -133,6 +134,11 @@ object Project {
case _ => JavaEnv.default
}

val sbt = project.sbt match {
case Config.Sbt.empty => None
case config => Some(config)
}

Project(
project.name,
AbsolutePath(project.directory),
Expand All @@ -151,7 +157,8 @@ object Project {
AbsolutePath(project.analysisOut),
project.platform,
jsToolchain,
nativeToolchain
nativeToolchain,
sbt
)
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/test/scala/bloop/engine/DagSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DagSpec {
def dummyProject(name: String, dependencies: List[String]): Project =
Project(name, dummyPath, dependencies.toArray, dummyInstance, Array(), classpathOptions, dummyPath, Array(),
Array(), Array(), Array(), Config.TestOptions.empty, JavaEnv.default, dummyPath, dummyPath,
Config.Platform.default, None, None)
Config.Platform.default, None, None, None)
// format: ON

private object TestProjects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ class IntegrationTestSuite(testDirectory: Path) {
analysisOut = classesDir.resolve(Config.Project.analysisFileName(rootProjectName)),
platform = Config.Platform.default,
jsToolchain = None,
nativeToolchain = None
nativeToolchain = None,
sbt = None
)
val state =
state0.copy(build = state0.build.copy(projects = rootProject :: previousProjects))
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/test/scala/bloop/tasks/TestUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ object TestUtil {
analysisOut = AbsolutePath(target.resolve(Config.Project.analysisFileName(name))),
platform = Config.Platform.default,
jsToolchain = None,
nativeToolchain = None
nativeToolchain = None,
sbt = None
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,15 @@ object MojoImplementation {

// FORMAT: OFF
val config = {
val sbt = Config.Sbt.empty
val test = Config.Test(testFrameworks, DefaultTestOptions)
val java = Config.Java(mojo.getJavacArgs().asScala.toArray)
val `scala` = Config.Scala(mojo.getScalaOrganization(), mojo.getScalaArtifactID(),
mojo.getScalaVersion(), scalacArgs, allScalaJars)
val javaHome = Some(abs(mojo.getJavaHome().getParentFile.getParentFile))
val platform = Config.Platform.Jvm(Config.JvmConfig(javaHome, launcher.getJvmArgs().toList))
val resolution = Config.Resolution.empty
val project = Config.Project(name, baseDirectory, sourceDirs, dependencyNames, classpath, out, analysisOut, classesDir, `scala`, java, test, platform, compileSetup, resolution)
val project = Config.Project(name, baseDirectory, sourceDirs, dependencyNames, classpath, out, analysisOut, classesDir, `scala`, java, sbt, test, platform, compileSetup, resolution)
Config.File(Config.File.LatestVersion, project)
}
// FORMAT: ON
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ object Bloop extends ExternalModule {
classesDir = classes(module).toNIO,
`scala` = scalaConfig(),
java = javaConfig(),
sbt = Config.Sbt.empty,
test = testConfig(),
platform = platform(),
compileSetup = compileSetup(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import sbt.{Def, Artifact, Keys, SettingKey}
import java.io.File

object Compat {
type PluginData = sbt.PluginData
val PluginData = sbt.PluginData
val PluginDiscovery = sbt.PluginDiscovery
val PluginManagement = sbt.PluginManagement

implicit def fileToRichFile(file: File): sbt.RichFile = new sbt.RichFile(file)

def generateCacheFile(s: sbt.Keys.TaskStreams, id: String) = s.cacheDirectory / id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import sbt.{Artifact, Exec, Keys, SettingKey}
import sbt.librarymanagement.ScalaModuleInfo

object Compat {
type PluginData = sbt.PluginData
val PluginData = sbt.PluginData
val PluginDiscovery = sbt.internal.PluginDiscovery
val PluginManagement = sbt.internal.PluginManagement

implicit class WithIvyScala(keys: Keys.type) {
def ivyScala: SettingKey[Option[ScalaModuleInfo]] = keys.scalaModuleInfo
}
Expand Down
Loading

0 comments on commit b33640d

Please sign in to comment.