CHAPTER 11

image

Simple Build Tool - SBT

Software development typically includes activities such as compiling source code into binary code, executing tests, packaging binary code into archives, and deploying archives to production systems. A build is a term that refers to compiled code packaged and deployed on production. A build is a technical term for a deliverable. Deliverable is the set of executable code expected by the stakeholders. The process through which a build has to go to become the deliverable is called a build process. Thus a build process, in general, is comprised of compilation, testing, packaging, and deployment.

Build automation is the act of automating the aforementioned build process by means of a build tool. These build tools require you to define the project configuration and dependencies in an artifact called build definition.

Image Note  A dependency is the term that refers to interdependencies between software constructs and is measured by the term coupling. Coupling is the degree of interdependencies between software constructs.

Figure 11-1 shows a quick overview of the popular build tools in the timeline. Make1 is atypical in this list, but it was included because it pioneered the build automation. All the build tools except Make in Figure 11-1 are popular in the JVM landscape.

9781484202333_Fig11-01.jpg

Figure 11-1. Build tools timeline

As mentioned, Make pioneered the build automation and allowed dependency management from the outset. Make remains widely used, especially in Unix.

Apache Ant2 is similar to Make but is implemented in the Java language and unlike Make, it uses XML to describe the build process and its dependencies. It necessitates the Java platform, and befits building Java projects. Contrary to Ant, Maven3 allows convention over configuration for the build procedure by providing sensible default behavior for projects.

Image Note  Convention over configuration (CoC) refers to, in general, a development approach centered on conventions. It enables developers to specify and configure only the unconventional aspects of the development.

Apache Ivy4 is a dependency manager. It is a sub-project of the Apache Ant project, and helps to resolve project dependencies.

Image Note  Apache Ivy competes to a large extent with Apache Maven, which also manages dependencies. ­However, Maven is a complete build tool, whereas Ivy focuses purely on managing dependencies.

Gradle5 builds upon Apache Ant and Apache Maven and but uses a Groovy-based domain-specific language (DSL) to declare the project configuration instead of XML.

Although you can use Ant and Maven to build your Scala projects, SBT6, is the standard build tool for Scala applications. Like Maven, SBT uses the same directory structure and convention over configuration approach and Apache Ivy for handling dependency management.

In essence, the build automation automates all the steps in the build process with build tools like Ant, Maven, Ivy, Gradle, or SBT and the best practice is to run the build continuously. This process is known as continuous integration. There are several tools that offer continuous integration such as Hudson7 and Jenkins8.

Getting Started with SBT

In this section you will see sbt in action using a simple hello world project. First you need to install sbt.

Installing SBT

The installation of sbt, in a nutshell, is akin to launching a JARsbt-launch.jar which can be downloaded from http://www.scala-sbt.org/0.13/tutorial/Manual-Installation.html, if you want to install sbt manually. Or you can install using sbtmsi, which you can download from http://www.scala-sbt.org/download.html.

SBT provides different installation packages for Mac, Windows, Linux, Typesafe9 Activator, or manual installation. You can find information on installation at http://www.scala-sbt.org/0.13.5/docs/Getting-Started/Setup.html#installing-sbt.

Typesafe Activator is a custom version of sbt that adds two extra commands, activator ui and activator new. The activator command is a superset of sbt, in short. You can obtain Activator from typesafe.com. Activator offers two downloads; the small “minimal” download contains only the wrapper script and launch jar, while the large “full” download contains a preloaded Ivy cache with jars for Scala, Akka10, and the Play11 Framework. You will use both sbt and activator in the next chapter when you build web application in Scala.

Once you have downloaded sbt you can check the version as shown:

F:sbthelloworld>sbt --version
sbt launcher version 0.13.7

Creating Hello World Project

In order to see sbt in action, set up a simple hello world project by creating a project directory helloworld with a hello.scala source file in it as shown in Listing 11-1.

Listing 11-1. hello.scala

object Helloworld {
def main(args: Array[String]) = println("Hello world!")
}

Now you can run sbt by typing sbt as shown

F:sbthelloworld>sbt
[info] Set current project to helloworld (in build file:/F:/sbt/helloworld/)
>

We will explain the [info] part of the preceding output a little later. When you run sbt in your project directory helloworld with no command-line arguments, sbt starts in an interactive mode. The interactive mode provides a command prompt with features such as tab completion and history.

Interactive mode remembers what you typed previously (history), even if you exit sbt and restart it. Sbt lists all the history commands when you type ! on sbt prompt as shown.

> !
History commands:
   !!    Execute the last command again
   !:    Show all previous commands
   !:n    Show the last n commands
   !n    Execute the command with index n, as shown by the !: command
   !-n    Execute the nth command before this one
   !string    Execute the most recent command starting with 'string'
   !?string    Execute the most recent command containing 'string'
>

Sbt provides a long list of commands. For a more complete list, see Command Line Reference at http://www.scala-sbt.org/release/docs/Command-Line-Reference.html.

Here are some of the most common sbt commands.

Command

Description

clean

Deletes all generated files (in the target directory)

compile

Compiles the main sources (in src/main/scala and src/main/java directories)

run

Runs the main class for the project in the same virtual machine as sbt

help <command>

Displays help for the specific command when you type help<command>. Displays brief description of all the commands if you type just help.

You can exit the interactive mode by typing exit.

Now you can run the project by typing run at the sbt prompt of the interactive mode as shown:

> run
[info] Updating {file:/F:/sbt/helloworld/}helloworld...
[info] Resolving org.scala-lang#scala-library;2.10.4 ...
  [info] Resolving org.scala-lang#scala-compiler;2.10.4 ...
  [info] Resolving org.scala-lang#jline;2.10.4 ...
  [info] Resolving org.fusesource.jansi#jansi;1.4 ...
  [info] Resolving org.scala-lang#scala-reflect;2.10.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to F:sbthelloworld argetscala-2.10classes..
[info] Running Helloworld
Hello world!
[success] Total time: 15 s, completed Jan 27, 2015 8:45:39 PM
>

As you can see in the preceding output, sbt runs the helloworld project and displays Hello World!. We will now explain the [info] part of the output.

F:sbthelloworld>sbt
 [info] Set current project to helloworld (in build file:/F:/sbt/helloworld/)
>

Sbt requires you to set the name of the project to helloworld and allows you generate a build definition. We will explain build definition in greater detail later in this chapter. Meanwhile, enter the following on sbt console:

set name:= "helloworld"

You will get the following output:

> set name:= "helloworld"
[info] Defining *:name
[info] The new value will be used by *:description, *:normalizedName and 5 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] Set current project to helloworld (in build file:/F:/sbt/helloworld/)

Now save the session by entering the following:

session save

Now you will see the following output:

> session save
[info] Reapplying settings...
[info] Set current project to helloworld (in build file:/F:/sbt/helloworld/)

Now enter exit and this  generates build.sbt in the root directory, helloworld. Listing 11-2 shows the generated build.sbt.

Listing 11-2. generatedbuild.sbt

name:= "helloworld"

You can edit the build.sbt to add the basic information as follows:

Listing 11-3. generatedbuild.sbt

name := "helloworld"
version := "1.0"
scalaVersion := "2.11.1"

We will go through the code in the Listing 11-3 and related concepts in the next section.

Build Definition

As we mentioned earlier, a build tool requires you to define the project configuration and dependencies in an artifact called build definition.

In Scala, there are three types of build definition:

  • .sbt build definition
  • .scala build definition
  • combination of .scala and .sbt build definition

As you learned earlier, the base directory of your hello world project—helloworld directory—is comprised of the build definition.sbt file. In addition to .sbt file there may also be build definitions as a .scala file located in the project/ subdirectory of the base directory. A sbt build definition consists of a list of settings as shown in Listing 11-4.

Listing 11-4. .sbt Build Definition

name := "helloworld"

version := "1.0"

scalaVersion := "2.11.1"

In the previous version of sbt, you were required to separate the setting expression by blank lines. You could not write an .sbtfile as shown in Listing 11-5 as it did not compile because of absence of blank lines.

Listing 11-5. .sbt Without Blank Lines (will not compile)

name := "helloworld"
version := "1.0"
scalaVersion := "2.10.x"

This restriction does not exist any longer from sbt0.13.7. but we mentioned this because you may find yourself into situations where older versions of SBT are still being used with newer versions of scala, and in such cases the build file without the blank lines won’t compile.

You learned how to set up a hello world scala project in sbt, but in general, industry strength applications are far more complex than a hello world project. In order to use sbt with industry strength applications efficiently, it is important to understand how the build definition works. We insert the Listing 11-3 to explain the build definition as Listing 11-6.

Listing 11-6. Generated build.sbt (from Listing 11-3)

name := "helloworld"
version := "1.0"
scalaVersion := "2.11.1"

A build definition is made up of build properties, where each property is a key value pair. From Listing 11-6 one build property is shown as follows:

name :=  "helloworld"

name is the key and the String "helloworld" is its value type and := is an operator method for transformation. Assignment with := is the simplest of all the transformation that sbt provides. The other transformation methods will be introduced in the later section of this chapter.

In SBT, keys are defined for different purposes. A key can be categorized into one of the following categories:

Key

Description

Setting key

When you define the key as a Setting key, the value of the key is computed on loading the project

Task key

When you define the key as a Task key, the value of the  key is recomputed each time it is executed

Input key

When you define the key as an Input key, the value of the key  take command-line arguments as input

The Setting keys provide build configuration. The keys such as name, version, and scalaVersion that you saw in Listing 11-3 are Setting keys. We will look at two other useful types of setting keys: libraryDependencies and resolvers keys in the following section.

The Task keys, as the name suggests, are geared toward tasks such as clean, compile, test, and so on.

Image Note  Because a Task key is computed on each execution, a Setting key cannot depend on a Task key. Trying to do so will throw an error.

The Input keys are the keys that have command-line arguments. An example of an input key is run key. The run key is used to run a main class with the command-line arguments. If no arguments are provided, a blank string is used. You execute run without any arguments when you run the hello world project.

Image Note  Each key can have more than one value, but in different context called scopes. In a given scope, a key has only one value.

In the following section you will learn about the Setting keys called libraryDependencies key and resolvers key.

LibraryDependencies and Resolvers

The libraryDependencies key is used to declare managed dependencies and the resolvers key is used to provide additional resource URIs for automatically managed dependencies. As we mentioned earlier, SBT uses Apache Ivy to implement managed dependencies. You should list your dependencies in the setting libraryDependencies. You can declare the dependency as shown in Listing 11-7.

Listing 11-7. Declaring Dependencies

libraryDependencies += groupID % artifactID % revision

In Listing 11-7, groupId, artifactId, and revision are strings.

You can also declare the dependencies as shown in Listing 11-8.

Listing 11-8. Declaring Dependencies With Configuration Value

libraryDependencies += groupID % artifactID % revision % configuration

In Listing 11-8, configuration can be a string or val.

In Listing 11-7 and Listing 11-8, you add a ModuleID created by % method to libraryDependencies. An astute reader might have noticed that we used a different operator method for transformation in Listing 11-7 and Listing 11-8 than the assignment operator method you learned in Listing 11-6.

The operator += appends to the existing value. You can also use another operator ++=, which appends the sequence of values to the existing value as shown in Listing 11-9.

Listing 11-9. Using ++=

libraryDependencies ++= Seq(
  groupID % artifactID % revision,
  groupID % otherID % otherRevision
)

Listing 11-9 uses++= to add a list of dependencies at the same time.

Listing 11-10 shows using the Apache Derby as the library dependency.

Listing 11-10. Using Apache Derby Dependency

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"

In Listing 11-10, SBT uses the default repository for downloading Derby. If you want to use the library dependency that is not in one of the default repositories, you need to add a resolver to help Ivy locate it. In order to provide the location of repository you can use the following syntax:

 resolvers += name at location

Here name is the String name of the repository and location is the String location of the repository.

Listing 11-11 shows how to add the additional repository.

Listing 11-11. Using Resolvers

resolvers += "Sonatype OSS Snapshots" at https://oss.sonatype.org/content/repositories/snapshots

In Listing 11-11 we add the sonatypeoss snapshots repository, which is located at the given URL.

Plugins

A plugin is an artifact that extends the build definition, usually by adding new settings. In order to declare this plugin dependency you need to pass the plugin’s Ivy module ID to addSbtPlugin as shown in Listing 11-12. Next we show you how to use an eclipse plugin using the hello world project we created earlier. You can obtain the sbt eclipse plugin's Ivy module ID from https://github.com/typesafehub/sbteclipse.

First create an .sbt file for the plugin in the helloworld project directory as shown in Listing 11-12.

Listing 11-12. plugin.sbt

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0")

Now you can run the eclipse command on the sbt prompt as shown:

> eclipse
[info] About to create Eclipse project files for your project(s).
[info] Updating {file:/F:/sbt/helloworld/}helloworld...
[info] Resolving org.scala-lang#scala-library;2.10.4 ...
[info] Resolving org.scala-lang#scala-compiler;2.10.4 ...
  [info] Resolving org.scala-lang#jline;2.10.4 ...
  [info] Resolving org.fusesource.jansi#jansi;1.4 ...
  [info] Resolving org.scala-lang#scala-reflect;2.10.4 ...
[info] Done updating.
[info] Successfully created Eclipse project files for project(s):
[info] helloworld
>

This creates the project directory structure for eclipse that adheres to convention over configuration and is same as the Maven directory structure.

Now open Eclipse and navigate File image Import as illustrated in the Figure 11-2.

9781484202333_Fig11-02.jpg

Figure 11-2. Importing the project

Once you click Import, you will see the select form as illustrated in Figure 11-3.

9781484202333_Fig11-03.jpg

Figure 11-3. Selecting existing projects into workspace

Now navigate to “Existing Projects into Workspace.” You will now see the Import dialog as illustrated in Figure 11-4. Click Browse to get to the base directory helloworld as illustrated in Figure 11-4.

9781484202333_Fig11-04.jpg

Figure 11-4. Selecting the root directory

Now click Finish to see the project structure shown in Figure 11-5.

9781484202333_Fig11-05.jpg

Figure 11-5. Project directory structure

SBT also allows you to generate IDEA project configuration. To do this you use an sbt plugin located at https://github.com/mpeltonen/sbt-idea. To generate NetBeans configuration you can find the sbt plugin for NetBeans at https://github.com/dcaoyuan/nbsbt.

One chapter is not enough to list all the features of SBT and it deserves a book of its own. For a detailed treatment on SBT we recommend you to go through the reference manual of SBT at http://www.scala-sbt.org/release/docs/.

This chapter did provide you a brief introduction to SBT, which is sufficient to use it in the next chapter for building a web application.

Summary

In this chapter you learned how to create a simple project with SBT and execute basic operations, such as compile, test, and run. You learned how to configure library dependencies and how to configure a dependency when it is not in a default repository. In the next chapter you will learn how to create web applications in Scala.

_____________________

1http://www.gnu.org/software/make/.

2http://ant.apache.org/.

3http://maven.apache.org/.

4http://ant.apache.org/ivy/.

5https://gradle.org/.

6http://www.scala-sbt.org/.

7http://hudson-ci.org/.

8http://jenkins-ci.org/.

9https://typesafe.com/.

10http://akka.io/.

11https://www.playframework.com/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset