CLOVER🍀

That was when it all began.

sbtでJava用のプロジェクトを作る

JavaでもGradleの名前をよく聞く昨今ですが、個人的にはGradleよりもsbtの方をよく使っているため、こちらでできてもいいよねーということで。

あ、ちょっと興味があったので調べて書いていますが、どちらかといえばネタエントリに近いかも…。まあ、慣れという点もあるので、こちらで考えたいですけどねー。

では、いってみましょう。

とりあえず、普通にbuild.sbtとJavaソースコードを作成する

build.sbtを作成します。内容は、まあ普通に。依存関係には、スケープゴート的にCommons Lang3でいきましょう。
build.sbt

name := "sbt-simple-java-project"

version := "0.0.1-SNAPSHOT"

organization := "org.littlewings"

libraryDependencies ++= Seq(
  "org.apache.commons" % "commons-lang3" % "3.3.2"
)

Javaソースコードは、こんな感じで用意。
src/main/java/App.java

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

import org.apache.commons.lang3.StringUtils;

public class App {
    public static void main(String[] args) {
        System.out.println(join(Arrays.asList("Hello", "World")));
    }

    public static String join(Iterable<String> iterable) {
        return StringUtils.join(iterable, ", ");
    }
}

この状態で、普通にコンパイルして

> compile

実行できます。

> run
[info] Running App 
Hello, World

このままでも、普通に使えそうですね?

プロジェクトから、Scalaへの依存関係を取り除く

上記のように、最低限の設定でも普通に使えそうですが、packageしてみるとJARファイルの名前に妙なものがくっついてきます。

> package
[info] Updating {file:/xxxxx/sbt-simple-java-project/}sbt-simple-java-project...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Java source to /xxxxx/sbt-simple-java-project/target/scala-2.10/classes...
[info] Packaging /xxxxx/sbt-simple-java-project/target/scala-2.10/sbt-simple-java-project_2.10-0.0.1-SNAPSHOT.jar ...

これですね。

sbt-simple-java-project_2.10-0.0.1-SNAPSHOT.jar

つまり、Scalaバージョンが付いています。

さらに言うと、Scalaへの依存関係までくっついています。
*sbt-dependency-graph(https://github.com/jrudolph/sbt-dependency-graph)使用

> dependencyTree
[info] Updating {file:/xxxxx/sbt-simple-java-project/}sbt-simple-java-project...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] org.littlewings:sbt-simple-java-project_2.10:0.0.1-SNAPSHOT [S]
[info]   +-org.apache.commons:commons-lang3:3.3.2

[S]が付いているのは、Scalaへの依存関係があることを表しています。

まず、できあがるJARファイルからScalaバージョンを除くには、以下をbuild.sbtに加えます。

crossPaths := false

依存関係にScalaが追加されないようにするには、以下を加えます。

autoScalaLibrary := false

ここまでで、build.sbtはこうなりました。sbtコンソールを起動している場合は、reloadしてください。

name := "sbt-simple-java-project"

version := "0.0.1-SNAPSHOT"

organization := "org.littlewings"

autoScalaLibrary := false

crossPaths := false

libraryDependencies ++= Seq(
  "org.apache.commons" % "commons-lang3" % "3.3.2"
)

Javaソースコードコンパイルオプションを指定する

特に何もしていせずとも実行環境のJavaバージョンで動くようですが、明示的に指定する場合は以下の様に設定します。

javacOptions ++= Seq("-source", "1.8", "-target", "1.8")

JUnitテストケースを実行する

作成したソースコードの、テストを用意することもやってみましょう。build.sbtの依存関係を、以下の様に修正します。

libraryDependencies ++= Seq(
  "org.apache.commons" % "commons-lang3" % "3.3.2",
  "junit" % "junit" % "4.11" % "test"
)

で、reload。

テストコードも、以下の様に用意してみます。
src/test/java/AppTest.java

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.util.Arrays;

import org.junit.Test;

public class AppTest {
    @Test
    public void testJoin() {
        assertThat(App.join(Arrays.asList("Hello", "World")), is("Hello, World"));
    }
}

では、テスト実行。

> test
[info] Updating {file:/xxxxx/sbt-simple-java-project/}sbt-simple-java-project...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Java source to /xxxxx/sbt-simple-java-project/target/classes...
[info] Compiling 1 Java source to /xxxxx/sbt-simple-java-project/target/test-classes...
[info] Passed: Total 0, Failed 0, Errors 0, Passed 0
[info] No tests to run for test:test

テストがないと言われます。

[info] No tests to run for test:test

sbtでJUnitのテストケースを実行するためには、もうちょっと設定が必要です。

依存関係に、以下を追加します。

libraryDependencies ++= Seq(
  "org.apache.commons" % "commons-lang3" % "3.3.2",
  "junit" % "junit" % "4.11" % "test",
  "com.novocode" % "junit-interface" % "0.11-RC1" % "test"
)

追加したのは、ここですね。

  "com.novocode" % "junit-interface" % "0.11-RC1" % "test"

sbtでJUnitテストケースを実行するには、junit-interfaceが必要になります。

junit-interface
https://github.com/sbt/junit-interface

reloadして、テスト実行。

> test
[info] Updating {file:/xxxxx/sbt-simple-java-project/}sbt-simple-java-project...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Java source to /xxxxx/sbt-simple-java-project/target/classes...
[info] Compiling 1 Java source to /xxxxx/sbt-simple-java-project/target/test-classes...
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1

テストが実行されました。

ここまでで、build.sbtの設定はこんな感じになっています。

name := "sbt-simple-java-project"

version := "0.0.1-SNAPSHOT"

organization := "org.littlewings"

javacOptions ++= Seq("-source", "1.8", "-target", "1.8")

autoScalaLibrary := false

crossPaths := false

libraryDependencies ++= Seq(
  "org.apache.commons" % "commons-lang3" % "3.3.2",
  "junit" % "junit" % "4.11" % "test",
  "com.novocode" % "junit-interface" % "0.11-RC1" % "test"
)

まあ、あんまり使うことはないかもしれませんが、簡単に使う分にはいいかも?

参考)
Beginning SBT - A Simple Java-Only Project with Eclipse
http://blog.jiffle.net/post/32451176640/beginning-sbt-a-simple-java-only-project-with-eclipse

Basic Definition Examples
http://www.scala-sbt.org/release/docs/Examples/Quick-Configuration-Examples.html

番外編 テストでのみScalaを使用する

通常はJavaで書きたいけれど、テストコードのみでScalaを使用したい場合は、依存関係に以下を加えます。

"org.scala-lang" % "scala-library" % scalaVersion.value % "test"

この時は、Scalaバージョンを指定した方がいい気がします。

scalaVersion := "2.11.0"

つまり、こんな感じになります。

name := "sbt-simple-java-project"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.11.0"

organization := "org.littlewings"

javacOptions ++= Seq("-source", "1.8", "-target", "1.8")

autoScalaLibrary := false

crossPaths := false

libraryDependencies ++= Seq(
  "org.apache.commons" % "commons-lang3" % "3.3.2",
  "junit" % "junit" % "4.11" % "test",
  "com.novocode" % "junit-interface" % "0.11-RC1" % "test",
  "org.scala-lang" % "scala-library" % scalaVersion.value % "test"
)

参考)
Configuring Scala
http://www.scala-sbt.org/0.13.2/docs/Detailed-Topics/Configuring-Scala.html