In previous posts (#1, #2) I described a bit of theory about using sbt. However, besides long, heavy topics there are also some smaller pieces of knowledge, that you can find useful. Most of them should be obvious for people, who use sbt a little bit longer, but I decided to gather these tips here nonetheless.
Choosing sbt version yourself
To make sure, that everyone would have the same sbt version, set it using
project/build.properties file (remember to commit it to the repo!):
If you generated a project from a template, it should be there, but if you are writing things from scratch you must remember about it yourself (also, worth knowing where to look in case you want to update something). This also applies to other hints in this article.
Setting JVM options
You can change JVM options using
JVM_OPTS - putting them into these variables works same as passing them into sbt/java command. With that, you can change memory sizes, garbage collector algorithm etc. Tools like Travis CI use them to provide some better values than defaults.
In your local environment you can use a bit saner defaults just by installing Paul Philips’ wrapper. Add it as an alias to your local dev environment and on CI and you might avoid some troubles once project size will grow.
If you want to put them into a file with the wrapper default file location is set to
.jvmopts in the root of the project.
If you want some plugins/settings to be available on every project on your computer (but not necessarily as an actual part of each project), you might look into
$HOME/.sbt directory. There:
~/.sbt/1.0/plugins/plugins.sbtis a nice location for all plugins, that you are using as a part of your workflow, which might not be necessarily useful for building, testing and deploying (e.g. checking updates, displaying deps graph, generating statistics),
~/.sbt/1.0/build.sbtis a place where you could place global settings (e.g. settings for global plugins, repo credentials).
I’ll list a few nice plugins later on.
The first thing you have to do is to make it compile build files and start the shell, once you are there things are getting easier. (If sbt crashed at this step… just comment out all troublesome parts and continue).
Are you not sure how to configure this new plugin, or what options you can use in general? Few hints:
- import project into IntelliJ with sbt sources included. This will allow you to easily jump into definitions and see what keys are defined,
- for auto-plugins it’s worth knowing, that it will always be something like:
com.package.PluginName.autoPlugin- the content of
autoPluginis what is being automatically imported into
.sbtfiles, so once you find it, you can check what you can use (or what to import in
The sbt console itself also can tell you a lot of things. With
show command you can display the current value of settings, to see if they have the content you expect.
For tasks a lot of insight you can get from
inspect command. It can show you the key’s description, tell you about task’s dependencies as well as changes made to the value. It is one of the most powerful commands when it comes to sbt debugging (if not the most powerful), so take a look what it can do.
To override setting from the shell you can use
set. It might shorten a bit: change value,
reload, run task, compare results cycle.
Personally, I also find helpful to not name all
build.sbt. One of the first things I noticed when I migrated from 0.13 to 1.0 was printing of loading order of
.sbt files. I could see how they are loaded and if one of them failed, I could see which one. With several
.sbt files I no longer had to play a guessing game in order to figure out which one broke the build. That is, once I gave each of them a unique name.
It is also a good thing to extract version into
version.sbt. If you want to use some script to bump version, it can be as simple as
echo $NEW_VERSION > version.sbt.
More than just calling a task once
With sbt you might be used to running a task, waiting for it to finish, and then running another one if previous one succeeds. You might be tired of doing it manually, and try to exit the shell and call
sbt task1 task to avoid that. Well, yes, except you just killed the JVM and it needs to warm up from scratch and recalculate the build.
Instead, you might run
;task1;task2. It will run tasks sequentially and stop at first failure. Notice, that you need to start with
; - using it only as a separator is not enough.
Play users also know about this trick
~task - it will start the task, but instead of finishing it, sbt will wait and check if any dependencies it used changed, and rerun it on change. It is used for things like
~run to recompile or restart an application, when you edited the code. It will give you a way to get out of this mode
These 2 feature doesn’t necessarily mix well. If you run
;~task1;task2, you would not get to
task2 as sbt would wait for you to unlock
task1. Thus defeating the purpose of using
; in the first place. (However,
;task1;~task2 would not have this issue - it’s just a matter of being aware what is exactly happening).
If you find yourself calling such sequences often enough (and don’t want to make a separate task for it), or maybe you just have some set of parameters you are passing for a task, you might need an alias:
This is a list of plugins I am fond of. Some of them I for most of my projects, some I have put into a global scope to have them available at all time.
Plugins that supplement the build process:
- sbt-assembly and sbt-native-packager in general - while sbt gives you a way to build and test your application, it doesn’t ship with a way for you to deploy it, even as a simple fat JAR. These plugins are a must if you want to make your project actually useful,
- sbt-git - I use it to generate a project’s version number from current git commit. This way I am avoiding the issue of relying on a snapshot everywhere else.
Linting and style-checking
They probably deserve their own post, but since I already written one long time ago, I’ll just list them:
- sbt-scalastyle - a most basic style checker for Scala,
- sbt-scalafmt (and its predecessor sbt-scalariform) - the sooner you start using formatter the better. Personally, I configure it to run on compile, but most people just want to check in CI if sources are well formatted. A long time ago scalariform was what I used (and it was fast), but ever since scalafmt appeared and was improved enough for me to jump on the bandwagon,
- sbt-wartremover and/or sbt-scapegoat - while I wouldn’t like to enable all warts, enabling most of them should increase your codebase’s quality, especially if you strongly prefer a functional approach to Java-in-Scala style. I learned about wartremover first, but later on, I was shown that few rules appear on scapegoat that do not appear in wartremover. Pick your poison,
- sbt-scoverage - while pursuing high coverage blindly is stupid, I like to be able to easily check which parts of my code are not tested, and so might need more attention.
Plugins, that don’t affect build nor testing, but still make a development a little bit easier:
- sbt-revolver - not every application is a Play application that you can easily terminate with Ctrl+D. Even some terminal applications, refuse to exit after you send EoF. Yet, you would like to be able to kill them without killing the sbt. Revolver lets you
reStartapplication in a fork and return control over the sbt immediately. Then you can
reStopit any time you want without killing sbt,
- clippy - better error messages than vanilla scalac provides,
- sbt-errors-summary - gather compilation errors by files,
- sbt-bloop - if you want to use bloop for compilation.
Build checking and improvement
Useful for providing additional information about your project, and dependencies in particular:
- sbt-dependency-check - checks if your dependencies might have some vulnerabilities you should be aware of (uses OWASP list).
- sbt-dependency-update - easily check if some of your dependencies can be updated,
- sbt-dependency-graph - can visualize dependency graph. I use
dependencyBrowseGraphquite often to check what transitive dependencies were introduced by that small little library and
dependencyStatsto see how much it actually affected the fat JAR’s size,
- sbt-stats - for checking how much code I wrote (not useful, but pleasant).
Task run in fork
Sometimes you want to run program which exits. And does some cleanup on program shutdown. And you cannot rely on sbt-revolver because it happens in
fork := true // run program in forked JVM - so exiting it won't kill sbt trapExit := false // sbt don't prevent process from exiting connectInput := true // forked process still reads from std-in outputStrategy := Some(StdoutOutput) // prevent output from prepending [info]
And if you want to be able to kill currently running task without killing the sbt:
Global / cancelable := true
This one is also handy if you see that compilation would fail, but apparently compiler would run for a long time before it exits with an error message.
While none of these is a discovery, I think it might be useful to someone who just started using sbt. If you know about some other nice-to-know let me know in the comments!
Update 18.06.17 - added Task run in fork.