kubuszok.com

Personally just a "developer" without X in front of it, currently working wth Scala.

I enjoy learning new things, especially more abstract like mathematics or algorithmics.

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!):

sbt.version=1.1.6


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 SBT_OPTS and 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.

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 ~compile or ~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:

addCommandAlias("myAliast", "commandToRun")


Useful plugins

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.

Build

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.

Development

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 reStart application in a fork and return control over the sbt immediately. Then you can reStop it 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

• 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 dependencyBrowseGraph quite often to check what transitive dependencies were introduced by that small little library and dependencyStats to see how much it actually affected the fat JAR’s size,
• sbt-stats - for checking how much code I wrote (not useful, but pleasant).

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 console.

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.

Summary

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!