Introduction
In this post, we are going to discuss SBT New version 1.0 features.
Most of the real-time projects are using SBT version 0.13.8. Lightbend is going to release SBT new version 1.0 with lot of new features and enhancements soon.
SBT New Version: 1.0 Features
SBT 1.0 is coming with lot of new features and massive enhancements to existing features
The two main new features are:
- Auto Plug-ins
- SBT as a build server
Java version
sbt 0.13 is on JDK 6. sbt 1.0 will be based on JDK 8.The plugin ecosystem
One of the most powerful aspects of sbt is its plugin ecosystem. Because plugins work exactly the same way as the build definitions, learning sbt translates smoothly into learning how to write plugins. The diversity of the sbt plugins are testament to the robustness of this basic concept. Among them the two that stand out are the Play Framework and Activator. These are built on top of sbt to provide interactive development experience.The sbt team is proud to announce the inclusion of auto plugins in sbt 0.13.5, which is a binary compatible technology preview of what's to come in sbt 1.0.
Auto Plugins
Auto plugins work just like the traditional sbt plugins: They can add new tasks, settings, and commands into a project's build definition. The primary difference is that auto plugins are a bit more opinionated about the way tasks and settings should be loaded into a build than their predecessors, providing the features needed to give users full control over plugins as well as allowing plugin authors to provide new functionality in a seamless way.Default settings are auto plugins
Starting in sbt 0.13.5, all the default settings are now provided through three auto plugins:- CorePlugin (introduces core sbt-isms)
- IvyPlugin (for dependency management)
- JvmPlugin (for compiling Scala/Java projects)
Let's look more into the features of auto plugins:
projectSettings and buildSettings
In the past, including a plugin into any build was a two part process:- Include the plugin in a
project/plugins.sbt
file. - Add the settings for the plugin to your
build.sbt
orproject/build.scala
file.
With auto plugins, all provided settings (e.g.
assemblySettings
) are provided by the plugin directly via the projectSettings
method. Here’s an example plugin that adds a command named hello
to sbt projects:package sbthello
import sbt._
import Keys._
object HelloPlugin extends AutoPlugin {
override lazy val projectSettings = Seq(commands += helloCommand)
lazy val helloCommand =
Command.command("hello") { (state: State) =>
println("Hi!")
state
}
}
If the plugin needs to append settings at the build-level (that is, in ThisBuild
) there's a buildSettings
method, and for global-level (in Global
), there's globalSettings
method.
This level of automation is convenient for plugins to automatically
work in sbt, but doesn’t give the user control over how plugins are
added. For that, there’re a few new controls on projects.
enablePlugins
To activate the HelloPlugin
, you would still need to declare dependency to sbt-hello in project/plugins.sbt
as follows:
addSbtPlugin("com.example" % "sbt-hello" % "0.1.0")
Next, instead of appending the setting sequence in build.sbt
, you can now call enablePlugins
method on project in build.sbt
:
lazy val root = project in file (".")
root.enablePlugins (HelloPlugin)
This will append HelloPlugin.projectSettings
into the root project's setting sequence.
Plugin dependencies
When a traditional plugin wanted to reuse some functionality from an
existing plugin, it would pull in the plugin as a library dependency,
and then it would either (1) add the setting sequence from the
dependency as part of its own setting sequence, or (2) tell the build
users to include them in the right order. This becomes complicated as
the number of plugins increase within an application, and becomes more
error prone.
The main goal of auto plugin is to alleviate this setting dependency
problem. An auto plugin can depend on other auto plugins and lensure
these dependency settings are loaded first.
Suppose we have the SbtLessPlugin
and the SbtCoffeeScriptPlugin
, which in turn depends on the SbtJsTaskPlugin
, SbtWebPlugin
, and JvmPlugin
. Instead of manually activating all of these plugins, a project can just activate the SbtLessPlugin
and SbtCoffeeScriptPlugin
like this:
lazy val root = project in file(".")
rootProject.addPlugins(SbtLessPlugin, SbtCoffeeScriptPlugin)
This will pull in the right setting sequence from the plugins in the
right order. The key notion here is you declare the plugins you want,
and sbt can fill in the gaps.
The second piece of this is for auto plugins to define their setting dependencies. Here’s how:
package sbtless
import sbt._
import Keys._
object SbtLessPlugin extends AutoPlugin {
override def requires = SbtJsTaskPlugin
override lazy val projectSettings = ...
}
The requires
method returns a value of type Plugins
, which is a DSL for constructing the dependency list. The requires
method typically contains one of the following values:
- empty (No plugins, this is the default)
- other auto plugins
- && operator (for defining multiple dependencies)
Triggered plugins
Plugin dependencies solve much of the annoyance dealing with multiple
inter-related plugins, but the build user still needs to manually
include them into each project. Autoplugins also provide a way for
plugins to automatically attach themselves to projects if their
dependencies are met. This is achieved using the trigger
method.
For example, we might want to create a triggered plugin that can
append commands automatically to the build. To do this, set the requires
method to return empty
(this is the default), and override the trigger
method with allRequirements
.
package sbthello
import sbt._
import Keys._
object HelloPlugin2 extends AutoPlugin {
override def trigger = allRequirements
override lazy val buildSettings = Seq(commands += helloCommand)
lazy val helloCommand =
Command.command("hello") { (state: State) =>
println("Hi!")
state
}
}
The build user still needs to include this plugin in project/plugins.sbt
,
but it is no longer needed to be included in build.sbt. This becomes
more interesting when you do specify a plugin with requirements. Let's
modify the SbtLessPlugin so that it depends on another plugin:
package sbtless
import sbt._
import Keys._
object SbtLessPlugin extends AutoPlugin {
override def trigger = allRequirements
override def requires = SbtJsTaskPlugin
override lazy val projectSettings = ...
}
As it turns out, PlayScala
plugin (in case you didn't know, the Play framework is an sbt plugin) lists SbtJsTaskPlugin
as one of it required plugins. So, if we define a build.sbt with:
plazy val root = project in file(".")
root.enablePlugins(PlayScala)
then the setting sequence from SbtLessPlugin
will be automatically appended somewhere after the settings from PlayScala.
This allows plugins to silently, and correctly, extend existing
plugins with more features. It also can help remove the burden of
ordering from the user, allowing the plugin authors greater freedom and
power when providing feature for their users.
Controlling the import with autoImport
In addition to providing settings, another thing the traditional
sbt.Plugin provided was a means of automatically adding methods, values
and types into the build.sbt
DSL. By default, any member of an sbt.Plugin
class was automatically imported. This lead to possible conflicts and
sbt-plugin conventions that promoted putting implementation details
outside the sbt.Plugin
instance itself.
Auto plugin corrects this by letting you be in charge of what names you want to expose to *.sbt
files. This is done by providing an autoImport
member within the AutoPlugin
instance. Let’s see an example:
package sbthello
import sbt._
import Keys._
object HelloPlugin3 extends AutoPlugin {
object autoImport {
val greeting = settingKey[String]("greeting")
}
import autoImport._
override def trigger = allRequirements
override lazy val buildSettings = Seq(
greeting := "Hi!"
commands += helloCommand)
lazy val helloCommand =
Command.command("hello") { (state: State) =>
println(greeting.value)
state
}
}
This hello plugin provides the greeting key directly in a build.sbt,
allowing users to directly reference it without imports. The build user
can still get to your plugin object by typing the full path
sbthello.HelloPlugin3.x. But by default, it will only wildcard import
names under a field (val, lazy val, or object) named autoImport.
No comments:
Post a Comment