誰かがSBTの正しい使い方を説明できますか?


100

これでクローゼットから降りてきます!SBTがわかりません。そこで、私はそれを言いました、今私を助けてください。

すべての道はローマに通ず、それはSBTのための同じである:を始めるためにSBTありSBTSBT LauncherSBT-extras、など、その後含まれ、リポジトリを決定するためにさまざまな方法があります。「最善の」方法はありますか?

時々私は少し迷子になるので私は尋ねています。SBTのドキュメントは非常に詳細で完全ですが、いつbuild.sbtor project/build.propertiesまたはproject/Build.scalaor を使用するかわからないことに気づきましたproject/plugins.sbt

そして、それが楽しくなり、そこにあるScala-IDESBT- それらを一緒に使用する正しい方法は何ですか?何が最初に来る、鶏または卵?

最も重要なことは、おそらく、プロジェクトに含める適切なリポジトリとバージョンをどのように見つけるかです。マシェットを引き出して、ハッキングを始めますか?すべてを含むプロジェクトと台所の流しが含まれているプロジェクトを見つけることがよくありますが、それに気づきました-少し迷っているのは私だけではありません。

簡単な例として、今、私は真新しいプロジェクトを始めています。私は、最新の機能を使用するSLICKScala、これはおそらく、SBTの最新バージョンが必要になります。始めるのに正しい点は何ですか?なぜですか?どのファイルでそれを定義し、どのように見えるべきですか?私はこれを機能させることができることを知っていますが、私は本当にすべてがどこに行くべきかについての専門家の意見が欲しいです(なぜそれが行くべきなのかはボーナスになります)。

私はSBT1年以上ずっと小さなプロジェクトに使用しています。私はSBTそれを使ってSBT Extras(それが魔法のようにいくつかの頭痛を消したので)、しかし、なぜどちらを使うべきなのか分かりません。私は物事がどのように組み合わさっているか(SBTおよびリポジトリ)を理解していないことに少し不満を感じています。これを人間の言葉で説明できれば、次の人がこのようにやって来るのを大変な苦労から救うことができると思います。


2
「Scala-IDEとSBTがある」とはどういう意味ですか?プロジェクトをsbtで定義すると、sbtはide(eclipse oder intellij)プロジェクトを生成できます。SBTが最初に来る...
Jan

2
@Jan Scala-IDEはビルドマネージャーとしてSBTを使用するため、assembla.com/spaces/scala-ide/wiki/SBT-based_build_managerを参照し、「SBTプロジェクトファイルを定義する必要はありません。」と述べた投稿の下部をご覧ください。私はそれを混乱させました。
Jack

OK。私は通常intellij(または崇高な)を使用してscalaを編集するので、それを知りませんでした。ビルダーが独自のsbt設定を生成すると思いますか?
2012

2
@JacobusR Scala IDEがSBTを使用してプロジェクトのソースを構築するという事実は実装の詳細であり、ユーザーはこれについて心配する必要ありません。実際には0の影響があります。Eclipseの外では、ユーザーはSBT、Maven、Antなどを使用してプロジェクトをビルドできます。これは、Scala IDEに影響を与えません。もう1つ、SBTプロジェクトがあったとしても、Scala IDEは気にしません。つまり、Build.scalaクラスパスを設定する必要はありません。そのため、実際にEclipse .classpathを生成するためにsbteclipseが必要です。お役に立てれば。
Mirco Dotta

1
@Jan Scala IDEが混乱に加わりました。そうです。優れたScala開発環境のセットアップに関する全体像と適切なプログラミングワークフローの確かなガイダンスを提供するドキュメントが非常に役に立ちます。
ジャック

回答:


29

最も重要なことは、おそらく、プロジェクトに含める適切なリポジトリとバージョンをどのように見つけるかです。マシェットを引き出して、ハッキングを始めますか?私はすべてのものと台所の流しを含むプロジェクトをよく見つけます

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に対する免疫を徐々に構築することだと思います。

理解してください:

  1. スコープフォーマット {<build-uri>}<project-id>/config:key(for task-key)
  2. 設定の3つの味(SettingKeyTaskKeyInputKey) -で「タスクキー」と呼ばれるセクションを読んでhttp://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def

ジャンプしてさまざまな定義や例を調べられるように、これらの4ページは常に開いたままにしてください。

  1. http://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def
  2. http://www.scala-sbt.org/release/docs/Detailed-Topics/index
  3. http://harrah.github.com/xsbt/latest/sxr/Keys.scala.html
  4. http://harrah.github.com/xsbt/latest/sxr/Defaults.scala.html

最大限に活用showし、inspect そしてタブ補完実際の設定の値は、その依存関係、定義および関連する設定に慣れるために。あなたが使用しinspectて発見する関係がどこにも文書化されているとは思いません。もっと良い方法があれば知りたいです。


25

私がsbtを使う方法は:

  1. sbt-extrasを使用する-シェルスクリプトを取得してプロジェクトのルートに追加するだけ
  2. sbtをセットアップするためprojectMyProject.scalaファイルを含むフォルダーを作成します。私はこれをbuild.sbtアプローチよりもはるかに好みます-それはscalaでより柔軟です
  3. を作成する project/plugins.sbtファイルをし、IDEに適切なプラグインを追加します。sbt-eclipse、sbt-idea、またはensime-sbt-cmdのいずれか。これにより、eclipse、intellij、またはensimeのプロジェクトファイルを生成できます。
  4. プロジェクトのルートでsbtを起動し、IDEのプロジェクトファイルを生成します。
  5. 利益

IDEプロジェクトファイルはsbtによって生成されるため、チェックインする必要はありませんが、そうしたい理由があるかもしれません。

このように設定した例をここに示します


良い答えをありがとう。もう1つの回答を受け入れました。これはより広い範囲をカバーするためです。また、あなたの原因を賛成票で投票したのも、本当に良いことです。できれば両方を受け入れたでしょう。
Jack

0

Typesafe Activatorを使用します。これは、プロジェクトテンプレートとシードに付属するsbtを呼び出すための特別な方法です。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)

5
疑わしいときは、ミックスに魔法を追加して問題が解決しない可能性が高いという考えには賛成です。
キュービック

0

取り付け

brew install sbt または技術的に言えば構成されているsbtをインストールします

実行すると sbtターミナルから、実際にはsbtランチャーbashスクリプトれます。個人的には、この三位一体について心配する必要はなく、sbtを1つのものであるかのように使用しました。

構成

特定のプロジェクトのsbtを構成するに.sbtoptsは、プロジェクトのルートにあるファイルを保存します。システム全体でsbtを変更するには、を変更し/usr/local/etc/sbtoptsます。実行sbt -helpすると、正確な場所がわかります。たとえば、sbtに1回限りの実行としてより多くのメモリを与える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の重要な概念の1つです。質問で説明してみます。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は、という名前の通常のScalaオブジェクトの単なるメンバーであることがわかります$9c2192aea3f1db3c251d。明らかに、適切なプロジェクトの依存関係ではなく、scalaj-httpプロジェクト定義の依存関係であるべき$9c2192aea3f1db3c251dです。ビルド定義のScalaプロジェクトが存在する場所なので、project/build.sbtではなくで宣言する必要があります。build.sbtproject

ビルド定義が単なる別のScalaプロジェクトであることを強調するには、を実行しsbt consoleProjectます。これにより、クラスパス上のビルド定義プロジェクトを含むScala REPLがロードされます。次の行に沿ってインポートが表示されるはずです

import $9c2192aea3f1db3c251d

これで、build.sbtDSL ではなくScala本体を使用してビルド定義プロジェクトを直接操作できるようになりました。たとえば、次のように実行しますfooTask

$9c2192aea3f1db3c251d.fooTask.eval

build.sbtルートプロジェクトの下には、ビルド定義Scalaプロジェクトを定義するのに役立つ特別なDSLがありますproject/

ビルド定義のScalaプロジェクトには、独自のビルド定義のScalaプロジェクトを含めることができますproject/project/sbtは再帰的であると言います。

sbtはデフォルトで並列です

sbt はタスクからDAGを構築します。これにより、タスク間の依存関係を分析し、それらを並行して実行し、重複排除を実行することもできます。build.sbtDSLはこれを念頭に置いて設計されているため、最初は驚くべきセマンティクスにつながる可能性があります。次のスニペットの実行順序はどうなっていると思いますか?

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

またはの順理由ab保証するものではありません

b
a
hello

おそらく逆説的に、sbtではシリアルよりパラレルの方が簡単です。シリアル注文が必要なDef.sequential場合はDef.taskDynfor-comprehensionなどの特別なものを使用するか、エミュレートする必要があります。

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まだ値を割り当てることができない理由が少しはっきりしているかもしれません。関係構築の段階ではまだ価値はありません。

では、Scala本体とDSL言語のセマンティクスの違いを明確に見ることができbuild.sbtます。ここに私に役立ついくつかの経験則があります

  • DAGはタイプの式から作成されます Setting[T]
  • ほとんどの場合、.value構文を使用するだけで、sbtは、Setting[T]
  • 時々、DAGの一部を手動で微調整する必要があり、そのために使用するDef.sequentialか、Def.taskDyn
  • これらの順序付け/関係のシンタティックな奇妙さが処理されると、タスクのビジネスロジックの残りの部分を構築するために、通常のScalaセマンティクスに依存できます。

 コマンドとタスク

コマンドは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

プラグイン

ビルド定義はにある適切なScalaプロジェクトであることを覚えておいてください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

主な参考文献

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.