Научился собирать код на Scala с помощью SBT

13 марта 2013

В этой заметке мы познакомися с утилитой sbt. Мы научимся собирать с ее помощью проекты на Scala, устанавливать сторонние библиотеки, а также узнаем, как собрать jar, который будет работать на любой машине, где есть Java, без установки каких-либо зависимостей. Напоминаю, что установка sbt была рассмотрена в посте, опубликованном в этот понедельник.

Важно! В мире Scala все меняется очень быстро и сия заметка уже слегка устарела. На данный момент рекомендуется использовать другой механизм подключения плагинов к sbt. См исходники к посту Работа с Excel-файлами в Scala.

Перейдем в новый каталог и создадим в нем файл build.sbt:

name := "twitsearch"

version := "0.1"

scalaVersion := "2.9.2"

Обратите внимание, что пустые строки значимы. Если о них забыть, sbt будет ругаться непонятными сообщениями об ошибках.

В каталог src/main/scala/me/eax/twitsearch кладем файл twitsearch.scala из предыдущей заметки. Несмотря на то, что sbt с тем же успехом скомпилирует программу, если положить twitsearch.scala в одном каталоге с build.sbt, лучше сразу приучать себя делать правильно. В реальном проекте вы же не станете складывать сотни файлов с расширением scala в одном каталоге, правда?

Теперь программу можно собрать с помощью sbt:

sbt compile

Чтобы запустить программу, воспользуйтесь командой:

sbt run

Если программе требуется передать какие-то аргументы, что как раз есть наш случай, возьмите их в кавычки вместе с «run»:

sbt "run #scala"

Если проект состоит из нескольких программ, вы можете выбрать, какую из них запускать, воспользовавшись командой run-main:

sbt "run-main ClassName arg1 arg2"

Часто требуется запустить REPL в контексте текущего проекта:

sbt console

Также sbt поддерживает непрерывную компиляцию. Это когда программа компилируется сразу при обнаружении изменений в ее исходном коде. На первый взгляд это может показаться не самой полезной функцией, но на практике она существенно ускоряет разработку:

sbt "~ ; compile ; test"

Теперь попробуем собрать standalone jar. В этом нам поможет плагин для sbt под названием sbt-assembly.

Создаем файл project/project/build.scala следующего содержания:

import sbt._

object Plugins extends Build {
  lazy val root = Project("root", file(".")) dependsOn(
         uri("git://github.com/sbt/sbt-assembly.git#0.8.6")
       )
}

В начало build.sbt дописываем:

import AssemblyKeys._

assemblySettings

Теперь говорим:

sbt assembly

Если все было сделано правильно, в каталоге target появится файл с именем twitsearch-assembly-0.1.jar. Можно проверить его работоспособность, выполнив команду:

java -jar target/twitsearch-assembly-0.1.jar '#scala'

Самое прекрасное, что в этот файл включаются все сторонние библиотеки и тп. То есть, для работы ему не нужно ничего, кроме обычной JVM. Конечно, если только в вашем проекте не используется Java Native Interface.

Кстати, о птичках. Давайте попробуем использовать в проекте какую-нибудь стороннюю библиотеку, например json4s. Для этого дописываем в конец build.sbt строчку:

libraryDependencies += "org.json4s" %% "json4s-native" % "3.1.0"

Командой sbt console запускаем REPL и смотрим на библиотеку в действии:

scala> import org.json4s._
import org.json4s._

scala> import org.json4s.native.JsonMethods._
import org.json4s.native.JsonMethods._

scala> parse("{\"xxx\":123}")
res4: org.json4s.package.JValue = JObject(List((xxx,JInt(123))))

Благодаря json4s, а также дополнению @ya_kostya к предыдущей заметке, мы можем переписать наш код следующим образом:

import scala.io._
import java.net._
import org.json4s._
import org.json4s.native.JsonMethods._

object Application extends App {
  if(args.isEmpty) {
    println("Usage: twitsearch <query>")
    exit(1)
  }
  val query = URLEncoder.encode(args(0), "UTF-8")
  val url = "http://search.twitter.com/search.json?q=" + query
  val json = parse(Source.fromURL(url).mkString)
  (json \ "results" \ "text" \\ classOf[JString]).foreach(println)
}

Смотрите, как здорово! Никаких type erasure, уродливых asInstanceOf и даже вложенных сопоставлений с образцом.

Осталось только решить маленькую проблему. Если вы сидите под Ubuntu и ваш домашний каталог зашифрован, то во время сборки standalone jar вы получите ошибку:

[error] (*:assembly) Error extracting zip entry 'очень-длинное-имя-файла.class (File name too long)

Но это решается довольно просто:

rm -rf ./target
mkdir /tmp/`echo $$`
ln -s /tmp/`echo $$` ./target

Вот так легко и непринужденно пишутся программы на Scala. Все исходники к этой заметке вы найдете в этом репозитории.

Дополнение: Правда, у json4s есть такой недостаток, что он добавляет к размеру standalone jar около 13 мегабайт. Возможно, кто-нибудь знает более легкую библиотеку?

Дополнение: Как отправить электронное письмо в Scala

Метки: , .

Понравился пост? Узнайте, как можно поддержать развитие этого блога.

Также подпишитесь на RSS, Facebook, ВКонтакте, Twitter или Telegram.