Cloudera CDH/CDP 및 Hadoop EcoSystem, Semantic IoT등의 개발/운영 기술을 정리합니다. gooper@gooper.com로 문의 주세요.
출처 : http://www.bench87.com/content/7
sbt는 자바의 maven과 비슷한 일을 하는 빌드툴이다. 이 툴이 어떻게 동작하는지 간단히 알아보려고 sbt Refference Mannual을 번역해볼 생각이다.
그래서 이 페이지는 getting started레퍼런스를 번역한 것이다. 하지만 영어 독해가 완벽하지 않아 이해가 되지 않을땐 과감하게 영어 원문을 참고하도록 하자.
sbt를 처음 사용하는 사용자는 이 글을 처음부터 차례대로 읽어야 한다. 그렇지 않으면 중간중간 이해 안되는 용어때문에 힘들 수도 있다.
그렇지 않고 이미 많이 사용해본 사람이라면 아래 목차로 원하는걸 바로 찾아 가자.
- Hello, World
- Directory structure
- Running
- .sbt build definition
- scopes
- more kinds of setting
- library dependencies
- multiple-project build
- using plugins
Hello, World
http://www.scala-sbt.org/0.13/docs/Hello.html
Create a project directory with source code
valid sbt project는 디렉토리 안에 *.scala 소스 파일을 포함하고 있다. 그럼 디렉토리를 하나 만들고 hw.scala 파일을 만들어서 아래 코드를 작성해보자.
object Hi { def main(args: Array[String]) = println("Hi!") }
Linux or Mac osx 터미널에서 아래와 같은 명령어로 위 작업을 간단히 하고 sbt run을 실행하는 방법은 아래와 같다.
$ mkdir hello $ cd hello $ echo 'object Hi { def main(args: Array[String]) = println("Hi!") }' > hw.scala $ sbt ... > run ... Hi!
위 케이스 같은 경우엔 sbt가 컨벤션에 의해 작업을 완료 할 것이다. 그럼 sbt가 어떻게 자동으로 파일을 찾는지 알아 보자.
- 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
기본적으로 sbt는 sbt가 실행하는 Scala 버젼의 Scala로 빌드를 한다.
You can run the project with sbt run or enter the Scala REPL with sbt console. sbt consolesets up your project’s classpath so you can try out live Scala examples based on your project’s code.
Build definition
대부분의 프로젝트는 수동으로 셋업을 하는게 필요하다. 기본 빌드 셋팅은 build.sbt 라는 프로젝트 루트에 있는 파일에 작성한다.
예를들어 만약 프로젝트의 베이스 디렉토리가 hello라면 hello/build.sbt 파일이 해당된다.
lazy val root = (project in file(".")). settings( name := "hello", version := "1.0", scalaVersion := "2.11.5" )
더 자세히 정의 하는걸 알고 싶다면 .sbt build definition 에 들어가보자.
Setting the sbt version
sbt의 버전을 강제로 바꾸고 싶다면 hello/project/build.properties 파일에 아래와 같이 작성하면된다.
sbt.version=0.13.9
sbt 0.13.9를 사용하도록 강제한다. sbt는 99% 호환 가능하지만 project/build.properties에 버전을 명시 함으로써 잠재적 혼란을 피할 수도 있다.
Directory structure
http://www.scala-sbt.org/0.13/docs/Directories.html
Base directory
sbt에서 말하는 base directory란 프로젝트를 포함하는 디렉토리이다. 그래서 만약에 hello/build.sbt와 hello/hw.scala를 포함하는 프로젝트를 만들었다면 hello가 base directory이다.
Source code
소스코드는 base directory에 위치 시켜도 되지만 대부분의 사람들은 실제 프로젝트에서 그렇게 하면 너무나 지저분해지기 때문에 그렇게 하길 원하지 않는다.
sbt는 Maven의 디렉토리 구조와 같다.
src/ main/ resources/ <files to include in main jar here> scala/ <main Scala sources> java/ <main Java sources> test/ resources <files to include in test jar here> scala/ <test Scala sources> java/ <test Java sources>
src 안에 있는 다른 디렉토리나 숨겨진 디렉토리는 다 무시된다.
sbt build definition files
이미 base directory에 있는 build.sbt를 봤지만, 다른 sbt files가 프로젝트 서브디렉토리에 나타난다.
프로젝트는 .sbt 파일들과 합쳐지기 위해서 .scala파일을 포함한다. See organizing the build for more.
build.sbt project/ Build.scala
.sbt 파일을 project/ 에서 볼 수 있는데 이 파일은 base directory에 있는 파일과 다르다. 이거에 대해선 다음에 할 것이다. 이걸 알기 위해선 이와 관련된 기반지식이 있어야 하기 때문이다.
Build products
빌드를 통해서 만들어진 파일(compiled classes, packaged jars, managed files, caches, and documentation)들은 target directory에 기본적으로 위치하게 된다.
Configuring version control
.gitignore (or equivalent for other version control systems)도 같이 포함이 될 것이다.
target/
Note that this deliberately has a trailing / (to match only directories) and it deliberately has no leading / (to match project/target/ in addition to plain target/).
Running
http://www.scala-sbt.org/0.13/docs/Running.html
Interactive mode
프로젝트 디렉토리에서 sbt 아무런 아규먼트 없이 실행해보자.
$ sbt
그럼 sbt가 인터렉티브 모드로 실행될 것이다 이 모드에선 명령어를 직접 실행하며 탭 자동완성도 지원이 된다. 그럼 아래와 같이 compile을 해보자.
> compile
한번더 compile을 하고 싶으면 위 화살 키보드를 누르면 이전에 입력 했던 커맨드가 나온다.
프로젝트를 실행하고 싶다면 run을 입력하자.
sbt interactive mode를 빠져나가고 싶다면 Ctrl+D (Unix) or Ctrl+Z (Windows).
Batch mode
sbt를 batch모드로 실행하는 것도 가능하다. 이를 하기 위해서 space로 아규먼트를 구분 지으면된다. 그리고 실행하는 명령어간에 아규먼트를 주려면 " <- 로 묶어 주면된다."
예를들어 아래와 같이 하면된다.
$ sbt clean compile "testOnly TestA TestB"
위 예제는 TestOnly만 아규먼트를 가지며, 순서는 clean -> compile -> testOnly 순으로 작업을 하게 된다.
Continuous build and test
언제든 save source file을 했을때, edit-compile-test cycle 빠르게 하기 위해서 sbt에게 자동으로 recompile 하거나 run tests를 하게 할 수 있다.
"~" 붙여서 실행하면 파일이 체인지 되는것을 감시하여 자동으로 해당 작업을 수행한다. 아래 예를 참고하자.
> ~ compile
enter키를 누르면 파일의 변화를 감시하던 프로세스가 중지된다. See Triggered Execution for more details.
Common commands
Here are some of the most common sbt commands. For a more complete list, see Command Line Reference.
clean | Deletes all generated files (in the target directory). |
compile | Compiles the main sources (in src/main/scala and src/main/java directories). |
test | Compiles and runs all tests. |
console | Starts the Scala interpreter with a classpath including the compiled sources and all dependencies. To return to sbt, type :quit, Ctrl+D (Unix), or Ctrl+Z (Windows). |
run <argument>* | Runs the main class for the project in the same virtual machine as sbt. |
package | Creates a jar file containing the files in src/main/resources and the classes compiled from src/main/scala and src/main/java. |
help <command> | Displays detailed help for the specified command. If no command is provided, displays brief descriptions of all commands. |
reload | Reloads the build definition (build.sbt, project/*.scala, project/*.sbt files). Needed if you change the build definition. |
Tab completion
Interactive mode has tab completion, including at an empty prompt. A special sbt convention is that pressing tab once may show only a subset of most likely completions, while pressing it more times shows more verbose choices.
History Commands
인터렉티브 모드에선 sbt를 종료하여 다시 실행 해도 이전에 입력한 커맨드가 히스토리로 남는다. 간단히 히스토리에 접근 하려면 화살표키를 이용하면되는데 아래 표와 같이 더 많은 기능을 제공하기도 한다.
! | Show history command help. |
!! | Execute the previous command again. |
!: | Show all previous commands. |
!:n | Show the last n commands. |
!n | Execute the command with index n, as shown by the !: command. |
!-n | Execute the nth command before this one. |
!string | Execute the most recent command starting with 'string.' |
!?string | Execute the most recent command containing 'string.' |
.sbt build definition
http://www.scala-sbt.org/0.13/docs/Basic-Def.html
Three Flavors of Build Definition
3가지의 build definition하는 방법이 있다.
- Multi-project .sbt build definition
- Bare .sbt build definition
- .scala build definition
이 페이지에서는 최신의 multi-project에 대해서 논할 것이다. .sbt build definition은 2개의 오래된 방법을 합쳐 놓았고 모든 케이스에 적합하다. 아마 우연히 오래된 2가지 방법을 실전에서 보았을 수도 있다.
See bare .sbt build definition and .scala build definition (later in Getting Started) for more on other flavors.
추가로, build definition은 공통으로 사용되는 value와 function을 정의 하기 위해서 위해서 base directory의 project/subdirectory에 위치한 .scala로 끝나는 파일을 포함한다.
What is a Build Definition?
디렉토리 및 빌드를 정의 하기 위한 셋을 검사한 후, sbt 프로젝트 정의를 끝낸다.
build.sbt파일에 Project 정의를 프로젝트의 현재 디렉토리에 위치 시켜 만들 것이다. 예는 아래와 같다.
lazy val root = (project in file("."))
각 프로젝트는 프로젝트를 describing한 immutable map(set of key-value pairs)으로 정의된다.
예를들어, name이라는 키가 String 값을 가지는건 프로젝트의 name이라는 뜻이다. build definition은 sbt map에 직접적으로 영향을 주지 않는다. 그 대신 build definition은 큰 list of objects with type Setting[T]을 생성하고 하나의Setting은 map으로 변환된것을 말한다.
아래 예는 어떻게 현재 디렉토리의 프로젝트에 이름을 Setting[String] 으로 연관짖는걸 보여준다.
lazy val root = (project in file(".")). settings( name := "hello" )
이 Setting[String]은 name이라는 키와 hello라는 value가 추가된 맵으로 변환되고, 변환된 맵은 sbt's 새로운 맵이 된다.
sbt가 맵을 만들땐, 같은 키에 대한 모든 변경사항을 함께 만들어지도록 list of settings를 정렬하고 다른 키에 의존하는 키는 그 키가 의존하는걸 먼저 처리 한 후 처리 한다. 그리고 sbt는 정렬된 리스트를 차례대로 돌면서 list of Settings를 맵으로 변환한다.
Summary: A build definition defines Projects with a list of Setting[T], where a Setting[T] is a transformation affecting sbt’s map of key-value pairs and T is the type of each value.
How build.sbt defines settings
build.sbt 는 Scala의 리스트 표현법으로 settings호출하는 Project를 정의한다.
아래는 그 예이다.
lazy val commonSettings = Seq( organization := "com.example", version := "0.1.0", scalaVersion := "2.11.5" ) lazy val root = (project in file(".")). settings(commonSettings: _*). settings( name := "hello" )
각 Setting는 Scala 문법으로 정의된다. Scala의 전체 문장으로 표현하는 것과 달리, settings의 표현들은 서로 독립적이다.
build.sbt vals, lazy vals, defs 이러한 타입들을 내부에 배치하곤 한다. 이런 값들은 project/ directory 에 full Scala 소스 파일로 들어간다.
왼편에 있는 name, version, scalaVersion이 키가 되며 하나의 키는 instance of SettingKey[T], TaskKey[T], InputKey[T]가 된다. T는 value의 타입을 뜻한다는건 스칼라 유저들은 알 것이다. 키의 종류는 아래에 설명되어 있다.
Key들은 := 메소드를 가지고 있는데 Setting[T]를 리턴한다. (자바에서 함수 호출 하는 것처럼 할 수도 있다.) 아래는 그 예이다.
lazy val root = (project in file(".")). settings( name.:=("hello") )
그러나 스칼라는 싱글 파라미터에 대해선 := "hello" 이렇게 쓸 수 있다.
키이름에 대한 := 메소드는 Setting[String]을 반환한다. String은 SettingKey[String]의 이름의 자체 타입으로 나타낸다.
이 케이스에선 returnSetring[String]이 sbt의 맵으로 name key를 add 하거나 replace 하는 변환을하고 hello라는 value를 반환한다.
만약에 잘못된 value type을 사용한다면 build definition은 컴파일 하지 않을 것이다.
lazy val root = (project in file(".")). settings( name := 42 // will not compile )
Keys
Types
There are three flavors of key
- SettingKey[T]: a key for a value computed once (the value is computed when loading the project, and kept around).
- TaskKey[T]: a key for a value, called a task, that has to be recomputed each time, potentially with side effects.
- InputKey[T]: a key for a task that has command line arguments as input. Check out Input Tasks for more details.
Built-in Keys
built-in Keys는 Keys라는 오브젝트에 필드로 정의되어 있다.build.sbt의 경우 암묵적으로 import sbt.Keys._한다. 그래서 위 예제에서 name은 sbt.Keys.name에 참조되는 것이다.
Custom Keys
Custom Key는 각 settingKey, taskKey, andinputKey 메소드로 만들 수 있다. 각 메소드는 해당 키와 관련된 타입으로 return type을 예상하며, key의 name은 val을 이용해서 정의한다.
For example, to define a key for a new task called hello
lazy val hello = taskKey[Unit]("An example task")
위 예를 통해서 .sbt파일에 vals, defs가 포함되어 추가적인 셋팅을 하는걸 알 수 있다. 모든 정의들은 어떤파일에 정의되었는지 전에 평가되고 vals, defs는 꼭 블랭크라인으로 구분되어야 한다.
Keys in sbt interactive mode
sbt 인터렉티브 모드에서 실행가능한 모든 테스크의 이름을 사용할 수 있는데 그래서 우리가 이전에 run이나 compile을 입력하여 해당 테스크를 실행 할 수 있는 이유다.
만약에 task key가 아닌 setting key를 타이핑 한다면 sbt 인터렉티브는 그 value의 셋팅된 값을 보여주는데 몇몇 information inspect display가 아직은 말이 안되지만, 윗부분을 보면 설정의 값 타입과 간단한 설정에 대해 설명되있다 그러나 task key를 타이핑 하면 작업을 실행하지만 결과를 값을 보여주지 않는다.
Scala indentifiers, command line name과 동일하게 keys name을 정의하는 컨벤션은 camelCase를 사용해야된다.
다른 키에 대해서 알고 싶다면 inspect <keyname>을 sbt 인터렉티브 모드에서 실행하자, 그러면 일부 정보가 표시될 것이다.
Imports in build.sbt
import 구문을 build.sbt 맨위에 작성할 수도 있다.
There are some implied default imports, as follows:
import sbt._ import Process._ import Keys._
(In addition, if you have .scala files, the contents of any Build or Plugin objects in those files will be imported. More on that when we get to .scala build definition.)
Adding library dependencies
써드파티 라이브러를 사용하기 위해서 두가지 옵션이 있다.
- lib/폴더에 jar 파일을 넣어 둔다.
- 의존성들을 추가 한다. 아래 처럼
val derby = "org.apache.derby" % "derby" % "10.4.1.3" lazy val commonSettings = Seq( organization := "com.example", version := "0.1.0", scalaVersion := "2.11.5" ) lazy val root = (project in file(".")). settings(commonSettings: _*). settings( name := "hello", libraryDependencies += derby )
This is how you add a managed dependency on the Apache Derby library, version 10.4.1.3.
Scopes
http://www.scala-sbt.org/0.13/docs/Scopes.html
The whole story about keys
이전에 key는 sbt key-value map에 하나와 일치하는 name이라고 간단히 설명하였다.
사실 각 키는 scope라는 컨텍스트 값을 가지고 있다.
Some concrete examples:
- 만약 여러개의 프로젝트가 build definition에 있다면, 각 키는 프로젝트마다 다른 값을 가진다.
- 만약 compile을 다르게 하고 싶다면 compile key는 main source와 test source에 있는 값이 다르다.
- 클래스를 패키징(packageBin) 하거나 소스코드를 패키징(packageSrc)를 할때 packageOptions key는 다르다.
값이 범위에 따라 다르기 때문에 주어진 key name은 싱글 값이 아니다.
그러나 주어진 scopped key는 단일값이다.
만약 프로젝트를 설명하는 key-value 맵을 만들기 위해서 sbt가 list of settings를 처리한다고 생각해보자, 이전에 논의했던 key-value맵 안에 있는 key는 scoped keys 이다. 각 셋팅이 build definition 안에 정의 scoped key가 적용된다.
종종 scope는 명시적이든 묵시적이든 default를 가진다. 그러나 default가 잘못되었을땐 build.sbt에 알려줘야 한다.
Scope axes
scope axis는 자신의 범위를 정의하는 타입이다. (that is, each instance can have its own unique values for keys).
There are three scope axes
- Projects
- Configurations
- Tasks
Scoping by project axis
만약 multiple project in a single build를 구성한다면, 각 셋팅은 그 프로젝트만의 셋팅이 필요하다. 이게 Key가 프로젝트에 한정된다는 것이다.
프로젝트 축은 모든 "entire build"에 적용가능하다. 그래서 하나의 싱글 프로젝트가 아닌 setting entire build에 적용된다. 빌드레벨의 셋팅은 한 프로젝트가 project의 특별한 셋팅을 정의 하지 않았을 경우 종종 fallback을 사용한다.
Scoping by configuration axis
configuration을 하나의 flavor로 설정하면 잠재적으로 classpath, sources, generated packages, etc를 구성한다. 이 configuration concept은 sbt가 의존성을 관리하는 Library Dependencies 와 MavenScopes 인 lvy로부터 가져왔다.
Some configurations you’ll see in sbt:
- Compile which defines the main build (src/main/scala).
- Test which defines how to build tests (src/test/scala).
- Runtime which defines the classpath for the run task.
기본적으로, 모든 컴파일, 패키징, 러닝에 연관된 key들은 configuration에 한정되므로 각 설정은 다르게 일을 한다. 가장 분명한 예, compile, package, run 태스크는 모든 그키들에 영향(such as sourceDirectories orscalacOptions or fullClasspath)을 미치는 configuration에 한정된다.
Scoping by task axis
Settings can affect how a task works. For example, the packageSrc task is affected by thepackageOptions setting.
To support this, a task key (such as packageSrc) can be a scope for another key (such aspackageOptions).
The various tasks that build a package (packageSrc, packageBin, packageDoc) can share keys related to packaging, such as artifactName and packageOptions. Those keys can have distinct values for each packaging task.
Setting은 태스크가 어떻게 일을 할지에 영향을 준다. 예를들어, packageSrc 태스크는 packageOption 셋팅에 영향을 받는다.
packageOptions setting.
To support this, a task key (such as packageSrc) can be a scope for another key (such aspackageOptions).
The various tasks that build a package (packageSrc, packageBin, packageDoc) can share keys related to packaging, such as artifactName and packageOptions. Those keys can have distinct values for each packaging task.
Global scope
각 스코프의 범위는 어떤 타입의 범위를 다루는지에 따라 다르지만, Global value는 모든 인스턴스에 영향을 준다.
Delegation
만약 어떤 value가 어떤 스코프에도 연관되지 않았다면 scoped key는 undefined일 것이다.
각 스코프를위해, 다른 스코프가 만들어 졌는지 sbt가 탐색을 한다. 일반적으로, 하나의 키가 특정 스코프에 연관되어 있지 않다면 sbt는 더 일반적인 스코프 즉 Global같은 곳에 있는지 찾아본다.
그래서 좀더 일반적인 scope에 value를 정의 하면 한정적인 스코프를 여러 스코프가 이 스코프의 값을 상속받는 효과를 낼 수 있다.
다음 문서를 보면 알겠지만, inspect 커맨드를 통해서 sbt가 어떻게 대체값을 찾는지 알 수 있다.
Referring to scoped keys when running sbt
커맨드라인과 인터렉티브 모드에서 sbt는 아래와 같이 scoped keys를 보여준다.
{<build-uri>}<project-id>/config:intask::key
- {<build-uri>}/<project-id> identifies the project axis. The <project-id> part will be missing if the project axis has “entire build” scope.
- config identifies the configuration axis.
- intask identifies the task axis.
- key identifies the key being scoped.
* can appear for each axis, referring to the Global scope.
If you omit part of the scoped key, it will be inferred as follows:
- the current project will be used if you omit the project.
- a key-dependent configuration will be auto-detected if you omit the configuration or task.
Examples of scoped key notation
- fullClasspath specifies just a key, so the default scopes are used: current project, a key-dependent configuration, and global task scope.
- test:fullClasspath specifies the configuration, so this is fullClasspath in the test configuration, with defaults for the other two scope axes.
- *:fullClasspath specifies Global for the configuration, rather than the default configuration.
- doc::fullClasspath specifies the fullClasspath key scoped to the doc task, with the defaults for the project and configuration axes.
- {file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath specifies a project,{file:/home/hp/checkout/hello/}default-aea33a, where the project is identified with the build{file:/home/hp/checkout/hello/} and then a project id inside that build default-aea33a. Also specifies configuration test, but leaves the default task axis.
- {file:/home/hp/checkout/hello/}/test:fullClasspath sets the project axis to “entire build” where the build is {file:/home/hp/checkout/hello/}.
- {.}/test:fullClasspath sets the project axis to “entire build” where the build is {.}. {.} can be writtenThisBuild in Scala code.
- {file:/home/hp/checkout/hello/}/compile:doc::fullClasspath sets all three scope axes.
Inspecting scopes
In sbt’s interactive mode, you can use the inspect command to understand keys and their scopes. Tryinspect test:fullClasspath:
$ sbt > inspect test:fullClasspath [info] Task: scala.collection.Seq[sbt.Attributed[java.io.File]] [info] Description: [info] The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies. [info] Provided by: [info] {file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath [info] Dependencies: [info] test:exportedProducts [info] test:dependencyClasspath [info] Reverse dependencies: [info] test:runMain [info] test:run [info] test:testLoader [info] test:console [info] Delegates: [info] test:fullClasspath [info] runtime:fullClasspath [info] compile:fullClasspath [info] *:fullClasspath [info] {.}/test:fullClasspath [info] {.}/runtime:fullClasspath [info] {.}/compile:fullClasspath [info] {.}/*:fullClasspath [info] */test:fullClasspath [info] */runtime:fullClasspath [info] */compile:fullClasspath [info] */*:fullClasspath [info] Related: [info] compile:fullClasspath [info] compile:fullClasspath(for doc) [info] test:fullClasspath(for doc) [info] runtime:fullClasspath
On the first line, you can see this is a task (as opposed to a setting, as explained in .sbt build definition). The value resulting from the task will have typescala.collection.Seq[sbt.Attributed[java.io.File]].
“Provided by” points you to the scoped key that defines the value, in this case{file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath (which is thefullClasspath key scoped to the test configuration and the{file:/home/hp/checkout/hello/}default-aea33a project).
“Dependencies” may not make sense yet; stay tuned for the next page.
You can also see the delegates; if the value were not defined, sbt would search through:
- two other configurations (runtime:fullClasspath, compile:fullClasspath). In these scoped keys, the project is unspecified meaning “current project” and the task is unspecified meaning Global
- configuration set to Global (*:fullClasspath), since project is still unspecified it’s “current project” and task is still unspecified so Global
- project set to {.} or ThisBuild (meaning the entire build, no specific project)
- project axis set to Global (*/test:fullClasspath) (remember, an unspecified project means current, so searching Global here is new; i.e. * and “no project shown” are different for the project axis; i.e.*/test:fullClasspath is not the same as test:fullClasspath)
- both project and configuration set to Global (*/*:fullClasspath) (remember that unspecified task meansGlobal already, so */*:fullClasspath uses Global for all three axes)
Try inspect fullClasspath (as opposed to the above example, inspect test:fullClasspath) to get a sense of the difference. Because the configuration is omitted, it is autodetected as compile.inspect compile:fullClasspath should therefore look the same as inspect fullClasspath.
Try inspect *:fullClasspath for another contrast. fullClasspath is not defined in theGlobal configuration by default.
Again, for more details, see Interacting with the Configuration System.
Referring to scopes in a build definition
If you create a setting in build.sbt with a bare key, it will be scoped to the current project, configurationGlobal and task Global:
lazy val root = (project in file(".")). settings( name := "hello" )
Run sbt and inspect name to see that it’s provided by{file:/home/hp/checkout/hello/}default-aea33a/*:name, that is, the project is{file:/home/hp/checkout/hello/}default-aea33a, the configuration is * (meaning global), and the task is not shown (which also means global).
Keys have an overloaded method called in used to set the scope. The argument to in can be an instance of any of the scope axes. So for example, though there’s no real reason to do this, you could set thename scoped to the Compile configuration:
name in Compile := "hello"
or you could set the name scoped to the packageBin task (pointless! just an example):
name in packageBin := "hello"
or you could set the name with multiple scope axes, for example in the packageBin task in the Compileconfiguration:
name in (Compile, packageBin) := "hello"
or you could use Global for all axes:
name in Global := "hello"
(name in Global implicitly converts the scope axis Global to a scope with all axes set to Global; the task and configuration are already Global by default, so here the effect is to make the project Global, that is, define */*:name rather than {file:/home/hp/checkout/hello/}default-aea33a/*:name)
If you aren’t used to Scala, a reminder: it’s important to understand that in and := are just methods, not magic. Scala lets you write them in a nicer way, but you could also use the Java style:
name.in(Compile).:=("hello")
There’s no reason to use this ugly syntax, but it illustrates that these are in fact methods.
When to specify a scope
You need to specify the scope if the key in question is normally scoped. For example, the compile task, by default, is scoped to Compile and Test configurations, and does not exist outside of those scopes.
To change the value associated with the compile key, you need to write compile in Compile orcompile in Test. Using plain compile would define a new compile task scoped to the current project, rather than overriding the standard compile tasks which are scoped to a configuration.
If you get an error like “Reference to undefined setting“, often you’ve failed to specify a scope, or you’ve specified the wrong scope. The key you’re using may be defined in some other scope. sbt will try to suggest what you meant as part of the error message; look for “Did you mean compile:compile?”
One way to think of it is that a name is only part of a key. In reality, all keys consist of both a name, and a scope (where the scope has three axes). The entire expression packageOptions in (Compile, packageBin) is a key name, in other words. Simply packageOptions is also a key name, but a different one (for keys with no in, a scope is implicitly assumed: current project, global config, global task).
More kinds of setting
http://www.scala-sbt.org/0.13/docs/More-About-Settings.html
Refresher: Settings
build definition은 sbt's description of build(which is a map of key-value pairs)으로 사용될 list of Setting을 만든다고 이전 글에서 언급했었다. Setting은 sbt의 입력과 새로운 출력의 맵의 변형이다. 그리고 새로운 맵은 sbt의 상태가 된다.
다른 설정이 다양한 방법으로 변형된다고 이전글에서 ":=" 메소드에 대해 언급을 했었다.
The Setting which := creates puts a fixed, constant value in the new, transformed map. For example, if you transform a map with the setting name := "hello" the new map has the string"hello" stored under the key name.
Appending to previous values: += and ++=
:= 을 통해서 정의하는것은 가장 간단한 변환 방법이지만, Keys는 다른 메소드로 가지고 있고 만약 SettingKey[T]가 시퀀스형이라면 해당 키에 해당하는 시퀀스에 값을 대체하는 것이 아닌 추가하는 일을 해야 할 것이다.
- += will append a single element to the sequence.
- ++= will concatenate another sequence.
예를들어 기본적으로 컴파일 폴더는 src/main/scala가 되는데, Compile의 Seq[File] 값을 가지는 sourceDirectories라는 키를 통해 컴파일 단계에서 source라는 디렉토리를 컴파일에 포함시키고 싶다면 아래와 같이 하면된다.
sourceDirectories in Compile += new File("source")
아니면 sbt 패키지에 포함되어 있는 file() 함수로 더 쉽게 할 수도 있다.
sourceDirectories in Compile += file("source")
(file() just creates a new File.)
++= 메소드를 사용하면 여러 디렉토리를 한번에 추가 하는 것도 가능하다.
sourceDirectories in Compile ++= Seq(file("sources1"), file("sources2"))
Where Seq(a, b, c, ...) is standard Scala syntax to construct a sequence.
default source directory를 완전 수정하고 싶다면 := 메소드를 사용하자.
sourceDirectories in Compile := Seq(file("sources1"), file("sources2")
Computing a value based on other keys’ values
다른 셋팅값에 있는 값 혹은 테스크에 있는 셋팅값으로 참조하는 것도 가능하다.
As a first example, consider defining the project organization to be the same as the project name.
첫번째 예로, organization이 프로젝트 명과 동일하다면 아래와 같이 하는게 가능하다.
// name our organization after our project (both are SettingKey[String]) organization := name.value
아니면 프로젝트 네임을 프로젝트 디렉토리명으로 할때 아래와 같이 한다.
// name is a Key[String], baseDirectory is a Key[File] // name the project after the directory it's inside name := baseDirectory.value.getName
This transforms the value of baseDirectory using the standard getName method of java.io.File.
Using multiple inputs is similar. For example,
name := "project " + name.value + " from " + organization.value + " version " + version.value
This sets the name in terms of its previous value as well as the organization and version settings.
Settings with dependencies
In the setting name := baseDirectory.value.getName, name will have a dependency onbaseDirectory. If you place the above in build.sbt and run the sbt interactive console, then typeinspect name, you should see (in part):
[info] Dependencies: [info] *:baseDirectory
This is how sbt knows which settings depend on which other settings. Remember that some settings describe tasks, so this approach also creates dependencies between tasks.
For example, if you inspect compile you’ll see it depends on another key compileInputs, and if you inspect compileInputs it in turn depends on other keys. Keep following the dependency chains and magic happens. When you type compile sbt automatically performs an update, for example. It Just Works because the values required as inputs to the compile computation require sbt to do theupdate computation first.
In this way, all build dependencies in sbt are automatic rather than explicitly declared. If you use a key’s value in another computation, then the computation depends on that key. It just works!
When settings are undefined
Whenever a setting uses :=, +=, or ++= to create a dependency on itself or another key’s value, the value it depends on must exist. If it does not, sbt will complain. It might say “Reference to undefined setting“, for example. When this happens, be sure you’re using the key in the scope that defines it.
It’s possible to create cycles, which is an error; sbt will tell you if you do this.
Tasks based on other keys’ values
You can compute values of some tasks or settings to define or append value for another task. It’s done by using Def.task and taskValue, as argument to :=, += or ++=.
As a first example, consider appending a source generator using the project base directory and compilation classpath.
sourceGenerators in Compile += Def.task { myGenerator(baseDirectory.value, (managedClasspath in Compile).value) }.taskValue
Tasks with dependencies
As noted in .sbt build definition, task keys create a Setting[Task[T]] rather than a Setting[T]when you build a setting with :=, etc. Tasks can use settings as inputs, but settings cannot use tasks as inputs.
Take these two keys (from Keys):
val scalacOptions = taskKey[Seq[String]]("Options for the Scala compiler.") val checksums = settingKey[Seq[String]]("The list of checksums to generate and to verify for dependencies.")
(scalacOptions and checksums have nothing to do with each other, they are just two keys with the same value type, where one is a task.)
It is possible to compile a build.sbt that aliases scalacOptions to checksums, but not the other way. For example, this is allowed:
// The scalacOptions task may be defined in terms of the checksums setting scalacOptions := checksums.value
There is no way to go the other direction. That is, a setting key can’t depend on a task key. That’s because a setting key is only computed once on project load, so the task would not be re-run every time, and tasks expect to re-run every time.
// The checksums setting may not be defined in terms of the scalacOptions task checksums := scalacOptions.value
Appending with dependencies: += and ++=
Other keys can be used when appending to an existing setting or task, just like they can for assigning with:=.
For example, say you have a coverage report named after the project, and you want to add it to the files removed by clean:
cleanFiles += file("coverage-report-" + name.value + ".txt")
Library dependencies
http://www.scala-sbt.org/0.13/docs/Library-Dependencies.html
라이브러리 의존성을 추가 하는건 두가지 방법이 있다.
- 의존성을 합쳐서 빌드하지 않고 lib폴더에 의존성 모듈을 넣는다.
- build definition이 의존성을 관리하게 하여 자동으로 다운로드 받게 한다.
Unmanaged dependencies
대부분의 사람들은 managed dependencies를 사용하지만 unmanaged는 처음 시작하기에 아주 간단해서 좋다.
unmanaged dependencies를 사용하는건 위에서도 말했듯이 lib 폴더에 jars파일을 두는것말고는 특별하 할게 없다.
테스트 모듈인 ScalaCheck, Specs2, ScalaTest 를 lib 폴더에 넣어두면 해당 클래스패스가 compile, test, run 테스크에서 사용가능하다. 물론 이 클래스패스를 dependencyClasspath 이 명령어로 바꿀 수도 있다.
이 방법은 특별히 다른 폴더를 지정하지 않는한 build.sbt에 읜존성을 위해서 무엇인가를 추가할 필요가 없다.
lib폴더가 아닌 다른폴더에 의존성을 관리하는 경우 아래와 같이 하면된다.
unmanagedBase := baseDirectory.value / "custom_lib"
BaseDirectory는 프로젝트의 루트 디렉토리이다. 그래서 이 값의 특별한 값 메소드인 value를 활용해서 unmanagedBase를 변경하였다.
unmanagedJars라는 jars리스트 태스크도 존재 한다. 만약 다양한 디렉토리 구조를 가지거나 다른 복잡한 무엇인가를 해야 할땐 unmanagedJars를 수정하자.
lib 디렉토리안의 파일과 관련없이 하는 방법(컴파일 테스크):
unmanagedJars in Compile := Seq.empty[sbt.Attributed[java.io.File]]
Managed Dependencies
sbt는 Apache Ivy를 managed dependencies 구현하는데 사용하였다. 그래서 만약 lvy나 Maven에 친숙하다면 이걸 배우는데 훨신 도움이 된다.
The libraryDependencies key
대부분, 의존성을 간단히 설정된 의존성 디렉토리에 리스팅하여 간단히 사용할 수 있다. 그러나 Maven POM 파일이나 lvy 설정처럼 파일로 의존성을 관리 할 수 있다. 그리고 sbt는 외부 설정파일을 사용할 수 있는데 이와 관련해서 이글을 읽어보자.
의존성을 정의하는건 아래오 와같이 groupId, artifactId, revision 문자열로 가능하다.
libraryDependencies += groupID % artifactID % revision
또는 아래와 같이 configuration을 포함하기도 한다.
libraryDependencies += groupID % artifactID % revision % configuration
libraryDependencies는 Keys안에 아래와 같이 정의되어있다.
val libraryDependencies = settingKey[Seq[ModuleID]]("Declares managed dependencies.")
% 메소드는 ModuleID를 문자열로 부터 만든다. 그런다음 ModuleID를 libraryDependencies에 추가 한다.
당연히 sbt는 어디서 모듈을 다운 받아야 하는지 알고 있고, 만약 모듈이 기본 리파지토리에 있다면 sbt는 이를 자동으로 가져온다. 예를들어 Apache Derby는 standard Maven2 리파지토리에 있는데 이를 가져오는 예이다.
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
만약 위 문장을 build.sbt에 넣어두면 sbt는 라이브러리를 다운받아서 ~/.iv2/cache/org.apache.derby/에 넣어 둔다.
당연히, ++= 메소드를 이용해서 더 많은 의존성을 한번에 추가 하는 것도 가능하다.
libraryDependencies ++= Seq( groupID % artifactID % revision, groupID % otherID % otherRevision )
드물지만, := 이 메소드를 사용해야 될때도 있다.
Getting the right Scala version with
만약 groupID % artifactID % revision이 아닌 groupID %% artifactID % revision 사용한다면, sbt는 해당 프로젝트의 Scala 버젼을 artifact 네임에 추가 할 것이다. %%를 사용하지 않고 쉽게 하는것도 있는데 아래와 같이 하면된다.
libraryDependencies += "org.scala-tools" % "scala-stm_2.11.1" % "0.3"
scalaVersion 2.11.1이라고 명시하였다. 아래는 org.scala-tools 앞에 %% 를 붙인경우다.
libraryDependencies += "org.scala-tools" %% "scala-stm" % "0.3"
이렇게 하면 다른 Scala버젼에서 컴파일이 가능하다. 그리고 이렇게 하면 내 프로젝트에 호환되는 모듈을 받을 수 있어서 좋다.
실제로 복잡성은 종종 의존성이 다른 스칼라버젼에서 동작하는것이다. 그러나 %%를 사용하면 스마트하게 이를 대처 할 수 없다. 그래서 만약 의존성모듈이 2.10.1에서 가능한데 스칼라버젼은 2.10.4를 쓴다면 %% 메소드를 사용하지 못할 것이다. If %% stops working, just go see which versions the dependency is really built for, and hardcode the one you think will work (assuming there is one).
See Cross Building for some more detail on this.
Ivy revisions
GroupID % artifactID % revison은 하나의 픽스된 버젼이 있는건 아니다. lvy는 픽스된 리비전을 사용하는 대신 사용자가 지정한 제약을 따르는 최신 revision을 선택 할 수 있다. 그와 관련해서 자세한 내용은 lvy revision문서를 참고하자.
Resolvers
모든 페키지가 같은 서버를 사용하는 것은 아니다. sbt는 standard Maven2 repository를 기본으로 사용하지만, 만약 디펜던시가 거기에 존재 하지 않는다면 resolver를 추가 함으로써 이를 해결 할 수 있다.
To add an additional repository, use
resolvers += name at location
두개의 특별한 문자열을 사용하여 추가 가능하다.
For example:
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
resolvers도 Keys에 아래와 같이 정의되어 있다.
val resolvers = settingKey[Seq[Resolver]]("The user-defined additional resolvers for automatically managed dependencies.")
The at method creates a Resolver object from two strings.
sbt can search your local Maven repository if you add it as a repository:
resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
or, for convenience:
resolvers += Resolver.mavenLocal
See Resolvers for details on defining other types of repositories.
Overriding default resolvers
resolvers는 default resolvers를 포함하지 않는다. 그래서 오직 build definition에 추가해야만 한다.
sbt combines resolvers with some default repositories to form externalResolvers. Therefore, to change or remove the default resolvers, you would need to override
externalResolvers instead of resolvers.
Per-configuration dependencies
종종 의존성은 테스트코드에서 사용이된다. (in src/test/scala, which is compiled by the Testconfiguration)
만약 이 의존성이 Test configuration에서만 사용이 가능하고 compile에서는 사용안하고 싶다면 % "test"를 추가하자.
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test"
타입 세이프 버젼으로 Test configuration을 사용하는 것도 가능하다.
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % Test
Now, if you type show compile:dependencyClasspath at the sbt interactive prompt, you should not see the derby jar. But if you type show test:dependencyClasspath, you should see the derby jar in the list.
이젠, sbt 인터렉티브 프롬프트에서 compile:dependencyClasspath를 통해 있는 derby jar를 볼 수 없다. 그러나 test에선 보여진다.
Multiple-project build
http://www.scala-sbt.org/0.13/docs/Multi-Project.html
project
여러 프로젝트를 하나의 빌드로 유지하는게 유용하다, 특히 프로젝트 서로 의존성이 있고 수정 잦다면 특히 유용하다.
자신의 소스 디렉토리를 가지는 각 서브프로젝트는 "run package"를 할때 자신만의 jar 파일을 생성하며 이는 다른 일반적인 프로젝트와 동일하게 일을 한다.
프로젝트정의는 lazy val type Project로 한다. For example :
lazy val util = project lazy val core = project
val로 정의한 상수는 프로젝트 ID와 기본 디렉토리 네임으로 사용된다. ID는 커맨드라인에서 프로젝트를 참조할때 사용되며, 기본 디렉토리는를 변경하는 것은 "in" 메소드를 통해서 변경이 가능하다.
예를들어 다음 예가 이전 예보다 더 명확히 나타내는 방법이다.
lazy val util = project.in(file("util")) lazy val core = project in file("core")
Common settings
멀티플 프로젝트에 공통으로 쓰일 값을 정의 하기 위해서 commonSettings라는 이름의 sequence를 만들고 settings method를 각각 프로젝트에 호출한다.
_* is required to pass sequence into a vararg method
lazy val commonSettings = Seq( organization := "com.example", version := "0.1.0", scalaVersion := "2.11.5" ) lazy val core = (project in file("core")). settings(commonSettings: _*). settings( // other settings ) lazy val util = (project in file("util")). settings(commonSettings: _*). settings( // other settings )
위와 같이 하면 reload build 할때 version을 각 서브프로젝트에 똑같이 적용될 것이다.
Dependencies
각 프로젝트는 서로 완전히 독립적일 수 있지만, 대개 서로 연관되어 의존성을 가질 수 있다. 그럴땐 aggregate와 classpath를 사용하면된다.
Aggregation
Aggregation은 aggregate 함수로 테스크를 묶는것을 말한다. For example:
lazy val root = (project in file(".")). aggregate(util, core) lazy val util = project lazy val core = project
위 예제는 루트 프로젝트가 util과 core과 함께 묶는 것이다. 위 예제 처럼 두 서브 프로젝트를 sbt로 컴파일 시도 하면 3개의 프로젝트가 빌드되는걸 볼 수 있을 것이다.
이런 경우 위 루트프로젝트는 aggregation-per-task를 조절 할 수 있다. 예를들어 aggregation the update task를 피하기 위해 아래와 같이 하면된다.
lazy val root = (project in file(".")). aggregate(util, core). settings( aggregate in update := false )
aggregate in update is the aggregate key scoped to the update task. (See scopes.)
Note: aggregation will run the aggregated tasks in parallel and with no defined ordering between them.
Classpath dependencies
한 프로젝트가 다른 프로젝트의 코드를 의존하기도 한다. 이때 dpendsOn메소드를 이용해서 해결 한다.
예를들어 core 프로젝트가 util을 자신의 classpath에서 사용하고 싶을때 아래와 같이 한다.
lazy val core = project.dependsOn(util)
이제 core 프로젝트에서 util 클래스를 사용 할 수 있다. util이 core가 컴파일 되기전에 컴파일이 되도록 순서를 정하게 된다.
여러 프로젝트에 의존하려면 dependsOn method에 여러 argument를 사용하면 된다.
dependsOn(bar, baz)
Per-configuration classpath dependencies
foo dependsOn(bar)의 뜻은 foo 컴파일 설정이 bar의 컴파일 설정에 의존한다는 뜻이다. 이걸 명시적으로 정의 하려면 dependsOn(bar % "compile -> compile)이라고 쓰면된다.
“compile -> compile” 의 화살표는 ‘depends on(~에 의존하다)’를 의미한다. 그러므로 “test ->compile”은 foo의 test 설정은 bar의 compile 설정을 의존한다는 의미이다.
"-> config part"를 생략하는것은 암묵적으로 "-> compile" 즉 dependsOn(bar % "test") test configuration은 foo를 쓰고 compile configurtion은 bar 것을 사용하라는 뜻이다.
유용한 declaration은 “test ->test”인데 test가 test를 의존한다는 뜻이다. 가령 utility code의 테스코드를 bar/src/test/scala 에 넣어고 사용은 foo/src/test/scala에서 할 수 있다는 뜻이다.
세미콜론을 이용해서 multiple configuration depency를 가질 수 있다. For example,
dependsOn(bar % "test->test;compile->compile")
Default root project
만약 프로젝트가 root directory로 정의되지 않았다면 sbt가 defualt로 다른 프로젝트를 하나로 빌드때 묶는다.
왜냐하면 hello-foo 프로젝트가 base=file(“foo”)로 정의되었고, subdirectory foo 안에 들어갈 것이다. 이것의 source는 foo/Foo.scala 처럼 바로 foo 아래 있을수 있다.
예를들어 foo/Foo.scala, or info/src/main/scala. 자주 사용하는 sbt directory structure로 foo 의 빌드 예외파일과 함께 밑에 적용한다.
만약 모든 프로젝트가 hello 안에 있다면 각 서버프로젝트의 버젼을 다르게 정의 해보자, 그러면 sbt interactive prompt에서 각 버젼을 아래와 같이 볼 수 있다.
> show version [info] hello-foo/*:version [info] 0.7 [info] hello-bar/*:version [info] 0.9 [info] hello/*:version [info] 0.5
hello-foo/*:version hello/foo/build.sbt에 정의 되었고, hello-bar/*:version은 hello/bar/build.sbt에 정의되었고, hello:/*version은 hello/build.sbt에 정의 되었다. syntax for scoped keys를 꼭 기억하자 각 버젼의 key는 그 프로젝트에 속한다.
- Each project’s settings can go in .sbt files in the base directory of that project, while the .scala file can be as simple as the one shown above, listing the projects and base directories. There is no need to put settings in the.scala file.*
아마 간단히 모든걸 하나에 .scala 에 모든 셋팅값을 넣어 두기도 할텐데, 각 서브프로젝트는 project라는 subdirectory를 가질 수 없다. 갖더라도 project/ 하위의 것들은 무시될 것이다.
Navigating projects interactively
sbt interactive prompt에서 project <projectname>을 타이핑 하여 현재 프로젝트로 선택을 하고 compile과 같은 task를 실행할하면 현재 프로젝트만 실행이 된다. 그래서 루트 프로젝트를 컴파일할 필요가 없다면 subproject만 컴파일 하는 것도 가능하다.
또, 명시적으로 특정 프로젝트의 ID를 이용해서 task를 실행 할 수 있다. 예를들어 assubProjectId/compile과 같은 커맨드로 가능하다.
Using plugins
http://www.scala-sbt.org/0.13/docs/Using-Plugins.html
What is a plugin?
plugin은 build definition을 확장하는 것인데, 일반적으로 settings를 추가 한다. 새로운 setting은 새로운 테스크를 추가 하기도 하는데 예를들어 test coverage report를 만드는 codeCoverage 테스크가 추가 되곤 한다.
Declaring a plugin
만약 프로젝트 디렉토리명이 hello라면 sbt-site 플러그인을 추가 하기 위해서 hello/project/site.sbt build definition을 만들어서 plugin 의존성을 정의하자.
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.7.0")
그리고 만약 sbt-assembly를 추가 하려 한다면 동일하게 hello/project/assembly.sbt 만들어서 아래와 같이 정의 하자.
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
Not every plugin is located on one of the default repositories and a plugin’s documentation may instruct you to also add the repository where it can be found:
모든 플러그인이 하나의 리파지토리에 있지 않기 때문에 repository를 resolver에 추가 해주어야 한다.
resolvers += Resolver.sonatypeRepo("public")
플러그인은 대개 settings에 활성화 시켜주어야 한다. 아래 섹션에 그에 대해 다룰 것이다.
Enabling and disabling auto plugins
플러그인은 자동적으로 build definition에 settings를 추가 한다. 즉 아무것도 하지 않아도 된다.
sbt 0.13.5에선 새로운 기능으로 auto plugins가 있다. 그래서 자동으로 안정하고 확실하게 자동으로 활성화를 한다. 대부분의 플러그인은 기본적으로 자동으로 활성화 되지만 그렇지 않은 경우 아래와 같이 명시적으로 호출해야 되는 경우가 있는데 만약 이러한 명시적으로 활성화 해야 하는 경우 아래와 같이 해야 한다.
lazy val util = (project in file("util")). enablePlugins(FooPlugin, BarPlugin). settings( name := "hello-util" )
enablePlugins 메소드는 명시적으로 auto plugin을 정의 한다.
또 프로젝트는 disablePlugins를 호출하여 플러그인을 비활성화 할 수도 있다.
lazy val util = (project in file("util")). enablePlugins(FooPlugin, BarPlugin). disablePlugins(plugins.IvyPlugin). settings( name := "hello-util" )
Auto plugins 명시적으로 활성화해야된다면 아마 문서화 되어 있을 것이다. 만약 이를 모르겠다면 sbt console에서 플러그인을 실행 해보자.
> plugins In file:/home/jsuereth/projects/sbt/test-ivy-issues/ sbt.plugins.IvyPlugin: enabled in scala-sbt-org sbt.plugins.JvmPlugin: enabled in scala-sbt-org sbt.plugins.CorePlugin: enabled in scala-sbt-org sbt.plugins.JUnitXmlReportPlugin: enabled in scala-sbt-org
위 결과를 보면 기본적으로 플러그인이 활성화 되도록 되어 있다.
sbt’s default settings are provided via three plugins:
- CorePlugin: Provides the core parallelism controls for tasks.
- IvyPlugin: Provides the mechanisms to publish/resolve modules.
- JvmPlugin: Provides the mechanisms to compile/test/run/package Java/Scala projects.
In addition, JUnitXmlReportPlugin provides an experimental support for generating junit-xml.
Older non-auto plugins often require settings to be added explictly, so that multi-project build could have different types of projects. The plugin documentation will indicate how to configure it, but typically for older plugins this involves adding the base settings for the plugin and customizing as necessary.
For example, for the sbt-site plugin, create site.sbt with the following content
site.settings
to enable it for that project.
If the build defines multiple projects, instead add it directly to the project:
// don't use the site plugin for the `util` project lazy val util = (project in file("util")) // enable the site plugin for the `core` project lazy val core = (project in file("core")). settings(site.settings : _*)
Global plugins
Plugins can be installed for all your projects at once by declaring them in ~/.sbt/0.13/plugins/.~/.sbt/0.13/plugins/ is an sbt project whose classpath is exported to all sbt build definition projects. Roughly speaking, any .sbt or .scala files in ~/.sbt/0.13/plugins/ behave as if they were in the project/ directory for all projects.
You can create ~/.sbt/0.13/plugins//build.sbt and put addSbtPlugin() expressions in there to add plugins to all your projects at once. Because doing so would increase the dependency on the machine environment, this feature should be used sparingly. See Best Practices.
Available Plugins
There’s a list of available plugins.
Some especially popular plugins are:
- those for IDEs (to import an sbt project into your IDE)
- those supporting web frameworks, such as xsbt-web-plugin.
For more details, including ways of developing plugins, see Plugins. For best practices, see Plugins-Best-Practices.