Next: defsystemの文法, Previous: 単純な例, Up: defsystemでシステムを定義する [Contents][Index]
defsystemのより複雑な使い方を説明するために、いくぶん込み入った例を挙げてみましょう。
(in-package :asdf-user)
(defsystem "foo"
:version (:read-file-form "variables" :at (3 2))
:components
((:file "package")
(:file "variables" :depends-on ("package"))
(:module "mod"
:depends-on ("package")
:serial t
:components ((:file "utils")
(:file "reader")
(:file "cooker")
(:static-file "data.raw"))
:output-files (compile-op (o c) (list "data.cooked"))
:perform (compile-op :after (o c)
(cook-data
:in (component-pathname (find-component c "data.raw"))
:out (first (output-files o c)))))
(:file "foo" :depends-on ("mod"))))
(defmethod action-description
((o compile-op) (c (eql (find-component "foo" "mod"))))
"cooking data")
それでは例を説明していきます。
fooを定義しますが、それ以外のフォームも含んでいます。この点については以下で説明します。
"mod"という名の:moduleコンポーネントを含んでいます。"mod"は3つのソースファイル(utils.lisp、reader.lisp、cooker.lisp)とdata.rawの集まりです。
:static-fileには、Lispのソースファイルと違って暗黙の拡張子がないことに注意してください。
"mod"コンポーネントに含まれる4つのファイルは、システムのディレクトリ直下のmod/というサブディレクトリにあるでしょう。(このディレクトリの場所は:pathnameオプションで任意に上書きすることができます。defsystemの文法内の解説を参照してください。)
:serial tはmodのそれぞれの子コンポーネントがその前のコンポーネントに依存していることを示しています。つまり、cooker.lispはreader.lispに依存し、reader.lispはutils.lispに依存します。最後にdata.rawがこれらすべてに依存していることになりますが、静的なファイルですから特に動作への影響はもたらしません。しかし、(:static-file "data.raw")が最初に置かれていた場合は、このデータファイルが変更されるとほかのすべてのソースファイルが再コンパイルされることになります。それはおそらくこの例では不都合でしょう。
:output-files、:perform以下のメソッド形式の表現は、特定のコンポーネントについてメソッドを定義するための省略表現です。つまり、以下の部分
:output-files (compile-op (o c) (list "data.cooked"))
:perform (compile-op :after (o c)
(cook-data
:in (component-pathname (find-component c "data.raw"))
:out (first (output-files o c))))
は次のようにメソッドを定義しているのと同じことになります。
(defmethod output-files ((o compile-op) (c (eql ...))) (list "data.cooked")) (defmethod perform :after ((o compile-op) (c (eql ...))) (cook-data :in (component-pathname (find-component c "data.raw")) :out (first (output-files o c))))
ただし、...の部分は当該のコンポーネントに対応します。上の例では、次のような式に相当するでしょう。
(find-component "foo" "mod")
文法の詳細はdefsystemの文法で、これらのメソッドが何をしているかはASDFのオブジェクトモデル以下のオペレーションで解説されています。
defmethodも似たような定義をしていますが、action-descriptionメソッドは(ASDF 3.1.5では)defsystem内で定義することができないため、外に分離されています。action-descriptionメソッドは廃止予定のexplainインターフェースの代わりにサポートされた機能であり、オペレーションとコンポーネントを引数に取って、オペレーションの説明を返すメソッドです。この例では、コンポーネント"mod"のcompile-opがデータクックをするオペレーションであることを説明しています。
cook-dataはそれらのパス名にASDFを通じてアクセスしています。
(in-package :asdf-user)から始まっていますが、これは冗長な記述であり、不要ですし推奨もされません。しかし、より複雑なことをしたい場合には(やはり推奨はされませんが)in-packageでパッケージを移動するのが役に立つかもしれません。
cl:loadでロードするわけではないし、あなた自身もそのように使うべきではありません。システムを操作したいときにはASDFを通してアクセスするべきです。しかし、どうしても.asdファイルを直接ロードしなければならない事情があるならば、asdf:load-asdが使えます。asdf:load-asdは*package*にasdf-userを束縛してからロードを行います。最近のバージョンのSLIME(2013-02以降)は、slime-asdf拡張をロードすればload-asdに対応します。.asdファイルに対してC-c C-kでslime-compile-and-load-fileを実行したときに、適切な処理を行うでしょう。
in-packageを使うべきではありません。in-package(とその前のdefpackage)を使うのは、新しいクラス・関数・変数・マクロなどを.asdファイル中で定義する際に、名前の衝突を避けるためだけにしましょう。古いバージョンのASDFのマニュアルでは.asdファイル中でin-packageを使うような記法が推奨されていましたが、ASDF 3ではもはや推奨されていません。代わりに、ASDFの拡張を定義した上で:defsystem-depends-onを使ってそれをロードすることを勧めます。(→ defsystemの文法)
.asdファイルの中ではasdf、common-lisp、uiopパッケージのシンボルを常に使うことができます。中でもいちばん重要なのはdefsystemですが、パッケージの接頭辞を使ってasdf:defsystemと書くのは冗長で悪いやり方です。単に(defsystem ...)と書きましょう。パッケージの接頭辞を付けるのは、(asdfパッケージをuseしていないパッケージから)動的にシステム定義を生成するような場合のみにしてください。
asdf-userは実はASDF 3から使えるようになったパッケージです。ASDF 1と2には.asd内のパッケージに関しておかしな仕様がありましたが14、ASDF 3では廃止されました。そして、今やすべての処理系がASDF 3を備えていますから、ASDF 2との互換性について気にするべきではありません。我々はもはやASDF 2をサポートしていないし、あなたも無視することを勧めます。
asdf-userがuiopをuseしているのはASDF 3.1からであり、それ以前のASDF 3ではuiop/packageのみが使えました。したがって、UIOPの関数を使う場合には、いちいちuiop:という接頭辞を付けるか、システムの依存関係を(defsystem foo :depends-on ((:version "asdf" "3.1.2"))で明示するか、あるいは#-asdf3.1 (error "MY-SYSTEM requires ASDF 3.1.2")という行を加えるかすることを勧めます。
(:read-file-form "variables" :at (3 2))は、variables.lispの4番目のフォーム中の3番目のフォーム(Lispコードでは0番目から数えるので3番目のフォーム中の2番目のフォームという指定になっています)からバージョンを抽出するように指定しています。おそらく、variables.lispの4番目のフォームは(defparameter *foo-version* "5.6.7")などとなっていて、"5.6.7"がバージョンの文字列として使われるのでしょう。
ASDF 1、2(バージョン2.26まで)は、各.asdファイルに対してasdfNという一時的なパッケージを作っては削除していました。これは名前の衝突を減らすための仕様でしたが、目的にかなってもいなかったし、ものごとを複雑にしただけでした。ASDF 3は単に共通のパッケージであるasdf-userを使い、シンボル衝突の回避はCommon Lispの慣習に委ねます。asdf-userパッケージの特筆すべき点は、uiop/common-lispをuseしていることです。このパッケージはcommon-lispパッケージのすべてのシンボルを再エクスポートしていて、いくつかのCommon Lisp処理系のANSI CL違反の挙動が修正されています。しかし、Corman Lisp、GCL、Genera、MCLを使っているのでなければ、この点を気にするべきではありません。