SBT stands for Simple Build Tool and it uses the Scala syntax to define how a project is built, managing dependencies, and so on. It uses .sbt files for this purpose. It also supports a setup based on Scala code in .scala files, as well as a mix of both.
To download SBT, go to http://www.scala-sbt.org/1.0/docs/Setup.html and follow the instructions. If you wish to obtain the newest version, then simply Google it and use the result you get back.
The following screenshot shows the structure of a skeleton SBT project:
It is important to show the contents of the main .sbt files.
The version.sbt file looks as follows:
version in ThisBuild := "1.0.0-SNAPSHOT"
It contains the current version that is automatically incremented if a release is made.
The assembly.sbt file has the following content:
assemblyMergeStrategy in assembly := {
case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first
case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
case "application.conf" => MergeStrategy.concat
case "unwanted.txt" => MergeStrategy.discard
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
assemblyJarName in assembly := { s"${name.value}_${scalaVersion.value}-${version.value}-assembly.jar" }
artifact in (Compile, assembly) := {
val art = (artifact in (Compile, assembly)).value
art.withClassifier(Some("assembly"))
}
addArtifact(artifact in (Compile, assembly), assembly)
It contains information about how to build the assembly JAR—a merge strategy, final JAR name, and so on. It uses a plugin called sbtassembly (https://github.com/sbt/sbt-assembly).
The build.sbt file is the file that contains the dependencies of the project, some extra information about the compiler, and metadata. The skeleton file looks as follows:
organization := "com.ivan.nikolov"
name := "skeleton-sbt"
scalaVersion := "2.12.4"
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")
javaOptions ++= Seq("-target", "1.8", "-source", "1.8")
publishMavenStyle := true
libraryDependencies ++= {
val sparkVersion = "2.2.0"
Seq(
"org.apache.spark" % "spark-core_2.11" % sparkVersion % "provided",
"com.datastax.spark" % "spark-cassandra-connector_2.11" % "2.0.5",
"org.scalatest" %% "scalatest" % "3.0.4" % "test",
"org.mockito" % "mockito-all" % "1.10.19" % "test" // mockito for tests
)
}
As you can see, here we define the Java version against which we compile some manifest information and the library dependencies.
The dependencies for our project are defined in the libraryDependencies section of our SBT file. They have the following format:
"groupId" %[%] "artifactId" % "version" [% "scope"]
If we decide to separate groupId and artifactId with %% instead of %, SBT will automatically use scalaVersion and append _2.12 (for Scala 2.12.*) to artifactId. This syntax is usually used when we include dependencies written in Scala, as the convention there requires us to have the Scala version added as part of artifactId. We can, of course, manually append the Scala version to artifactId and use %. This is also done in cases when we import libraries written in a different major version of Scala. In the latter case, however, we need to be careful with binary compatibility. Of course, not all libraries will be written in the version we use, so we either have to thoroughly test them and make sure they won't break our application, change our Scala version, or look for alternatives.
The dependencies shown will not be needed at any point in this book (the one for Spark and the Datastax one). They are here just for illustration purposes, and you can safely remove them if not needed.
SBT requires each statement to be on a new line and to be separated with a blank line from the previous one if we work with .sbt files. When using .scala files, we just write code in Scala.
The %% syntax in the dependencies is a syntactic sugar, which, using scalaVersion, will replace the name of the library, for example, scalatest will become scalatest_2.12 in our case.
SBT allows the engineer to express the same things differently. One example is the preceding dependencies—instead of adding a sequence of dependencies, we can add them one by one. The final result will be the same. There is also a lot of flexibility with other parts of SBT. For more information on SBT, refer to the documentation.
The project/build.properties defines the sbt version to be used when building and interacting with the application under sbt. It is as simple as the following:
sbt.version = 1.1.0
Finally, there is the project/plugins.sbt file that defines different plugins used to get things up and running. We already mentioned sbtassembly:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
There are different plugins online that provide useful functionalities. Here are some common
sbt commands that can be run from the root folder in the Terminal of this skeleton project:
- sbt: This opens the sbt console for the current project. All of the commands that will follow can be issued from here by omitting the sbt keyword.
- sbt test: This runs the application unit tests.
- sbt compile: This compiles the application.
- sbt assembly: This creates an assembly of the application (a fat JAR) that can be used to run as any other Java JAR.