Documentation

You are viewing the documentation for the 2.8.4 release in the 2.8.x series of releases. The latest stable release series is 3.0.x.

§Debugging your build

If you are having difficulties getting sbt to do what you want it to do, you may need to use some of the built in utilities that sbt provides to help you debug your build.

§Debugging dependencies

By default, sbt generates reports of all your dependencies, including dependency trees to show which dependencies transitively brought in other dependencies, and conflict resolution tables showing how sbt decided which version of a dependency it selected when multiple were requested.

The reports are generated into xml files, with an accompanying XSL stylesheet that allow browsers that support XSL to convert the XML reports into HTML. Browsers with this support include Firefox and Safari, and notably don’t include Chrome.

The reports can be found in the target/scala-2.12/resolution-cache/reports/ directory of your project, one is generated for each scope in your project, and are named organization-projectId_scalaVersion-scope.xml, for example, com.example-my-first-app_2.13-compile.xml. When opened in Firefox, this report looks something like this:

§Debugging settings

There are a few useful commands that sbt provides that can be used to understand your build and work out where things may be going wrong.

§The show command

The show command shows the return value from any sbt task. So for example, if you’re not sure if a certain source file is being compiled or not, you can run show sources to see if sbt is including it in the sources:

[my-first-app] $ show sources
[info] ArrayBuffer(my-first-app/app/controllers/Application.scala, 
  my-first-app/target/scala-2.13/twirl/main/views/html/index.template.scala,
  my-first-app/target/scala-2.13/twirl/main/views/html/main.template.scala,
  my-first-app/target/scala-2.13/src_managed/main/routes_reverseRouting.scala,
  my-first-app/target/scala-2.13/src_managed/main/routes_routing.scala,
  my-first-app/target/scala-2.13/src_managed/main/controllers/routes.java)

The output above has been formatted to ensure it fits cleanly on the screen, you may need to copy it to an editor to make sense of it if the task you run returns a long list of items.

You can also specify a task a particular scope, eg test:sources or compile:sources, or for a particular project, my-project/compile:sources, and in some cases, where tasks are scoped by another task, you can specify that scope too, for example, to see everything that will be packaged into your projects jar file, you want to show the mappings task, scoped to the packageBin task:

[my-first-app] $ show compile:packageBin::mappings
[info] List(
  (my-first-app/target/scala-2.13/classes/application.conf,application.conf),
  (my-first-app/target/scala-2.13/classes/controllers/Application.class,controllers/Application.class),
  ...

§The inspect command

The inspect command gives you detailed information about a task, including what it depends on, what depends on it, where it was defined, etc. It can be used like the show command:

[my-first-app] $ inspect managedSources
[info] Task: scala.collection.Seq[java.io.File]
[info] Description:
[info] 	Sources generated by the build.
[info] Provided by:
[info] 	{file:my-first-app/}root/compile:managedSources
[info] Defined at:
[info] 	(sbt.Defaults) Defaults.scala:185
[info] Dependencies:
[info] 	compile:sourceGenerators
[info] Reverse dependencies:
[info] 	compile:sources
...

Here we’ve inspected the managedSources command, it tells us that this is a task that produces a sequence of files, it has a description of Sources generated by the build. You can see that it depends on the sourceGenerators task, and the sources task depends on it. You can also see where it was defined, in this case, it’s from sbt’s default task definitions, line 185.

§The inspect tree command

The inspect tree command shows a whole tree of task dependencies for a particular task. If we inspect the tree for the unmanagedSources task, we can see it here:

[my-first-app] $ inspect tree unmanagedSources
[info] compile:unmanagedSources = Task[scala.collection.Seq[java.io.File]]
[info]   +-*/*:sourcesInBase = true
[info]   +-*/*:unmanagedSources::includeFilter = sbt.SimpleFilter@3dc46f24
[info]   +-compile:unmanagedSourceDirectories = List(my-first-app/app, my-first-a..
[info]   | +-compile:javaSource = app
[info]   | | +-*:baseDirectory = my-first-app
[info]   | |   +-*:thisProject = Project(id root, base: my-first-app, configurations: List(compile,..
[info]   | |   
[info]   | +-compile:scalaSource = app
[info]   |   +-*:baseDirectory = my-first-app
[info]   |     +-*:thisProject = Project(id root, base: my-first-app, configurations: List(compile,..
[info]   |     
[info]   +-*:baseDirectory = my-first-app
[info]   +-*/*:excludeFilter = sbt.HiddenFileFilter$@49e479da

This shows the whole tree of tasks that sbt uses to discover the sources in your project, including the filters to decide which files to be included or excluded. The inspect tree command is particularly useful when you’re not sure how some part of your build is structured, and you want to find out how it all fits together so you can then dive in deeper.

§Debugging incremental compilation

A common problem that people have in Play is they find Play recompiles and reloads when they don’t expect it to. This is often caused by source generators or IDEs that inadvertently update elements of Play’s classpath, forcing a reload. To debug problems like this, we can look at the debug log from the compile task. When sbt runs a task it captures all the log output, whether it displays it or not, so that you can inspect it later if you want. It can be inspected using the last command.

So, let’s say you compile, and a file needs to be recompiled:

[my-first-app] $ compile
[info] Compiling 1 Scala source to my-first-app/target/scala-2.13/classes...
[success] Total time: 1 s, completed 07/04/2015 1:28:43 PM

You can get a full debug log of what happened during the compile command by running last compile. This will dump a lot of output, but only the first part is what we are interested in, shown here:

[my-first-app] $ last compile
[debug]
[debug] Initial source changes:
[debug] 	removed:Set()
[debug] 	added: Set()
[debug] 	modified: Set(my-first-app/app/controllers/Application.scala)
[debug] Removed products: Set()
[debug] External API changes: API Changes: Set()
[debug] Modified binary dependencies: Set()
[debug] Initial directly invalidated sources: Set(my-first-app/app/controllers/Application.scala)
[debug]
[debug] Sources indirectly invalidated by:
[debug] 	product: Set()
[debug] 	binary dep: Set()
[debug] 	external source: Set()

What this tells us is that a recompile was triggered because my-first-app/app/controllers/Application.scala was modified.

Next: Configuration