SBT Crash Course

30
SBT CRASH COURSE By / Michal Bigos @teliatko

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/

Transcript of SBT Crash Course

Page 2: SBT Crash Course

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

Page 3: SBT Crash Course

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

Page 4: SBT Crash Course

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

Page 5: SBT Crash Course

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.

Page 6: SBT Crash Course

WHY SBTLITTLE HISTORY: GOOD PARTS

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

Page 7: SBT Crash Course

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

Page 8: SBT Crash Course

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

Page 9: SBT Crash Course

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

Page 10: SBT Crash Course

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

Page 11: SBT Crash Course

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

Page 12: SBT Crash Course

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

Page 13: SBT Crash Course

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

Page 14: SBT Crash Course

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"})

Page 15: SBT Crash Course

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

Page 16: SBT Crash Course

EXAMPLE OF SIMPLE PROJECTA COMMON DEVELOPERS USAGE

Page 17: SBT Crash Course

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"

Page 18: SBT Crash Course
Page 19: SBT Crash Course

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

Page 20: SBT Crash Course

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]

Page 21: SBT Crash Course

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")

Page 22: SBT Crash Course

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]

Page 23: SBT Crash Course

CORE CONCEPTSDEFINING DEPENDENCIES

Definition of exact version

Cross-compiled dependency

groupId % artifactId % version % scope

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

Page 24: SBT Crash Course

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.

Page 25: SBT Crash Course

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

Page 26: SBT Crash Course

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

Page 27: SBT Crash Course

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

Page 28: SBT Crash Course

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

Page 29: SBT Crash Course

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

Page 30: SBT Crash Course

THANKS FOR YOUR ATTENTION