system とは、アプリケーションまたはライブラリを構成する Lisp ファイルの集まりであり、そのため全体として管理されるべきものです。system definition は、どのソースファイルが system を構成するのか、それらの依存関係は何か、どの順序でコンパイルおよびロードされるべきかを記述します。
ASDF
ASDF は Common Lisp の標準ビルドシステムです。ほとんどの Common Lisp 実装に同梱されています。UIOP、すなわち “the Utilities for Implementation- and OS- Portability” も含まれています。マニュアル と チュートリアルおよびベストプラクティス を読むことができます。
簡単な例
system definition をロードする
Lisp を起動した時点で、Lisp は内部モジュールについては知っていますが、デフォルトでは、あなたのすばらしい新プロジェクトが ~/code/foo/bar/new-ideas/ ディレクトリの下にあることを知る方法がありません。そのため、プロジェクトをイメージにロードするには、次の3つの方法のいずれかを使います。
- ASDF または Quicklisp のデフォルトを使う
- ASDF または Quicklisp がプロジェクト定義を探す場所を設定する
- プロジェクト定義を明示的にロードする
getting started#how-to-load-an-existing-project ページの該当節を読んでください。
system をロードする
Lisp が system とは何か、どこにあるかを知ったら、それをロードできます。
ASDF の最も単純な使い方は、asdf:load-system を呼び出してライブラリをロードすることです。その後、それを使えます。たとえば、そのライブラリが foobar package で some-fun という関数を export しているなら、(foobar:some-fun ...) として呼び出せますし、次のようにもできます。
(in-package :foobar)
(some-fun ...)
Quicklisp も使えます。
Quicklisp は内部で ASDF を呼び出します。依存関係がまだインストールされていなければ、それらをダウンロードしてインストールしてくれる利点があります。
(ql:quickload "foobar")
;; =>
;; installs all dependencies
;; and loads the system.
また、Emacs コマンド M-x slime-load-system、またはプロンプトでの , load-system comma command を使って、SLIME から system をロードすることもできます。この方法の面白い点は、SLIME が処理中の system warning と error をすべて集め、ロード完了後に対話的に調べられる *slime-compilation* buffer に入れることです。
system をテストする
system のテストを実行するには、次を使えます。
(asdf:test-system :foobar)
慣習として、テストが成功しなかった場合は error が signal されるべきです。
system を指定する
プログラム内で system を指定する適切な方法は、symbol ではなく小文字の string を使うことです。たとえば次のようにします。
(asdf:load-system "foobar")
(asdf:test-system "foobar")
些細な system definition の書き方
些細な system は、プロジェクトのルートにある foobar.lisp という1つの Lisp ファイルだけを持つでしょう。そのファイルは、汎用ユーティリティの alexandria や pattern-matching の trivia など、既存のライブラリに依存するとします。この system を ASDF でビルド可能にするため、次の内容を持つ foobar.asd という system definition file を作ります。
(asdf:defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar")))
上のファイル名では、foobar.lisp の型 lisp が暗黙的であることに注意してください。そのファイルの内容は次のようになります。
(defpackage :foobar
(:use :common-lisp :alexandria :trivia)
(:export
#:some-function
#:another-function
#:call-with-foobar
#:with-foobar))
(in-package :foobar)
(defun some-function (...)
...)
...
複数の完全な package を using する代わりに、その一部だけを import したい場合もあります。
(defpackage :foobar
(:use #:common-lisp)
(:import-from #:alexandria
#:some-function
#:another-function))
(:import-from #:trivia
#:some-function
#:another-function))
...)
定義した system を使う
system が ~/common-lisp/、~/quicklisp/local-projects/、または ASDF 用にすでに設定された他のファイルシステム階層の下にインストールされていると仮定すると、(asdf:load-system "foobar") でロードできます。
そのファイルを作った時点で Lisp がすでに起動していた場合は、次のどちらかが必要になることがあります。
- 新しい .asd ファイルをロードする:
(asdf:load-asd "path/to/foobar.asd")、または Slime でC-c C-kを使ってファイル全体をコンパイルしてロードします。- 注意: ASDF ファイルに組み込みの
loadを使うのは避けてください。動くかもしれませんが、asdf:load-asdが推奨されます。
- 注意: ASDF ファイルに組み込みの
(asdf:clear-configuration)で設定を再処理します。
些細なテスト定義の書き方
どれほど些細な system であっても、いくらかのテストは必要です。いずれ変更されることになり、その変更が利用側のコードを壊さないことを確認したいからです。テストは期待される振る舞いを文書化するよい方法でもあります。
テストを書く最も簡単な方法は、foobar-tests.lisp というファイルを用意し、上の foobar.asd を次のように変更することです。
(asdf:defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar"))
:in-order-to ((test-op (test-op "foobar/tests"))))
(asdf:defsystem "foobar/tests"
:depends-on ("foobar" "fiveam")
:components ((:file "foobar-tests"))
:perform (test-op (o c) (symbol-call :fiveam '#:run! :foobar)))
最初の system の :in-order-to clause により、(asdf:test-system :foobar) を使えるようになり、それが foobar/tests へ連鎖します。2つ目の system の :perform clause がテストそのものを実行します。
test system では、fiveam は人気のあるテストライブラリの名前であり、perform method の内容は、このライブラリを呼び出して test suite :foobar を実行する方法です。別のライブラリを使う場合は、当然ながら事情は変わります。
プロジェクトスケルトンを作成する
cl-project を使うと、プロジェクトスケルトンを生成できます。デフォルトの ASDF 定義を作成し、ユニットテスト用の system を生成する、などを行います。
インストールします。
(ql:quickload "cl-project")
プロジェクトを作成します。
(cl-project:make-project #p"lib/cl-sample/"
:author "Eitaro Fukamachi"
:email "e.arrows@gmail.com"
:license "LLGPL"
:depends-on '(:clack :cl-annot))
;-> writing /Users/fukamachi/Programs/lib/cl-sample/.gitignore
; writing /Users/fukamachi/Programs/lib/cl-sample/README.markdown
; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample-test.asd
; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample.asd
; writing /Users/fukamachi/Programs/lib/cl-sample/src/hogehoge.lisp
; writing /Users/fukamachi/Programs/lib/cl-sample/t/hogehoge.lisp
;=> T
これで完了です。
Page source: ja/systems.md