SBTを使ったプロジェクト作成(2)

スコープに関するお話

昨日の続き。 実はSettingKeyにはスコープがあり、異なるプロジェクトではことなる値を使うことができる。そもそも同じ場所に置くなよという気はするがテスト用とかリリース用にビルドしたい場合に便利。このビルドの設定を分けるスコープの”軸”がSBTには3つある。

  • プロジェクト
  • コンフィギュレーション
  • タスク

プロジェクト軸によるスコープ

それぞれのプロジェクトに対して設定が必要という場合がこのケース。ビルド全体(全プロジェクト)でもよい。

コンフィギュレーション軸によるスコープ

ビルドの種類によるわけかた。テストとかリリース用とか、デバッグ用とか。

タスク軸によるスコープ

少しよくわからないけれど、他のタスクに影響を受けるようなスコープのことらしい。

スコープはinspectコマンドで調べられるらしい。

$ sbt
> inspect name
[info] Setting: java.lang.String = hello
[info] Description:
[info]  Project name.
[info] Provided by:
[info]  {file:/Users/sasakiumi/MyWorks/hello/}default-6cbc7c/*:name
[info] Defined at:
[info]  /Users/sasakiumi/MyWorks/hello/build.sbt:1
[info] Reverse dependencies:
[info]  *:normalized-name
[info]  *:project-info
[info]  *:organization
[info]  *:description
[info]  *:on-load-message
[info] Delegates:
[info]  *:name
[info]  {.}/*:name
[info]  */*:name
>

Provided byで書かれた部分を見ると

  • {file:/Users/sasakiumi/MyWorks/hello/}default-6cbc7c/ → プロジェクト
  • アスタリスク → コンフィギュレーション
  • →なし

ということらしい。

ライブラリ依存性

SBTはプロジェクトが必要なライブラリとかパッケージを管理してくれる。この依存ライブラリの管理には2つのタイプがある。

アンマネージ依存性

libディレクトリに直にjarファイルを入れるタイプ。特に管理とかされているわけではなく自分でダウンロードして、設置する必要があるのでアンマネージ。lib以下に入れるとプロジェクトのクラスパスに追加される。それだけだ!

マネージ依存性

Apache Ivyを使ってライブラリの依存解決を行う。これを使うためにはlibraryDependenciesキーに値を入れるだけでうまくいく。グループ、ライブラリ名、バージョンの順番だ。

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"

これをbuild.sbtに書いてupdateを打つと大体うまくいく。でも時折、参照先のリポジトリに欲しいパッケージがない場合がある。この場合はSBTにリポジトリを追加してやらないといけない。

solvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

名前 at URLというフォーマット。

.scalaビルド設定

build.sbt以外にもproject/に置いたscalaファイルでビルド設定できる。 厳密にはbuils.sbtは結局project/以下のビルド設定にマージされることになるので、単なる略記法という意味以上のものはない。とはいっても便利。具体例で説明。

``` scala project/Build.scala import sbt._ import Keys._

object HelloBuild extends Build {

val sampleKeyA = SettingKey[String]("sample-a", "demo key A")
val sampleKeyB = SettingKey[String]("sample-b", "demo key B")
val sampleKeyC = SettingKey[String]("sample-c", "demo key C")
val sampleKeyD = SettingKey[String]("sample-d", "demo key D")

override lazy val settings = super.settings ++
    Seq(sampleKeyA := "A: in Build.settings in Build.scala", resolvers := Seq())

lazy val root = Project(id = "hello",
                        base = file("."),
                        settings = Project.defaultSettings ++ Seq(sampleKeyB := "B: in the root project settings in Build.scala")) } ```

次にbuild.sbt

``` scala build.sbt sampleKeyC in ThisBuild := “C: in build.sbt scoped to ThisBuild” sampleKeyD := “D: in build.sbt”


ここでsbtインタラクティブモードで`inspect sample-a`とうつ

``` scala bash
$ sbt
> inspect sample-a
[info] Setting: java.lang.String = A: in Build.settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sample-a

続いてsample-c

``` scala bash

inspect sample-c [info] Setting: java.lang.String = C: in build.sbt scoped to ThisBuild [info] Provided by: [info] {file:/home/hp/checkout/hello/}/*:sample-c ```

Provided byで表示されるスコープは同じなため、.sbtと.scalaはどちらに何を設定しても同じということになる。

``` scala bash

inspect sample-b [info] Setting: java.lang.String = B: in the root project settings in Build.scala [info] Provided by: [info] {file:/home/hp/checkout/hello/}hello/:sample-b inspect sample-d [info] Setting: java.lang.String = D: in build.sbt [info] Provided by: [info] {file:/home/hp/checkout/hello/}hello/:sample-d ```

sample-bは異なるスコープになっていることがわかる。これはsample-dに対応していることがわかる。

あとはプラグインとかの話しがあったけれど、ここらで自分なりにプロジェクトを作ってみたいと思う。 まとめのページはここ。このページもGitHub Pagesでできてるのか。

http://scalajp.github.io/sbt-getting-started-guide-ja/summary/ でも開発がそんなに活発でないかも。Epicの開発はほぼ停止してるみたいだし。

SBTを使ったプロジェクト作成(1)

SBT(Simple Build Tool)というScalaのビルドツールを使ってみる。 ここを参考にした。

インストール

まずインストール。macの場合はHomebrewで用意されているみたい。

$ brew install sbt

プロジェクトの作成

ひな形は自分で作る。ここではhelloプロジェクトを作ってみる。

$ mkdir hello
$ echo 'object Hi { def main(args: Array[String]) = println("Hi!") }' > hw.scala
$ sbt
# ここで必要なパッケージとか勝手にインストールしてビルドしてくれるみたい
> run
・・・
Hi!

複雑なことはまだたくさんあるけれど、必要なことはこれだけだ。あとは蛇足と後学のため。でもきっと大事なことだ。

ソースコード

sbtではソースツリーはMavenと同じものを使うらしい。Maven知らない。

hello/
 build.sbt  (パッケージのmeta情報とかここにまとめる)
 project/
   Build.scala (ビルド設定とかここに書く。Makefileみたいなもの?)
 src/
   main/
     resources/
        <メインの jar に含むファイル>
     scala/
        <メインの Scala ソース>
     java/
        <メインの Java ソース>
   test/
     resources/
        <テストの jar に含むファイル>
     scala/
        <テストの Scala ソース>
     java/
        <テストの Java ソース>

これ以外は無視されるらしい。sbtコマンドでビルドが終わるとここにtargetディレクトリが追加されて、そこにできたclassファイルとかjarファイルが置かれる。Gitで管理したいなら.gitignoreにはこう書くのが定石らしい。

target/

インタラクティブモード

sbtを引数なしで実行するとインタラクティブモードになる。さっき>が出てきてrunと打ったらところで見ているはずだ。インタラクティブモードではcompileとかrunが使える。みたまんまだ。

バッチモード

コマンド(さっきインタラクティブモードで一個一個打ってたやつ)を一度に実行できる。引数も与えられる。(test-onlyにはtestAとtestBを引数に与えている)

$ sbt compile run "test-only testA testB"

継続的ビルド

ソースファイルの変更をトリガーに実行したければコマンドの先頭に~をつける。例えばインタラクティブモードで以下のようにすると、ソースをいじるたびに勝手にコンパイルしてくれるようになる。

> ~ compile
  • clean: targetの削除 make cleanみたいなもの
  • compile: コンパイルする
  • test: コンパイルして実行
  • console: コンパイル済のソースを依存ライブラリにパスを通してscalaインタプリタに入る
  • run: プロジェクトのメインクラスを実行
  • package: src/main/resources 内のファイルと src/main/scala からコンパイルされたクラスを含む jar を作る

build.sbtの書き方

ここで何書いていいかわからなかった大事なbuild.sbtが出てくる。 はじめの方に書いたけれどビルド定義にはbuild.sbtとproject/.scalaの2通りがある。ただ慣例的にはメインでbuild.sbtを使いそれでできないことを.scalaファイルで行うことが多いらしい。さてビルド定義。

ビルド定義って?

プロジェクトを調べ、全てのビルド定義ファイルを処理した後、sbt は、ビルドを記述した不可変マップ(キーと値のペア)を最終的に作る。例えば、name というキーがあり、それは文字列の値、つまり君のプロジェクト名に関連付けられる。 ビルド定義ファイルは直接には sbt のマップに影響を与えない。その代わり、ビルド定義は、型が Setting[T] のオブジェクトを含んだ巨大なリストを作る。 T はマップ内の値の型だ。(Scala の Setting[T] は Java の Setting と同様。) Setting は、新しいキーと値のペアや、既存の値への追加など、マップの変換を記述する。 (関数型プログラミングの精神に則り、変換は新しいマップを返し、古いマップは更新されない。) build.sbt では、プロジェクト名の Setting[String] を以下のように作る:

name := “hello”

この Setting[String] は name キーを追加(もしくは置換)して “hello” という値に設定することでマップを変換する。 変換されたマップは新しい sbt のマップとなる。マップを作るために、sbt はまず、同じキーへの変更が一緒に起き、かつ他のキーに依存する値の処理が依存するキーの後にくるように Setting のリストをソートする。 次に、sbt はソートされた Setting のリストを順番にみていって、一つづつマップに適用する。

まとめ: ビルド定義は Setting[T] のリストを定義し、Setting[T] は sbt のキー・値ペアへの変換を表し、T は値の型を指す。

何言ってるかわからない。 例えば最初の方に出てきたbuild.sbtはこんなだった。

name := "hello"

version := "1.0"

scalaVersion := "2.9.1"

これは:=の左側がSetting[T]型のオブジェクトで右側がその値だ。これらの値は組み込み型で型が決まっているので

name := 42

とやるとコンパイルしてくれないそうな。 キーの種類にはSettingとTaskがあり、nameとかversionはSetting。packageとかcompileはTaskとなる。なんだかまだ良くわからないけれど、設定値と実行命令みたいなものに2種類を設定できるっぽい。 ライブラリ依存性とかを書く場合はこんな感じ。ほうほう。

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"

今日は一旦このへんで。

Scalaを使うことに

汎用人工知能の開発をするにあたって使う言語とかを考えていた。汎用人工知能(まだそれが何を意味するのか明瞭にはわかってない)で必要なスペックは次のものなのではないかなとあげてみた。

  • 高速
  • 並列処理に向いている
  • 関数型(少なくとも副作用を前提としないプログラム)

ここであげた条件には自分の好み、向き不向きが含まれているけれど漠然とした現状を考えるとこれが妥当なのではないかな。 高速であることはいうまでもない。多分CとかC++が一番よい。でもC、C++はメモリ管理とか大変そうで個人的にはデカイものを書くときには向いてない気がする。 並列処理に向いているというのは機械学習を行わせたいので今流行のMahoutもJubatusも並列で計算をおこなっているのでそれが向いているものがいい。ErlangとかScalaがいいらしい。特にScalaのActorアーキテクチャはデッドロックや並列計算での問題が解消されやすいみたい。

http://www.atmarkit.co.jp/ait/articles/1209/06/news134.html

あと関数型。副作用を前提とした言語だとプログラムを書いていてバグを埋め込みやすいのでできれば関数型言語を使いたい。Haskellなんかは純粋関数型言語で今流行だけど、ライブラリの数とかでは叶わない。あとはちょっとこてこてなのでSchemeとかかな。

というわけで選んだのはScalaです。一番バランスがよかったのでこの言語で作っていこうと思います。今はこちらでScalaの勉強中。

http://www.atmarkit.co.jp/ait/articles/1202/10/news122.html

About the Author

Hi there! I’m Paul. I’m a physics major turned programmer. Ever since I first learned how to program while taking a scientific computing for physics course, I have pursued programming as a passion, and as a career. Check out my personal website for more information on my other projects (including more Jekyll themes!), as well as some of my writing.