SBT Crash Course

Preview:

DESCRIPTION

An introduction to SBT and how it works internally. Talk from September 2013 Slovak Scala User Group meet-up, http://www.meetup.com/slovak-scala/events/133327122/

Citation preview

SBT CRASH COURSE1. Why SBT2. How SBT works3. Example of simple project4. Core concepts

WHY SBTGOOD BUILD TOOL CRITERIA

Reproducibility - automating build, gives you more time todo real stuffConventions - using sensible defaults, no need to specifyevery last option and commandExperience - distilation of developer wisdom, e.g. testbefore publishPortability - good build tool should protect you fromdifferences betweeen systemsEcosystem - allow to extend build easily

WHY SBTLITTLE HISTORY: APACHE ANT

Former standard build tool for Java projectsPros:

1. Portability - build is defined in XML by chaining tasks2. Experience - abillity to explicitly define dependencies

between tasksCons:

1. Conventions - no default build lifecycle, each build ispiece of art

2. Ecosystem - not easy to extend, task definitiondistributed as jar

WHY SBTLITTLE HISTORY: APACHE MAVEN

Currently heavily used in enterprise Java projectsMost opinionated build toolPros:

1. Portability - build is defined in XML so called POM2. Experience, Conventions - Maven introduces default

lifecycle with its phases and tasks. It promotesdeclarative dependency management.

Cons:1. Ecosystem - not easy to extend. Maven plug-in

archtecrure requires plug-in written in Java, then POMsand packaging and availability in repository.

WHY SBTLITTLE HISTORY: GOOD PARTS

Default project layout (Maven)Default project behavior (Maven)Declarative dependency management (Maven)Portability (Ant, Maven)

WHY SBTFEATURES AT GLACE

Uses Scala to describe buildCan be used for Java and ScalaMinimal configuration (inspired by Maven)A number of build-in tasksDeclarative dependency management (using Apache Ivy)PortabilityReactive development environmentAllows use Scala REPLIncremental compilationAutomatic re-compilation with different versions of Scala

HOW SBT WORKSTASKS

Task based, more like ANT, no phases like in MavenIf you want to do something you execute a taskIf you want to ensure that a task runs after another, add anexplicit dependency between the tasksOutput of a task is value, which can be of any type and pastto any another taskMultiple tasks can depend upon the output of the sametaskBy default tasks are executed in parallelUsing dependency tree sbt can work out what can be run inparallel or in sequence

HOW SBT WORKSDEFAULT STRUCTURE AND LAYOUT

Inspired by Maven{project root} project/ build.properties plugins.sbt src/ main/ scala/ java/ resources/ test/ scala/ java/ resources/ target/ build.sbt

HOW SBT WORKSTASKS 'VS PLUGINS

Appear directly in build definition file, shared via VCSTask can be turned into plugin and shared via repository

val gitHeadCommitSHA = taskKey[String]("Determines the current git commit SHA")gitHeadComitSHA := Process("git rev-parse HEAD").lines.head

HOW SBT WORKSPHASES 'VS TASK DEPENDENCIES

In Maven, the order of execution tasks in phases alwaysleads to confusion

Default goals for a phase are executed before explicitlydefined and those are executed in implicit order ofdefinition in POM file

Implicit order of execution can cause problems whenparallelizing build, if there are dependencies between goalsSBT is per default parallel, that's why explicit definition oftask dependencies is needed

It's similar to definition a custom lifecycle in Maven,which is not easy too

HOW SBT WORKSPARALLEL EXECUTION

If task A depends on B, and C also depends on B => SBT willrun B first and then A and C in parallel

HOW SBT WORKSPASSING INFORMATION BETWEEN TASKS

In Maven and ANT it's very hard to pass an informationbetween tasks

Usually through an intermediate fileIn SBT, you can simply return the value from the task anduse it in another dependent taskThis makes chaining tasks a lot easier

HOW SBT WORKSWORKING WITH SCALA

Cross compilation for multiple Scala versions => Scala isbinary compatible only between minor version releases

Not restricted to Scala versions either

scalaVersion := "2.10.1"crossScalaVersions := Seq("2.8.2", "2.9.2")

libraryDependencies += (scalaBinaryVersion.value match { case "2.10" => "org.specs2" %% "specs2" % "2.0" case "2.9.1" => "org.specs2" %% "specs2" % "1.12.4"})

HOW SBT WORKSWORKING WITH SCALA, TAKE TWO

Scala compiler generates lots more classes and JVM takeslonger to start-up => interactive environmentScala compilation is slow (in comparision to Java) =>incremental compilationMulti-module builds => parallel execution, child modulesdon't need to know about parent module

EXAMPLE OF SIMPLE PROJECTA COMMON DEVELOPERS USAGE

CORE CONCEPTSBUILD FILES

build.properties

build.sbt

Blank line is required between settings

project/ build.properties - defines SBT version plugins.sbt - defines SBT plugins build.sbt - defines actual build, project settings

sbt.version = 0.12.4

name := "big-project"

version := "1.0"

scalaVersion := "2.10.0"

libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test"

CORE CONCEPTSSETTINGS

Mechanism to configure a build to to perform th ework weneed to

SBT reads all the settings defined in build at load time andruns their initializations

name := "big-project"| | |key operator intialization

CORE CONCEPTSSETTINGS, TAKE TWO

Settings are typesafe => every key has only one type and anyvalue placed into setting must match exact type

Grouping of SettingKey[T] with Initialize[T]creates Setting[T]

name := "big-project"| |SettingKey[String] Initialize[String]

CORE CONCEPTSSETTINGS, TAKE THREE

Operators used with settings

Types have to match

name := "big-project" | assignment

libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" | append

append multiple values |libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "1.9.1" % "test", "org.specs2" %% "specs2" % "2.0" % "test")

CORE CONCEPTSSETTINGS, TAKE FOUR

Initializations are a Scala expressions that can produce valueof desired type

Intializations may use other settings via setting.valuemethod.

version := "1.0"

libraryDependencies += ("org.scalatest" %% "scalatest" % version.value)| |SetingKey[ModuleID] Initialization[ModuleId] \ / Setting[ModuleID]

CORE CONCEPTSDEFINING DEPENDENCIES

Definition of exact version

Cross-compiled dependency

groupId % artifactId % version % scope

groupId %% artifactId % version % scope | Use appropriate scala version from project

CORE CONCEPTSCIRCULAR REFERENCES

Because SBT can use values of one setting to instatiateanother, it's possible to create circular referencesBuild will fail to load when circular references aredetected.

CORE CONCEPTSCUSTOM TASKS AND SETTINGS

For version 0.12.4 have to be defined in Scala file not sbtoneFrom 0.13.0 they can be defined in sbt too val gitHeadCommitSHA = taskKey[String]("Determines the current git commit SHA") | setting/task definition

gitHeadComitSHA := Process("git rev-parse HEAD").lines.head | | setting/task key block of code returning value

Definitions are compiled first and can reference anotherdefinitionsSettings are executed after definitions, hence can refenceany definitionTasks are executed every time the value is requested

CORE CONCEPTSCUSTOM TASKS AND SETTINGS

For version 0.12.4 have to be defined in Scala file not sbtoneFrom 0.13.0 they can be defined in sbt too val gitHeadCommitSHA = taskKey[String]("Determines the current git commit SHA") | setting/task definition

gitHeadComitSHA := Process("git rev-parse HEAD").lines.head | | setting/task key block of code returning value

Definitions are compiled first and can reference anotherdefinitionsSettings are executed after definitions, hence can refenceany definitionTasks are executed every time the value is requested

MORE TO COVER1. Scopes2. Multi-module projects3. Basic SBT objects in Scala

THANKS FOR YOUR ATTENTION