CLOVER🍀

That was when it all began.

ScalaでGroovyのGrapeのようなことがやりたい

GroovyのGrapeって、便利ですよね。個人的には、Groovyを使う大きな理由のひとつだったりします。

だって、お手軽だもん。

@Grab('org.apache.commons:commons-lang3:3.1')
import org.apache.commons.lang3.StringUtils

スクリプトに、これを書くだけで依存関係を解決してくれます。ちょっとしたスクリプトを作る時に、すごく重宝しますよ。

んで、これがScalaでもできないだろうかと思って、ちょっと調べたのですが…sbtがそんな機能を一応持っているようです。

Scripts, REPL, and Dependencies
http://www.scala-sbt.org/release/docs/Detailed-Topics/Scripts

ScalaスクリプトとREPLで、依存関係の解決機能が使えるようになるということで。

conscriptを使ってインストール、みたいなことが書かれていますが、
https://github.com/n8han/conscript
今回は手動でインストールすることにしました。

用意としては、scalasとscreplという2つのスクリプトを作成します。

こちらのページでは、カレントディレクトリにsbt-launch.jarがあり、そこでスクリプトを作成するような手順になっています。
http://www.scala-sbt.org/release/docs/Detailed-Topics/Scripts

なんですけど、自分の趣向から、ここは「/usr/loca/sbt」ディレクトリに作成することにしました。つまり、こんな感じです。

$ ll /usr/local/sbt
合計 1100
drwxr-xr-x  2 root root    4096 Apr 25 23:16 ./
drwxr-xr-x 29 root root    4096 Apr 14 16:03 ../
-rwxr-xr-x  1 root root     294 Apr 25 23:13 sbt*
-rw-r--r--  1 root root 1105722 Apr  6 00:59 sbt-launch.jar
-rwxr-xr-x  1 root root     122 Apr 25 23:15 scalas*
-rwxr-xr-x  1 root root     123 Apr 25 23:16 screpl*

scalasがScalaスクリプト用で、中身はこんな感じ。

#!/bin/sh
java -Dsbt.main.class=sbt.ScriptMain -Dsbt.boot.directory=$HOME/.sbt/boot -jar `dirname $0`/sbt-launch.jar "$@"

screplはREPL用のスクリプトです。

#!/bin/sh
java -Dsbt.main.class=sbt.ConsoleMain -Dsbt.boot.directory=$HOME/.sbt/boot -jar `dirname $0`/sbt-launch.jar "$@"

共に、実行権限を付与しておきます。

$ sudo chmod a+x scalas
$ sudo chmod a+x screpl

では、使ってみます。

scalas

今回も、相変わらずスケープゴートはCommons Lang。

まずは、Scalaスクリプトを用意します。
script.scala

#!/usr/bin/env scalas

/***
scalaVersion := "2.10.1"

libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.1"
*/

import org.apache.commons.lang3.StringUtils

println(StringUtils.replace("Hello %TOKEN%",
                            "%TOKEN%",
                            args.toList.headOption.getOrElse("World")))

ポイントは

#!/usr/bin/env scalas

/***
scalaVersion := "2.10.1"

libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.1"
*/

ですね。「/***」で始まるコメントの中に、sbtの定義を書くみたいです。

そして、Scalaスクリプトに実行権限を与えます。

$ chmod a+x script.scala

では、実行。

$ ./script.scala 
[info] Set current project to default-2ce05f (in build file:/xxxxx/.sbt/boot/90e274b044364a6d7400/)
Hello World

動きました!

引数があっても大丈夫です。

$ ./script.scala Scala!
[info] Set current project to default-2ce05f (in build file:/xxxxx/90e274b044364a6d7400/)
Hello Scala!

ちなみに、

[info] Set current project to default-2ce05f (in build file:/xxxxx/90e274b044364a6d7400/)

と出力されているように、scalasスクリプトで指定した

-Dsbt.boot.directory=$HOME/.sbt/boot

がプロジェクトのディレクトリになっているようなので、あしからず。

screpl

続いて、REPLで。

screplの後に、引数として依存関係の定義を与えます。

$ screpl "org.apache.commons % commons-lang3 % 3.1"

すると、sbtの表示が表れつつREPLが起動します。

$ screpl "org.apache.commons % commons-lang3 % 3.1"
[info] Set current project to default-f7dc2e (in build file:/xxxx/.sbt/boot/ivy-console/)
Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_21).
Type in expressions to have them evaluated.
Type :help for more information.

あとは、定義した依存関係からimportすれば使えます。

scala> import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.StringUtils

scala> StringUtils.isEmpty(null)
res0: Boolean = true

この時、プロジェクトのディレクトリは

[info] Set current project to default-f7dc2e (in build file:/xxxx/.sbt/boot/ivy-console/)

となっています。

また、Scalaバージョンは2.9.2みたいです。

Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_21).

たぶんこれ、sbtが使っているScalaバージョンですよね、微妙…。

そして、調べたはいいんですけど、やっぱりScala+sbtだと起動が重すぎてGroovy+Grapeほどの快適さは残念ながらないなぁと改めて思った次第です…。

やっぱり、こういう使い方をする時はGroovyを選ぶかな?