build.sbt (155 lines of code) (raw):
import lmcoursier.internal.shaded.coursier.core.Version
import sbt.{Def, ThisBuild, url}
import scala.collection.{Seq, mutable}
lazy val PublishingSettings: Seq[Def.Setting[?]] = Seq(
organization := "org.jetbrains.scala",
// Optional but nice-to-have
organizationName := "JetBrains",
organizationHomepage := Some(url("https://www.jetbrains.com/")),
licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0")),
homepage := Some(url("https://github.com/JetBrains/sbt-structure")),
// Source-control coordinates
scmInfo := Some(
ScmInfo(
url("https://github.com/JetBrains/sbt-structure"),
"git@github.com:JetBrains/sbt-structure.git"
)
),
// Required by Sonatype for publishing
developers := List(
Developer(
id = "JetBrains",
name = "JetBrains",
email = "scala-developers@jetbrains.com",
url = url("https://github.com/JetBrains")
)
),
)
lazy val sbtStructure = project.in(file("."))
.aggregate(core, extractor, extractorLegacy_013)
.settings(PublishingSettings)
.settings(
name := "sbt-structure",
// disable publishing in the root project
publish / skip := true,
crossScalaVersions := List.empty,
crossSbtVersions := List.empty,
publishTo := None,
)
//NOTE: an extra scala 2.12 version is used just to distinguish between different sbt 1.x versions
// when calculating pluginCrossBuild / sbtVersion
val scala212_Earlier: String = "2.12.20" //used for sbt < 1.3
val scala212: String = "2.12.21" //used for sbt >= 1.3
val scala3: String = "3.7.4" //used for sbt 2
val Scala_2_10_Legacy = "2.10.7"
val SbtVersion_1_0 = "1.0.0"
val SbtVersion_1_3 = "1.3.0"
val SbtVersion_2 = "2.0.0-RC7" //TODO: update to the latest?
val SbtVersion_0_13_Legacy = "0.13.17"
val CommonSharedCoreDataSourcesSettings: Seq[Def.Setting[Seq[File]]] = Seq(
Compile / unmanagedSourceDirectories +=
(ThisBuild / baseDirectory).value / "shared" / "src" / "main" / "scala",
)
lazy val core = project.in(file("core"))
.settings(PublishingSettings)
.settings(
name := "sbt-structure-core",
libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "2.4.0",
scalaVersion := scala212,
crossScalaVersions := Seq(scala3, "2.13.16", scala212),
CommonSharedCoreDataSourcesSettings,
)
lazy val extractor = project.in(file("extractor"))
.enablePlugins(SbtPlugin)
.settings(PublishingSettings)
.settings(
name := "sbt-structure-extractor",
scalacOptions ++= Seq("-deprecation", "-feature") ++ {
// Mute some warnings
// We have to use some deprecated things because we cross-compile for 2.10, 2.12 and 3.x
if (scalaBinaryVersion.value.startsWith("3")) {
val patterns = Seq(
"""msg=(?s)`_` is deprecated for wildcard arguments of types. use `\?` instead.*:silent""",
"""msg=(?s)method mapValues in trait MapOps is deprecated since 2.13.0.*:silent""",
"""msg=.*is no longer supported for vararg splices.*:silent""",
"""msg=method toIterable in class IterableOnceExtensionMethods is deprecated since 2.13.0.*:silent""",
"""msg=method right in class Either is deprecated since 2.13.0.*:silent""",
"""msg=method get in class RightProjection is deprecated since 2.13.0.*:silent""",
"""msg=object JavaConverters in package scala.collection is deprecated since 2.13.0.*:silent""",
// We have to use IntegrationTest to support older sbt versions
"""msg=value IntegrationTest in trait LibraryManagementSyntax is deprecated since 1.9.0.*:silent""",
)
Seq(s"-Wconf:${patterns.mkString(",")}")
} else
Seq.empty
},
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
"org.dom4j" % "dom4j" % "2.2.0" % Test
),
scalaVersion := scala212,
// scalaVersion := scala3,
crossScalaVersions := Seq(
scala212_Earlier,
scala212,
scala3,
),
crossSbtVersions := Seq(
SbtVersion_1_0,
SbtVersion_1_3,
SbtVersion_2
),
pluginCrossBuild / sbtVersion := {
// keep this as low as possible to avoid running into binary incompatibility such as https://github.com/sbt/sbt/issues/5049
val scalaVer = scalaVersion.value
scalaVer match {
case `scala212_Earlier` => SbtVersion_1_0
case `scala212` => SbtVersion_1_3
case `scala3` => SbtVersion_2
case _ =>
throw new AssertionError(s"Unexpected scala version $scalaVer")
}
},
// By default, when you crosscompile sbt plugin for multiple sbt 1.x versions,
// it will use the same binary version 1.0 for all of them
// It will use the same source directory `scala-sbt-1.0`, same target dirs and same artifact names.
// But we need different directories because some code compiles in sbt 1.x but not in sbt 1.y
pluginCrossBuild / sbtBinaryVersion := {
val originalBinaryVersion = (pluginCrossBuild / sbtBinaryVersion).value
val sbtVersion3Digits = (pluginCrossBuild / sbtVersion).value
sbtVersion3Digits match {
case SbtVersion_2 =>
// For sbt 2, the binary version has been changed to a simple "2".
// https://github.com/sbt/librarymanagement/pull/437
originalBinaryVersion
case SbtVersion_1_3 =>
// For sbt [1.3.x, 2.x) we set our own custom binary version.
// This is because we offer the sbt-structure-extractor as a specially compiled binary for sbt 1.3.x and above.
"1.3"
case SbtVersion_1_0 =>
// For sbt [1.0.x, 1.2.x], we use the default sbt 1 binary version "1.0".
originalBinaryVersion
}
},
Compile / unmanagedSourceDirectories ++= {
val sbtVersion = Version((pluginCrossBuild / Keys.sbtVersion).value)
val baseDir = (Compile / sourceDirectory).value
val result = mutable.Buffer[File]()
if (sbtVersion.repr.startsWith("1")) {
result += baseDir / "scala-sbt-1.0-1.x"
}
if (sbtVersion >= Version("1.3"))
result += baseDir / "scala-sbt-1.3+"
result
},
CommonSharedCoreDataSourcesSettings,
// We only run tests in Scala 2.
// This is done to avoid cross-compilation for test sources, which would introduce some redundant burden.
// TODO: ensure CI is updated (TeamCity & GitHub)
Test / unmanagedSourceDirectories := {
if (scalaVersion.value.startsWith("2"))
(Test / unmanagedSourceDirectories).value
else
Nil
},
Test / parallelExecution := false
)
// We use separate module for 0.13 with many sources duplicated as an alternative to cross-compilation.
// Such an approach should be easier than cross-compiling against 0.13, 1.0, 1.2, 2.x.
// Trying to cross-compile between 3 major versions of sbt (and thus scala 2.10, 2.12, 3.x) is very fragile
lazy val extractorLegacy_013 = project.in(file("extractor-legacy-0.13"))
.enablePlugins(SbtPlugin)
.settings(PublishingSettings)
.settings(
name := "sbt-structure-extractor-legacy-0.13",
// NOTE: use the same module name for 0.13 when publishing.
// We have to do this explicitly because we extracted the 0.13 code to a separate project with a different name
// which is used as the module name by default.
moduleName := (extractor / Keys.moduleName).value,
scalaVersion := Scala_2_10_Legacy,
crossScalaVersions := Seq(Scala_2_10_Legacy),
crossSbtVersions := Seq(SbtVersion_0_13_Legacy),
pluginCrossBuild / sbtVersion := SbtVersion_0_13_Legacy,
CommonSharedCoreDataSourcesSettings,
)
// just running "ci-release" in the root will run it for all aggregated projects
// Running extra "clean" to ensure that there is no unexpected files cached
// in sonatype-staging or sonatype-bundle local directories in target directory
val publishAll =
"; clean ; ci-release"
val publishCoreCommand =
"; clean ; project core ; ci-release"
val publishExtractorCommand =
"; clean ; project extractor ; ci-release ; project extractorLegacy_013 ; ci-release"
addCommandAlias("publishAll", publishAll)
addCommandAlias("publishCore", publishCoreCommand)
addCommandAlias("publishExtractor", publishExtractorCommand)
// note: we can only run tests for Scala 2 (see comments in extractor module)
addCommandAlias("crossCompileAndRunTests", s"""; +compile ; project extractor ; set scalaVersion := "$scala212" ; test""")