Essential SBT (build tool for Scala)

By William "Scala William" Narmontas

Don't learn how SBT works, learn how to use it!

To run these commands you'll need a shell or a terminal. For Windows, you may want to install Babun, a Windows shell you will love.

SBT installation steps: Mac, Windows, Linux, or manual. You'll also need Oracle JDK 8 or OpenJDK 8.

The aim of this is to show you how SBT can be simple to get started with, and how to get production level iterations going. We don't aim to build a strong understanding of SBT - there are plenty of other materials online for this. The aim is to be as minimalist as possible: no text editors, no IDEs; just a copy-paste into the shell. Follow along with the video if you're like to see how this works. I hope this to be more fruitful for somebody who comes, say, from a Python or PHP background and prefer to see immediate results.

Essential #SBT for #Scala - https://t.co/LUKDY8w1Zr

— William Narmontas (@ScalaWilliam) December 10, 2016


Create an basic empty SBT project

AHH
mkdir -p new-project
cd new-project
mkdir -p project
echo 'sbt.version=0.13.13' > project/build.properties

Compile a project

sbt compile

Recompile on code change

sbt '~compile'

Create a source file

mkdir -p src/main/scala/
cat > src/main/scala/SimpleApp.scala << EOF
object SimpleApp extends App {
  println("It works")
}
EOF

Run continuously

Run ~ commands in another terminal window. Press Enter to exit.

sbt '~run'

Change source to verify it works

sed -i -e 's/works/definitely works/' \
      src/main/scala/SimpleApp.scala

Add ScalaTest

echo 'libraryDependencies += "org.scalatest" %%' \
       '"scalatest" % "3.0.1" % "test"' >> build.sbt

Test continuously

sbt '~test'

Create a test

mkdir -p src/test/scala/
cat > src/test/scala/SimpleTest.scala << EOF
import org.scalatest._
import Matchers._
class SimpleTest extends FunSuite {
  def sum(a: Int, b: Int): Int = ???
  test("sum works for 1 + 2 = 3") {
    sum(1, 2) shouldEqual 3
  }
}
EOF

Make it pass

sed -i -e 's/???/a + b/' src/test/scala/SimpleTest.scala

Use Scala 2.12

echo 'scalaVersion := "2.12.1"' >> build.sbt

Add the sbt-native-packager plugin

echo 'addSbtPlugin("com.typesafe.sbt" % '\
       '"sbt-native-packager" % "1.1.4")' \
       > project/native-packager.sbt

Enable sbt-native-packager

echo 'enablePlugins(JavaAppPackaging)' >> build.sbt

Package the app

sbt 'show dist'

Run packaged app

unzip -o -d /tmp/my-project \
        ./target/universal/new-project-0.1-SNAPSHOT.zip
/tmp/my-project/new-project-0.1-SNAPSHOT/bin/new-project

Dockerize your app

eval $(docker-machine env default)
sbt docker:publishLocal

Run the docker image

docker run new-project:0.1-SNAPSHOT

Create a submodule

echo 'lazy val fizzbuzz = project' >> build.sbt

Compile the submodule

sbt fizzbuzz/compile

Use Scala 2.12 for submodule

echo '  .settings(scalaVersion := "2.12.1")' >> build.sbt

Add ScalaTest to submodule

echo '  .settings(libraryDependencies += "org.scalatest"' \
       '%% "scalatest" % "3.0.1" % "test")' >> build.sbt

Continually test the submodule

sbt '~fizzbuzz/test'

Create a method in the submodule

mkdir -p fizzbuzz/src/main/scala/
cat > fizzbuzz/src/main/scala/Fizz.scala << EOF
object Fizz {
  def buzz(n: Int): String = ???
}
EOF

Create a test in the submodule

mkdir -p fizzbuzz/src/test/scala/
cat > fizzbuzz/src/test/scala/FizzTest.scala << EOF
import org.scalatest._
import Matchers._
class FizzTest extends FunSuite {
  test("fizzBuzz for 4 is 4") {
    Fizz.buzz(4) shouldEqual "4"
  }
}
EOF

Pass the test

sed -i -e 's/???/s"$n"/' \
      fizzbuzz/src/main/scala/Fizz.scala

Make the app depend on the submodule

echo 'dependsOn(fizzbuzz)' >> build.sbt

Use the submodule in the app

sed -i -e 's/".*"/Fizz.buzz(args.last.toInt)/'\
      src/main/scala/SimpleApp.scala

Run the app with arguments

sbt "run --- 123"

Explicitly name the project

echo 'name := "new-project"' >> build.sbt

Explicitly version the project

echo 'version := "0.2"' >> build.sbt

Add JSoup Java library to main project

echo 'libraryDependencies += "org.jsoup" % "jsoup"' \
       '% "1.10.1"' >> build.sbt

Print all BBC News headlines

mkdir -p src/main/scala/
cat > src/main/scala/SimpleApp.scala << EOF
import org.jsoup.Jsoup
import scala.collection.JavaConverters._
object SimpleApp extends App {
  val doc = Jsoup.connect("http://www.bbc.com/news").get()
  val newsHeadlines = doc.select(".title-link__title-text")
  newsHeadlines.asScala.map(_.text()).foreach(println)
}
EOF

You may also find this interesting

I just published “Limit degrees of freedom in development” https://t.co/A99m04mIua

— William Narmontas (@ScalaWilliam) December 17, 2016

This page is editable on GitHub.