Меню:


В данной заметке рассматривается использование утилиты Leiningen (версии 1.x) для сборки проектов, написанных на Clojure.5

Что это такое?

Leiningen — утилита для сборки кода написанного на Clojure. Leiningen намного проще по сравнению с Maven и позволяет описывать конфигурацию проекта и его зависимости используя сам язык Clojure1. Leiningen использует внешние утилиты и библиотеки для работы с зависимостями и сборки кода, поэтому он имеет достаточно небольшой размер исходного кода. В последнее время эта система становится все более популярной — она имеет возможности расширения с помощью дополнительных модулей, например, для компиляции кода на Java и т.п.

Из коробки Leiningen позволяет выполнять базовые задачи — компиляцию кода, тестирование, упаковку кода в jar-архив, сборку jar-архива со всеми зависимостями и т.д. Кроме того, имеется базовая поддержка работы с Maven, что позволяет использовать собранный код в других проектах.

Установка

Установка Leiningen на Unix-подобные ОС и на MS Windows немного различаются. Установка Leiningen на Unix достаточно проста — вам нужно скачать скрипт lein. После этого, поместите скрипт в каталог, перечисленный в путях поиска программ, сделайте его выполняемым, и выполните команду lein self-install для завершения процедуры установки. В процессе этой процедуры, Leiningen скачает и установит все компоненты, необходимые для его работы.

Для установки на MS Windows необходимо скачать файл lein-win32.zip со страницы проекта, который содержит все необходимые для работы утилиты, распаковать его в нужный каталог, и добавить этот каталог в пути поиска, и выполните команду lein self-install. После этого вы можете начать использовать Leiningen.

Начало работы и структура проекта

Для своей работы Leiningen предполагает использование проекта определенной структуры — в корне проекта должен находиться файл project.clj, который содержит определение проекта. Единственной обязательной конструкцией является defproject — макрос Clojure, который раскрывается в набор инструкций по сборке. project.clj также может содержать произвольный код на Clojure, который будет выполнен в процессе сборки.

Исходный код проекта должен находиться в каталоге src, тесты — в каталоге test, а дополнительные ресурсы — в каталоге resources/. Каталог lib содержит библиотеки, нужные для работы проекта — они копируются туда с помощью команды lein deps, используя информацию о зависимостях, определенных в описании проекта. В том случае, если какая-то из библиотек отсутствует в репозиториях, то вы можете просто скопировать ее в каталог lib, и она станет доступна при работе с проектом.

Однако названия каталогов не являются неизменяемыми — вы можете использовать опции defproject для изменения этих значений:

:source-path
название каталога с исходным кодом проекта (по умолчанию — src);
:compile-path
название каталога для хранения откомпилированного кода (по умолчанию — classes/);
:resources-path
название каталога с ресурсами проекта (по умолчанию — resources/);
:test-path
название каталога с тестами (по умолчанию — test/);
:library-path
название каталога для хранения библиотек (по умолчанию — lib/).

Вы также можете добавить в определение проекта его описание (опция :description) и ссылку на домашнюю страницу (опция :url).

Самым простым способом создать новый проект является использование команды lein new, которая принимает один обязательный аргумент — название проекта. Данная команда создаст новый каталог, чье имя совпадает с именем проекта, и внутри него создаст файл project.clj с зависимостями от Clojure и clojure-contrib, файл README с описанием проекта, а также два каталога — src и test для исходного кода и тестов, соответственно. После этого вы можете начать работать с данным проектом.

Пример проекта

Рассмотрим простой проект, описанный в файле project.clj следующим кодом (полный код для данного проекта вы можете найти тут):

(defproject test-project "1.0-SNAPSHOT"
  :description "A test project."
  :url "http://my-cool-project.com"
  :dependencies [[org.clojure/clojure "1.1.0"]
                 [org.clojure/clojure-contrib "1.1.0"]]
  :dev-dependencies [ [swank-clojure "1.2.0"] ]
)

который определяет новый проект test-project с зависимостями от Clojure и набора библиотек clojure-contrib, а также зависимостью, которая используется в процессе разработки — swank-clojure.

В каталоге src находится один файл — simple.clj, определяющий модуль simple и внутри него, одну функцию следующего вида:

(ns simple)

(defn hello
  ([] "Hello world!")
  ([name] (str "Hello " name "!")))

В каталоге test находится файл simple_test.clj, содержащий тест для модуля simple. Для тестирования используется стандартный модуль clojure.test. Исходный код теста выглядит следующим образом:

(ns simple-test
  (:use clojure.test)
  (:use simple))

(deftest simple-test
  (is (= (hello) "Hello world!"))
  (is (= (hello "test") "Hello test!")))

Это полный проект для которого можно выполнять все команды поддерживаемые Leiningen.

Задание зависимостей

Одной из важных и наиболее часто используемых частей defproject является определение зависимостей от других библиотек, находящихся во внешних репозиториях. Для проектов написанных на Clojure это в первую очередь зависимость от самого языка, как это показано в примере.

Различают разные виды зависимостей для которых используются разные опции проекта:

:dependencies
стандартные зависимости — для библиотек, которые используются в программе;
:dev-dependencies
зависимости для библиотек, которые используются во время разработки программы, например, зависимость от swank-clojure и т.п. библиотек.

Кроме этого, имеется плагин native-deps, который реализует поддержку зависимостей от непереносимых библиотек (специфических для конкретных платформ). Вот пример использования этого плагина.

Каждая из перечисленных опций является вектором, каждый элемент которого является вектором с описанием библиотеки. Это описание состоит из (вы уже видели эти определения в примере проекта):

[log4j "1.2.15" :exclusions [javax.mail/mail
                             javax.jms/jms
                             com.sun.jdmk/jmxtools
                             com.sun.jmx/jmxri]]

Репозитории

Leiningen по умолчанию подключает три репозитория:

Помимо этого, пользователь может указать дополнительные репозитории с помощью опции :repositories. В качестве параметров для этой опции указывается отображение (map) состоящее из имени репозитория и его адреса (URL). Например, вот так подключается репозиторий проекта Apache:

(defproject test2 "1.0.0-SNAPSHOT"
  :dependencies [....]
  :repositories {"apache-releases" "http://repository.apache.org/content/repositories/releases/"}
  )

Использование Leiningen

Типичное использование Leiningen выглядит следующим образом:

Этот процесс достаточно прост и повторяется до полной готовности вашего кода :-)

Основные команды Leiningen

Список команд, которые могут быть выполнены Leiningen не является фиксированным — дополнительные плагины могут добавлять новые команды. В минимальной конфигурации Leiningen реализует следующие команды, которые запускаются как lein имя_команды [опции]:

help [имя_команды]
показывает справку об использовании Leiningen или по конкретной команде (за исключением repl) , если ее имя указано в командной строке
new имя проекта [опции]
создает новый пустой проект;
deps
копирует все зависимости в каталог lib/. Эта команда должна выполняться после каждого изменения зависимостей, чтобы остальные команды имели доступ к правильным пакетам;
compile
выполняет компиляцию кода (AOT, ahead-of-time) в классы Java. Полученные классы помещаются в каталог classes/. Пользователь может управлять тем, какие пространства имен будут откомпилированы — для этого имеется опция :namespaces, в которой надо указать список пространств имен для которых будет производиться компиляция;
test [список пространств имен]
выполняет тесты из указанных пространств имен, или все тесты, если список пространств имен не указан;
jar
создает пакет (jar) с кодом проекта;
uberjar
создает автономный пакет (jar) с кодом проекта и всеми зависимостями. Это позволяет достаточно просто распространять ваши программы. При запуске с помощью команды java -jar ... точкой входа является пространство имен, указанное в опции :main в defproject;
pom
создает файл pom.xml, содержащий описание проекта. Этот файл нужен если вы хотите использовать ваш пакет в другом проекте;
install
устанавливает собранный пакет в локальный репозиторий Maven;
clean
удаляет все файлы, созданные в процессе сборки (включая библиотеки из каталога lib/);
repl
запускает цикл ввода команд (REPL) с правильно выставленными путями поиска библиотек (classpath) — в него включаются библиотеки из каталога lib/, а также каталоги src/ и classes/. При запуске lein2 автоматически определяет наличие библиотеки jline, так что вам становится доступна история введенных команд.

Дополнительные команды

Дополнительные команды реализуются плагинами, расширяющими возможности Leiningen. Существует некоторое количество популярных плагинов доступных для Leiningen, обзор которых вы можете найти в следующем постинге. Сюда можно отнести и плагины для запуска серверов Swank и Nailgun, которые реализуют интерактивную работу из привычной среды Emacs или Vim. При этом происходит автоматическое подключение всех указанных зависимостей, так что вы работаете в том же окружении, как и ваша программа.

Для работы с сервером Swank вам необходимо указать в модуль swank-clojure3 в списке зависимостей используемых при разработке (:dev-dependencies)4. После этого вам станет доступна команда lein swank, выполнив которую на порту 4005 будет запущен сервер Swank, к которому вы можете подключиться используя команду Emacs slime-connect. Если же вы пользователь Vim, то вам необходимо указать в зависимостях модуль lein-nailgun, о работе с которым вы можете прочитать на странице проекта vimclojure.

Расширение функциональности Leiningen

Leiningen является расширяемой системой. В случае необходимости вы можете реализовывать свои команды — для этого необходимо создать проект, определяющий пространство имен leiningen.имя_команды и содержащий реализацию в виде функции имя_команды, принимающей описание проекта в качестве аргумента. Подробнее про написание плагинов вы можете прочитать в следующем постинге. Одним из примеров расширения функциональности Leiningen является плагин lein-swank, который реализует поддержку сервера Swank, и находится прямо в репозитории Leiningen.

В репозитории Clojars имеется достаточное количество плагинов, которые были написаны для расширения базовой функциональности Leiningen — автоматической заливки кода в репозиторий Clojars, сборки кода написанного на Java, и т.д. Обычно их имя начинается со слова lein-, которое вы можете использовать в поиске. Для использования этих плагинов вы должны указать их в качестве зависимостей, используемых при разработке (опция :dev-dependencies).

Заключение

Я очень надеюсь, что данный рассказ поможет вам в работе с Leiningen при разработке на Clojure. Если у вас имеются вопросы, то вы можете оставить комментарии на сайте или написать мне по электронной почте — я постараюсь на них ответить.


1. стоит также отметить, что существует проект Polyglot Maven, целью которого является предоставление возможностей описания конфигураций Maven на разных языках — Clojure, Scala, Groovy и др.

2. в отличии от других команд repl реализована напрямую в скрипте lein, а не написана на Clojure

3. пример подключения swank-clojure вы можете увидеть в нашем тестовом примере.

4. и не забудьте выполнить команду lein deps после добавления этой зависимости!

5. в составе Leiningen также имеется небольшой документ, описывающий начала работы с lein.

Last change: 05.03.2013 16:54

blog comments powered by Disqus