CommonLisp:asdf

CommonLisp:asdf

Common Lisp の勉強を始めて、asdfというものの存在を知ったのですが、どういうものかわからないので勉強がてらにasdfのマニュアルを和訳してみます。訳があやしい部分は英語を残しておくので、誰かツッコミを入れるなりしてくれると助かります。 ---- はやみず (2007/04/12 20:51:10 PDT)


asdf Manual: http://constantly.at/lisp/asdf/index.html

asdf: another system definition facility

このマニュアルでは、Common Lispプログラムとライブラリのためのシステム定義機構 asdf について説明する。

asdf Copyright © 2001-2004 Daniel Barlow and contributors

This manual Copyright © 2001-2004 Daniel Barlow and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

システムを読み込むためにasdfを使う

この章では、asdfを使って、Lispのプログラムやライブラリをコンパイルしてロードする方法を説明する。

Setting up asdf

普通にasdfを使うかぎりは、asdf.lispというファイルのみを使う。一度実行中のLispプログラムでasdf.lispを読み込んでしまえば、それでasdfを使う準備は完了だ。最大限に便利な環境を用意するために、Lisp処理系を立ち上げたときにいつもasdfがロードされた状態にしたいということもあるだろう。例えば、起動スクリプトからasdfをロードしたり、カスタムコアをダンプしたり(dumping a custom core, なにを意味してるのかわからないので、適切に訳せません^ ^; )。こういったことがしたい場合は、自分の使っているLisp処理系のマニュアルを参照してほしい。

変数 asdf:*central-registry* は、"システムディレクトリ指定子"のリストである[1]。システムディレクトリ指定子は、評価するとシステム定義ファイルのあるディレクトリのパスを返す式で、asdfがシステム定義ファイルを探すときに評価される。Lispの初期化ファイルで *central-registry* に値を設定したい場合は、例えば次のようにできる:

(setf asdf:*central-registry*
  '(*default-pathname-defaults*
    #p"/home/me/cl/systems/"
    #p"/usr/share/common-lisp/systems/"))

システムをロードするための設定

システムをコンパイルしてロードするためには、*central-registry*のディレクトリのリスト中に、そのシステム定義へのシンボリックリンクがある必要がある[2]。

例えば、#p"/home/me/cl/systems/"(末尾のスラッシュに注意)が *central-registry* のメンバだとすると、/home/me/src/foo にあるシステムをasdfからロードできるようにするには、次のコマンドをシェルで実行すればいい(これは1回やるだけでよい)。

$ cd /home/me/cl/systems/
$ ln -s ~/src/foo/foo.asd .

システムをロードする

次の式を、Lisp処理系で評価すると、システム foo がロードされる(必要に応じて、コンパイルもできる)。

(asdf:operate 'asdf:load-op 'foo)

他人の作ったシステムをロードして使うだけなら、これだけを知っていればいい。このマニュアルの残りの部分では、自分で書いたLispプログラム用にシステムを定義する方法について説明する。

[1] ここで"ディレクトリ"というときは、"指定されたディレクトリパスの指定子"の意味である。(When we say "directory" here, we mean ""designator for a pathname with a supplied DIRECTORY component".)

[2] システム定義ファイルの検索をカスタマイズすることもできる。これは上級者向けなので、後で述べることにする。*system-definition-search-functions* について書いてある場所を探してみるといい。"defsystemでシステムを定義する"を参照。

defsystemでシステムを定義する

この章では、asdfを使ってシステムを定義し、ソフトウェアを開発する方法を説明する。

defsystem 形式

make-instanceを使ってコンポーネントのインスタンスを作成してゆくことで、システムを構成するプログラムを書くことができる。しかし、ほとんどの場合はdefsystem形式を使うほうが実用的である。この章ではまずシステムの定義の例を示してから、その後にdefsystemの完全な文法について触れることにする。

まず、次に示す簡単なシステムを見てみよう。

(defsystem "hello-lisp"
    :description "hello-lisp: a sample Lisp system."
    :version "0.2"
    :author "Joe User <joe@example.com>"
    :licence "Public Domain"
    :components ((:file "packages")
                 (:file "macros" :depends-on ("packages"))
                 (:file "hello" :depends-on ("macros"))))

この例の注目すべき点:

[1] ほとんど必要に迫られることはないだろうが、この挙動はオーバーライドすることができる

もう複雑な例

では、さっきの例よりもすこし複雑な例を通して、もうちょっと込み入ったdefsystemの使い方をみてみることにしよう。

(defsystem "foo"
  :version "1.0"
  :components ((:module "foo" :components ((:file "bar") (:file"baz")
                                           (:file "quux"))
                :perform (compile-op :after (op c)
                          (do-something c))
                :explain (compile-op :after (op c)
                          (explain-something c)))
               (:file "blah")))

The method-form tokens need explaining: essentially, this part:

                :perform (compile-op :after (op c)
                          (do-something c))
                :explain (compile-op :after (op c)
                          (explain-something c))

has the effect of

(defmethod perform :after ((op compile-op) (c (eql ...)))
           (do-something c))
(defmethod explain :after ((op compile-op) (c (eql ...)))
           (explain-something c))

where ... is the component in question; note that although this also supports :before methods, they may not do what you want them to - a :before method on perform ((op compile-op) (c (eql ...))) will run after all the dependencies and sub-components have been processed, but before the component in question has been compiled.

The defsystem grammar

system-definition := ( defsystem system-designator {option}* )

option := :components component-list
        | :pathname pathname
        | :default-component-class
        | :perform method-form 
        | :explain method-form
        | :output-files  method-form
        | :operation-done-p method-form
        | :depends-on ( {simple-component-name}* ) 
        | :serial [ t | nil ]
        | :in-order-to ( {dependency}+ )

component-list := ( {component-def}* )
                
component-def  := simple-component-name
                | ( component-type name {option}* )

component-type := :module | :file | :system | other-component-type

dependency := (dependent-op {requirement}+)
requirement := (required-op {required-component}+)
             | (feature feature-name)
dependent-op := operation-name
required-op := operation-name | feature
Serial dependencies

If the :serial t option is specified for a module, asdf will add dependencies for each each child component, on all the children textually preceding it. This is done as if by :depends-on.

:components ((:file "a") (:file "b") (:file "c"))
:serial t

is equivalent to

:components ((:file "a")
             (:file "b" :depends-on ("a"))
             (:file "c" :depends-on ("a" "b")))
Source location

The :pathname option is optional in all cases for systems defined via defsystem, and in the usual case the user is recommended not to supply it.

Instead, asdf follows a hairy set of rules that are designed so that

  1. find-system will load a system from disk and have its pathname default to the right place
  2. this pathname information will not be overwritten with *default-pathname-defaults* (which could be somewhere else altogether) if the user loads up the .asd file into his editor and interactively re-evaluates that form.

If a system is being loaded for the first time, its top-level pathname will be set to:

If a system is being redefined, the top-level pathname will be

More ...