Введение в Scala.

С нуля до распределенных приложений.

Сборка проекта. Simple Build Tool

Одна из основных систем сборки (компиляции, тестирования и запуска) проектов для Scala - SBT (Simple Build Tool).

Здесь будут изложены основы создания sbt-проекта.

Минимальный проект

Минимальный рабочий sbt-проект может состоять из единственного scala файла:

HelloWorld.scala

object HelloWorld extends App {
  println("Hello, world from SBT!")
}

Запустим ее командой sbt run:

$ sbt run
...
Hello, world from SBT!
[success] Total time: 3 s, completed Jul 6, 2015 1:26:52 AM

В данном случае sbt находит все необходимое исключительно по соглашениям.

Соглашения по умолчанию

SBT находит следующие элементы автоматически:

  • Файлы исходного кода в корневом каталоге
  • Файлы исходного кода в src/main/scala или src/main/java
  • Тесты в src/test/scala или src/test/java
  • Файлы данных в src/main/resources или src/test/resources
  • jar-библиотеки в lib

Структура папок

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

Поэтому sbt автоматически поддерживает следующие соглашения для структуры подпапок:

src/
  main/
    resources/
      <файлы для добавления в основной jar файл>
    java/
      <исходный код на языке java>
    scala/
      <исходный код на языке scala>
  test/
    resources/
      <файлы для добавления в тестовый jar файл>
    java/
      <тесты на языке java>
    scala/
      <тесты языке scala>
lib/
  <неуправляемые зависимости (jar-файлы)>

build.sbt

Большая часть проектов нуждается в генерации jar-файлов и зависимостях от других проектов или скомпилированных библиотек. Для этой цели в sbt используется файл build.sbt помещенный в корневую папку проекта.

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

Для генерации jar-файла необходимо как минимум наличие в нем следующих двух настроек:

name := "My great project"

version := "1.0-SNAPSHOT"

Зависимости от внешних библиотек

Бум JVM как платформы для проектов с открытым кодом обусловлено не в последнюю очередь обменом готовыми библиотеками кода. Как в виде jar-файлов, так и адресов в общих онлайн репозиториях (в .NET аналогичный механизм появился совсем недавно и называется NuGet).

Так назывемая неуправляемая зависимость (unmanaged dependency) от библиотеки создается помещением jar-файла в папку lib в корне проекта.

Управляемая зависимость (managed dependency) от онлайн библиотеки объявляется следующей строкой в build.sbt:

libraryDependencies += <groupID> % <artifactID> % <revision> [% <configuration>]

У зависимости есть 4 компоненты:

  • groupID - название группы
  • artifactID - название артефакта, обычно с добавлением версии scala
  • revision - версия библиотеки
  • configuration - опционально указание зависимости для конфигурации, например библиотеки необходимые для тестирования указывают здесь test.

Несколько зависимостей можно так же добавлять одновременно.

Примеры:

libraryDependencies += "com.typesafe.play" %% "play-json" % "2.4.0",

libraryDependencies ++= Seq(
  "org.scalatest" % "scalatest_2.11" % "2.2.4" % "test",
  "net.databinder.dispatch" %% "dispatch-core" % "0.11.3" // %% вместо dispatch-core_2.11
)

Синтаксис %% означает автоматическое дописывание к artifactID версии Scala для выбора артифакта скомпилированного под указанную версию.

Многопроектные сборки

Зачастую удобно собирать одновременно несколько зависимых друг от друга проектов.

Каждый подпроект может иметь свой build.sbt файл, генерировать свой jar-файл и в остальном функционировать как самостоятельный проект.

Проект указывается объявлений lazy val типа Project. Например:

lazy val util = project

lazy val core = project

Имя переменной используется как идентификатор проекта и указание его базовой папки. ID проекта используется для выбора его в командной строке, а папка может быть изменена следующим образом:

lazy val util = project in file("other-utils")

lazy val core = project

Зависимости между проектами указываются командой dependsOn:

lazy val util = project in file("other-utils")

lazy val core = project.dependsOn(util)

lazy val portal = project.dependsOn(core, util)

Теперь код в проекте core может использовать классы и объекты из проекта util, а portal из их обоих.

Навигация по проектам

В командной строке вызываемой командой sbt в корневой папке проекта можно посмотреть список всех проектов в решении командой projects:

$ sbt
[info] Loading global plugins from /Users/tmagomedov/.sbt/0.13/plugins
[info] Set current project to fotm
> projects
[info] In file:/Users/tmagomedov/workspace/playground/fotm-info/
[info]     bnetapi
[info]     core
[info]     crawler
[info]   * fotm-info
[info]     util

Переход в нужный проект выполняется командой project <имя проекта>:

> project core
[info] Set current project to core

Откуда потом можно запустить необходимый объект реализующий App командой run-main <полное имя объекта>:

> run-main info.fotm.clustering.ClusteringEvaluator
...
[info] Running info.fotm.clustering.ClusteringEvaluator

Справочная информация

Гораздо больше подробной информации об остальных возможностях sbt можно найти на http://www.scala-sbt.org/0.13/tutorial/index.html