例題でみる sbt
このページは、 sbt runner をインストールしたことを前提とする。
sbt の内部がどうなっているかや理由みたいなことを解説する代わりに、例題を次々と見ていこう。
最小 sbt ビルドを作る
mkdir foo-build
cd foo-build
touch build.sbt
mkdir project
echo "sbt.version=2.0.0-RC2" > project/build.properties
sbt シェルを起ち上げる
$ sbt
[info] welcome to sbt 2.0.0-RC2 (Azul Systems, Inc. Java)
....
[info] started sbt server
sbt:foo-build>
sbt シェルを終了させる
sbt シェルを終了させるには、exit
と入力するか、Ctrl+D (Unix) か Ctrl+Z (Windows) を押す。
sbt:foo-build> exit
プロジェクトをコンパイルする
表記の慣例として sbt:...>
や >
というプロンプトは、sbt シェルに入っていることを意味することにする。
$ sbt
sbt:foo-build> compile
[success] elapsed time: 0 s, cache 0%, 1 onsite task
コード変更時に再コンパイルする
compile
コマンド (やその他のコマンド) を ~
で始めると、プロジェクト内のソース・ファイルが変更されるたびにそのコマンドが自動的に再実行される。
sbt:foo-build> ~compile
[success] elapsed time: 0 s, cache 100%, 1 disk cache hit
[info] 1. Monitoring source files for foo-build/compile...
[info] Press <enter> to interrupt or '?' for more options.
ソース・ファイルを書く
上記のコマンドは走らせたままにする。別のシェルかファイルマネージャーからプロジェクトのディレクトリへ行って、src/main/scala/example
というディレクトリを作る。次に好きなエディタを使って example
ディレクトリ内に以下のファイルを作成する:
package example
@main def main(args: String*): Unit =
println(s"Hello ${args.mkString}")
この新しいファイルは実行中のコマンドが自動的に検知したはずだ:
[info] Build triggered by /tmp/foo-build/src/main/scala/example/Hello.scala. Running 'compile'.
[info] compiling 1 Scala source to /tmp/foo-build/target/out/jvm/scala-3.3.3/foo/backend ...
[success] elapsed time: 1 s, cache 0%, 1 onsite task
[info] 2. Monitoring source files for foo-build/compile...
[info] Press <enter> to interrupt or '?' for more options.
~compile
を抜けるには Enter
を押す。
以前のコマンドを実行する
sbt シェル内で上矢印キーを 2回押して、上で実行した compile
コマンドを探す。
sbt:foo-build> compile
ヘルプを読む
help
コマンドを使って、基礎コマンドの一覧を表示する。
sbt:foo-build> help
<command> (; <command>)* Runs the provided semicolon-separated commands.
about Displays basic information about sbt and the build.
tasks Lists the tasks defined for the current project.
settings Lists the settings defined for the current project.
reload (Re)loads the current project or changes to plugins project or returns from it.
new Creates a new sbt build.
new Creates a new sbt build.
projects Lists the names of available projects or temporarily adds/removes extra builds to the session.
....
特定のタスクの説明を表示させる:
sbt:foo-build> help run
Runs a main class, passing along arguments provided on the command line.
アプリを実行する
sbt:foo-build> run
[info] running example.main
Hello
[success] elapsed time: 0 s, cache 50%, 1 disk cache hit, 1 onsite task
sbt シェルから scalaVersion を設定する
sbt:foo-build> set scalaVersion := "3.7.2"
[info] Defining scalaVersion
[info] The new value will be used by Compile / bspBuildTarget, Compile / dependencyTreeCrossProjectId and 51 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] set current project to foo (in build file:/tmp/foo-build/)
scalaVersion
セッティングを確認する:
sbt:foo-build> scalaVersion
[info] 3.7.2
セッションを build.sbt へと保存する
アドホックに設定したセッティングは session save
で保存できる。
sbt:foo-build> session save
[info] Reapplying settings...
[info] set current project to foo-build (in build file:/tmp/foo-build/)
[warn] build source files have changed
[warn] modified files:
[warn] /tmp/foo-build/build.sbt
[warn] Apply these changes by running `reload`.
[warn] Automatically reload the build when source changes are detected by setting `Global / onChangedBuildSource := ReloadOnSourceChanges`.
[warn] Disable this warning by setting `Global / onChangedBuildSource := IgnoreSourceChanges`.
build.sbt
ファイルは以下のようになったはずだ:
scalaVersion := "3.7.2"
プロジェクトに名前を付ける
エディタを使って、build.sbt
を以下のように変更する:
scalaVersion := "3.3.3"
organization := "com.example"
name := "Hello"
ビルドの再読み込み
reload
コマンドを使ってビルドを再読み込みする。このコマンドは build.sbt
を読み直して、そこに書かれたセッティングを再適用する。
sbt:foo-build> reload
[info] welcome to sbt 2.x (Azul Systems, Inc. Java)
[info] loading project definition from /tmp/foo-build/project
[info] loading settings for project hello from build.sbt ...
[info] set current project to Hello (in build file:/tmp/foo-build/)
sbt:Hello>
プロンプトが sbt:Hello>
に変わったことに注目してほしい。
libraryDependencies に toolkit-test を追加する
エディタを使って、build.sbt
を以下のように変更する:
scalaVersion := "3.3.3"
organization := "com.example"
name := "Hello"
libraryDependencies += "org.scala-lang" %% "toolkit-test" % "0.1.7" % Test
reload
コマンドを使って、build.sbt
の変更を反映させる。
sbt:Hello> reload
差分テストを実行する
sbt:Hello> test
差分テストを継続的に実行する
sbt:Hello> ~test
テストを書く
上のコマンドを走らせたままで、エディタから src/test/scala/example/HelloSuite.scala
という名前のファイルを作成する:
package example
class HelloSuite extends munit.FunSuite:
test("Hello should start with H") {
assert("hello".startsWith("H"))
}
end HelloSuite
~test
が検知したはずだ:
example.HelloSuite:
==> X example.HelloSuite.Hello should start with H 0.012s munit.FailException: /tmp/foo-build/src/test/scala/example/HelloSuite.scala:5 assertion failed
4: test("Hello should start with H") {
5: assert("hello".startsWith("H"))
6: }
at munit.FunSuite.assert(FunSuite.scala:11)
at example.HelloSuite.$init$$$anonfun$1(HelloSuite.scala:5)
[error] Failed: Total 1, Failed 1, Errors 0, Passed 0
[error] Failed tests:
[error] example.HelloSuite
[error] (Test / testQuick) sbt.TestsFailedException: Tests unsuccessful
[error] elapsed time: 1 s, cache 50%, 3 disk cache hits, 3 onsite tasks
テストが通るようにする
エディタを使って src/test/scala/example/HelloSuite.scala
を以下のように変更する:
package example
class HelloSuite extends munit.FunSuite:
test("Hello should start with H") {
assert("Hello".startsWith("H"))
}
end HelloSuite
テストが通過したことを確認して、Enter
を押して継続的テストを抜ける。
ライブラリ依存性を追加する
エディタを使って、build.sbt
を以下のように変更する:
scalaVersion := "3.3.3"
organization := "com.example"
name := "Hello"
libraryDependencies ++= Seq(
"org.scala-lang" %% "toolkit" % "0.1.7",
"org.scala-lang" %% "toolkit-test" % "0.1.7" % Test,
)
reload
コマンドを使って、build.sbt
の変更を反映させる。
Scala REPL を使う
New York の現在の天気を調べてみる。
sbt:Hello> console
Welcome to Scala 3.3.3 (1.8.0_402, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala>
import sttp.client4.quick.*
import sttp.client4.Response
val newYorkLatitude: Double = 40.7143
val newYorkLongitude: Double = -74.006
val response: Response[String] = quickRequest
.get(
uri"https://api.open-meteo.com/v1/forecast?latitude=\$newYorkLatitude&longitude=\$newYorkLongitude¤t_weather=true"
)
.send()
println(ujson.read(response.body).render(indent = 2))
// press Ctrl+D
// Exiting paste mode, now interpreting.
{
"latitude": 40.710335,
"longitude": -73.99307,
"generationtime_ms": 0.36704540252685547,
"utc_offset_seconds": 0,
"timezone": "GMT",
"timezone_abbreviation": "GMT",
"elevation": 51,
"current_weather": {
"temperature": 21.3,
"windspeed": 16.7,
"winddirection": 205,
"weathercode": 3,
"is_day": 1,
"time": "2023-08-04T10:00"
}
}
import sttp.client4.quick._
import sttp.client4.Response
val newYorkLatitude: Double = 40.7143
val newYorkLongitude: Double = -74.006
val response: sttp.client4.Response[String] = Response({"latitude":40.710335,"longitude":-73.99307,"generationtime_ms":0.36704540252685547,"utc_offset_seconds":0,"timezone":"GMT","timezone_abbreviation":"GMT","elevation":51.0,"current_weather":{"temperature":21.3,"windspeed":16.7,"winddirection":205.0,"weathercode":3,"is_day":1,"time":"2023-08-04T10:00"}},200,,List(:status: 200, content-encoding: deflate, content-type: application/json; charset=utf-8, date: Fri, 04 Aug 2023 10:09:11 GMT),List(),RequestMetadata(GET,https://api.open-meteo.com/v1/forecast?latitude=40.7143&longitude...
scala> :q // to quit
サブプロジェクトを作成する
build.sbt
を以下のように変更する:
scalaVersion := "3.3.3"
organization := "com.example"
lazy val hello = project
.in(file("."))
.settings(
name := "Hello",
libraryDependencies ++= Seq(
"org.scala-lang" %% "toolkit" % "0.1.7",
"org.scala-lang" %% "toolkit-test" % "0.1.7" % Test
)
)
lazy val helloCore = project
.in(file("core"))
.settings(
name := "Hello Core"
)
reload
コマンドを使って、build.sbt
の変更を反映させる。
全てのサブプロジェクトを列挙する
sbt:Hello> projects
[info] In file:/tmp/foo-build/
[info] * hello
[info] helloCore
サブプロジェクトをコンパイルする
sbt:Hello> helloCore/compile
サブプロジェクトに toolkit-test を追加する
build.sbt
を以下のように変更する:
scalaVersion := "3.3.3"
organization := "com.example"
val toolkitTest = "org.scala-lang" %% "toolkit-test" % "0.1.7"
lazy val hello = project
.in(file("."))
.settings(
name := "Hello",
libraryDependencies ++= Seq(
"org.scala-lang" %% "toolkit" % "0.1.7",
toolkitTest % Test
)
)
lazy val helloCore = project
.in(file("core"))
.settings(
name := "Hello Core",
libraryDependencies += toolkitTest % Test
)
コマンドをブロードキャストする
hello
に送ったコマンドを helloCore
にもブロードキャストするために集約を設定する:
scalaVersion := "3.3.3"
organization := "com.example"
val toolkitTest = "org.scala-lang" %% "toolkit-test" % "0.1.7"
lazy val hello = project
.in(file("."))
.aggregate(helloCore)
.settings(
name := "Hello",
libraryDependencies ++= Seq(
"org.scala-lang" %% "toolkit" % "0.1.7",
toolkitTest % Test
)
)
lazy val helloCore = project
.in(file("core"))
.settings(
name := "Hello Core",
libraryDependencies += toolkitTest % Test
)
reload
後、~test
は両方のサブプロジェクトに作用する:
sbt:Hello> ~test
Enter
を押して継続的テストを抜ける。
hello が helloCore に依存するようにする
サブプロジェクト間の依存関係を定義するには .dependsOn(...)
を使う。ついでに、toolkit への依存性も helloCore
に移そう。
scalaVersion := "3.3.3"
organization := "com.example"
val toolkitTest = "org.scala-lang" %% "toolkit-test" % "0.1.7"
lazy val hello = project
.in(file("."))
.aggregate(helloCore)
.dependsOn(helloCore)
.settings(
name := "Hello",
libraryDependencies += toolkitTest % Test
)
lazy val helloCore = project
.in(file("core"))
.settings(
name := "Hello Core",
libraryDependencies += "org.scala-lang" %% "toolkit" % "0.1.7",
libraryDependencies += toolkitTest % Test
)
uJson を使って JSON をパースする
helloCore
に uJson を追加しよう。
core/src/main/scala/example/core/Weather.scala
を追加する:
package example.core
import sttp.client4.quick._
import sttp.client4.Response
object Weather:
def temp() =
val response: Response[String] = quickRequest
.get(
uri"https://api.open-meteo.com/v1/forecast?latitude=40.7143&longitude=-74.006¤t_weather=true"
)
.send()
val json = ujson.read(response.body)
json.obj("current_weather")("temperature").num
end Weather
次に src/main/scala/example/Hello.scala
を以下のように変更する:
package example
import example.core.Weather
@main def main(args: String*): Unit =
val temp = Weather.temp()
println(s"Hello! The current temperature in New York is $temp C.")
アプリを走らせてみて、うまくいったか確認する:
sbt:Hello> run
[info] compiling 1 Scala source to /tmp/foo-build/core/target/scala-2.13/classes ...
[info] compiling 1 Scala source to /tmp/foo-build/target/scala-2.13/classes ...
[info] running example.Hello
Hello! The current temperature in New York is 22.7 C.
一時的に scalaVersion を変更する
sbt:Hello> ++3.3.3!
[info] Forcing Scala version to 3.3.3 on all projects.
[info] Reapplying settings...
[info] Set current project to Hello (in build file:/tmp/foo-build/)
scalaVersion
セッティングを確認する:
sbt:Hello> scalaVersion
[info] helloCore / scalaVersion
[info] 3.3.3
[info] scalaVersion
[info] 3.3.3
このセッティングは reload
後には無効となる。
バッチ・モード
sbt のコマンドをターミナルから直接渡して sbt をバッチモードで実行することができる。
$ sbt clean "testOnly HelloSuite"
sbt new コマンド
sbt new
コマンドを使って手早く簡単な Hello world ビルドをセットアップすることができる。
$ sbt new scala/scala-seed.g8
....
A minimal Scala project.
name [My Something Project]: hello
Template applied in ./hello
プロジェクト名を入力するプロンプトが出てきたら hello
と入力する。
これで、hello
ディレクトリ以下に新しいプロジェクトができた。
クレジット
本ページは William “Scala William” Narmontas さん作の Essential sbt というチュートリアルに基づいて書かれた。