MavenからSBTへのビルド環境の移行

Posted on 2012-01-25 by takezoux2

この記事は、sbt0.11.X系列の記事です。それ以前のバージョンでは正しく動作しません。

SBTとは

Scala(とついでにJava)のためのビルドツールです。ちなみにSimple Build Toolの略です。
特徴としては、設定等全てscalaで書くことができ拡張性が高いビルドツールになっています。

この記事では、Mavenの主要な機能を順次sbtへと移植していきます。
現在ScalaをMavenと連携して使用しているけど、sbtも安定してきた気がするしsbtへ乗り換えてみようかなと思っている人達を対象にしています。
sbtに関しては、使う上での最低限の情報と、Mavenからの移植に必要な部分のみ触れています。
きちんとsbtの使い方を知りたい方は、本家のWikiを読んでください。

SBTのセットアップ

1. SBTのインストール

sbtはこちらから取得できます。

1.1 Windows

まず、Setupのinstalling sbtのところからsbt-launch.jarをダウンロードして適当なフォルダに置いてください。
次にjarを保存したディレクトリに以下の内容を書いたsbt.batを作ります。

“` set SCRIPT_DIR=%~dp0 java -Xmx512M -jar ”%SCRIPT_DIR%sbt-launch.jar" %* “`

最後に、コントロールパネルの環境変数でPathにこのsbt.batとsbt-launch.jarをおいたディレクトリを通してください。
以上でSBTを使う準備は完了です。

LinuxとかMacとか

だいたい手順は一緒です。書くのがめんどくさいので本家Wikiを参照してください。

2. Hello SBT

2.1 起動

sbtを使うだけならば、コマンドプロンプトから適当なディレクトリで

sbt

とだけ入力してください。SBTの対話モードが起動します。

2.2 ソースコード

sbtのプロジェクトのディレクトリ構成は以下のようになります。

src/
  main/
    resources/
       <リソースファイル>
    scala/
       <scalaソースコード>
    java/
       <javaソースコード>
  test/
    resources
       <テストリソース>
    scala/
       <scalaテストコード>
    java/
       <javaテストコード>
project
       <sbtプロジェクトファイル>
target
       <コンパイルされたクラスファイルなどsbtが自動生成したファイルの出力先>

Mavenとソースコードのディレクトリ構成は同じです。

src/main/scalaに以下のようなhellosbt.scalaを入れておいてください。

object App{
  def main(args : Array[String]) {
    println("hello sbt!")
  }
}

2.3 コンパイルと実行

実行は次の手順で行えます。

> sbt
sbt> reload
 ...
sbt> update
  ...
  [success] ...
sbt> compile
  [success] ...
sbt> run
hello sbt!

これでさきほどのScalaプログラムが実行できました。簡単ですね。

3. SBTの概要

3.1 よく使うSBTコンソールコマンドの説明

先ほどHelloWorldで使用した4つのコマンドに加えて、よく使うコマンド5つを簡単に説明しておきます。とりあえずこれだけ覚えとけばsbtは使えます。

reload

sbtのプロジェクトファイルを再読み込みします。sbtはプロジェクトファイルの変更を監視していないので、依存関係の追加などの設定変更を行った場合、sbt自体を再起動するか、reloadコマンドを実行してやる再読み込みを行う必要があります。(今さっきのHello sbtではこのコマンドは不要でした。)

update

設定した依存関係を解決し、jarファイルをローカルのIvyレポジトリへダウンロードしてきます。依存関係に変更がない限り、1度だけ実行すれば大丈夫です。(これも先ほどのHello sbtでは不要でした)

compile

コンパイルをします。

test

src/test/以下のテストを実行します。

run

自動でmain関数を探して実行します。

console

依存関係のjarを全てパスに通した状態でscalaのREPLを起動します。

publish

プロジェクトのプログラムを、ローカルレポジトリや外部のレポジトリへ配置します。

clean

target内のファイルを削除し、まっさらな状態にリセットします。全てのソースを再コンパイルしたい場合などに使用します。

tasks

現在のsbt projectで使用可能なコマンドの一覧が表示されます。他の機能は暇な時にこれで表示させてみてください。

Mavenの違いとしては、mvn compileやmvn testでは自動で依存関係の更新まで行なってくれますが、sbtでは依存関係の更新は明示的にupdateを実行する必要があります。(そのかわり、testやcompileのときにいちいち依存関係に解決を行わないので早い)
あとは、sbtには簡単にコードを実行できるrunとconsoleコマンドがあります。とくにconsoleのほうは、ライブラリを試しに使って見るときに非常に重宝します。

3.2 pom.xmlに変わるもの

sbtでは、

  • Build.sbtによる、KeyValue型簡易設定方式
  • projectディレクトリに*.scalaを記述する詳細設定方式

の2種類のプロジェクト設定方法が用意されています。
今回は、後者の詳細設定方式を使いたいと思います。

プロジェクトの準備

1. SBTプロジェクトファイルの準備

今回は、JSONライブラリのGSONを使ってJSONのパースをするプログラムとそのテストを作りたいと思います。
まず、適当に空のディレクトリを作成しそこへ移動してください。

1.1 sbtプロジェクトファイルの用意

projectディレクトリを作成し、Build.scalaというファイルを作成し以下のコードを書いてください。

import sbt._
import Keys._

object HelloBuild extends Build {
  lazy val root = Project(id = "hello",
    base =  file("."),
    settings = Project.defaultSettings ++ Seq(
      version := "0.0.1-SNAPSHOT"
    )
  )

}

あとは、sbtを実行(すでに実行している場合は、reloadで再読み込み)を行なってエラーが起きないことを確認してください。
これが、最小構成のsbtのプロジェクトファイルになります。
sbtの思想を簡単に説明しておくと、sbtでは全ての設定がSettingKeyとValueのペアとなっています。この例では、

version := "0.0.1-SNAPSHOT"

の部分が設定に相当し、versionというSettingKeyに"0.0.1-SNAPSHOT"というValueを設定しています。その他の設定もこのようにKeyValueのペアとして表現されます。

2. 基本設定

2.1 groupIdやartifactId等の設定

mavenのgroupIdやartifactIdなどの基本情報の設定は以下のように対応します。
これ以外の使用可能なSettingKeyはsbt.Keysに定義されています。

Mavenでのタグ名 SettingKey
groupId organization
artifactId Project@id + ”_“ + scalaVersion
version version
description description
import sbt._
import Keys._

object HelloBuild extends Build {
  lazy val root = Project(id = "sbt-gson",
    base =  file("."),
    settings = Project.defaultSettings ++ Seq(
      version := "0.0.1-SNAPSHOT",
      organization := "com.geishatokyo",
      description := "this is gson program"
    )
  )
}

はpomファイルの

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.geishatokyo</groupId>
    <artifactId>sbt-gson_2.9.1</artifactId>
    <packaging>jar</packaging>
    <description>this is gson program</description>
    <version>0.0.1-SNAPSHOT</version>
    <name>sbt-gson</name>
    ...
</project>

と等価になります。なお、sbtコンソールで

make-pom

コマンドを使用するとpomファイルを生成してくれます。

2.2 scala version

sbtではコンパイルに使用するscalaのバージョンと、CrossVersionBuildの設定が簡単に行えます。
現在はデフォルトではscala2.9.1が使用されます。

SettingKeyは

scalaVersion String scalaVersion := "2.9.1”
crossScalaVersions Seq[String] crossScalaVersion := Seq(“2.7.7”,“2.8.1”,“2.9.0”,“2.9.0-1”,“2.9.1”)

crossScalaVersionsを設定しておくと、

+compile
+publish

と+を付けてコマンド実行すると設定したscala version全てでビルドや配置を行なってくれます。

今回はscala2.9.1とscala2.9.0でコンパイルします。
最終的なBuild.scalaは以下のようになります。

import sbt._
import Keys._

object HelloBuild extends Build {
  lazy val root = Project(id = "sbt-gson",
    base =  file("."),
    settings = Project.defaultSettings ++ Seq(
      version := "0.0.1-SNAPSHOT",
      organization := "com.geishatokyo",
      description := "this is gson program",
      scalaVersion := "2.9.1",
      crossScalaVersions := Seq("2.9.0-1","2.9.0")
    )
  )
}

依存関係解決

この項目ではおもに依存関係の解決方法の説明をします。
sbtは内部でApache Ivyを使用しています。
MavenCentralやScala-toolsなどのMavenRepositoryは問題なく使えますが、ローカルのMavenとの連携は多少設定が必要となります。

1. 依存関係の設定

依存関係は、libraryDependenciesのSettingKeyに設定することになります。
libraryDependenciesの受け取る型はSeq[ModuleID]となっており、依存するライブラリを全て列挙することとなります。

javaのライブラリの場合

mavenでは

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.1</version>
</dependency>

sbtでは

libraryDependencies += "com.google.code.gson" % "gson" % "2.1" % "compile"

のように書きます。
まず、演算子が := では無く、 +=になっていることに注意してください。
:= は代入となり、値を全て上書きしてしまいます。+=は新しい要素の追加になります。
ModuleIDはimplicit conversionによって変換されています。演算子には%を使います。書式は

"groupID" % "artifactID" % "version" [% "scope"]

です。scopeには、"compile",“test”,“provided"が使用可能です。またscopeは省略可能で、デフォルトでは"compile"とみなされます。

scalaライブラリの場合

scalaのライブラリは慣習的にartifactIDの末尾にscalaのバージョンが付与されます。

<dependency>
    <groupId>org.scalatest</groupId>
    <artifactId>scalatest_2.9.1</artifactId>
    <version>1.7.RC1</version>
</dependency>

このようなscalaライブラリの場合、sbtでは

libraryDependencies += "org.scalatest" %% "scalatest" % "1.7.RC1"

と設定します。注意する点は、groupIDのあとの演算子が % -> %%になっており、artifactIDにscalaVersionをくっつけていないという点です。
このように記述することで、sbtが依存関係の解決を行う際に自動でartifactIDに現在設定しているscalaのバージョンを付与してくれます。

2. 外部Repositoryの設定

デフォルトでMavenCentralRepositoryやScalaToolsが設定されています。
が場合によっては社内Repositoryや野良Repositoryを使いたくなる場合があると思います。
このようなRepositoryの設定はresolversのSettingKeyに追加します
設定方法

resolvers ++= Seq(
  "Company Repos" at "http://hoge.nexus.com",
  "Another Company Repos" at "http://another.company.com"
)

今回は複数のRepositoryを一括で登録して見ました。
代入演算子++=はSeq同士を結合する演算子になります。resolversはSeq[Resolver]なのでこの演算子が使用可能です。
また、今回もimplicit conversionが使用されておりReslverを明示的にインスタンス化する必要はありません。

3. MavenLocalRepositoryの設定

Mavenを使っている人はmvn installでローカルにいろいろなライブラリをインストールしている思います。
しかし、sbtはIvyを使用しているので、デフォルトではMavenのローカルレポジトリを参照してくれません。
でも安心して下さい。sbtには簡単にMavenLocalRepositoryを追加する手段が用意されています。

resolvers += Resolver.mavenLocal

を書き加えるだけでMavenLocalRepositoryの設定は完了します。

4. settings.xmlの置き換え

Mavenを使っているなら社内レポジトリなどの共通設定は当然settings.xmlに全て設定して無駄な手間をなくしていると思います。
sbtを使っていても同じ事を思いますよね?sbtにもsettings.xmlのようにグローバルな設定を記述する方法が用意されています。

ユーザーのホームディレクトリの下に.sbtディレクトリを作り、その中にglobal.sbtファイルを作成してください。

windows
C:\Users\{user name}\.sbt\global.sbt
Linuxなど
~/.sbt/global.sbt

global.sbt

resolvers += "Company Repository" at "http://mycompany.nexus.com/maven/snapshot/"

credentials += Credentials("Sonatype Nexus Repository Manager", "mycompany.nexus.com", {user name}, {password})

設定の方法はBuild.scalaとほぼ一緒です。ただし、Build.sbtと同様な簡易記法になります。そのため、クラスの定義は不要で、SettingKeyとValueだけを列挙すれば大丈夫です。
注意点は、1設定ごと1行開けないと正しく動きません。

credentialsは、認証の設定です。APIによると

Credencial.apply(realm : String,host : String, userName : String, password : String)

となっています。認証のかかっているサーバーごとに正しく設定しましょう。

現在のBuild.scalaはこのようになっています。
依存関係は、いくつも列挙することになると思うので別途定義しておいたほうが綺麗になります。

import sbt._
import Keys._

object HelloBuild extends Build {

  val gson = "com.google.code.gson" % "gson" % "2.1"
  val scalaTest = "org.scalatest" %% "scalatest" % "1.7.RC1" % "test"
  lazy val dependencies = Seq(
    gson,
    scalaTest
  )

  lazy val root = Project(id = "sbt-gson",
    base =  file("."),
    settings = Project.defaultSettings ++ Seq(
      version := "0.0.1-SNAPSHOT",
      organization := "com.geishatokyo",
      description := "this is gson program",
      scalaVersion := "2.9.1",
      crossScalaVersions := Seq("2.9.0-1","2.9.0"),
      libraryDependencies ++= dependencies,
      resolvers += Resolver.mavenLocal
    )
  )
}

Tags

Scala maven sbt