Some(SBT)
By
Sam Smoot
.NET
Ruby
Other Things
Scala is the closest I've ever been to being a Rocket Scientist :-p
http://substantiality.net
(When I bother to make sure it's online.)
ssmoot@gmail.com
@srsmoot
Chris Gibson
.NET
Perl
Ruby
Rails
Beer
Hates Musicals
"I think Scala... TBD"
@chrislgibson
¡Inspirado!
Introduction
What is SBT?
Simple Build Tool
(Scala Build Tool?)
Features
- Scala REPL
- Framework for Writing Tasks
-
Continuous Testing
- Incremental Compilation
- Documentation Generation
- Manages Packaged Dependencies (JARs)
- Manages Source Dependencies (File, or Git)
- Packaging
- Deployment
- Builds Mixed Scala/Java Projects
- Aggregate Multiple Projects
- Manage Dependencies Between Projects
Installation
(Official Docs)
OSX
brew install sbt
brew install sbt
Commands
-
help
- clean
- compile
- console
- exit
- project
- reload
- run
- session
- set
- show
- test
- test-only
- update
Structure of a SBT Project
- Sources in the base directory
- Sources in src/main/scala or src/main/java
- Tests in src/test/scala or src/test/java
- Data files in src/main/resources or src/test/resources
- jars in lib
Basic Maven layout.
If you're working at a Java shop you might be able to replace Maven with SBT today.
SBT Files
build.sbt
Recursive (in multi-project structures)
project/Build.scala
For arbitrary Scala code like writing Tasks, or to define intra-project dependencies in a multi-project build definition.
project/build.properties
sbt.version=0.12.2
project/plugins.sbt
addSbtPlugin("play" % "sbt-plugin" % "2.1.0")
build.sbt Vs Build.scala
Don't Worry, Be Happy
My First SBT Project
Giter8
https://github.com/wiecklabs/wieck-scala.g8
Create a template project using your favorite SBT settings, then use it to quickly generate new projects.
g8 wiecklabs/wieck-scala
Interactive
Minimal
$ mkdir example
$ cd example
$ sbt
> set name := "bob"
> session save
> exit
Baseline
> set name := "bob"
> set version := "1.0-SNAPSHOT"
> set scalaVersion := "2.10.1"
None At All
$ cat <<EOS | tee Hi.scala > /dev/null object Hi { def main(args: Array[String]) = println("Hi!") } EOS $ sbt run
[info] Loading global plugins from /Users/sam/.sbt/plugins [info] Set current project to default-3da5a3 (in build file:/Users/sam/src/example/) [info] Updating {file:/Users/sam/src/example/}default-3da5a3... [info] Resolving org.scala-lang#scala-library;2.9.2 ... [info] Done updating. [info] Compiling 1 Scala source to /Users/sam/src/example/target/scala-2.9.2/classes... [info] Running Hi Hi! [success] Total time: 2 s, completed May 22, 2013 3:17:06 PM
Pro-Tip
Use the right Scala
$ sbt ++2.10.1 run
[info] Loading global plugins from /Users/sam/.sbt/plugins [info] Set current project to default-3da5a3 (in build file:/Users/sam/src/example/) Setting version to 2.10.1 [info] Set current project to default-3da5a3 (in build file:/Users/sam/src/example/) [info] Updating {file:/Users/sam/src/example/}default-3da5a3... [info] Resolving org.scala-lang#scala-library;2.10.1 ... [info] Done updating. [info] Compiling 1 Scala source to /Users/sam/src/example/target/scala-2.10/classes... [info] Running Hi Hi!
There Can Be Only One?
$ cd example
$ ls
Hi.scala
$ cat <<EOS | tee Hello.scala > /dev/null object Hello { def main(args: Array[String]) = println("Hello!") } EOS
$ ls
Hi.scala Hello.scala
$ sbt ++2.10.1 run [info] Loading global plugins from /Users/sam/.sbt/plugins [info] Set current project to default-3da5a3 (in build file:/Users/sam/src/example/) Setting version to 2.10.1 [info] Set current project to default-3da5a3 (in build file:/Users/sam/src/example/) Multiple main classes detected, select one to run: [1] Hi [2] Hello Enter number: 2 [info] Running Hello Hello! [success] Total time: 2 s, completed May 22, 2013 3:54:42 PM
From Scratch
$ mkdir example
$ cd example
$ mkdir project
$ echo "sbt.version=0.12.2" | tee project/build.properties
$ vim build.sbt
build.sbt Syntax
- Blank line between expressions
- %% means "Current Scala Version"
- := is for binding, not assignment
- Dependencies are scoped
- Build commands are scoped
InAction
name := "scube"
version := "1.0-SNAPSHOT"
scalaVersion := "2.10.1"
scalacOptions in ThisBuild ++= Seq(
"-language:_",
"-feature",
"-unchecked",
"-deprecation")
resolvers ++= Seq(
"Maven Central" at "http://repo1.maven.org/maven2",
"Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/")
testOptions in Test := Nil
parallelExecution in Test := false
libraryDependencies ++= Seq(
"joda-time" % "joda-time" % "2.2" % "test",
"org.joda" % "joda-convert" % "1.2" % "test",
"com.typesafe" %% "scalalogging-slf4j" % "1.0.1",
"org.codehaus.janino" % "janino" % "2.6.1",
"ch.qos.logback" % "logback-classic" % "1.0.13",
"org.scalatest" %% "scalatest" % "2.0.M5b" % "test",
"net.databinder.dispatch" %% "dispatch-core" % "0.10.0",
"com.typesafe" % "config" % "1.0.0")
The POWTJW
How would you get the classpath for an SBT project?
→ sbt > show classpath [error] Not a valid key: classpath (similar: full-classpath, classpath-types, classpath-filter)
> show full-classpath [info] Compiling 14 Scala sources to /Users/sam/src/scube/target/scala-2.10/classes... [info] List(Attributed(/Users/sam/src/scube/target/scala-2.10/classes), Attributed(/Users/sam/.sbt/boot/scala-2.10.1/lib/scala-library.jar), Attributed(/Users/sam/.ivy2/cache/com.typesafe/scalalogging-slf4j_2.10/jars/scalalogging-slf4j_2.10-1.0.1.jar), Attributed(/Users/sam/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.10.0.jar), Attributed(/Users/sam/.ivy2/cache/net.databinder.dispatch/dispatch-core_2.10/jars/dispatch-core_2.10-0.10.0.jar), Attributed(/Users/sam/.ivy2/cache/com.ning/async-http-client/jars/async-http-client-1.7.11.jar), Attributed(/Users/sam/.ivy2/cache/io.netty/netty/bundles/netty-3.6.3.Final.jar), Attributed(/Users/sam/.ivy2/cache/com.typesafe/config/bundles/config-1.0.0.jar)) [success] Total time: 7 s, completed May 23, 2013 2:01:28 PM
The Principle of Wow That Just Wokred!
Grumpy Cat
Task Mastery
Hello World
import sbt._
import Keys._
object HelloBuild extends Build {
val hello = TaskKey[Unit]("hello", "Prints 'Hello World!'")
val helloTask = hello := println("Hello World!")
lazy val main = Project("HelloWorld", file(".")) settings helloTask
}
Discovering Tasks
$ sbt
> tasks # Displays the main tasks > tasks -v # Displays additional tasks > help tasks -V # Displays all tasks
Having Arguments
$ sbt
> my-task -- --some-option=foo -Z
Pro-Tip
(Use the Tilde Luke!)
$ sbt
> ~test
> ~test-only
> ~compile
> ~run
~clean
Working With Multiple Projects
Project
Where you define project names, paths, settings.
RootProject
A Reference to an external Project (like a github repository).
Running Commands in Sub-Projects
$ sbt
> ming/test
> flash/test
> test
> play/run
> project play
> run
^D
> test
> project /
Aggregating Projects
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
lazy val root = Project(id = "farm", base = file(".")).
aggregate(util, io, data, web, migrations)
lazy val util = Project("farm-util", file("farm-util")).dependsOn(hasher)
lazy val utilWithTest = util % "test->test;compile->compile"
lazy val io = Project("farm-io", file("farm-io"))
lazy val data = Project("farm-data", file("farm-data")).dependsOn(utilWithTest)
lazy val web = play.Project("farm-web", path = file("farm-web")).
settings(libraryDependencies += "io.spray" %% "spray-json" % "1.2.3").
dependsOn(utilWithTest, io, data, hasher)
lazy val hasher = RootProject(uri("git://github.com/Nycto/Hasher.git"))
}
build.sbt for Aggregate Project
name := "Farm"
version := "1.0-SNAPSHOT"
scalaVersion in ThisBuild := "2.10.1"
scalacOptions in ThisBuild ++= Seq(
"-language:_",
"-feature",
"-unchecked",
"-deprecation")
testOptions in Test := Nil
parallelExecution in Test := false
resolvers ++= Seq(
"Maven Central" at "http://repo1.maven.org/maven2",
"Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/")
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M5b" % "test"
in ThisBuild
Applies to all projects/contexts in the Build.
build.sbt for Child Project
name := "farm-data"
version := "1.0-SNAPSHOT" parallelExecution in Test := false libraryDependencies ++= Seq( "net.databinder.dispatch" %% "dispatch-core" % "0.10.0", "com.typesafe.akka" %% "akka-actor" % "2.1.0", "com.typesafe.akka" %% "akka-testkit" % "2.1.0", "com.typesafe" %% "scalalogging-slf4j" % "1.0.1", "joda-time" % "joda-time" % "2.2", "org.joda" % "joda-convert" % "1.2" % "compile", "com.typesafe.akka" %% "akka-slf4j" % "2.1.0", "ch.qos.logback" % "logback-classic" % "1.0.1" % "runtime")
That Feeling?
It's The Feel of Winning.
Global Configuration
Resolving PermGen Errors
$ cat ~/.sbtconfig
SBT_OPTS="-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:PermSize=256M -XX:MaxPermSize=512M"
Plugins
$ cat ~/.sbt/plugins/build.sbt
logLevel := Level.Warn
resolvers += "Maven Central" at "http://repo1.maven.org/maven2/"
resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.4.0")
addSbtPlugin("org.ensime" % "ensime-sbt-cmd" % "0.1.1")
IntelliJ IDEA Plugin
$ sbt
> gen-idea
Sharing Your Code
Nexus
(If you can figure it out)
Ivy
(Just scp the files up somewhere)
Source
(Stick it in a public git repository)
Documentation
History
SBT < 0.10.0
Problems
- Starting a Play app in a multi-project Build
- PermGen
- Typesafe Repository
- Debugger Integration (I am not a smart man)
- Actor Systems (making a clean exit)
Ctrl-C
Further Reading
Josh Suereth's "SBT in Action" MEAP Book
(covers SBT version 0.13.0)
Summary
"At the very root of the Scala eco-system, lies SBT."
-Sam Smoot © 2013
SBT is to Scala, as RVM, RDoc, Rubygems, Bundler, Rake, Rackup
and Continuous Testing is to Ruby. Ruby wouldn't be Ruby without those, and Scala wouldn't be the Scala you know without SBT.
Thank You
Mark Harrah
For writing the best build/test/run/compile/packaging tool ever written.
Some(SBT)
By Sam Smoot
Some(SBT)
- 1,522