SBT build generator for role-based projects, based on Ammonite and Coursier.
Details: Monorepo or Multirepo? Role-Based Repositories.
See also: role-based projects for .NET/C# and JS
Create a file sbtgen.sc and paste the following code:
#!/bin/sh
coursier launch com.lihaoyi:ammonite_2.13.0:1.6.9 --fork -M ammonite.Main -- sbtgen.sc $*
exit
!#
import $ivy.`io.7mind.izumi.sbt::sbtgen:0.0.66`, izumi.sbtgen._, izumi.sbtgen.model._
val globalSettings = GlobalSettings(
groupId = "my.org",
)
@main
def entrypoint(args: String*): Unit = {
Entrypoint.main(MyProject.root, globalSettings, Seq("-o", ".") ++ args)
}
object Platforms {
val jvm = PlatformEnv(
platform = Platform.Jvm,
language = Seq(ScalaVersion("2.13.3")),
settings = Seq("scalacSettings" += "-Xlint:_"),
)
}
object MyProject {
val root = Project(
name = ArtifactId("my-project"),
aggregates = Seq(
Aggregate(
name = ArtifactId("my-lib"),
artifacts = Seq(
Artifact(
name = ArtifactId("my-module-a"),
libs = Seq.empty,
depends = Seq.empty,
platforms = Seq(Platforms.jvm),
groups = Set(Group("groupA")),
),
Artifact(
name = ArtifactId("my-module-b"),
libs = Seq.empty,
depends = Seq.empty,
platforms = Seq(Platforms.jvm),
groups = Set(Group("groupB")),
),
),
),
),
)
}Install Coursier and launch the script:
chmod +x sbtgen.sc
./sbtgen.scAlternatively, you may launch it with Ammonite if it's installed:
amm sbtgen.scThis will generate build.sbt for my-project in current directory.
Use ./sbtgen.sc -u groupA or ./sbtgen.sc -u groupB to build only my-module-a or my-module-b
Use ./sbtgen.sc --help for help:
$ ./sbtgen.sc --help
Error: Unknown option --help
sbtgen
Usage: sbtgen [options]
--nojvm disable jvm projects
--js enable js projects
--native enable native projects
--nta don't publish test artifacts
-d, --debug enable debug output
-c, --compactify deduplicate repetative settings
-t, --isolate-tests don't inherit test scopes
-o, --output <value> output directory
-u, --use <value> use only groups specified
Cannot parse commandlinesbt-izumi is published for both sbt majors: sbt 1.x picks up sbt-izumi_2.12_1.0,
sbt 2.x picks up sbt-izumi_sbt2_3. addSbtPlugin resolves the right one, so nothing
changes in project/plugins.sbt.
To make sbtgen emit a build for sbt 2.x, set sbtTarget and a matching sbtVersion:
val globalSettings = GlobalSettings(
groupId = "my.org",
sbtVersion = Some("2.0.9"),
sbtTarget = SbtTarget.Sbt2,
)The two must agree, otherwise generation fails.
Differences in the generated output:
LibraryType.Autodependencies use%%rather than%%%: sbt 2.x removed%%%and made%%platform-aware. For the same reasonLibraryType.AutoJvmdependencies of a cross-platform artifact are emitted as(... %% ...).platform(Platform.jvm), so they keep resolving the JVM artifact instead of a_sjs1one.- Artifacts declaring
sbtPlugin := trueget nocrossScalaVersions/scalaVersion. An sbt 2.x plugin must be built with the metabuild's own Scala version; pinning one from the model either clashes with sbt's (conflicting cross-version suffixes) or produces TASTy the metabuild cannot read. Leaving the axis to sbt also survives sbt 2.x upgrades. - Project definitions are emitted flat, rather than grouped into the anonymous-class
holders used to stay under the JVM classfile size limit on sbt 1.x. Scala 3, which
compiles
build.sbton sbt 2.x, infersObjectinstead of a structural refinement fornew { ... }, so the holders' members would be unreachable. rootPluginsandtopLevelSettingsare attached to the root project instead of being emitted as bare statements, because sbt 2.x injects bare statements into every subproject.
Known limitations on sbt 2.x:
-
Scala.js builds cannot use
sbt-scalajs-bundlerorsbt-jsdependencies, which have no sbt 2.x releases. SetbundlerVersion = NoneandsbtJsDependenciesVersion = None; generation fails otherwise rather than silently dropping them. -
Scala.js/Native also need plugin versions newer than the defaults this project pins, since the older ones were never published for sbt 2.x:
scalaJsVersion = Version.VConst("1.22.0"), scalaNativeVersion = Version.VConst("0.5.12"), crossProjectVersion = Version.VConst("1.4.0"),
-
sbt-izumidoes not re-exportsbt-duplicates-finderorsbt-statsthere (no sbt 2.x releases).sbt-dependency-treeis in-sourced into sbt 2.x itself, so it is still available. -
IzumiExposedTestScopesPlugin.itSettingsthrows on sbt 2.x: theIntegrationTestconfiguration was removed, and integration tests are meant to be a separate subproject. -
sbt 2.x defaults a project's
organizationto its project id.withBuildInfotherefore emits useless coordinates ("bi" %% "bi" % version) for projects that never set one, so setThisBuild / organizationif you use it.
sbtgen rewrites what it generates, but settings you supply as RawSettingDef are passed through
verbatim, and two sbt 2.x changes bite almost every consumer:
- Cached tasks. sbt 2.x caches every task, and a task whose result type has no
JsonFormatfails at load withgiven evidence sjsonnew.HashWriter[...] is not found; opt out of caching by annotating the key with @transient, or as foo := Def.uncached(...). Wrap those inDef.uncached { ... }. Do the same for any task that must re-run for its side effects, or sbt will serve the cached result and silently skip them. mappingsisSeq[(HashedVirtualFileRef, String)], no longerSeq[(File, String)]. Convert withfileConverter.value.toVirtualFile(...), ortoFileRefsMappingfrom sbt2-compat.
Intellij has built-in support for Ammonite scripts, if it doesn't work go to Preferences -> Languages and Frameworks -> Scala -> Worksheet and change Treat .sc files as: to Always Ammonite
To enable syntax highlighting for the library, ensure that sbtgen.sc is opened as an Ammonite script – there should be a Run script button in the upper-left corner. Press it, after script finishes Intellij should prompt to include $ivy dependencies in the script into the project – this will enable full IntelliSense for the script.
Complete projects that use this tool:
-
Izumi framework build:
-
izumi-reflectbuild: -
D4S build:
-
Test project:
Use
./copy-test-directory.shTo deal with build diff failures if you changed the expected output of the DSL
To release new version via CI:
sbt +clean +test release