Leiningen, jak nemít vlasy v ohni

Leiningen je buildovací a projektový nástroj pro Clojure, který se velmi silně inspiroval Javovským Mavenem nebo Groovyovským Gradle. Jeho podtitulem je “automating Clojure projects without setting your hair on fire”. Po vlastních zkušenostech musím říct, že podtitul je zvolen velmi trefně. Leiningen bych rozhodně doporučil, jako startovací bod pro seznamování se s Clojure.

Pravdou je, že prvotní rozchození Clojure je tvrdým oříškem i pro zkušené hackery a pro nováčky může být trochu stresující. (OK, tak pro ty z nás, kteří jsou opravdu dobří, to zas takový problém není ;-)

Instalace

Instalace na linuxu je triviální (viz README):

  1. stáhnne se skript,
  2. přidá se do $PATH,
  3. udělá se spustitelným (chmod 755 lein).

Na Windows je to o něco málo složitejší (opět, viz README).

Korektní instalaci ověříme příkazem lein version:

Leiningen 1.7.1 on Java 1.7.0_147-icedtea OpenJDK 64-Bit Server VM

REPL

REPL bývá silnou zbraní skriptovacích jazyků. Nejinak je tomu i v Clojure. Pokud máme nainstalovaný Leiningen, můžeme rovnou začít používat Clojure (bez jeho instalace) příkazem: lein repl

REPL ukončíme buď klávesovou zkratkou Ctrl-C, nebo příkazem:

(System/exit 0)

První projekt

Leiningen umožňuje vytvořit kostru nového projektu pomocí příkazu: lein new <project>

Vytvoření a layout projektu

Vytvoření a layout projektu

V layoutu projektu vidíme klasické rozdělení na zdrojový/produkční kód a testy (adresáře src a test) a také soubor s definicí projektu project.clj.

Pokud chceme mít v projektu jiné namespacy, než je název projektu můžeme použít pro vytvoření projektu syntaxi: lein new project directory

Projekt s jiným namespacem

Projekt s jiným namespacem

Definice projektu

Konfiguračním souborem pro projekt (a Leiningen) je project.clj, jehož iniciální podoba je na následujícím výpisu:

(defproject my-project "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.3.0"]])

Nejzajímavější položkou je keyword :dependencies, které (překvapivě) definuje závislosti projektu. Podobně jaku v Mavenu jsou závislosti stahovány z repository — defaultními úložišti jsou Clojars a Maven Central. Další repository lze přidat pomocí keywordu :repositories.

:repositories {
  "my-local" "http://my-local/repository"
  "java.net" "http://download.java.net/maven/2"}

Pokud chceme použít některé závislosti pouze při vývoji, použijeme pro jejich definici keyword :dev-depencencies. Já ho používám např. pro Midje.

:dev-dependencies [[midje "1.3.2-SNAPSHOT"]
                   [lein-midje "1.0.8"]]

Pokud budeme výsledný projekt distribuovat jako spustitelný JAR soubor, hodí se nám keyword :main, které definuje namespace, ve kterém se nachází funkce -main (která bude defaultně zavolána při spuštění lein run (viz dále)); a také se zpropaguje do položky Main-Class v MANIFEST.MF.

Když už jsme u manifestu, existuje i keyword :manifest, které umožní vložit specifické položky do souboru MANIFEST.MF.

:main blog-lein.core
:manifest {"Build-Version" ~project-version
           "Built-Time" ~(str (Date.))}

Další tasky

Kompletní seznam tasků Leiningenu lze vypsat příkazem: lein help

Ty které jsou shodné s Mavenem není potřeba představovat/vysvětlovat:

  • clean
  • compile
  • deploy
  • install
  • test

Z těch ostatních jsou zajímavý:

  • classpath — vypíše classpath aktuálního projektu.
  • deps — dotáhne z repozitářů závislosti definované v :dependencies a :dev-dependencies. Defaultní umístění je adresář lib, ev. lib/dev a dá se ovlivnit keywordem :library-path.
  • pom — vytvoří z project.clj soubor pom.xml pro Maven.
  • repl — spustí REPL, buď samostatně (standalone), nebo v rámci projektu — v tom případě přidá do classpath všechny knihovny definované v :dependencies.
  • run — spustí -main funkci projektu. Namespace funkce musí být definován keywordem :main.
  • search — prohledá repozitáře a vypíše dostupné závislosti.
  • uberjar — zabalí všechny soubory a závislosti do jednoho JAR souboru. Vhodné pro standalone distribuci. Pokud má být JAR spustitelný, musí namespace s funkcí -main obsahovat ve své deklaraci direktivu :gen-class.

Zkušební projekt

Pro studijní důvody jsem vytvořil jednoduchý projekt, na kterém se dají vyzkoušet výše uvedené příkazy (samozřejmě kromě lein new):

[Update] Projekt je k dispozici na Bitbucketu:

[/Update]

Projekt má následující definici (soubor project.clj):

(import java.util.Date)
(def project-version "1.0.0-SNAPSHOT")
(defproject blog-lein project-version
  :description "Sometimes Clojure, Leiningen"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :dev-dependencies [[midje "1.3.2-SNAPSHOT"]
                     [lein-midje "1.0.9"]]
  :main blog-lein.core
  :manifest {"Build-Version" ~project-version
             "Built-Time" ~(str (Date.))})

Doporučoval bych vyzkoušet si:

  • lein classpath
  • lein jar (a mrknout do MANIFEST.MF)
  • lein pom
  • lein repl (a zkusit si nějaké Midje funkce)
  • lein run
  • lein uberjar (a spustit si kód pomocí java -jar <jarfile>.jar)

Věřím, že po tomto malém úvodu se vám bude s Clojure začínat mnohem jednoduššeji (a nemít přitom vlasy v jednom ohni ;-)