Scalaプログラミング6 sbtを使ってみる2 依存ライブラリ管理とクロスビルド
酢豚が美味しかった。 by HIRAOKA,Yasunobu
初心者ぺちぱーがGitHubでScalaレッスンを始めたぞ。今回はScalaの標準的なビルドツールであるsbtの使い方の続き。クロスビルドとか、設定に使う演算子みたいなメソッドとか、継続ビルド(continuous build)とか、そのへんの話。
Cross-building and library dependencies setting
前回はほぼマニュアル通りの設定ファイルを書いてみた。これにもう少し設定を追加してみよう。
Getting-Started/Cross-buildingによると、crossScalaVersions
を指定しておくと、クロスビルドできるようだ。Scalaカンファレンスで聞いた話だと、バイナリーバージョンが違うと、ライブラリが使用できないらしいので、もしGitHubなんかでライブラリ開発をするような場合は、クロスビルドしておかないと対応可能なはずのバージョンで使用出来ない、なんてことになってしまうのかもしれない。
2.9.0以降のバージョンとScalaTestをbuild.sbtに追加してみよう。
name := "hello"
version := "1.0"
scalaVersion := "2.10.1"
crossScalaVersions := Seq(
"2.9.0",
"2.9.1",
"2.9.2",
"2.9.3",
//"2.10.0",
"2.10.1"
)
libraryDependencies <+= scalaVersion(v => v match {
case "2.10.1" => "org.scalatest" % "scalatest_2.10" % "1.9.1" % "test"
case _ => "org.scalatest" %% "scalatest" % "1.9.1" % "test"
})
2.10.0を外しているのは、コンパイル後のコードがtarget/scala-2.10
というディレクトリに生成されるから、という理由。2.10以降でバイナリーバージョンが同じになったから? 2.9までは、target/
以下にscala-2.9.2
とかscala-2.9.1
みたいなディレクトリに分かれている。
同じディレクトリに生成されるので、2.10.0と2.10.1を設定していると、+ compile
したときに、ファイル変更が無くても毎回コンパイルされてしまう。+ test
でも同じく、毎回コンパイルされた後にテストが実行される。2.10.1だけにすると、ファイル変更がなければコンパイルは一度だけになった。
$ tree
.
├── build.properties
├── build.sbt
├── project
│ └── target
│ └── config-classes
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── scala
│ │ └── jp
│ │ └── satooshi
│ │ └── hello
│ │ └── Hello.scala
│ └── test
│ ├── java
│ ├── resources
│ └── scala
│ └── jp
│ └── satooshi
│ └── hello
│ └── HelloTest.scala
└── target
├── resolution-cache
│ ├── hello
│ │ ├── hello_2.10
│ │ │ └── 1.0
│ │ │ ├── resolved.xml.properties
│ │ │ └── resolved.xml.xml
│ │ ├── hello_2.9.0
│ │ │ └── 1.0
│ │ │ ├── resolved.xml.properties
│ │ │ └── resolved.xml.xml
│ │ ├── hello_2.9.1
│ │ │ └── 1.0
│ │ │ ...
│ │ ├── hello_2.9.2
│ │ │ └── 1.0
│ │ │ ...
│ │ └── hello_2.9.3
│ │ └── 1.0
│ │ ...
├── scala-2.10
│ ├── cache
│ │ └── default-ea94fa
│ │ ├── compile
│ │ │ ├── copy-resources
│ │ │ ├── for_doc
│ │ │ │ └── scala
│ │ │ │ ├── inputs
│ │ │ │ └── output
│ │ │ └── inc_compile
│ │ ├── global
│ │ │ └── update
│ │ │ ├── inputs
│ │ │ └── output
│ │ └── test
│ │ ├── copy-resources
│ │ ├── inc_compile
│ │ └── succeeded_tests
│ ├── classes
│ │ └── jp
│ │ └── satooshi
│ │ └── hello
│ │ ├── Hello$.class
│ │ └── Hello.class
│ └── test-classes
│ └── jp
│ └── satooshi
│ └── hello
│ ├── HelloTest$$anonfun$1.class
│ └── HelloTest.class
├── scala-2.9.0
│ ├── cache
│ │ └── default-ea94fa
│ │ ├── compile
│ │ │ ├── copy-resources
│ │ │ └── inc_compile
│ │ ├── global
│ │ │ └── update
│ │ │ ├── inputs
│ │ │ └── output
│ │ └── test
│ │ ├── copy-resources
│ │ ├── inc_compile
│ │ └── succeeded_tests
│ ├── classes
│ │ └── jp
│ │ └── satooshi
│ │ └── hello
│ │ ├── Hello$.class
│ │ └── Hello.class
│ └── test-classes
│ └── jp
│ └── satooshi
│ └── hello
│ ├── HelloTest$$anonfun$1.class
│ └── HelloTest.class
├── scala-2.9.1
...
├── scala-2.9.2
...
なので、2.10系は最新版だけいれておけば良さそうな気がする。
libraryDependencies
には見慣れない演算子みたいなメソッドが使われている。これは後で説明。この設定の意味は、scalaVersion
に応じて、使用するライブラリ(ここではScalaTest)を変更する、というものだ。実は、ScalaTestの場合、デフォルトケースだけで設定しても問題なかったが、設定方法は2.10かどうかで分けて書いてあったので、これにならって設定した。
では、クロスビルドしてみる。タスクの前に+
を付けるとcrossScalaVersions
で設定した各バージョンでタスクが実行されるようだ。
> + compile
Setting version to 2.9.0
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Updating {file:/Users/satoshi/prj/work/scala/hello/}default-ea94fa...
[info] Resolving org.sonatype.oss#oss-parent;7 ...
[info] downloading http://repo1.maven.org/maven2/org/scalatest/scalatest_2.9.0/1.9.1/scalatest_2.9.0-1.9.1.jar ...
[info] [SUCCESSFUL ] org.scalatest#scalatest_2.9.0;1.9.1!scalatest_2.9.0.jar (10447ms)
[info] Done updating.
[success] Total time: 14 s, completed 2013/03/28 16:17:14
Setting version to 2.9.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Updating {file:/Users/satoshi/prj/work/scala/hello/}default-ea94fa...
[info] Resolving org.sonatype.oss#oss-parent;7 ...
[info] downloading http://repo1.maven.org/maven2/org/scalatest/scalatest_2.9.1/1.9.1/scalatest_2.9.1-1.9.1.jar ...
[info] [SUCCESSFUL ] org.scalatest#scalatest_2.9.1;1.9.1!scalatest_2.9.1.jar (6783ms)
[info] Done updating.
[success] Total time: 10 s, completed 2013/03/28 16:17:24
Setting version to 2.9.2
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Updating {file:/Users/satoshi/prj/work/scala/hello/}default-ea94fa...
[info] Resolving org.sonatype.oss#oss-parent;7 ...
[info] downloading http://repo1.maven.org/maven2/org/scalatest/scalatest_2.9.2/1.9.1/scalatest_2.9.2-1.9.1.jar ...
[info] [SUCCESSFUL ] org.scalatest#scalatest_2.9.2;1.9.1!scalatest_2.9.2.jar (6780ms)
[info] Done updating.
[success] Total time: 9 s, completed 2013/03/28 16:17:33
Setting version to 2.9.3
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Updating {file:/Users/satoshi/prj/work/scala/hello/}default-ea94fa...
[info] Resolving org.sonatype.oss#oss-parent;7 ...
[info] downloading http://repo1.maven.org/maven2/org/scalatest/scalatest_2.9.3/1.9.1/scalatest_2.9.3-1.9.1.jar ...
[info] [SUCCESSFUL ] org.scalatest#scalatest_2.9.3;1.9.1!scalatest_2.9.3.jar (11996ms)
[info] Done updating.
[success] Total time: 17 s, completed 2013/03/28 16:17:50
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[success] Total time: 0 s, completed 2013/03/28 16:17:50
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
> + run
Setting version to 2.9.0
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Running jp.satooshi.hello.Hello
へろー
[success] Total time: 0 s, completed 2013/03/28 16:17:59
Setting version to 2.9.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Running jp.satooshi.hello.Hello
へろー
[success] Total time: 0 s, completed 2013/03/28 16:18:00
Setting version to 2.9.2
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Running jp.satooshi.hello.Hello
へろー
[success] Total time: 0 s, completed 2013/03/28 16:18:00
Setting version to 2.9.3
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Running jp.satooshi.hello.Hello
へろー
[success] Total time: 0 s, completed 2013/03/28 16:18:00
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Running jp.satooshi.hello.Hello
へろー
[success] Total time: 0 s, completed 2013/03/28 16:18:00
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
> + test
Setting version to 2.9.0
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
へろー
[info] HelloTest:
[info] - Hello should run main
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 0 s, completed 2013/03/28 16:18:04
Setting version to 2.9.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
へろー
[info] HelloTest:
[info] - Hello should run main
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 0 s, completed 2013/03/28 16:18:04
Setting version to 2.9.2
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
へろー
[info] HelloTest:
[info] - Hello should run main
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 0 s, completed 2013/03/28 16:18:04
Setting version to 2.9.3
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
へろー
[info] HelloTest:
[info] - Hello should run main
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 0 s, completed 2013/03/28 16:18:05
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
へろー
[info] HelloTest:
[info] - Hello should run main
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 0 s, completed 2013/03/28 16:18:05
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
> + doc
Setting version to 2.9.0
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Generating Scala API documentation for main sources to /Users/satoshi/prj/work/scala/hello/target/scala-2.9.0/api...
model contains 2 documentable templates
[info] Scala API documentation generation successful.
[success] Total time: 2 s, completed 2013/03/28 16:29:08
Setting version to 2.9.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Generating Scala API documentation for main sources to /Users/satoshi/prj/work/scala/hello/target/scala-2.9.1/api...
model contains 2 documentable templates
[info] Scala API documentation generation successful.
[success] Total time: 1 s, completed 2013/03/28 16:29:09
Setting version to 2.9.2
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Generating Scala API documentation for main sources to /Users/satoshi/prj/work/scala/hello/target/scala-2.9.2/api...
model contains 2 documentable templates
[info] Scala API documentation generation successful.
[success] Total time: 1 s, completed 2013/03/28 16:29:10
Setting version to 2.9.3
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Generating Scala API documentation for main sources to /Users/satoshi/prj/work/scala/hello/target/scala-2.9.3/api...
model contains 2 documentable templates
[info] Scala API documentation generation successful.
[success] Total time: 1 s, completed 2013/03/28 16:29:11
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
[info] Generating Scala API documentation for main sources to /Users/satoshi/prj/work/scala/hello/target/scala-2.10/api...
model contains 2 documentable templates
[info] Scala API documentation generation successful.
[success] Total time: 1 s, completed 2013/03/28 16:29:11
Setting version to 2.10.1
[info] Set current project to hello (in build file:/Users/satoshi/prj/work/scala/hello/)
Setting manner
More Kinds of Settingと、harrah / xsbtには、設定に使えるメソッドの説明が記載してあった。
Refresher
:=
上書き
新しい設定値を設定する。または、既存の設定値を上書きする。
name := "My Project"
Appending to previous values
+=
1つ追加(他の設定値は使用しない)
++=
Seqを追加(他の設定値は使用しない)
既存の設定に設定値を追加する。上書きされない。
libraryDependencies += "junit" % "junit" % "4.8" % "test"
libraryDependencies ++= Seq(
"org.scala-tools.testing" %% "scalacheck" % "1.9" % "test",
"org.scala-tools.testing" %% "specs" % "1.6.8" % "test"
)
Appending with dependencies
<+=
1つ追加(他の設定値を使用)
<++=
Seqを追加(他の設定値を使用)
他の設定値を使用して、設定値を追加する。上書きされない。依存する設定値は1つでも複数でもいいらしい。
cleanFiles <+= (name) { n => file("coverage-report-" + n + ".txt") }
libraryDependencies <+= scalaVersion( "org.scala-lang" % "scala-compiler" % _ )
libraryDependencies <++= scalaVersion { sv =>
("org.scala-lang" % "scala-compiler" % sv) ::
("org.scala-lang" % "scala-swing" % sv) ::
Nil
}
Transforming a value
~=
現在の設定値を変換
scalacOptions in Compile ~= { (options: Seq[String]) =>
options filterNot ( _ startsWith "-Y" )
}
Computing a value based on other keys' values
<<=
他の複数の設定値を使用して、設定を変更する
Getting-Started/More Kinds of Settingを読むと、依存する複数の設定値を使用して、設定値を決めることができるようだ。しかし、harrah / xsbtを読むと、他の全てのメソッドはこの<<=
で書けるらしい。新規の設定値の場合はどうなるんだろうか。このへんがまだよく分かってない。
libraryDependencies <<= libraryDependencies apply { (deps: Seq[ModuleID]) =>
// Note that :+ is a method on Seq that appends a single value
deps :+ ("junit" % "junit" % "4.8" % "test")
}
Continuous build
Continuous build and testには~
をタスクの前に付けると、ファイル変更を検知してタスクを実行できるという記載がある。
To speed up your edit-compile-test cycle, you can ask sbt to automatically recompile or run tests whenever you save a source file.
Make a command run when one or more source files change by prefixing the command with ~. For example, in interactive mode try:
> ~ compile
Press enter to stop watching for changes.
You can use the ~ prefix with either interactive mode or batch mode.
なので、ファイル変更の度にテストを実行する場合は、~ test
となる。クロスコンパイルする場合は、~ + compile
でいけた。
> ~ test
> ~ + compile
Conclustion
これでsbtを使って複数バージョンでテストしたり、依存ライブラリを管理できるようになったんじゃないか!?やったね!まとめ!
- クロスビルドの設定と実行方法
- 依存ライブラリの設定
- ファイル変更を監視して特定のタスクを実行する方法