BT

ClojarsとLeiningenを使ったClojure向け自動ライブラリ依存関係管理

| 作者: Werner Schuster , 翻訳者 徳武 聡 投稿日 2009年11月23日. 推定読書時間: 1 分 |

原文(投稿日:2009/11/19)へのリンク

ライブラリを使うのは簡単だ。しかし、必要なライブラリをシステムに正しく組み込むのは面倒な作業だ。多くの言語はこの問題をリポジトリを使って解決してきた。リポジトリにはライブラリ + メタデータがホストされていて、ツールを使って簡単に正しいバージョンのライブラリが取得できる。Rubyの世界ではRubyGemsがツールとパッケージングのフォーマットを提供して、この仕組みを実現している。Rubyforge(Gemsのホスティングは近いうちにGemCutterに移行する予定。GemCutterはRubyforgeより柔軟性に富んでいる。)にホストされている既定のリポジトリを使えば簡単にGemパッケージを公開したり、利用したりできる。Javaの世界では、Mavenが人気がある。

ClojarsClojureライブラリの共有や利用を簡単にするための新しいリポジトリだ。Leiningenという名で呼ばれるClojure用ビルドツールを使えば、作ったライブラリをまとめてClojarsへプッシュでき、ライブラリを取得するときもClojureにとって既定の正しい位置に落としてくれる。このLeiningenはPhil Hagelberg氏が作った。

InfoQはClojarsの作者であるAlex Osborne氏に、Clojars作成の動機、実装の工夫について話を聞いた。

InfoQ:ClojarsはどのようにMavenを利用しますか。Mavenを使ってClojarsから何かインストールをすると、何が入手できるのでしょう。普通にライブラリがインストールされますか。それとも、なにか違うものが取得できるのですか。

Clojarsは単なるリポジトリにすぎません。Mavenを使うのはjarファイルとメタデータを多くのビルドツールが想定している形式にするためです。"インストール"が意味することは、使うビルドツールによって全く異なります。LeiningenかMavenを使えば、取得されたライブラリはローカルのMavenリポジトリに配置されます。Leingingenの場合は、基本的にローカルのMavenリポジトリをキャッシュとして扱います。ですのでローカルのMavenリポジトリから、swank-clojureのようなツールが認識できるプロジェクトの"lib"ディレクトリへと依存関係をコピーします。ということは、特別なツールやマジックを使わなくても、下記のようにすればクラスパスを設定できるということでもあります。
java -cp 'src:classes:lib/*' myproject.main
私は、Rubyのgemを使うとコマンドラインから実行可能なファイル(例えば"rails" や "rake")をインストールできるのが、とても気に入っています。gemと同じようなことを実現すべきだと考えていますが、まだ具体的な案はありません。現時点では、Leiningenのプラグインを使えば、同じようなことができます。

InfoQ: Clojarsにライブラリをプッシュしたい場合、どんなメタデータを作成すればいいのでしょう。作成するのに最適な方法はどのようなものですか。

Clojarsが想定しているメタデータの形式はMavenと同様の形式の小さなpom.xmlファイルだけです。このファイルには最低でもartifactId、groupId、バージョンと依存関係を記述しておく必要があります。また、記述しておいた方がいいメタデータフィールドもあります。description、url そして license等です。これらの記述があれば、Clojarsのサイトのインデックスに登録され、検索できるようになります。しかし、強制ではありません。現時点では本当に基本的な検索機能しかありませんが、徐々に改良して、Leiningenに検索コマンドを加えて、ターミナルから離れなくても検索ができるようにしたいです。
POMの構文はタイプするのも苦痛なくらいです。普通はXML名前空間やスキーマも記述しなければなりません。このため、POMの作成にIDEのプラグインを使わなくても済むように、Leiningenの書式を使う方法をお教えします。下記のように、
(defproject myproject "0.1.0"
  :description "An example project."
  :dependencies [[org.clojure/clojure "1.1.0-alpha-SNAPSHOT"]
                 [org.clojure/clojure-contrib "1.0-SNAPSHOT"]
                 [compojure "0.3.1"]])

そして、下記のようにしてLeiningenを使ってPOMを出力します。

lein pom

それから、下記のようにしてClojarsへアップします。

scp pom.xml myproject-0.1.0.jar clojars@clojars.org:

lein-clojarsプラグインを使えば、"lein push"を実行することで、上のふたつのコマンドを実行できます。しかし、上で見たようにもともとシンプルなのであまりご利益がないかもしれませんが。
私の場合、JavaのプロジェクトでもPOMのテンプレートを作るのに"lein pom"を使うことがあります。普通はMavenの原型を使うのでしょう。しかし、そのためにはあの巨大で長いコマンドをタイプしなければなりませんが、私には覚えられません。

InfoQ:Mavenの悪夢に苦しみ、叫び声をあげながら目を覚ましている人々に何か一言。

あなたは一人じゃない、と言いたいですね(笑)。シンプルなプロジェクトのビルドはシンプルであるべきだと思います。Clojars とLeiningenがより優れたツールになって、Mavenに苦しめられている人々をよく眠れるようにしてあげたいですね。

InfoQ: あなたはClojarsのフロントエンドを構築するのにClojureのWebフレームワークであるCompojureを使っていますね。Compojureの使い心地はどうでしょう。

私はよくRubyでSinatraとHamlを使っていました。なので、Compojureにはなじみやすかったです。今までのところ、私はCompojureを気に入っています。私はミニマリストのツールが大好きで(笑)、というのは、共通のことを簡単に実現できて複雑化することなく問題解決しやすいからです。

InfoQ: Clojarsはどこにホストされていますか。リポジトリが動いているのはどこでしょうか。

現時点では、すべてLinode.comの小さなVPS上にあります。リポジトリもディスク上にフラットファイル (lighttpdで静的にサーブされています)として置かれています。WebサイトにはJettyを使い、metadataはSQLiteに保存して検索などができるようにしています。ライブラリの登録のためには、Nailgunを使ってクライアントからのSSHコネクションを受け付けているだけです。また、ClojarsにはClojureで実装したscpサーバがあります(ほとんど知られていないプロトコルなのですが)。このサーバで、アップロードされたPOMとjarファイルが正しいかどうか検証し、問題なかったらMavenは対象のファイルをリポジトリに配置します。リポジトリが十分大きくなったら、jarファイルの実体をAmazon S3へ移行するかもしれませんが、Webサイトやメタデータについては何か強化しようとは考えていません。

InfoQ:あなたは、ClojarsはGemcutterに似ていると考えられる、と言っていますね。ライブラリ名の予約やフォーク等についてどのようにアプローチしていますか。単純に早い者勝ちの仕組みですか。

これは厄介な問題で、私自身何度か考えてみましたが、完璧な答えがあるとは思っていません。幸い、Gemとは違って、POMには名前空間の仕組み(groupId)が備わっているので、Gemと比べればいくらか選択肢が多いです。
私が気付いたのは、Mavenではほとんどの場合groupidは役に立たないということです。私の体験について話すと、私は正式なバージョンの"compojure"を使いたいと思っていました。誰が作ったのか、どこにホストされているかについては考えたくありません。よくMavenで取得できるライブラリのバージョンが0.3以下であるのを見ることがあります。それらのライブラリはGitHubにホストされていたかもしれません。これらの古いライブラリのgroupIdは"com.github.weavejester.compojure"のようになっています。その後、リポジトリが独自のドメインを突然取得した結果、groupIdが"org.compojure"に変わってしまいます。こうなると、間違えて古いライブラリを取得してしまって、2ヶ月後になって初めて自分のプロジェクトで古いバージョンのライブラリを使っていることに気付き、ドキュメントが合っていない理由も明らかになるというわけです。このような体験をしたことがある人は私だけではないはずです。

この問題に対処する方法として事実上の基準になっているのは、正式なバージョンのライブラリにはgroupIdの値をartifactIdと同じにするという方法です。Phil Hagelberg氏がこれをLeiningenに採用することを決めました。なので、単に"compojure"という場合、それは"compojure/compojure"の略称になります。Clojarsも同じ仕組みにしたいと思っています。Mavenの開発者はこの方法を好まないでしょうが、彼らは少し理想主義的すぎると思います。CPAN、PyPIやRubyGemsが示してくれたのは、この問題はそれほど扱いづらくはないということ、とりわけ何らかのセントラルリポジトリがあれば、そう難しくはないということです。もちろん、問題が起きることもあるでしょう。しかし、すべての名前空間にドメイン名を使うのは*ライブラリの利用者*にとって、とても複雑なやり方です。しかも本来、この問題は利用者ではなく*ライブラリの作成者*側の問題で、名前が衝突したときにのみ問題になるだけなのにも関わらず、です。

しかし、この方法にも少し手を加えなければならないと感じています。例えば、Compojureの正式なバージョンはJettyに依存しているはずですが、他のサーバでも利用できるように変更したいとしても、upstreamは認められず、パッチは拒否されてしまいます。また、誰かが書いたライブラリを使いたくても、そのライブラリがClojars にも Maven Centralにもない場合、そのライブラリの名前を勝手に変更するわけにはいきません。このような状況の場合、何らかの名前空間の仕組みがあるととても便利です。そこで、この名前空間の仕組みとして、jarファイルをプッシュするときに"org.clojars.username"という形式のgroupIdを設定するすることが推奨されます。org.clojars.username/projectという形式の代わりに、単純にusername/projectという形式でも問題ないかと思っていましたが、ドメイン名がすべて含まれているのは醜いかも知れませんがこの場合は利益があります。こうしておけば、対象のライブラリが正式なバージョンでないことがはっきりわかりますし、正式なリリースには短い形式を使うように促せます。

現在のところ、あなたが特定のgroupIdをつけたjarファイルをプッシュしたら、そのjarファイルのオーナーはあなただけで、ほかの人はプッシュできません。Webサイトを通してグループにメンバを追加することもできます。これは、まったくの早い者勝ちです。Clojureのコミュニティは親しみやすく、成熟していますからこの方式でも問題はあまり発生しないと思っています。しかし、もし紛争が発生して誰が正しいかもはっきりとわからない状況になったら、私が介入して、紛争が収まるまで公式のグループ名を予約してしまおうと思います。紛争がおこったときに個別にその問題に対処するほうが、何らかの一貫した承認方式で解決するよりもよい方法だと思います。一貫した承認方式を用いると皆の時間を無駄にしますし、問題を提起した側も、承認する側も落ち着かない気持ちになってしまいます。

InfoQ: Clojarsと通信するために必要なツールは何ですか。

私は本当にPhilのLeiningenが気に入っています。先ほどお話しした、CompojureとSinatraを使ったときの感慨と同じものを与えてくれます。"必要十分"で邪魔なものがありません。また、LeiningenはMaven型のリポジトリでも使えますので、作成しているClojureプログラムに必要なJavaライブラリをすべてリパッケージする必要はありません。私としてはLeiningenをお勧めしますが、Mavenからダウンロードできるどんな依存関係管理ツールでもClojarsを使うことはできるでしょうし、Javaの依存関係管理ツールであれば大抵は利用できるでしょう。

プッシュするにはLeiningenもMavenもその他のツールも必要なく、scpがあればいいだけです。scpは実際はすべてのUnixシステムにインストールされていますし、多くのWindows開発者もPuTTYやMSysGitやCygwinと一緒にインストールしていると思います。なので、依存関係の手動管理やビルド、ライブラリのプッシュもすべてシェルスクリプトでやろうとしても、すべてうまくいきます。

この記事に星をつける

おすすめ度
スタイル

こんにちは

コメントするには InfoQアカウントの登録 または が必要です。InfoQ に登録するとさまざまなことができます。

アカウント登録をしてInfoQをお楽しみください。

あなたの意見をお聞かせください。

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

このスレッドのメッセージについてEmailでリプライする
コミュニティコメント

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

このスレッドのメッセージについてEmailでリプライする

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

このスレッドのメッセージについてEmailでリプライする

ディスカッション
BT