From aa3374be29bed39ff760dbbbadbfac5bd9ea8fbc Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 12 Jun 2018 19:32:18 +0200 Subject: [PATCH] Persist sbt metadata in project model 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. --- .drone.yml | 5 +- build.sbt | 92 +++++++++++-------- .../bloop/config/ConfigEncoderDecoders.scala | 3 + .../bloop/config/ConfigEncoderDecoders.scala | 3 + .../src/main/scala/bloop/config/Config.scala | 13 ++- frontend/src/main/scala/bloop/Project.scala | 11 ++- .../src/test/scala/bloop/engine/DagSpec.scala | 2 +- .../bloop/tasks/IntegrationTestSuite.scala | 3 +- .../src/test/scala/bloop/tasks/TestUtil.scala | 3 +- .../maven/MojoImplementation.scala | 3 +- .../bloop/integrations/mill/MillBloop.scala | 1 + .../bloop/integrations/sbt/Compat.scala | 5 + .../bloop/integrations/sbt/Compat.scala | 5 + .../bloop/integrations/sbt/SbtBloop.scala | 54 ++++++++--- .../build.sbt | 1 + .../changes/export-jars.sbt | 1 + .../project/plugins.sbt | 4 +- .../cross-compile-test-configurations/test | 3 +- .../sbt-test/sbt-bloop/meta-builds/build.sbt | 22 ++++- .../meta-builds/changes/set-sources.sbt | 2 +- .../sbt-bloop/meta-builds/project/plugins.sbt | 2 + .../src/sbt-test/sbt-bloop/meta-builds/test | 4 +- project/BuildPlugin.scala | 19 +++- project/build.sbt | 4 +- project/project/build.sbt | 12 +++ 25 files changed, 202 insertions(+), 75 deletions(-) create mode 100644 integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/changes/export-jars.sbt create mode 100644 integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/project/plugins.sbt create mode 100644 project/project/build.sbt diff --git a/.drone.yml b/.drone.yml index f9f0de2a35..90c48b0b51 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,7 +3,8 @@ matrix: SBT_RUN: - sbt "benchmarks/compile" \ - "+jsonConfig/test" \ + "jsonConfig210/test" \ + "jsonConfig212/test" \ "frontend/integrationSetUpBloop" \ "backend/test" \ "nativeBridge/test" \ @@ -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: diff --git a/build.sbt b/build.sbt index 7c2ba2da09..6bf3e4378d 100644 --- a/build.sbt +++ b/build.sbt @@ -47,40 +47,51 @@ 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").getAbsoluteFile, + 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").getAbsoluteFile, + 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.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) @@ -100,6 +111,7 @@ lazy val frontend: Project = project Dependencies.bsp, Dependencies.monix, Dependencies.caseApp, + Dependencies.nuprocess, Dependencies.ipcsocket % Test ), dependencyOverrides += Dependencies.shapeless @@ -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) @@ -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(".")) @@ -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", @@ -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", diff --git a/config/src/main/scala-2.10/bloop/config/ConfigEncoderDecoders.scala b/config/src/main/scala-2.10/bloop/config/ConfigEncoderDecoders.scala index 0cf5cb72ca..306db462c5 100644 --- a/config/src/main/scala-2.10/bloop/config/ConfigEncoderDecoders.scala +++ b/config/src/main/scala-2.10/bloop/config/ConfigEncoderDecoders.scala @@ -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 diff --git a/config/src/main/scala-2.12/bloop/config/ConfigEncoderDecoders.scala b/config/src/main/scala-2.12/bloop/config/ConfigEncoderDecoders.scala index c59d23037d..c1e7c0654b 100644 --- a/config/src/main/scala-2.12/bloop/config/ConfigEncoderDecoders.scala +++ b/config/src/main/scala-2.12/bloop/config/ConfigEncoderDecoders.scala @@ -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 diff --git a/config/src/main/scala/bloop/config/Config.scala b/config/src/main/scala/bloop/config/Config.scala index 8f909cff0a..e166f8e5c9 100644 --- a/config/src/main/scala/bloop/config/Config.scala +++ b/config/src/main/scala/bloop/config/Config.scala @@ -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") @@ -180,6 +189,7 @@ object Config { classesDir: Path, `scala`: Scala, java: Java, + sbt: Sbt, test: Test, platform: Platform, compileSetup: CompileSetup, @@ -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" @@ -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, diff --git a/frontend/src/main/scala/bloop/Project.scala b/frontend/src/main/scala/bloop/Project.scala index d1c4915b5e..4e0fbaf863 100644 --- a/frontend/src/main/scala/bloop/Project.scala +++ b/frontend/src/main/scala/bloop/Project.scala @@ -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) @@ -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), @@ -151,7 +157,8 @@ object Project { AbsolutePath(project.analysisOut), project.platform, jsToolchain, - nativeToolchain + nativeToolchain, + sbt ) } diff --git a/frontend/src/test/scala/bloop/engine/DagSpec.scala b/frontend/src/test/scala/bloop/engine/DagSpec.scala index eb80456b2a..96ad93cc24 100644 --- a/frontend/src/test/scala/bloop/engine/DagSpec.scala +++ b/frontend/src/test/scala/bloop/engine/DagSpec.scala @@ -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 { diff --git a/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala b/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala index 484cf51617..75d622dec0 100644 --- a/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala +++ b/frontend/src/test/scala/bloop/tasks/IntegrationTestSuite.scala @@ -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)) diff --git a/frontend/src/test/scala/bloop/tasks/TestUtil.scala b/frontend/src/test/scala/bloop/tasks/TestUtil.scala index e94e3c0ba0..1801567290 100644 --- a/frontend/src/test/scala/bloop/tasks/TestUtil.scala +++ b/frontend/src/test/scala/bloop/tasks/TestUtil.scala @@ -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 ) } diff --git a/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala b/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala index b1ed328db9..9e82551ae3 100644 --- a/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala +++ b/integrations/maven-bloop/src/main/scala/bloop/integrations/maven/MojoImplementation.scala @@ -123,6 +123,7 @@ 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(), @@ -130,7 +131,7 @@ object MojoImplementation { 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 diff --git a/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala b/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala index 268e6a99b6..71bb08b43d 100644 --- a/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala +++ b/integrations/mill-bloop/src/main/scala/bloop/integrations/mill/MillBloop.scala @@ -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(), diff --git a/integrations/sbt-bloop/src/main/scala-sbt-0.13/bloop/integrations/sbt/Compat.scala b/integrations/sbt-bloop/src/main/scala-sbt-0.13/bloop/integrations/sbt/Compat.scala index 5a0fb6f022..6f85079a1f 100644 --- a/integrations/sbt-bloop/src/main/scala-sbt-0.13/bloop/integrations/sbt/Compat.scala +++ b/integrations/sbt-bloop/src/main/scala-sbt-0.13/bloop/integrations/sbt/Compat.scala @@ -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 diff --git a/integrations/sbt-bloop/src/main/scala-sbt-1.0/bloop/integrations/sbt/Compat.scala b/integrations/sbt-bloop/src/main/scala-sbt-1.0/bloop/integrations/sbt/Compat.scala index 8a07e0b94c..5ae2e3b414 100644 --- a/integrations/sbt-bloop/src/main/scala-sbt-1.0/bloop/integrations/sbt/Compat.scala +++ b/integrations/sbt-bloop/src/main/scala-sbt-1.0/bloop/integrations/sbt/Compat.scala @@ -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 } diff --git a/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala b/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala index a6615a59ca..8fc2dd7c64 100644 --- a/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala +++ b/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala @@ -18,7 +18,6 @@ import sbt.{ Keys, LocalRootProject, Logger, - Project, ProjectRef, ResolvedProject, Test, @@ -47,8 +46,9 @@ object BloopKeys { settingKey[Boolean]("Is this a meta build?") val bloopAggregateSourceDependencies: SettingKey[Boolean] = settingKey[Boolean]("Flag to tell bloop to aggregate bloop config files in the same bloop dir.") - val bloopExportSourceAndDocJars: SettingKey[Boolean] = - settingKey[Boolean]("Export the source and javadoc jars to the bloop configuration file") + val bloopExportJarClassifiers: SettingKey[Option[Set[String]]] = + settingKey[Option[Set[String]]]( + "The classifiers that will be exported with `updateClassifiers`.") val bloopProductDirectories: TaskKey[Seq[File]] = taskKey[Seq[File]]("Bloop product directories") val bloopManagedResourceDirectories: SettingKey[Seq[File]] = @@ -76,16 +76,22 @@ object BloopDefaults { import sbt.{Task, Defaults, State} lazy val globalSettings: Seq[Def.Setting[_]] = List( + BloopKeys.bloopExportJarClassifiers := None, BloopKeys.bloopInstall := bloopInstall.value, BloopKeys.bloopAggregateSourceDependencies := false, + // Override classifiers so that we don't resolve always docs + Keys.transitiveClassifiers in Keys.updateClassifiers := { + val old = (Keys.transitiveClassifiers in Keys.updateClassifiers).value + val bloopClassifiers = BloopKeys.bloopExportJarClassifiers.in(ThisBuild).value + (if (bloopClassifiers.isEmpty) old else bloopClassifiers.get).toList + }, BloopKeys.bloopIsMetaBuild := Keys.sbtPlugin.in(LocalRootProject).value, Keys.onLoad := { - { (state: State) => + val oldOnLoad = Keys.onLoad.value + oldOnLoad.andThen { state => val isMetaBuild = BloopKeys.bloopIsMetaBuild.value - if (isMetaBuild) { - val extracted = Project.extract(state) - runCommandAndRemaining("bloopInstall")(state) - } else state + if (!isMetaBuild) state + else runCommandAndRemaining("bloopInstall")(state) } } ) @@ -111,7 +117,8 @@ object BloopDefaults { // We create build setting proxies to global settings so that we get autocompletion (sbt bug) lazy val buildSettings: Seq[Def.Setting[_]] = List( BloopKeys.bloopInstall := BloopKeys.bloopInstall.in(Global).value, - BloopKeys.bloopExportSourceAndDocJars := false, + // Repeat definition so that sbt shows autocopmletion for these settings + BloopKeys.bloopExportJarClassifiers := BloopKeys.bloopExportJarClassifiers.in(Global).value, // Bloop users: Do NEVER override this setting as a user if you want it to work BloopKeys.bloopAggregateSourceDependencies := BloopKeys.bloopAggregateSourceDependencies.in(Global).value @@ -426,8 +433,9 @@ object BloopDefaults { } lazy val updateClassifiers: Def.Initialize[Task[Option[sbt.UpdateReport]]] = Def.taskDyn { - val runUpdateClassifiers = BloopKeys.bloopExportSourceAndDocJars.value + val runUpdateClassifiers = BloopKeys.bloopExportJarClassifiers.value.nonEmpty if (!runUpdateClassifiers) Def.task(None) + else if (BloopKeys.bloopIsMetaBuild.value) Def.task(Some(Keys.updateSbtClassifiers.value)) else Def.task(Some(Keys.updateClassifiers.value)) } @@ -511,6 +519,27 @@ object BloopDefaults { // FORMAT: ON } + case class SbtMetadata(base: File, config: Config.Sbt) + lazy val computeSbtMetadata: Def.Initialize[Task[Option[SbtMetadata]]] = Def.taskDyn { + val isMetaBuild = BloopKeys.bloopIsMetaBuild.value + if (!isMetaBuild) Def.task(None) + else { + Def.task { + val structure = Keys.buildStructure.value + val buildUnit = structure.units(structure.root) + val sbtVersion = Keys.sbtVersion.value + val plugins = buildUnit.unit.plugins + val data = Keys.pluginData.value + + // We do what sbt.Load does here: discover all plugins to find out the auto imports + val initialPluginLoader = new PluginManagement.PluginClassLoader(plugins.loader) + initialPluginLoader.add(data.classpath.map(_.data.toURI.toURL)) + val autoImports = PluginDiscovery.discoverAll(data, initialPluginLoader).imports.toList + Some(SbtMetadata(plugins.base, Config.Sbt(sbtVersion, autoImports))) + } + } + } + lazy val bloopGenerate: Def.Initialize[Task[Option[File]]] = Def.taskDyn { val logger = Keys.streams.value.log val project = Keys.thisProject.value @@ -614,9 +643,10 @@ object BloopDefaults { val c = Keys.classpathOptions.value val compileSetup = Config.CompileSetup(compileOrder, c.bootLibrary, c.compiler, c.extra, c.autoBoot, c.filterLibrary) val analysisOut = out.resolve(Config.Project.analysisFileName(projectName)) + + val sbt = computeSbtMetadata.value.map(_.config).getOrElse(Config.Sbt.empty) val project = Config.Project(projectName, baseDirectory, sources, dependenciesAndAggregates, - classpath, out, analysisOut, classesDir, `scala`, java, - testOptions, platform, compileSetup, resolution) + classpath, out, analysisOut, classesDir, `scala`, java, sbt, testOptions, platform, compileSetup, resolution) Config.File(Config.File.LatestVersion, project) } // format: ON diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/build.sbt b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/build.sbt index 5a8d9de89a..84b5638ab2 100644 --- a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/build.sbt +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/build.sbt @@ -1,5 +1,6 @@ import java.nio.file.{Files, Path} +val BloopClassifiers = Set("sources", "javadoc") val foo = project .settings( libraryDependencies ++= List( diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/changes/export-jars.sbt b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/changes/export-jars.sbt new file mode 100644 index 0000000000..9954476ec9 --- /dev/null +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/changes/export-jars.sbt @@ -0,0 +1 @@ +bloopExportJarClassifiers in ThisBuild := Some(Set("sources", "javadoc")) diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/project/plugins.sbt b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/project/plugins.sbt index ca6761db49..eb0c1209a5 100644 --- a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/project/plugins.sbt +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/project/plugins.sbt @@ -1,5 +1,3 @@ // Sbt classloads jawn 0.8.0 so let's use a parser that doesn't use a bin incompatible version addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % sys.props.apply("plugin.version")) -libraryDependencies ++= List( - "io.circe" %% "circe-jackson29" % "0.9.0" -) +libraryDependencies ++= List("io.circe" %% "circe-jackson29" % "0.9.0") diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/test b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/test index 8e699e312f..20e80c5022 100644 --- a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/test +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/cross-compile-test-configurations/test @@ -3,6 +3,7 @@ > woo/test:compile > bloopInstall > checkBloopFile -> set bloopExportSourceAndDocJars in ThisBuild := true +$ copy-file changes/export-jars.sbt export-jars.sbt +> reload > bloopInstall > checkSourceAndDocs \ No newline at end of file diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/build.sbt b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/build.sbt index 5d7f853166..21c47474b7 100644 --- a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/build.sbt +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/build.sbt @@ -2,8 +2,25 @@ val foo = project.in(file(".")) val bar = project import java.nio.file.Files -val checkSourceAndDocs = taskKey[Unit]("Check source and doc jars are resolved and persisted") -checkSourceAndDocs in ThisBuild := { + +val checkMetaBuildInfo = taskKey[Unit]("Check sbt meta build info") +checkMetaBuildInfo in ThisBuild := { + def readBareFile(p: java.nio.file.Path): String = + new String(Files.readAllBytes(p)).replaceAll("\\s", "") + + def check(f: File): Unit = { + val metaContents = readBareFile(f.toPath) + assert(metaContents.contains("\"autoImports\":[\""), "Missing auto imports.") + assert(metaContents.contains("\"sbtVersion\":\""), "Missing sbt version.") + } + + val bloopMetaDir = Keys.baseDirectory.value./("project")./(".bloop") + val metaBuildConfig = bloopMetaDir./("meta-builds-build.json") + check(metaBuildConfig) +} + +val checkSourceJars = taskKey[Unit]("Check source jars are resolved and persisted") +checkSourceJars in ThisBuild := { def readBareFile(p: java.nio.file.Path): String = new String(Files.readAllBytes(p)).replaceAll("\\s", "") @@ -12,5 +29,4 @@ checkSourceAndDocs in ThisBuild := { val metaBuildTestConfig = bloopDir./("meta-builds-build-test.json") val contents = readBareFile(metaBuildConfig.toPath) assert(contents.contains("\"classifier\":\"sources\""), "Missing source jar.") - assert(contents.contains("\"classifier\":\"javadoc\""), "Missing doc jar.") } diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/changes/set-sources.sbt b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/changes/set-sources.sbt index 6dbe68974d..c00cf880fb 100644 --- a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/changes/set-sources.sbt +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/changes/set-sources.sbt @@ -1 +1 @@ -bloopExportSourceAndDocJars in ThisBuild := true +bloopExportJarClassifiers in Global := Some(Set("sources", "javadoc")) diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/project/plugins.sbt b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/project/plugins.sbt new file mode 100644 index 0000000000..a4a378eb12 --- /dev/null +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % sys.props.apply("plugin.version")) + diff --git a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/test b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/test index 7fa75c6373..988b0bab3e 100644 --- a/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/test +++ b/integrations/sbt-bloop/src/sbt-test/sbt-bloop/meta-builds/test @@ -1,6 +1,6 @@ -> show name +> checkMetaBuildInfo $ exists project/.bloop/meta-builds-build.json -$ exists project/.bloop/meta-builds-build-test.json $ copy-file changes/set-sources.sbt project/set-sources.sbt > reload -> checkSourceAndDocs +> checkSourceJars diff --git a/project/BuildPlugin.scala b/project/BuildPlugin.scala index 3d0dc2a192..f3eeff392d 100644 --- a/project/BuildPlugin.scala +++ b/project/BuildPlugin.scala @@ -6,7 +6,7 @@ import bintray.BintrayKeys import ch.epfl.scala.sbt.release.Feedback import com.typesafe.sbt.SbtPgp.{autoImport => Pgp} import pl.project13.scala.sbt.JmhPlugin.JmhKeys -import sbt.{AutoPlugin, BuildPaths, Command, Def, Keys, PluginTrigger, Plugins, Task, ThisBuild} +import sbt.{AutoPlugin, BuildPaths, Compile, Def, Keys, PluginTrigger, Plugins, Task, ThisBuild} import sbt.io.IO import sbt.io.syntax.fileToRichFile import sbt.librarymanagement.syntax.stringToOrganization @@ -14,6 +14,8 @@ import sbt.util.FileFunction import sbtassembly.PathList import sbtdynver.GitDescribeOutput +import ch.epfl.scala.sbt.release.ReleaseEarlyPlugin.{autoImport => ReleaseEarlyKeys} + object BuildPlugin extends AutoPlugin { import sbt.plugins.JvmPlugin import com.typesafe.sbt.SbtPgp @@ -165,6 +167,19 @@ object BuildKeys { } ) + def sbtPluginSettings(sbtVersion: String, jsonConfig: Reference): Seq[Def.Setting[_]] = List( + Keys.name := "sbt-bloop", + Keys.sbtPlugin := true, + Keys.sbtVersion := sbtVersion, + Keys.target := (file("integrations") / "sbt-bloop" / "target" / sbtVersion).getAbsoluteFile, + BintrayKeys.bintrayPackage := "sbt-bloop", + BintrayKeys.bintrayOrganization := Some("sbt"), + BintrayKeys.bintrayRepository := "sbt-plugin-releases", + Keys.publishMavenStyle := + ReleaseEarlyKeys.releaseEarlyWith.value == ReleaseEarlyKeys.SonatypePublisher, + Keys.publishLocal := Keys.publishLocal.dependsOn(Keys.publishLocal in jsonConfig).value + ) + import sbtbuildinfo.BuildInfoKeys def benchmarksSettings(dep: Reference): Seq[Def.Setting[_]] = List( Keys.skip in Keys.publish := true, @@ -223,8 +238,6 @@ object BuildImplementation { def GitHubDev(handle: String, fullName: String, email: String) = Developer(handle, fullName, email, url(s"https://github.com/$handle")) - import ch.epfl.scala.sbt.release.ReleaseEarlyPlugin.{autoImport => ReleaseEarlyKeys} - final val globalSettings: Seq[Def.Setting[_]] = Seq( Keys.cancelable := true, BuildKeys.schemaVersion := "2.6", diff --git a/project/build.sbt b/project/build.sbt index 927289b9fc..204e7c531e 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -2,7 +2,7 @@ val mvnVersion = "3.5.2" val mvnPluginToolsVersion = "3.5" val circeDerivation = "io.circe" %% "circe-derivation" % "0.9.0-M3" -val root = project +val `bloop-build` = project .in(file(".")) .settings( scalaVersion := "2.12.4", @@ -17,7 +17,7 @@ val root = project addSbtPlugin("ch.epfl.scala" % "sbt-release-early" % "2.1.1"), addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.1"), addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2"), - addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.1"), + addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.3"), libraryDependencies += { "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value }, // Let's add our sbt plugin to the sbt too ;) unmanagedSourceDirectories in Compile ++= { diff --git a/project/project/build.sbt b/project/project/build.sbt new file mode 100644 index 0000000000..843b87b067 --- /dev/null +++ b/project/project/build.sbt @@ -0,0 +1,12 @@ +libraryDependencies += "io.circe" %% "circe-derivation" % "0.9.0-M3" +// Let's add our sbt plugin to the sbt too ;) +unmanagedSourceDirectories in Compile ++= { + val baseDir = baseDirectory.value.getParentFile.getParentFile + val pluginMainDir = baseDir / "integrations" / "sbt-bloop" / "src" / "main" + List( + baseDir / "config" / "src" / "main" / "scala", + baseDir / "config" / "src" / "main" / s"scala-${Keys.scalaBinaryVersion.value}", + pluginMainDir / "scala", + pluginMainDir / s"scala-sbt-${Keys.sbtBinaryVersion.value}" + ) +}