モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)
目次目次を開く/閉じる

酢豚の基本的な使い方 (sbt)

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2016/01/25
最終更新最終更新
2018/06/30
記事区分記事区分
一般公開

目次

    アカウント プロフィール画像 (サイドバー)

    Software Engineer @ Tokyo

    0
    ステッカーを贈るとは?

    sbt は Scala および Java を主な対象としたビルドツールです。Scala Build Tool の略ではありませんが、Simple Build Tool という明示的な記述も公式ドキュメントなどには見当りません。以下 sbt の基本的な使用例をまとめます。使用した sbt のバージョンは 0.13 です。

    公式ドキュメント

    インストール方法

    sbt の実体は jar ファイルです。OSX, Windows, Linux 等、JVM (1.6 以上) が動作する環境であればどのプラットフォームでも動作します。Macports, Homebrew, msi インストーラ, yum RPM などが提供されていますが、結局のところ、シェルスクリプトまたはバッチファイルで JAR を実行しているだけです。CentOS で手動インストールする例を記載します。

    JVM が必要なため、JDK または JRE をインストールします。

    sudo yum install java-1.8.0-openjdk-devel
    

    sbt JAR をダウンロードして適当なディレクトリに保存します。

    wget https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.13.9/sbt-launch.jar
    mv sbt-launch.jar ~/bin/
    

    JAR を実行するシェルスクリプトを用意します。

    ~/bin/sbt

    #!/bin/bash
    SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxMetaspaceSize=256M"
    java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"
    

    パーミッションを設定します。

    chmod u+x ~/bin/sbt
    

    サンプルコードのビルド

    mkdir hello
    cd hello/
    vi hw.scala
    

    hw.scala

    object Hi {
      def main(args: Array[String]) = println("Hi!")
    }
    

    ビルド

    sbt
    ...
    > run
    > exit
    

    初回実行時は非常に時間がかかります。~/.sbt/ および ~/.ivy2/ に Scala の JAR 等、sbt の動作に必要な JAR をダウンロードするためです。

    ディレクトリ構成

    先程のサンプルコード hw.scala のように sbt コマンドはプロジェクトのベースディレクトリのファイルもビルドできます。しかしながら、ファイル分割された一般的なプロジェクトの場合は以下のようなディレクトリ構成でファイルを整理します。これは Maven と同じ構成です。

    ベースディレクトリ/
      .gitignore
      build.sbt
      project/
        build.properties
        <その他の設定ファイル .scala または .sbt>
        target/
          <コンパイル結果 class ファイルなど>
      lib/
        <ライブラリ JAR ファイル>
      src/
        main/
          resources/
            <jar に含めたいファイル>
          scala/
            <Scala ソースコード>
          java/
            <Java ソースコード>
        test/
          resources
            <test jar に含めたいファイル>
          scala/
            <Scala テストコード>
          java/
            <Java テストコード>
      target/
        <成果物 JAR など>
    

    .gitignore を利用する場合は target/ を記載します。/target/ でも target でもなく target/ です。target ディレクトリは様々な階層に生成されます。

    バージョンの固定

    設定ファイルに Scala および sbt のバージョンを記載することで、例えば複数人で開発する場合でも同じ開発環境が構築できます。バージョンの差異によって生じるバグを回避できます。

    Scala バージョンの固定

    scalaVersion を指定することで Scala バージョンを固定できます。初回ビルド時は自動で該当の Scala JAR をダウンロードして ~/.ivy2/ に保存します。無指定の場合は sbt が動作のために利用している Scala バージョンが利用されます。

    build.sbt

    lazy val root = (project in file(".")).
      settings(
        name := "my-scala-app",
        version := "1.0",
        scalaVersion := "2.11.7"
      )
    

    sbt バージョンの固定

    sbt バージョンによる差異が基本的にありませんが、sbt のバージョンも固定しておくことはよいことです。

    project/build.properties

    sbt.version=0.13.9
    

    よく使用するコマンド

    インタラクティブモード

    $ sbt
    > run
    

    バッチモード

    $ sbt run
    

    ファイルの変化を検知して自動コンパイル

    $ sbt
    > ~ compile
    

    または

    $ sbt "~ compile"
    

    target に生成されたファイルを削除

    $ sbt clean
    

    コンパイルしてテストを実行

    $ sbt test
    

    Scala インタープリタを起動

    $ sbt console
    

    Main クラスを実行

    $ sbt run
    

    JAR 成果物を生成

    $ sbt package
    

    jar コマンドで成果物の中身を確認してみましょう。

    $ jar tf target/scala-2.11/hello_2.11-1.0.jar
    META-INF/MANIFEST.MF
    Hi$.class
    Hi.class
    

    src/main/scalasrc/main/java をコンパイルしたクラスファイルおよび src/main/resources が格納されています。

    ヘルプ

    $ sbt help
    $ sbt "help package"
    

    設定の再読み込み

    build.sbt, project/*.scala, project/*.sbt ファイルを編集した場合は reload を実行して再読み込みする必要があります。

    $ sbt
    > reload
    

    ライブラリの使用方法

    sbt でライブラリを利用する方法は二種類あります。

    • Unmanaged dependencies
      • lib ディレクトリに jar ファイルを置くことで利用
    • Managed dependencies
      • build.sbt に設定を記載することでインターネットから jar ファイルをダウンロードして利用
      • Maven の pom.xml に記載する plugin と同等の概念

    Unmanaged dependencies 設定例

    SWT で GUI ツールを作成できます。

    build.sbt

    lazy val root = (project in file(".")).
      settings(
        name := "hello",
        version := "1.0",
        scalaVersion := "2.11.7",
        mainClass in assembly := Some("com.mycompany.app.Hi")
      )
    

    .gitignore

    target/
    

    lib/swt.jar

    <動作させる予定の OS 用にダウンロードしたもの>
    

    project/build.properties

    sbt.version=0.13.9
    

    src/main/scala/hw.scala

    package com.mycompany.app
    
    import org.eclipse.swt.SWT
    import org.eclipse.swt.layout.RowLayout
    import org.eclipse.swt.widgets.Button
    import org.eclipse.swt.widgets.Display
    import org.eclipse.swt.widgets.Shell
    import org.eclipse.swt.widgets.Text
    
    object Hi {
      def main(args: Array[String]) {
    
        val display = new Display()
    
        val shell = new Shell(display)
        shell.setText("SWT アプリケーション")
        shell.setLayout(new RowLayout())
    
        val button = new Button(shell, SWT.NULL)
        button.setText("押してください")
    
        shell.open()
    
        while(!shell.isDisposed()) {
          if(!display.readAndDispatch()) {
            display.sleep()
          }
        }
        display.dispose()
    
      }
    }
    

    sbt-assembly プラグインを利用すると Scala JAR や lib 以下の JAR をすべて含めた全部入りの fat JAR を生成できます。Maven の Apache Maven Assembly Plugin や Apache Maven Shade Plugin のようなものです。

    project/assembly.sbt

    addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.1")
    

    ビルド

    $ sbt
    > assembly
    

    実行例 (OSX で SWT アプリケーションを実行する場合は XstartOnFirstThread が必要です)

    $ java -XstartOnFirstThread -jar ./target/scala-2.11/hello-assembly-1.0.jar
    

    Managed dependencies 設定例

    Java のロガー Logback を利用してみます。

    build.sbt

    lazy val root = (project in file(".")).
      settings(
        name := "hello",
        version := "1.0",
        scalaVersion := "2.11.7",
        libraryDependencies ++= Seq(
          "ch.qos.logback" % "logback-classic" % "1.1.3",
          "org.slf4j" % "slf4j-api" % "1.7.12"
        )
      )
    

    .gitignore

    target/
    /logs/
    

    project/build.properties

    sbt.version=0.13.9
    

    src/main/scala/hw.scala

    import org.slf4j.Logger
    import org.slf4j.LoggerFactory
    
    object Hi {
      def main(args: Array[String]) {
        val logger = LoggerFactory.getLogger("Hi")
        logger.info("info: {}", 1)
        logger.warn("warn: {}", 2)
        logger.error("error: {}", 3)
      }
    }
    

    src/main/resources/logback.xml

    <configuration>
      <property name="LOG_DIR" value="./logs" />
      <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/myapp.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!-- daily rollover  -->
          <fileNamePattern>${LOG_DIR}/myapp.%d{yyyy-MM-dd}.log</fileNamePattern>
          <!-- keep 30 days' worth of history -->
          <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
          <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        </encoder>
      </appender>
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
          <pattern>%msg%n</pattern>
        </encoder>
      </appender>
      <root level="info">
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
      </root>
    </configuration>
    

    ビルド

    $ sbt
    > run
    [info] Running Hi
    info: 1
    warn: 2
    error: 3
    [success] Total time: 2 s, completed 2016/01/26 15:48:23
    

    ログが生成されました。

    $ cat logs/myapp.log
    2016-01-26 15:47:08,646 INFO [run-main-0] Hi [hw.scala:7] info: 1
    2016-01-26 15:47:08,662 WARN [run-main-0] Hi [hw.scala:8] warn: 2
    2016-01-26 15:47:08,662 ERROR [run-main-0] Hi [hw.scala:9] error: 3
    

    JAR ファイルをキャッシュ保存

    bundler の package のようなオプションが sbt にもあります。ネットワーク上にある JAR ファイルの消失に備えてプロジェクト内に保存しておきたい場合に有効化します。

    lazy val root = (project in file(".")).
      settings(
        ...
    
        // Copy all managed dependencies to <build-root>/lib_managed/
        //   This is essentially a project-local cache and is different
        //   from the lib_managed/ in sbt 0.7.x.  There is only one
        //   lib_managed/ in the build root (not per-project).
        retrieveManaged := true,
    
        ...
      )
    

    複数プロジェクトを管理

    複数の関連プロジェクトを同じ sbt で管理することができます。

    lazy val commonSettings = Seq(
        scalaVersion := "2.11.7",
        retrieveManaged := true,
        libraryDependencies ++= Seq(
        )
    )
    
    lazy val root = (project in file(".")).
      aggregate(module1, module2).
      dependsOn(module1, module2).
      settings(commonSettings: _*).
      settings(
        libraryDependencies ++= Seq(
        )
      )
    
    lazy val module1 = (project in file("module1")).
      settings(commonSettings: _*).
      settings(
        name := "myapp-module2",
        version := "1.0",
        mainClass in assembly := Some("myapp.module1.Main"),
        libraryDependencies ++= Seq(
        )
      )
    
    lazy val module2 = (project in file("module2")).
      settings(commonSettings: _*).
      settings(
        name := "myapp-module2",
        version := "1.0",
        mainClass in assembly := Some("myapp.module2.Main"),
        libraryDependencies ++= Seq(
        )
      )
    
    • dependsOn(module1, module2) によって root プロジェクトで module1, module2 のクラスを import して利用可能
    • aggregate(module1, module2) によって root プロジェクトで compile などを実行すると module1, module2 でも compile が実行される

    ソースファイルなどはそれぞれの階層で管理します。

    • root: /src/*
    • module1: /module1/src/*
    • module2: /module2/src/*

    コンソールでのプロジェクトの切り替え

    $ sbt
    > projects
    [info] In file:/paty/to/myapp/
    [info]    module1
    [info]    module2
    [info]  * root
    > project module1
    [info] Set current project to myapp-module1 (in build file:/paty/to/myapp/)
    > projects
    [info] In file:/paty/to/myapp/
    [info]  * module1
    [info]    module2
    [info]    root
    

    現在のプロジェクトでタスクを実行

    > run
    

    プロジェクトを指定してタスクを実行

    > root/run
    > module1/run
    > module2/run
    

    sbt-assembly 時の Merge エラー対応

    上記 assembly タスクを実行すると、依存 JAR などがすべて JARに Merge されてパッケージングされます。その際、重複した名前のファイルが複数の JAR に含まれていると Merge 時にコンフリクトします。

    [error] (*:assembly) deduplicate: different file contents found in the following:
    

    コンフリクトを解決するための規則を予め build.sbt に記載しておくことでこれを回避できます。

    lazy val root = (project in file(".")).
      settings(
        name := "my-scala-app",
        version := "1.0",
        scalaVersion := "2.11.7",
        assemblyMergeStrategy in assembly := {
          case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first
          case PathList("path", "to", "file.txt") => MergeStrategy.discard
          case "unwanted.txt" => MergeStrategy.discard
          case x =>
            val oldStrategy = (assemblyMergeStrategy in assembly).value
            oldStrategy(x)
        }
      )
    

    上記設定では以下のようにコンフリクトを解決します。パターンマッチを利用して Path 毎に設定します。

    • /javax/servlet/* 最初に出現したものを JAR に含める
    • /path/to/file.txt JAR に含めない
    • /unwanted.txt JAR に含めない
    • その他は既定 (コンフリクト時にはエラー)

    その他

    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    Software Engineer @ Tokyo

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事

      • Scala 文字列の処理
        書式指定 object Main { def main(args: Array[String]): Unit = { println("%d + %d = %d".format(1, 1, 2)) //=> 1 + 1 = 2 } } 文字列の比較 ヒアドキュメント 他の言語でいう「ヒアドキュメント」のようなものは """ で囲うことで実現できます。 object Main ...
        したくんしたくん6/18/2018に更新
        いいねアイコン画像0
      • Scala 日付に関する処理
        Date クラスを文字列にフォーマット import java.util.Date object Main { def main(args: Array[String]): Unit = { // format は Date に限らない文字列用の機能です。 println("%d-%d-%d" format (1, 1, 1)) //=> 1-1-1 printl...
        したくんしたくん6/5/2018に更新
        いいねアイコン画像0
      • Scala 関数のサンプルコード
        「デフォルト引数」および「Unit 型を返す関数」 object HelloWorld { def main(args: Array[String]): Unit = { def myPrint(myArg: String = "default_value") = println(myArg + "!") val result = myPrint() //=> defau...
        したくんしたくん6/26/2018に更新
        いいねアイコン画像0
      • Scala 組み込みの制御構造
        if-else 条件分岐で知られる if-else は三項演算子のようにも使用されます。 object HelloWorld { def main(args: Array[String]): Unit = { val myVal = if (!args.isEmpty) args(0) else "default" println(myVal) } ...
        したくんしたくん10/7/2021に更新
        いいねアイコン画像0
      • Scala の基本文法
        Scala は JVM 上で動作するバイトコードにコンパイルできる言語です。JAVA よりも柔軟な記述ができます。事前にこちらからダウンロードおよびインストールしておいてください。基本的な文法をまとめます。 変数および定数 object HelloWorld { def main(args: Array[String]): Unit = { val constVal = 1 //
        したくんしたくん10/7/2021に更新
        いいねアイコン画像0