This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 18.2, “How to compile, run, and package a Scala project with SBT.”
Problem
You want to use the SBT build tool to compile and run a Scala project, and then package the project as a JAR file.
Solution
Create a directory layout to match what SBT expects, then run sbt compile
to compile your project, sbt run
to run your project, and sbt package
to package your project as a JAR file.
To demonstrate this, create a new SBT project directory structure as shown in Recipe 18.1, and then create a file named Hello.scala in the src/main/scala directory with these contents:
package foo.bar.baz object Main extends App { println("Hello, world") }
Unlike Java, in Scala, the file’s package name doesn’t have to match the directory name. In fact, for simple tests like this, you can place this file in the root directory of your SBT project, if you prefer.
From the root directory of the project, you can compile the project:
$ sbt compile
run the project:
$ sbt run
and package the project into a JAR file:
$ sbt package
Discussion
The first time you run SBT, it may take a while to download all the dependencies it needs, but after that first run, it will download new dependencies only as needed. The commands executed in the Solution, along with their output, are shown here:
$ sbt compile [info] Loading global plugins from /Users/Al/.sbt/plugins [info] Set current project to Basic (in build file:/Users/Al/SbtTests/) [success] Total time: 0 s $ sbt run [info] Loading global plugins from /Users/Al/.sbt/plugins [info] Set current project to Basic (in build file:/Users/Al/SbtTests/) [info] Running foo.bar.baz.Main Hello, world [success] Total time: 1 s $ sbt package [info] Loading global plugins from /Users/Al/.sbt/plugins [info] Set current project to Basic (in build file:/Users/Al/SbtTests/) [info] Packaging /Users/Al/SbtTests/target/scala-2.10/basic_2.10-1.0.jar ... [info] Done packaging. [success] Total time: 0 s
Because compile
is a dependency of run
, you don’t have to run compile
before each run
; just type sbt run
.
The JAR file created with sbt package
is a normal Java JAR file. You can list its contents with the usual jar tvf
command:
$ jar tvf target/scala-2.10/basic_2.10-1.0.jar 261 Sat Apr 13 13:58:44 MDT 2013 META-INF/MANIFEST.MF 0 Sat Apr 13 13:58:44 MDT 2013 foo/ 0 Sat Apr 13 13:58:44 MDT 2013 foo/bar/ 0 Sat Apr 13 13:58:44 MDT 2013 foo/bar/baz/ 2146 Sat Apr 13 13:57:52 MDT 2013 foo/bar/baz/Main$.class 1003 Sat Apr 13 13:57:52 MDT 2013 foo/bar/baz/Main.class 759 Sat Apr 13 13:57:52 MDT 2013 foo/bar/baz/Main$delayedInit$body.class
You can also execute the main
method in the JAR file with the Scala interpreter:
$ scala target/scala-2.10/basic_2.10-1.0.jar Hello, world SBT commands
As with any Java-based command, there can be a little startup lag time involved with running SBT commands, so when you’re using SBT quite a bit, it’s common to run these commands in interactive mode from the SBT shell prompt to improve the speed of the process:
$ sbt > compile > run > package
You can run multiple commands at one time, such as running clean
before compile
:
> clean compile
Common SBT commands
At the time of this writing, there are 247 SBT commands available (which I just found out by hitting the Tab key at the SBT shell prompt, which triggered SBT’s tab completion). Table 18-1 shows a list of the most common commands.
Table 18-1. Descriptions of the most common SBT commands
Command | Description |
---|---|
clean |
Removes all generated files from the target directory. |
compile |
Compiles source code files that are in src/main/scala, src/main/java, and the root directory of the project. |
~ compile |
Automatically recompiles source code files while you’re running SBT in interactive mode (i.e., while you’re at the SBT command prompt). |
console |
Compiles the source code files in the project, puts them on the classpath, and starts the Scala interpreter (REPL). |
consoleQuick |
Starts the Scala interpreter (REPL) with the project dependencies on the classpath. |
doc |
Generates API documentation from your Scala source code using scaladoc. |
help |
Issued by itself, the help command lists the common commands that are currently available. When given a command, help provides a description of that command. |
inspect
|
Displays information about
|
package |
Creates a JAR file (or WAR file for web projects) containing the files in src/main/scala, src/main/java, and resources in src/main/resources. |
package-doc |
Creates a JAR file containing API documentation generated from your Scala source code. |
publish |
Publishes your project to a remote repository. See Recipe 18.15, “Publishing Your Library”. |
publish-local |
Publishes your project to a local Ivy repository. See Recipe 18.15, “Publishing Your Library”. reload Reloads the build definition files (build.sbt, project/.scala, and project/.sbt), which is necessary if you change them while you’re in an interactive SBT session. |
run |
Compiles your code, and runs the main class from your project, in the same JVM as SBT. If your project has multiple main methods (or objects that extend App), you’ll be prompted to select one to run. |
test |
Compiles and runs all tests. |
test:console |
Compiles the source code files in the project, puts them on the classpath, and starts the Scala interpreter (REPL) in “test” mode (so you can use ScalaTest, Specs2, ScalaCheck, etc.). |
update |
Updates external dependencies. |
There are many other SBT commands available, and when you use plug-ins, they can also make their own commands available. For instance, Recipe 18.7, “Using SBT with Eclipse” shows that the “sbteclipse” plug-in adds an eclipse
command. See the SBT documentation for more information.
Continuous compiling with SBT
As mentioned, you can eliminate the SBT startup lag time by starting the SBT interpreter in “interactive mode.” To do this, type sbt
at your operating system command line:
$ sbt >
When you issue your commands from the SBT shell, they’ll run noticeably faster.
As shown in the Solution, you can issue the compile
command from within the SBT shell, but you can also take this a step further and continuously compile your source code by using the ~ compile
command instead. When you issue this command, SBT watches your source code files, and automatically recompiles them whenever it sees the code change.
To demonstrate this, start the SBT shell from the root directory of your project:
$ sbt
Then issue the ~ compile
command:
> ~ compile [info] Compiling 1 Scala source to /Users/Al/SbtTests/target/scala-2.10/classes [success] Total time: 4 s, completed Apr 13, 2013 2:34:23 PM 1. Waiting for source changes... (press enter to interrupt)
Now, any time you change and save a source code file, SBT automatically recompiles it. You’ll see these new lines of output when SBT recompiles the code:
[info] Compiling 1 Scala source to /Users/Al/SbtTests/target/scala-2.10/classes [success] Total time: 2 s, completed Apr 13, 2013 2:34:32 PM 2. Waiting for source changes... (press enter to interrupt) Use last to get more information on the last command
From time to time when working in the SBT shell you may have a problem, such as with incremental compiling. When issues like this come up, you may be able to use the shell’s last
command to see what happened.
For instance, you may issue a compile
command, and then see something wrong in the output:
> compile [info] Updating ... [info] Resolving com.typesafe#config;1.0.0 ... 18.2. Compiling, Running, and Packaging a Scala Project with SBT | 587 [info] Compiling 1 Scala source to YIKES!
I made up the “YIKES!” part, but you get the idea; something goes wrong. To see what happened, issue the last compile
command:
> last compile [debug] [debug] Initial source changes: [debug] removed:Set() [debug] added: Set(/Users/Al/Projects/Scala/Foo/Test.scala) [debug] modified: Set() [debug] Removed products: Set() [debug] Modified external sources: Set() many more lines of debug output here ...
The last
command prints logging information for the last command that was executed. This can help you understand what’s happening, including understanding why something is being recompiled over and over when using incremental compilation.
Typing help last
in the SBT interpreter shows a few additional details, including a note about the last-grep
command, which can be useful when you need to filter a large amount of output.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
More Scala/SBT information
- The SBT command-line reference.
- Information on publishing an SBT project.
- Incremental compiling can often be much (much!) faster than compiling an entire project. See the Scala website for more details on how it works in SBT.
- Lightbend has made SBT’s incremental compiler available as a standalone tool named Zinc, which can be used with other tools, like Maven.