나는 이것에 대해 옷장에서 내릴거야! SBT를 이해하지 못합니다. 내가 말 했으니 이제 제발 도와주세요.
모든 길은 로마로 통한다, 그것은 SBT에 대해 동일합니다 : 시작하려면 SBT
이 SBT
, SBT Launcher
, SBT-extras
, 등, 다음을 포함하고 저장소에 결정하는 여러 가지 방법이 있습니다. ‘최상의’방법이 있습니까?
때때로 나는 조금 길을 잃기 때문에 묻는 것입니다. SBT 문서는 매우 철저하고 완료,하지만 난 사용할 때 자신이 모르는 발견 build.sbt
하거나 project/build.properties
또는 project/Build.scala
나 project/plugins.sbt
.
그러면 재미있어집니다. Scala-IDE
그리고 SBT
– 함께 사용하는 올바른 방법은 무엇입니까? 가장 먼저 오는 것은 무엇입니까? 닭고기 또는 계란?
가장 중요한 것은 아마도 프로젝트에 포함 할 올바른 저장소와 버전을 어떻게 찾 느냐는 것입니다. 기계를 꺼내서 해킹을 시작하면 되나요? 나는 모든 것과 부엌 싱크대를 포함하는 프로젝트를 자주 찾는다. 그리고 나는 깨달았다.-내가 조금 길을 잃는 유일한 사람은 아니다.
간단한 예로, 지금은 새로운 프로젝트를 시작하고 있습니다. 나는의 최신 기능을 사용하려면 SLICK
및 Scala
이 아마 SBT의 최신 버전이 필요합니다. 시작하기위한 올바른 요점은 무엇이며 그 이유는 무엇입니까? 어떤 파일에서 정의해야하며 어떻게 표시되어야합니까? 나는 이것을 작동시킬 수 있다는 것을 알고 있지만 모든 것이 어디로 가야하는지에 대한 전문가 의견을 정말로 원합니다 (왜 거기에 가야하는지 보너스가있을 것입니다).
저는 SBT
지금까지 1 년 넘게 소규모 프로젝트에 사용 하고 있습니다. 나는 사용 SBT
하고 SBT Extras
(약간의 두통이 마술처럼 사라지기 때문에) 사용했지만 왜 둘 중 하나를 사용 해야하는지 잘 모르겠습니다. 나는 사물이 어떻게 조화를 이루는 지 ( SBT
및 저장소) 이해하지 못하는 것에 대해 약간 실망하고 있으며, 이것이 인간의 용어로 설명 될 수 있다면 다음 사람이 이런 식으로 오는 많은 어려움을 덜어 줄 것이라고 생각합니다.
답변
가장 중요한 것은 아마도 프로젝트에 포함 할 올바른 저장소와 버전을 어떻게 찾 느냐는 것입니다. 기계를 꺼내서 해킹을 시작하면 되나요? 나는 모든 것을 포함하는 프로젝트와 부엌 싱크대를 자주 찾습니다.
Scala 기반 종속성의 경우 저자가 권장하는대로 진행합니다. 예 : http://code.google.com/p/scalaz/#SBT 는 다음을 사용함을 나타냅니다.
libraryDependencies += "org.scalaz" %% "scalaz-core" % "6.0.4"
또는 https://github.com/typesafehub/sbteclipse/ 에 추가 할 위치에 대한 지침이 있습니다.
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0-RC1")
Java 기반 종속성의 경우 http://mvnrepository.com/ 을 사용 하여 무엇이 있는지 확인한 다음 SBT 탭을 클릭합니다. 예를 들어 http://mvnrepository.com/artifact/net.sf.opencsv/opencsv/2.3 은 다음을 사용함을 나타냅니다.
libraryDependencies += "net.sf.opencsv" % "opencsv" % "2.3"
그런 다음 기계를 꺼내서 해킹을 시작하십시오. 운이 좋으면 동일한 jar에 의존하지만 호환되지 않는 버전을 사용하는 jar를 사용하지 않는 것이 좋습니다. Java 에코 시스템을 고려할 때 모든 항목과 주방 싱크를 포함하는 경우가 많으며 종속성을 제거하거나 필수 종속성이 누락되지 않았는지 확인하는 데 약간의 노력이 필요합니다.
간단한 예로, 지금은 새로운 프로젝트를 시작하고 있습니다. SLICK 및 Scala의 최신 기능을 사용하고 싶습니다.이를 위해서는 최신 버전의 SBT가 필요할 것입니다. 시작하기위한 올바른 요점은 무엇이며 그 이유는 무엇입니까?
나는 sbt에 대한 면역을 점진 적으로 구축 하는 것이 제정 한 포인트라고 생각합니다 .
이해했는지 확인하십시오.
- 범위 형식
{<build-uri>}<project-id>/config:key(for task-key)
- 설정의 3 개 맛 (
SettingKey
,TaskKey
,InputKey
) -에서 “작업 키”라는 섹션을 읽어 http://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def
이 4 페이지를 항상 열어 두어 다양한 정의와 예제를 바로 찾아 볼 수 있습니다.
- http://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def
- http://www.scala-sbt.org/release/docs/Detailed-Topics/index
- http://harrah.github.com/xsbt/latest/sxr/Keys.scala.html
- http://harrah.github.com/xsbt/latest/sxr/Defaults.scala.html
최대 사용 확인 show
및 inspect
및 탭 완성을 설정, 의존성, 정의와 관련 설정의 실제 값에 익숙해 질 수 있습니다. 나는 당신이 발견하게 될 관계 inspect
가 어디에도 문서화되어 있다고 믿지 않습니다 . 더 나은 방법이 있다면 그것에 대해 알고 싶습니다.
답변
sbt를 사용하는 방법은 다음과 같습니다.
- sbt-extras 사용 -쉘 스크립트를 가져 와서 프로젝트의 루트에 추가하십시오.
- sbt 설정
project
을위한MyProject.scala
파일이 있는 폴더를 만듭니다 . 나는build.sbt
접근 방식 보다 이것을 훨씬 선호합니다 -스칼라이고 더 유연합니다. - 크리에이트
project/plugins.sbt
파일을 당신의 IDE에 해당하는 플러그인을 추가 할 수 있습니다. eclipse, intellij 또는 ensime 용 프로젝트 파일을 생성 할 수 있도록 sbt-eclipse, sbt-idea 또는 ensime-sbt-cmd 중 하나입니다. - 프로젝트의 루트에서 sbt를 시작하고 IDE 용 프로젝트 파일을 생성합니다.
- 이익
IDE 프로젝트 파일은 sbt에 의해 생성되었으므로 확인하지 않아도되지만 그렇게하려는 이유가있을 수 있습니다.
여기에서 이와 같은 설정 예를 볼 수 있습니다 .
답변
프로젝트 템플릿 및 시드와 함께 제공되는 sbt를 호출하는 멋진 방법 인 Typesafe Activator 사용 : https://typesafe.com/activator
Activator new
Fetching the latest list of templates...
Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
1) minimal-java
2) minimal-scala
3) play-java
4) play-scala
(hit tab to see a list of all templates)
답변
설치
brew install sbt
또는 기술적으로 말하면 다음과 같은 유사한 설치 sbt
- sbt 런처 스크립트 (bash 스크립트) https://github.com/sbt/sbt-launcher-package
- sbt 런처 jar (sbt-launcher.jar) https://github.com/sbt/launcher
- 코어 sbt (sbt.jar) https://github.com/sbt/sbt
sbt
터미널에서 실행할 때 실제로 sbt 실행기 bash 스크립트를 실행합니다. 개인적으로 저는이 삼위 일체에 대해 걱정할 필요가 없었고 sbt를 하나의 물건 인 것처럼 사용합니다.
구성
프로젝트 .sbtopts
의 루트에 있는 특정 프로젝트 저장 파일에 대해 sbt를 구성하려면 . sbt 시스템 전체를 구성하려면 /usr/local/etc/sbtopts
. 실행 sbt -help
하면 정확한 위치를 알려줄 것입니다. 예를 들어,주고 SBT 더 많은 메모리에 일회성으로 실행 sbt -mem 4096
또는 저장 -mem 4096
으로 .sbtopts
또는 sbtopts
영구적으로 적용하려면 메모리 증가.
프로젝트 구조
sbt new scala/scala-seed.g8
최소한의 Hello World sbt 프로젝트 구조를 만듭니다.
.
├── README.md // most important part of any software project
├── build.sbt // build definition of the project
├── project // build definition of the build (sbt is recursive - explained below)
├── src // test and main source code
└── target // compiled classes, deployment package
빈번한 명령
test // run all test
testOnly // run only failed tests
testOnly -- -z "The Hello object should say hello" // run one specific test
run // run default main
runMain example.Hello // run specific main
clean // delete target/
package // package skinny jar
assembly // package fat jar
publishLocal // library to local cache
release // library to remote repository
reload // after each change to build definition
무수한 포탄
scala // Scala REPL that executes Scala language (nothing to do with sbt)
sbt // sbt REPL that executes special sbt shell language (not Scala REPL)
sbt console // Scala REPL with dependencies loaded as per build.sbt
sbt consoleProject // Scala REPL with project definition and sbt loaded for exploration with plain Scala langauage
빌드 정의는 적절한 Scala 프로젝트입니다.
이것은 주요 관용적 sbt 개념 중 하나입니다. 나는 질문으로 설명하려고 노력할 것이다. scalaj-http로 HTTP 요청을 실행할 sbt 작업을 정의하고 싶다고 가정 해 보겠습니다. 직관적으로 우리는 내부에서 다음을 시도 할 수 있습니다.build.sbt
libraryDependencies += "org.scalaj" %% "scalaj-http" % "2.4.2"
val fooTask = taskKey[Unit]("Fetch meaning of life")
fooTask := {
import scalaj.http._ // error: cannot resolve symbol
val response = Http("http://example.com").asString
...
}
그러나 이것은 누락이라는 오류가 발생 import scalaj.http._
합니다. 우리는 오른쪽 위, 추가했을 때 어떻게 이런 일이 가능하다 scalaj-http
에 libraryDependencies
? 또한, 대신 종속성을 추가 할 때 왜 작동 project/build.sbt
합니까?
// project/build.sbt
libraryDependencies += "org.scalaj" %% "scalaj-http" % "2.4.2"
대답은 fooTask
실제로 메인 프로젝트 와는 별개의 Scala 프로젝트의 일부입니다 . 이 다른 Scala 프로젝트는 컴파일 된 클래스 project/
가있는 자체 target/
디렉토리가있는 디렉토리에서 찾을 수 있습니다 . 사실, 아래 project/target/config-classes
에는 다음과 같이 디 컴파일하는 클래스가 있어야합니다.
object $9c2192aea3f1db3c251d extends scala.AnyRef {
lazy val fooTask : sbt.TaskKey[scala.Unit] = { /* compiled code */ }
lazy val root : sbt.Project = { /* compiled code */ }
}
우리는 그것이 fooTask
단순히 이름이 $9c2192aea3f1db3c251d
. 분명히 적절한 프로젝트의 종속성이 아니라 scalaj-http
프로젝트 정의의 종속성이어야합니다 $9c2192aea3f1db3c251d
. 따라서 빌드 정의 Scala 프로젝트가있는 곳 이기 때문에 project/build.sbt
대신 에서 선언해야 합니다.build.sbt
project
빌드 정의가 또 다른 Scala 프로젝트라는 점을 알리려면 sbt consoleProject
. 그러면 클래스 경로에서 빌드 정의 프로젝트와 함께 Scala REPL이로드됩니다. 당신은 라인을 따라 수입을 볼 수 있습니다
import $9c2192aea3f1db3c251d
따라서 이제 build.sbt
DSL 대신 Scala로 적절하게 호출하여 빌드 정의 프로젝트와 직접 상호 작용할 수 있습니다 . 예를 들어, 다음은fooTask
$9c2192aea3f1db3c251d.fooTask.eval
build.sbt
루트 프로젝트 아래에는 빌드 정의 Scala 프로젝트를 정의하는 데 도움이되는 spcial DSL이 project/
있습니다.
그리고 빌드 정의 Scala 프로젝트는 자체 빌드 정의 Scala 프로젝트를 가질 수 있습니다 project/project/
. 우리는 sbt가 재귀 적이 라고 말합니다 .
sbt는 기본적으로 병렬입니다.
sbt 는 작업에서 DAG 를 빌드 합니다. 이를 통해 작업 간의 종속성을 분석하고 병렬로 실행하고 중복 제거를 수행 할 수도 있습니다. build.sbt
DSL은이를 염두에두고 설계 되었기 때문에 처음에는 놀라운 의미를 가질 수 있습니다. 다음 스 니펫에서 실행 순서가 무엇이라고 생각하십니까?
def a = Def.task { println("a") }
def b = Def.task { println("b") }
lazy val c = taskKey[Unit]("sbt is parallel by-default")
c := {
println("hello")
a.value
b.value
}
직관적으로 여기서 흐름은 먼저 인쇄 hello
하고 실행 a
한 다음 b
작업하는 것이라고 생각할 수 있습니다 . 그러나 이것은 실제로 실행 수단 a
과 b
의 평행 한 전 println("hello")
그래서
a
b
hello
또는 주문 a
및 b
보장되지 않기 때문에
b
a
hello
아마도 역설적이게도 sbt에서는 직렬보다 병렬을 수행하는 것이 더 쉽습니다. 직렬 주문이 필요한 경우 Def.sequential
또는 이해를위한Def.taskDyn
에뮬레이션 과 같은 특별한 것을 사용해야 합니다.
def a = Def.task { println("a") }
def b = Def.task { println("b") }
lazy val c = taskKey[Unit]("")
c := Def.sequential(
Def.task(println("hello")),
a,
b
).value
비슷하다
for {
h <- Future(println("hello"))
a <- Future(println("a"))
b <- Future(println("b"))
} yield ()
구성 요소간에 종속성이없는 반면
def a = Def.task { println("a"); 1 }
def b(v: Int) = Def.task { println("b"); v + 40 }
def sum(x: Int, y: Int) = Def.task[Int] { println("sum"); x + y }
lazy val c = taskKey[Int]("")
c := (Def.taskDyn {
val x = a.value
val y = Def.task(b(x).value)
Def.taskDyn(sum(x, y.value))
}).value
비슷하다
def a = Future { println("a"); 1 }
def b(v: Int) = Future { println("b"); v + 40 }
def sum(x: Int, y: Int) = Future { x + y }
for {
x <- a
y <- b(x)
c <- sum(x, y)
} yield { c }
우리가 보는 곳 sum
은 a
및 b
.
다시 말해
- 대한 실용적 의미, 사용
.value
- 위한 모나드 의미론 사용
sequential
하거나taskDyn
고려 또 다른 의 종속성 건물 자연의 결과로 의미 혼란 조각을 value
대신
`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.
val x = version.value
^
우리는 써야한다
val x = settingKey[String]("")
x := version.value
구문 .value
은 DAG의 관계에 관한 것이며
“지금 나에게 가치를 줘”
대신 그것은 다음과 같은 것을 의미합니다.
“제 발신자가 먼저 저에게 의존하며 전체 DAG가 어떻게 결합되는지 알게되면 요청한 값을 발신자에게 제공 할 수 있습니다.”
이제 왜 x
아직 값을 할당 할 수 없는지 좀 더 명확해질 수 있습니다. 관계 구축 단계에서는 아직 사용할 수있는 가치가 없습니다.
.NET에서 Scala 고유와 DSL 언어 간의 의미 체계 차이를 명확하게 볼 수 있습니다 build.sbt
. 여기 저에게 맞는 엄지 손가락 규칙이 몇 가지 있습니다.
- DAG는 유형의 표현으로 만들어집니다.
Setting[T]
- 대부분의 경우 우리는 단순히
.value
구문을 사용 하고 sbt는Setting[T]
- 때때로 우리는 DAG의 일부를 수동으로 조정해야하며이를 위해
Def.sequential
또는Def.taskDyn
- 이러한 순서 / 관계 구문 적 이상이 처리되면 작업의 나머지 비즈니스 로직을 구축하기 위해 일반적인 스칼라 의미론에 의존 할 수 있습니다.
명령 대 작업
명령은 DAG에서 벗어나는 게으른 방법입니다. 명령을 사용하면 빌드 상태를 변경하고 원하는대로 작업을 직렬화하는 것이 쉽습니다. 비용은 DAG에서 제공하는 작업의 병렬화 및 중복 제거를 느슨하게하기 때문에 작업이 선호되는 방식입니다. 명령은 내부에서 수행 할 수있는 세션의 일종의 영구 기록이라고 생각할 수 있습니다 sbt shell
. 예를 들어, 주어진
vval x = settingKey[Int]("")
x := 13
lazy val f = taskKey[Int]("")
f := 1 + x.value
다음 세션의 결과를 고려하십시오.
sbt:root> x
[info] 13
sbt:root> show f
[info] 14
sbt:root> set x := 41
[info] Defining x
[info] The new value will be used by f
[info] Reapplying settings...
sbt:root> show f
[info] 42
특히 빌드 상태를 set x := 41
. 명령을 사용하면 위 세션을 영구적으로 기록 할 수 있습니다. 예를 들면
commands += Command.command("cmd") { state =>
"x" :: "show f" :: "set x := 41" :: "show f" :: state
}
우리는 또한 사용하여 명령 형태 보증을 할 수 Project.extract
및runTask
commands += Command.command("cmd") { state =>
val log = state.log
import Project._
log.info(x.value.toString)
val (_, resultBefore) = extract(state).runTask(f, state)
log.info(resultBefore.toString)
val mutatedState = extract(state).appendWithSession(Seq(x := 41), state)
val (_, resultAfter) = extract(mutatedState).runTask(f, mutatedState)
log.info(resultAfter.toString)
mutatedState
}
범위
다음과 같은 질문에 답하려고 할 때 스코프가 작동합니다.
- 작업을 한 번 정의하고 다중 프로젝트 빌드의 모든 하위 프로젝트에서 사용 가능하게 만드는 방법은 무엇입니까?
- 기본 클래스 경로에 대한 테스트 종속성을 피하는 방법은 무엇입니까?
sbt에는 슬래시 구문을 사용하여 탐색 할 수있는 다축 범위 지정 공간이 있습니다. 예를 들면 다음과 같습니다.
show root / Compile / compile / scalacOptions
| | | |
project configuration task key
개인적으로 범위에 대해 걱정할 필요가 거의 없습니다. 때로는 테스트 소스 만 컴파일하고 싶습니다.
Test/compile
또는 먼저 해당 프로젝트로 이동할 필요없이 특정 하위 프로젝트에서 특정 작업을 실행할 수 있습니다. project subprojB
subprojB/Test/compile
다음 경험 규칙이 범위 지정 합병증을 방지하는 데 도움이된다고 생각합니다.
- 여러
build.sbt
파일이 없지만 다른 모든 하위 프로젝트를 제어하는 루트 프로젝트 아래에 하나의 마스터 만 있습니다. - 자동 플러그인을 통해 작업 공유
- 일반적인 설정을 일반 Scala로 추출
val
하고 각 하위 프로젝트에 명시 적으로 추가합니다.
다중 프로젝트 빌드
각 하위 프로젝트에 대해 여러 build.sbt 파일 대신
.
├── README.md
├── build.sbt // OK
├── multi1
│ ├── build.sbt // NOK
│ ├── src
│ └── target
├── multi2
│ ├── build.sbt // NOK
│ ├── src
│ └── target
├── project // this is the meta-project
│ ├── FooPlugin.scala // custom auto plugin
│ ├── build.properties // version of sbt and hence Scala for meta-project
│ ├── build.sbt // OK - this is actually for meta-project
│ ├── plugins.sbt // OK
│ ├── project
│ └── target
└── target
build.sbt
그들 모두를 지배 할 단일 마스터 가
.
├── README.md
├── build.sbt // single build.sbt to rule theme all
├── common
│ ├── src
│ └── target
├── multi1
│ ├── src
│ └── target
├── multi2
│ ├── src
│ └── target
├── project
│ ├── FooPlugin.scala
│ ├── build.properties
│ ├── build.sbt
│ ├── plugins.sbt
│ ├── project
│ └── target
└── target
다중 프로젝트 빌드에서 공통 설정 을 고려 하는 일반적인 관행이 있습니다.
val에 일련의 공통 설정을 정의하고 각 프로젝트에 추가합니다. 그런 식으로 배울 개념이 적습니다.
예를 들면
lazy val commonSettings = Seq(
scalacOptions := Seq(
"-Xfatal-warnings",
...
),
publishArtifact := true,
...
)
lazy val root = project
.in(file("."))
.settings(settings)
.aggregate(
multi1,
multi2
)
lazy val multi1 = (project in file("multi1")).settings(commonSettings)
lazy val multi2 = (project in file("multi2")).settings(commonSettings)
프로젝트 탐색
projects // list all projects
project multi1 // change to particular project
플러그인
빌드 정의는 project/
. 여기에서 .scala
파일 을 생성하여 플러그인을 정의 합니다.
. // directory of the (main) proper project
├── project
│ ├── FooPlugin.scala // auto plugin
│ ├── build.properties // version of sbt library and indirectly Scala used for the plugin
│ ├── build.sbt // build definition of the plugin
│ ├── plugins.sbt // these are plugins for the main (proper) project, not the meta project
│ ├── project // the turtle supporting this turtle
│ └── target // compiled binaries of the plugin
여기에 최소한의 자동 플러그인 아래는project/FooPlugin.scala
object FooPlugin extends AutoPlugin {
object autoImport {
val barTask = taskKey[Unit]("")
}
import autoImport._
override def requires = plugins.JvmPlugin // avoids having to call enablePlugin explicitly
override def trigger = allRequirements
override lazy val projectSettings = Seq(
scalacOptions ++= Seq("-Xfatal-warnings"),
barTask := { println("hello task") },
commands += Command.command("cmd") { state =>
"""eval println("hello command")""" :: state
}
)
}
재정의
override def requires = plugins.JvmPlugin
enablePlugin
에서 명시 적으로 호출하지 않고도 모든 하위 프로젝트에 대해 플러그인을 효과적으로 활성화해야합니다 build.sbt
.
IntelliJ 및 SBT
다음 설정을 활성화하십시오 (실제로 기본적 으로 활성화되어야 함 ).
use sbt shell
아래에
Preferences | Build, Execution, Deployment | sbt | sbt projects