simple-component-name
):defsystem-depends-on
):build-operation
):weakly-depends-on
):require
:feature
):serial
):pathname
):if-feature
):entry-point
)バージョン3.3.3.3 (2019-08-02) マニュアル
このマニュアルはASDF――Common Lispプログラムとライブラリのためのシス テム定義ツール――について解説したものです。
このマニュアルの最新バージョン(訳注: 英語版)は以下にあります: https://common-lisp.net/project/asdf/asdf.html
[訳者補足]
この非公式の日本語訳は、ASDFの公式リポジトリのフォークとしてGitLab上で編集されています。(https://gitlab.common-lisp.net/hishimaru/asdf/tree/ja) 翻訳に関する訂正、意見などは常に歓迎します。その際はIssuesを使っていただけると確実です。
[訳者補足終わり]
ASDF Copyright © 2001-2019 Daniel Barlow and contributors.
This manual Copyright © 2001-2019 Daniel Barlow and contributors.
This manual revised © 2009-2019 Robert P. Goldman and Francois-Rene Rideau.
This manual translated into Japanese © 2018-2019 Hugo Ishimaru.
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 (Another System Definition Facility) はビルドシステムです。ASDFは、システムがどのようなコンポーネント(=サブシステムとファイル)を含んでいるか、それらにどの順序でどういう操作をすれば良いか記述するためのツールであり、Common Lispソフトウェアのコンパイル・ロード・テストなどを支援します。ASDFを使ったことがない方は、クイックスタートガイドを読むと良いでしょう。
このマニュアルは3つのケースを想定して書かれています: 1つ目は、ユーザーとして他人のコードを使いたい場合であり、2つ目は、開発者としてシステムをビルドする手順を記述したい場合です。そして3つ目は、(ASDFのような)Common Lisp拡張の実装者として、ビルドシステムそのものに関わりたい場合です。より具体的には、ASDFを使うにシステムをロードする方法が、defsystemでシステムを定義するにシステムを定義する方法が、ASDFのオブジェクトモデルにASDFの内部仕様と拡張方法が書かれています。
ASDFはライブラリやシステムをインストールするためのツールではありません。ASDFの役割は言うなればmake
やant
のようなものであり、パッケージマネージャとしての機能は持ちません。特に、QuicklispやASDF-InstallのようにASDFシステムを探してダウンロードするツールと、ASDFそのものとを混同しないようにしましょう。ASDF-Installは、その名に反してASDFの一部ではなく独立したソフトウェアであり、また、ずっと前からメンテナンスされておらず、使われてもいません。我々は代わりにQuicklispを推奨します。Quicklispは優秀なパッケージマネージャであり、現在もメンテナンスが続いています。もしあなたが、自分でコードを修正できるように、tarアーカイブではなくバージョン管理システムのリポジトリからソフトウェアを手に入れたい場合は、clbuildを使うと良いでしょう。
ソフトウェアをインストールする場所としては~/common-lisp/を推奨します。ASDF 3.1.2 (2014)からは、このディレクトリはソースレジストリの既定のパスに含まれています。1
なお、このマニュアルは未完成です。基本事項はすべて網羅されていますが、多くの発展的な話題についてはわずかに触れているのみですし、例もあまり多くはありません。最終的な情報源はソースコードであり、使用例についてはQuicklispに登録されているフリーソフトウェアを参照するのがベストでしょう。また、助言を得る場所としては、メーリングリストが良いでしょう。
(require "asdf")
でASDFそのものをLispイメージにロードします。ASDFのバージョンは(asdf:asdf-version)
で確認できます。これらがうまく動かない場合など、ロードの詳細についてはASDFをロードするを参照してください。
(asdf:load-system "my-system")
でお望みのシステムをロードします。(→ ASDFを使う)
(asdf:load-system "my-system")
を実行して、すべてがうまくいっていることを確認しましょう。(→ ASDFを使う)
ASDFをロードする際は、次のフォームを実行するとよいでしょう:
(require "asdf")
現在メンテナンスされているすべてのLisp処理系にはASDF 3が含まれており、require
関数でロードすることができます。2
お使いの処理系に最近のASDF 3が含まれていない場合、処理系をアップグレードすると良いでしょう。何らかの理由でアップグレードできない場合は、ASDFを置き換えることを勧めます。(→ 処理系付属のASDFを置き換える)それもできない場合はASDFをソースからロードするを参照してください。
もしあなたがアクティブにメンテナンスされているLisp処理系を使っていて、ASDFの最新安定版が付属していない場合は、ベンダーにバグレポートを送ってこの点を訴えるのも良いでしょうし、あるいは、フリーソフトウェアの場合は自分で直すのも良いでしょう。
このマニュアルの執筆時点では、次の処理系がASDF 3を提供しています: ABCL、Allegro CL、CLASP、Clozure CL、CMUCL、ECL、GNU CLISP、LispWorks、MKCL、SBCL。 次の処理系はASDF 2です: MOCL、XCL。 次の処理系はASDFを提供していません: Corman CL、GCL、Genera、MCL、SCL。 後半に挙げた処理系は現在メンテナンスされていません(おそらくGCLを除く)。それらが再びリリースされることがあれば、ASDF 3を含むことでしょう。
利便性のために、Lisp処理系が起動するときにASDFが自動でロードされるようにしたい方もいるかもしれません。これを実現するには例えば、起動スクリプトにロードの処理を加えたり、ASDFが含まれるカスタムコアをダンプしたりといった方法がありますが、詳細はお使いの処理系のマニュアルに求めるべきでしょう。また、SLIMEのslime-asdf
拡張はASDFを扱いやすくする機能として、特筆に値します。
次のフォームを実行すると、ASDFが正しくロードされたか調べることができます:
(asdf:asdf-version)
文字列が返った場合、それは現在インストールされているASDFのバージョンを表します。表示されているバージョンが十分に新しければ(例えば3.1.2以降ならば)次の章に進んでかまいません。(→ ASDFを設定する)
エラーになる場合は、ASDFがロードされていないか、あるいは、はるか昔のバージョンを使っているのでASDF 3に更新する必要があるか、でしょう。
バージョンを判定するための詳細は、How do I detect the ASDF version?を参照するとよいでしょう。
ASDFに関するトラブルに直面したときは、我々のところにイシューを立てる前に、まず下に示す方法で最新のリリースにアップグレードしてみてください。
お使いの処理系にすでにASDF 3以降が付属している場合(そのはずです)、さらに新しいバージョンを使いたければ、ほかのシステムと同様に、設定で決まっているパスにただ新しいASDFをインストールすれば良いです。 具体的には、公式のtarボールかgitリポジトリを~/common-lisp/asdf/に展開することを勧めます。3(→ ASDFがシステムを見つけられるように設定する)
ASDFのソースコードがいったん正しい場所に置かれれば、これまで通り(require "asdf")
を実行するだけで新しいASDFがロードされます。ASDF 3は、他のシステムのコンパイルを行う前に、自身の新しいバージョンが所定のパスに存在するかどうかを自動的に判断し、存在する場合には新しいバージョンを使います。
お使いの処理系にASDF 3以降が付属していない場合は、処理系付属のASDFを置き換えるを参照してください。
現在もメンテナンスされている処理系であれば最新版にはASDF 3が付属しているので、お使いのものに含まれていない場合は最新版にアップグレードすることを勧めます。
ASDFが付属していない、あるいは古いバージョンが付属している処理系を使い続けたい場合は、下記の通りに最近のASDFをインストールすることを勧めます。その際、書き込みのアクセス権を変えたり、管理者権限で実行したりといったことが必要になるかもしれません。
ASDFのソースリポジトリには、処理系のASDFをアップグレードするためのスクリプトファイルが含まれており、シェルからtools/asdf-tools install-asdf lispworks
(lispworks
は適切な処理系の名前に変えてください)で呼び出したり、REPLから(load "tools/install-asdf.lisp")
で実行したりできます。
このスクリプトが動作する処理系はAllegro CL、Clozure CL、CMU CL、ECL、GCL、GNU CLISP、LispWorks、MKCL、SBCL、SCL、XCLです。ABCL、Corman CL、Genera、MCL、MOCLでは動きません。幸いなことに、ABCLにはたいてい最新のASDFが付属しているので、このスクリプトは必要ないでしょう。GCLで動かすには最近のバージョンが必要であり、また、十分なテストがなされていません。Corman CL、Genera、MCLはもはや使われていない処理系であり、MOCLは未完成です。
もしもあなたがポータブルなビルドスクリプトを書きたいのであれば――つまり最新のASDFを備えているとは限らないような古いマシンと処理系でも動くスクリプトを書きたい場合には――asdf.lispをどこかに置いた上で、
(load "/path/to/your/installed/asdf.lisp")
でロードしなければならないかもしれません。
通常、ASDFを使うために必要なのはasdf.lispだけです。このファイルをASDF ウェブサイト上の最新リリースのtarボールから得ると良いでしょう。
バグを報告する場合には、gitリポジトリから最新版を得ることができます。最新版を手に入れるを参照してください。
スクリプトの冒頭にrequire
を置いてASDFを使おうとしたが、それがうまくいかないので難しいやり方でロードを試みる、というような場合には、ソースリポジトリにあるtools/load-asdf.lispを参考にするか、cl-launchのコードを見るとよいでしょう。
標準的な使い方をする限り、ASDFを使うのに追加設定は必要ないでしょう。以下のセクションはざっと流し読みして、自分のシステムにパスを通す方法を知り、自分に適した(Lispソフトウェアの)インストール方法を選びましょう。そして、そのままASDFを使うに進めば十分です。ASDFがオブジェクトファイルをどのように保持しているか意識しなければならないことはめったにないでしょうし、ASDFの設定をリセットする必要にせまられるのもたいていはコーナーケースにおいてのみ起こることです。
システムをコンパイル、ロードするためには、システム定義の書かれた.asdファイルをASDFが見つけられるように設定しなければなりません。これには簡単なものから複雑なものまで、多数のやり方があります:
のことです。これらの場所にソフトウェアをインストールするなら特に追加設定は必要ないので、4 次のセクションに進んでかまいません。(→ システムをロードする)
source-registry
機能を使って、ASDFがシステムを探すパスを設定するのがもっともよい方法です。この機能は1つの章を充てて(ASDFがシステムを探す場所を設定する)網羅的に解説しますので、ここでは簡単な使い方を説明するにとどめます。
まず初めに~/.config/common-lisp/source-registry.conf.d/ディレクトリ
5
を作り、このディレクトリに拡張子が.conf
6
のファイルを作ります。例えば50-luser-lisp.confという名前のファイルを作ったとしましょう。このファイルに
(:tree "/home/luser/lisp/")
という記述を加えると、ASDFは/home/luser/lisp/以下のディレクトリを再帰的にスキャンして、すべての.asdファイルを見つけるようになります。
これで完了です。/home/luser/lisp/はソースコードをインストールしたい場所に自由に置き換えてください。ASDF 3.1.2以降で~/common-lisp/を使う限り、このような設定は必要ありませんが、それ以前のASDF 3を使っている場合、最近のASDFを~/common-lisp/asdf/に置いた上で、ブートストラップのために.confファイルに(:tree (:home "common-lisp/"))
を加える、というようなテクニックが必要かもしれません、
別のやり方として、1つのディレクトリに.asdファイルへのリンクを集める方式もあります。この方式は上記の再帰的なスキャンに比べると速いですが、管理は大変になります。リンクを集めるディレクトリを/home/luser/.asd-link-farm/としましょう。この場合は、ソースレジストリディレクトリに例えば42-asd-link-farm.confファイルを作り、(:directory "/home/luser/.asd-link-farm/")
という行を加えれば有効になります。
ASDFは初めてシステムを探すときに自動的に.confファイルを読み込みますが、必要であれば、
(asdf:clear-source-registry)
でソースレジストリの設定をリセットすることができます。(→ ソースレジストリのAPI)
asdf:*central-registry*
を使って設定されていました。詳細については次のセクション(ASDFがシステムを見つけられるように設定する(古いスタイル))を参照してください。これについて知る必要がなければ、飛ばしてASDFがオブジェクトファイルを保持する場所を設定するに進みましょう。
なお、あなたのOSディストリビューションやシステム管理者が、既にシステム全体のライブラリ管理を設定しているというケースもあることに注意してください。
初心者はこのセクションを飛ばしてかまいません。初心者はasdf:*central-registry*
を使わないでください。また、初心者にasdf:*central-registry*
を使うよう教えないでください。
古いASDFでは、システムを探す場所を設定するのに、asdf:*central-registry*
にディレクトリのパス名をpush
する、という方法が使われていました。
この変数を設定するのは、ASDF 3以降をロードした後、ASDFの機能を使う前でなければなりません。このロードと設定は、初期設定スクリプトの一部として実行されなければなりません。典型的には、自分のプロジェクトをビルドするスクリプトか、処理系の初期設定スクリプト(例: SBCLの~/.sbclrc)がこれにあたります。
また、あなたがASDF 2以前を使っていて、ASDF 3以降を後からロードするという場合は、ASDF 1または2がロードされた後にASDF 3の場所をasdf:*central-registry*
で設定し、その上でASDF 3を(asdf:operate 'asdf:load-op "asdf")
などでロードし、ロード後に再びasdf:*central-registry*
を設定する、という流れになります。というのも、ASDF 2からASDF 3へアップグレードする時にasdf:*central-registry*
は保持されないからです。もっとも、前セクションの方法で設定されたソースレジストリはアップグレードの影響を受けないので、おそらくそちらを使ったほうが良いでしょう。(ただし、ASDF 2でasdf:initialize-source-registry
を引数付きで実行した場合は別で、この場合はASDF 3で再び実行する必要があります。)しかし我々は、あなたがASDF 2以前を使っている場合は、処理系をアップグレードするなり、ASDFを最近のバージョンに置き換えるなりすることを強く推奨します。(→ 処理系付属のASDFを置き換える)
asdf:*central-registry*
は、ASDF 2または3ではデフォルトで空であり、ASDF 1との互換性のために残されています。この変数を使った設定は、上記のソースレジストリの設定に優先します。7
例として、/home/me/src/foo/foo.asdがASDFに見つかるようにしてみます。お使いの処理系の初期設定ファイルに、次のようなフォームを加えてください:
(require "asdf") (push "/home/me/src/foo/" asdf:*central-registry*)
末尾のスラッシュが必要なことに注意してください。システムを探すとき、ASDFはasdf:*central-registry*
のそれぞれの要素をパス名に変換します。正確に言うと、それぞれの要素はeval
されます。つまり、S式をasdf:*central-registry*
にpush
することも可能です。文字列やパス名は自己評価オブジェクトなのでeval
は何もしませんが、S式を使うと、シェル変数やユーザーに応じた値を返すなど、コンテキストに依存した設定をすることができます。なお、nil
に評価された場合、その要素はスキップされます。
このように、asdf:*central-registry*
は「システムディレクトリ指示子」のリストであると言えます。システムディレクトリ指示子(system directory desinator)はシステムを探すときに実行されるフォームであり、ディレクトリ(あるいはnil
)を返さなくてはなりません。ここでいう「ディレクトリ」とは「空でないディレクトリコンポーネントを持つパス名の指示子」8のことです。
パス名の末尾のスラッシュは、ファイルではなくディレクトリを指していることを示すのに必要です。このスラッシュがないと、(古い)ASDFは/home/me/src/foo/
ではなく/home/me/src/
を探してしまうおそれがあります。現在のバージョンでは、単にエラーを通知し、asdf:*central-registry*
からこのエントリを取り除くリスタートが起動するでしょう。
多くの場合、.asdファイルは大量にありますから、それらの存在するディレクトリを1つ1つasdf:*central-registry*
にpush
するよりは、各.asdファイルのシンボリックリンクを1つの共通ディレクトリに置き、そのディレクトリだけをpush
するという方法が使われていました。ASDFはシステムへのシンボリックリンクを適切に解釈することができます。9
例えば#p"/home/me/cl/systems/"
が*central-registry*
に含まれているとき、次のようにセットアップすればfooシステムを使えるようになります。
$ cd /home/me/cl/systems/ $ ln -s ~/src/foo/foo.asd .
なお、このセクションの設定方法は古いやり方であり、新しいユーザーには推奨されません。古くからのユーザーや、ASDFのパスをプログラムによって簡単にコントロールしたいユーザーのために残されています。
ASDFがオブジェクトファイルを保存する場所は、あなた自身が設定することもできますが、デフォルトの設定は適切に考えられたものになっているので、通常のケースではこの領域を気にするべきではありません。
デフォルトの仕組みによって、複数のLisp処理系の複数のバージョン間で同じソースコードリポジトリを共有することができますし、さらには複数のユーザー(ソースディレクトリへの書き込み権限のないユーザーを含む)、複数のコンパイルオプションなども区別可能です。また、ソースコードのディレクトリがオブジェクトファイル(faslファイル)で散らかることもありません。
ASDF 2以降にはasdf-output-translations
機能が備わっていて、オブジェクトファイルが保存される場所をコントロールすることができます。この機能についてはASDFがコンパイルされたファイルを保持する場所を設定するで網羅的に解説します。
ASDF 2以前では、同様の機能はASDF-Binary-Locations、cl-launch、common-lisp-controllerなどといった、それぞれ微妙に異なる非互換な方式で提供されていました。ASDF-Binary-Locationsはもはや不要な拡張なので使うべきではありませんし、cl-launch 3.000とcommon-lisp-controller 7.2はオブジェクトファイルの配置をASDFに委ねています。
Lispのコアイメージをダンプしてから復元するときや、設定を変更するときなどは、いったんASDFの設定をリセットしたいことがあるでしょう。このために次の関数があります。
ASDFの設定をデフォルトに戻します。具体的には、ソースレジストリとアウトプットトランスレーションの設定が消去されます。10
この関数はデフォルトでuiop:*image-dump-hook*
にプッシュされているので、uiop:dump-image
やasdf:image-op
、asdf:program-op
などを使ってイメージを保存するときには、自動的に呼ばれて設定をクリアします。しかし、UIOPではなく処理系依存の機能を使ってイメージをダンプする場合には、この関数は呼ばれないので、手動で実行するか、あるいはuiop:*image-dump-hook*
と同等の処理系依存のフックを使うことになります: SBCLではsb-ext:*save-hooks*
が、CMUCLとSCLではext:*before-save-initializations*
がこれに相当します。
fooという名のシステムは、次のフォームを実行することでロード(必要ならコンパイルも)されます。
(asdf:load-system :foo)
処理系によっては、cl:require
にASDFがフックされているので、単に
(require :foo)
でロードできます。(→ 便利な関数)
標準のシステム名は文字列であり、すべて小文字です。システム名を指定する時にはシンボル(キーワード含む)も使えます。シンボルはsymbol-name
で文字列に変換され、さらに小文字に変換されます。また、システム名の文字列はmake-pathname
の:name
引数として(システムがスキャンされるファイルシステム上で)有効でなければなりません。
小文字を標準とする仕様はCLの慣習と違っていますが、検討の末このような仕様になりました。我々がサポートするシステムには、小文字を使うのが通例のもの(Unix、Mac、Windows)と、小文字を暗黙に大文字に変換するもの(ANSI CLの論理パス名)があるからです。
システムに対するもっとも基本的なオペレーションについては、次の3つのコマンドが用意されています: load-system
、compile-system
、test-system
load-system
の変種としてrequire-system
があり、後者はシステムが既にロードされている場合はロードを行いません。これは例えば、処理系のコアイメージに最初から含まれているライブラリの再ロードを避けるためなどに使えます。
これらに加えてmake
という関数もあり、これを呼んだときのオペレーションはシステム開発者が選択することができます。何も指定されていない場合はload-system
と同じオペレーション(load-op
)を実行しますが、例えば、ドキュメントを出力するためのシステムを作った場合、make
で(ロードではなく)ドキュメントを出力するようにできます。
ASDFは拡張可能なシステムであり、コンポーネントに対する新しいオペレーションを定義することもできます。オペレーション一般を呼び出す総称関数としてoperate
があり、デフォルトのものだけでなく任意のオペレーションを扱うことができます。(operate
のエイリアスとしてoos
が定義されているので、REPL上ではこちらがよく使われます。oos
はoperate-on-systemの略でmk-defsystem
11から受け継いだ名前です。) コンパイル・ロード・テスト以外のオペレーションを行いたい場合にはoperate
を使うと良いでしょう。
load-op
、system、キーワード引数にoperate
を適用します。システムを現在のイメージにロードしたい場合は、load-system
を呼び出すのがもっとも標準的で推奨されるやり方です。
compile-op
、system、キーワード引数にoperate
を適用します。この関数はシステムのすべてのファイルをコンパイルしますが、必ずしもそれらを現在のイメージにロードするとは限りません(が、しないとも限りません)。実際のところ、ほとんどのシステムでは、すべてのオブジェクトファイルがロードされるということはないでしょう。この関数はload-system
との対称性のために存在しますが、挙動をちゃんと理解した上でビルドスクリプトを書く、という場合以外は使わないほうがよいです。もっとも、そのようなケースではcompile-op
よりもprogram-op
のほうが適切なこともあるでしょう。
test-op
、system、キーワード引数にoperate
を適用します。test-opの解説を参照してください。
ASDF 3.1から加わった関数であり、システムに対して何らかのオペレーションを実行します。デフォルトの動作はload-system
と同じですが、システム開発者はシステム定義の中で実行すべきオペレーションを指定することができます。具体的には、defsystem
フォームの:build-operation
にオペレーションを指定し、:build-pathname
に出力先のパス名を指定します。(→ ビルドオペレーション)
この関数は実験的であり、テストが十分ではありませんので、自己責任で使ってください。
require-system
はcl:require
と類似の動作をします。つまり、既にロードされているシステムを(例えシステムが更新されていても)ロードしません。同じ動作はload-system
にロード済みシステムを除外するようにキーワード引数を指定しても可能です。12 現在メンテナンスされているフリーの処理系(ABCL、Clozure CL、CMUCL、ECL、GNU CLISP、MKCL、SBCLの最近のバージョン)では、いったんASDFがロードされると、cl:require
でもASDFシステムをロードできるようになりますが、その動作は、cl:require
が処理系の知らないモジュール名を受け取ったときはrequire-system
に渡す、という仕組みになります。(逆にrequire-system
がcl:require
を呼び出すことはありません。潜在的に無限ループを生んでしまう可能性があるからです。)
cl:require
やrequire-system
は、現在のセッションでは修正されていないコードをロードするのに向いています。この2つの違いを説明すると、cl:require
は処理系の提供する拡張モジュールをロードするのに使われますが、require-system
は普通はその用途では使えません。(ただし、SBCLやMKCLのように、処理系の拡張モジュールがそもそもシステムとして定義されている場合はrequire-system
でもロードできます。)また、あなたが開発もデバッグもしていないシステムで、インストール済みのバージョンを使えば問題ないと考えられる場合にもrequire-system
(あるいは、上に挙げた処理系ではcl:require
)でロードするとよいかもしれません。
しかし、あなたが開発、デバッグしていたり、あるいは何にせよ変更を行ったシステムに対してはload-system
を使うべきです。load-system
は変更されたファイルとその依存関係に基づいて適切に再ビルドを行います。(指定されたシステムとそれが依存しているシステムです、それ以外のビルドは行いません。)13
これまでにロードされたシステムの名前のリストを返します。
ここまでの解説で、他人が作ったシステムをロードするのに必要な情報は網羅されています。以降は、あなた自身が作ったLispソフトウェアにシステム定義を書くための知識を扱います。また、新しいオペレーションやコンポーネントを定義してASDFを拡張するための情報も含みます。
この章では、ASDFを使ってシステムを定義し、ソフトウェア開発を行う方法を解説します。
まずシステム定義の例を挙げ、後のセクションでdefsystem
の完全な文法を解説します。
それでは単純なシステムを定義してみましょう。次の例はhello-lisp.asdというファイルに保存されます。(ASDFが"hello-lisp"
という名のシステムを処理するように指示されたときに、この定義を見つけられるようにするためです。)
;; Lispのコメントを書くことができます (defsystem "hello-lisp" :description "hello-lisp: a sample Lisp system." :version "0.0.1" :author "Joe User <joe@example.com>" :licence "Public Domain" :depends-on ("optima.ppcre" "command-line-arguments") :components ((:file "packages") (:file "macros" :depends-on ("packages")) (:file "hello" :depends-on ("macros"))))
それでは例を説明していきます。
defsystem
フォームはhello-lisp
という名前のシステムを定義します。このシステムはpackages.lisp、macros.lisp、hello.lispという3つのソースファイルを含んでいます。
.asd
ファイルと同じディレクトリにあります。
optima.ppcre
(正規表現のマッチングを行うための便利なインターフェースを提供するライブラリ)とcommand-line-arguments
(シェルコマンドから与えられた引数をパースするライブラリ)です。つまり、hello-lisp
を使うためには、ASDFがこの2つのシステムのある場所を見つけられるように設定されていなければなりません。ASDFはhello-lisp
をコンパイル、ロードする前にこの2つのシステムをロードするでしょう。
:bug-tracker
, :mailto
、:long-name
、:long-description
、:source-control
などが使えます。これらの使用は任意ですが、少なくとも:description
、:version
、:author
、:licence
は定義しておくことを強く推奨します。(特にQuicklispにソフトウェアを登録するつもりであれば。)
:version
の文字列はASDFにパースされるので、なんでもよいわけではありません。今のところは、ピリオド.
で区切られた非負整数のみが許容されています。(→ バージョン指定子)
defsystem
フォームだけを含んでいて、in-package
もなければ、パッケージの接頭辞asdf:
も付いていません。これが我々の推奨する書き方です。より複雑なシステム定義ファイルを書きたければ任意のLispコードが使えますが、可能ならばシンプルな定義に保っておくことを勧めます。そのほうが堅牢で、将来においても有効なシステム定義になるでしょうから。
以上のことを知っていれば、単純なシステムを定義するには十分です。次のセクションの例はずっと込み入っているので、複雑なことをしたいときの助けになるかもしれません。しかし、究極的には任意のLispコードが書けるのですから、完全な解説をすることはできませんが。
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"
がバージョンの文字列として使われるのでしょう。
system-definition := ( defsystem system-designator system-option* ) # → システム指示子 system-designator := simple-component-name | complex-component-name # アンダースコアは許容されていません。→ 単独のコンポーネント名 simple-component-name := lower-case string | symbol complex-component-name := string | symbol # → 複合的なコンポーネント名 system-option := :defsystem-depends-on system-list | :weakly-depends-on system-list | :class class-name # → システムクラス名 | :build-pathname pathname-specifier | :build-operation operation-name | system-option/asdf3 | module-option | option # このオプションはASDF 3以降(正確には、そのアルファリリースである2.27以降)でのみ使えます。 system-option/asdf3 := :homepage string | :bug-tracker string | :mailto string | :long-name string | :source-control source-control | :version version-specifier # → バージョン指定子 | :entry-point object # → エントリーポイント source-control := ( keyword string ) module-option := :components component-list | :serial [ t | nil ] option := :description string | :long-description string | :author person-or-persons | :maintainer person-or-persons | :pathname pathname-specifier # → パス名指定子 | :default-component-class class-name | :perform method-form | :explain method-form | :output-files method-form | :operation-done-p method-form | :if-feature feature-expression | :depends-on ( dependency-def* ) | :in-order-to ( dependency+ ) # → Controlling file compilation person-or-persons := string | ( string+ ) system-list := ( simple-component-name* ) component-list := ( component-def* ) component-def := ( component-type simple-component-name option* ) # component-typeが:moduleであればmodule-optionも使えます。 component-type := :module | :file | :static-file | other-component-type other-component-type := symbol-by-name # → コンポーネント型 # dependency-defは:depends-onの中で使われます。 dependency-def := simple-component-name | ( :feature feature-expression dependency-def ) # → フィーチャーに応じた依存関係 | ( :version simple-component-name version-specifier ) | ( :require module-name ) # dependencyは:in-order-toの中で使われます。 dependency := ( dependent-op requirement+ ) requirement := ( required-op required-component+ ) dependent-op := operation-name required-op := operation-name # [訳注] 現状では required-component := dependency-def と考えて良さそうです。 # パス名はすべて小文字であるべきです。アンダースコアを含むべきではありませんが、 # ハイフンは許容されます。 pathname-specifier := pathname | string | symbol version-specifier := string | ( :read-file-form pathname-specifier form-specifier? ) | ( :read-file-line pathname-specifier line-specifier? ) line-specifier := :at integer # 0-based form-specifier := :at [ integer | ( integer+ ) ] method-form := ( operation-name qual lambda-list &rest body ) qual := method-qualifier? method-qualifier := :before | :after | :around feature-expression := keyword | ( :and feature-expression* ) | ( :or feature-expression* ) | ( :not feature-expression ) operation-name := symbol
システム指示子は単独のコンポーネント名(例: "foo"
)か、またはスラッシュで区切られた複合的なコンポーネント名(例: "foo/bar/baz"
)です。
simple-component-name
)単独のコンポーネント名は文字列またはシンボルで指定することができます。
文字列を使う場合は、小文字のみを使ってください。
シンボルはsymbol-name
で文字列に変換され、さらに小文字に変換されます。言い換えれば、シンボルは指示子として有効ですが、コンポーネント名そのものは文字列だということです。
文字列であれシンボルであれ、コンポーネント名にアンダースコアを含めてはいけません。
また、単独のコンポーネント名にスラッシュ/
を使ってはいけません。スラッシュは複合的なコンポーネント名を表すのに使われます。この点については次のサブセクションを参照してください。スラッシュが不適切に使われている場合、ASDFは警告を発するでしょう。
これらの制限を犯した場合、つまり、大文字と小文字を混ぜたり、アンダースコアを使ったりした場合、ASDFがシステムやコンポーネントを見つけるのが不可能になる可能性があります。というのも、コンポーネント名はファイル名として解釈されるからです。特に、論理パス名を使うようにASDFを設定したユーザーは、確実にこのような問題に直面するでしょう。
複合的なコンポーネント名は、いくつかの名前を階層に従ってスラッシュ区切りで並べたものです。この仕組みは、1つの.asdファイルに複数のシステム定義を置いた場合に、ASDFがそれらを見つけられるようにするためにあります。
この際、最上位の名前(master name)は.asdファイルの名前と一致していなければなりません。例えば、foo.asdファイルにはシステムfoo
が定義されているでしょうが、それに加えてfoo/test
やfoo/docs
などのシステムも定義することが可能です。ASDFは、システムfoo/test
をロードするように要求されたとき、foo.asdファイルを探せばよいことを知っているというわけです。
コンポーネント型の名前は、(仮にキーワードで指定されたとしても)まずカレントパッケージのシンボルから検索されます。カレントパッケージに見つからなければasdf
パッケージから検索されます。つまり、カレントパッケージmy-system-asd
に属するコンポーネント型my-component-type
は、:my-component-type
またはmy-component-type
で指定することができます。
system
とそのサブクラスは、システムの子コンポーネントのコンポーネント型として使うことはできません。
システムクラス名はコンポーネント型と同じように検索されますが、ここではsystem
とそのサブクラスのみを使うことができます。典型的な用途としては、何らかのASDF拡張で定義された標準外のシステムクラスを使う場合に、下述の:defsystem-depends-on
でその拡張モジュールをロードしつつ、:class
にクラス名を指定します。ASDFパッケージの中でこのクラス名を指定するときは、シンボル名の衝突を防ぐために
:class :MY-NEW-SYSTEM-SUBCLASS
のようにキーワードを使うことを推奨します。キーワードではなくMY-NEW-SYSTEM-SUBCLASS
で指定した場合、この名前のシンボルがカレントパッケージに読み込まれたあと、(ASDF拡張が:defsystem-depends-on
によってロードされて、そのパッケージから)同名のシンボルがエクスポートされる、という順序で処理が行われるため、名前の衝突が起きてしまいます。15
:defsystem-depends-on
):defsystem-depends-on
オプションを使うと、システム定義が処理される前に、ASDFで定義された別のシステム(またはシステムのまとまり)をロードすることができます。このオプションは典型的には、システム定義の中でASDFの拡張をロードするために使われます。
:build-operation
):build-operation
オプションには、システムに対してmake
(→ make)を実行したときに、どのオペレーションを適用するか指定することができます。デフォルトではload-op
が使われます。このオプションの値はオペレーションの名前でなければなりません。(例えば:build-operation doc-op
など)
この機能は実験的でテストが不十分です。自己責任で使ってください。
:weakly-depends-on
)この機能を使うことは推奨しません。システムbar
に弱依存しているシステムfoo
を作りたい場合は、パラメトリックにfoo
を定義して、スペシャル変数やフックで動作を変えられるようにすると良いでしょう。そして、システムfoo+bar
を定義してまとめてフックできるようにしましょう。
(廃止予定の):weakly-depends-on
オプションで指定されたシステム(あるいはそのまとまり)については、ASDFはそれらのロードを試みますが、ロードの成功が必須であるとはみなしません。このオプションは典型的には、システムの基本的な機能には必要ないが付加的な機能をもたらす、という依存関係を指定するのに使われます。
上の文法定義では、このオプションはdefsystem
にのみ指定されるものとなっていますが、今のところはどのコンポーネントにも指定可能です。しかし、defsystem
直下以外に指定するのはおそらく無意味でしょうし、この(例外的な)挙動は将来、警告なしに変わる可能性があるので、プログラマの方にはdefsystem
直下以外に指定しないように念を押しておきます。
パス名指定子(pathname specifier)はパス名、文字列、シンボルのいずれかですが、多くの場合、コンポーネントは:pathname
オプション無しで記述されるでしょう。省略された場合にはコンポーネント名そのものが使われます。
普通は文字列を与えますが、文字列はUnixスタイルのパス名として解釈され、文字/
はディレクトリのセパレータとみなされます。たいていは相対パスが使われ、その場合は親コンポーネントのパスを基点にした相対パスとみなされます。文字列はコンポーネント型に応じてファイルともディレクトリとも解釈されますが、ファイルの場合、コンポーネント型によっては自動的に拡張子が付加されることがあります。
例えば、コンポーネント型の1つである:module
は、ディレクトリを対象とするので、文字列"foo/bar"
はパス名#p"foo/bar/"と解釈されます。
また、コンポーネント型の1つである:file
は、ファイル形式がlispのファイルを対象とするので、文字列"foo/bar"
はパス名#p"foo/bar.lisp"と解釈され、"foo/bar.quux"
は#p"foo/bar.quux.lisp"と解釈されます。16
最後に、コンポーネント型の1つである:static-file
は、特定のファイル形式を前提とせずにファイルを対象にとるので、文字列"foo/bar"
はパス名#p"foo/bar"と解釈され、"foo/bar.quux"
は#p"foo/bar.quux"と解釈されます。
".."
はパス名のディレクトリコンポーネント中の:back
と解釈されます。17
つまり、文字列中の".."
は1つ上の階層のディレクトリを意味します。
シンボルが与えられた場合、小文字の文字列に変換されます。小文字に変換するのは慣習と異なっていますが、検討の末このような仕様になりました。我々がサポートするファイルシステムには、小文字を使うのが通例のもの(Unix、Mac、Windows)と、小文字を暗黙に大文字に変換するもの(ANSI CLの論理パス名)があるので、単にmake-pathname
を:case :common
として使う方式(うまく動かない処理系もある)よりも適切と思われます。
システム名やコンポーネント(モジュール、ファイル)名にアンダースコアを使うのは避けてください。論理パス名がアンダースコアに対応していないためです。(→ 論理パス名を使う)
パス名オブジェクトを与える場合、一般的には#p
や#.(make-pathname ...)
のようなリーダーマクロが使われるでしょう。しかし、#p...
は#.(parse-namestring '...)
と同等であり、parse-namestring
は論理パス名を使わない限りまったくポータブルではないし、論理パス名もまた別のポータブルでない挙動を含んでいるという問題があります。(→ 論理パス名を使う) また、たいていは、パス名を#.(make-pathname ...)
で作るより、上述の文字列を使う方式のほうが簡単です。本当にパス名オブジェクトを使う必要があるのは、コンポーネントのデフォルトのファイル形式(拡張子)を上書きする必要がある場合のみです。従って、パス名オブジェクトはめったに使われないでしょう。残念ながらASDF 1には、コンポーネント名そのものをディレクトリを指す文字列としてパースして適切に扱う仕組みが無かったので、#.(make-pathname ...)
という面倒な書き方をしなければなりませんでした。リード時評価#.
の代わりに(eval `(defsystem ... ,pathname ...))
で代用することも可能です。
文字列が与えられた場合にコンポーネント型が考慮されてパス名が解釈されるのと異なり、パス名オブジェクトが与えられた場合はそのまま使われるということに注意してください。つまり、パス名オブジェクトを使うなら、コンポーネント型の制約を満たすように(例えばディレクトリや特定のファイル形式が指定されるように)あなた自身が気を配って書かなければなりません。他方では、ファイル形式の制約をあえて無視するために使うこともできます。
バージョン指定子(version specifier)は文字列であり、ピリオド#\.
で区切られた整数としてパースされます。例えば、"0.2.1"
は大ざっぱに言えば(0 2 1)
というバージョンと解釈されます。留意点として、"0.0002.1"
は"0.2.1"
と同じバージョンと判断されますが、前者は正規の書き方ではないので、警告が通知されるでしょう。また、バージョンは小数として扱われるわけではないので、"1.3"
や"1.4"
は"1.30"
と比較してバージョンが小さい((uiop:version< "1.3" "1.30")
が真を返す)ことにも注意してください。
:version
引数には、バージョンを文字列リテラルで直接指定する代わりに、他の場所からバージョン文字列を抽出するための表現を与えることも可能で、次のようなDSLが用意されています: (:read-file-form <パス名または文字列> [:at <access-at-specifier>])
あるいは(:read-file-line <パス名または文字列> [:at <access-at-specifier>])
。名前が示すように、前者は与えられたパス(相対パス名やunix-namestring
で与えられた場合はシステムのパスをベースに解釈される18)から1つのフォームを読み、後者は1つの行を読みます。:at
引数にはuiop:access-at
と同様の指定をすることができますが、デフォルトでは0
、つまり最初のフォーム/行を読み込むことになります。(訳注: 記述の例は複雑な例にあります。) また、:read-file-form
にはサブフォーム、つまりフォーム中のフォームを指定することもできます。例えば(1 2 2)
は「2番目のフォーム中の3番目のフォーム中の3番目のフォーム」を指定しています。この指定は0番目から数えることに気をつけましょう。
システムを定義する際には、x.y.zという形式でバージョンを指定し、それぞれメジャーバージョン、マイナーバージョン、パッチレベルに対応させることを推奨します。APIの重要な非互換が発生する場合は、メジャーバージョンを上げてそのことを示しましょう。
:require
依存モジュールをロードするのに(ASDFのload-op
ではなく)処理系のrequire
関数を使う場合には、(:require モジュール名)
という指定をします。
特定の処理系でのみ、その提供モジュールに依存しているという場合には、単に#+処理系名 (:require モジュール名)
と書くよりは、(:feature 処理系名 (:require モジュール名))
を使うほうが良いスタイルと言えます。(→ フィーチャーに応じた依存関係)
:feature
)フィーチャーに応じた依存関係は(:feature feature-expression dependency)
というフォームで指定します。システム定義がパースされる時にfeature-expressionが満たされれば(訳注: 例えば、*features*
にfeature-expressionが含まれれば)依存関係が使われ、満たされない場合は無視されます。
ただし、:feature
は今定義しているシステムが特定のフィーチャーに依存していることを示すために使うことはできないという点に注意しましょう。すなわち、システム定義をロードするのに必要なフィーチャーを指定することはできません。例えば、システムをSBCLでしか使えないようにしようとして(:feature :sbcl)
と書いても無意味です。
なお、フィーチャーに応じた依存関係を、if-feature
や廃れた機能であるfeature requirement(廃止)と混同しないようにしましょう。
我々は一般に、論理パス名を使うことを推奨しません。(最近Common Lispを使い始めた人にはなおのことです。) しかし、論理パス名を好む古参の方のためにサポートはしています。
論理パス名を使うなら、コンポーネントの定義中の:pathname
に対して#p"LOGICAL-HOST:absolute;path;to;component.lisp"
などといった文法でパス名オブジェクトを与える必要があるでしょう。
論理パス名はシステムや親コンポーネントに指定すれば十分です。それらに属する子コンポーネントには特にパスを指定しなくても、名前を相対パスとして親コンポーネントのパスと適切にマージされます。なお、:pathname
に文字列を与える場合は、論理パス名のホストの指定をすることはできません。
asdf-output-translations
レイヤー(→ ASDFがコンパイルされたファイルを保持する場所を設定する)は論理パス名の解決・解釈をすることを避けます。この仕様の良いところは、論理パス名をどのように解釈するかあなた自身で決められることですが、解釈を定義しなかった場合に、論理パス名を含むシステムが異なる環境のasdf-output-translations
で異なる振る舞いをするという欠点もあります。
したがって、論理パス名を使いたい時は、使われる前にその解釈(logical-pathname-translations
)を設定する必要があるでしょう。ASDFには今のところ、論理パス名の解釈を決めるための機能がありません。
我々が論理パス名の使用を推奨しない理由としては、(1) 論理パス名が使われる前にその解釈を設定するポータブルな方法がないことと、(2) 論理パス名には1つのレターケース(大文字か小文字のどちらか)、数字、ハイフンしか使えないことがあります。1つ目の問題はあなた自身で解決することもできるでしょうが、15の処理系についてそれぞれどのように対処するかは、我々がドキュメントとして書ける範囲を超えています。また、2つ目の制約はSBCLが要求しているので、ポータビリティのためには無視することができません。したがって、この制約を侵す物理パス名を参照したい時は、あなた自身が何らかのエンコーディングを定義して独立したマッピングを追加しなくてはなりません。とりわけ、大規模プロジェクトの一部としてLispファイルが含まれる場合、この仕様は悩みの種になるかもしれません。典型的な例としては、プロジェクトのファイル、ディレクトリ名にバージョン番号が含まれる場合や、あるいは他のプログラミング言語で書かれたソフトウェアが共存していて、ファイル名にアンダースコアやドット、キャメルケースが使われる場合などがあります。
:serial
)モジュールの定義で:serial t
オプションを指定すると、そのモジュールの子コンポーネントは常に前の子コンポーネントに依存しているとみなされます。これは:depends-on
でいちいち依存関係を指定するのと同じことになります。つまり、
:serial t :components ((:file "a") (:file "b") (:file "c"))
は次の定義と同等です。
:components ((:file "a") (:file "b" :depends-on ("a")) (:file "c" :depends-on ("a" "b")))
:pathname
)defsystem
でシステムを定義する際、:pathname
オプションを任意で指定できますが、通常は必要ありません。単純なプロジェクトでは、ソースファイルはシステムと同じディレクトリにあるでしょうし、モジュールを使う場合はモジュールと同じ名前のサブディレクトリにあるでしょうから。
より詳細に説明すると、ASDFは次の挙動を実現するために、複雑なルールに従っています。
find-system
はシステムをディスクからロードして、そのデフォルトのパス名19を正しく設定します。
*default-pathname-defaults*
(この場合、まったく別の場所を指しているかもしれません)によって書き換えられることはありません。
システムが初めて定義される場合、そのトップレベルのパス名は次のように決まるでしょう:
*load-truename*
にパス名が束縛されている場合、そのホスト、デバイス、ディレクトリが使われます。
*load-truename*
にパス名が束縛されていない場合は、*default-pathname-defaults*
が代わりに使われます。
システムが再定義される場合、トップレベルのパス名は次のように決まります:
*load-truename*
にパス名が束縛されている場合は、新しいソースの場所を反映するように更新されます。
*default-pathname-defaults*
によってセットされていた場合は、更新されます。
*load-truename*
によってセットされていて、現在は*load-truename*
がnil
である場合は、更新されません。この仕様によって、開発者はdefsystem
フォームを(ソースの場所に影響を与えずに)エディタ内で評価することができます。
:if-feature
)このオプションは、特定のコンポーネントをビルドに含めるかどうかを(#+
のように)フィーチャーに応じた条件で指定することができます。フィーチャーが満たされない場合、そのコンポーネントは無視され、また、他のコンポーネントがそのコンポーネントに依存するという記述も無視されます。リード時に評価される#+
と異なり、このオプションを使うと、コンポーネントの階層にオブジェクトを追加し、そのオブジェクトをプロジェクトをビルドする際に利用したり、システム構造について推論したい外部のコードからアクセスしたりといったことができます。
プログラマはいつ:if-feature
が評価されるか知っておくべきです。ASDFは最初にビルド全体の計画を建て、次にその計画を実行するという順で動作しますが、フィーチャーが満たされるかどうかは計画時にチェックされます。従って、ビルド中に*features*
を変更して:if-feature
に影響を与えるということはできません。:if-feature
はビルドオペレーションが実行される直前の*features*
の状態だけをチェックするのです。
このオプションはASDF 3で追加されました。詳細な情報については、必要なフィーチャーを参照してください。
:entry-point
):entry-point
オプションには、program-op
で実行可能ファイルを作る際のエントリーポイントを指定することが出来ます。
program-op
が呼ばれた時、このオプションに与えられたフォームはuiop:ensure-function
で関数に変換され、uiop:*image-entry-point*
に束縛されます。uiop:ensure-function
は任意のオブジェクトを引数に取れますが、このオプションでは一般に"foo:main"
のような文字列が使われ、その場合、実行可能ファイルはfoo:main
関数から開始することになります。なお、シンボルfoo:main
を使うとうまく動かない可能性があることに注意してください。なぜなら、ASDFがdefsystem
フォームをリードする時にfoo
パッケージが既に存在しているとは限らないからです。program-op
に関する詳細は組み込みのオペレーションを参照してください。
この機能はASDF 3.1で廃止されたので、使わないでください。ほとんどの場合は:if-feature
(→ if-featureオプション)で代替できるでしょう。
この機能は、特定のフィーチャーが欠けているときに、コンポーネントの依存関係の連鎖が断たれるようにするためのものでした。:if-component-dep-fails
と併せて使うと、条件つきのコンパイルを(遠回りではありますが)記述することができたのです。
.asdファイルは、load
で実行可能な通常のLispのソースファイルであり20、任意のLispコードを含むことができます。しかし、追加のフォームは最小限にとどめ、複雑な処理が必要な場合はdefsystem
の拡張を別に定義して:defsystem-depends-on
で読み込むことを推奨します。
とは言っても、.asdファイルにコードを含めなければならない場合もあるでしょう。例えば、コンパイル時の環境を調べて設定するために*features*
にフィーチャーを加える、などといったケースが考えられます。その場合は、ユーザーが.asdファイル中の処理の詳細をコントロールできるように、次の慣習に従うことを推奨します:
*standard-output*
に出力するべきです。(ただし、警告とエラーを除きます。それらはコンディションシステムが対処する領域です。)ASDFのオペレーション由来の出力を、ユーザーが容易に扱えるようにするためです。
バージョン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
ASDFは、全体がオブジェクト指向でデザインされています。システムの構造も、システムに対するオペレーションも、拡張可能なプロトコルに従っているので、プログラマはASDFに新しい機能を加えることができます。例えば、cffi
はcffi-grovel
というASDF拡張によって、Cライブラリへのインターフェースの記述ファイルや、LispにCコードを埋め込むラッパーファイルを扱うコンポーネント型(それぞれcffi-grovel-file
とcffi-wrapper-file
)を追加し、ABCL提供のasdf-jar
はJavaのJARアーカイブを作る機能をもたらし、poiu
はバックグラウンドプロセスを使って並列ビルドを可能にします。
ASDFの最も基本的なクラスはcomponent
とoperation
です。component
が表すのは、1つのソースファイルか、複数のソースファイルのまとまりか、あるいはそれらから生み出されるもの(faslファイルなど)です。operation
はコンポーネントに対して行われる何らかの変換であり、コンポーネントを中間結果や最終的な生成物に変えます。コンポーネント同士は依存関係によって関係づけられていて、それはシステム定義の中で指定されます。
operate
によってコンポーネント(たいていはシステム)に対するオペレーションを指示したとき、ASDFはまずmake-plan
関数を使って、依存関係を調べ、処理全体の計画(plan)を建てます。22
make-plan
の返すplan
オブジェクトは実行すべきアクション(action)の順序付きリストを含んでいます。アクションとはoperation
とcomponent
のペアであり、ビルドの1ステップとしてperform
されるものです。また、このアクションのリストは、どのアクションも必要な依存関係が満たされるまでは実行されないことを保証します。23
この章では我々は、ASDFのオブジェクト指向プロトコル――プロトコルを構成するクラスと、それに対する総称関数――について解説します。これらの総称関数は多くの場合、コンポーネントとオペレーションの両方を引数に取ります。CLOSの多重ディスパッチを利用することで、ASDFは強力な機能を持ち、設定が柔軟に変更できるようになっています。我々は、まず組み込みのコンポーネントとオペレーションについて解説し、さらに新しいクラスやメソッドを定義してASDFプロトコルを拡張する方法を説明します。また、ASDFの関数の挙動をカスタマイズするためのさまざまなフックについても解説します。
ユーザーがシステムに対して何らかの処理を要求したときは、必ず適切なオペレーションオブジェクトが生成されます。例えば次のような処理が考えられるでしょう:
オペレーションは単に直接呼び出すこともできますし、実行せずにその結果がどうなるか確認することもできます。そこで実際の細々とした仕事をするのは、オペレーションとコンポーネント型によって特定化されたメソッド(のまとまり)です。オペレーションは、システムとともに引数としてoperate
に与えることで、呼び出すことができます。(→ operate)
頻出の作業についてはoperation
の組み込みサブクラスがありますし、あまり使われない作業についても既にあることが多いでしょう。これに加えてASDFは、プログラマがASDF拡張を定義するためのビルドブロックとして使える「抽象的な」operation
クラスも備えています。これについては以下に順を追って解説します。
オペレーションは、operate
によってシステムに対して呼び出されます。
operate
はcomponentに対してoperationを呼び出します。oos
(operate-on-systemの略)はoperate
のエイリアスです。
operationはオペレーション指示子(operation designator)です。オペレーション指示子はoperation
オブジェクトそのものでもよいですが、通常はシンボルが使われるでしょう。シンボルはmake-operation
に渡され、(そこでmake-instance
が呼ばれて)operation
オブジェクトが作られます。
componentはコンポーネント指示子(component designator)です。コンポーネント指示子は同様に、component
オブジェクトそのものや、コンポーネント(たいていはシステム)の名前を表す文字列・シンボル(string-downcase
で小文字化される)ですが、システムの子コンポーネントを指したい場合は文字列・シンボルのリスト 24も使えます。
initargsがmake-operation
に渡される仕様は廃止予定であり、削除されるでしょう。詳しくはmake-operationを参照してください。依存関係によって、オペレーションがシステムやその子コンポーネントに対して他のオペレーションを呼び出すことがありますが、この際、元のinitargsが渡されるかどうかは(今のところ)不確定です。
forceが:all
の場合、(依存関係に含まれる)すべてのシステムが、前回のコンパイルから未変更でも再コンパイルされます。forceがt
の場合、ロードされるシステムに限り、前回のコンパイルから未変更でも再コンパイルされます。forceが(システム指示子の)リストの場合、リストに含まれるシステムは前回のコンパイルから未変更でも再コンパイルされます。force-notが:all
の場合、(依存関係に含まれる)すべてのシステムは、前回のコンパイルから変更されていても再コンパイルされません。force-notがt
の場合、ロードされるシステム以外のシステムは、前回のコンパイルから変更されていても再コンパイルされません。(この仕様はASDF 3.1.2で変更されました。)forceが(システム指示子の)リストの場合、リストに含まれるシステムは前回のコンパイルから変更されていてもコンパイルされません。
forceとforce-notに影響されるのは、依存関係に含まれていて、既にコンパイルされているシステムです。force-notは当然forceに優先する(べきな)のですが、この挙動は残念ながらASDF 3.1.2以降で初めて保証されました。また、関数register-immutable-system
(ASDF 3.1.5以降)によってイミュータブルと登録されたシステムは、(たとえ.asdファイルがファイルシステムによって更新されていなくても)常にforce-notであるとみなされます。(→ Miscellaneous Functions)
次のフォームを実行すると、operate
が実際に何をどのような順序で実行するのか確かめることができます。25
(asdf:traverse operation-class system-name)
initargsは、オペレーションオブジェクトを作る際にmake-instance
に渡されます。
注意: operation
のinitargsは廃止予定であり、近い将来ASDFから削除されるでしょう。
注意: operation
のインスタンスは、直接make-instance
で作ってはいけません。必ずmake-operation
を使ってください。operation
インスタンスを直接作ろうとすると、ランタイムエラーを起こすでしょう。
このセクションで解説するオペレーションはすべてasdf
パッケージにあり、総称関数operate
で呼び出すことができます。
(asdf:operate 'asdf:オペレーション名 :システム名 {オペレーションのオプション ...})
このオペレーションは、指定されたコンポーネントをコンパイルします。つまり、cl-source-file
はcompile-file
でコンパイルされ、依存する、あるいは子コンポーネントであるシステム、モジュールには再帰的にcompile-op
が適用されます。
compile-op
はprepare-op
に依存していて、prepare-op
は依存するすべてのコンポーネントに対するload-op
に依存しています。したがって、operate
でcompile-op
を呼び出すと、それらのコンポーネント群は単にコンパイルされるだけでなくロードもされるでしょう、しかし、compile-op
では、指定されたシステムそのもの(の一部)は、それに依存するシステムがないのでロードされずに残ることになります。システムをロードしたい場合にはload-op
を使ってください。
このオペレーションは、指定されたコンポーネントのコンパイル済みコードをロードします。つまり、cl-source-file
についてはそのfaslファイルがロードされ、依存する、あるいは子コンポーネントであるシステム、モジュールには再帰的にload-op
が適用されます。なおload-op
はcompile-op
に依存していて、faslファイルはcompile-op
の出力として得られます。
load-op
はprepare-op
にも依存していて、prepare-op
は依存するすべてのコンポーネントに対するload-op
に依存しています。
このオペレーションは、指定されたコンポーネントが依存する(再帰的な意味で)すべてのコンポーネントが(load-op
で)ロードされることを保証します。compile-op
やload-op
が実行される前に、必要な前提を満たすためのオペレーションです。
load-source-op
はload-op
と似ていますが、モジュールに属するcl-source-file
をfaslからではなくソースからロードします。load-source-op
は(prepare-op
に代えて)prepare-source-op
に依存しています。prepare-source-op
はprepare-op
に類似のオペレーションで、依存関係がload-source-op
でロードされることを保証します。
このオペレーションはモジュールのテストを行うためにありますが、デフォルトのメソッドは何もしません。また、デフォルトではモジュールに対して先にload-op
が実行されることを要求します。なお、デフォルトのoperation-done-p
メソッドは(訳注: たとえ、test-op
が正常に実行された後であっても)常にnil
を返すので、このオペレーションは決して完了したことにはなりません。(→ 新しいオペレーションを定義する) つまり、あなたがtest-op
を呼ぶと、既にそのシステムをテストしたことがあっても、もう一度テストしたいのだとみなされるわけです。
ASDFはこのオペレーションの結果について何も定義していません。というのも、テスト結果をユーザーに伝える方法を、Lispコミュニティで使われる様々なテストのテクニックやライブラリすべてに対して互換性を持つように、統一的に定義するのは困難だとわかっているからです。さらに、ASDFのオペレーションは成功・失敗を返り値とするわけではないという点も、この問題を難しくしています。テスト結果を返すようにしたいのであれば、何らかのコンディションを定義し、test-op
が失敗した時にそのコンディションを通知することを勧めます。どのテストが失敗したか、という情報はコンディションに保持させると良いでしょう。
一般的に、テストコードはテスト用の別のシステムに記述されることが多いです。テスト用のシステムを分離すれば、メインのシステムの依存関係には不要なテストフレームワークを指定しなくてすみます。それでは、テストフレームワークfiveamを使った例を見てみましょう:
(defsystem "foo" :in-order-to ((test-op (test-op "foo/test"))) ...) (defsystem "foo/test" :depends-on ("foo" "fiveam") ...)
さらに、test-op
のperform
メソッドを次のように定義します:
(defsystem "foo/test" :depends-on ("foo" "fiveam") :perform (test-op (o s) (uiop:symbol-call :fiveam '#:run! (uiop:find-symbol* '#:foo-test-suite :foo-tests))) :components ((:file "test")) ; (訳者追加) ...)
[訳者補足]
上のように定義すると、(asdf:test-system :foo)
を実行してシステムfoo
に対するtest-op
を呼び出した際、:in-order-to
オプションによって、まずはシステムfoo/test
に対するtest-op
が呼び出されます。(foo
のtest-op
は何もしないので、アクションを「委託」しているのと同じことになります。)呼び出されたときの動作はfoo/test
の:perform
オプションで定義されており、この例では、端的に言えば(fiveam:run! 'foo-tests:foo-test-suite)
が実行されてテストが走ることになります。test.lispには例えば次のようなテストコードが書かれているでしょう。
(defpackage :foo-tests (:use :cl :fiveam)) (in-package :foo-tests) ;; スイート(テストのまとまり)を定義する (def-suite foo-test-suite) (in-suite foo-test-suite) ;; テストを定義する (test basic-test (is (= 1 (foo:baz 10))) ; (foo:baz 10)の返り値が1であることをテストする (signals type-error (foo:baz "10"))) ; (foo:baz "10")がtype-errorになることをテストする
fiveam
はfoo-test-suite
に属する一連のテスト(この場合はbasic-test
のみ)を実行し、その結果を標準出力に表示するでしょう。ただし、システム定義(defsystem "foo/test" ...)
のリード時にfiveam
パッケージが存在するとは限りませんから、(fiveam:run! ...)
とそのまま書くとエラーになる可能性があります。uiop:symbol-call
は「fiveam
パッケージに属するrun!
関数を呼び出す」フォームを安全に書く――シンボルの解決を実行時に遅らせる――ための関数であり、この用途でよく使われます。uiop:find-symbol*
も同様の目的で使われています。UIOPのマニュアルも参照すると良いでしょう。
[訳者補足終わり]
これらは「バンドル(bundle)」に関わるオペレーションです。バンドルは、アプリケーション全体(もしくはアプリケーション中の各システム)を単一のファイルに束ねたものです。
compile-bundle-op
はターゲットとなるシステムとそれが依存する各システムについて、それぞれ単一のfaslファイルを作ります。monolithic-compile-bundle-op
はターゲットとなるシステムとそれが依存するシステムをすべて束ねて、単一のfaslファイルを作ります。つまり、アプリケーション全体を1つのfaslファイルにすることができます。load-bundle-op
はcompile-bundle-op
の出力をロードします。ただし、compile-bundle-op
は(出力が最新でない場合)コンパイルの副作用として、中間生成物である各faslファイルをロードすることもあるという点に注意してください。バンドルに関するオペレーションは特にECLにおいて重要です。というのもECLでは、ダイナミックリンクの際に、数十もの独立したfaslファイルをロードするのは1つのファイルをロードするのに比べてずっと重いからです。
注意: compile-bundle-op
、monolithic-compile-bundle-op
、load-bundle-op
、monolithic-load-bundle-op
、deliver-asd-op
、monolithic-deliver-asd-op
は、ASDF 3.1以前はそれぞれfasl-op
、monolithic-fasl-op
、load-fasl-op
、monolithic-load-fasl-op
、binary-op
、monolithic-binary-op
と呼ばれていました。後方互換性のために古い名称も残されていますが、それらは実際の機能を表していない不適切な名前ではあります。
compile-bundle-op
で作った単一のfaslファイルを配布する時は、precompiled-system
クラスを使うことで、ユーザーはソースで配布されたものと互換性を持ったシステムとして使えるようになります。単一のfaslとともに配布される.asdファイルは次のようになるでしょう:
(defsystem :mysystem :class :precompiled-system :fasl (faslのパス名に評価されるフォーム))
あるいは、deliver-asd-op
やmonolithic-deliver-asd-op
を使えば、ASDFはcompile-bundle-op
の出力に加えてシステム定義も作ります。これによって、あなたのシステムやアプリケーションのコードを単一のファイルとして提供することができます。当然ながら、これらの結果をカレントイメージでテストしたい時は、新しくできた.asdファイルを使おうとする前に(asdf:clear-configuration)
(あるいは少なくとも(asdf:clear-source-registry)
)を実行することを忘れないでください。ファイルシステムからソースレジストリを再設定するためです。
program-op
は指定されたシステムとその依存関係から、実行可能ファイルを作り出します。その際、コアイメージをダンプ・復元する時のフックや、コマンドライン引数の処理が必要になるかもしれませんが、UIOPにはこの用途で便利なユーティリティがあります。実行時のエントリーポイントはdefsystem
の:entry-point
オプションで指定できます。例えばエントリーポイントをmy-app:main
関数にする場合は、:entry-point "my-app:main"
とします。処理系によっては、(asdf:operate 'asdf:program-op :my-app)
を実行すると、オペレーションの完了時に現在のLispイメージが終了するかもしれません。program-op
の使用例はtest/hello-world-example.asd、test/hello.lispを参照してください。ビルド、テストはtest/test-program.script、test/make-hello-world.lispに書かれています。
image-op
はイメージをダンプしますが、スタンドアローンとは限らないし、システムのエントリーポイントが使われたりすることもありません。お使いのLisp処理系のコアイメージの扱いに従い、システムがプリロードされたイメージを出力することになるでしょう。これは中間的なビルド結果として使われたり、ラッパーとなるスクリプトとともに使われたりします。
lib-op
はターゲットとなるシステムとそれが依存する各システムについて、リンク可能なオブジェクト(FFIファイル、あるいはECLであればLispファイルも)から.aファイル(Windowsでは.lib)をそれぞれビルドするためのオペレーションであり、monolithic-lib-op
はアプリケーション全体に対して1つの.aファイルを作ります。dll-op
、monolithic-dll-op
も類似のオペレーションですが、ダイナミックリンクのための.soファイル(Windowsでは.dll、MacOS Xでは.dynlib)を作ります。
これらの「バンドル」オペレーションは、ASDF 3以降で、アクティブにメンテナンスされているLisp処理系であれば常に利用可能ですが、メンテナンスされていない古い処理系では使えない可能性があります。この機能は、以前は特定の処理系のためにasdf-bundle
というシステムに分離されて実装されていて、さらに遡ればECL限定の機能であったasdf-ecl
が元になっています。
「バンドル」オペレーションの出力先のパスは、他のオブジェクトファイルと同様にアウトプットトランスレーションの設定に従いますが、defsystem
の:build-operation
オプションに設定されたオペレーションに限っては異なります。26 この仕様は満足のいくものとは言いがたいので、将来的には変わる可能性があります。より良い仕様についてあなたの提案はありますか?
concatenate-source-op
は、ターゲットとなるシステムとそれが依存する各システムについて、すべてのcl-source-file
を依存関係に沿った順序でそれぞれ1つのソースファイルに連結します。monolithic-concatenate-source-op
は、ターゲットとなるシステムとそれが依存するシステムをすべて束ねて、単一のソースファイルを出力します。上のリストには、さらに連結した出力ファイルをロードしたり、コンパイルしてからロードしたりといったオペレーションが含まれています。
これらのオペレーションは、システムやアプリケーションを単一のソースファイルとして配布するのに役立ちます。その際に、ロードやコンパイル後のロードが適切に行われるかテストするのにも使われるでしょう。
ASDF自身も、monolithic-concatenate-source-op
を使って単一のソースファイルasdf.lispとして配布されており、asdf/defsystem
システムそのものの前に前処理とuiop
ライブラリが挿入されています。
ASDFは、オブジェクト指向の手法で拡張可能なようにデザインされています。プログラマはoperation
のサブクラスを作って必要な挙動を実装することで、ASDFに新しい機能を追加することができます。
ASDFの組み込みのオペレーションは、ユーザー定義のオペレーションに対して「特権的」な地位にあるわけではありませんが、開発者の方には、独自のオペレーションを追加するときに決してasdf
パッケージを使わないようにお願いします。というのも、名前の衝突は避けるべきですし、かといって我々は「ASDFのオペレーション名を管理するグローバルなレジストリ」をいちいち作りたくもないからです。
[訳者補足]
また、ASDFにはオペレーション・アクション間の依存関係を指定するためのサブクラスとして、downward-operation
、upward-operation
、sideway-operation
、selfward-operation
、non-propagating-operation
の5つが備わっており、すべてのオペレーションはこの内の少なくとも1つを継承するべきです。オペレーションの依存関係を指定する必要がないときも、non-propagating-operation
を継承するようにしてください。operation
のみを継承すると、そのオペレーションを呼んだ時に警告が通知されるでしょう。
例として、組み込みのload-op
を簡略化したmy-load-op
を定義してみましょう:
(defclass my-load-op (downward-operation selfward-operation) ((selfward-operation :initform '(prepare-op compile-op) :allocation :class) ;; (downward-operation :initform nil :allocation :class) ))
non-propagating-operation
を除く4つのクラスはすべて、同名のスロットをクラス変数として備えています。selfward-operation
は「同一コンポーネントに対する別のオペレーション」に依存するオペレーションです。上の例では、コンポーネントfoo
に対してmy-load-op
が呼ばれた時、先だってfoo
にprepare-op
とcompile-op
が適用されます。次に、downward-operation
は「子コンポーネントに対するオペレーション」に依存するオペレーションです。上の例ではdownward-operation
スロットを省略していますが、デフォルトのnil
は同じオペレーションmy-load-op
を意味します。つまり、コンポーネントfoo
に対してmy-load-op
が呼ばれた時、先だってfoo
のすべての子コンポーネントに対してmy-load-op
が適用されます。
asdf::operation-designator
型は、オペレーションオブジェクト・シンボル・クラスのいずれかです。多くの場合、シンボルが使われるでしょう。その際、nil
は「同じオペレーション」を意味します。
downward-operation
は子コンポーネントについての依存関係を持つオペレーションです。baz-op
がfoo-op
のdownward-operation
スロットに指定されているとき、ASDFはコンポーネントparent
に対するアクション(foo-op . parent)
を実行する前に、まずはparent
のすべての子コンポーネントchild
についてアクション(baz-op . child)
を実行します。
upward-operation
は親コンポーネントについての依存関係を持つオペレーションです。baz-op
がfoo-op
のupward-operation
スロットに指定されているとき、ASDFはコンポーネントchild
に対するアクション(foo-op . child)
を実行する前に、まずはchild
の親コンポーネントであるparent
についてアクション(baz-op . parent)
を実行します。
sideway-operation
は依存コンポーネントについての依存関係を持つオペレーションです。baz-op
がfoo-op
のsideway-operation
スロットに指定されているとき、ASDFはコンポーネントc
に対するアクション(foo-op . c)
を実行する前に、まずはc
が依存している(:depends-on
)すべてのコンポーネントd
についてアクション(baz-op . d)
を実行します。
selfward-operation
は同一コンポーネントについての依存関係を持つオペレーションです。baz-op
がfoo-op
のselfward-operation
スロットに指定されているとき、ASDFはコンポーネントc
に対するアクション(foo-op . c)
を実行する前に、まずはアクション(baz-op . c)
を実行します。selfward-operation
スロットには、複数のオペレーションをリストとして指定することができます。
non-propagating-operation
は依存関係を持たない、単独で実行されるオペレーションです。
上で例示したように、これらのオペレーションは多重継承することができます。(もっとも、non-propagating-operation
は単一継承するべきでしょう。)また、依存関係が循環しないようにするのはプログラマの責任に委ねられています。
[訳者補足終わり]
新しく作られたオペレーションには、多くの場合、次の総称関数の内の少なくとも1つについてメソッドを定義しなければならないでしょう。
perform
は何らかの入力ファイルから出力ファイルを作り出すための総称関数です。prepare-op
のように依存関係の伝搬だけするようなオペレーションは別にしても、たいていのオペレーションについてはperform
メソッドが最も重要でしょう。perform
が呼ばれると、(すべての依存関係についてperform
が実行されたあと)ターゲットのコンポーネントに対して処理が実行されます。
perform
メソッドは、その入力と出力のパスを決めるのにinput-files
とoutput-files
(下述)を呼ばなければなりません。ユーザーが、このメソッドを上書きしたり、アウトプットトランスレーションを変更したりできるようにするためです。output-files
は多値を返しますが、perform
は最初の値――出力パス名のリスト――のみを使うべきです。出力ファイルがただ1つと決まっている場合は、代わりに関数output-file
が使えます。output-file
は出力パス名が1つだけであることを保証し、そのパス名を返します。
perform
メソッドが何らかの出力ファイルを伴うならば、その出力先をASDFが定められるようにこのメソッドも定義しなくてはなりません。
output-files
は2つの値を返します。1つ目はパス名のリストであり、2つ目はブーリアンです。ブーリアンがnil
の場合(返り値が1つしかなく、2つ目が暗黙にnil
となる場合も含む)、:around
メソッドがアウトプットトランスレーションに従ってパス名を変換します。例えば、コンパイルされたオブジェクトファイルが処理系依存のキャッシュとして保存される場合などは、アウトプットトランスレーションが使われています。ブーリアンがt
の場合は:around
メソッドで変換されることはありません。
この総称関数は、アクションを引数に取り、依存するアクションをまとめたリストを返します。リストのフォーマットについては下で説明します。
[訳者補足]
上述の5つのサブクラスを継承してオペレーション間の依存関係を指定すれば、component-depends-on
メソッドは適切に構成されるので、通常は明示的にこのメソッドを定義する必要はありません。また、特定のコンポーネント、システムに対して依存関係を記述したい場合も、:in-order-to
オプションを使えば事足りるでしょう。(→ アクションの依存関係) しかし、それらを超えるような複雑な依存関係を指定したい場合――例えば特定のコンポーネント型に対するオペレーションに限って依存関係を追加したい場合など――は、メソッド定義が必要になるかもしれません。
なお、名前の印象に反して、コンポーネント間の依存関係を返す関数ではないことに気を付けましょう。システムの:depends-on
オプションに与えた依存コンポーネントを調べたい場合は、代わりにsystem-depends-on
という総称関数があります。
[訳者補足終わり]
自分でこのメソッドを定義する場合は、メソッドで追加したい内容と(call-next-method)
で返るリストを常にappend
して返すことを強く推奨します。あなたがASDFの内部を熟知しているのでない限り、そうしないと「興味深い」エラーが起こる可能性が高いです。これは概念的には、メソッドコンビネーションのappend
を手動でやっているのと同じことになります。(残念ながら、標準のメソッドコンビネーションを互換的に導入するのは、手遅れでした。)
それでは、返り値について説明します。component-depends-on
が返すリストの各エントリもまたリストです。
各エントリの最初の要素(car
)はオペレーション指示子、つまりオペレーションオブジェクトそのものかオペレーションクラスのシンボル(例えばload-op
、compile-op
、prepare-op
など)であり、後者はmake-operation
でインスタンス化されるでしょう。
各エントリの2番目以降(cdr
)はコンポーネント指示子のリストです。コンポーネント指示子はコンポーネントオブジェクトそのものか、find-component
でコンポーネントオブジェクトに変換される識別子(identifier)です。find-component
は現在の親コンポーネントを第1引数に、識別子を第2引数にして呼び出されるでしょう。識別子は一般的には、文字列か、シンボル(coerce-name
で小文字の文字列になる)か、あるいは文字列・シンボルのリストです。特にnil
は親コンポーネントそのものを指します。
新しく作られたオペレーションには、次の総称関数についてメソッドを定義することができます。
input-files
は、perform
に対する入力ファイルのパス名のリストを返します。
デフォルトのinput-files
のメカニズムは賢くできているので、あなたが自らメソッドを定義する必要はないことのほうが多いでしょう。メソッド定義が必要なのは、最終的な入力ファイル(のまとまり)が複数通り考えられるケースのみです。ほとんどのオペレーションはselfward-operation
を継承していて、selfward-operation
はソースファイルを含むように入力ファイルを適切に設定します。
この総称関数は、アクション(オペレーションとコンポーネントの対)がもう一度perform
されなければならない場合にnil
を返します。デフォルトは常にt
です。
operation-done-p
にメソッドを定義する必要があるのは、ファイルシステム上のタイムスタンプが変わっていなくても前回のオペレーションの実行を無効にしてやりなおすべき状況がある、という場合に限ります。そしてその場合、このメソッドはnil
を返すべきでしょう。
例えば、このメソッドはtest-op
については常にnil
を返します。テストというのは決して完了する性質のオペレーションではないからです。もちろん、単にテストのリポートファイルから結果を読み出すだけのオペレーションとしてtest-report-op
のようなものを定義し、システム定義の:in-order-to
オプションでtest-op
をこのオペレーションに委ねて、何度繰り返しても同じになるようにすることは可能です。その場合はこのメソッドがt
を返すようにしても良いでしょう。
何らかのメッセージを表示するオペレーションは、Lispのコンパイラやローダと同様に、出力先を標準の*standard-output*
ストリームにするべきです。
component
オブジェクトが表すのは、1つのソースファイルか、複数のソースファイルのまとまりか、あるいはそれらから変換されるもの(faslなど)です。
component
の組み込みのサブクラスとして重要なのはsystem
、source-file
、module
です。system
はコンポーネント階層のトップレベルにある特別なコンポーネントであり、find-system
によって発見することができます。source-file
は1つのソースファイルに対応します。module
は中間的なコンポーネントであり、他のコンポーネント(source-file
やさらなるmodule
であっても良い)のまとまりを表します。(→ コンポーネントの組み込みのサブクラス)
システム指示子(system designator)はシステムそのものか、システムを表す文字列、シンボルです。(コンポーネント名の仕様と同じく、シンボルは小文字に変換されます。)
コンポーネント指示子(component designator)は基点となるコンポーネントに対する相対的な指示子であり、コンポーネントそのものか、文字列、シンボルか、あるいはコンポーネント指示子のリストです。
find-system
はシステム指示子を引数に取り、システムを返します。システムが見つからなかった場合、デフォルトではエラーmissing-component
を通知しますが、error-pがnil
の場合は単にnil
を返します。
システム定義ファイルを見つけてメモリ上のシステムをアップデートするために、find-system
はリスト*system-definition-search-functions*
に含まれる関数をそれぞれfuncall
します。それらの関数はシステム名を引数に取り、(システム定義ファイルの)パス名かシステムオブジェクト(system-source-directory
でパス名に変換される)を返すべきであり、返ったパス名はメモリ上に登録されるでしょう。こうして見つかったシステム定義ファイルがロードされるのは、次の条件のいずれかを満たす場合です:
.asdファイルからシステム定義がロードされる際の暗黙のカレントパッケージはasdf-user
であり、asdf-user
はasdf
、uiop
、uiop/common-lisp
をuse
しています。27
.asdファイル内で新しい変数・関数・クラスを定義するなど、単純なシステム定義にとどまらないことをする場合は、defpackage
とin-package
で別のパッケージを作り、asdf-user
とは干渉しないようにするべきです。ただ、フォームを追加する目的が、(開発・デバッグのために)cl:load
で手動で.asdファイルをロードできるようにすることなのであれば28、そうするよりもasdf:load-asd
を使うことを推奨します。なお、SLIMEを使っている場合は、slime-asdf
拡張をロードすれば、.asdファイルの編集時にasdf:load-asd
が適切に適用されるようになります。
デフォルトでは*system-definition-search-functions*
は3つの関数からなるリストです。最初の関数は、*central-registry*
の要素を評価して得られた各ディレクトリから、ファイル名がシステム名であり、拡張子が.asdであるファイルを探し、最初に見つかったパス名を返します。ただし、探索対象のシステムがそのファイル内で実際に定義されているかどうかはチェックしません。2つ目の関数は同様のことをsource-registry
で指定されたディレクトリに対して行いますが、実際にファイルシステムを探すのは1回のみであり、結果はキャッシュされます。29 3つ目の関数はpackage-inferred-system
拡張で使われます。詳細はpackage-inferred-system拡張を参照してください。
システムの探索関数の仕様からもわかるように、システムfoo
はfoo.asdという名のファイルに定義するべきであり、また、ファイルはセントラルレジストリかソースレジストリに指定されたディレクトリに置くようにするべきです。
1つのファイル内で複数のシステムを定義するのが便利なケースも良くあるでしょうが、ASDFはシステム名に基づいてシステム定義ファイルを探すことしかできません。この理由により、ASDF 3からはシステム定義を探すアルゴリズムが拡張され、ファイルfoo.asdには、システムfoo
に加えてfoo/bar
、foo/baz
、foo/quux
といった名のシステムも定義できるようになりました。/
で区切られたシステム名の最初の部分をシステムのプライマリネーム(primary name)と呼び、対応するシステムをプライマリシステム(primary system)と呼びます。ASDF 3は、名前にスラッシュを含むシステムを探すように指示されると、関数asdf:primary-system-name
によってプライマリネームを抽出し、まずはプライマリシステムを探してロードしようとします。そして、見つかった場合は、そのファイルにターゲットのシステムが定義されているかどうか、もしくはpackage-inferred-system
が定義されているかどうか調べます。30
foo.asdに、この命名ルールに従わないシステム(例えばfoo-test
など)が定義されている場合、ASDFはシステム定義の場所を自動的に見つけることができないので、事前に(asdf:find-system "foo")
を実行するなどして、明示的にプライマリシステムの定義をロードしておかなければなりません。我々はそのような命名の慣習をやめることを強く推奨しますが、今のところ、後方互換性のためにサポートされてはいます。
nameがスラッシュ/
によって区切られている場合、primary-system-name
はプライマリネーム、つまり最初の部分を返します。
一般的には、この関数を直接呼び出すべきではありません。この関数がAPIの一部としてエクスポートされているのは、独自の*system-definition-search-functions*
を使いたいプログラマのためです。
locate-system
はシステム名の指示子を引数に取り、システム定義をどこからロードすれば良いか調べようとします。
[訳者補足]
この関数そのものはシステム定義をロードすることはない――と原文には書かれています。しかし現在のバージョンでは、プライマリネームが絡んでいるケースで、例えば(asdf:locate-system :baz/test)
などとすると、baz
とbaz/test
のシステム定義がロードされることがあるようです。いずれにせよ、システム定義のロードにはfind-system
を、システムそのもののロードにはload-system
を使うべきです。
[訳者補足終わり]
この関数は6つの値を返します: foundp、found-system、pathname、
previous、previous-time、previous-primary。foundpはシステムが見つかれば(未登録であれ、登録済みであれ)真です。found-systemがnil
でない場合、システムオブジェクトです。(そのシステムは登録されているかもしれないし、されていないかもしれません。)
pathnameがnil
でない場合、システム定義ファイルのパス名であり、このパス名はfound-systemかpreviousのシステムと対応しているでしょう。previousがnil
でない場合、前回ロードされた同名のシステムオブジェクトです。(なお、ここでロードされているのはシステム定義であって、システムそのものではないことに注意しましょう。)previous-timeがnil
でない場合、previousの定義が前回ロードされた際のシステム定義ファイルのタイムスタンプです。previous-primaryがnil
でない場合、システムpreviousのプライマリシステムです。
例えば、ソースレジストリに登録されたパス/current/path/to/foo.asdにシステム定義ファイルがあるとしましょう。しかし、システムfoo
は前回は/previous/path/to/foo.asdからロードされたとします。このとき、locate-system
は次の値を返すでしょう:
t
です。
nil
です。
#p"/current/path/to/foo.asd"
です。
system
オブジェクトであり、そのsystem-source-file
スロットは#p"/previous/path/to/foo.asd"
です。
#p"/previous/path/to/foo.asd"
がロードされたときのタイムスタンプです。
baseコンポーネント(あるいは指示子)とpathを引数に取り、baseを基点としてpathで指示されるコンポーネントを見つけます。
pathがコンポーネントオブジェクトの場合は、baseに関係なく、そのままpathが返ります。
pathが文字列かシンボル(coerce-name
で文字列に変換される)の場合、baseはシステムかモジュールを指していなくてはなりません。この場合、返り値はpathの指すbaseの子コンポーネントです。
pathがcons
セルの場合、find-component
はbaseとpathのcar
から再帰的に子コンポーネントを辿り、末端のコンポーネントを返します。
baseがnil
の場合、(find-component path nil)
と同等になります。
baseが文字列かシンボル(coerce-name
で文字列に変換される)の場合、baseはfind-system
の返すシステムであるとみなされます。
baseがcons
セルの場合、baseは(find-component (car base) (cdr base))
の返すコンポーネントであるとみなされます。
すべてのコンポーネントはその型にかかわらず、次の属性(attribute)を持っています。コンポーネント名以外の属性は任意です。
コンポーネント名(component name)は文字列またはシンボルであり、シンボルはasdf:coerce-name
によって小文字の文字列に変換されます。コンポーネント名には総称関数component-name
でアクセスできます。
:pathname
属性を上書きしない限り、コンポーネント名はUNIXスタイルの相対パスとしてそのままパス名指定子に使われるでしょう。(→ パス名指定子)
:version
)この属性は任意であり、コンポーネントのバージョンを指定します。バージョンは、一般的にはドットで区切られた整数の文字列で、例えば‘1.0.11’などです。(→ バージョン指定子) コンポーネントのバージョンには総称関数component-version
でアクセスできます。
ASDFが、:depends-on
で指定された依存コンポーネントの:version
オプションについて調べる時に、コンポーネントのバージョンをversion-satisfies
で問い合わせるかもしれません。例えばシステム定義に:depends-on (((:version "mydepname" "1.0.11"))
という記述がある時は、mydepname
コンポーネントのバージョンが1.0.11
以上であるかどうか確認されることになります。:version
オプションについてはdefsystemの文法を、version-satisfies
関数については関連する関数を参照してください。
一般的には、バージョン番号が問題になるのはsystem
型のコンポーネントだけであることに注意してください。ライブラリの作者が、システムに含まれる様々なファイルにバージョン番号を付けて自ら同期させるというような使い方は、おそらくまったく有用ではないでしょう。
:if-feature
)defsystem
内で処理系に応じてファイルを含めたり除外したりする時は、伝統的にはリーダーマクロ#+
が使われていました。例えばポータブルなCのFFIであるCFFIには、システム定義に次のような一行があります:
#+sbcl (:file "cffi-sbcl")
しかしこの方法には、どの処理系もシステム全体を読み込むことができないという欠点があります。例えば、新しくarchive-op
というオペレーションを作り、システムのすべてのソースファイルをまとめたアーカイブを作ろうとしたとしましょう。この時、上の例のcffi-sbcl.lispはSBCL以外の処理系では不可視であり、archive-op
を実行してもアーカイブに収集されないという問題が起きます。
この問題を解決するため、ASDF 3からコンポーネントに:if-feature
オプションが導入されました。このオプションの値はフィーチャー表現であり、つまり、#+
と同じ文法を使います。フィーチャー表現が満たされないとき、そのコンポーネントはコンパイル、ロード、リンクの際に無視されます。なお、「同じ文法」と述べましたが、このフィーチャー表現は標準のリーダーで読まれるので、シンボルを使う際には:
を前置して明示的にキーワードにしなくてはなりません。#+
に続くフィーチャー表現は暗黙にキーワードパッケージを使いますから、その点は異なることになります。
例えば(:file "foo-sbcl" :if-feature (:and :x86 (:or :sbcl :cmu :scl)))
という記述は、x86マシン上のSBCL、CMUCL、Scieneer CLにおいてのみ、foo-sbcl.lispのコンパイル、ロードがなされるという意味です。先に述べた通り、(:file "foo-sbcl" :if-feature (and x86 (or sbcl cmu scl)))
と書くことはできません。
:in-order-to
)この属性には、当該コンポーネントに関するアクションが別のアクションに依存していることを記述します。任意の属性ですが、しばしば必要になります。
依存関係はオペレーションとコンポーネントのペアについて指定します。例えば次のように記述したとしましょう:
:in-order-to ((compile-op (load-op "a" "b") (compile-op "c")) (load-op (load-op "foo")))
このとき、このコンポーネントに対してcompile-op
を実行する前に、ASDFはa
とb
に対してload-op
を実行し、さらにc
に対してcompile-op
を実行しなければなりません。また、load-op
を実行する前に、foo
がロードされていなければなりません。
文法はおおむね次のようになります。
(this-op {(other-op required-components)}+) required-components := component-name | (required-components required-components) component-name := simple-component-name | (:version simple-component-name minimum-version-object) simple-component-name := string | symbol
[付記]
これはACLのdefsystemと同等の機能です。mk-defsystemはやや一般性に欠けていて、次のような暗黙の依存関係
すべてのソースファイルxについて、(load x)は(compile x)に依存する
を導入していました。:depends-on
引数でbがaに依存すると指定したとき、それが実際に意味しているのは
(compile b)は(load a)に依存する
ことだったのです。しかし、これは不十分な機能でした。McCLIMのように、何らかのファイルがコンパイルされる前に、まずはすべてロードされなければならないようなシステムを記述できなかったのです。
[付記終わり]
ASDFでは、与えられたアクションがどのアクションに依存しているかを(component-depends-on operation component)
で問い合わせることができます。このフォームが返すリストは
((load-op "a" "b") (compile-op "c") (load-op "d") ...)
のような形式です。
component-depends-on
メソッドは、特定のコンポーネント・オペレーションのクラスについて定義することも可能です。その際、(デフォルトの依存関係を完全に上書きしたいのでなければ)自分のメソッドの結果と(call-next-method)
の返り値をappend
して返す必要があります。
この結合は、CLISPのことを考えなければ、メソッドコンビネーションのlist
を使って透過的に実現できるでしょうが、我々はCLISPもサポートする必要があるので、この仕様を使っていません。あなたにCLISPをハックする時間があれば、この部分の修正はきっと歓迎されることでしょう。
コンポーネント(通常はシステム)に特定のバージョン以降を要求したい場合は、ここでも:version
オプションが使えます。この場合、:in-order-to ((load-op (load-op "other-system'')))
の代わりに:in-order-to ((load-op (load-op (:version "other-system" "1.2.3"))))
などと記述することになるでしょう。
:pathname
)この属性の指定は任意であり、たいていは省略されるでしょう。その場合、コンポーネント名が(相対パスとして)使われます。
パス名がどのように解釈されるかについては、パス名指定子を参照してください。
また、「トップレベル」のシステムを定義するdefsystem
マクロは、システム直下のコンポーネントがファイルシステム上のどこにあるか決めるために、追加の処理をします。詳しくはdefsystemでシステムを定義するを参照してください。
componentに対応するパス名を返します。ソースファイルのようなコンポーネントに対しては、ファイルのパス名を返すでしょう。以下に例を挙げます:
CL-USER> (asdf:component-pathname (asdf:find-system "xmls")) #P"/Users/rpg/lisp/xmls/"
CL-USER> (asdf:component-pathname (asdf:find-component (asdf:find-system "xmls") "xmls")) #P"/Users/rpg/lisp/xmls/xmls.lisp"
:properties
)この属性は任意です。
[訳者補足]
この属性は廃止予定であり、ASDF 2との後方互換性のためだけに存在します。また、その名に反して、:properties
オプションにはプロパティリストではなく連想リストを指定します。昔のASDFでは独自の属性が必要な時にcomponent-property
で読み書きしていた、という点だけ知っていれば十分でしょう。
[訳者補足終わり]
システムをパッケージングするにあたって、ASDFの組み込みの属性に指定した情報に加えて、ファイルやシステムについての他の情報が必要になることはしばしばあります。ASDFシステムからベンダーのパッケージを作るプログラムは、これらのシステムを満たすために「プレースホルダ―」情報を作る必要があります。ASDFシステムを作る方は、その付加的な情報を直接読み書きすることがあるでしょう。
(component-property component property-name)
はsetf
のプレースとしても使用可能で、この情報を読み書きすることができます。property-nameの同一性はeql
で判定されるので、シンボルやキーワード等を使いましょう。
ソースファイルとは、システムの他のコンポーネントから生成する方法をシステムが知らないファイルを指します。
これは一般的な意味でのソースファイル、つまり「コンパイラに渡すデータを含んだファイル」と必ずしも同じでないことに注意してください。何らかのプリプロッセンシグを経て生成されたファイル(例えば.h.inからautoconfによって生成された.hファイルなど)は、定義上、ソースファイルとは呼ばれないことになります。逆に、(毎回自動生成されない)画像ファイルや、バイナリとして得られるプロプライエタリな共有ライブラリなどは、我々の目的においてはソースファイルとみなすことになります。
さまざまな言語に対してsource-file
のサブクラスが存在します。
[訳者補足]
cl-source-file
、c-source-file
、java-source-file
はそれぞれCL、C、Javaのソースファイルに対応し、ファイル形式はlisp、c、javaです。static-file
はビルドの際にそのまま出力されることを意図したファイルを表し、ファイル形式は不定です。doc-file
はstatic-file
のサブクラス、html-file
はdoc-file
のサブクラスであり、html-file
のファイル形式はhtmlです。
defsystem
フォームの:components
オプションにコンポーネントを記述するとき、:file
は既定ではcl-source-file
を意味します。このクラスは親コンポーネントの:default-component-class
オプションで書き換えることができます。
[訳者補足終わり]
モジュールは、コンポーネントのまとまりとしてのコンポーネントです。
モジュールコンポーネントには次の追加の属性が指定できます。
:components
このモジュールに含まれるコンポーネント群です。
:default-component-class
クラスが明示されていない子コンポーネントはこのクラスであるとみなされます。
:if-component-dep-fails
この属性はASDF 3で削除されたので、使わないでください。代わりに:if-feature
を使いましょう。(→ 必要なフィーチャー あるいは → if-featureオプション)
:serial
この属性が真の場合、(:components
に指定された)それぞれの子コンポーネントは、それ以前のすべての子コンポーネントに依存しているとみなされます。
デフォルトのオペレーションはモジュールをどのように走査すれば良いか知っているため、ほとんどのオペレーションにはモジュールに特定化されたメソッドを定義する必要はないでしょう。
他の言語のライブラリ群やアーカイブファイルを表すのに、module
のサブクラスを定義することもできるでしょう。
システムはモジュールのサブクラスです。
システムは、主にドキュメントを付けるためにいくつかの属性を追加されたモジュールです。詳細はdefsystemの文法を参照してください。
ユーザーは自分のシステムのために自ら新しいクラスを定義することもできます。定義したクラスはdefsystem
マクロの:class
オプションに指定することで使えます。
新しいコンポーネント型を作るには、既存のコンポーネントのサブクラスを作り、新しいクラスに特定化されたメソッドを定義します。
例を見てみましょう。あなたのシステムは処理系依存の機能をいくつか備えています。あなたは、それらのソースファイルを1つの(サポートする)Lisp処理系ごとに1つのサブディレクトリに隔離したいと考えました。まずはcl-source-file
のサブクラスを定義しましょう。
(defclass unportable-cl-source-file (cl-source-file) () (:documentation "処理系依存のLispソースファイル"))
サブディレクトリの名前は、asdf:implementation-type
(バージョン2.014.14以降でエクスポートされています)の返り値を使うことにします。あとは、unportable-cl-source-file
のパス名を推論する仕組みを加えるだけです。
(defmethod component-pathname ((component unportable-cl-source-file)) (merge-pathnames* (parse-unix-namestring (format nil "~(~A~)/" (asdf:implementation-type))) (call-next-method)))
これで完了です。新しいコンポーネント型は、defsystem
フォーム中で次のように使われます:
(defsystem :foo :components ((:file "packages") ... (:unportable-cl-source-file "threads" :depends-on ("packages" ...)) ... ))
ビルドを成功させるためには、アクションのグラフが循環的であってはなりません。あなたが(ASDFの純粋なユーザーであれ、拡張を実装しているのであれ)依存関係のグラフにループを含めてしまうと、ASDFは盛大なエラーを引き起こすでしょう。依存の向きを明確にするため、ASDF 3では、アクションが他のアクションに依存していることをrequiring、requiredという言葉で表しています。つまり“requiring action”は、それ自身がperform
される前に、そのすべての“required action”が完了することに依存(depends-on
)しているわけです。
defsystem
の文法を使うと、ユーザーはオブジェクトの階層グラフに沿った直接の依存関係(コンポーネントとその親コンポーネント、子コンポーネント、兄弟コンポーネント31)を簡単に書くことができますし、CLOSメソッドをカスタマイズすることでさらに複雑な依存関係を表現することも可能です。もっとも一般的なオペレーションであるload-op
、compile-op
、load-source-op
などはコンポーネントの階層を「下向き」に伝搬し、「共変的」に振る舞います。つまり、親コンポーネントに対するオペレーションを行う前に、まずはすべての子コンポーネントに対して同じオペレーションを行わなくてはならないわけです。いっぽう、prepare-op
や(ASDF 3で導入された)prepare-souce-op
のようなオペレーションは、コンポーネントの階層を「上向き」に伝搬し、「反変的」に振る舞います。つまり、子コンポーネントに対するコンパイルを準備するオペレーションを行う前に、まずは親コンポーネントに対して同じオペレーションを行わなくてはならず、そのような挙動によって、子コンポーネントがコンパイル・ロードされる前に、親コンポーネントの依存関係がすべて(コンパイル、)ロードされることが保証されるわけです。最後に、test-op
やload-bundle-op
はターゲットのシステムのレベルにとどまるオペレーションであり、階層を伝搬していくことはありません。これらはシステムに対してグローバルな処理を行うのです。
[訳者補足]
このセクションはオペレーションの依存関係について述べたものですが、実践的には、新しいオペレーションを定義するの補足に挙げた5つのオペレーションについて理解していれば事足りるでしょう。つまり、
load-op
、compile-op
、load-source-op
はdownward-operation
(かつselfward-operation
)であり、
prepare-op
、prepare-source-op
はupward-operation
(かつsideway-operation
)であり、
test-op
、load-bundle-op
はselfward-operation
です。
[訳者補足終わり]
versionがversion-specを満たすか判定する総称関数です。組み込みのメソッドとしては、versionがcomponent
のものと文字列のものが用意されており、component
の場合はそのバージョン文字列が使われます。いっぽう、version-specは文字列であるべきです。
versionがversion-specを満たしているとは、version-specと同じか新しいバージョンであることです。したがって、"1.9.1"
、"1.9.2"
、"1.10"
はすべて"1.9.1"
を満たしますが、"1.8.4"
や"1.9"
は満たしません。version-satisfies
がバージョンの文字列をどのようにパースし、解釈するかについては、バージョン指定子とコンポーネントの共通の属性を参照してください。
ASDF 3.0.1以前(ASDF 1、2を含む)では、version-satisfies
はversionとversion-specが同じメジャーバージョン番号(1番目の整数)であることも必要条件にしていました。メジャーバージョンが違う場合はversion-specを満たさないとみなされたのです。しかし、この挙動はドキュメントには書かれておらず、おそらく仕様として依拠することはできなかったし、一部のユーザーにとっては悩みの種でした。ASDF 3.0.1から、version-satisfies
はメジャーバージョンを特別扱いしなくなり、単にバージョンが同じか新しければt
を返すようになりました。
なお、バージョン指定付きの依存関係を表す(:version ...)
構文32は現在は特定のバージョン「以上」という指定しかできませんが、将来的には「未満」も指定できるようになるかもしれません。
ASDFがシステム定義ファイルを探す場所は、ソースレジストリ(source registry)に指定されています。
[訳者補足]
ソースレジストリ(や次章で解説するアウトプットトランスレーション)の設定を記述したファイルは、システム設定ファイル(system configuration file)または単に設定ファイル(configuration file)と呼ばれます。ファイル形式は.confです。システム定義ファイル(.asdファイル)と混同しないようにしましょう。さらに、設定ディレクトリ(configuration directory)という概念もあります。設定ディレクトリは設定ファイルを含むディレクトリですが、設定ディレクトリ中の設定ファイルはその文法が微妙に異なるので注意が必要です。詳しくはソースレジストリを設定するDSLと設定ディレクトリ(ソースレジストリ)を参照してください。
また、以下の記述は、OSや処理系、環境変数によって異なる複雑なロジックを漏れなく解説したものではありませんが、これを補完するのは訳者の手に余ります。正確な挙動を把握する必要がある場合はsource-registry.lispとuiop/configuration.lispを参照してください。
ソースレジストリは次のように設定されます。おおむね、上から順に処理されていくと考えてかまいません。
[訳者補足終わり]
CL_SOURCE_REGISTRY
が存在する場合は、ソースレジストリがこの変数によって設定されます。
XDG_CONFIG_DIRS
とユーザー設定ファイル$XDG_CONFIG_DIRS/common-lisp/source-registry.confが存在する場合は、ソースレジストリがこのファイルによって設定されます。XDG_CONFIG_DIRS
のデフォルトの値は~/.config/です。
XDG_CONFIG_DIRS
とユーザー設定ディレクトリ$XDG_CONFIG_DIRS/common-lisp/source-registry.conf.d/が存在する場合は、ソースレジストリがこのディレクトリ内の設定ファイルによって設定されます。
XDG_DATA_HOME
のデフォルトの値は~/.local/share/です。Windowsで環境変数XDG_DATA_HOME
が存在しない場合は、代わりにLocal AppData
またはAppData
が使われます。
XDG_DATA_DIRS
のデフォルトは/usr/local/shareと/usr/shareです。WindowsでXDG_DATA_DIRS
が存在しない場合、代わりにAppData
とCommon AppData
が使われるでしょう。
これらの設定は、S式による簡単なDSLで記述します。(→ ソースレジストリを設定するDSL) 加えて、環境変数で設定する場合は、よりシェルに向いた文法も用意されています。(→ シェル向けの設定の構文(ソースレジストリ))
また、上に挙げた各設定は、必ずしもすべてが「結合」して使われるわけではありません。各設定が、継承する設定を明示的にあるいは暗黙にインクルードしない限り、以降の設定は無視されることになります。
さらに、処理系依存のディレクトリが、設定ファイルで指定されたディレクトリ群の先頭に自動的に加わることがあり、これは設定ファイルで継承をどのように設定しても変えられません。(訳注: 上記リストの1番目の処理のこと)
初期のASDFが導入した偉大な革新の1つは、ソースコードの場所やビルドを行う場所を決めるのにcl:truename
を活用したことでした。これによって(システム定義ファイルへの)シンボリックリンクを集めたディレクトリを管理の対象にできるようになりました。このメカニズムは単純ですが効果的であり、プログラムで扱うのが容易でした。ASDF 3は今なおこのスタイルをサポートしていますし、デフォルトで有効です。しかし我々は、以下で解説するソースレジストリの仕組みを代わりに使うことを推奨します。そちらのほうが特定のユーザー、処理系に依存しないポータブルな設定をするのが容易だからです。
また、truename
を嫌う人もいます。というのも、システムによってはtruename
はとても遅いし、また、いわゆる内容アドレス記憶装置(CAS, content-addressable storage)を使っている場合、ファイルのtruename
が指すのは個々のコンテンツのダイジェストであり、同じプロジェクト内の別のファイルではないからです。そこで、ASDF 3にはtruename
を避ける方法も用意されています: asdf:*resolve-symlinks*
にnil
をセットしてください。
PS: Vernor Vingeの短編 “True Names... and Other Dangers”(邦題: 『マイクロチップの魔術師』)を読んだことがない方は、きっと楽しめるでしょう。偉大なSFの古典です。
設定ファイルやデータファイル、キャッシュファイルを保持する場所に関しては、我々はXDGベースディレクトリの仕様に従うことを目指しています。XDGの環境変数については、次のドキュメントを参照しています。
http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
この規格は、ユーザーがアプリケーションをカスタマイズするための環境変数を規定しています。
ASDF 3.1.5より、Windows上で(Cygwin不使用で)もXDGベースディレクトリの仕様に可能な限り従うようになりました。もっとも、この規格はWindowsアプリケーションの一般的な慣習に必ずしも適合するわけではありません。この際、Unixのパス~/.local、/usr/local、/usrは、おおむねWindows上で同等と考えられるLocal AppData、AppData、Common AppDataなどに置き換えています。また、Windowsのレジストリに問い合わせるのは、大量のポータブルなCommon Lispコードにおいては不可能ですから、ASDF 3は(レジストリと同期していることを願って)対応する環境変数を使っています。詳細についてはuiop/configuration.lispを参照してください。また、改善案があればぜひお願いします。
後方互換性のために、また、ハッカーに実用的なバックドアを提供するために、ASDF 3はセントラルレジストリという古い仕組みもサポートしています。ASDFは、ソースレジストリを探す前にまずasdf:*central-registry*
に指定されたディレクトリから.asdファイルを探します。
この変数の使い方についてはASDFがシステムを見つけられるように設定する(古いスタイル)を参照してください。
デフォルトではasdf:*central-registry*
は空でしょう。
つまり、この古い方法は、使わなければあなたには関係ありませんが、使う場合は新しい仕組みに優先することになります。
ソースレジストリの設定は、S式によるDSLで行います。以下はその文法です:
;; 設定の1単位は1つのS式であり、S式はキーワード:source-registryから
;; 始まって1つ以上のディレクティブが続きます。
CONFIGURATION := (:source-registry DIRECTIVE ...)
;; ディレクティブは次のいずれかです:
DIRECTIVE :=
;; 継承ディレクティブ:
;; CONFIGURATIONは必ず次の2つのディレクティブのいずれかを含ま
;; なければなりません。
:inherit-configuration |
;; 継承した設定をその位置に挿入するディレクティブで、多くの場合、
;; 最後に指定されます。(34)
:ignore-inherited-configuration |
;; 継承した設定を除外します。どこに指定してもかまいません。
;; 前方互換性のためのディレクティブ(ASDF 2.011.4)以降です。新し
;; い設定機能を使いたいが、その機能をサポートしていない古いASDFか
;; ら新しいASDFをブートストラップして使っているという場合には、こ
;; のディレクティブが必要でしょう。
:ignore-invalid-entries |
;; 1つのディレクトリを探索の対象に加えます。(ただし再帰的探索はしない)
(:directory DIRECTORY-PATHNAME-DESIGNATOR) |
;; 1つのディレクトリ階層を再帰的探索の対象に加えます。
;; 除外するパターンは下述のディレクティブで指定できます。
(:tree DIRECTORY-PATHNAME-DESIGNATOR) |
;; デフォルトの除外パターンを上書きします。
(:exclude EXCLUSION-PATTERN ...) |
;; デフォルトの除外パターンに追加します。
(:also-exclude EXCLUSION-PATTERN ...) |
;; 除外パターンが適用されるスコープはその設定(もしくはファイル)
;; の残りの部分です。
;; 別の設定ファイルの内容をパースして、この位置に挿入します。
(:include REGULAR-FILE-PATHNAME-DESIGNATOR) |
;; デフォルトのレジストリをこの位置に挿入します。(35)
:default-registry
REGULAR-FILE-PATHNAME-DESIGNATOR
:= PATHNAME-DESIGNATOR ; ファイル名として解釈されます
DIRECTORY-PATHNAME-DESIGNATOR
:= PATHNAME-DESIGNATOR ; ディレクトリ名として解釈されます
PATHNAME-DESIGNATOR :=
NIL | ; このエントリをスキップします
ABSOLUTE-COMPONENT-DESIGNATOR ; パス名のDSLを参照(下述)
EXCLUSION-PATTERN := ワイルドカードを含まない文字列です。あるサブディ
レクトリの名前とこの文字列が正確に一致する場合、そのパス名は除外されま
す。例えば"_darcs"
は#p"/foo/bar/_darcs/src/bar.asd"にマッチする
でしょう。
パス名は別のDSLで指定されます。このDSLはアウトプットトランスレーションの設定と共通であり、asdf:resolve-location
によって解決されます。この関数はまだドキュメント化されていません。
ABSOLUTE-COMPONENT-DESIGNATOR := (ABSOLUTE-COMPONENT-DESIGNATOR RELATIVE-COMPONENT-DESIGNATOR ...) | STRING | ;; 文字列はネームストリング(絶対パスを推奨、ディレクトリが必要な ;; 箇所ではディレクトリと判断される)です。アウトプットトランスレー ;; ションにおいては、常にディレクトリと判断され、最後に**/*.*.*が ;; 加わります。MCLではMacOS XスタイルのPOSIXネームストリングが使わ ;; れます。(MacOS 9のスタイルが必要であれば、リテラル#p"..."を使 ;; いましょう。) ;; なお、この文字列は*central-registry*で使われる文字列とは異なる ;; ことに注意してください。セントラルレジストリはこのDSLを使ってお ;; らず、普通のネームストリングとして処理されます。しかし、 ;; asdf:resolve-locationを呼べば、*central-regisrry*で使えるオブジェ ;; クトは得られます: ;; (asdf:resolve-location "/Users/fare/cl/cl-foo/") PATHNAME | ;; パス名は絶対パスを推奨します。アウトプットトランスレーションに ;; おいては(その後にRELATIVE-COMPONENT-DESIGNATORが続かない限り) ;; **/*.*.*のようなワイルドカードを含めたほうがよいでしょう。 :HOME | ; user-homedir-pathname、つまり~/を指します :USER-CACHE | ; ユーザーキャッシュの既定の場所を探します(36) :HERE | ;; 設定ファイルのあるディレクトリを指します。(対話的に呼び出され ;; た場合は*default-pathname-defaults*です。) :ROOT ;; アウトプットトランスレーションでのみ使われます。入力指示子では ;; すべてのホストとデバイスにマッチし、出力指示子では入力パスのホ ;; ストとデバイス(のルート)を表します。 キーワード:SYSTEM-CACHEはASDF 3.1以降では受け付けません。セキュリティ 上の問題を引き起こすためです。 RELATIVE-COMPONENT-DESIGNATOR := (RELATIVE-COMPONENT-DESIGNATOR RELATIVE-COMPONENT-DESIGNATOR ...) | STRING | ;; parse-unix-namestringで解釈される相対パスです。アウトプットト ;; ランスレーションにおいては、最後のコンポーネントに**/*.*.*が ;; 加わります。 PATHNAME | ; 末尾にある場合を除いて、ディレクトリと判断されます。 :IMPLEMENTATION | ;; implementation-identifierに基づくディレクトリです。 ;; 例えばsbcl-1.0.45-linux-x64/など。 :IMPLEMENTATION-TYPE | ;; lisp-implementation-typeに基づくディレクトリです。例えばsbcl/など。 :*/ | ; 直下のすべてのサブディレクトリです。(ASDF 2.011.4以降) :**/ | ; 再帰的に辿れるすべてのサブディレクトリです。(ASDF 2.011.4以降) :*.*.* ; すべてのファイルです。(ASDF 2.011.4以降) キーワード:UID、:USERNAME、:DEFAULT-DIRECTORYは今ではサポートされていません。
単純な例を挙げてみましょう。~/.config/common-lisp/source-registry.confは、ASDFが設定ファイルを探す既定のパスです。このファイルの内容が
(:source-registry (:tree (:home "cl")) :inherit-configuration)
であれば、ソースレジストリに~/cl/が加わり、ASDFがシステム定義ファイルを見つける際には、このディレクトリ以下が再帰的に探索されることになります。
設定ディレクトリ内の.confファイルのDSLは、ソースレジストリを設定するDSLと少し異なります。まず、ディレクティブを(:source-registry ...)
で囲む必要はありません。また、継承ディレクティブ:inherit-configuration
、:ignore-inherited-configuration
は使えず、ディレクティブのリストの末尾に暗黙に:inherit-configuration
が加わります。これらに反した設定ファイルを読み込むと、ASDFはuiop:invalid-configuration
エラーを通知するでしょう。なお、ディレクトリ中の設定ファイルは、そのネームストリングに従ってstring<
の順で読み込まれ、ディレクティブもこの順番で結合されます。
Common Lispソフトウェアのディストリビューションは、自身の設定ファイル(例えば10-foo.conf)を設置する際、すべてのユーザーに対する設定は/etc/common-lisp/source-registry.conf.d/10-foo.confを、ユーザー単位の設定は~/.config/common-lisp/source-registry.conf.d/10-foo.confを使うことができます。そうすれば、インストールされるソフトウェアの設定情報を、容易かつモジュール化された形で登録できるでしょう。例えばDebianのパッケージや、将来のバージョンのclbuild
がそうなることでしょう。37
設定ディレクトリ内の設定ファイル名は、上で挙げたように2つの数字から始めるのが慣習です。こうすることで、設定が読み込まれる順番を決めることができます。また、ファイル形式は.confでなければなりません。この仕様のメリットは、単に実装が簡単であるだけでなく、設定ファイルを無効にするにはただファイル形式を変えればよい、という点にもあります。
設定ファイル内で別の設定ディレクトリをインクルードしたい場合は、ディレクトリのパス名か文字列を:include
ディレクティブに指定してください:
(:include "/foo/bar/")
例として、前のセクションの~/.config/common-lisp/source-registry.confと同じ設定を、設定ディレクトリで行ってみましょう。ファイル~/.config/common-lisp/source-registry.conf.d/33-home-fare-cl.confを次の内容で作ります38:
(:tree "/home/fare/cl/")
:here
ディレクティブ:here
ディレクティブは、いま処理されている設定ファイルのあるディレクトリを、絶対パスで指します。
:here
ディレクティブは、複雑なシステムの配布を簡単にするために、また、リビジョン管理システムの下にあるプロジェクトを簡単に設定できるようにするために用意されています。我々のデザイン原則――プロジェクトの各参加者は各々だけがアクセスできる情報をすべて提供できるべきである――に従ってのことです。
例えば、Xさんという人が、複雑なプロジェクトのソースコードリポジトリを設定し、マスターディレクトリをdir/にしたとしましょう。普通なら、ユーザーには次のディレクティブを加えてもらうことになるかもしれません。
(:tree "path/to/dir")
しかし、dir/以下には巨大なサブツリーがあって、Javaのソースコードやアイコンの画像など、多様なファイルが存在するとしたらどうでしょうか。ASDFのシステム定義ファイルがあるのはdir/src/lisp/とdir/extlib/lisp/以下のツリーだけで、探すべきなのもそこだけだとしましょう。
この場合、Xさんはdir/に次のようなasdf.confファイルを置くでしょう。
(:source-registry (:tree (:here "src/lisp/")) (:tree (:here "extlib/lisp")) (:directory (:here "outlier/")))
こうすれば、Yさんがこのリポジトリのコピーをチェックアウトした時、自分の設定ファイルに
(:include "/path/to/my/checkout/directory/asdf.conf")
の1行を加えるだけで済みます。あるいは、
(asdf:initialize-source-registry '(:source-registry (:include "/path/to/my/checkout/directory/asdf.conf") :inherit-configuration))
をREPLで実行してもかまいません。ASDFはXさんが提供したasdf.confファイルを見つけて、その内容通りにソースの場所を(dir/を基点にした相対パスを解釈して)設定するでしょう。
環境変数CL_SOURCE_REGISTRY
が空の文字列の時、ASDFはこの設定をスキップします。また、(
から始まる場合は、先に述べたDSLによるS式と解釈し、全体にread
を適用します。しかし、それ以外の場合は別の構文によるパスのリストとみなします。この構文は以下の通りで、TEXINPUTS
のスタイルに似ています。
:
であり、それ以外(主にWindows)では;
です。
:directory
ディレクティブと同等であり、再帰的探索はしません。
//
の場合は、再帰的探索の対象になります。:tree
ディレクティブと同等です。
念のため説明しておくと、与えられたシステム名を持つシステムを探すとき、ディレクティブは順番に処理されていきます。
ディレクトリ(:directory
)を探索する場合、システムが見つかれば探索は成功であり、見つからなければ次の探索に進みます。
ツリー(:tree
)を探索する場合、システムがただ1つ見つかれば探索は成功です。複数のシステムが見つかった時の動作はバージョンによって異なりますが、ASDF 3.2.1以降では、パス名の(ノーマライズされた)ディレクトリコンポーネント40の長さを比較し、短いほうが探索結果として採用されるようになりました。長さが同じ場合は、パス名のunix-namestring
をstring<
で比較して小さいほうが採用されます。それ以前のASDFでは最初に見つかったシステムを返していましたが、どのシステムが返るかは処理系によって異なり、上の仕様のようにstring<
に関して小さいパス名が返るかどうかも不確定でした。また、XCVB41ではエラーが通知されていました。システムが見つからなかった場合は次の探索に進みます。
:exclude
、:also-exclude
ディレクティブには、ツリーを探索するときに除外するサブディレクトリ名を指定します。一般的には、バージョン管理システムのデータベースディレクトリ(.git、_darcsなど)は除外されるでしょう。なお、除外の指定は継承された設定には伝搬しません。すべての設定は、デフォルトではasdf::*default-source-registry-exclusions*
に登録されている除外パターンを採用します。
:include
ディレクティブでインクルードされた設定については、探索は再帰的に適用されます。
また、:inherit-configuration
ディレクティブで継承された設定についても、探索は再帰的に適用されます。
ソースレジストリがどのタイミングで構成されるかは、実装に任されています。つまり、ソースレジストリの実装は、設定とファイルシステムからソースレジストリを先行して(eagerly)構成してもかまわないし、あるいは情報が必要になるたびに(lazily)再構成してもかまいません。また、一部をキャッシュすることも許されています。実践的には、推奨される実装とは、ソースレジストリを先行して構成してキャッシュし、設定やファイルシステムの変更を反映したい場合はユーザーが明示的にキャッシュをクリアしなくてはならないというものです。42
いっぽう、古いスタイルである*central-registry*
のメカニズムは、ファイルシステムに毎回問い合わせるというものでした。
ファイルシステムを変更したあと、キャッシュを明示的にクリアする方法についてはソースレジストリのAPIを参照してください。例えばasdf:clear-source-registry
を呼ぶなどの方法があります。
ASDF 3.1.4以降では、ツリー内にある.asdファイルの場所を保持する、永続的なキャッシュファイルを作れるようになりました。仕様を説明すると、:tree
ディレクティブで指定されたディレクトリ以下を再帰的に探索している途中で.cl-source-registry.cacheファイルを発見した場合、そのファイルに指定された.asdファイルの場所だけを登録し、再帰を打ち切るようになりました。.cl-source-registry.cacheは:source-registry-cache
から始まる1つのフォームで構成され、(:source-registry-cache "foo/bar.asd" "path/to/more.asd" ...)
のように、ツリーに含まれる.asdファイルがunix-namestring
で指定されます。なお、(:source-registry-cache)
のような空の指定も許されており、その場合は単にその再帰を打ち切るだけになります。43
このキャッシュファイルを新たに作りたい場合、あるいはソースリポジトリのインストール・アップデート・削除などを行ったあとにキャッシュファイルをアップデートしたい場合、ASDFに含まれるスクリプトが使えます: tools/cl-source-registry-cache.lisp /path/to/directory
。また、キャッシュを無効にしたい場合は単に.cl-source-registry.cacheを削除すればよいでしょう。いずれの場合も、起動中のLispプロセスにこの変更を反映するためには(asdf:clear-source-registry)
などで(メモリ上の)キャッシュをクリアする必要があります。
開発者は自分の開発ツリー内に安全にキャッシュファイルを作ることができますし、もしもソースツリーにたくさんのファイルやディレクトリが含まれているのであれば、そうすることを勧めます。このキャッシュファイルをアップデートする必要があるのは、.asdファイルを作成・削除・移動したときだけです。また、ソフトウェアディストリビューションの管理者も安全にキャッシュファイルを作ることができますが、ソースリポジトリやインストールパッケージをインストール・アップデート・アンインストールする際にキャッシュファイルが更新されるように注意しなければなりません。最後に、経験ある開発者でソースレジストリ内の大量のコードを管理しなければならない方は、Lispプログラムを高速に起動するためにキャッシュファイルを手動で管理することも可能です。
この永続的なキャッシュは起動を高速化するのに役立ちます。例えば数百ものソースレジストリを1つのマシンに抱えている場合、キャッシュファイルがあれば、SBCLで#!/usr/bin/cl
スクリプトを起動するのが半秒ほど早くなりますし、他の処理系ではさらに差が大きいでしょう。これは主観的なインタラクティビティやユーザビリティに大きな違いをもたらします。なお、この起動の高速化を実現するには、処理系付属のASDFのバージョンが3.1.3.7以降である必要があります。すなわち、新しいASDFをソースレジストリに設定された場所に置いて処理系付属のASDFからブートストラップするというやり方では、この方式の恩恵は得られないでしょう。なぜなら、新しいASDFをキャッシュ無しで探すことになるからです。(→ 処理系付属のASDFを置き換える)
次の関数はasdf
パッケージからエクスポートされています。
設定を読み込み、すべての内部変数を初期化します。その際、引数を与えることで、環境変数や設定ファイルによる設定を上書きしたり拡張したりすることができます。parameterがnil
の時は設定の上書きは起こりません。S式の時はS式によるDSLが適用されます。文字列の時はシェル向けのDSLが適用されます。パス名の時は対応する設定ファイル・設定ディレクトリから設定が読み込まれます。シンボルの時はfuncall
され、返ったオブジェクトの型に応じて同じ処理が適用されます。
ソースレジストリの現在の設定と探索アルゴリズムによるキャッシュをすべて削除します。コアイメージのダンプをする際、そのイメージを別の(ソースレジストリの)設定で復元する予定であれば、その前にこの関数(あるいはclear-configuration
)を呼んで、設定を空にしておく必要があるかもしれません。なお、この関数を呼んでも、現在のイメージにロードされているシステム定義は削除されないことに注意してください。消えるのは(まだシステム定義が読み込まれていない)システムを見つける際にどこを探すか、という情報だけです。
ソースレジストリが既にinitialize-source-registry
で初期化されたかどうかチェックします。されていない場合はinitialize-source-registry
を呼びます。
find-system
が呼ばれた時、(直接呼ばれたにせよ、operate
やload-system
などから呼ばれたにせよ)ASDFは(ensure-source-registry nil)
を実行します。つまり、find-system
が初めて呼ばれた時に、ソースレジストリは初期化されるわけです。また、設定ファイルを変更したあとは、明示的にinitialize-source-registry
で初期化するか、あるいはclear-source-registry
やclear-configuration
でソースレジストリをクリアし、次のfind-system
で初期化が行われるようにする必要があります。
*source-registry-parameter*
変数*source-registry-parameter*
は、前回initialize-source-registry
が呼ばれた時の引数を保持しているので、過去のソースレジストリの設定について知りたい時に参照できます。デフォルトはnil
です。この変数はリードオンリーです。書き込んではいけません。
システム間の依存関係について知りたい時には、次の3つの関数が使えます。これらは依存関係の解析を行いたいプログラマのために用意されています。
systemが弱依存しているシステムの名前のリストを返します。弱依存は廃止予定の機能であり、使うことは推奨されていません。defsystem
フォームの:weakly-depends-on
オプションに指定されたシステムについては、ASDFはそれらのシステムを探しますが、仮に見つからなくてもエラーを通知しません。(→ 弱い依存関係)
上の2つの関数については、返り値はdependency-def
のリスト(→ defsystemの文法)であるため、必ずしもシステム名のリストとは限りません。いっぽう、system-weakly-depends-on
は常にシステム名のリストを返します。
ソースレジストリの仕組みは大いに成功をおさめたので、我々はもはやasdf:*central-registry*
は推奨しないと宣言しました。しかし、サポートは継続するでしょう。
探索の仕組みのうち、処理系に依存する部分についてはすべてasdf::wrapping-source-registry
関数(ソースレジストリの設定の1番目の処理にあたる)に統合され、常に暗黙に利用されています。
ASDF 2の開発中に私(FRR)が検討し、却下した代案には、次のようなものがあります。
asdf:*central-registry*
as the master with its current semantics,
and somehow the configuration parser expands the new configuration
language into a expanded series of directories of subdirectories to
lookup, pre-recursing through specified hierarchies. This is kludgy,
and leaves little space of future cleanups and extensions.
asdf:*central-registry*
as the master but extend its semantics
in completely new ways, so that new kinds of entries may be implemented
as a recursive search, etc. This seems somewhat backwards.
asdf:*central-registry*
and break backwards compatibility.
Hopefully this will happen in a few years after everyone migrate to
a better ASDF and/or to XCVB, but it would be very bad to do it now.
asdf:*central-registry*
by a symbol-macro with appropriate magic
when you dereference it or setf it. Only the new variable with new
semantics is handled by the new search procedure.
Complex and still introduces subtle semantic issues.
また、私は次の機能を提案しましたが、けっきょく却下しました。ASDFを必要以上に複雑にしないためです。
(:add-directory X)
を(:directory X)
と、(:add-directory-hierarchy X)
や(:add-directory X :recurse t)
を(:tree X)
と同等にする、など。
user-homedir-pathname
と$SBCL_HOME
を使うことになります。おそらく、これらは既に:default-registry
ディレクティブで置き換えられているでしょう。
//
の代わりにシェル向けでない/**
にする案。いずれにせよLispの構文とは相性がよくありません。
最初のアイデアについてはStelian Ionescuに多大な感謝をささげます。
最初の実装の試みについてはRommel Martinezに感謝をささげます。
良いデザイン案やエレガントな実装のトリックは彼らのおかげですが、悪いデザイン案や実装のバグはすべて私に帰するものです。
— Francois-Rene Rideau fare@tunes.org, Mon, 22 Feb 2010 00:07:33 -0500
fasl44、つまりコンパイルされたファイルのフォーマットは、処理系によって異なります。faslを扱うにあたっては、厄介な問題がいくつか存在します。まず、複数の処理系(あるいは1つの処理系の複数のバージョン)を使っている場合に、ソースディレクトリが様々な形式のfaslファイル(fasl、dfsl、cfslなど)で散らかってしまいます。その上、異なる処理系が同じ拡張子を使っていることがあり、また、同じ処理系でもバージョン間、プラットフォーム間でfaslの互換性がないのに同じ拡張子を維持していたりします。このため、別の処理系に切り替えたときに、膨大なエラーと混乱が引き起こされるかもしれません。さらに、faslをソースファイルと同じディレクトリに保持するスタイルは、ソースディレクトリへの書き込みを必要とするので、複数のユーザーが同じソースディレクトリを共有するのを困難にします。
この問題を軽減するため、ASDF 2以降にはasdf-output-translations
機能が備わっています。
アウトプットトランスレーション(output translations)は(ソースファイルなどの)入力パスに(faslなどの)出力パスを対応させる仕組みです。アウトプットトランスレーションの設定ファイルは、ソースレジストリと同様に、XDGベースディレクトリの仕様に沿った場所に置かれます。
[訳者補足]
アウトプットトランスレーション(や前章で解説したソースレジストリ)の設定を記述したファイルは、システム設定ファイル(system configuration file)または単に設定ファイル(configuration file)と呼ばれます。ファイル形式は.confです。システム定義ファイル(.asdファイル)と混同しないようにしましょう。さらに、設定ディレクトリ(configuration directory)という概念もあります。設定ディレクトリは設定ファイルを含むディレクトリですが、設定ディレクトリ中の設定ファイルはその文法が微妙に異なるので注意が必要です。詳しくはアウトプットトランスレーションを設定するDSLと設定ディレクトリ(アウトプットトランスレーション)を参照してください。
また、以下の記述は、OSや処理系、環境変数によって異なる複雑なロジックを漏れなく解説したものではありませんが、これを補完するのは訳者の手に余ります。正確な挙動を把握する必要がある場合はoutput-translations.lispとuiop/configuration.lispを参照してください。
アウトプットトランスレーションは次のように設定されます。おおむね、上から順に処理されていくと考えてかまいません。
[訳者補足終わり]
ASDF_OUTPUT_TRANSLATIONS
が存在する場合は、アウトプットトランスレーションがこの変数によって設定されます。
XDG_CONFIG_DIRS
とユーザー設定ファイル$XDG_CONFIG_DIRS/common-lisp/asdf-output-translations.confが存在する場合は、アウトプットトランスレーションがこのファイルによって設定されます。XDG_CONFIG_DIRS
のデフォルトの値は~/.config/です。
XDG_CONFIG_DIRS
とユーザー設定ディレクトリ$XDG_CONFIG_DIRS/common-lisp/asdf-output-translations.conf.d/が存在する場合は、アウトプットトランスレーションがこのディレクトリ内の設定ファイルによって設定されます。
これらの設定は、S式による簡単なDSLで記述します。(→ アウトプットトランスレーションを設定するDSL) 加えて、環境変数で設定する場合は、よりシェルに向いた構文も用意されています。(→ シェル向けの設定の構文(アウトプットトランスレーション))
また、上に挙げた各設定は、必ずしもすべてが「結合」して使われるわけではありません。各設定が、継承する設定を明示的にあるいは暗黙にインクルードしない限り、以降の設定は無視されることになります。
なお、アウトプットトランスレーションのデフォルトの設定では、出力ファイルにはユーザーごとに分離されたキャッシュディレクトリが使われます。46 この仕様によって、インストールされたソフトウェアを複数のユーザー間でシームレスに使うことができますし、開発者がソースコードを閲覧する際に出力ファイルが邪魔にならないようにできます。もっとも、開発者が出力ファイルを掃除したくなった場合にアウトプットトランスレーションについて学習する必要がある、という代償もありますが。47
アウトプットトランスレーションと同様の機能を持つ従来のツールとしてASDF-Binary-Locations
、common-lisp-controller
、cl-launch
などがありましたが、ASDFにはこれらの以前のバージョンとの後方互換性がありません。(この仕様は意図的なものです。)従来のツールは、ユーザーが設定ファイルで簡単に設定できるようなデザインになっていませんでした。なお、最近のバージョンのcommon-lisp-controller
(7.2)とcl-launch
(3.000)はasdf-output-translations
のAPIを使っており、また、ASDF-Binary-Locations
は完全に置き換えられているのでもはや使うべきではありません。
もっとも、この非互換性によって不便を被る人はあまりいないでしょう。実のところ、これらのツールをカスタマイズして使っている人はほとんどいませんし、その少数の人々はエキスパートであり、新しい設定に容易に適応するでしょう。そして、エキスパートでないほとんどの人々は(デフォルトの設定で使えている限りは)これらのツールを適切に設定することができず、システムのディストリビュータによる設定やデフォルト設定によってソフトウェアが「ただ動く」ことを知るだけでしょうから。
しかし、ASDF-Binary-Locations
を好む方のために、限定的なエミュレーションモードが用意されています:
この機能はasdf-output-translations
の初期化をASDF-Binary-Locations
と似た仕組みで行います。ASDF-Binary-Locations
はいくつかのグローバル変数(*centralize-lisp-binaries*
、*default-toplevel-directory*
、*include-per-user-information*
、*map-all-source-files*
、*source-to-target-mappings*)で設定を行っていましたが、この関数では同名のキーワード引数に値を渡すことで、設定が可能になります。なお、留意点として、:source-to-target-mappings
引数の値は、単に文字列とパス名だけではなくasdf-output-translations
で使える有効なパス名指示子であればなんでもよい、ということを述べておきます。
また、アウトプットトランスレーションはデフォルトで有効になっていることに気をつけましょう。無効にするためには(asdf:disable-output-translations)
を実行します。特に、従来のASDF-Binary-Locations
(ASDFの古いバージョンに組み込まれたものではなく、最新版に拡張としてロードできるもの)を使い続ける方は、始めに無効にするように気を付けてください。そうしないと「興味深い」問題が起きるかもしれません。
アウトプットトランスレーションの設定は、S式によるDSLで行います。以下はその文法です:
;; 設定の1単位は1つのS式であり、S式はキーワード:source-registryから ;; 始まって1つ以上のディレクティブが続きます。 CONFIGURATION := (:output-translations DIRECTIVE ...) ;; ディレクティブは次のいずれかです: DIRECTIVE := ;; 継承ディレクティブ: ;; CONFIGURATIONは必ず次の2つのディレクティブのいずれかを含ま ;; なければなりません。 :inherit-configuration | ;; 継承した設定をその位置に挿入するディレクティブで、多くの場合、 ;; 最後に指定されます。(48) :ignore-inherited-configuration | ;; 継承した設定を除外します。どこに指定してもかまいません。 ;; 前方互換性のためのディレクティブ(ASDF 2.011.4)以降です。新し ;; い設定機能を使いたいが、その機能をサポートしていない古いASDFか ;; ら新しいASDFをブートストラップして使っているという場合には、こ ;; のディレクティブが必要でしょう。 :ignore-invalid-entries | ;; 別の設定ファイルまたは設定ディレクトリをインクルードします。 (:include PATHNAME-DESIGNATOR) | ;; ユーザーキャッシュを有効にします。環境に応じて、例えば ;; ~/.common-lisp/cache/sbcl-1.0.45-linux-amd64/や ;; C:/Users/foo/AppData/Local/cache/common-lisp/sbcl-1.4.2-win-x64/ ;; などが使われるでしょう。デフォルトでは有効です。 :enable-user-cache | ;; ユーザーキャッシュを無効にします。/が/にマップされることになり ;; ます。 :disable-cache | ;; 1つのマッピングを(入力指示子 出力指示子)として追加します。 (DIRECTORY-DESIGNATOR DIRECTORY-DESIGNATOR) ;; 1つのマッピングを追加します。入力パスの変換は関数で行われます。 (DIRECTORY-DESIGNATOR (:function TRANSLATION-FUNCTION)) DIRECTORY-DESIGNATOR := NIL | ; 入力に指定した場合: このエントリをスキップします。 ; 出力に指定した場合: 入力されたパス名を同じパス名にマップします。 T | ; 入力に指定した場合: あらゆる入力パスに対応します。 ; 出力に指定した場合: 入力されたパス名を同じパス名にマップします。 ABSOLUTE-COMPONENT-DESIGNATOR ; → ソースレジストリを設定するDSL TRANSLATION-FUNCTION := ;; 入力パスを出力パスに変換する関数です。この関数は2つの引数を取 ;; り、1つ目は変換するべきパス名であり、2つ目は入力に指定した ;; DIRECTORY-DESIGNATOR(から導出されたワイルドカードを含むパス名) ;; です。 SYMBOL | LAMBDA
ABSOLUTE-COMPONENT-DESIGNATORの末尾のコンポーネントがパス名でない場合、/**/*.*が加わっているものとみなされます。末尾のコンポーネントにパス名を使うと、#p"some/path/**/foo*/bar-*.fasl"
のようにより細かいパターンを指定することができます。(→ ソースレジストリを設定するDSL)
#+フィーチャー
のようなフィーチャー表現も設定ファイル中で使うことが出来ます。
入力指示子がt
である場合、すべてのパスにマッチします。入力指示子が:root
から始まる場合、すべてのホストとデバイスにマッチします。いずれの場合も(出力指示子がt
でなく:root
から始まるのでもなければ)apply-output-translations
で入力パスが変換される際に、出力指示子のホストとデバイスが入力パスにマージされることになります。
出力指示子がt
(またはnil
)である場合、マッピングは恒等写像であり、入力パスがそのまま出力パスになります。
出力指示子が:root
から始まる場合、マッピングは入力パスのホストとデバイスを保持します。ファイルが存在するディレクトリのサブディクレトリにマッピングしたい場合にはこの機能を使って実現できますが、旧来の構文は次のようにやや複雑なものでした。
#.(let ((wild-subdir (make-pathname :directory '(:relative :wild-inferiors))) (wild-file (make-pathname :name :wild :version :wild :type :wild))) `((:root ,wild-subdir ,wild-file) (:root ,wild-subdir :implementation ,wild-file)))
しかし、ASDF 2.011.4からは次のように簡単に書けるようになりました。49
(:root (:root :**/ :implementation :*.*.*))
また、ASDF-Binary-Locationsを使いたい方は、enable-asdf-binary-locations-compatibility
を:centralize-lisp-binaries nil
オプションで呼び出しても内部では同じ動作をすることになります。(→ 後方互換性のための機能(アウトプットトランスレーション)
:include
ディレクティブでインクルードされる設定ファイル、設定ディレクトリについては再帰的に処理が適用されることになります。
入力パスから出力パスへの変換はcl:translate-pathname
(をポータブルにラップしたuiop:translate-pathname*
)で行われますが、望む変換ができない場合は、ユーザーは自ら変換アルゴリズムを書いて関数として指定することも出来ます。このような関数は出力指示子で:function
キーワードの後に指定します。関数を指すシンボルでも、ラムダ式でもかまいません。この関数は2つの引数を取り、変換されたパス名を返さなければなりません。1つ目の引数は入力ファイルのパス名であり、2つ目は入力指示子(から導出されたワイルドカードを含むパス名)です。
:inherit-configuration
は次の設定について再帰的に処理を適用します。次の設定とは、アウトプットトランスレーションの設定のリストにおいて下で処理される設定のことです。
ユーザーキャッシュに関するディレクティブは以下の通りです:
:enable-user-cache
は (t :user-cache)
と同等です。
:disable-cache
は(t t)
と同等です。
:user-cache
はasdf:*user-cache*
の値に対応します。unixならデフォルトでは(:home ".cache" "common-lisp" :implementation)
と同等です。50
設定ディレクトリ内の.confファイルのDSLは、アウトプットトランスレーションを設定するDSLと少し異なります。まず、ディレクティブを(:output-translations ...)
で囲む必要はありません。また、継承ディレクティブ:inherit-configuration
、:ignore-inherited-configuration
は使えず、ディレクティブのリストの末尾に暗黙に:inherit-configuration
が加わります。これらに反した設定ファイルを読み込むと、ASDFはuiop:invalid-configuration
エラーを通知するでしょう。なお、ディレクトリ中の設定ファイルは、そのネームストリングに従ってstring<
の順で読み込まれ、ディレクティブもこの順番で結合されます。
Common Lispソフトウェアのディストリビューションは、自身の設定ファイル(例えば10-foo.conf)を設置する際、すべてのユーザーに対する設定は/etc/common-lisp/asdf-output-translations.conf.d/10-foo.confを、ユーザー単位の設定は~/.config/common-lisp/asdf-output-translations.conf.d/10-foo.confを使うことができます。そうすれば、インストールされるソフトウェアの設定情報を、容易かつモジュール化された形で登録できるでしょう。例えばDebianのパッケージや、将来のバージョンのclbuild
がそうなることでしょう。51
設定ディレクトリ内の設定ファイル名は、上で挙げたように2つの数字から始めるのが慣習です。こうすることで、設定が読み込まれる順番を決めることができます。また、ファイル形式は.confでなければなりません。この仕様のメリットは、単に実装が簡単であるだけでなく、設定ファイルを無効にするにはただファイル形式を変えればよい、という点にもあります。
設定ファイル内で別の設定ディレクトリをインクルードしたい場合は、ディレクトリのパス名か文字列を:include
ディレクティブに指定してください:
(:include "/foo/bar/")
ASDFは環境変数ASDF_OUTPUT_TRANSLATIONS
を次のように処理します:
(
から始まる場合は、先に述べたDSLによるS式と解釈し、全体にread
を適用します。
ディレクトリのリストは、正確に言えばディレクトリのペアのリストであり、1つのペアがマッピングのディレクティブを表します。各エントリの区切りは、UNIX(Cygwin含む)では:
であり、それ以外(主にWindows)では;
です。
空のエントリがペアの1つ目のエントリ(つまり入力指示子)の位置に現れた場合、その部分に継承された設定が挿入されます。この場合、2つ目のエントリはないものとして扱われ、次のエントリは新しいペアとみなされます。空のエントリがペアの2つ目のエントリ(つまり出力指示子)の位置に現れた場合、1つ目のエントリと同じディレクトリを指します。(つまり、ディレクトリを繰り返し書いたのと同等です。)
例として"/foo:/bar::/baz:"
という設定を見てみましょう。この設定の最初のペアは/foo:/bar
ですから、/foo/以下のパスは/bar/以下に変換されることになります。その次は空のエントリなので、継承された設定がこの部分に挿入されます。最後のペアは/baz:
であり、これは/baz:/baz
と同等ですから、/baz/以下のパスは変換されないことになります。
From the specified configuration, a list of mappings is extracted in a straightforward way: mappings are collected in order, recursing through included or inherited configuration as specified. To this list is prepended some implementation-specific mappings, and is appended a global default.
The list is then compiled to a mapping table as follows: for each entry, in order, resolve the first designated directory into an actual directory pathname for source locations. If no mapping was specified yet for that location, resolve the second designated directory to an output location directory add a mapping to the table mapping the source location to the output location, and add another mapping from the output location to itself (unless a mapping already exists for the output location).
Based on the table, a mapping function is defined, mapping source pathnames to output pathnames: given a source pathname, locate the longest matching prefix in the source column of the mapping table. Replace that prefix by the corresponding output column in the same row of the table, and return the result. If no match is found, return the source pathname. (A global default mapping the filesystem root to itself may ensure that there will always be a match, with same fall-through semantics).
The implementation is allowed to either eagerly compute the information from the configurations and file system, or to lazily re-compute it every time, or to cache any part of it as it goes. To explicitly flush any information cached by the system, use the API below.
The specified functions are exported from package ASDF.
will read the configuration and initialize all internal variables.
You may extend or override configuration
from the environment and configuration files
with the given PARAMETER, which can be
nil
(no configuration override),
or a SEXP (in the SEXP DSL),
a string (as in the string DSL),
a pathname (of a file or directory with configuration),
or a symbol (fbound to function that when called returns one of the above).
will initialize output translations in a way that maps every pathname to itself, effectively disabling the output translation facility.
undoes any output translation configuration
and clears any cache for the mapping algorithm.
You might want to call this function
(or better, clear-configuration
)
before you dump an image that would be resumed
with a different configuration,
and return an empty configuration.
Note that this does not include clearing information about
systems defined in the current image, only about
where to look for systems not yet defined.
checks whether output translations have been initialized. If not, initialize them with the given PARAMETER. This function will be called before any attempt to operate on a system.
Applies the configured output location translations to PATHNAME
(calls ensure-output-translations
for the translations).
Every time you use ASDF’s output-files
, or
anything that uses it (that may compile, such as operate
, perform
, etc.),
ensure-output-translations
is called with parameter nil
,
which the first time around causes your configuration to be read.
If you change a configuration file,
you need to explicitly initialize-output-translations
again,
or maybe clear-output-translations
(or clear-configuration
),
which will cause the initialization to happen next time around.
Thanks a lot to Peter van Eynde for Common Lisp Controller
and to Bjorn Lindberg and Gary King for ASDF-Binary-Locations
.
All bad design ideas and implementation bugs are to mine, not theirs. But so are good design ideas and elegant implementation tricks.
— Francois-Rene Rideau fare@tunes.org
ASDFが不正なシステム定義を検出すると、system-definition-error
を通知します。
いっぽう、オペレーションが失敗した場合(例えばソースファイルがエラーを含んでいた場合)は、operation-error
が通知されます。
ASDFは、ファイルのコンパイル時の警告、エラーをチェックしており、それらを検出した時の動作は変数*compile-file-warnings-behaviour*
と*compile-file-failure-behaviour*
で制御することができます。有効な値は:error
、:warn
、:ignore
です。
ASDF includes several additional features that are generally useful for system definition and development.
When declaring a component (system, module, file),
you can specify a keyword argument :around-compile function
.
If left unspecified (and therefore unbound),
the value will be inherited from the parent component if any,
or with a default of nil
if no value is specified in any transitive parent.
The argument must be either nil
, an fbound symbol,
a lambda-expression (e.g. (lambda (thunk) ...(funcall thunk ...) ...)
)
a function object (e.g. using #.#'
but that’s discouraged
because it prevents the introspection done by e.g. asdf-dependency-grovel),
or a string that when read
yields a symbol or a lambda-expression.
nil
means the normal compile-file function will be called.
A non-nil value designates a function of one argument
that will be called with a function that will
invoke compile-file*
with various arguments;
the around-compile hook may supply additional keyword arguments
to pass to that call to compile-file*
.
One notable argument that is heeded by compile-file*
is
:compile-check
,
a function called when the compilation was otherwise a success,
with the same arguments as compile-file
;
the function shall return true if the compilation
and its resulting compiled file respected all system-specific invariants,
and false (nil
) if it broke any of those invariants;
it may issue warnings or errors before it returns nil
.
(NB: The ability to pass such extra flags
is only available starting with ASDF 2.22.3.)
This feature is notably exercised by asdf-finalizers.
By using a string, you may reference
a function, symbol and/or package
that will only be created later during the build, but
isn’t yet present at the time the defsystem form is evaluated.
However, if your entire system is using such a hook, you may have to
explicitly override the hook with nil
for all the modules and files
that are compiled before the hook is defined.
Using this hook, you may achieve such effects as: locally renaming packages, binding *readtables* and other syntax-controlling variables, handling warnings and other conditions, proclaiming consistent optimization settings, saving code coverage information, maintaining meta-data about compilation timings, setting gensym counters and PRNG seeds and other sources of non-determinism, overriding the source-location and/or timestamping systems, checking that some compile-time side-effects were properly balanced, etc.
Note that there is no around-load hook. This is on purpose.
Some implementations such as ECL, GCL or MKCL link object files,
which allows for no such hook.
Other implementations allow for concatenating FASL files,
which doesn’t allow for such a hook either.
We aim to discourage something that’s not portable,
and has some dubious impact on performance and semantics
even when it is possible.
Things you might want to do with an around-load hook
are better done around-compile,
though it may at times require some creativity
(see e.g. the package-renaming
system).
Starting with ASDF 2.21, components accept a :encoding
option
so authors may specify which character encoding should be used
to read and evaluate their source code.
When left unspecified, the encoding is inherited
from the parent module or system;
if no encoding is specified at any point,
or if nil
is explicitly specified,
an extensible protocol described below is followed,
that ultimately defaults to :utf-8
since ASDF 3.
The protocol to determine the encoding is
to call the function detect-encoding
,
which itself, if provided a valid file,
calls the function specified by *encoding-detection-hook*,
or else defaults to the *default-encoding*.
The *encoding-detection-hook* is by default bound
to function always-default-encoding
,
that always returns the contents of *default-encoding*.
*default-encoding* is bound to :utf-8
by default
(before ASDF 3, the default was :default
).
Whichever encoding is returned must be a portable keyword,
that will be translated to an implementation-specific external-format designator
by function encoding-external-format
,
which itself simply calls the function specified *encoding-external-format-hook*;
that function by default is default-encoding-external-format
,
that only recognizes :utf-8
and :default
,
and translates the former to the implementation-dependent *utf-8-external-format*,
and the latter to itself (that itself is portable but has an implementation-dependent meaning).
In other words, there now are plenty of extension hooks, but
by default ASDF enforces the previous de facto standard behaviour
of using :utf-8
, independently from
whatever configuration the user may be using.
Thus, system authors can now rely on :utf-8
being used while compiling their files,
even if the user is currently using :koi8-r
or :euc-jp
as their interactive encoding.
(Before ASDF 3, there was no such guarantee, :default
was used,
and only plain ASCII was safe to include in source code.)
Some legacy implementations only support 8-bit characters,
and some implementations provide 8-bit only variants.
On these implementations, the *utf-8-external-format*
gracefully falls back to :default
,
and Unicode characters will be read as multi-character mojibake.
To detect such situations, UIOP will push the :asdf-unicode
feature
on implementations that support Unicode, and you can use reader-conditionalization
to protect any :encoding encoding
statement, as in
#+asdf-unicode :encoding #+asdf-unicode :utf-8
.
We recommend that you avoid using unprotected :encoding
specifications
until after ASDF 2.21 or later becomes widespread.
As of May 2016, all maintained implementations provide ASDF 3.1,
so you may prudently start using this and other features without such protection.
While it offers plenty of hooks for extension,
and one such extension is available (see asdf-encodings
below),
ASDF itself only recognizes one encoding beside :default
,
and that is :utf-8
, which is the de facto standard,
already used by the vast majority of libraries that use more than ASCII.
On implementations that do not support unicode,
the feature :asdf-unicode
is absent, and
the :default
external-format is used
to read even source files declared as :utf-8
.
On these implementations, non-ASCII characters
intended to be read as one CL character
may thus end up being read as multiple CL characters.
In most cases, this shouldn’t affect the software’s semantics:
comments will be skipped just the same, strings with be read and printed
with slightly different lengths, symbol names will be accordingly longer,
but none of it should matter.
But a few systems that actually depend on unicode characters
may fail to work properly, or may work in a subtly different way.
See for instance lambda-reader
.
We invite you to embrace UTF-8
as the encoding for non-ASCII characters starting today,
even without any explicit specification in your .asd files.
Indeed, on some implementations and configurations,
UTF-8 is already the :default
,
and loading your code may cause errors if it is encoded in anything but UTF-8.
Therefore, even with the legacy behaviour,
non-UTF-8 is guaranteed to break for some users,
whereas UTF-8 is pretty much guaranteed not to break anywhere
(provided you do not use a BOM),
although it might be read incorrectly on some implementations.
:utf-8
has been the default value of *default-encoding*
since ASDF 3.
If you need non-standard character encodings for your source code,
use the extension system asdf-encodings
, by specifying
:defsystem-depends-on ("asdf-encodings")
in your defsystem
.
This extension system will register support for more encodings using the
*encoding-external-format-hook*
facility,
so you can explicitly specify :encoding :latin1
in your .asd file.
Using the *encoding-detection-hook*
it will also
eventually implement some autodetection of a file’s encoding
from an emacs-style -*- mode: lisp ; coding: latin1 -*-
declaration,
or otherwise based on an analysis of octet patterns in the file.
At this point, asdf-encoding
only supports the encodings
that are supported as part of your implementation.
Since the list varies depending on implementations,
we still recommend you use :utf-8
everywhere,
which is the most portable (next to it is :latin1
).
Recent versions of Quicklisp include asdf-encodings
;
if you’re not using it, you may get this extension using git:
git clone https://gitlab.common-lisp.net/asdf/asdf-encodings.git
or
git clone git@gitlab.common-lisp.net:asdf/asdf-encodings.git.
You can also browse the repository on
https://gitlab.common-lisp.net/asdf/asdf-encodings.
When you use asdf-encodings
,
any .asd file loaded
will use the autodetection algorithm to determine its encoding.
If you depend on this detection happening,
you should explicitly load asdf-encodings
early in your build.
Note that :defsystem-depends-on
cannot be used here: by the time
the :defsystem-depends-on
is loaded, the enclosing
defsystem
form has already been read.
In practice, this means that the *default-encoding*
is usually used for .asd files.
Currently, this defaults to :utf-8
, and
you should be safe using Unicode characters in those files.
This might matter, for instance, in meta-data about author’s names.
Otherwise, the main data in these files is component (path)names,
and we don’t recommend using non-ASCII characters for these,
for the result probably isn’t very portable.
These functions are exported by ASDF for your convenience.
It’s often handy to locate a file relative to some system.
The system-relative-pathname
function meets this need.
It takes two mandatory arguments system and name
and a keyword argument type:
system is name of a system, whereas name and optionally type
specify a relative pathname, interpreted like a component pathname specifier
by coerce-pathname
. See パス名指定子.
It returns a pathname built from the location of the system’s source directory and the relative pathname. For example:
> (asdf:system-relative-pathname 'cl-ppcre "regex.data") #P"/repository/other/cl-ppcre/regex.data"
ASDF does not provide a turnkey solution for locating
data (or other miscellaneous) files
that are distributed together with the source code of a system.
Programmers can use system-source-directory
to find such files.
Returns a pathname object.
The system-designator may be a string, symbol, or ASDF system object.
It is sometimes useful to force recompilation of a previously loaded system.
For these cases, (asdf:clear-system :foo)
will remove the system from the table of currently loaded systems:
the next time the system foo
or one that depends on it is re-loaded,
foo
will be loaded again.52
Note that this does not and cannot undo
the previous loading of the system.
Common Lisp has no provision for such an operation,
and its reliance on irreversible side-effects to global data structures
makes such a thing impossible in the general case.
If the software being re-loaded is not conceived with hot upgrade in mind,
re-loading may cause many errors, warnings or subtle silent problems,
as packages, generic function signatures, structures, types, macros, constants, etc.
are being redefined incompatibly.
It is up to the user to make sure that reloading is possible and has the desired effect.
In some cases, extreme measures such as recursively deleting packages,
unregistering symbols, defining methods on update-instance-for-redefined-class
and much more are necessary for reloading to happen smoothly.
ASDF itself goes to extensive effort to make a hot upgrade possible
with respect to its own code.
If you want, you can reuse some of its utilities such as
uiop:define-package
and uiop:with-upgradability
,
and get inspiration (or disinspiration)
from what it does in header.lisp and upgrade.lisp.
A system with name name,
created by make-instance
with extra keys keys
(e.g. :version
),
is registered as preloaded.
If version is t
(default), then the version is copied from the defined system
of the same name (if registered) or else is nil
(this automatic copy of version is only available starting since ASDF 3.1.8).
A preloaded system is considered as having already been loaded into the current image,
and if at some point some other system :depends-on
it yet no source code is found,
it is considered as already provided,
and ASDF will not raise a missing-component
error.
This function is particularly useful if you distribute your code
as fasls with either compile-bundle-op
or monolithic-compile-bundle-op
,
and want to register systems so that dependencies will work uniformly
whether you’re using your software from source or from fasl.
Note that if the system was already defined or loaded from source code,
its build information will remain active until you call clear-system
on it,
at which point a system without build information will be registered in its place.
A system with name name is registered as preloaded,
and additionally is marked as immutable:
that is, attempts to compile or load it will be succeed
without actually reading, creating or loading any file,
as if the system was passed as a force-not
argument
to all calls to plan
or operate
.
There will be no search for an updated .asd file
to override the loaded version,
whether from the source-register or any other method.
If a version keyword argument is specified as t
or left unspecified,
then the version is copied from the defined system
of the same name (if registered) or else is nil
.
This automatic copy of version is available starting
since immutable systems have been available in ASDF 3.1.5.
This function, available since ASDF 3.1.5, is particularly useful if you distribute a large body of code as a precompiled image, and want to allow users to extend the image with further extension systems, but without making thousands of filesystem requests looking for inexistent (or worse, out of date) source code for all the systems that came bundled with the image but aren’t distributed as source code to regular users.
This function is obsolete and present only for the sake of backwards-compatibility: “If it’s not backwards, it’s not compatible”. We strongly discourage its use. Its current behaviour is only well-defined on Unix platforms (which include MacOS X and cygwin). On Windows, anything goes. The following documentation is only for the purpose of your migrating away from it in a way that preserves semantics.
Instead we recommend the use run-program
, described in the next section, and
available as part of ASDF since ASDF 3.
run-shell-command
takes as arguments a format control-string
and arguments to be passed to format
after this control-string
to produce a string.
This string is a command that will be evaluated with a POSIX shell if possible;
yet, on Windows, some implementations will use CMD.EXE,
while others (like SBCL) will make an attempt at invoking a POSIX shell
(and fail if it is not present).
The below functions are not exported by ASDF itself, but by UIOP, available since ASDF 3. Some of them have precursors in ASDF 2, but we recommend that for active developments, you should rely on the package UIOP as included in ASDF 3. UIOP provides many, many more utility functions, and we recommend you read its README.md and sources for more information.
Coerce name into a pathname using standard Unix syntax.
Unix syntax is used whether or not the underlying system is Unix;
on non-Unix systems it is only usable for relative pathnames.
In order to manipulate relative pathnames portably, it is crucial
to possess a portable pathname syntax independent of the underlying OS.
This is what parse-unix-namestring
provides, and why we use it in ASDF.
When given a pathname
object, just return it untouched.
When given nil
, just return nil
.
When given a non-null symbol
, first downcase its name and treat it as a string.
When given a string
, portably decompose it into a pathname as below.
#\/
separates directory components.
The last #\/
-separated substring is interpreted as follows:
1- If type is :directory
or ensure-directory is true,
the string is made the last directory component, and its name
and type
are nil
.
if the string is empty, it’s the empty pathname with all slots nil
.
2- If type is nil
, the substring is a file-namestring,
and its name
and type
are separated by split-name-type
.
3- If type is a string, it is the given type
, and the whole string is the name
.
Directory components with an empty name the name .
are removed.
Any directory named ..
is read as dot-dot,
which must be one of :back
or :up
and defaults to :back
.
host
, device
and version
components are taken from defaults,
which itself defaults to *nil-pathname*
.
*nil-pathname*
is also used if defaults is nil
.
No host or device can be specified in the string itself,
which makes it unsuitable for absolute pathnames outside Unix.
For relative pathnames, these components (and hence the defaults) won’t matter
if you use merge-pathnames*
but will matter if you use merge-pathnames
,
which is an important reason to always use merge-pathnames*
.
Arbitrary keys are accepted, and the parse result is passed to ensure-pathname
with those keys, removing type, defaults and dot-dot.
When you’re manipulating pathnames that are supposed to make sense portably
even though the OS may not be Unixish, we recommend you use :want-relative t
so that parse-unix-namestring
will throw an error if the pathname is absolute.
This function is a replacement for merge-pathnames
that uses the host and device
from the defaults rather than the specified pathname when the latter
is a relative pathname. This allows ASDF and its users to create and use relative pathnames
without having to know beforehand what are the host and device
of the absolute pathnames they are relative to.
This function takes a pathname and a subpath and a type.
If subpath is already a pathname
object (not namestring),
and is an absolute pathname at that, it is returned unchanged;
otherwise, subpath is turned into a relative pathname with given type
as per parse-unix-namestring
with :want-relative t :type
type,
then it is merged with the pathname-directory-pathname
of pathname,
as per merge-pathnames*
.
We strongly encourage the use of this function for portably resolving relative pathnames in your code base.
This function returns nil
if the base pathname is nil
,
otherwise acts like subpathname
.
run-program
takes a command argument that is either
a list of a program name or path and its arguments,
or a string to be executed by a shell.
It spawns the command, waits for it to return,
verifies that it exited cleanly (unless told not too below),
and optionally captures and processes its output.
It accepts many keyword arguments to configure its behaviour.
run-program
returns three values: the first for the output,
the second for the error-output, and the third for the return value.
(Beware that before ASDF 3.0.2.11, it didn’t handle input or error-output,
and returned only one value,
the one for the output if any handler was specified, or else the exit code;
please upgrade ASDF, or at least UIOP, to rely on the new enhanced behaviour.)
output is its most important argument;
it specifies how the output is captured and processed.
If it is nil
, then the output is redirected to the null device,
that will discard it.
If it is :interactive
, then it is inherited from the current process
(beware: this may be different from your *standard-output*,
and under SLIME will be on your *inferior-lisp*
buffer).
If it is t
, output goes to your current *standard-output* stream.
Otherwise, output should be a value that is a suitable first argument to
slurp-input-stream
(see below), or
a list of such a value and keyword arguments.
In this case, run-program
will
create a temporary stream for the program output;
the program output, in that stream,
will be processed by a call to slurp-input-stream
,
using output as the first argument
(or if it’s a list the first element of output and the rest as keywords).
The primary value resulting from that call
(or nil
if no call was needed)
will be the first value returned by run-program
.
E.g., using :output :string
will have it return the entire output stream as a string.
And using :output '(:string :stripped t)
will have it return the same string stripped of any ending newline.
error-output is similar to output, except that
the resulting value is returned as the second value of run-program
.
t
designates the *error-output*.
Also :output
means redirecting the error output to the output stream,
in which case nil
is returned.
input is similar to output, except that
vomit-output-stream
is used, no value is returned,
and t
designates the *standard-input*.
element-type
and external-format
are passed on
to your Lisp implementation, when applicable, for creation of the output stream.
One and only one of the stream slurping or vomiting may or may not happen in parallel in parallel with the subprocess, depending on options and implementation, and with priority being given to output processing. Other streams are completely produced or consumed before or after the subprocess is spawned, using temporary files.
force-shell
forces evaluation of the command through a shell,
even if it was passed as a list rather than a string.
If a shell is used, it is /bin/sh on Unix or CMD.EXE on Windows,
except on implementations that (erroneously, IMNSHO)
insist on consulting $SHELL
like clisp.
ignore-error-status
causes run-program
to not raise an error if the spawned program exits in error.
Following POSIX convention, an error is anything but
a normal exit with status code zero.
By default, an error of type subprocess-error
is raised in this case.
run-program
works on all platforms supported by ASDF, except Genera.
See the source code for more documentation.
slurp-input-stream
is a generic function of two arguments, a target object and an input stream,
and accepting keyword arguments.
Predefined methods based on the target object are as follows:
'string
or :string
, the content is captured into a string.
Accepted keywords include the element-type and a flag stripped,
which when true causes any single line ending to be removed as per uiop:stripln
.
:lines
, the content is captured as a list of strings,
one per line, without line ending. If the count keyword argument is provided,
it is a maximum count of lines to be read.
:line
, the content is captured as with :lines
above,
and then its sub-object is extracted with the at argument,
which defaults to 0
, extracting the first line.
A number will extract the corresponding line.
See the documentation for uiop:access-at
.
:forms
, the content is captured as a list of s-expressions,
as read by the Lisp reader.
If the count argument is provided,
it is a maximum count of lines to be read.
We recommend you control the syntax with such macro as
uiop:with-safe-io-syntax
.
:form
, the content is captured as with :forms
above,
and then its sub-object is extracted with the at argument,
which defaults to 0
, extracting the first form.
A number will extract the corresponding form.
See the documentation for uiop:access-at
.
We recommend you control the syntax with such macro as
uiop:with-safe-io-syntax
.
まずはどのバージョンが欲しいか決めましょう。開発はmaster
ブランチで行われており、HEAD
は最新のバグフィックスとポータビリティの修正を含んでいて、たいてい使えます。また、我々は(限られた)テストスイートも備えてはいます。しかし、リグレッションが起こることはあります。
用心深い人はrelease
ブランチを使うべきでしょう。たいていの場合、このブランチはより多くのテストがなされていますし、リリースは既知の解決されていないイシューがなくなったタイミングでカットされるからです。
ASDFのソースリポジトリはgitを使って得られます: git clone https://gitlab.common-lisp.net/asdf/asdf.git
上で述べたタグはこのリポジトリに見つかるでしょう。また、リポジトリはhttps://gitlab.common-lisp.net/asdf/asdfで閲覧することもできます。
ASDFの開発に関する議論はメーリングリストで行われています。(→ メーリングリスト)
ASDFのバグはlaunchpadにトラックされています: https://launchpad.net/asdf
バグかどうかわからない場合、あるいは一般的な議論をしたい場合は、メーリングリストasdf-develを使ってください。(→ メーリングリスト)
ASDFの開発に関する議論は、メーリングリストasdf-devel@common-lisp.netで行われています。http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel
We released ASDF 2.000 on May 31st 2010, ASDF 3.0.0 on May 15th 2013, ASDF 3.1.2 on May 6th 2014. Releases of ASDF 2 and now ASDF 3 have since then been included in all actively maintained CL implementations that used to bundle ASDF 1, plus many implementations that previously did not. ASDF has been made to work with all actively maintained CL implementations and even a few implementations that are not actively maintained.
Furthermore, it is possible to upgrade from ASDF 1 to ASDF 2 or ASDF 3 on the fly (though we recommend instead upgrading your implementation or replacing its ASDF module). For this reason, we have stopped supporting ASDF 1 and ASDF 2. If you are using ASDF 1 or ASDF 2 and are experiencing any kind of issues or limitations, we recommend you upgrade to ASDF 3 — and we explain how to do that. See ASDFをロードする.
Note that in the context of compatibility requirements,
ASDF 2.27, released on Feb 1st 2013, and further releases up to 2.33,
count as pre-releases of ASDF 3, and define the :asdf3
feature,
though the first stable release of ASDF 3 was release 3.0.1.
Significant new or improved functionality were added in ASDF 3.1;
the :asdf3.1
feature is present in recent enough versions to detect this functionality;
the first stable release since then was ASDF 3.1.2.
New *features*
are only added at major milestones,
and the next one will probably be :asdf3.2
.
ASDF 1 refers to any release earlier than 1.369 or so (from August 2001 to October 2009), and to any development revision earlier than 2.000 (May 2010). If your copy of ASDF doesn’t even contain version information, it’s an old ASDF 1. Revisions between 1.656 and 1.728 may count as development releases for ASDF 2.
ASDF 2 refers to releases from 2.000 (May 31st 2010) to 2.26 (Oct 30th 2012), and any development revision newer than ASDF 1 and older than 2.27 (Feb 1st 2013).
ASDF 3 refers to releases from 2.27 (Feb 1st 2013) to 2.33 and 3.0.0 onward (May 15th 2013). 2.27 to 2.33 count as pre-releases to ASDF 3.
ASDF 3.1 refers to releases from 3.1.2 (May 6th 2014) onward. These releases are also considered part of ASDF 3.
All releases of ASDF
push :asdf
onto *features*
.
Releases starting with ASDF 2
push :asdf2
onto *features*
.
Releases starting with ASDF 3 (including 2.27 and later pre-releases)
push :asdf3
onto *features*
.
Furthermore, releases starting with ASDF 3.1.2 (May 2014),
though they count as ASDF 3, include enough progress that they
also push :asdf3.1
onto *features*
.
You may depend on the presence or absence of these features
to write code that takes advantage of recent ASDF functionality
but still works on older versions, or at least detects the old version and signals an error.
Additionally, all releases starting with ASDF 2
define a function (asdf:asdf-version)
you may use to query the version.
All releases starting with 2.013 display the version number prominently
on the second line of the asdf.lisp source file.
If you are experiencing problems or limitations of any sort with ASDF 1 or ASDF 2, we recommend that you should upgrade to the latest release, be it ASDF 3 or other.
Finally, here is a code snippet to programmatically determine what version of ASDF is loaded, if any, that works on all versions including very old ones:
(when (find-package :asdf) (let ((ver (symbol-value (or (find-symbol (string :*asdf-version*) :asdf) (find-symbol (string :*asdf-revision*) :asdf))))) (etypecase ver (string ver) (cons (with-output-to-string (s) (loop for (n . m) on ver do (princ n s) (when m (princ "." s))))) (null "1.0"))))
If it returns nil
then ASDF is not installed.
Otherwise it should return a string.
If it returns "1.0"
, then it can actually be
any version before 1.77 or so, or some buggy variant of 1.x.
If it returns anything older than "3.0.1"
,
you really need to upgrade your implementation or at least upgrade its ASDF.
See 処理系付属のASDFを置き換える.
Common Lisp namestrings are not portable, except maybe for logical pathname namestrings, that themselves have various limitations and require a lot of setup that is itself ultimately non-portable.
In ASDF 1, the only portable ways to refer to pathnames inside systems and components
were very awkward, using #.(make-pathname ...)
and
#.(merge-pathnames ...)
.
Even the above were themselves were inadequate in the general case
due to host and device issues, unless horribly complex patterns were used.
Plenty of simple cases that looked portable actually weren’t,
leading to much confusion and greavance.
ASDF 2 implements its own portable syntax for strings as pathname specifiers.
Naming files within a system definition becomes easy and portable again.
See system-relative-pathname,
merge-pathnames*
,
coerce-pathname
.
On the other hand, there are places where systems used to accept namestrings
where you must now use an explicit pathname object:
(defsystem ... :pathname "LOGICAL-HOST:PATH;TO;SYSTEM;" ...)
must now be written with the #p
syntax:
(defsystem ... :pathname #p"LOGICAL-HOST:PATH;TO;SYSTEM;" ...)
See パス名指定子.
A popular feature added to ASDF was output pathname translation:
asdf-binary-locations
, common-lisp-controller
,
cl-launch
and other hacks were all implementing it in ways
both mutually incompatible and difficult to configure.
Output pathname translation is essential to share source directories of portable systems across multiple implementations or variants thereof, or source directories of shared installations of systems across multiple users, or combinations of the above.
In ASDF 2, a standard mechanism is provided for that,
asdf-output-translations
,
with sensible defaults, adequate configuration languages,
a coherent set of configuration files and hooks,
and support for non-Unix platforms.
See ASDFがコンパイルされたファイルを保持する場所を設定する.
Configuring ASDF used to require special magic to be applied just at the right moment, between the moment ASDF is loaded and the moment it is used, in a way that is specific to the user, the implementation he is using and the application he is building.
This made for awkward configuration files and startup scripts that could not be shared between users, managed by administrators or packaged by distributions.
ASDF 2 provides a well-documented way to configure ASDF, with sensible defaults, adequate configuration languages, and a coherent set of configuration files and hooks.
We believe it’s a vast improvement because it decouples application distribution from library distribution. The application writer can avoid thinking where the libraries are, and the library distributor (dpkg, clbuild, advanced user, etc.) can configure them once and for every application. Yet settings can be easily overridden where needed, so whoever needs control has exactly as much as required.
At the same time, ASDF 2 remains compatible
with the old magic you may have in your build scripts
(using *central-registry*
and
*system-definition-search-functions*
)
to tailor the ASDF configuration to your build automation needs,
and also allows for new magic, simpler and more powerful magic.
See ASDFがシステムを探す場所を設定する.
In ASDF 1, you had to use the awkward syntax
(asdf:oos 'asdf:load-op :foo)
to load a system,
and similarly for compile-op
, test-op
.
In ASDF 2, you can use shortcuts for the usual operations:
(asdf:load-system :foo)
, and
similarly for compile-system
, test-system
.
The following issues and many others have been fixed:
:version
and
the :force (system1 .. systemN)
feature have been fixed.
Between new features, old bugs fixed, and new bugs introduced, there were various releases of ASDF in the wild, and no simple way to check which release had which feature set. People using or writing systems had to either make worst-case assumptions as to what features were available and worked, or take great pains to have the correct version of ASDF installed.
With ASDF 2, we provide a new stable set of working features
that everyone can rely on from now on.
Use #+asdf2
to detect presence of ASDF 2,
(asdf:version-satisfies (asdf:asdf-version) "2.345.67")
to check the availability of a version no earlier than required.
When an old version of ASDF was loaded, it was very hard to upgrade ASDF in your current image without breaking everything. Instead you had to exit the Lisp process and somehow arrange to start a new one from a simpler image. Something that can’t be done from within Lisp, making automation of it difficult, which compounded with difficulty in configuration, made the task quite hard. Yet as we saw before, the task would have been required to not have to live with the worst case or non-portable subset of ASDF features.
With ASDF 2, it is easy to upgrade from ASDF 2 to later versions from within Lisp, and not too hard to upgrade from ASDF 1 to ASDF 2 from within Lisp. We support hot upgrade of ASDF and any breakage is a bug that we will do our best to fix. There are still limitations on upgrade, though, most notably the fact that after you upgrade ASDF, you must also reload or upgrade all ASDF extensions.
When vendors were releasing their Lisp implementations with ASDF, they had to basically never change version because neither upgrade nor downgrade was possible without breaking something for someone, and no obvious upgrade path was visible and recommendable.
With ASDF 2, upgrade is possible, easy and can be recommended. This means that vendors can safely ship a recent version of ASDF, confident that if a user isn’t fully satisfied, he can easily upgrade ASDF and deal with a supported recent version of it. This means that release cycles will be causally decoupled, the practical consequence of which will mean faster convergence towards the latest version for everyone.
The main pitfalls in upgrading to ASDF 2 seem to be related to the output translation mechanism.
enable-asdf-binary-locations-compatibility
in
see Backward Compatibility.
But thou shalt not load ABL on top of ASDF 2.
Other issues include the following:
:pathname
argument
to a defsystem
and its components,
a logical pathname (or implementation-dependent hierarchical pathname)
must now be specified with #p
syntax
where the namestring might have previously sufficed;
moreover when evaluation is desired #.
must be used,
where it wasn’t necessary in the toplevel :pathname
argument
(but necessary in other :pathname
arguments).
(directory "/configured/path/**/*.asd")
for every configured path (:tree "/configured/path/")
in your source-registry
configuration can cause a slight pause.
Try to (time (asdf:initialize-source-registry))
to see how bad it is or isn’t on your system.
If you insist on not having this pause,
you can avoid the pause by overriding the default source-registry configuration
and not use any deep :tree
entry but only :directory
entries
or shallow :tree
entries.
Or you can fix your implementation to not be quite that slow
when recursing through directories.
Update: This performance bug fixed the hard way in 2.010.
(defmethod source-file-type ((component cl-source-file) (system (eql (find-system 'foo))))
(declare (ignorable component system)) "lis")
.
Now, the pathname for a component is eagerly computed when defining the system,
and instead you will (defclass cl-source-file.lis (cl-source-file) ((type :initform "lis")))
and use :default-component-class cl-source-file.lis
as argument to defsystem
,
as detailed in a see How do I create a system definition where all the source files have a .cl extension? below.
source-file-type
is deprecated. To access a component’s
file-type, use file-type
, instead. source-file-type
will
be removed.
While ASDF 3 is largely compatible with ASDF 2, there are a few pitfalls when upgrading from ASDF 2, due to limitations in ASDF 2.
:depends-on ((:version "asdf" "3.1.2"))
,
but that you also check that ASDF 3 is installed,
or else the upgrade catastrophe might happen before that specification is checked,
by starting your .asd file with a version check as follows:
#-asdf3 (error "MY-SYSTEM requires ASDF 3.1.2")
(require "asdf") #-asdf3.1 (error "ASDF 3.1 or bust")
require
at first, and
make heroic attempts to load it the hard way if at first they don’t succeed,
see tools/load-asdf.lisp distributed with the ASDF source repository,
or the code of cl-launch
.
(ignore-errors (funcall 'require "asdf")) ;; <--- try real hard ;; <--- insert heroics here, if that failed to provide ASDF 2 or 3 ;; <--- insert configuration here, if that succeeded (asdf:load-system "asdf") ;; <--- re-configure here, too, in case at first you got ASDF 2
asdf-ecl
and its short-lived successor asdf-bundle
are no more,
having been replaced by code now built into ASDF 3.
Moreover, the name of the bundle operations has changed since ASDF 3.1.3.
Starting with ASDF 3.2.0, load-system
will once again use load-bundle-op
instead of load-op
on ECL,
as originally intended by asdf-ecl
authors, but disabled for a long time
due to bugs in both ECL and ASDF.
Note that some of the bundle operations were renamed after ASDF 3.1.3, and the old names have been removed. Old bundle operations, and their modern equivalents are:
fasl-op
is now compile-bundle-op
load-fasl-op
is now load-bundle-op
binary-op
is now deliver-asd-op
monolithic-fasl-op
is now monolithic-compile-bundle-op
monolithic-load-fasl-op
is now monolithic-load-bundle-op
monolithic-binary-op
is now monolithic-deliver-asd-op
If you have a recent implementation, it should already come with ASDF 3 or later. If you need a more recent version than is provided, we recommend you simply upgrade ASDF by installing a recent version in a path configured in your source-registry. See ASDFをアップグレードする.
If you have an old implementation that does not provide ASDF 3, we recommend you replace your implementation’s ASDF. See 処理系付属のASDFを置き換える.
Since ASDF 2,
it should always be a good time to upgrade to a recent version of ASDF.
You may consult with the maintainer for which specific version they recommend,
but the latest release
should be correct.
Though we do try to test ASDF releases against all implementations that we can,
we may not be testing against all variants of your implementation,
and we may not be running enough tests;
we trust you to thoroughly test it with your own implementation
before you release it.
If there are any issues with the current release,
it’s a bug that you should report upstream and that we will fix ASAP.
As to how to include ASDF, we recommend the following:
(require "asdf")
should load the version of ASDF that is bundled with your system.
If possible so should (require "ASDF")
.
You may have it load some other version configured by the user,
if you allow such configuration.
cl:require
,
then it would be nice to add ASDF to this hook the same way that
ABCL, CCL, CLISP, CMUCL, ECL, SBCL and SCL do it.
Please send us appropriate code to this end.
cl:require
these modules that are provided by your Lisp distribution;
if you do, you should add these modules in the beginning of both
wrapping-source-registry
and wrapping-output-translations
.
asdf-ecl
and asdf-ecl.asd, or
sb-asdf
and sb-asdf.asd.
Indeed, if you made asdf.asd a magic system,
then users would no longer be able to upgrade ASDF using ASDF itself
to some version of their preference that
they maintain independently from your Lisp distribution.
wrapping-source-registry
,
and you are welcome to include asdf.asd amongst them.
Non-magic systems should be at the back of the wrapping-source-registry
while magic systems are at the front.
If they are precompiled,
they should also be in the wrapping-output-translations
.
(require "uiop")
and not load ASDF,
or one may (require "asdf")
which would implicitly require and load the former.
When you upgrade the ASDF running in your Lisp image
from an ancient ASDF 2 or older to ASDF 3 or newer,
then you may have to re-configure ASDF.
If your configuration only consists in
using the source-registry and output-translations (as it should),
and if you are not explicitly calling asdf:initialize-source-registry
or asdf:initialize-output-translations
with a non-nil argument,
then ASDF will reconfigure itself.
Otherwise, you will have to configure ASDF 2 (or older) to find ASDF 3,
then configure ASDF 3.
Notably, *central-registry* is not maintained across upgrades from ASDF 2.
See note about ASDF reconfiguration after upgrade.
Problems like this may be experienced if one loads Quicklisp (which as of this writing bundles an obsolete ASDF version 2.26), upgrades ASDF, and then tries to load new systems. The correct solution is to load the most up-to-date ASDF you can, then configure it, then load Quicklisp and any other extension. Do not try to upgrade from ASDF 2 after loading Quicklisp, for it will leave both ASDF and Quicklisp badly misconfigured. For details see the discussion at the above cross-reference.
Also, if you are experiencing such failures due to Quicklisp shipping an ancient ASDF, please complain to Zach Beane about it.
See ASDFがコンパイルされたファイルを保持する場所を設定する.
Note that in the past there was an add-on to ASDF called
ASDF-binary-locations
, developed by Gary King.
That add-on has been merged into ASDF proper,
then superseded by the asdf-output-translations
facility.
Note that use of asdf-output-translations
can interfere with one aspect of your systems
— if your system uses *load-truename*
to find files
(e.g., if you have some data files stored with your program),
then the relocation that this ASDF customization performs
is likely to interfere.
Use asdf:system-relative-pathname
to locate a file
in the source directory of some system, and
use asdf:apply-output-translations
to locate a file
whose pathname has been translated by the facility.
To permanently disable the compiler output cache for all future runs of ASDF, you can:
mkdir -p ~/.config/common-lisp/asdf-output-translations.conf.d/ echo ':disable-cache' > \ ~/.config/common-lisp/asdf-output-translations.conf.d/99-disable-cache.conf
This assumes that you didn’t otherwise configure the ASDF files
(if you did, edit them again),
and don’t somehow override the configuration at runtime
with a shell variable (see below) or some other runtime command
(e.g. some call to asdf:initialize-output-translations
).
To disable the compiler output cache in Lisp processes
run by your current shell, try (assuming bash
or zsh
)
(on Unix and cygwin only):
export ASDF_OUTPUT_TRANSLATIONS=/:
To disable the compiler output cache just in the current Lisp process, use (after loading ASDF but before using it):
(asdf:disable-output-translations)
Note that this does NOT belong in a .asd file. Please do not tamper with ASDF configuration from a .asd file, and only do this from your personal configuration or build scripts.
Sometimes ASDF will be unable to find and load your systems, although you believe that it should be able to. There are a number of things you can do to debug such issues.
If you are using asdf:*central-registry*
(see ASDFがシステムを見つけられるように設定する(古いスタイル)),
you can
simply look at the pathnames and namestrings in this variable, and use
conventional tools such as cl:probe-file
and cl:directory
to poke around and see why your systems are not being found.
If you are using one of the newer methods for configuring ASDF’s system finding (see ASDFがシステムを探す場所を設定する), you can try:
(alexandria:hash-table-alist asdf/source-registry::*source-registry*)
(alphabetizing the results here may be helpful). Or for a higher-level view:
(asdf/source-registry:flatten-source-registry)
Finally, if you use the source registry cache (see ソースレジストリのキャッシュ), you can:
find ~/common-lisp -name .cl-source-registry.cache
at the shell.
It is still, unfortunately, an open question how to monitor ASDF’s interpretation of its source configuration as it happens.
ASDF provides a predefined test operation, test-op
.
See test-op.
The test operation, however, is largely left to the system definer to specify.
test-op
has been
a topic of considerable discussion on the
asdf-devel mailing list
(see メーリングリスト),
and on the
launchpad bug-tracker (see どこにバグを報告すればよいですか).
We provide some guidelines in the discussion of test-op
.
Various ASDF extensions provide some kind of doc-op
operation.
See also https://bugs.launchpad.net/asdf/+bug/479470.
See cffi
’s cffi-grovel
.
By default, the files contained in an asdf module go
in a subdirectory with the same name as the module.
However, this can be overridden by adding a :pathname ""
argument
to the module description.
For example, here is how it could be done
in the spatial-trees ASDF system definition for ASDF 2 or later:
(asdf:defsystem "spatial-trees" :components ((:module "base" :pathname "" :components ((:file "package") (:file "basedefs" :depends-on ("package")) (:file "rectangles" :depends-on ("package")))) (:module tree-impls :depends-on ("base") :pathname "" :components ((:file "r-trees") (:file "greene-trees" :depends-on ("r-trees")) (:file "rstar-trees" :depends-on ("r-trees")) (:file "rplus-trees" :depends-on ("r-trees")) (:file "x-trees" :depends-on ("r-trees" "rstar-trees")))) (:module viz :depends-on ("base") :pathname "" :components ((:static-file "spatial-tree-viz.lisp"))) (:module tests :depends-on ("base") :pathname "" :components ((:static-file "spatial-tree-test.lisp"))) (:static-file "LICENCE") (:static-file "TODO")))
All of the files in the tree-impls
module are at the top level,
instead of in a tree-impls/ subdirectory.
Note that the argument to :pathname
can be either a pathname object or a string.
A pathname object can be constructed with the #p"foo/bar/" syntax,
but this is discouraged because the results of parsing a namestring are not portable.
A pathname can only be portably constructed with such syntax as
#.(make-pathname :directory '(:relative "foo" "bar"))
,
and similarly the current directory can only be portably specified as
#.(make-pathname :directory '(:relative))
.
However, as of ASDF 2, you can portably use a string to denote a pathname.
The string will be parsed as a /
-separated path from the current directory,
such that the empty string ""
denotes the current directory, and
"foo/bar"
(no trailing /
required in the case of modules)
portably denotes the same subdirectory as above.
When files are specified, the last /
-separated component is interpreted
either as the name component of a pathname
(if the component class specifies a pathname type),
or as a name component plus optional dot-separated type component
(if the component class doesn’t specifies a pathname type).
Starting with ASDF 2.014.14, you may just pass
the builtin class cl-source-file.cl
as
the :default-component-class
argument to defsystem
:
(defsystem my-cl-system :default-component-class cl-source-file.cl ...)
Another builtin class cl-source-file.lsp
is offered
for files ending in .lsp.
If you want to use a different extension for which ASDF doesn’t provide builtin support, or want to support versions of ASDF earlier than 2.014.14 (but later than 2.000), you can define a class as follows:
;; Prologue: make sure we're using a sane package. (defpackage :my-asdf-extension (:use :asdf :common-lisp) (:export #:cl-source-file.lis)) (in-package :my-asdf-extension) (defclass cl-source-file.lis (cl-source-file) ((type :initform "lis")))
Then you can use it as follows:
(defsystem my-cl-system :default-component-class my-asdf-extension:cl-source-file.lis ...)
Of course, if you’re in the same package, e.g. in the same file,
you won’t need to use the package qualifier before cl-source-file.lis
.
Actually, if all you’re doing is defining this class
and using it in the same file without other fancy definitions,
you might skip package complications:
(in-package :asdf) (defclass cl-source-file.lis (cl-source-file) ((type :initform "lis"))) (defsystem my-cl-system :default-component-class cl-source-file.lis ...)
There is no provision in ASDF for ensuring that
some components are always loaded as source, while others are always
compiled.
There is load-source-op
(see load-source-op),
but that is an operation to be applied to a
system as a whole, not to one or another specific source files.
While this idea often comes up in discussions,
it doesn’t play well with either the linking model of ECL
or with various bundle operations.
In addition, the dependency model of ASDF would have to be modified incompatibly
to allow for such a trick.
It is possible to configure the lisp syntax by modifying the currently-active readtable.
However, this same readtable is shared globally by all software being compiled by ASDF,
especially since load
and compile-file
both bind *readtable*,
so that its value is the same across the build at the start of every file
(unless overridden by some perform :around
method),
even if a file locally binds it to a different readtable during the build.
Therefore, the following hygiene restrictions apply. If you don’t abide by these restrictions, there will be situations where your output files will be corrupted during an incremental build. We are not trying to prescribe new restrictions for the sake of good style: these restrictions have always applied implicitly, and we are simply describing what they have always been.
If you want to use readtable modifications that cannot abide by those restrictions, you must create a different readtable object and set *readtable* to temporarily bind it to your new readtable (which will be undone after processing the file).
For that, we recommend you use system named-readtables
to define or combine such readtables using named-readtables:defreadtable
and use them using named-readtables:in-readtable
.
Equivalently, you can use system cl-syntax
,
that itself uses named-readtables
,
but may someday do more with, e.g. *print-pprint-dispatch*.
For even more advanced syntax modification beyond what a readtable can express, you may consider either:
perform
method that compiles a constant file that contains a single form
#.*code-read-with-alternate-reader*
in an environment where this special variable
was bound to the code read by your alternate reader, or
reader-interception
.
Beware that it is unsafe to use ASDF from the REPL to compile or load systems
while the readtable isn’t the shared readtable previously used to build software.
You must manually undo any binding of *readtable* at the REPL
and restore its initial value whenever you call operate
(via e.g. load-system
, test-system
or require
)
from a REPL that is using a different readtable.
Use from the named-readtables
system the macro named-readtables:in-readtable
.
If the other system fails to use named-readtables
, fix it and send a patch upstream.
In the day and age of Quicklisp and clbuild, there is little reason
to eschew using such an important library anymore.
Use from the named-readtables
system the macro named-readtables:defreadtable
.
Output from ASDF and ASDF extensions are sent to the CL stream
*standard-output*
, so rebinding that stream around calls to
asdf:operate
should redirect all output from ASDF operations.
Conventional Common Lisp code may use *LOAD-TRUENAME*
or *LOAD-PATHNAME*
to find
files adjacent to source files. This will generally not work in
ASDF-loaded systems. Recall that ASDF relocates the FASL files it
builds, typically to a special cache directory. Thus the value of
*LOAD-PATHNAME*
and *LOAD-TRUENAME*
at load time, when ASDF is loading your system,
will typically be a pathname in that cache directory, and useless to you
for finding other system components.
There are two ways to work around this problem:
system-relative-pathname
function. This can readily be
used from outside the system, but it is probably not good software
engineering to require a source file of a system to know what
system it is going to be part of. Contained objects should not have to
know their containers.
(or *compile-file-pathname* *load-truename*)
(or *LOAD-PATHNAME*
, if you prefer)
in a macro expansion or other compile-time evaluated context.
This not-so-frequently asked question is primarily for ASDF developers, but those who encounter an unexpected error in some test may be interested, too.
Here’s the procedure for experimenting with tests in a REPL:
;; BEWARE! Some tests expect you to be in the .../asdf/test directory ;; If your REPL is not there yet, change your current directory: ;; under SLIME, you may: ,change-directory ~/common-lisp/asdf/test/ ;; otherwise you may evaluate something like: (require "asdf") (asdf:upgrade-asdf) ;load UIOP & update asdf.lisp (uiop:chdir (asdf:system-relative-pathname :asdf "test/")) (setf *default-pathname-defaults* (uiop:getcwd)) ;; Load the test script support. (load "script-support.lisp") ;; Initialize the script support for interaction. ;; This will also change your *package* to asdf-test ;; after frobbing the asdf-test package to make it usable. ;; NB: this function is also available from package cl-user, ;; and also available with the shorter name da in both packages. (asdf-test:debug-asdf) ;; Now, you may experiment with test code from a .script file. ;; See the instructions given at the end of your failing test ;; to identify which form is needed, e.g. (run-test-script "test-utilities.script")
現在すべきことの一覧については、ソースリポジトリのTODOファイルを参照してください。
また、バグは今のところlaunchpadでトラックされています: https://launchpad.net/asdf
defsystem-depends-on
mechanism
(and more generally, the ability to call ASDF from within an .asd
file)
allows for multiple phases of execution resulting
in “dynamic” dependencies with a “suspending” scheduler.
The rebuilder essentially uses a “dirty bit”, except that the in-image model
and the multiple phase support mean that’s actually more than a bit:
instead it’s three bits plus the timestamp plus a phase depth level.
The build is guaranteed “minimal” in number of steps computed.
It is local. It assumes but does not enforce determinism.
It does not assume early cutoff of the build when rebuild dependencies didn’t change.
quick-build
は1ファイル、183行のコード(空行、コメント、docstringを除くと117行)で書かれた単純で堅牢なパッケージビルドシステムであり、faslpath
に似ています。残念ながら公開されておらず、2014年4月現在では権利関係が曖昧です。asdf/package-system
は(トップレベル階層の異なる構成を除けば)おおむねquick-build
互換です。
faslpath
はquick-build
やasdf/package-system
に似たシステムですが、セパレータとして/
ではなく.
を使う点が異なります。
https://code.google.com/p/faslpath/
mk-defsystem
)とdefsystem(defsystem-4
、下記のKent M. Pitmanのメモを参照)の比較などを含んでいます。
DEFSYSTEM
: A make
for Common Lisp, A Thoughtful Re-Implementation of an Old Idea”, 2002.
CLOCCのリポジトリにはdefsystem-4
の提案が含まれています。
mk-defsystem
の変種であるdefsystem-3.x
がCLOCCリポジトリにあります。
Jump to: | *
:
A B C D E I L M O P Q R S T V ア エ オ コ シ ソ バ パ プ ロ |
---|
Jump to: | *
:
A B C D E I L M O P Q R S T V ア エ オ コ シ ソ バ パ プ ロ |
---|
Jump to: | A C D E F I L M O P R S T U V |
---|
Jump to: | A C D E F I L M O P R S T U V |
---|
Jump to: | *
A |
---|
Jump to: | *
A |
---|
Jump to: | B C D F H I J L M N O P S T U |
---|
Jump to: | B C D F H I J L M N O P S T U |
---|
[訳注] Quicklispがインストールされている場合、手動でソフトウェアを置く場所としてはql:*local-project-directories*
(~/quicklisp/local-projects/など)も使えます。同名のシステムがある場合、ローカルプロジェクトのものがQuicklisp配布のバージョンに優先します。Quicklispはこれらのパスを(少なくとも、(ql:register-local-projects)
を実行すれば)ASDFからも使えるように自動設定しているはずです。
また、すべてをRoswellで管理するという方法もあるでしょう。Roswellは、処理系マネージャとしての機能を中心にした高機能なコマンドラインツールです。Roswellから処理系を起動している場合は~/.roswell/local-projects/が同じように使えます。
clbuild(とその後継のclbuild2)は2018年現在ではメンテナンスされておらず、やや古いツールと思われます。
(require "ASDF")
や(require 'asdf)
、(require :asdf)
などもGNU CLISP以外では有効ですが、ポータビリティのためには(require "asdf")
を使うべきです。
[訳注] ~/common-lisp/などのディレクトリに移動してgit clone https://gitlab.common-lisp.net/asdf/asdf.git
を実行します。
~/common-lisp/がデフォルトのパスに含まれるのはASDF 3.1.2以降です。それ以前のASDFを使っている場合は、後述のように、このパスを使うことを明示的に設定する必要があるかもしれません。
[訳注] また、Windowsの場合、後者は%LOCALAPPDATA%/common-lisp/source/または%APPDATA%/Local/common-lisp/source/に相当することが多いでしょう。詳細についてはASDFがシステムを探す場所を設定するを参照するべきですが、特に必要でなければ~/common-lisp/を使うことを推奨します。
Windowsの場合、ASDF 3.1.5以降では、%LOCALAPPDATA%以下にconfig/common-lisp/source-registry.conf.d/ディレクトリを作りましょう。%LOCALAPPDATA%は通常なら~/AppData/Local/を指していますが、実際の値はCMD.EXE
上でecho %LOCALAPPDATA%
を実行することで確かめることができます。
拡張子が.confでないファイルは無視されるので、これを利用して特定の設定ファイルを無効にすることができます。例えば、エディタのバックアップなどのファイルは同じディレクトリにあっても読み込まれないでしょう。
また、ASDFは.で始まるファイル名も無視します。
慣習では、これらの設定ファイル名は2つの数字から始まります。こうすることで設定ディレクトリ内の.confファイルの並びが決まり、設定が読み込まれる順番をコントロールすることができます。
.asdファイルの探索をさらにカスタマイズすることも可能です。これは発展的な使い方になるので、後に扱います。*system-definition-search-functions*
で検索してください。
[訳注] 実装を見る限りでは、eval
の返す値がuiop:directory-pathname-p
を満たせば良いようです(満たさない場合はエラーが通知される)。
WindowsではPOSIXのシンボリックリンクの代わりにショートカットを使うことができます。MacOSのエイリアスについては、うまく動くかどうかの情報を求めています。
[訳注] asdf:clear-source-registry
とasdf:clear-output-translations
が実行されます。
[訳注] ASDF以前に使われていたシステム定義ツール。
:force-not (already-loaded-systems)
を指定すると実現できます。
[訳注] 例を挙げて説明すると、システムlibrary
とシステムapplication
があってapplication
がlibrary
に依存しているとき、library
のコードを変更して(asdf:load-system :application)
を実行すると、library
、application
の順で必要な再コンパイル、ロードが行われます。
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を使っているのでなければ、この点を気にするべきではありません。
[訳注] 現在のバージョンでは、キーワード指定するとasdf/interface
パッケージからクラスが探されます。別のパッケージのクラスを使いたい場合は"package:my-new-system-subclass"
のように文字列で指定しましょう。
[訳注] "foo/bar.lisp"
は#p"foo/bar.lisp.lisp"と解釈されるので、拡張子を明示して書くことはできません。
[訳注] ここはわかりにくいですが、文字列中では".."
がUnixのパスと同じように使えるということを知っていれば十分です。ANSI CLのパス名オブジェクトは6つのコンポーネントを含みますが、その内の1つであるディレクトリコンポーネント中に:back
が含まれているのと同じ意味になるということです。CLHS: 19.2.2.4.3 Restrictions on Examining a Pathname Directory Componentも参照。
[訳注] CLのパス名(リテラル#p...
あるいは#.(make-pathname ...)
による)を与えるか、文字列でUnixスタイルの相対パスを与えるかの2択であり、絶対パスを指定したい場合は(稀と思われますが)前者のパス名しか使えないということです。相対パスは.asdファイルのあるディレクトリを基点に解釈されるので、:pathname
オプションでシステムのトップレベルのディレクトリを変更していても影響しないという点にも注意しましょう。
なお、具体的には(uiop:subpathname (asdf:system-source-file <システム名>) <パス名または文字列>)
の返すパス名が使われています。
[訳注] component-pathname
でアクセスできるパス名です。
[訳注] ただしこれは、load-system
で読み込むのとload
で読み込むのが同等、という意味ではありません。ASDFがシステム定義を.asdファイルから読み込むときはカレントパッケージが:asdf-user
になるなど、load
による実行と比べるといろいろな違いがあります。
[訳注] システムfoo
のディレクトリ構成として、システム定義ファイルをfoo/foo.asdに置き、ソースファイルをすべてfoo/src/以下に置く方式を選んだとしましょう。この時、foo/src/baz.lispに対応するシステムはデフォルトではfoo/src/baz
ですが、:pathname "src"
でトップレベルのパスをfoo/src/に変えるとfoo/baz
にできます。:pathname
オプションについてはdefsystemの文法を参照してください。
古いASDFでは、計画を作る関数はtraverse
であり、実行すべきアクションのリストを返していました。しかし、ASDF 3でplan
オブジェクトが導入され、make-plan
がこのオブジェクトを返す関数として代わりに使われるようになったため、traverse
は廃止予定です。traverse
は後方互換性とデバッグのためだけに残されており、近い将来には削除されるかもしれません。
アクション(action)という用語は、Kent Pitmanの論文 “The Description of Large Systems”(→ 参考文献)で使われていて、これはUnixのmake
に端を発するのではないかと我々は考えています。この用語がASDFハッカーたちに使われるようになったのはASDF 2からですが、それ以前も単に明示されてはいなかっただけで、概念としてはASDF 1の当初からありました。
[訳注] 降順。例えば(defsystem "foo" :components ((:module "src" ...)))
のsrc
モジュールはリスト("foo" "src")
で指定できます。
[訳注] traverse
はオペレーション指示子とコンポーネント指示子を引数に取り、実行されるアクション(オペレーションとコンポーネントの対)のリストを順序付きで返します。ただし、廃止予定。
[訳注] この場合、:build-pathname
の指定があれば出力先はそのパス名になります。指定がない場合はcomponent-pathname
の返すディレクトリが使われるようですが、公式の仕様かどうかは明示されていません。
バージョン2.27~3.0.3では、uiop
の代わりにuiop/package
のみがuse
されていました。バージョン3.1.2以前に対応したコードを書くためには、uiop
をuse
したパッケージを自ら定義するか、あるいはuiop:
というパッケージ接頭辞をいちいち付ける必要があります。
[訳注] 例えば冒頭に(cl:in-package :asdf-user)
を追加する、など。
[訳注] clear-source-registry
でキャッシュのリセットができます。
ASDF 2.26以前はプライマリネームの機能をサポートしていませんでした。従って、それらのバージョンでは、システムfoo/bar
がfoo.asdに定義されている場合は、(asdf:find-system "foo")
を実行するなどして、事前にそのファイルを明示的にロードしなければなりません。我々はASDF 2をサポートしていませんので、ASDF 3にアップグレードすることを推奨します。
[訳注] ASDFでは、:depends-on
でつながっているコンポーネントをsibling(兄弟姉妹)と呼びます。
[訳注] defsystem
の:depends-on
オプションで使われる構文です。(→ defsystemの文法)
[訳注] このリストの3番目以降の処理は、asdf:*default-source-registries*
中の7つの関数に順に対応しています。リストの最後の項目の意図は判然としませんが、処理系などが独自にこの変数を編集していると、処理が加わったり(あるいは減ったり)するでしょう。Mezzanoなどにその例があります。
[訳注] 継承した設定(inherited configuration)とは、ソースレジストリの一連の設定において、後で処理される設定を指します。つまり、大ざっぱに言えば、箇条書きの下の処理に相当します。「継承」の語感から逆に考える方もいるかもしれないので、注意しておきます。(append 新しい設定 (次の処理))
のように再帰のイメージを持つと間違えにくいかもしれません。:ignore-inherited-configuration
の指定があると、ソースレジストリの設定がそこで終わることになります。
[訳注] asdf::default-user-source-registry
とasdf::default-system-source-registry
による設定が挿入されます。ソースレジストリの設定のリスト中の6番目と9番目の処理に相当します。
[訳注] asdf:*user-cache*
のパス名が使われます。主にアウトプットトランスレーションに関係します。アウトプットトランスレーションを設定するDSLを参照。
[訳注] clbuildは2018年現在、メンテナンスされていません。
[訳注] こちらの例ではユーザー名が明示されていますが、もちろん(:tree (:home "cl"))
という記述も有効です。
[訳注] 文字列の末尾を:
(または;
)にすると、最後に空のエントリを追加したことになります。
[訳注] 具体的には(uiop:normalize-pathname-directory-component (pathname-directory パス名))
です。uiop:normalize-pathname-directory-component
は、処理系によって異なるディレクトリコンポーネントのフォーマットをCLHS標準のリストに直す関数です。
[訳注] ASDFの競合だったビルドシステムです。今は使われていません。
[訳注] 現在の実装もそうなっています。
[訳注] .cl-source-registry.cacheの読み込みはignore-errors
で行われるため、キャッシュファイルが正しい構文で書かれていない場合、単に無視されるだけでエラーは通知されないことに注意してください。
“FASL”は“FASt Loading”の略です。
[訳注] このリストの3番目以降の処理は、asdf/output-translations::*default-output-translations*
中の5つの関数に順に対応しています。ソースレジストリの場合と違ってこのシンボルはエクスポートされていないため、処理系などが独自にこの変数を編集する可能性は考えられていないのでしょう。
[訳注] 順序としては、このディレクトリの設定は上のリストの1番目の処理で追加されます。無効にする方法についてはアウトプットトランスレーションを設定するDSLを参照してください。
部分的には、clean-op
がこの解決策になるかもしれません。
[訳注] 継承した設定(inherited configuration)とは、アウトプットトランスレーションの一連の設定において、後で処理される設定を指します。:ignore-inherited-configuration
の指定があると、アウトプットトランスレーションの設定がそこで終わることになります。
[訳注] 入力指示子の:root
は(:root :**/ :*.*.*)
の略であり、すべてのホスト、デバイスのすべてのファイルにマッチします。このマッピングが適用されると、例えば#P"C:/Users/foo/common-lisp/bar.lisp"は#P"C:/Users/foo/common-lisp/sbcl-1.4.2-win-x64/bar.lisp"などに変換されます。
[訳注] asdf:*user-cache*
はデフォルトでは(uiop:xdg-cache-home "common-lisp" :implementation)
にセットされます。(→ XDGベースディレクトリ)
[訳注] clbuildは2018年現在、メンテナンスされていません。
Alternatively, you could touch foo.asd
or
remove the corresponding fasls from the output file cache.
Forward incompatibility can be determined using the variable
asdf/upgrade::*oldest-forward-compatible-asdf-version*
,
which is 2.33 at the time of this writing.