Mike Slinn

SBT Tasks and Settings

— Draft —

Published 2013-09-19. Last modified 2019-11-05.
Time to read: 10 minutes.

This lecture discusses SBT tasks and settings.

This lecture discusses the functionality provided by sbt on macOS, Linux and WSL.

The native Windows installer places a file called sbt.bat script in C:\Program Files (x86)\sbt\bin. The Windows sbt.bat script does not support the features I describe in this section. That’s fine, because you would never use native Windows to develop Scala code, right?

sbt Script

Sbt is actually a Java jar that is normally launched by a bash script called sbt. To discover where the bash script resides, type:

Shell
$ which sbt
/usr/bin/sbt 

You can discover the command-line arguments that the sbt bash script supports using the -h option.

Shell
$ sbt -h
Usage: sbt [options]
-h | -help print this message -v | -verbose this runner is chattier -d | -debug set sbt log level to debug -no-colors disable ANSI color codes -sbt-create start sbt even if current directory contains no sbt project -sbt-dir <path> path to global settings/plugins directory (default: ~/.sbt) -sbt-boot <path> path to shared boot directory (default: ~/.sbt/boot in 0.11 series) -ivy <path> path to local Ivy repository (default: ~/.ivy2) -mem <integer> set memory options (default: 1024, which is -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m) -no-share use all local caches; no sharing -no-global uses global caches, but does not use global ~/.sbt directory. -jvm-debug <port> Turn on JVM debugging, open at the given port. -batch Disable interactive mode
# sbt version (default: from project/build.properties if present, else latest release) -sbt-version <version> use the specified version of sbt -sbt-jar <path> use the specified jar as the sbt launcher -sbt-rc use an RC version of sbt -sbt-snapshot use a snapshot version of sbt
# java version (default: java from PATH, currently java version "1.8.0_201") -java-home <path> alternate JAVA_HOME
# jvm options and output control JAVA_OPTS environment variable, if unset uses "" .jvmopts if this file exists in the current directory, its contents are appended to JAVA_OPTS SBT_OPTS environment variable, if unset uses "" .sbtopts if this file exists in the current directory, its contents are prepended to the runner args /etc/sbt/sbtopts if this file exists, it is prepended to the runner args -Dkey=val pass -Dkey=val directly to the java runtime -J-X pass option -X directly to the java runtime (-J is stripped) -S-X add -X to sbt’s scalacOptions (-S is stripped)
In the case of duplicated or conflicting options, the order above shows precedence: JAVA_OPTS lowest, command line options highest.
If this is the first time you run SBT, there will be a long pause while it downloads the entire known universe.
Two command-line options are worthy of special mention: -jvm-debug and -mem.

Enabling Remote Debugging

You can enable remote debugging on any JVM program, using a JVM option similar to this.

JVM Option
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999

The sbt script has a handy command-line option, -jvm-debug, which does the same thing. To use it, just specify the port.

Shell
$ sbt -jvm-debug 9999 run

The sbt script will launch your Scala program and you can attach a debugger to it if the program gives you time to do so before it finishes. If you want the debugger to pause while you attach a debugger, you need to specify the long form, with suspend=y, and preface the options with -J.

Shell
$ sbt -JXrunjdwp:transport=dt_socket,server=y,suspend=y,address=9999 run

Here is a variant that saves all output to a file called output.txt.

Shell
$ sbt -no-colors -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9999 run &> output.txt

Allocating Memory

The -mem option allocates the total amount of memory specified according to logic present in sbt-launch.bash, a bash script invoked by sbt. Thus if you have 2G of memory for your Scala program, specify -mem 2G and the memory will be allocated properly.

SBT Command Line Tasks

Following are commonly used command-line tasks. Here is a more complete list. Here is an index of common methods, types, and values you might find in an sbt build definition.

clean
Deletes files produced by the build, such as generated sources, compiled classes, and task caches. It does not remove all compiled artifacts. Here is deeper clean:
Shell
$ rm -rf target/*
compile
Compiles sources; automatically runs update first.
~compile
Continuously compiles sources each time a file changes.
console
Starts the Scala interpreter with the project dependencies, compiled code and resources on the classpath.
doc
Generates Scaladoc for src/main into target/api/index.html. See also test:doc.
help command*
Displays help message or prints detailed help on requested commands.
offline
Configures SBT to work without a network connection where possible.
project
Selects a specific SBT project in an SBT project that has multiple subprojects
run
Runs a main class; compiles if necessary. Currently does not detect Java classes with static void main() methods. However, you can type the following to see all Java and Scala entry points:
Shell
$ sbt ’show discoveredMainClasses’
~run
Runs a main class, recompiling each time you change a file. Currently does not detect Java classes with static void main() methods.
runMain
Runs a main class, passing along arguments provided on the command line. This task can also be specified as run-main. For example:
Shell
$ sbt ’runMain com.micronautics.akka.dispatch.futureScala.Zip’
Windows does not understand single quotes, so you must use double quotes with Windows:
Shell
$ sbt "runMain com.micronautics.akka.dispatch.futureScala.Zip"
test
Executes all tests that are not marked with ignore.
~testQuick
Like test, but uses incremental compiler to only run test classes that are affected by your latest change. This task can also be specified as ~test-quick.
testOnly
Executes tests in one test class. This task can also be specified as test-only. For example:
Shell
$ sbt ’testOnly com.blah.MyTest’
Windows does not understand single quotes, so you must use double quotes with Windows:
Shell
$ sbt "testOnly com.blah.MyTest"
update
Download binary dependencies, including transitive dependencies. If your project uses dependencies that are snapshots, the most recent versions are downloaded.
updateClassifiers
Download sources and Javadoc for all project dependencies
updateSbtClassifiers
Download sources and Javadoc for all meta-project dependencies

Do You Commute to Work?

If you commute to work and would like to learn Scala or do Scala programming with your laptop, but you don’t have a WiFi connection while en route, here is the magic incantation that you should type before leaving:

Shell
$ git pull
$ sbt ";update ;updateClassifiers ;set offline:=true"

When you get to the office, type:

Shell
$ sbt ";set offline:=false"

Playing with SBT Console

The SBT console is a REPL that provides your entire project, including dependencies, on the classpath. This allows you to manually invoke the code in your project in an interactive session, providing that your code compiles.

The courseNotes directory in the git repository provided with this course contains a sample project. Start by cloning the repository.

Shell
$ cd ~/myScalaProjects
$ git clone https://scalaStudent@bitbucket.org/mslinn/course_scala_intro_code.git

Now change to the courseNotes/ directory.

Shell
$ cd course_scala_intro_code/courseNotes

The dependencies are specified in build.sbt like this.

build.sbt dependencies
libraryDependencies ++= Seq(
  "org.specs2"    %% "specs2-core"  % "4.5.1"        % Test withSources(),
    "org.specs2"    %% "specs2-junit" % "4.5.1"        % Test withSources(),
  "org.scalatest" %% "scalatest"    % "3.1.0-SNAP9"  % Test withSources(),
  "junit"         %  "junit"        % "4.12"         % Test // Scala IDE requires this; IntelliJ IDEA does not
)

Also notice the initialCommands section in the same build.sbt file that specifies the command executed when sbt console, sbt consoleQuick, but not the sbt consoleProject tasks are executed.

build.sbt initialComments
initialCommands in console := """
  import java.io.File
  import scala.language.postfixOps
  import java.net.URL
  import scala.util.control.NoStackTrace
  import scala.util.{Try,Success,Failure}
""".stripMargin

The courseNotes/ project defines several classes in the com.micronautics.scalaIntro package, including one called ScalaClass3. Let’s use the SBT console to play with that class. The first time you run that command it might take a long time to download many jars.

Scala REPL
$ sbt console
[info] Loading settings for project global-plugins from idea.sbt ...
[info] Loading global plugins from /home/mslinn/.sbt/1.0/plugins
[info] Loading settings for project coursenotes-build from build.sbt ...
[info] Loading project definition from /mnt/c/course_scala_intro_code/courseNotes/project
[info] Updating ProjectRef(uri("file:/mnt/c/course_scala_intro_code/courseNotes/project/"), "coursenotes-build")...
[info] Done updating.
[info] Loading settings for project coursenotes from build.sbt ...
[info] Set current project to intro-scala-course (in build file:/mnt/c/course_scala_intro_code/courseNotes/)
[info] Updating ...
[info] Done updating.
[info] Compiling 43 Scala sources and 2 Java sources to /mnt/c/course_scala_intro_code/courseNotes/target/scala-2.12/classes ...
[warn] /mnt/c/course_scala_intro_code/courseNotes/src/main/scala/TypeHierarchy.scala:37:66: fruitless type test: a value of type DogHog.Dog cannot also be a DogHog.Hog
[warn]   println(s"Should a dog be compared to a hog? ${dog.isInstanceOf[Hog]}")
[warn]                                                                  ^
[warn] /mnt/c/course_scala_intro_code/courseNotes/src/main/scala/CaseClasses.scala:32:98: comparing case class values of types CaseClasses2.Dog and CaseClasses2.Hog using `==’ will always yield false
[warn]   println(s"Comparing ${dog1.name} with ${hog.name} even though we should not do so gives: ${dog1==hog}")
[warn]                                                                                                  ^
[warn] /mnt/c/course_scala_intro_code/courseNotes/src/main/scala/CaseClasses.scala:84:39: comparing case class values of types StdMethods.Frog9a and StdMethods.Frog9c using `==’ will always yield false
[warn]   println(s"frog9a == frog9c=${frog9a == frog9c}")
[warn]                                       ^
[warn] there were three feature warnings; re-run with -feature for details
[warn] four warnings found
[info] Done compiling.
[info] Starting scala interpreter...
Welcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_201).
Type in expressions for evaluation.
Or try :help.
scala>
import com.micronautics.scalaIntro._ import com.micronautics.scalaIntro._
scala>
val sc3 = new ScalaClass3(1, 2.0) sc3: com.micronautics.scalaJava.ScalaClass3 = prop1=01; prop2=2.0; prop3=02; prop4=6.0

SBT Console Help

SBT console has its own command set, which you can view from the SBT console by typing :help, like this:

Scala REPL
scala> :help
All commands can be abbreviated, e.g., :he instead of :help.
:completions <string>    output completions for the given string
:edit <id>|<line>        edit history
:help [command]          print this summary or command-specific help
:history [num]           show the history (optional num is commands to show)
:h? <string>             search the history
:imports [name name ...] show import history, identifying sources of names
:implicits [-v]          show the implicits in scope
:javap <path|class>      disassemble a file or class name
:line <id>|<line>        place line(s) at the end of history
:load <path>             interpret lines in a file
:paste [-raw] [path]     enter paste mode or paste a file
:power                   enable power user mode
:quit                    exit the interpreter
:replay [options]        reset the repl and replay all previous commands
:require <path>          add a jar to the classpath
:reset [options]         reset the repl to its initial state, forgetting all session entries
:save <path>             save replayable session to a file
:sh <command line>       run a shell command (result is implicitly => List[String])
:settings <options>      update compiler options, if possible; see reset
:silent                  disable/enable automatic printing of results
:type [-v] <expr>        display the type of an expression without evaluating it
:kind [-v] <type>        display the kind of a type.
see also :help kind
:warnings                show the suppressed warnings from the most recent line which had any 

Persisting, Restoring and Resetting REPL Sessions

The :save and :load commands are especially useful. You can use them to save and reload your Scala REPL or sbt console sessions, including definitions you create on the fly for classes, traits, methods and variables. Only your command input is saved; the output is not saved. Because the REPL session file is stored in plain text, you can use any text editor to modify the commands. Let’s first try out these commands using the Scala REPL.

Scala REPL
$ scala
Welcome to Scala 2.13.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_201).
Type in expressions for evaluation.
Or try :help.
scala>
val hi = "hi " hi: String = "hi "
scala>
hi * 3 res0: String = "hi hi hi "
scala>
res0.toUpperCase res1: String = "HI HI HI "
scala>
:save hi.repl
scala> :quit

Now lets look at the file we just created, hi.repl. I use the convention that the .repl filetype is for SBT repl sessions, but that is not required.

Shell
$ cat hi.repl
val hi = "hi "
hi * 3
res0.toUpperCase 

We can start a new REPL session and load the file containing the saved REPL session.

Scala REPL
$ scala
Welcome to Scala 2.13.0-RC1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_201).
Type in expressions for evaluation.
Or try :help. 
scala> :load hi.repl Loading hi... hi: String = "hi " res2: String = "hi hi hi " res3: String = "HI HI HI "

The :reset commant initializes REPL to its default state, and flushes the history.

Scala REPL
scala> :reset
Resetting interpreter state.
Forgetting this session history:
val hi = "hi " hi * 3 res0.toUpperCase
Forgetting all expression results and named terms: $intp, hi
scala> :load hi.repl Loading hi.repl... hi: String = "hi " res0: String = "hi hi hi " res1: String = "HI HI HI "

SBT console supports the :save, :load and :reset commands just the same.

Scala REPL
$  sbt console
...snip ...
Welcome to Scala 2.13.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_201).
Type in expressions for evaluation.
Or try :help.
scala> val hi = "hi " hi: String = "hi "
scala> hi * 3 res0: String = "hi hi hi "
scala> res0.toUpperCase res1: String = "HI HI HI "
scala> :save "hi.repl"
scala> :reset Resetting interpreter state. Forgetting this session history:
val hi = "hi " hi * 3 res0.toUpperCase
Forgetting all expression results and named terms: $intp, hi
scala> :load hi.repl Loading hi.repl... hi: String = "hi " res0: String = "hi hi hi " res1: String = "HI HI HI "
scala>

Exercise – Experiment

Now you can experiment live with your partially complete code base! Try the tab completion feature.

Managing Conflicting Dependencies

This section was largely made obsolete by Scala 3. If you still use Scala 2, then you will need this information. Otherwise, skip this section.

It is possible that transitive dependencies originating from various explicitly specified dependencies might have conflicting versions. If that happens, you will see a message something like this whenever sbt update is run. Remember that sbt compile first invokes sbt update, and sbt run first invokes sbt compile.

sbt Message
[warn] Multiple dependencies with the same organization/name but different versions.
To avoid conflict, pick one version:
[warn]  * org.apache.httpcomponents:httpclient:(4.2.1, 4.3.6)
[warn] There may be incompatibilities among your library dependencies.
[warn] Here are some of the libraries that were evicted:
[warn]     * com.github.nscala-time:nscala-time_2.10:2.0.0 -> 2.4.0

If you ignore this problem, and run your program, don’t be surprised if you see a runtime error message like: Exception: Attempt to invoke virtual method. This happens because a method implemented in one version of the transitive dependency was invoked but the other version of the dependency was present in the classpath.

There are two three ways of handling problems like this: ConflictManager, force() and exclude(). Only use force() or exclude(), not both.

Use ConflictManager

This was released October 2019: ConflictManager. To use it in an SBT project, add the following to project/plugins.sbt.

project/plugins.sbt fragment
// See https://www.scala-lang.org/2019/10/17/dependency-management.html#static-analysis-checks
addSbtPlugin("ch.epfl.scala" % "sbt-missinglink" % "0.2.0")
// See https://get-coursier.io/docs/sbt-coursier addSbtPlugin("io.get-coursier" % "sbt-coursier" % "2.0.0-RC4")

Add this to build.sbt:

build.sbt fragment
versionReconciliation += "*" % "*" % "semver"

Then type the following at a command prompt:

Shell
$ sbt update

Use force()

You can force SBT to use a specific version of a dependency by explicitly declaring the version that you need, along with the force() option.

build.sbt dependency specification
"org.apache.httpcomponents" % "httpclient"  % "4.3.6" withSources() force()

The force() option has the result that only the version of the dependency that you request is included, and no other versions that might be otherwise brought in as transitive dependencies will be used. Be aware that if you force a version of a transitive dependency, you might need to force all the other transitive dependencies of the dependency that referenced it.

Use exclude()

Instead of using force(), it is generally considered better form to explicitly exclude transitive dependencies in relation to the explicit dependencies that bring them in. However, force() can be easier to use than exclude(), especially when only one transitive dependency was brought in by an explicit dependency, and you just want to force that one dependency’s version.

For example, the nscala-time dependency brings in the joda-time dependency. The following tells SBT to include nscala-time v2.4.0, but not the version of joda-time \ that it normally brings along as a transitive dependency. The exclude() option merely suppresses the transitive dependency, unlike force(), which ensures that only a specific version of a dependency be used, even if other versions might otherwise have been included as transitive dependencies.

build.sbt dependency specification
"com.github.nscala-time" %% "nscala-time" % "2.4.0" exclude("joda-time", "joda-time")

Handy Settings

These SBT settings naturally require SBT, so they do not work in a Scala REPL started with the scala command.

Cross-Version Support For Scala Sources

If your project has code that only works on a specific version of Scala, place it in a specially named branch of the src/main/ directory. For example, if you have code specific to each of Scala 2.13.0-RC1, 2.11.x and 2.12.x, put your code in directories called src/main/scala-<scalaBinaryVersion>/.

src/
main/
  scala/
    anotherPackage/
      X.scala
  scala-2.13.0-RC1/
    SomethingMagic.scala
    my/
      package/
        Blah2_10.scala
  scala-2.11/
    SomethingMagic.scala
    my/
      package/
        CodeForScala2_11.scala
  scala-2.12/
    SomethingMagic.scala
    anotherPackage/
      X.scala

Notice that a version of SomethingMagic.scala is provided for each of Scala 2.13.0-RC1 and 2.11 and 2.12, but the other files are not. There is no requirement that a unique version of a file exist for each Scala version.

This feature is enabled when the SBT crossPaths setting is true, which is the default. Normally you would only specify this setting in build.sbt if you wish to disable it, like this.

build.sbt fragment
crossPaths := false

When this setting is enabled, sbt includes src/main/scala-<scalaBinaryVersion>/ to the SBT Compile scope addition to src/main/scala.

scalaBinaryVersion is for major Scala versions only; for example, both Scala 2.11.5 and Scala 2.11.8 both resolve to the same directory: src/main/scala-2.11.

Clear Screen

You can clear the screen when running SBT at any time by pressing Ctrl-L. That works pretty well, but if you want this to happen each time a build starts you have options.

The SBT Global Setup lecture discussed how .sbtrc could be used to define SBT command aliases that cleared the screen each time an update or compilation was triggered. Here is a better way, using the sbt.Watched object which will cause any triggered SBT command such as ~update, ~compile or ~run to first clear the screen. There are two ways to work with sbt.Watched.

  1. You can specify this setting for any SBT project by typing the following in response to an SBT prompt:
    Shell
    $ sbt
    [info] Loading settings for project global-plugins from idea.sbt ...
    [info] Loading global plugins from /home/mslinn/.sbt/1.0/plugins
    [info] Loading settings for project coursenotes-build from build.sbt ...
    [info] Loading project definition from /mnt/c/course_scala_intro_code/courseNotes/project
    [info] Loading settings for project coursenotes from build.sbt ...
    [info] Set current project to intro-scala-course (in build file:/mnt/c/course_scala_intro_code/courseNotes/)
    [info] sbt server started at local:///home/mslinn/.sbt/1.0/server/5fe9d8e8b553e8f095e1/sock 
    >set triggeredMessage in ThisBuild := Watched.clearWhenTriggered [info] Defining {.}/*:triggeredMessage [info] The new value will be used by *:watch [info] Reapplying settings... [info] Set current project to IntroScalaCourse (in build file:/var/work/course_scala_intermediate_code/courseNotes/)
    > ~compile // screen automatically clears prior to compilation
  2. You can permanently apply this setting to a project by adding the following to your build.sbt file:
    build.sbt fragment
    triggeredMessage in ThisBuild := Watched.clearWhenTriggered

    Now whenever you type the following, the screen will be cleared each time a changed file triggers the recompilation and rerunning of the project.
    Shell
    $ sbt ~run

    The build.sbt file for the courseNotes project already has this line in it, but it is commented out. Why don’t you uncomment it and see if you like this feature?

Control-C Trap

By default, when you run or test an application from SBT and type CTRL-C your program is terminated and SBT exits. The following setting installs a Control-C trap, which causes your program to stop without exiting SBT. Again, there are two ways to use it.

  1. You can specify this setting for any SBT project by typing the following in response to an SBT prompt:
    Shell
    $ sbt
    [info] Loading settings for project global-plugins from idea.sbt ...
    [info] Loading global plugins from /home/mslinn/.sbt/1.0/plugins
    [info] Loading settings for project coursenotes-build from build.sbt ...
    [info] Loading project definition from /mnt/c/course_scala_intro_code/courseNotes/project
    [info] Loading settings for project coursenotes from build.sbt ...
    [info] Set current project to intro-scala-course (in build file:/mnt/c/course_scala_intro_code/courseNotes/)
    [info] sbt server started at local:///home/mslinn/.sbt/1.0/server/5fe9d8e8b553e8f095e1/sock 
    > set cancelable in Global := true [info] Defining */*:cancelable [info] The new value will be used by */*:taskCancelStrategy [info] Reapplying settings... [info] Set current project to IntroScalaCourse (in build file:/var/work/course_scala_intermediate_code/courseNotes/)
    > run // Typing Control-C after run causes the program to stop without exiting SBT
  2. You can permanently apply this setting to a project by adding the following to your build.sbt file:
    cancelable in Global := true

Handy One-Liners

Some of these one-liners are inspired by the SBT manual, which can be a bit hard to read. I used the courseNotes project to generate this output.

History Commands

Rerun the last command with !!, show the command history with !: and display information about all the history-related commands with !.

Shell
$ sbt
[info] Loading settings for project global-plugins from idea.sbt ...
[info] Loading global plugins from /home/mslinn/.sbt/1.0/plugins
[info] Loading settings for project coursenotes-build from build.sbt ...
[info] Loading project definition from /mnt/c/course_scala_intro_code/courseNotes/project
[info] Loading settings for project coursenotes from build.sbt ...
[info] Set current project to intro-scala-course (in build file:/mnt/c/course_scala_intro_code/courseNotes/)
[info] sbt server started at local:///home/mslinn/.sbt/1.0/server/5fe9d8e8b553e8f095e1/sock
> !
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’ 

Get Basic Information About an SBT Project

Shell
$ sbt about
...lots of output ...
[info] This is sbt 1.2.8
[info] The current project is ProjectRef(uri("file:/mnt/c/course_scala_intro_code/courseNotes/"), "coursenotes") 2.12.8
[info] The current project is built against Scala 2.12.8
[info] Available Plugins
[info]  - com.orrsella.sbtsublime.SublimePlugin
[info]  - com.typesafe.sbteclipse.plugin.EclipsePlugin
[info]  - sbt.ScriptedPlugin
[info]  - sbt.plugins.CorePlugin
[info]  - sbt.plugins.Giter8TemplatePlugin
[info]  - sbt.plugins.IvyPlugin
[info]  - sbt.plugins.JUnitXmlReportPlugin
[info]  - sbt.plugins.JvmPlugin
[info]  - sbt.plugins.SbtPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.12.7 %}

Examine the Classpath Used For Compilation

You can’t see it in the courseNotes project because it does not have any dependencies. Here is output from another project.

Shell
$ sbt "show compile:dependencyClasspath"
...lots of output ...
[info] * Attributed(/opt/activator-dist-1.3.6/repository/org.scala-lang/scala-library/2.11.7/jars/scala-library.jar) 

Examine the Classpath Used for Unit Tests

Unit tests require a lot of dependencies.

Shell
$ sbt "show test:dependencyClasspath"
... lots of output ...
[info] * Attributed(/mnt/c/course_scala_intro_code/courseNotes/target/scala-2.12/classes)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.specs2/specs2-junit_2.12/jars/specs2-junit_2.12-4.5.1.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scalatest/scalatest_2.12/jars/scalatest_2.12-3.1.0-SNAP9.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.specs2/specs2-core_2.12/jars/specs2-core_2.12-4.5.1.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/junit/junit/jars/junit-4.12.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.1.0-SNAP9.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.specs2/specs2-matcher_2.12/jars/specs2-matcher_2.12-4.5.1.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.hamcrest/hamcrest-core/jars/hamcrest-core-1.3.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.specs2/specs2-common_2.12/jars/specs2-common_2.12-4.5.1.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.2.0.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12/bundles/scala-parser-combinators_2.12-1.1.1.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.12.8.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.specs2/specs2-fp_2.12/jars/specs2-fp_2.12-4.5.1.jar)
[info] * Attributed(/home/mslinn/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.8.jar)
[success] Total time: 1 s, completed Apr 26, 2019 10:54:19 AM
%}

List Main Classes

This does not show Java main classes, unfortunately. This list is alphabetically sorted.

Shell
$ sbt "show compile:discoveredMainClasses"
...lots of output...
[info] * AbstractSealed1
[info] * AbstractSealed2
[info] * Aliases
[info] * Animals7
[info] * Animals8
[info] * Auxiliary
[info] * Bicycle
[info] * BlendedEnums
[info] * BoldlyGo
[info] * CaseClasses1
[info] * CaseClasses2
[info] * CaseClasses3
[info] * Closures
[info] * ComplexCase
[info] * DogHog
[info] * EitherDemo1
[info] * EitherDemo2
[info] * ExtendJavaSet
[info] * Extractor
[info] * Extractors1
[info] * Extractors2
[info] * Extractors3
[info] * Fib
[info] * Fib3
[info] * Fib4
[info] * FibBad1
[info] * FibBad2
[info] * FibCheck
[info] * FibMem
[info] * FibWhile
[info] * FormattedStringSamples
[info] * Fun
[info] * Fun2
[info] * Fun3
[info] * GetSetDemo1
[info] * GetSetDemo2
[info] * HeirGround
[info] * ImplementedTrait
[info] * JavaEnum
[info] * LazyEval
[info] * LazyEvalLevel1
[info] * MaybeDog2
[info] * MultipleParamLists
[info] * OperatorOverload
[info] * OptionDemo1
[info] * OptionDemo2
[info] * PMQuiz
[info] * PackageDemo
[info] * PatMatch1
[info] * PatMatch2
[info] * PatMatch3
[info] * PatMatch4
[info] * PatMatch5
[info] * PatMatch6
[info] * PureTrait
[info] * Quine
[info] * ScalaEnum
[info] * ScalaEnumCase
[info] * SealedDemo
[info] * StdMethods
[info] * TimedDemo
[info] * TryCatch
[info] * TryCatch2
[info] * TryCatch3
[info] * TryCatch4
[info] * TryCatch5
[info] * TryDemo
[info] * TupleDemo
[info] * Tweeters
[info] * TypeWidening1
[info] * TypeWidening2
[info] * Uniform1
[info] * Uniform2
[info] * VarArgsJava
[info] * Wallflower
[info] * WhichTrait
[info] * WithFun
[info] * YodaHeIs
[info] * animals.Animals1
[info] * animals.Animals2
[info] * animals.Animals3
[info] * animals.Animals4
[info] * animals.Animals5
[info] * animals.Animals6
[info] * animals.Anonymous1
[info] * animals.Anonymous2
[info] * animals.Anonymous3
[info] * animals.ConcreteClasses
[info] * animals.VarArgsScala
[info] * com.micronautics.scalaIntro.VisibleMain
[info] * solutions.Birds1
[info] * solutions.Birds2
[info] * solutions.Emotions
[info] * solutions.Extractor
[info] * solutions.Fact
[info] * solutions.Fact1
[info] * solutions.FactLoop
[info] * solutions.FactMem
[info] * solutions.FunSel
[info] * solutions.Hello
[info] * solutions.PatMatch101a
[info] * solutions.TryCatchFinally
[info] * solutions.TupleAnswer
[info] * solutions.Unapply
[info] * solutions.Unapply2
[info] * solutions.WordSearch
[success] Total time: 1 s, completed Apr 26, 2019 10:55:10 AM 

To run a specific entry point from the list, like Tweeters, type.

Shell
$ sbt "runMain Tweeters"

If you prefer to select the entry point to run from a numbered list, type.

Shell
$ sbt run

List Unit Test Classes

Shell
$ sbt "show test:definedTestNames"
... lots of output ...
[info] Compiling 4 Scala sources to /mnt/c/course_scala_intro_code/courseNotes/target/scala-2.12/test-classes ...
[info] Done compiling.
[info] * TestScalaTest
[info] * solutions.Specs2Solution
[info] * Specs2Demo
[info] * solutions.ScalaTestSolution
[success] Total time: 7 s, completed Apr 26, 2019 10:56:56 AM 

To run a specific unit test from the list, like ScalaTestDemo, type.

Shell
$ sbt "testOnly ScalaTestDemo"

If you prefer to run all the unit tests, type.

Shell
$ sbt test

Run All Tests In a Specific Subproject

If you have an SBT project that contains multiple subprojects, you can switch between the subprojects using the project task. Here is the transcript of a project I am working on. One test passes, one test fails.

Shell
$ sbt
[info] Loading global plugins from /home/mslinn/.sbt/0.13/plugins
[info] Loading project definition from /var/work/experiments/play/play25multiproject/project
[info] Set current project to root (in build file:/var/work/experiments/play/play25multiproject/)
[root] $ projectTAB
/                                                       common
nonPlay                                                 root
serviceA                                                serviceB
{file:/var/work/experiments/play/play25multiproject/}
[root] $ project common
[info] Set current project to common (in build file:/var/work/experiments/play/play25multiproject/)
[common] $ test
[info] CommonSpec:
[info] Common
[info] - should send 404 on a bad request
[info] - should render the index page *** FAILED ***
[info]   404 was not equal to 200 (CommonSpec.scala:21)
[info] ScalaTest
[info] Run completed in 1 second, 243 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***
[error] Failed: Total 2, Failed 1, Errors 0, Passed 1
[error] Failed tests:
[error]     controllers.common.CommonSpec
[error] (common/test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 7 s, completed Jul 24, 2016 3:29:00 PM 

Here is one way to express this in one line. Note that the semicolon provides a newline character.

Shell
$ sbt "; project common; test"

Of course, if you want to run all the common project’s tests each time a source file or resource in that project is modified, preface the command with a tilde (~).

Shell
$ sbt ~"; project common; test"

Alternative Syntax

Here is an alternative syntax for specifying a project, followed by an SBT task to perform. I again specify the common project should have all tests run.

Shell
$ sbt common/test

You can also use this alternative syntax to supply runtime arguments for a specific SBT project:

Shell
$ sbt common/run arg1 arg2

Use a Specific Compiler

The ++ task sets the compiler. To use it from the command line:

Shell
$ sbt "++2.13.0! compile"
$ sbt "++2.13.0-RC1! compile"
$ sbt "++2.12.8! compile"
$ sbt "++2.11.12! compile"
$ sbt "++2.10.6! compile"

To use the ++ task from the SBT prompt:

Shell
$ sbt
> ++2.13.0!
[info] Forcing Scala version to 2.13.0 on all projects.
[info] Reapplying settings...
[info] Set current project to mslinn (in build file:/home/mslinn/work/)
> compile

Exploring Direct and Transitive Dependencies

Each dependency that you specify can itself have dependencies – these secondary dependencies are termed transitive dependencies. For example, NScala-Time has a transitive dependency on Joda-Time. You can examine the direct and transitive dependencies after an SBT update task is run, or another SBT task runs that invokes update. Once this happens, SBT generates reports in target/resolution-cache/reports/. There is a separate report for each scope (including internal/private SBT scopes). The reports are named organization-projectId_scalaVersion-scope.xml. For example, a project called test by the organization com.micronautics would have a report for the Compile scope called com.micronautics-test_2.11-compile.xml.

The courseNotes project does not have any runtime dependencies, so lets look at another project I wrote, awslib, which has a more complex build.sbt.

Shell
$ git clone https://github.com/mslinn/awslib_scala
Cloning into ’awslib_scala’...
remote: Counting objects: 1467, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 1467 (delta 4), reused 4 (delta 4), pack-reused 1459
Receiving objects: 100% (1467/1467), 427.50 KiB | 0 bytes/s, done.
Resolving deltas: 100% (568/568), done.
Checking connectivity...
done. 
$ cd awslib_scala
$ sbt update

This handy one-line command ignores files with sources in their name, and lists the remaining files of interest. The file ending with runtime.xml is the one you are most likely to be interested in.

Shell
$ (cd target/resolution-cache/reports; find .
  -name ’*.xml’ -not -name ’*$sources*’)
  ./com.micronautics-awslib_scala_2.10-provided.xml
  ./com.micronautics-awslib_scala_2.10-sources.xml
  ./com.micronautics-awslib_scala_2.10-docs.xml
  ./com.micronautics-awslib_scala_2.11-test-internal.xml
  ./com.micronautics-awslib_scala_2.11-optional.xml
  ./com.micronautics-awslib_scala_2.10-test-internal.xml
  ./com.micronautics-awslib_scala_2.11-compile.xml
  ./org.scala-sbt.temp-temp-module-750703454be73072ce64f1c215ff85550284ae5b-component.xml
  ./com.micronautics-awslib_scala_2.11-scala-tool.xml
  ./com.micronautics-awslib_scala_2.11-runtime.xml
  ./com.micronautics-awslib_scala_2.11-sources.xml
  ./com.micronautics-awslib_scala_2.11-pom.xml
  ./com.micronautics-awslib_scala_2.11-provided.xml
  ./com.micronautics-awslib_scala_2.10-scala-tool.xml
  ./com.micronautics-awslib_scala_2.11-runtime-internal.xml
  ./com.micronautics-awslib_scala_2.10-test.xml
  ./com.micronautics-awslib_scala_2.10-runtime-internal.xml
  ./com.micronautics-awslib_scala_2.10-plugin.xml
  ./com.micronautics-awslib_scala_2.11-compile-internal.xml
  ./com.micronautics-awslib_scala_2.10-compile-internal.xml
  ./com.micronautics-awslib_scala_2.10-compile.xml
  ./com.micronautics-awslib_scala_2.10-optional.xml
  ./com.micronautics-awslib_scala_2.10-ensime-internal.xml
  ./com.micronautics-awslib_scala_2.11-test.xml
  ./com.micronautics-awslib_scala_2.10-pom.xml
  ./com.micronautics-awslib_scala_2.11-ensime-internal.xml
  ./com.micronautics-awslib_scala_2.11-plugin.xml
  ./com.micronautics-awslib_scala_2.10-runtime.xml
  ./com.micronautics-awslib_scala_2.11-docs.xml

You can view these XML files in most web browsers, with the exception of Google Chrome, which cannot use a style sheet to display an XML file. You can view the most interesting file by typing the following at the command line from the awslib_scala directory.

Shell
$ firefox target/resolution-cache/reports/com.micronautics-awslib_scala_2.10-compile.xml

Even better, here is a bash command that displays the compilation dependencies in Firefox:

Shell
$ firefox `find  target/resolution-cache/reports/ -name \*compile.xml | grep -v javadoc`

Sample output looks like this:

You can also use IntelliJ IDEA to open the file from the Project pane by:

  1. Navigating to target/resolution-cache/reports/
  2. Right-clicking on com.micronautics-introscalacourse_2.11-runtime.xml
  3. Selecting Open in Browser
  4. Selecting Firefox

Suppressing the Dependency Report (Supplemental)

SBT has had an experimental feature, since v0.13.7 which can be enabled by adding this incantation to build.sbt.

build.sbt fragment
updateOptions := updateOptions.value.withCachedResolution(cachedResoluton = true)

If this statement is present, two things happen:

  1. Builds become faster because dependency graphs are cached instead of being recomputed
  2. Dependency reports are no longer generated for the project

Experimental features might be discontinued and are not guaranteed to be present in future releases. Use this feature at your own risk... however this feature has been around for years.


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.