The Common Lisp Cookbook – system を定義する

Table of Contents

The Common Lisp Cookbook – system を定義する

📢 🎓 ⭐ Learn Common Lisp efficiently in videos, by the Cookbook's main contributor. Learn more.

🖊️ Discover a new Common Lisp and Coalton editor for beginners: mine and a new VSCode extension for Common Lisp: OLIVE.

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つの方法のいずれかを使います。

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 がすでに起動していた場合は、次のどちらかが必要になることがあります。

些細なテスト定義の書き方

どれほど些細な 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

T
O
C