Previous: .asdファイル中の他のコード, Up: defsystemでシステムを定義する [Contents][Index]
バージョン3.1.2から、ASDFは1ファイル1パッケージのスタイルをサポートしています。このスタイルは、1つのソースファイルに1つのシステムが対応し、システム同士の依存関係は(訳注: おそらく各ファイルの冒頭に書かれるであろう)defpackage
またはuiop:define-package
から推測されるという仕組みです。
このスタイルでは、まずパッケージは同じ名前(ただし小文字化される)のシステムに対応します。そして、:class package-inferred-system
を指定されたシステムが存在する時、そのシステム名から始まるシステム(セパレータは/
)は元のシステムの場所からシステム名に基づいて辿ったパスにあるファイルに対応します。後者は例を挙げたほうがわかりやすいでしょう。システムmy-lib
が:class package-inferred-system
オプションつきで/foo/bar/my-lib/my-lib.asdに定義されているとします。この時、システムmy-lib/src/utility
はファイル/foo/bar/my-lib/src/utility.lispに対応するというわけです。
1ファイル1パッケージのスタイルは、以前にfaslpath
やquick-build
といったツールで一般的になった方法であり、パッケージの規則を厳格化することと引き換えにメンテナンスしやすいコードを書くことができます。ASDF 3からは、このスタイルはASDF自身に採用されていますし、lisp-interface-library
やいくつかの他のライブラリでも使われています。
ではこのスタイルによるシステム定義の例を見てみましょう。トップレベルのシステム名をmy-lib
とします。まずはmy-lib.asdファイルを作り、defsystem
フォームに:class :package-inferred-system
オプションを指定します。例えば次のような定義になるでしょう:
;; この例はLISP-INTERFACE-LIBRARYのlil.asdに基づいています。 #-asdf3.1 (error "MY-LIBにはASDF 3.1以降が必要です。") (defsystem "my-lib" :class :package-inferred-system :depends-on ("my-lib/interface/all" "my-lib/src/all" "my-lib/extras/all") :in-order-to ((test-op (load-op "my-lib/test/all"))) :perform (test-op (o c) (symbol-call :my-lib/test/all :test-suite))) (defsystem "my-lib/test" :depends-on ("my-lib/test/all")) (register-system-packages "my-lib/interface/all" '(:my-lib-interface)) (register-system-packages "my-lib/src/all" '(:my-lib-implementation)) (register-system-packages "my-lib/test/all" '(:my-lib-test)) (register-system-packages "closer-mop" '(:c2mop :closer-common-lisp :c2cl :closer-common-lisp-user :c2cl-user))
package-inferred-system
を扱えるのはASDF 3.1以降なので、最初のフォームはそのチェックをしています。もっとも、主要なLisp処理系でこれ以前のバージョンを含んでいるものはありませんから、おそらくもはや必要のない記述でしょう。
このスタイルでは、システムやコンポーネントによってuse
される、あるいは提供されるパッケージは、パッケージ名(の小文字化)とシステム名が一致していない場合はregister-system-packages
で登録されなければなりません。
次に、my-lib
を構成する各ソースファイルは、defpackage
かuiop:define-package
によるパッケージ定義から始まるでしょう。それらの間の依存関係は:use
や:mix
などのオプションから推論されます。例えば、interface/order.lispというファイルが次のパッケージ定義で始まるとします:
(uiop:define-package :my-lib/interface/order (:use :closer-common-lisp :my-lib/interface/definition :my-lib/interface/base) (:mix :fare-utils :uiop :alexandria) (:export ...))
ASDFはこのファイルがシステムcloser-mop
に依存していることを知り(use
しているのはcloser-common-lisp
パッケージですが、このパッケージがシステムcloser-mop
に対応していることは上で登録されています)、また、2つのシステムmy-lib/interface/definition
、my-lib/interface/base
にも依存していると判断します。
しかし、ASDFはどのようにして、トップレベルのシステムmy-lib
からinterface/order.lispにたどり着くのでしょうか? 上の例では、interface/all.lisp(と他のall.lisp)が、それ以下の階層にあるパッケージの(エクスポートされている)シンボルをまとめて再エクスポートしているでしょう。このようなファイルはuiop:define-package
で簡単に書けます:
(uiop:define-package :my-lib/interface/all (:nicknames :my-lib-interface) (:use :closer-common-lisp) (:mix :fare-utils :uiop :alexandria) (:use-reexport :my-lib/interface/definition :my-lib/interface/base :my-lib/interface/order :my-lib/interface/monad/continuation))
こうすれば、my-lib.asdの依存関係に各my-lib/.../all
システムを指定するだけで、ASDFがinterface/order.lispや他のすべての依存関係を:use-reexport
から発見してくれます。このように、:use-reexport
はエクスポートされたシンボルを「継承」するオプションであり、他にもuiop:define-package
にはこの用途で便利なオプションがたくさんあります。
また、ASDFは:import-from
オプションからも依存関係を発見できます、パッケージからシンボルをインポートすると、ASDFはそのパッケージに対応するシステムを依存関係に含めるでしょう。次の例を見てください。最初の:import-from
節ではfoo/baz
パッケージからsym1
、sym2
がインポートされていますが、この記述でASDFは、カレントシステムがシステムfoo/baz
に依存していることを推論します。いっぽう、特にシンボルをインポートせず、foo/quux:bletch
のようにパッケージの接頭辞付きで使いたいケースもあるでしょう。この場合は、2番目の:import-from
節のように、単に依存関係を宣言するための空インポートをします。
(defpackage :foo/bar (:use :cl) (:import-from :foo/baz #:sym1 #:sym2) (:import-from :foo/quux) (:export ...))
ASDF 3.1.5.6から、ASDFはソースファイルの場所を(:pathname
オプションで指定できる)component-pathname
を基点に探すようになりました。それ以前のバージョンではsystem-source-directory
を使っていたので、明示的に:pathname
を指定していても、.asdファイルのあるディレクトリを基点にしていました。21
[訳注] システムfoo
のディレクトリ構成として、システム定義ファイルをfoo/foo.asdに置き、ソースファイルをすべてfoo/src/以下に置く方式を選んだとしましょう。この時、foo/src/baz.lispに対応するシステムはデフォルトではfoo/src/baz
ですが、:pathname "src"
でトップレベルのパスをfoo/src/に変えるとfoo/baz
にできます。:pathname
オプションについてはdefsystemの文法を参照してください。