Clojure, Leiningen の Windows へのインストールと使い方
Clojure は JVM 上で動作する実用的な Lisp 系の関数型言語です。
この Clojure にはパッケージ管理兼ビルドツールである Leiningen があります。
今回は Clojure と Leiningen の Windows 環境でのインストール方法と使い方を紹介します。
Clojure とは
私はわりとプログラミング言語はいろいろと知っているつもりだったのですが、 Clojure に関しては不覚にも『7つの言語 7つの世界』を見るまではよく知りませんでした。7つの言語 7つの世界 (2011/07/23) Bruce A. Tate 商品詳細を見る |
正確には聞いたことはあったのですが、 最初に JVM で動作する Lisp 方言の一種と聞いて、Lisp の方言や JVM への移植はよくあることだったので、 そこで興味をなくしてよく知らないままにしていました。
しかし、Clojure は知れば知るほど魅力の詰まった言語だったのです。
Lisp の美しさ + 実用性
私は "最も美しいプログラミング言語は ?" と聞かれたら、間違いなく Lisp と答えるでしょう。式、コード、データなど全ての統一感、 "これぞプログラミング"といえる小さな関数を組み立ていく思想 と実に美しいと思います。
ぱっと見は括弧だらけでみにくいのですが、これは使っているうちにじわじわと感じてきます。 見かけはダサくて、どんくさいけど、付き合ってみて初めて分かる内面的な美しさといったところでしょう。
しかし、私は実用主義でもあります。
Lisp の方言は Common Lisp 、 Scheme を筆頭として色々あるのですが、 それでアプリをバリバリ作るとなると "それはちょっと" と思ってしまいます。 Emacs Lisp はよく使いますが、 これは汎用プログラミング言語とは言いがたいです。
そんな中、 Clojure は実用的な Lisp です。
JVM 言語ですので、 Java の豊富なライブラリーを使えますし、 Java, JRuby などの言語と組み合わせてアプリケーションを作ることもできます。 Clojure はオブジェクト指向のクラスが使えたり、 括弧を減らす工夫がされていたりと、 Lisp に比べると少し垢抜けちゃった感があります。 多少統一感の美しさが削がれた気はしますが、 その分実用性は上がっており、 美しさと実用性が上手く配分された言語に仕上がっていると思います。
関数型プログラミング
実用性のなかでも、特に魅力的なのは Clojure が関数型言語である点です。"そもそも Lisp って関数型じゃないの" という人もいるかもしれません。 確かに以前は関数型の代表のように紹介されていたこともありましたが、 今では関数型に入れられないことが多いです。
高階関数は当然使えます。 しかし、入力が同じならば結果が常に同じという数学的な意味での関数、 変数の変更をしない参照透過性 に関しては不十分です。
その点、 Clojure は関数型プログラミングを問題なく行うことができます。 また、Agent, STM といった並列処理を止めないデータ共有の機能もあり、 今後重要になってくる並列処理に強い言語になっています。
Java を知らない人でも始めやすい
関数型の JVM 言語といえば、 Scala も有名です。こちらは関数型とオブジェクト指向の共存が主目的です。 ただ、そのため Scala を使うには結構 Java の知識が必要になってきます。 ライブラリーや開発環境周りは特に必要でしょう。
Clojure も当然 Java のクラスは使えるのですが、 言語のスタイルにはあまりそぐいません。しかし、その分 主要なものは独自のライブラリーが用意されていることが多いです。 また、次章の Leiningen は優れたパッケージ管理、ビルドツールで、 それほど Java の開発環境の知識を必要としません。 Java を知らないけど、関数型言語を覚えたいという人には向いていると思います。
Leiningen とは
Java では Maven というツールが最近よく使われるようになりました。これには、プロジェクトの作成、ビルドといったプロジェクト管理機能に加えて、 ネットから必要なライブラリーを取ってくるといったパッケージ管理機能があります。
Leiningen はこの Maven の Clojure 版です。
Maven をあまり使ったことがないのですが、サイトの説明によれば、 Maven の使いづらさを解消しているということです。
また、 Maven から完全に独立しているわけではなく、 Maven に登録された Java のライブラリーを Clojure で取ってくるということもできます。
因みに読み方は「ライニンゲン」らしいです。
Leiningen のインストール
Leiningen をインストールすれば、 それを使って Clojure をインストールできるので、 先に Leiningen のインストール方法について説明します。 Leiningen では 2 つのインストール方法があります。- インストーラーの使用
- lein.bat によるセルフインストール
前準備
先に JDK をインストールする必要があります。 JDK のインストールに関しては以下の記事を見て下さい。 なお、この際 "Program Files" のように JDK のパスにスペースが含まれていると Emacs から repl を呼び出す際に失敗してしまいます。また、プロキシー経由でないと外部にアクセスできない場合には、 先に環境変数 http_proxy, https_proxy(https 用) でプロキシーサーバーとポート番号を指定する必要があります。
プロキシーサーバー:ポート番号Windows の場合はバージョンによって少し違いますが、次の手順で環境変数を設定します。
(例) foo.bar.co.jp:8080
- [(マイ)コンピューター] を右クリックして [プロパティ]
- [詳細設定]
- [環境変数]
- [詳細設定]
インストーラーの使用
まずダウンロードページからインストーラー(leiningen-installer-xxxx.exe)をダウンロードします。 インストーラーを起動するとまずインストール先(Leiningen のホームフォルダー)を聞いてきます。インストーラー(beta1)のバグだと思いますが、ここで変更したとしても、 実行ファイルの場所だけ代わって、 Leiningen のホームフォルダーは "(ユーザーフォルダー)/.lein" になってしまいます。 変更したい場合にはあらかじめ環境変数 LEIN_HOME の値に設定しておく必要があります。
次に使用する JDK を聞かれるので、インストールした JDK を選択します。
環境変数の設定もインストーラーがやってくれるので、 インストールが完了すれば、 lein(.bat) で Leiningen が使えるようになっています。
なお、 JDK のバージョンを変えるなどで、場所が変わった場合には、 以下のコマンドを実行するとインストールウィザード風のものが現れて、変更することができます。
- (Leiningen のインストール先)/bin/configure-leiningen-installer.exe
lein.bat によるセルフインストール
こちらは lein.bat を先にダウンロードして、自身でインストールさせる方法です。先に Leiningen のインストール先のフォルダーを用意し、 以下の環境変数を設定します。
環境変数 | 内容 |
---|---|
LEIN_HOME | インストール先のフォルダー |
PATH | インストール先のフォルダー(%LEIN_HOME%)を追加 |
Windows 7 以上であれば、 PowerShell は標準なので、そのままで大丈夫でしょう。 ちなみにインストーラーでは一緒に Curl をインストールしています。
インストール先のフォルダーに移動し、以下のコマンドを実行すると Leiningen がインストールされます。
~/.lein $ lein.bat self-install
Leiningen 内で使用する java コマンドは環境変数 JAVA_CMD で指定されたコマンドです。ここに JDK 内にある java コマンド(プライベート JRE)のパスを指定します。 JAVA_CMD は他のツールなどでも使われるので、 Leiningen だけで使用したい場合には LEIN_JAVA_CMD で指定します。
どちらも指定されていない場合は、通常の(PATH で見つかる) java コマンドが使用されます。
Clojure のインストールと使い方
Leiningen を使えば、 Clojure を自動的にインストールしてくれるのですが、 プロジェクトを作っておく必要があります。 1 つのファイルをスクリプトとして実行する場合には Clojure をそのまま使います。 サンプル等をちょっと動かすにはこちらの方が便利なので、 Clojure のインストールと使い方についても説明します。インストール
まず、 Clojure のダウンロードページから圧縮ファイル(clojure-X.X.X.zip)をダウンロードします。 安定版である Stable Release の方がよいと思います。圧縮ファイル内にあるclojure-X.X.X.jarを好きなフォルダーに置けば完了です。
正確には国際化用の pom ファイルも一緒に置くのが正しいのですが、 日本語対応はされていないので、 jar だけで構いません。
なお、 Leiningen で自動的にインストールした Clojure を使うこともできます。
Leiningen がインストールした場合には以下のフォルダーの下にバージョン別に置かれます。
(ユーザーフォルダー)/.m2/repository/org/clojure/clojure
使い方
Clojure は以下の形式で使うことができます。java -jar "Clojureのjarのパス" [引数]
スクリプトとして実行する場合には引数に Clojure のソースファイル(clj)を渡します。
hello.clj :
(println "Hello World!")
~/clojure $ java -jar d:/usr/jar/clojure-1.5.1.jar hello.clj
Hello World!
Ruby や Perl のように -e によるワンライナーの実行も可能です。また、 引数を何も指定しないと repl(対話モード)として起動します。 ただし、 quit などによる終了ができないので、 後述する Leiningen から実行した repl の方が使いやすいと思います。
その他のコマンドに関して詳しくは -h のヘルプで確認して下さい。
なお、スクリプトファイルの後の引数は *command-line-args* 変数に格納されます。 コマンド引数の処理に関しては以下の記事をご覧ください。
バッチファイルの利用
jar ファイルを指定して実行するのは、若干面倒くさいです。 そこで clojure 呼び出しの bat ファイルを作成しました。使用される場合は clojure-X.X.X.jar を置いたフォルダーを PATH に追加し、 以下のファイルを同じフォルダーにダウンロード([名前をつけて保存])して下さい。 jar ファイルの後に渡す引数と同じものを渡して実行します。
~/clojure $ clojure.bat hello.clj
Hello World!
同一フォルダー内に複数の clojure-X.X.X.jar ファイルがある場合には、
新しいバージョンを使います。ただし、あまりちゃんと作ってないので、 clojure-1.5.9.jar と clojure-1.5.10.jar では clojure-1.5.9.jar の方を新しいと判定してしまいます。
Leiningen の使い方
Leiningen (lein コマンド)の使い方について説明します。簡単に説明していますので、詳しくは以下のヘルプまたは Tutorial で確認して下さい
$ lein help [コマンド]
repl(対話モード)
対話形式の repl で実行する場合には repl コマンドを実行します。
~ $ lein repl
初回時には必要なライブラリー等をインストールします。
その後 repl が起動します。終了する場合は quit または exit と入力します。
プロジェクトの作成
新しいプロジェクトを作成する場合には作成したいフォルダーに移動して new コマンドを実行します。$ lein new テンプレート名 プロジェクト名 (例) ~/clojure $ lein new app sampleテンプレート名に app を指定するとアプリケーション、 省略または default でライブラリー用のプロジェクトが作成されます。
app で作成すると以下のような構成になります。
(sample) |-- project.clj プロジェクトファイル |-- README.md readme の markdown ファイル |-- LICENSE ライセンスファイル(Eclipse Public License) |-- doc ドキュメント | `-- intro.md |-- resources 画像などのリソース |-- src ソースファイル | `-- sample | `-- core.clj |-- target ビルドされたファイル(ビルド後に作成) `-- test テスト用コード `-- sample `-- core_test.cljちなみに src/sample/core.clj は次の内容です。
(ns sample.core (:gen-class)) (defn -main "I don't do a whole lot ... yet." [& args] (println "Hello, World!"))
プロジェクトの実行、ビルド、テスト
プロジェクトのビルド関連のコマンドをいくつかあげてみます。コマンド | 機能 |
---|---|
run | スクリプトとして実行 |
compile | class ファイルの作成 |
jar | jar ファイルの作成 |
uberjar | 単体で動く jar ファイルを作成 |
clean | ビルドしたファイルをクリア |
test | test コードの実行 |
jar コマンドで作成した場合は、 他の依存している jar ファイルなどを指定して実行しないとエラーとなります。 それに対して uberjar では必要なものを全部まとめた jar ファイルを作成してくれます。
~/clojure/sample $ lein run Hello, World! ~/clojure/sample $ lein uberjar Compiling sample.core Created d:\home\clojure\sample\target\uberjar\sample-0.1.0-SNAPSHOT.jar Created d:\home\clojure\sample\target\sample-0.1.0-SNAPSHOT-standalone.jar ~/clojure/sample $ java -jar target/sample-0.1.0-SNAPSHOT-standalone.jar Hello, World!
パッケージの検索
Clojure には contrib (ClojureDocs)という標準ライブラリーを始め、多くのライブラリーがあります。ライブラリーを使いたい場合は、 Clojars などのリポジトリーからパッケージを取得してインストールします。
パッケージは Clojars のサイトや lein の search コマンドで検索することができます。
どんなパッケージがあるかなどはネットで探すことの方が多いので、 Ruby の gem のように他のパッケージ管理ツールでは検索機能がついていてもあまり使うことはありません。
しかし、 Leiningen では他のツールのようにバージョンを省略して最新バージョンということができないので、 必ずバージョンも指定します。 このため、パッケージ名がわかっていても、 lein を使って最新バージョンの番号を調べることがあります。
~ $ lein search markdown-clj
Searching over Artifact ID...
== Showing page 1 / 1
[markdown-clj "0.6"] Markdown parser
[markdown-clj "0.5"] Markdown parser
:
[markdown-clj "0.9.32"] Markdown parser
[markdown-clj "0.9.33"] Markdown parser
[clj-markdown "0.1.0"] A Clojure wrapper around the MarkdownJ library.
因みに search では初回起動時にライブラリーのインデックスを取得します。
a few minitus と出てますが、かなり時間はかかります。また、容量も結構使います。ネットでパッケージを探す場合には、 ライブラリーのまとめサイトとして以下のサイトが有名どころだと思います。 日本語でライブラリーの紹介されているサイトもいくつかあります。
パッケージのインストール
パッケージを追加する場合には、 まずプロジェクトファイル project.clj の :dependencies に使用したいパッケージ名とバージョンを追加します。(defproject sample "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.5.1"] [markdown-clj "0.9.33"]] :main sample.core :profiles {:uberjar {:aot :all}})その後、run, uberjar などプロジェクトに対する lein のなんらかのコマンドを実行すると、 コマンド実行前にパッケージがインストールされます。
~/clojure/sample $ lein uberjar
Retrieving markdown-clj/markdown-clj/0.9.33/markdown-clj-0.9.33.pom from clojars
Retrieving markdown-clj/markdown-clj/0.9.33/markdown-clj-0.9.33.jar from clojars
Created d:\home\clojure\sample\target\sample-0.1.0-SNAPSHOT.jar
Created d:\home\clojure\sample\target\sample-0.1.0-SNAPSHOT-standalone.jar
なお、インストールされたパッケージは "(ユーザーフォルダー)/.m2/repository" 以下に置かれます。
プロファイル(プラグイン)
Leiningen の機能を拡張したり、よく使うライブラリーの記述を省略したい場合にプロファイルを使います。これは Leiningen 2.x 以降から lein の plugin コマンドに代わって使われるようになった機能で、 plugin コマンドを実行するとプロファイルを使うようにというメッセージが出ます。
使用する場合には Leiningen のホームフォルダー(LEIN_HOME)に以下のようなファイルを作成します。
profiles.clj :
{:user { :plugins [[lein-pprint "1.1.1"] [lein-typed "0.3.0"]] :dependencies [[slamhound "1.3.3"] [hiccup "1.0.4"]] } }
:user
内で以下のものを指定しています。
キー | 説明 |
---|---|
:plugins | lein の拡張用プラグイン。 "lein pprint" コマンドで project.clj をわかりやすく表示(Pretty Print)できるようになるなど。 |
:dependencies | よく使うライブラリー。 project.clj にマージされる。 |
この
:user
では自分用全般に追加されます。この他にも
:test
で test コマンド実行時だけ、
:dev
で REPL など開発用機能実行時だけのライブラリーを指定できたりします。詳しくは以下のサイトなどを参考にして下さい。
Clojure でコマンドライン引数を使う
今回は Clojure でコマンドライン引数を扱うための方法について説明します。
1 ファイルのスクリプト
まずは Clojure で 1 ファイルをスクリプトとして実行する場合です。 スクリプトとして実行する方法については以前の記事をご覧下さい。 1 ファイルを実行する場合には、コマンドライン引数は *command-line-args* という特殊な名前の変数に格納されています。(特殊な変数が * で囲まれているのは Emacs っぽいですね)
args.clj :
(println *command-line-args*)
~/clojure $ clojure.bat args.clj foo bar baz
(foo bar baz)
*command-line-args* はシーケンスなので、各種のシーケンス用関数を使って値を取り出して使います。
args-seq.clj :
(doseq [arg *command-line-args*] (println arg))
~/clojure $ clojure.bat args-seq.clj foo bar baz
foo
bar
baz
Leiningen プロジェクト
次は Leiningen を使って作成した Clojure のプロジェクトの場合です。
~/clojure $ lein new app argsample
Generating a project called argsample based on the 'app' template.
プロジェクトの場合は分かりやすいでしょう。
core.clj の -main 関数の引数 args
がそのままコマンドラインの引数です。
argsample/src/argsample/core.clj :
(ns argsample.core (:gen-class)) (defn -main "Command line arguments sample" [& args] (println args) (doseq [arg args] (println arg)) )
~/clojure/argsample $ lein run foo bar baz
(foo bar baz)
foo
bar
baz
オプション引数解析
今回はコマンド引数を単に扱う方法を紹介しました。 しかし、実際にプログラムを作る場合には更にオプションの解析を行いたいことでしょう。Clojure には、 tools.cli というオプション解析用のライブラリーが用意されています。 これの使い方に関しては以下の記事を見てください。
tools.cli を使った Clojure でのコマンド引数のオプション解析
今回は Clojure でコマンドライン引数のオプション解析を行う方法について説明します。
with-command-line から tools.cli へ
Clojure でオプション解析する場合、以前は contrib の with-command-line が使われていたようです。しかし、 これが contrib から分離して、今は tools.cli になっています。 with-command-line の頃は Java の悪習を引きずって、 POSIX 規格を無視した -(ハイフン) が一つのロング名が使われていました。 これが、 tools.cli ではちゃんと GNU スタイルでのオプションの指定になっています。
基本的な使い方
サンプルを使って tools.cli の使い方を説明していきます。対象 | リンク |
---|---|
ブラウズ | cmdprs |
圧縮ファイル | cmdprs.zip |
プロジェクトへの追加
ライブラリーを使うので、 Leiningen でプロジェクトを作って利用します。Leiningen の使い方に関して詳しくは以前の記事を参考にしてください。
~/clojure/commandline $ lein new app cmdprs
project.cli に追記
:dependencies [[org.clojure/clojure "1.5.1"] [org.clojure/tools.cli "0.3.1"]]おそらく tools.cli はよく使うので、プロファイル($LEIN_HOME/profiles.clj)に追加してもいいのではないかと思います。
使い方の流れ
tools.cli で使う関数は parse-opts のみで、次のような流れで使用します。- オプション仕様を定義
- 解析する引数とオプション仕様の定義を渡して parse-opts を実行
- 戻り値の解析結果を利用
;; オプション仕様定義 (def option-spec [["-h" "--help" "Show help."] ["-v" "--version" "Show program version."] [nil "--verbose" "Output log verbosity."] ;; 引数付き ["-o" "--output FILE" "Output file path (Default: \"a.out\")" :default "a.out" ] ]) (defn -main "Command line option parse sample program." [& args] ;; 引数を解析 (parse-opts 引数リスト オプション仕様) (let [info (parse-opts args option-spec)] ;; 解析結果を使用 (pprint info)) )
~/clojure/commandline/cmdprs $ lein run -- -v -k -o test.out foo bar baz {:options {:version true, :output "test.out"}, :arguments ["foo" "bar" "baz"], :summary " -h, --help Show help.\n -v, --version Show program version.\n --verbose Output log verbosity.\n -o, --output FILE a.out Output file path (Default: \"a.out\")", :errors ["Unknown option: \"-k\""]}なお、 通常 lein run の後の引数がプログラムの引数として渡されます。 lein 側の引数と紛らわしい場合など "--" を使って明示的に区切ることができます。
解析結果
parse-opts の解析結果は次のようなマップです。キー | 値 |
---|---|
:options | オプションのキー(ロング名)と値の map |
:arguments | オプションを取り除いたコマンド引数の vector |
:summary | オプションの説明(Usage で使用) |
:errors | エラーメッセージの vector |
オプション仕様の定義
オプション仕様の定義の仕方についてもう少し詳しく説明します。フラグ
オプション仕様の定義は次のような形式の vector の vector となります。["ショート名" "ロング名" "説明"]
[["-h" "--help" "Show help."] ["-v" "--version" "Show program version."]]引数を指定していない場合はフラグで、 -v のようにオプションを指定すると true が設定されます。
ショート、ロング形式は nil を指定するとどちらかのみになります。
[nil "--verbose" "Output log verbosity."]ロング名は options のマップのキーにもなるので、 ロング名を省略した場合には :id でキーを指定する必要があります。
ただ、 個人的にはショートはアルファベットの数の制限上のために指定しないことはあっても、 ロング名は付けておくべきだと思います。
引数をとるオプション
引数をとるオプションを定義するためには、ロング形式の後に引数を書きます。["-o" "--output FILE" "Output file path"]指定した引数は文字列として格納されます。
これを :parse-fn で変換関数を指定して数値として格納したり、 :validate で引数の妥当性チェックもできます。
ただ、個人的には細かいエラー処理がやりずらくなるので、 これはオプション解析が終わった後に別途やってもよいのではないかと思います。
デフォルト値
定義の vector のオプションの説明の後に、 キーと値を書くことによって拡張した設定を書くことができます。:default で省略時のデフォルト値を設定となります。
["-o" "--output FILE" "Output file path (Default: \"a.out\")" :default "a.out" ]
:assoc-fn を使った拡張
オプションを複数回指定すると後のもので上書きされます。 通常はこれで問題ありませんが、 grep の -e オプションのように指定されたものを全部使いたい場合などもあると思います。こういった場合には :assoc-fn でデフォルトの動作を変更することができます。
まずはデフォルトの動作を確認するため、ダンプ処理と通常の assoc を行う関数にしてみます。
["-e" "--expr PATTERN" "Regular expression pattern" :assoc-fn (fn [m k v] (println (str "m=" m ", k=" k ", v=" v)) (assoc m k v) ) ]
~/clojure/commandline/cmdprs $ lein run -- -e foo -e bar -e baz m={:output "a.out"}, k=:expr, v=foo m={:expr "foo", :output "a.out"}, k=:expr, v=bar m={:expr "bar", :output "a.out"}, k=:expr, v=baz {:options {:expr "baz", :output "a.out"}, :arguments [], :summary "...", :errors nil}これを次のように変更すると複数回指定したものを vector にためていきます。
["-e" "--expr PATTERN" "Regular expression pattern" :assoc-fn (fn [m k v] (assoc m k (let [vec (get m k)] (if vec (conj vec v) [v]))) ) ]
~/clojure/commandline/cmdprs $ lein run -- -e foo -e bar -e baz {:options {:expr ["foo" "bar" "baz"], :output "a.out"}, :arguments [], :summary "...", :errors nil}
その他
説明しなかった変換、妥当性チェックやサブコマンドタイプへの対応など tools.cli では、その外にもいろいろと機能があります。 それらについては以下のサイト等を参考にしてください。- Documentation for 0.2.4 · clojure/tools.cli Wiki · GitHub
- clojure.tools.cli - Command-line processor 0.3.2 API documentation
- tools.cli: Clojureでコマンドライン引数を扱う - Qiita
サンプルコード
もう少し実際にコマンドラインプログラムで使いそうなサンプルを書いてみました。ここでの挙動はなるべく標準的な動作にそって実装しています。
対象 | リンク |
---|---|
ブラウズ | cmdsamp |
圧縮ファイル | cmdsamp.zip |
core.clj :
: (defn -main [& args] (let [{:keys [options arguments errors summary]} (parse-opts args option-spec)] ;; 中断処理 (cond ;; --help (:help options) (do (print-usage summary) (System/exit 0)) ;; --verion (:version options) (do (print-version) (System/exit 0)) ;; 解析時のエラー errors (do (print-err-msg errors) (System/exit 1)) ;; 引数が 0 個 (< (count arguments) 1) (do (print-err-msg "FILE isn't specified.") (print-usage summary) (System/exit 1)) ) ;; アプリの処理 (println "Options : " options) (println "Arguments : " arguments) ))
変数の格納
解析結果をそのまま格納するのではなく、バインディングを使って個々の変数に格納します。(let [{:keys [options arguments errors summary]} (parse-opts args option-spec)]
ヘルプ、バージョン処理
解析結果を元にエラー等で中断するかチェックしています。--help や --version は必ず用意しておかなければならないオプションです。 それぞれ指定するとヘルプやバージョン情報を標準出力に表示して終了します。
(cond ;; --help (:help options) (do (print-usage summary) (System/exit 0)) ;; --verion (:version options) (do (print-version) (System/exit 0))ヘルプではオプションの説明は tools.cli が用意してくれるのでそれを使います。
(defn print-usage [options-summary] (println "Usage:" program-name "[Options] FILE [...]\n" "FILE: Input file path.\n" (str "Options:\n" options-summary)))
~/clojure/commandline/cmdsamp $ lein run -- -h
Usage: cmdsamp [Options] FILE [...]
FILE: Input file path.
Options:
-h, --help Show help.
-v, --version Show program version.
--verbose Output log verbosity.
-o, --output OUT_FILE a.out Output file path (Default: "a.out")
エラー処理
オプション解析エラーは vector で格納され、エラーが発生していない場合は nil です。;; 解析時のエラー errors (do (print-err-msg errors) (System/exit 1))エラーメッセージは標準エラー出力(*err*)に出力しています。メッセージスタイルも標準のスタイルに合わせています。
(defn print-err-msg [errors] (let [errmsgs (if (vector? errors) errors [errors])] (.println *err* (join \newline (map #(str program-name ":Error:" %1) errmsgs))) ))
~/clojure/commandline/cmdsamp $ lein run -- -u foo -o
cmdsamp:Error:Unknown option: "-u"
cmdsamp:Error:Missing required argument for "-o OUT_FILE"
また、サンプルではコマンド引数が 1 個以上ないとエラーにし、 Usage も表示しています。
;; 引数が 0 個 (< (count arguments) 1) (do (print-err-msg "FILE isn't specified.") (print-usage summary) (System/exit 1))
~/clojure/commandline/cmdsamp $ lein run --
Usage: cmdsamp [Options] FILE [...]
FILE: Input file path.
Options:
-h, --help Show help.
-v, --version Show program version.
--verbose Output log verbosity.
-o, --output OUT_FILE a.out Output file path (Default: "a.out")
cmdsamp:Error:FILE isn't specified.