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を使っているのでなければ、この点を気にするべきではありません。