ASDF マニュアル

Table of Contents

ASDF: Another System Definition Facility

バージョン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.


1 はじめに

ASDF (Another System Definition Facility) はビルドシステムです。ASDFは、システムがどのようなコンポーネント(=サブシステムとファイル)を含んでいるか、それらにどの順序でどういう操作をすれば良いか記述するためのツールであり、Common Lispソフトウェアのコンパイル・ロード・テストなどを支援します。ASDFを使ったことがない方は、クイックスタートガイドを読むと良いでしょう。

このマニュアルは3つのケースを想定して書かれています: 1つ目は、ユーザーとして他人のコードを使いたい場合であり、2つ目は、開発者としてシステムをビルドする手順を記述したい場合です。そして3つ目は、(ASDFのような)Common Lisp拡張の実装者として、ビルドシステムそのものに関わりたい場合です。より具体的には、ASDFを使うにシステムをロードする方法が、defsystemでシステムを定義するにシステムを定義する方法が、ASDFのオブジェクトモデルにASDFの内部仕様と拡張方法が書かれています。

ASDFはライブラリやシステムをインストールするためのツールではありません。ASDFの役割は言うなればmakeantのようなものであり、パッケージマネージャとしての機能は持ちません。特に、QuicklispやASDF-InstallのようにASDFシステムを探してダウンロードするツールと、ASDFそのものとを混同しないようにしましょう。ASDF-Installは、その名に反してASDFの一部ではなく独立したソフトウェアであり、また、ずっと前からメンテナンスされておらず、使われてもいません。我々は代わりにQuicklispを推奨します。Quicklispは優秀なパッケージマネージャであり、現在もメンテナンスが続いています。もしあなたが、自分でコードを修正できるように、tarアーカイブではなくバージョン管理システムのリポジトリからソフトウェアを手に入れたい場合は、clbuildを使うと良いでしょう。 ソフトウェアをインストールする場所としては~/common-lisp/を推奨します。ASDF 3.1.2 (2014)からは、このディレクトリはソースレジストリの既定のパスに含まれています。1

なお、このマニュアルは未完成です。基本事項はすべて網羅されていますが、多くの発展的な話題についてはわずかに触れているのみですし、例もあまり多くはありません。最終的な情報源はソースコードであり、使用例についてはQuicklispに登録されているフリーソフトウェアを参照するのがベストでしょう。また、助言を得る場所としては、メーリングリストが良いでしょう。


2 クイックスタートガイド


3 ASDFをロードする


3.1 インストール済みの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を扱いやすくする機能として、特筆に値します。


3.2 ASDFがロードされているか調べる

次のフォームを実行すると、ASDFが正しくロードされたか調べることができます:

(asdf:asdf-version)

文字列が返った場合、それは現在インストールされているASDFのバージョンを表します。表示されているバージョンが十分に新しければ(例えば3.1.2以降ならば)次の章に進んでかまいません。(→ ASDFを設定する

エラーになる場合は、ASDFがロードされていないか、あるいは、はるか昔のバージョンを使っているのでASDF 3に更新する必要があるか、でしょう。

バージョンを判定するための詳細は、How do I detect the ASDF version?を参照するとよいでしょう。

ASDFに関するトラブルに直面したときは、我々のところにイシューを立てる前に、まず下に示す方法で最新のリリースにアップグレードしてみてください。


3.3 ASDFをアップグレードする

お使いの処理系にすでにASDF 3以降が付属している場合(そのはずです)、さらに新しいバージョンを使いたければ、ほかのシステムと同様に、設定で決まっているパスにただ新しいASDFをインストールすれば良いです。 具体的には、公式のtarボールかgitリポジトリを~/common-lisp/asdf/に展開することを勧めます。3(→ ASDFがシステムを見つけられるように設定する

ASDFのソースコードがいったん正しい場所に置かれれば、これまで通り(require "asdf")を実行するだけで新しいASDFがロードされます。ASDF 3は、他のシステムのコンパイルを行う前に、自身の新しいバージョンが所定のパスに存在するかどうかを自動的に判断し、存在する場合には新しいバージョンを使います。

お使いの処理系にASDF 3以降が付属していない場合は、処理系付属のASDFを置き換えるを参照してください。


3.4 処理系付属のASDFを置き換える

現在もメンテナンスされている処理系であれば最新版にはASDF 3が付属しているので、お使いのものに含まれていない場合は最新版にアップグレードすることを勧めます。

ASDFが付属していない、あるいは古いバージョンが付属している処理系を使い続けたい場合は、下記の通りに最近のASDFをインストールすることを勧めます。その際、書き込みのアクセス権を変えたり、管理者権限で実行したりといったことが必要になるかもしれません。

ASDFのソースリポジトリには、処理系のASDFをアップグレードするためのスクリプトファイルが含まれており、シェルからtools/asdf-tools install-asdf lispworkslispworksは適切な処理系の名前に変えてください)で呼び出したり、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は未完成です。


3.5 ASDFをソースからロードする

もしもあなたがポータブルなビルドスクリプトを書きたいのであれば――つまり最新のASDFを備えているとは限らないような古いマシンと処理系でも動くスクリプトを書きたい場合には――asdf.lispをどこかに置いた上で、

(load "/path/to/your/installed/asdf.lisp")

でロードしなければならないかもしれません。

通常、ASDFを使うために必要なのはasdf.lispだけです。このファイルをASDF ウェブサイト上の最新リリースのtarボールから得ると良いでしょう。

バグを報告する場合には、gitリポジトリから最新版を得ることができます。最新版を手に入れるを参照してください。

スクリプトの冒頭にrequireを置いてASDFを使おうとしたが、それがうまくいかないので難しいやり方でロードを試みる、というような場合には、ソースリポジトリにあるtools/load-asdf.lispを参考にするか、cl-launchのコードを見るとよいでしょう。


4 ASDFを設定する

標準的な使い方をする限り、ASDFを使うのに追加設定は必要ないでしょう。以下のセクションはざっと流し読みして、自分のシステムにパスを通す方法を知り、自分に適した(Lispソフトウェアの)インストール方法を選びましょう。そして、そのままASDFを使うに進めば十分です。ASDFがオブジェクトファイルをどのように保持しているか意識しなければならないことはめったにないでしょうし、ASDFの設定をリセットする必要にせまられるのもたいていはコーナーケースにおいてのみ起こることです。


4.1 ASDFがシステムを見つけられるように設定する

システムをコンパイル、ロードするためには、システム定義の書かれた.asdファイルをASDFが見つけられるように設定しなければなりません。これには簡単なものから複雑なものまで、多数のやり方があります:

なお、あなたのOSディストリビューションやシステム管理者が、既にシステム全体のライブラリ管理を設定しているというケースもあることに注意してください。


4.2 ASDFがシステムを見つけられるように設定する(古いスタイル)

初心者はこのセクションを飛ばしてかまいません。初心者は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のパスをプログラムによって簡単にコントロールしたいユーザーのために残されています。


4.3 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に委ねています。


4.4 ASDFの設定をリセットする

Lispのコアイメージをダンプしてから復元するときや、設定を変更するときなどは、いったんASDFの設定をリセットしたいことがあるでしょう。このために次の関数があります。

関数: clear-configuration

ASDFの設定をデフォルトに戻します。具体的には、ソースレジストリアウトプットトランスレーションの設定が消去されます。10

この関数はデフォルトでuiop:*image-dump-hook*にプッシュされているので、uiop:dump-imageasdf:image-opasdf:program-opなどを使ってイメージを保存するときには、自動的に呼ばれて設定をクリアします。しかし、UIOPではなく処理系依存の機能を使ってイメージをダンプする場合には、この関数は呼ばれないので、手動で実行するか、あるいはuiop:*image-dump-hook*と同等の処理系依存のフックを使うことになります: SBCLではsb-ext:*save-hooks*が、CMUCLとSCLではext:*before-save-initializations*がこれに相当します。


5 ASDFを使う


5.1 システムをロードする

fooという名のシステムは、次のフォームを実行することでロード(必要ならコンパイルも)されます。

(asdf:load-system :foo)

処理系によっては、cl:requireにASDFがフックされているので、単に

(require :foo)

でロードできます。(→ 便利な関数

標準のシステム名は文字列であり、すべて小文字です。システム名を指定する時にはシンボル(キーワード含む)も使えます。シンボルはsymbol-nameで文字列に変換され、さらに小文字に変換されます。また、システム名の文字列はmake-pathname:name引数として(システムがスキャンされるファイルシステム上で)有効でなければなりません。

小文字を標準とする仕様はCLの慣習と違っていますが、検討の末このような仕様になりました。我々がサポートするシステムには、小文字を使うのが通例のもの(Unix、Mac、Windows)と、小文字を暗黙に大文字に変換するもの(ANSI CLの論理パス名)があるからです。


5.2 便利な関数

システムに対するもっとも基本的なオペレーションについては、次の3つのコマンドが用意されています: load-systemcompile-systemtest-system

load-systemの変種としてrequire-systemがあり、後者はシステムが既にロードされている場合はロードを行いません。これは例えば、処理系のコアイメージに最初から含まれているライブラリの再ロードを避けるためなどに使えます。

これらに加えてmakeという関数もあり、これを呼んだときのオペレーションはシステム開発者が選択することができます。何も指定されていない場合はload-systemと同じオペレーション(load-op)を実行しますが、例えば、ドキュメントを出力するためのシステムを作った場合、makeで(ロードではなく)ドキュメントを出力するようにできます。

ASDFは拡張可能なシステムであり、コンポーネントに対する新しいオペレーションを定義することもできます。オペレーション一般を呼び出す総称関数としてoperateがあり、デフォルトのものだけでなく任意のオペレーションを扱うことができます。(operateのエイリアスとしてoosが定義されているので、REPL上ではこちらがよく使われます。oosはoperate-on-systemの略でmk-defsystem11から受け継いだ名前です。) コンパイル・ロード・テスト以外のオペレーションを行いたい場合にはoperateを使うと良いでしょう。

関数: load-system system &rest keys &key force force-not verbose version &allow-other-keys

load-opsystem、キーワード引数にoperateを適用します。システムを現在のイメージにロードしたい場合は、load-systemを呼び出すのがもっとも標準的で推奨されるやり方です。

関数: compile-system system &rest keys &key force force-not verbose version &allow-other-keys

compile-opsystem、キーワード引数にoperateを適用します。この関数はシステムのすべてのファイルをコンパイルしますが、必ずしもそれらを現在のイメージにロードするとは限りません(が、しないとも限りません)。実際のところ、ほとんどのシステムでは、すべてのオブジェクトファイルがロードされるということはないでしょう。この関数はload-systemとの対称性のために存在しますが、挙動をちゃんと理解した上でビルドスクリプトを書く、という場合以外は使わないほうがよいです。もっとも、そのようなケースではcompile-opよりもprogram-opのほうが適切なこともあるでしょう。

関数: test-system system &rest keys &key force force-not verbose version &allow-other-keys

test-opsystem、キーワード引数にoperateを適用します。test-opの解説を参照してください。

関数: make system &rest keys &key &allow-other-keys

ASDF 3.1から加わった関数であり、システムに対して何らかのオペレーションを実行します。デフォルトの動作はload-systemと同じですが、システム開発者はシステム定義の中で実行すべきオペレーションを指定することができます。具体的には、defsystemフォームの:build-operationにオペレーションを指定し、:build-pathnameに出力先のパス名を指定します。(→ ビルドオペレーション

この関数は実験的であり、テストが十分ではありませんので、自己責任で使ってください。

関数: require-system system &rest keys &key &allow-other-keys

require-systemcl:requireと類似の動作をします。つまり、既にロードされているシステムを(例えシステムが更新されていても)ロードしません。同じ動作はload-systemにロード済みシステムを除外するようにキーワード引数を指定しても可能です。12 現在メンテナンスされているフリーの処理系(ABCL、Clozure CL、CMUCL、ECL、GNU CLISP、MKCL、SBCLの最近のバージョン)では、いったんASDFがロードされると、cl:requireでもASDFシステムをロードできるようになりますが、その動作は、cl:requireが処理系の知らないモジュール名を受け取ったときはrequire-systemに渡す、という仕組みになります。(逆にrequire-systemcl:requireを呼び出すことはありません。潜在的に無限ループを生んでしまう可能性があるからです。)

cl:requirerequire-systemは、現在のセッションでは修正されていないコードをロードするのに向いています。この2つの違いを説明すると、cl:requireは処理系の提供する拡張モジュールをロードするのに使われますが、require-systemは普通はその用途では使えません。(ただし、SBCLやMKCLのように、処理系の拡張モジュールがそもそもシステムとして定義されている場合はrequire-systemでもロードできます。)また、あなたが開発もデバッグもしていないシステムで、インストール済みのバージョンを使えば問題ないと考えられる場合にもrequire-system(あるいは、上に挙げた処理系ではcl:require)でロードするとよいかもしれません。

しかし、あなたが開発、デバッグしていたり、あるいは何にせよ変更を行ったシステムに対してはload-systemを使うべきです。load-systemは変更されたファイルとその依存関係に基づいて適切に再ビルドを行います。(指定されたシステムとそれが依存しているシステムです、それ以外のビルドは行いません。)13

関数: already-loaded-systems

これまでにロードされたシステムの名前のリストを返します。


5.3 この先へ進む

ここまでの解説で、他人が作ったシステムをロードするのに必要な情報は網羅されています。以降は、あなた自身が作ったLispソフトウェアにシステム定義を書くための知識を扱います。また、新しいオペレーションやコンポーネントを定義してASDFを拡張するための情報も含みます。


6 defsystemでシステムを定義する

この章では、ASDFを使ってシステムを定義し、ソフトウェア開発を行う方法を解説します。


6.1 単純な例

まずシステム定義の例を挙げ、後のセクションで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"))))

それでは例を説明していきます。

以上のことを知っていれば、単純なシステムを定義するには十分です。次のセクションの例はずっと込み入っているので、複雑なことをしたいときの助けになるかもしれません。しかし、究極的には任意のLispコードが書けるのですから、完全な解説をすることはできませんが。


6.2 複雑な例

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")

それでは例を説明していきます。


6.3 defsystemの文法

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

6.3.1 システム指示子

システム指示子は単独のコンポーネント名(例: "foo")か、またはスラッシュで区切られた複合的なコンポーネント名(例: "foo/bar/baz")です。

6.3.2 単独のコンポーネント名(simple-component-name

単独のコンポーネント名は文字列またはシンボルで指定することができます。

文字列を使う場合は、小文字のみを使ってください。

シンボルはsymbol-nameで文字列に変換され、さらに小文字に変換されます。言い換えれば、シンボルは指示子として有効ですが、コンポーネント名そのものは文字列だということです。

文字列であれシンボルであれ、コンポーネント名にアンダースコアを含めてはいけません。

また、単独のコンポーネント名にスラッシュ/を使ってはいけません。スラッシュは複合的なコンポーネント名を表すのに使われます。この点については次のサブセクションを参照してください。スラッシュが不適切に使われている場合、ASDFは警告を発するでしょう。

これらの制限を犯した場合、つまり、大文字と小文字を混ぜたり、アンダースコアを使ったりした場合、ASDFがシステムやコンポーネントを見つけるのが不可能になる可能性があります。というのも、コンポーネント名はファイル名として解釈されるからです。特に、論理パス名を使うようにASDFを設定したユーザーは、確実にこのような問題に直面するでしょう。

6.3.3 複合的なコンポーネント名

複合的なコンポーネント名は、いくつかの名前を階層に従ってスラッシュ区切りで並べたものです。この仕組みは、1つの.asdファイルに複数のシステム定義を置いた場合に、ASDFがそれらを見つけられるようにするためにあります。

この際、最上位の名前(master name)は.asdファイルの名前と一致していなければなりません。例えば、foo.asdファイルにはシステムfooが定義されているでしょうが、それに加えてfoo/testfoo/docsなどのシステムも定義することが可能です。ASDFは、システムfoo/testをロードするように要求されたとき、foo.asdファイルを探せばよいことを知っているというわけです。

6.3.4 コンポーネント型

コンポーネント型の名前は、(仮にキーワードで指定されたとしても)まずカレントパッケージのシンボルから検索されます。カレントパッケージに見つからなければasdfパッケージから検索されます。つまり、カレントパッケージmy-system-asdに属するコンポーネント型my-component-typeは、:my-component-typeまたはmy-component-typeで指定することができます。

systemとそのサブクラスは、システムの子コンポーネントのコンポーネント型として使うことはできません。

6.3.5 システムクラス名

システムクラス名はコンポーネント型と同じように検索されますが、ここではsystemとそのサブクラスのみを使うことができます。典型的な用途としては、何らかのASDF拡張で定義された標準外のシステムクラスを使う場合に、下述の:defsystem-depends-onでその拡張モジュールをロードしつつ、:classにクラス名を指定します。ASDFパッケージの中でこのクラス名を指定するときは、シンボル名の衝突を防ぐために

:class :MY-NEW-SYSTEM-SUBCLASS

のようにキーワードを使うことを推奨します。キーワードではなくMY-NEW-SYSTEM-SUBCLASSで指定した場合、この名前のシンボルがカレントパッケージに読み込まれたあと、(ASDF拡張が:defsystem-depends-onによってロードされて、そのパッケージから)同名のシンボルがエクスポートされる、という順序で処理が行われるため、名前の衝突が起きてしまいます。15

6.3.6 defsystemの依存(:defsystem-depends-on

:defsystem-depends-onオプションを使うと、システム定義が処理されるに、ASDFで定義された別のシステム(またはシステムのまとまり)をロードすることができます。このオプションは典型的には、システム定義の中でASDFの拡張をロードするために使われます。

6.3.7 ビルドオペレーション(:build-operation

:build-operationオプションには、システムに対してmake(→ make)を実行したときに、どのオペレーションを適用するか指定することができます。デフォルトではload-opが使われます。このオプションの値はオペレーションの名前でなければなりません。(例えば:build-operation doc-opなど)

この機能は実験的でテストが不十分です。自己責任で使ってください。

6.3.8 弱い依存関係(:weakly-depends-on

この機能を使うことは推奨しません。システムbarに弱依存しているシステムfooを作りたい場合は、パラメトリックにfooを定義して、スペシャル変数やフックで動作を変えられるようにすると良いでしょう。そして、システムfoo+barを定義してまとめてフックできるようにしましょう。

(廃止予定の):weakly-depends-onオプションで指定されたシステム(あるいはそのまとまり)については、ASDFはそれらのロードを試みますが、ロードの成功が必須であるとはみなしません。このオプションは典型的には、システムの基本的な機能には必要ないが付加的な機能をもたらす、という依存関係を指定するのに使われます。

上の文法定義では、このオプションはdefsystemにのみ指定されるものとなっていますが、今のところはどのコンポーネントにも指定可能です。しかし、defsystem直下以外に指定するのはおそらく無意味でしょうし、この(例外的な)挙動は将来、警告なしに変わる可能性があるので、プログラマの方にはdefsystem直下以外に指定しないように念を押しておきます。

6.3.9 パス名指定子

パス名指定子(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 ...))で代用することも可能です。

文字列が与えられた場合にコンポーネント型が考慮されてパス名が解釈されるのと異なり、パス名オブジェクトが与えられた場合はそのまま使われるということに注意してください。つまり、パス名オブジェクトを使うなら、コンポーネント型の制約を満たすように(例えばディレクトリや特定のファイル形式が指定されるように)あなた自身が気を配って書かなければなりません。他方では、ファイル形式の制約をあえて無視するために使うこともできます。

6.3.10 バージョン指定子

バージョン指定子(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の重要な非互換が発生する場合は、メジャーバージョンを上げてそのことを示しましょう。

コンポーネントの共通の属性

6.3.11 :require

依存モジュールをロードするのに(ASDFのload-opではなく)処理系のrequire関数を使う場合には、(:require モジュール名)という指定をします。

特定の処理系でのみ、その提供モジュールに依存しているという場合には、単に#+処理系名 (:require モジュール名)と書くよりは、(:feature 処理系名 (:require モジュール名))を使うほうが良いスタイルと言えます。(→ フィーチャーに応じた依存関係

6.3.12 フィーチャーに応じた依存関係(:feature

フィーチャーに応じた依存関係は(:feature feature-expression dependency)というフォームで指定します。システム定義がパースされる時にfeature-expressionが満たされれば(訳注: 例えば、*features*feature-expressionが含まれれば)依存関係が使われ、満たされない場合は無視されます。

ただし、:featureは今定義しているシステムが特定のフィーチャーに依存していることを示すために使うことはできないという点に注意しましょう。すなわち、システム定義をロードするのに必要なフィーチャーを指定することはできません。例えば、システムをSBCLでしか使えないようにしようとして(:feature :sbcl)と書いても無意味です。

なお、フィーチャーに応じた依存関係を、if-featureや廃れた機能であるfeature requirement(廃止)と混同しないようにしましょう。

6.3.13 論理パス名を使う

我々は一般に、論理パス名を使うことを推奨しません。(最近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ファイルが含まれる場合、この仕様は悩みの種になるかもしれません。典型的な例としては、プロジェクトのファイル、ディレクトリ名にバージョン番号が含まれる場合や、あるいは他のプログラミング言語で書かれたソフトウェアが共存していて、ファイル名にアンダースコアやドット、キャメルケースが使われる場合などがあります。

6.3.14 連続的な依存関係(: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")))

6.3.15 ソースの場所(:pathname

defsystemでシステムを定義する際、:pathnameオプションを任意で指定できますが、通常は必要ありません。単純なプロジェクトでは、ソースファイルはシステムと同じディレクトリにあるでしょうし、モジュールを使う場合はモジュールと同じ名前のサブディレクトリにあるでしょうから。

より詳細に説明すると、ASDFは次の挙動を実現するために、複雑なルールに従っています。

  1. find-system はシステムをディスクからロードして、そのデフォルトのパス名19を正しく設定します。
  2. ユーザーが.asdファイルをエディタに読み込んでフォームを1つ1つ評価したような場合でも、このパス名の情報は*default-pathname-defaults*(この場合、まったく別の場所を指しているかもしれません)によって書き換えられることはありません。

システムが初めて定義される場合、そのトップレベルのパス名は次のように決まるでしょう:

システムが再定義される場合、トップレベルのパス名は次のように決まります:

6.3.16 フィーチャーに応じたコンポーネント(:if-feature

このオプションは、特定のコンポーネントをビルドに含めるかどうかを(#+のように)フィーチャーに応じた条件で指定することができます。フィーチャーが満たされない場合、そのコンポーネントは無視され、また、他のコンポーネントがそのコンポーネントに依存するという記述も無視されます。リード時に評価される#+と異なり、このオプションを使うと、コンポーネントの階層にオブジェクトを追加し、そのオブジェクトをプロジェクトをビルドする際に利用したり、システム構造について推論したい外部のコードからアクセスしたりといったことができます。

プログラマはいつ:if-featureが評価されるか知っておくべきです。ASDFは最初にビルド全体の計画を建て、次にその計画を実行するという順で動作しますが、フィーチャーが満たされるかどうかは計画時にチェックされます。従って、ビルド中に*features*を変更して:if-featureに影響を与えるということはできません。:if-featureはビルドオペレーションが実行される直前の*features*の状態だけをチェックするのです。

このオプションはASDF 3で追加されました。詳細な情報については、必要なフィーチャーを参照してください。

6.3.17 エントリーポイント(: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に関する詳細は組み込みのオペレーションを参照してください。

6.3.18 feature requirement(廃止)

この機能はASDF 3.1で廃止されたので、使わないでください。ほとんどの場合は:if-feature(→ if-featureオプション)で代替できるでしょう。

この機能は、特定のフィーチャーが欠けているときに、コンポーネントの依存関係の連鎖が断たれるようにするためのものでした。:if-component-dep-failsと併せて使うと、条件つきのコンパイルを(遠回りではありますが)記述することができたのです。


6.4 .asdファイル中の他のコード

.asdファイルは、loadで実行可能な通常のLispのソースファイルであり20、任意のLispコードを含むことができます。しかし、追加のフォームは最小限にとどめ、複雑な処理が必要な場合はdefsystemの拡張を別に定義して:defsystem-depends-onで読み込むことを推奨します。

とは言っても、.asdファイルにコードを含めなければならない場合もあるでしょう。例えば、コンパイル時の環境を調べて設定するために*features*にフィーチャーを加える、などといったケースが考えられます。その場合は、ユーザーが.asdファイル中の処理の詳細をコントロールできるように、次の慣習に従うことを推奨します:


6.5 package-inferred-system拡張

バージョン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パッケージのスタイルは、以前にfaslpathquick-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を構成する各ソースファイルは、defpackageuiop: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/definitionmy-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パッケージからsym1sym2がインポートされていますが、この記述で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


7 ASDFのオブジェクトモデル

ASDFは、全体がオブジェクト指向でデザインされています。システムの構造も、システムに対するオペレーションも、拡張可能なプロトコルに従っているので、プログラマはASDFに新しい機能を加えることができます。例えば、cfficffi-grovelというASDF拡張によって、Cライブラリへのインターフェースの記述ファイルや、LispにCコードを埋め込むラッパーファイルを扱うコンポーネント型(それぞれcffi-grovel-filecffi-wrapper-file)を追加し、ABCL提供のasdf-jarはJavaのJARアーカイブを作る機能をもたらし、poiuはバックグラウンドプロセスを使って並列ビルドを可能にします。

ASDFの最も基本的なクラスはcomponentoperationです。componentが表すのは、1つのソースファイルか、複数のソースファイルのまとまりか、あるいはそれらから生み出されるもの(faslファイルなど)です。operationはコンポーネントに対して行われる何らかの変換であり、コンポーネントを中間結果や最終的な生成物に変えます。コンポーネント同士は依存関係によって関係づけられていて、それはシステム定義の中で指定されます。

operateによってコンポーネント(たいていはシステム)に対するオペレーションを指示したとき、ASDFはまずmake-plan関数を使って、依存関係を調べ、処理全体の計画(plan)を建てます。22 make-planの返すplanオブジェクトは実行すべきアクション(action)の順序付きリストを含んでいます。アクションとはoperationcomponentのペアであり、ビルドの1ステップとしてperformされるものです。また、このアクションのリストは、どのアクションも必要な依存関係が満たされるまでは実行されないことを保証します。23

この章では我々は、ASDFのオブジェクト指向プロトコル――プロトコルを構成するクラスと、それに対する総称関数――について解説します。これらの総称関数は多くの場合、コンポーネントとオペレーションの両方を引数に取ります。CLOSの多重ディスパッチを利用することで、ASDFは強力な機能を持ち、設定が柔軟に変更できるようになっています。我々は、まず組み込みのコンポーネントとオペレーションについて解説し、さらに新しいクラスやメソッドを定義してASDFプロトコルを拡張する方法を説明します。また、ASDFの関数の挙動をカスタマイズするためのさまざまなフックについても解説します。


7.1 オペレーション

ユーザーがシステムに対して何らかの処理を要求したときは、必ず適切なオペレーションオブジェクトが生成されます。例えば次のような処理が考えられるでしょう:

オペレーションは単に直接呼び出すこともできますし、実行せずにその結果がどうなるか確認することもできます。そこで実際の細々とした仕事をするのは、オペレーションとコンポーネント型によって特定化されたメソッド(のまとまり)です。オペレーションは、システムとともに引数としてoperateに与えることで、呼び出すことができます。(→ operate

頻出の作業についてはoperationの組み込みサブクラスがありますし、あまり使われない作業についても既にあることが多いでしょう。これに加えてASDFは、プログラマがASDF拡張を定義するためのビルドブロックとして使える「抽象的な」operationクラスも備えています。これについては以下に順を追って解説します。

オペレーションは、operateによってシステムに対して呼び出されます。

総称関数: operate operation component &rest initargs &key force force-not verbose &allow-other-keys
総称関数: oos operation component &rest initargs &key &allow-other-keys

operatecomponentに対してoperationを呼び出します。oos(operate-on-systemの略)はoperateのエイリアスです。

operationはオペレーション指示子(operation designator)です。オペレーション指示子はoperationオブジェクトそのものでもよいですが、通常はシンボルが使われるでしょう。シンボルはmake-operationに渡され、(そこでmake-instanceが呼ばれて)operationオブジェクトが作られます。 componentはコンポーネント指示子(component designator)です。コンポーネント指示子は同様に、componentオブジェクトそのものや、コンポーネント(たいていはシステム)の名前を表す文字列・シンボル(string-downcaseで小文字化される)ですが、システムの子コンポーネントを指したい場合は文字列・シンボルのリスト 24も使えます。

initargsmake-operationに渡される仕様は廃止予定であり、削除されるでしょう。詳しくはmake-operationを参照してください。依存関係によって、オペレーションがシステムやその子コンポーネントに対して他のオペレーションを呼び出すことがありますが、この際、元のinitargsが渡されるかどうかは(今のところ)不確定です。

force:allの場合、(依存関係に含まれる)すべてのシステムが、前回のコンパイルから未変更でも再コンパイルされます。forcetの場合、ロードされるシステムに限り、前回のコンパイルから未変更でも再コンパイルされます。forceが(システム指示子の)リストの場合、リストに含まれるシステムは前回のコンパイルから未変更でも再コンパイルされます。force-not:allの場合、(依存関係に含まれる)すべてのシステムは、前回のコンパイルから変更されていても再コンパイルされません。force-nottの場合、ロードされるシステム以外のシステムは、前回のコンパイルから変更されていても再コンパイルされません。(この仕様はASDF 3.1.2で変更されました。)forceが(システム指示子の)リストの場合、リストに含まれるシステムは前回のコンパイルから変更されていてもコンパイルされません。

forceforce-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)
関数: make-operation operation-class &rest initargs

initargsは、オペレーションオブジェクトを作る際にmake-instanceに渡されます。

注意: operationinitargsは廃止予定であり、近い将来ASDFから削除されるでしょう。

注意: operationのインスタンスは、直接make-instance作ってはいけません。必ずmake-operationを使ってください。operationインスタンスを直接作ろうとすると、ランタイムエラーを起こすでしょう。


7.1.1 組み込みのオペレーション

このセクションで解説するオペレーションはすべてasdfパッケージにあり、総称関数operateで呼び出すことができます。

(asdf:operate 'asdf:オペレーション名 :システム名 {オペレーションのオプション ...})
オペレーション: compile-op

このオペレーションは、指定されたコンポーネントをコンパイルします。つまり、cl-source-filecompile-fileでコンパイルされ、依存する、あるいは子コンポーネントであるシステム、モジュールには再帰的にcompile-opが適用されます。

compile-opprepare-opに依存していて、prepare-opは依存するすべてのコンポーネントに対するload-opに依存しています。したがって、operatecompile-opを呼び出すと、それらのコンポーネント群は単にコンパイルされるだけでなくロードもされるでしょう、しかし、compile-opでは、指定されたシステムそのもの(の一部)は、それに依存するシステムがないのでロードされずに残ることになります。システムをロードしたい場合にはload-opを使ってください。

オペレーション: load-op

このオペレーションは、指定されたコンポーネントのコンパイル済みコードをロードします。つまり、cl-source-fileについてはそのfaslファイルがロードされ、依存する、あるいは子コンポーネントであるシステム、モジュールには再帰的にload-opが適用されます。なおload-opcompile-opに依存していて、faslファイルはcompile-opの出力として得られます。

load-opprepare-opにも依存していて、prepare-opは依存するすべてのコンポーネントに対するload-opに依存しています。

オペレーション: prepare-op

このオペレーションは、指定されたコンポーネントが依存する(再帰的な意味で)すべてのコンポーネントが(load-opで)ロードされることを保証します。compile-opload-opが実行される前に、必要な前提を満たすためのオペレーションです。

オペレーション: load-source-op
オペレーション: prepare-source-op

load-source-opload-opと似ていますが、モジュールに属するcl-source-fileをfaslからではなくソースからロードします。load-source-opは(prepare-opに代えて)prepare-source-opに依存しています。prepare-source-opprepare-opに類似のオペレーションで、依存関係がload-source-opでロードされることを保証します。

オペレーション: test-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-opperformメソッドを次のように定義します:

(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が呼び出されます。(footest-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になることをテストする

fiveamfoo-test-suiteに属する一連のテスト(この場合はbasic-testのみ)を実行し、その結果を標準出力に表示するでしょう。ただし、システム定義(defsystem "foo/test" ...)のリード時にfiveamパッケージが存在するとは限りませんから、(fiveam:run! ...)とそのまま書くとエラーになる可能性があります。uiop:symbol-callは「fiveamパッケージに属するrun!関数を呼び出す」フォームを安全に書く――シンボルの解決を実行時に遅らせる――ための関数であり、この用途でよく使われます。uiop:find-symbol*も同様の目的で使われています。UIOPのマニュアルも参照すると良いでしょう。

[訳者補足終わり]

オペレーション: compile-bundle-op
オペレーション: monolithic-compile-bundle-op
オペレーション: load-bundle-op
オペレーション: monolithic-load-bundle-op
オペレーション: deliver-asd-op
オペレーション: monolithic-deliver-asd-op
オペレーション: lib-op
オペレーション: monolithic-lib-op
オペレーション: dll-op
オペレーション: monolithic-dll-op
オペレーション: image-op
オペレーション: program-op

これらは「バンドル(bundle)」に関わるオペレーションです。バンドルは、アプリケーション全体(もしくはアプリケーション中の各システム)を単一のファイルに束ねたものです。

compile-bundle-opはターゲットとなるシステムとそれが依存する各システムについて、それぞれ単一のfaslファイルを作ります。monolithic-compile-bundle-opはターゲットとなるシステムとそれが依存するシステムをすべて束ねて、単一のfaslファイルを作ります。つまり、アプリケーション全体を1つのfaslファイルにすることができます。load-bundle-opcompile-bundle-opの出力をロードします。ただし、compile-bundle-opは(出力が最新でない場合)コンパイルの副作用として、中間生成物である各faslファイルをロードすることもあるという点に注意してください。バンドルに関するオペレーションは特にECLにおいて重要です。というのもECLでは、ダイナミックリンクの際に、数十もの独立したfaslファイルをロードするのは1つのファイルをロードするのに比べてずっと重いからです。

注意: compile-bundle-opmonolithic-compile-bundle-opload-bundle-opmonolithic-load-bundle-opdeliver-asd-opmonolithic-deliver-asd-opは、ASDF 3.1以前はそれぞれfasl-opmonolithic-fasl-opload-fasl-opmonolithic-load-fasl-opbinary-opmonolithic-binary-opと呼ばれていました。後方互換性のために古い名称も残されていますが、それらは実際の機能を表していない不適切な名前ではあります。

compile-bundle-opで作った単一のfaslファイルを配布する時は、precompiled-systemクラスを使うことで、ユーザーはソースで配布されたものと互換性を持ったシステムとして使えるようになります。単一のfaslとともに配布される.asdファイルは次のようになるでしょう:

(defsystem :mysystem
  :class :precompiled-system
  :fasl (faslのパス名に評価されるフォーム))

あるいは、deliver-asd-opmonolithic-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.asdtest/hello.lispを参照してください。ビルド、テストはtest/test-program.scripttest/make-hello-world.lispに書かれています。

image-opはイメージをダンプしますが、スタンドアローンとは限らないし、システムのエントリーポイントが使われたりすることもありません。お使いのLisp処理系のコアイメージの扱いに従い、システムがプリロードされたイメージを出力することになるでしょう。これは中間的なビルド結果として使われたり、ラッパーとなるスクリプトとともに使われたりします。

lib-opはターゲットとなるシステムとそれが依存する各システムについて、リンク可能なオブジェクト(FFIファイル、あるいはECLであればLispファイルも)から.aファイル(Windowsでは.lib)をそれぞれビルドするためのオペレーションであり、monolithic-lib-opはアプリケーション全体に対して1つの.aファイルを作ります。dll-opmonolithic-dll-opも類似のオペレーションですが、ダイナミックリンクのための.soファイル(Windowsでは.dll、MacOS Xでは.dynlib)を作ります。

これらの「バンドル」オペレーションは、ASDF 3以降で、アクティブにメンテナンスされているLisp処理系であれば常に利用可能ですが、メンテナンスされていない古い処理系では使えない可能性があります。この機能は、以前は特定の処理系のためにasdf-bundleというシステムに分離されて実装されていて、さらに遡ればECL限定の機能であったasdf-eclが元になっています。

「バンドル」オペレーションの出力先のパスは、他のオブジェクトファイルと同様にアウトプットトランスレーションの設定に従いますが、defsystem:build-operationオプションに設定されたオペレーションに限っては異なります。26 この仕様は満足のいくものとは言いがたいので、将来的には変わる可能性があります。より良い仕様についてあなたの提案はありますか?

オペレーション: concatenate-source-op
オペレーション: monolithic-concatenate-source-op
オペレーション: load-concatenated-source-op
オペレーション: compile-concatenated-source-op
オペレーション: load-compiled-concatenated-source-op
オペレーション: monolithic-load-concatenated-source-op
オペレーション: monolithic-compile-concatenated-source-op
オペレーション: monolithic-load-compiled-concatenated-source-op

concatenate-source-opは、ターゲットとなるシステムとそれが依存する各システムについて、すべてのcl-source-fileを依存関係に沿った順序でそれぞれ1つのソースファイルに連結します。monolithic-concatenate-source-opは、ターゲットとなるシステムとそれが依存するシステムをすべて束ねて、単一のソースファイルを出力します。上のリストには、さらに連結した出力ファイルをロードしたり、コンパイルしてからロードしたりといったオペレーションが含まれています。

これらのオペレーションは、システムやアプリケーションを単一のソースファイルとして配布するのに役立ちます。その際に、ロードやコンパイル後のロードが適切に行われるかテストするのにも使われるでしょう。

ASDF自身も、monolithic-concatenate-source-opを使って単一のソースファイルasdf.lispとして配布されており、asdf/defsystemシステムそのものの前に前処理とuiopライブラリが挿入されています。


7.1.2 新しいオペレーションを定義する

ASDFは、オブジェクト指向の手法で拡張可能なようにデザインされています。プログラマはoperationのサブクラスを作って必要な挙動を実装することで、ASDFに新しい機能を追加することができます。

ASDFの組み込みのオペレーションは、ユーザー定義のオペレーションに対して「特権的」な地位にあるわけではありませんが、開発者の方には、独自のオペレーションを追加するときに決してasdfパッケージを使わないようにお願いします。というのも、名前の衝突は避けるべきですし、かといって我々は「ASDFのオペレーション名を管理するグローバルなレジストリ」をいちいち作りたくもないからです。

[訳者補足]

また、ASDFにはオペレーション・アクション間の依存関係を指定するためのサブクラスとして、downward-operationupward-operationsideway-operationselfward-operationnon-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が呼ばれた時、先だってfooprepare-opcompile-opが適用されます。次に、downward-operationは「子コンポーネントに対するオペレーション」に依存するオペレーションです。上の例ではdownward-operationスロットを省略していますが、デフォルトのnilは同じオペレーションmy-load-opを意味します。つまり、コンポーネントfooに対してmy-load-opが呼ばれた時、先だってfooのすべての子コンポーネントに対してmy-load-opが適用されます。

オペレーション: downward-operation (downward-operation :type operation-designator :allocation :class)
オペレーション: upward-operation (upward-operation :type operation-designator :allocation :class)
オペレーション: sideway-operation (sideway-operation :type operation-designator :allocation :class)
オペレーション: selfward-operation (selfward-operation :type (or operation-designator list) :allocation :class)
オペレーション: non-propagating-operation

asdf::operation-designator型は、オペレーションオブジェクト・シンボル・クラスのいずれかです。多くの場合、シンボルが使われるでしょう。その際、nilは「同じオペレーション」を意味します。

downward-operationは子コンポーネントについての依存関係を持つオペレーションです。baz-opfoo-opdownward-operationスロットに指定されているとき、ASDFはコンポーネントparentに対するアクション(foo-op . parent)を実行する前に、まずはparentのすべての子コンポーネントchildについてアクション(baz-op . child)を実行します。

upward-operationは親コンポーネントについての依存関係を持つオペレーションです。baz-opfoo-opupward-operationスロットに指定されているとき、ASDFはコンポーネントchildに対するアクション(foo-op . child)を実行する前に、まずはchildの親コンポーネントであるparentについてアクション(baz-op . parent)を実行します。

sideway-operationは依存コンポーネントについての依存関係を持つオペレーションです。baz-opfoo-opsideway-operationスロットに指定されているとき、ASDFはコンポーネントcに対するアクション(foo-op . c)を実行する前に、まずはcが依存している(:depends-on)すべてのコンポーネントdについてアクション(baz-op . d)を実行します。

selfward-operationは同一コンポーネントについての依存関係を持つオペレーションです。baz-opfoo-opselfward-operationスロットに指定されているとき、ASDFはコンポーネントcに対するアクション(foo-op . c)を実行する前に、まずはアクション(baz-op . c)を実行します。selfward-operationスロットには、複数のオペレーションをリストとして指定することができます。

non-propagating-operationは依存関係を持たない、単独で実行されるオペレーションです。

上で例示したように、これらのオペレーションは多重継承することができます。(もっとも、non-propagating-operationは単一継承するべきでしょう。)また、依存関係が循環しないようにするのはプログラマの責任に委ねられています。

[訳者補足終わり]

新しく作られたオペレーションには、多くの場合、次の総称関数の内の少なくとも1つについてメソッドを定義しなければならないでしょう。

総称関数: perform operation component

performは何らかの入力ファイルから出力ファイルを作り出すための総称関数です。prepare-opのように依存関係の伝搬だけするようなオペレーションは別にしても、たいていのオペレーションについてはperformメソッドが最も重要でしょう。performが呼ばれると、(すべての依存関係についてperformが実行されたあと)ターゲットのコンポーネントに対して処理が実行されます。

performメソッドは、その入力と出力のパスを決めるのにinput-filesoutput-files(下述)を呼ばなければなりません。ユーザーが、このメソッドを上書きしたり、アウトプットトランスレーションを変更したりできるようにするためです。output-filesは多値を返しますが、performは最初の値――出力パス名のリスト――のみを使うべきです。出力ファイルがただ1つと決まっている場合は、代わりに関数output-fileが使えます。output-fileは出力パス名が1つだけであることを保証し、そのパス名を返します。

総称関数: output-files operation component

performメソッドが何らかの出力ファイルを伴うならば、その出力先をASDFが定められるようにこのメソッドも定義しなくてはなりません。

output-filesは2つの値を返します。1つ目はパス名のリストであり、2つ目はブーリアンです。ブーリアンがnilの場合(返り値が1つしかなく、2つ目が暗黙にnilとなる場合も含む)、:aroundメソッドがアウトプットトランスレーションに従ってパス名を変換します。例えば、コンパイルされたオブジェクトファイルが処理系依存のキャッシュとして保存される場合などは、アウトプットトランスレーションが使われています。ブーリアンがtの場合は:aroundメソッドで変換されることはありません。

総称関数: component-depends-on operation component

この総称関数は、アクションを引数に取り、依存するアクションをまとめたリストを返します。リストのフォーマットについては下で説明します。

[訳者補足]

上述の5つのサブクラスを継承してオペレーション間の依存関係を指定すれば、component-depends-onメソッドは適切に構成されるので、通常は明示的にこのメソッドを定義する必要はありません。また、特定のコンポーネント、システムに対して依存関係を記述したい場合も、:in-order-toオプションを使えば事足りるでしょう。(→ アクションの依存関係) しかし、それらを超えるような複雑な依存関係を指定したい場合――例えば特定のコンポーネント型に対するオペレーションに限って依存関係を追加したい場合など――は、メソッド定義が必要になるかもしれません。

なお、名前の印象に反して、コンポーネント間の依存関係を返す関数ではないことに気を付けましょう。システムの:depends-onオプションに与えた依存コンポーネントを調べたい場合は、代わりにsystem-depends-onという総称関数があります。

[訳者補足終わり]

自分でこのメソッドを定義する場合は、メソッドで追加したい内容と(call-next-method)で返るリストを常にappendして返すことを強く推奨します。あなたがASDFの内部を熟知しているのでない限り、そうしないと「興味深い」エラーが起こる可能性が高いです。これは概念的には、メソッドコンビネーションのappendを手動でやっているのと同じことになります。(残念ながら、標準のメソッドコンビネーションを互換的に導入するのは、手遅れでした。)

それでは、返り値について説明します。component-depends-onが返すリストの各エントリもまたリストです。

各エントリの最初の要素(car)はオペレーション指示子、つまりオペレーションオブジェクトそのものかオペレーションクラスのシンボル(例えばload-opcompile-opprepare-opなど)であり、後者はmake-operationでインスタンス化されるでしょう。

各エントリの2番目以降(cdr)はコンポーネント指示子のリストです。コンポーネント指示子はコンポーネントオブジェクトそのものか、find-componentでコンポーネントオブジェクトに変換される識別子(identifier)です。find-componentは現在の親コンポーネントを第1引数に、識別子を第2引数にして呼び出されるでしょう。識別子は一般的には、文字列か、シンボル(coerce-nameで小文字の文字列になる)か、あるいは文字列・シンボルのリストです。特にnilは親コンポーネントそのものを指します。

新しく作られたオペレーションには、次の総称関数についてメソッドを定義することができます

総称関数: input-files operation component

input-filesは、performに対する入力ファイルのパス名のリストを返します。

デフォルトのinput-filesのメカニズムは賢くできているので、あなたが自らメソッドを定義する必要はないことのほうが多いでしょう。メソッド定義が必要なのは、最終的な入力ファイル(のまとまり)が複数通り考えられるケースのみです。ほとんどのオペレーションはselfward-operationを継承していて、selfward-operationはソースファイルを含むように入力ファイルを適切に設定します。

総称関数: operation-done-p operation component

この総称関数は、アクション(オペレーションとコンポーネントの対)がもう一度performされなければならない場合にnilを返します。デフォルトは常にtです。

operation-done-pにメソッドを定義する必要があるのは、ファイルシステム上のタイムスタンプが変わっていなくても前回のオペレーションの実行を無効にしてやりなおすべき状況がある、という場合に限ります。そしてその場合、このメソッドはnilを返すべきでしょう。

例えば、このメソッドはtest-opについては常にnilを返します。テストというのは決して完了する性質のオペレーションではないからです。もちろん、単にテストのリポートファイルから結果を読み出すだけのオペレーションとしてtest-report-opのようなものを定義し、システム定義の:in-order-toオプションでtest-opをこのオペレーションに委ねて、何度繰り返しても同じになるようにすることは可能です。その場合はこのメソッドがtを返すようにしても良いでしょう。

何らかのメッセージを表示するオペレーションは、Lispのコンパイラやローダと同様に、出力先を標準の*standard-output*ストリームにするべきです。


7.2 コンポーネント

componentオブジェクトが表すのは、1つのソースファイルか、複数のソースファイルのまとまりか、あるいはそれらから変換されるもの(faslなど)です。

componentの組み込みのサブクラスとして重要なのはsystemsource-filemoduleです。systemはコンポーネント階層のトップレベルにある特別なコンポーネントであり、find-systemによって発見することができます。source-fileは1つのソースファイルに対応します。moduleは中間的なコンポーネントであり、他のコンポーネント(source-fileやさらなるmoduleであっても良い)のまとまりを表します。(→ コンポーネントの組み込みのサブクラス

システム指示子(system designator)はシステムそのものか、システムを表す文字列、シンボルです。(コンポーネント名の仕様と同じく、シンボルは小文字に変換されます。)

コンポーネント指示子(component designator)は基点となるコンポーネントに対する相対的な指示子であり、コンポーネントそのものか、文字列、シンボルか、あるいはコンポーネント指示子のリストです。

関数: find-system system-designator &optional (error-p t)

find-systemはシステム指示子を引数に取り、システムを返します。システムが見つからなかった場合、デフォルトではエラーmissing-componentを通知しますが、error-pnilの場合は単にnilを返します。

システム定義ファイルを見つけてメモリ上のシステムをアップデートするために、find-systemはリスト*system-definition-search-functions*に含まれる関数をそれぞれfuncallします。それらの関数はシステム名を引数に取り、(システム定義ファイルの)パス名かシステムオブジェクト(system-source-directoryでパス名に変換される)を返すべきであり、返ったパス名はメモリ上に登録されるでしょう。こうして見つかったシステム定義ファイルがロードされるのは、次の条件のいずれかを満たす場合です:

.asdファイルからシステム定義がロードされる際の暗黙のカレントパッケージはasdf-userであり、asdf-userasdfuiopuiop/common-lispuseしています。27 .asdファイル内で新しい変数・関数・クラスを定義するなど、単純なシステム定義にとどまらないことをする場合は、defpackagein-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拡張を参照してください。

システムの探索関数の仕様からもわかるように、システムfoofoo.asdという名のファイルに定義するべきであり、また、ファイルはセントラルレジストリかソースレジストリに指定されたディレクトリに置くようにするべきです。

1つのファイル内で複数のシステムを定義するのが便利なケースも良くあるでしょうが、ASDFはシステム名に基づいてシステム定義ファイルを探すことしかできません。この理由により、ASDF 3からはシステム定義を探すアルゴリズムが拡張され、ファイルfoo.asdには、システムfooに加えてfoo/barfoo/bazfoo/quuxといった名のシステムも定義できるようになりました。/で区切られたシステム名の最初の部分をシステムのプライマリネーム(primary name)と呼び、対応するシステムをプライマリシステム(primary system)と呼びます。ASDF 3は、名前にスラッシュを含むシステムを探すように指示されると、関数asdf:primary-system-nameによってプライマリネームを抽出し、まずはプライマリシステムを探してロードしようとします。そして、見つかった場合は、そのファイルにターゲットのシステムが定義されているかどうか、もしくはpackage-inferred-systemが定義されているかどうか調べます。30 foo.asdに、この命名ルールに従わないシステム(例えばfoo-testなど)が定義されている場合、ASDFはシステム定義の場所を自動的に見つけることができないので、事前に(asdf:find-system "foo")を実行するなどして、明示的にプライマリシステムの定義をロードしておかなければなりません。我々はそのような命名の慣習をやめることを強く推奨しますが、今のところ、後方互換性のためにサポートされてはいます。

関数: primary-system-name name

nameがスラッシュ/によって区切られている場合、primary-system-nameはプライマリネーム、つまり最初の部分を返します。

関数: locate-system name

一般的には、この関数を直接呼び出すべきではありません。この関数がAPIの一部としてエクスポートされているのは、独自の*system-definition-search-functions*を使いたいプログラマのためです。

locate-systemはシステム名の指示子を引数に取り、システム定義をどこからロードすれば良いか調べようとします。

[訳者補足]

この関数そのものはシステム定義をロードすることはない――と原文には書かれています。しかし現在のバージョンでは、プライマリネームが絡んでいるケースで、例えば(asdf:locate-system :baz/test)などとすると、bazbaz/testのシステム定義がロードされることがあるようです。いずれにせよ、システム定義のロードにはfind-systemを、システムそのもののロードにはload-systemを使うべきです。

[訳者補足終わり]

この関数は6つの値を返します: foundpfound-systempathnamepreviousprevious-timeprevious-primaryfoundpはシステムが見つかれば(未登録であれ、登録済みであれ)真です。found-systemnilでない場合、システムオブジェクトです。(そのシステムは登録されているかもしれないし、されていないかもしれません。) pathnamenilでない場合、システム定義ファイルのパス名であり、このパス名はfound-systempreviousのシステムと対応しているでしょう。previousnilでない場合、前回ロードされた同名のシステムオブジェクトです。(なお、ここでロードされているのはシステム定義であって、システムそのものではないことに注意しましょう。)previous-timenilでない場合、previousの定義が前回ロードされた際のシステム定義ファイルのタイムスタンプです。previous-primarynilでない場合、システムpreviousのプライマリシステムです。

例えば、ソースレジストリに登録されたパス/current/path/to/foo.asdにシステム定義ファイルがあるとしましょう。しかし、システムfooは前回は/previous/path/to/foo.asdからロードされたとします。このとき、locate-systemは次の値を返すでしょう:

  1. foundptです。
  2. found-systemnilです。
  3. pathname#p"/current/path/to/foo.asd"です。
  4. previoussystemオブジェクトであり、そのsystem-source-fileスロットは#p"/previous/path/to/foo.asd"です。
  5. previous-timeは前回#p"/previous/path/to/foo.asd"がロードされたときのタイムスタンプです。
  6. previous-primarypreviousと同じオブジェクトです。
関数: find-component base path

baseコンポーネント(あるいは指示子)とpathを引数に取り、baseを基点としてpathで指示されるコンポーネントを見つけます。

pathがコンポーネントオブジェクトの場合は、baseに関係なく、そのままpathが返ります。

pathが文字列かシンボル(coerce-nameで文字列に変換される)の場合、baseはシステムかモジュールを指していなくてはなりません。この場合、返り値はpathの指すbaseの子コンポーネントです。

pathconsセルの場合、find-componentbasepathcarから再帰的に子コンポーネントを辿り、末端のコンポーネントを返します。

basenilの場合、(find-component path nil)と同等になります。

baseが文字列かシンボル(coerce-nameで文字列に変換される)の場合、basefind-systemの返すシステムであるとみなされます。

baseconsセルの場合、base(find-component (car base) (cdr base))の返すコンポーネントであるとみなされます。


7.2.1 コンポーネントの共通の属性

すべてのコンポーネントはその型にかかわらず、次の属性(attribute)を持っています。コンポーネント名以外の属性は任意です。

7.2.1.1 コンポーネント名

コンポーネント名(component name)は文字列またはシンボルであり、シンボルはasdf:coerce-nameによって小文字の文字列に変換されます。コンポーネント名には総称関数component-nameでアクセスできます。

:pathname属性を上書きしない限り、コンポーネント名はUNIXスタイルの相対パスとしてそのままパス名指定子に使われるでしょう。(→ パス名指定子

7.2.1.2 バージョン識別子(: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型のコンポーネントだけであることに注意してください。ライブラリの作者が、システムに含まれる様々なファイルにバージョン番号を付けて自ら同期させるというような使い方は、おそらくまったく有用ではないでしょう。

7.2.1.3 必要なフィーチャー(: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)))と書くことはできません。

if-featureオプション

7.2.1.4 アクションの依存関係(:in-order-to

この属性には、当該コンポーネントに関するアクションが別のアクションに依存していることを記述します。任意の属性ですが、しばしば必要になります。

依存関係はオペレーションとコンポーネントのペアについて指定します。例えば次のように記述したとしましょう:

:in-order-to ((compile-op (load-op "a" "b") (compile-op "c"))
              (load-op (load-op "foo")))

このとき、このコンポーネントに対してcompile-opを実行する前に、ASDFはabに対して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引数でbaに依存すると指定したとき、それが実際に意味しているのは

  (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"))))などと記述することになるでしょう。

7.2.1.5 パス名(:pathname

この属性の指定は任意であり、たいていは省略されるでしょう。その場合、コンポーネント名が(相対パスとして)使われます。

パス名がどのように解釈されるかについては、パス名指定子を参照してください。

また、「トップレベル」のシステムを定義するdefsystemマクロは、システム直下のコンポーネントがファイルシステム上のどこにあるか決めるために、追加の処理をします。詳しくはdefsystemでシステムを定義するを参照してください。

総称関数: component-pathname component

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"

7.2.1.6 プロパティ(:properties

この属性は任意です。

[訳者補足]

この属性は廃止予定であり、ASDF 2との後方互換性のためだけに存在します。また、その名に反して、:propertiesオプションにはプロパティリストではなく連想リストを指定します。昔のASDFでは独自の属性が必要な時にcomponent-propertyで読み書きしていた、という点だけ知っていれば十分でしょう。

[訳者補足終わり]

システムをパッケージングするにあたって、ASDFの組み込みの属性に指定した情報に加えて、ファイルやシステムについての他の情報が必要になることはしばしばあります。ASDFシステムからベンダーのパッケージを作るプログラムは、これらのシステムを満たすために「プレースホルダ―」情報を作る必要があります。ASDFシステムを作る方は、その付加的な情報を直接読み書きすることがあるでしょう。

(component-property component property-name)setfのプレースとしても使用可能で、この情報を読み書きすることができます。property-nameの同一性はeqlで判定されるので、シンボルやキーワード等を使いましょう。


7.2.2 コンポーネントの組み込みのサブクラス

コンポーネント: source-file

ソースファイルとは、システムの他のコンポーネントから生成する方法をシステムが知らないファイルを指します。

これは一般的な意味でのソースファイル、つまり「コンパイラに渡すデータを含んだファイル」と必ずしも同じでないことに注意してください。何らかのプリプロッセンシグを経て生成されたファイル(例えば.h.inからautoconfによって生成された.hファイルなど)は、定義上、ソースファイルとは呼ばれないことになります。逆に、(毎回自動生成されない)画像ファイルや、バイナリとして得られるプロプライエタリな共有ライブラリなどは、我々の目的においてはソースファイルとみなすことになります。

さまざまな言語に対してsource-fileのサブクラスが存在します。

[訳者補足]

コンポーネント: cl-source-file
コンポーネント: c-source-file
コンポーネント: java-source-file
コンポーネント: static-file
コンポーネント: doc-file
コンポーネント: html-file

cl-source-filec-source-filejava-source-fileはそれぞれCL、C、Javaのソースファイルに対応し、ファイル形式はlispcjavaです。static-fileはビルドの際にそのまま出力されることを意図したファイルを表し、ファイル形式は不定です。doc-filestatic-fileのサブクラス、html-filedoc-fileのサブクラスであり、html-fileのファイル形式はhtmlです。

defsystemフォームの:componentsオプションにコンポーネントを記述するとき、:fileは既定ではcl-source-fileを意味します。このクラスは親コンポーネントの:default-component-classオプションで書き換えることができます。

[訳者補足終わり]

コンポーネント: module

モジュールは、コンポーネントのまとまりとしてのコンポーネントです。

モジュールコンポーネントには次の追加の属性が指定できます。

デフォルトのオペレーションはモジュールをどのように走査すれば良いか知っているため、ほとんどのオペレーションにはモジュールに特定化されたメソッドを定義する必要はないでしょう。

他の言語のライブラリ群やアーカイブファイルを表すのに、moduleのサブクラスを定義することもできるでしょう。

コンポーネント: system

システムはモジュールのサブクラスです。

システムは、主にドキュメントを付けるためにいくつかの属性を追加されたモジュールです。詳細はdefsystemの文法を参照してください。

ユーザーは自分のシステムのために自ら新しいクラスを定義することもできます。定義したクラスはdefsystemマクロの:classオプションに指定することで使えます。


7.2.3 新しいコンポーネント型を定義する

新しいコンポーネント型を作るには、既存のコンポーネントのサブクラスを作り、新しいクラスに特定化されたメソッドを定義します。

例を見てみましょう。あなたのシステムは処理系依存の機能をいくつか備えています。あなたは、それらのソースファイルを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" ...))
     ...
    ))

7.3 依存関係について

ビルドを成功させるためには、アクションのグラフが循環的であってはなりません。あなたが(ASDFの純粋なユーザーであれ、拡張を実装しているのであれ)依存関係のグラフにループを含めてしまうと、ASDFは盛大なエラーを引き起こすでしょう。依存の向きを明確にするため、ASDF 3では、アクションが他のアクションに依存していることをrequiringrequiredという言葉で表しています。つまり“requiring action”は、それ自身がperformされる前に、そのすべての“required action”が完了することに依存(depends-on)しているわけです。

defsystemの文法を使うと、ユーザーはオブジェクトの階層グラフに沿った直接の依存関係(コンポーネントとその親コンポーネント、子コンポーネント、兄弟コンポーネント31)を簡単に書くことができますし、CLOSメソッドをカスタマイズすることでさらに複雑な依存関係を表現することも可能です。もっとも一般的なオペレーションであるload-opcompile-opload-source-opなどはコンポーネントの階層を「下向き」に伝搬し、「共変的」に振る舞います。つまり、親コンポーネントに対するオペレーションを行う前に、まずはすべての子コンポーネントに対して同じオペレーションを行わなくてはならないわけです。いっぽう、prepare-opや(ASDF 3で導入された)prepare-souce-opのようなオペレーションは、コンポーネントの階層を「上向き」に伝搬し、「反変的」に振る舞います。つまり、子コンポーネントに対するコンパイルを準備するオペレーションを行う前に、まずは親コンポーネントに対して同じオペレーションを行わなくてはならず、そのような挙動によって、子コンポーネントがコンパイル・ロードされる前に、親コンポーネントの依存関係がすべて(コンパイル、)ロードされることが保証されるわけです。最後に、test-opload-bundle-opはターゲットのシステムのレベルにとどまるオペレーションであり、階層を伝搬していくことはありません。これらはシステムに対してグローバルな処理を行うのです。

[訳者補足]

このセクションはオペレーションの依存関係について述べたものですが、実践的には、新しいオペレーションを定義するの補足に挙げた5つのオペレーションについて理解していれば事足りるでしょう。つまり、

[訳者補足終わり]


7.4 関連する関数

総称関数: version-satisfies version version-spec

versionversion-specを満たすか判定する総称関数です。組み込みのメソッドとしては、versioncomponentのものと文字列のものが用意されており、componentの場合はそのバージョン文字列が使われます。いっぽう、version-specは文字列であるべきです。

versionversion-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-satisfiesversionversion-specが同じメジャーバージョン番号(1番目の整数)であることも必要条件にしていました。メジャーバージョンが違う場合はversion-specを満たさないとみなされたのです。しかし、この挙動はドキュメントには書かれておらず、おそらく仕様として依拠することはできなかったし、一部のユーザーにとっては悩みの種でした。ASDF 3.0.1から、version-satisfiesはメジャーバージョンを特別扱いしなくなり、単にバージョンが同じか新しければtを返すようになりました。

なお、バージョン指定付きの依存関係を表す(:version ...)構文32は現在は特定のバージョン「以上」という指定しかできませんが、将来的には「未満」も指定できるようになるかもしれません。


8 ASDFがシステムを探す場所を設定する


8.1 ソースレジストリの設定

ASDFがシステム定義ファイルを探す場所は、ソースレジストリ(source registry)に指定されています。

[訳者補足]

ソースレジストリ(や次章で解説するアウトプットトランスレーション)の設定を記述したファイルは、システム設定ファイル(system configuration file)または単に設定ファイル(configuration file)と呼ばれます。ファイル形式は.confです。システム定義ファイル(.asdファイル)と混同しないようにしましょう。さらに、設定ディレクトリ(configuration directory)という概念もあります。設定ディレクトリは設定ファイルを含むディレクトリですが、設定ディレクトリ中の設定ファイルはその文法が微妙に異なるので注意が必要です。詳しくはソースレジストリを設定するDSL設定ディレクトリ(ソースレジストリ)を参照してください。

また、以下の記述は、OSや処理系、環境変数によって異なる複雑なロジックを漏れなく解説したものではありませんが、これを補完するのは訳者の手に余ります。正確な挙動を把握する必要がある場合はsource-registry.lispuiop/configuration.lispを参照してください。

ソースレジストリは次のように設定されます。おおむね、上から順に処理されていくと考えてかまいません。

[訳者補足終わり]

  1. ソースレジストリには、ハードコーディングされた設定が加わることがあります。これは主に処理系(特にSBCL)自身が提供するシステムの場所を追加する処理で、処理系のバージョンによってパスが変わるのにも対応しています。
  2. アプリケーションは、ソースレジストリのAPIを使って明示的に設定を初期化することが許されており、その場合はその設定が優先されることになります。設定はコマンドラインからも行えますし、スクリプトファイルからも、あるいは独自のシステム設定ファイルからも行うことができます。(→ ソースレジストリのAPI
  3. 環境変数CL_SOURCE_REGISTRYが存在する場合は、ソースレジストリがこの変数によって設定されます。
  4. 環境変数XDG_CONFIG_DIRSとユーザー設定ファイル$XDG_CONFIG_DIRS/common-lisp/source-registry.confが存在する場合は、ソースレジストリがこのファイルによって設定されます。XDG_CONFIG_DIRSのデフォルトの値は~/.config/です。
  5. 環境変数XDG_CONFIG_DIRSとユーザー設定ディレクトリ$XDG_CONFIG_DIRS/common-lisp/source-registry.conf.d/が存在する場合は、ソースレジストリがこのディレクトリ内の設定ファイルによって設定されます。
  6. ソースレジストリには、既定のディレクトリである~/common-lisp/(ASDF 3.1.2以降のみ)、~/.sbcl/systems/(SBCLのみ)、$XDG_DATA_HOME/common-lisp/systems/(ただし、再帰的探索はしない)、$XDG_DATA_HOME/common-lisp/source/が加わります。XDG_DATA_HOMEのデフォルトの値は~/.local/share/です。Windowsで環境変数XDG_DATA_HOMEが存在しない場合は、代わりにLocal AppDataまたはAppDataが使われます。
  7. 設定ファイル/etc/common-lisp/source-registry.confが存在する場合、ソースレジストリがこのファイルによって設定されます。Unixのみの処理です。
  8. 設定ディレクトリ/etc/common-lisp/source-registry.conf.d/が存在する場合、ソースレジストリがこのディレクトリ内の設定ファイルによって設定されます。Unixのみの処理です。
  9. $XDG_DATA_DIRS/common-lisp/systems/$XDG_DATA_DIRS/common-lisp/source/がソースレジストリに加わります。ただし、前者には再帰的探索がされないので、.asdファイルへのリンクを置く目的で使います。UnixではXDG_DATA_DIRSのデフォルトは/usr/local/share/usr/shareです。WindowsでXDG_DATA_DIRSが存在しない場合、代わりにAppDataCommon AppDataが使われるでしょう。
  10. ソースレジストリは、処理系提供の拡張モジュールに対応する処理系依存のディレクトリを含むことがあります。33

これらの設定は、S式による簡単なDSLで記述します。(→ ソースレジストリを設定するDSL) 加えて、環境変数で設定する場合は、よりシェルに向いた文法も用意されています。(→ シェル向けの設定の構文(ソースレジストリ)

また、上に挙げた各設定は、必ずしもすべてが「結合」して使われるわけではありません。各設定が、継承する設定を明示的にあるいは暗黙にインクルードしない限り、以降の設定は無視されることになります。

さらに、処理系依存のディレクトリが、設定ファイルで指定されたディレクトリ群の先頭に自動的に加わることがあり、これは設定ファイルで継承をどのように設定しても変えられません。(訳注: 上記リストの1番目の処理のこと)


8.2 Truenames and other dangers

初期の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の古典です。


8.3 XDGベースディレクトリ

設定ファイルやデータファイル、キャッシュファイルを保持する場所に関しては、我々は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 AppDataAppDataCommon AppDataなどに置き換えています。また、Windowsのレジストリに問い合わせるのは、大量のポータブルなCommon Lispコードにおいては不可能ですから、ASDF 3は(レジストリと同期していることを願って)対応する環境変数を使っています。詳細についてはuiop/configuration.lispを参照してください。また、改善案があればぜひお願いします。


8.4 後方互換性のための機能

後方互換性のために、また、ハッカーに実用的なバックドアを提供するために、ASDF 3はセントラルレジストリという古い仕組みもサポートしています。ASDFは、ソースレジストリを探すにまずasdf:*central-registry*に指定されたディレクトリから.asdファイルを探します。

この変数の使い方についてはASDFがシステムを見つけられるように設定する(古いスタイル)を参照してください。

デフォルトではasdf:*central-registry*は空でしょう。

つまり、この古い方法は、使わなければあなたには関係ありませんが、使う場合は新しい仕組みに優先することになります。


8.5 ソースレジストリを設定するDSL

ソースレジストリの設定は、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がシステム定義ファイルを見つける際には、このディレクトリ以下が再帰的に探索されることになります。


8.6 設定ディレクトリ

設定ディレクトリ内の.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/")

8.6.1 :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/を基点にした相対パスを解釈して)設定するでしょう。


8.7 シェル向けの設定の構文

環境変数CL_SOURCE_REGISTRYが空の文字列の時、ASDFはこの設定をスキップします。また、(から始まる場合は、先に述べたDSLによるS式と解釈し、全体にreadを適用します。しかし、それ以外の場合は別の構文によるパスのリストとみなします。この構文は以下の通りで、TEXINPUTSのスタイルに似ています。


8.8 探索アルゴリズム

念のため説明しておくと、与えられたシステム名を持つシステムを探すとき、ディレクティブは順番に処理されていきます。

ディレクトリ(:directory)を探索する場合、システムが見つかれば探索は成功であり、見つからなければ次の探索に進みます。

ツリー(:tree)を探索する場合、システムがただ1つ見つかれば探索は成功です。複数のシステムが見つかった時の動作はバージョンによって異なりますが、ASDF 3.2.1以降では、パス名の(ノーマライズされた)ディレクトリコンポーネント40の長さを比較し、短いほうが探索結果として採用されるようになりました。長さが同じ場合は、パス名のunix-namestringstring<で比較して小さいほうが採用されます。それ以前のASDFでは最初に見つかったシステムを返していましたが、どのシステムが返るかは処理系によって異なり、上の仕様のようにstring<に関して小さいパス名が返るかどうかも不確定でした。また、XCVB41ではエラーが通知されていました。システムが見つからなかった場合は次の探索に進みます。

:exclude:also-excludeディレクティブには、ツリーを探索するときに除外するサブディレクトリ名を指定します。一般的には、バージョン管理システムのデータベースディレクトリ(.git_darcsなど)は除外されるでしょう。なお、除外の指定は継承された設定には伝搬しません。すべての設定は、デフォルトではasdf::*default-source-registry-exclusions*に登録されている除外パターンを採用します。

:includeディレクティブでインクルードされた設定については、探索は再帰的に適用されます。 また、:inherit-configurationディレクティブで継承された設定についても、探索は再帰的に適用されます。


8.9 ソースレジストリのキャッシュ

ソースレジストリがどのタイミングで構成されるかは、実装に任されています。つまり、ソースレジストリの実装は、設定とファイルシステムからソースレジストリを先行して(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を置き換える


8.10 ソースレジストリのAPI

次の関数はasdfパッケージからエクスポートされています。

関数: initialize-source-registry &optional (parameter *source-registry-parameter*)

設定を読み込み、すべての内部変数を初期化します。その際、引数を与えることで、環境変数や設定ファイルによる設定を上書きしたり拡張したりすることができます。parameternilの時は設定の上書きは起こりません。S式の時はS式によるDSLが適用されます。文字列の時はシェル向けのDSLが適用されます。パス名の時は対応する設定ファイル・設定ディレクトリから設定が読み込まれます。シンボルの時はfuncallされ、返ったオブジェクトの型に応じて同じ処理が適用されます。

関数: clear-source-registry

ソースレジストリの現在の設定と探索アルゴリズムによるキャッシュをすべて削除します。コアイメージのダンプをする際、そのイメージを別の(ソースレジストリの)設定で復元する予定であれば、その前にこの関数(あるいはclear-configuration)を呼んで、設定を空にしておく必要があるかもしれません。なお、この関数を呼んでも、現在のイメージにロードされているシステム定義は削除されないことに注意してください。消えるのは(まだシステム定義が読み込まれていない)システムを見つける際にどこを探すか、という情報だけです。

関数: ensure-source-registry &optional (parameter *source-registry-parameter*)

ソースレジストリが既にinitialize-source-registryで初期化されたかどうかチェックします。されていない場合はinitialize-source-registryを呼びます。

find-systemが呼ばれた時、(直接呼ばれたにせよ、operateload-systemなどから呼ばれたにせよ)ASDFは(ensure-source-registry nil)を実行します。つまり、find-systemが初めて呼ばれた時に、ソースレジストリは初期化されるわけです。また、設定ファイルを変更したあとは、明示的にinitialize-source-registryで初期化するか、あるいはclear-source-registryclear-configurationでソースレジストリをクリアし、次のfind-systemで初期化が行われるようにする必要があります。


8.11 イントロスぺクション


8.11.1 *source-registry-parameter*変数

*source-registry-parameter*は、前回initialize-source-registryが呼ばれた時の引数を保持しているので、過去のソースレジストリの設定について知りたい時に参照できます。デフォルトはnilです。この変数はリードオンリーです。書き込んではいけません。


8.11.2 システムの依存関係についての情報

システム間の依存関係について知りたい時には、次の3つの関数が使えます。これらは依存関係の解析を行いたいプログラマのために用意されています。

関数: system-defsystem-depends-on system
関数: system-depends-on system
関数: system-weakly-depends-on system

systemが弱依存しているシステムの名前のリストを返します。弱依存は廃止予定の機能であり、使うことは推奨されていません。defsystemフォームの:weakly-depends-onオプションに指定されたシステムについては、ASDFはそれらのシステムを探しますが、仮に見つからなくてもエラーを通知しません。(→ 弱い依存関係

上の2つの関数については、返り値はdependency-defのリスト(→ defsystemの文法)であるため、必ずしもシステム名のリストとは限りません。いっぽう、system-weakly-depends-onは常にシステム名のリストを返します。


8.12 現状

ソースレジストリの仕組みは大いに成功をおさめたので、我々はもはやasdf:*central-registry*は推奨しないと宣言しました。しかし、サポートは継続するでしょう。

探索の仕組みのうち、処理系に依存する部分についてはすべてasdf::wrapping-source-registry関数(ソースレジストリの設定の1番目の処理にあたる)に統合され、常に暗黙に利用されています。


8.13 却下されたアイデア

ASDF 2の開発中に私(FRR)が検討し、却下した代案には、次のようなものがあります。

  1. Keep 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.
  2. Keep 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.
  3. Completely remove 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.
  4. Replace 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を必要以上に複雑にしないためです。


8.14 TODO


8.15 ソースレジストリに関するクレジット

最初のアイデアについてはStelian Ionescuに多大な感謝をささげます。

最初の実装の試みについてはRommel Martinezに感謝をささげます。

良いデザイン案やエレガントな実装のトリックは彼らのおかげですが、悪いデザイン案や実装のバグはすべて私に帰するものです。

— Francois-Rene Rideau fare@tunes.org, Mon, 22 Feb 2010 00:07:33 -0500


9 ASDFがコンパイルされたファイルを保持する場所を設定する

fasl44、つまりコンパイルされたファイルのフォーマットは、処理系によって異なります。faslを扱うにあたっては、厄介な問題がいくつか存在します。まず、複数の処理系(あるいは1つの処理系の複数のバージョン)を使っている場合に、ソースディレクトリが様々な形式のfaslファイル(fasldfslcfslなど)で散らかってしまいます。その上、異なる処理系が同じ拡張子を使っていることがあり、また、同じ処理系でもバージョン間、プラットフォーム間でfaslの互換性がないのに同じ拡張子を維持していたりします。このため、別の処理系に切り替えたときに、膨大なエラーと混乱が引き起こされるかもしれません。さらに、faslをソースファイルと同じディレクトリに保持するスタイルは、ソースディレクトリへの書き込みを必要とするので、複数のユーザーが同じソースディレクトリを共有するのを困難にします。

この問題を軽減するため、ASDF 2以降にはasdf-output-translations機能が備わっています。


9.1 アウトプットトランスレーションの設定

アウトプットトランスレーション(output translations)は(ソースファイルなどの)入力パスに(faslなどの)出力パスを対応させる仕組みです。アウトプットトランスレーションの設定ファイルは、ソースレジストリと同様に、XDGベースディレクトリの仕様に沿った場所に置かれます。

[訳者補足]

アウトプットトランスレーション(や前章で解説したソースレジストリ)の設定を記述したファイルは、システム設定ファイル(system configuration file)または単に設定ファイル(configuration file)と呼ばれます。ファイル形式は.confです。システム定義ファイル(.asdファイル)と混同しないようにしましょう。さらに、設定ディレクトリ(configuration directory)という概念もあります。設定ディレクトリは設定ファイルを含むディレクトリですが、設定ディレクトリ中の設定ファイルはその文法が微妙に異なるので注意が必要です。詳しくはアウトプットトランスレーションを設定するDSL設定ディレクトリ(アウトプットトランスレーション)を参照してください。

また、以下の記述は、OSや処理系、環境変数によって異なる複雑なロジックを漏れなく解説したものではありませんが、これを補完するのは訳者の手に余ります。正確な挙動を把握する必要がある場合はoutput-translations.lispuiop/configuration.lispを参照してください。

アウトプットトランスレーションは次のように設定されます。おおむね、上から順に処理されていくと考えてかまいません。

[訳者補足終わり]

  1. アウトプットトランスレーションには、ハードコーディングされた設定が加わることがあります。これはソースレジストリの特別なエントリに対応した特別な出力先として機能します。
  2. アプリケーションは、アウトプットトランスレーションのAPIを使って明示的に設定を初期化することが許されており、その場合はその設定が優先されることになります。設定はコマンドラインからも行えますし、スクリプトファイルからも、あるいは独自のシステム設定ファイルからも行うことができます。(→ Output location API
  3. 環境変数ASDF_OUTPUT_TRANSLATIONSが存在する場合は、アウトプットトランスレーションがこの変数によって設定されます。
  4. 環境変数XDG_CONFIG_DIRSとユーザー設定ファイル$XDG_CONFIG_DIRS/common-lisp/asdf-output-translations.confが存在する場合は、アウトプットトランスレーションがこのファイルによって設定されます。XDG_CONFIG_DIRSのデフォルトの値は~/.config/です。
  5. 環境変数XDG_CONFIG_DIRSとユーザー設定ディレクトリ$XDG_CONFIG_DIRS/common-lisp/asdf-output-translations.conf.d/が存在する場合は、アウトプットトランスレーションがこのディレクトリ内の設定ファイルによって設定されます。
  6. 設定ファイル/etc/common-lisp/asdf-output-translations.confが存在する場合、アウトプットトランスレーションがこのファイルによって設定されます。Unixのみの処理です。
  7. 設定ディレクトリ/etc/common-lisp/asdf-output-translations.conf.d/が存在する場合、ソースレジストリがこのディレクトリ内の設定ファイルによって設定されます。Unixのみの処理です。 45

これらの設定は、S式による簡単なDSLで記述します。(→ アウトプットトランスレーションを設定するDSL) 加えて、環境変数で設定する場合は、よりシェルに向いた構文も用意されています。(→ シェル向けの設定の構文(アウトプットトランスレーション)

また、上に挙げた各設定は、必ずしもすべてが「結合」して使われるわけではありません。各設定が、継承する設定を明示的にあるいは暗黙にインクルードしない限り、以降の設定は無視されることになります。

なお、アウトプットトランスレーションのデフォルトの設定では、出力ファイルにはユーザーごとに分離されたキャッシュディレクトリが使われます。46 この仕様によって、インストールされたソフトウェアを複数のユーザー間でシームレスに使うことができますし、開発者がソースコードを閲覧する際に出力ファイルが邪魔にならないようにできます。もっとも、開発者が出力ファイルを掃除したくなった場合にアウトプットトランスレーションについて学習する必要がある、という代償もありますが。47


9.2 後方互換性のための機能

アウトプットトランスレーションと同様の機能を持つ従来のツールとしてASDF-Binary-Locationscommon-lisp-controllercl-launchなどがありましたが、ASDFにはこれらの以前のバージョンとの後方互換性がありません。(この仕様は意図的なものです。)従来のツールは、ユーザーが設定ファイルで簡単に設定できるようなデザインになっていませんでした。なお、最近のバージョンのcommon-lisp-controller (7.2)とcl-launch (3.000)はasdf-output-translationsのAPIを使っており、また、ASDF-Binary-Locationsは完全に置き換えられているのでもはや使うべきではありません。

もっとも、この非互換性によって不便を被る人はあまりいないでしょう。実のところ、これらのツールをカスタマイズして使っている人はほとんどいませんし、その少数の人々はエキスパートであり、新しい設定に容易に適応するでしょう。そして、エキスパートでないほとんどの人々は(デフォルトの設定で使えている限りは)これらのツールを適切に設定することができず、システムのディストリビュータによる設定やデフォルト設定によってソフトウェアが「ただ動く」ことを知るだけでしょうから。

しかし、ASDF-Binary-Locationsを好む方のために、限定的なエミュレーションモードが用意されています:

関数: enable-asdf-binary-locations-compatibility &key centralize-lisp-binaries default-toplevel-directory include-per-user-information map-all-source-files source-to-target-mappings

この機能は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の古いバージョンに組み込まれたものではなく、最新版に拡張としてロードできるもの)を使い続ける方は、始めに無効にするように気を付けてください。そうしないと「興味深い」問題が起きるかもしれません。


9.3 アウトプットトランスレーションを設定するDSL

アウトプットトランスレーションの設定は、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は次の設定について再帰的に処理を適用します。次の設定とは、アウトプットトランスレーションの設定のリストにおいて下で処理される設定のことです。

ユーザーキャッシュに関するディレクティブは以下の通りです:


9.4 設定ディレクトリ

設定ディレクトリ内の.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/")

9.5 シェル向けの設定の構文

ASDFは環境変数ASDF_OUTPUT_TRANSLATIONSを次のように処理します:

ディレクトリのリストは、正確に言えばディレクトリのペアのリストであり、1つのペアがマッピングのディレクティブを表します。各エントリの区切りは、UNIX(Cygwin含む)では:であり、それ以外(主にWindows)では;です。

空のエントリがペアの1つ目のエントリ(つまり入力指示子)の位置に現れた場合、その部分に継承された設定が挿入されます。この場合、2つ目のエントリはないものとして扱われ、次のエントリは新しいペアとみなされます。空のエントリがペアの2つ目のエントリ(つまり出力指示子)の位置に現れた場合、1つ目のエントリと同じディレクトリを指します。(つまり、ディレクトリを繰り返し書いたのと同等です。)

例として"/foo:/bar::/baz:"という設定を見てみましょう。この設定の最初のペアは/foo:/barですから、/foo/以下のパスは/bar/以下に変換されることになります。その次は空のエントリなので、継承された設定がこの部分に挿入されます。最後のペアは/baz:であり、これは/baz:/bazと同等ですから、/baz/以下のパスは変換されないことになります。


9.6 アウトプットトランスレーションのセマンティクス

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).


9.7 Caching Results

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.


9.8 Output location API

The specified functions are exported from package ASDF.

Function: initialize-output-translations &optional PARAMETER

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).

Function: disable-output-translations

will initialize output translations in a way that maps every pathname to itself, effectively disabling the output translation facility.

Function: clear-output-translations

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.

Function: ensure-output-translations &optional PARAMETER

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.

Function: apply-output-translations PATHNAME

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.


9.9 Credits for output translations

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


10 エラー処理

10.1 ASDFのエラー

ASDFが不正なシステム定義を検出すると、system-definition-errorを通知します。

いっぽう、オペレーションが失敗した場合(例えばソースファイルがエラーを含んでいた場合)は、operation-errorが通知されます。

10.2 コンパイル時のエラー・警告の処理

ASDFは、ファイルのコンパイル時の警告、エラーをチェックしており、それらを検出した時の動作は変数*compile-file-warnings-behaviour**compile-file-failure-behaviour*で制御することができます。有効な値は:error:warn:ignoreです。


11 Miscellaneous additional functionality

ASDF includes several additional features that are generally useful for system definition and development.


11.1 Controlling file compilation

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).


11.2 Controlling source file character encoding

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.


11.3 Miscellaneous Functions

These functions are exported by ASDF for your convenience.

Function: system-relative-pathname system name &key type

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"
Function: system-source-directory system-designator

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.

Function: clear-system system-designator

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.

Function: register-preloaded-system name &rest keys &key version &allow-other-keys

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.

Function: register-immutable-system name &rest keys

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.

Function: run-shell-command control-string &rest args

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).


11.4 Some Utility Functions

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.

Function: parse-unix-namestring name &key type defaults dot-dot ensure-directory &allow-other-keys

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.

Function: merge-pathnames* specified &optional defaults

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.

Function: subpathname pathname subpath &key type

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.

Function: subpathname* pathname subpath &key type

This function returns nil if the base pathname is nil, otherwise acts like subpathname.

Function: run-program command &key ignore-error-status force-shell input output error-output if-input-does-not-exist if-output-exists if-error-output-exists element-type external-format &allow-other-keys

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.

Function: slurp-input-stream processor input-stream &key

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:


12 最新版を手に入れる

まずはどのバージョンが欲しいか決めましょう。開発はmasterブランチで行われており、HEADは最新のバグフィックスとポータビリティの修正を含んでいて、たいてい使えます。また、我々は(限られた)テストスイートも備えてはいます。しかし、リグレッションが起こることはあります。

用心深い人はreleaseブランチを使うべきでしょう。たいていの場合、このブランチはより多くのテストがなされていますし、リリースは既知の解決されていないイシューがなくなったタイミングでカットされるからです。

ASDFのソースリポジトリはgitを使って得られます: git clone https://gitlab.common-lisp.net/asdf/asdf.git

上で述べたタグはこのリポジトリに見つかるでしょう。また、リポジトリはhttps://gitlab.common-lisp.net/asdf/asdfで閲覧することもできます。

ASDFの開発に関する議論はメーリングリストで行われています。(→ メーリングリスト


13 FAQ


13.1 どこにバグを報告すればよいですか

ASDFのバグはlaunchpadにトラックされています: https://launchpad.net/asdf

バグかどうかわからない場合、あるいは一般的な議論をしたい場合は、メーリングリストasdf-develを使ってください。(→ メーリングリスト


13.2 メーリングリスト

ASDFの開発に関する議論は、メーリングリストasdf-devel@common-lisp.netで行われています。http://common-lisp.net/cgi-bin/mailman/listinfo/asdf-devel


13.3 “What has changed between ASDF 1, ASDF 2, and ASDF 3?”

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.


13.3.1 What are ASDF 1, ASDF 2, and ASDF 3?

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.


13.3.2 How do I detect the ASDF version?

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を置き換える.


13.3.3 ASDF can portably name files in subdirectories

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 パス名指定子.


13.3.4 Output translations

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がコンパイルされたファイルを保持する場所を設定する.


13.3.5 Source Registry Configuration

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がシステムを探す場所を設定する.


13.3.6 Usual operations are made easier to the user

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.


13.3.7 Many bugs have been fixed

The following issues and many others have been fixed:


13.3.8 ASDF itself is versioned

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.


13.3.9 ASDF can be upgraded

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.


13.3.10 Decoupled release cycle

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.


13.3.11 Pitfalls of the transition to ASDF 2

The main pitfalls in upgrading to ASDF 2 seem to be related to the output translation mechanism.

Other issues include the following:


13.3.12 Pitfalls of the upgrade to ASDF 3

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.


13.3.13 What happened to the bundle operations?

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:


13.4 Issues with installing the proper version of ASDF


13.4.1 “My Common Lisp implementation comes with an outdated version of ASDF. What to do?”

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を置き換える.


13.4.2 “I’m a Common Lisp implementation vendor. When and how should I upgrade 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:


13.4.3 After upgrading ASDF, ASDF (and Quicklisp) can’t find my systems

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.


13.5 Issues with configuring ASDF


13.5.1 “How can I customize where fasl files are stored?”

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.


13.5.2 “How can I wholly disable the compiler output cache?”

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.


13.5.3 How can I debug problems finding ASDF systems?

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.


13.6 Issues with using and extending ASDF to define systems


13.6.1 “How can I cater for unit-testing in my system?”

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.


13.6.2 “How can I cater for documentation generation in my system?”

Various ASDF extensions provide some kind of doc-op operation. See also https://bugs.launchpad.net/asdf/+bug/479470.


13.6.3 “How can I maintain non-Lisp (e.g. C) source files?”

See cffi’s cffi-grovel.


13.6.4 “I want to put my module’s files at the top level. How do I do this?”

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).


13.6.5 How do I create a system definition where all the source files have a .cl extension?

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
  ...)

13.6.6 How do I mark a source file to be loaded only and not compiled?

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.


13.6.7 How do I work with readtables?

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:

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.

13.6.7.1 How should my system use a readtable exported by another system?

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.

13.6.7.2 How should my library make a readtable available to other systems?

Use from the named-readtables system the macro named-readtables:defreadtable.


13.6.8 How can I capture ASDF’s output?

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.


13.6.9 *LOAD-PATHNAME* and *LOAD-TRUENAME* have weird values, help!

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:

  1. Use the 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.
  2. Store the pathname at compile time, so that you get the pathname of the source file, which is presumably what you want. To do this, you can capture the value of (or *compile-file-pathname* *load-truename*) (or *LOAD-PATHNAME*, if you prefer) in a macro expansion or other compile-time evaluated context.

13.7 ASDF development FAQs


13.7.1 How do I run the tests interactively in a REPL?

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")

ASDFプロジェクトで進行中の作業

現在すべきことの一覧については、ソースリポジトリのTODOファイルを参照してください。

また、バグは今のところlaunchpadでトラックされています: https://launchpad.net/asdf


参考文献


索引(用語)

Jump to:   *   :  
A   B   C   D   E   I   L   M   O   P   Q   R   S   T   V                      
Index Entry  Section

*
*features*: How do I detect the ASDF version?

:
:also-exclude source config directive: ソースレジストリを設定するDSL
:around-compile: Controlling file compilation
:asdf: はじめに
:asdf2: はじめに
:asdf3: はじめに
:build-operation: defsystemの文法
:compile-check: Controlling file compilation
:default-registry source config directive: ソースレジストリを設定するDSL
:defsystem-depends-on: defsystemの文法
:directory source config directive: ソースレジストリを設定するDSL
:entry-point: defsystemの文法
:exclude source config directive: ソースレジストリを設定するDSL
:feature dependencies: defsystemの文法
:if-feature component option: defsystemの文法
:ignore-invalid-entries source config directive: ソースレジストリを設定するDSL
:include source config directive: ソースレジストリを設定するDSL
:inherit-configuration source config directive: ソースレジストリを設定するDSL
:require dependencies: defsystemの文法
:tree source config directive: ソースレジストリを設定するDSL
:version: 単純な例
:version: defsystemの文法
:version: コンポーネントの共通の属性
:weakly-depends-on: defsystemの文法

A
action: ASDFのオブジェクトモデル
also-exclude source config directive: ソースレジストリを設定するDSL
around-compile keyword: Controlling file compilation
ASDF output: How can I capture ASDF's output?
ASDF versions: はじめに
ASDF-BINARY-LOCATIONS compatibility: 後方互換性のための機能(アウトプットトランスレーション)
asdf-output-translations: ASDFがコンパイルされたファイルを保持する場所を設定する
ASDF-related features: はじめに
asdf-user: 単純な例
ASDF-USER package: コンポーネント

B
bug tracker: どこにバグを報告すればよいですか
build-operation: 便利な関数

C
Capturing ASDF output: How can I capture ASDF's output?
compile-check keyword: Controlling file compilation
component: コンポーネント
component designator: コンポーネント

D
default-registry source config directive: ソースレジストリを設定するDSL
DEFSYSTEM grammar: defsystemの文法
directory source config directive: ソースレジストリを設定するDSL

E
exclude source config directive: ソースレジストリを設定するDSL

I
ignore-invalid-entries source config directive: ソースレジストリを設定するDSL
immutable systems: オペレーション
immutable systems: Miscellaneous Functions
include source config directive: ソースレジストリを設定するDSL
inherit-configuration source config directive: ソースレジストリを設定するDSL

L
launchpad: どこにバグを報告すればよいですか
logical pathnames: defsystemの文法

M
mailing list: メーリングリスト

O
One package per file systems: package-inferred-system拡張
operation: オペレーション

P
Package inferred systems: package-inferred-system拡張
Packages, inferring dependencies from: package-inferred-system拡張
pathname specifiers: defsystemの文法
Primary system name: コンポーネント

Q
Quicklisp: After upgrading ASDF

R
readtables: How do I work with readtables?

S
serial dependencies: defsystemの文法
system: コンポーネント
system designator: コンポーネント
System names: コンポーネント

T
Testing for ASDF: はじめに
tree source config directive: ソースレジストリを設定するDSL

V
version specifiers: defsystemの文法

アウトプットトランスレーション: ASDFがコンパイルされたファイルを保持する場所を設定する
アクション: ASDFのオブジェクトモデル

エントリーポイント: defsystemの文法

オペレーション: オペレーション

コンポーネント: コンポーネント
コンポーネントシジシ(コンポーネント指示子): コンポーネント

システム: コンポーネント
システムシジシ(システム指示子): コンポーネント

ソースレジストリ: ASDFがシステムを探す場所を設定する

バージョンシテイシ(バージョン指定子): defsystemの文法

パスメイシテイシ(パス名指定子): defsystemの文法

プライマリシステム: コンポーネント
プライマリネーム: コンポーネント

ロンリパスメイ(論理パス名): defsystemの文法

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  
Index Entry  Section

A
already-loaded-systems: 便利な関数
apply-output-translations: Output location API
asdf-version: How do I detect the ASDF version?

C
clear-configuration: ASDFの設定をリセットする
clear-output-translations: ASDFがオブジェクトファイルを保持する場所を設定する
clear-output-translations: Output location API
clear-source-registry: ソースレジストリのAPI
clear-system: Miscellaneous Functions
coerce-name: 新しいオペレーションを定義する
coerce-name: コンポーネント
coerce-name: コンポーネントの共通の属性
compile-file*: Controlling file compilation
compile-system: 便利な関数
component-depends-on: 新しいオペレーションを定義する
component-pathname: コンポーネントの共通の属性

D
define-package: package-inferred-system拡張
defsystem: 単純な例
defsystem: 複雑な例
defsystem: defsystemの文法
disable-output-translations: Output location API

E
enable-asdf-binary-locations-compatibility: 後方互換性のための機能(アウトプットトランスレーション)
ensure-output-translations: Output location API
ensure-source-registry: ソースレジストリのAPI

F
file-type: Pitfalls of the transition to ASDF 2
find-component: 新しいオペレーションを定義する
find-component: コンポーネント
find-system: コンポーネント
flatten-source-registry: How can I debug problems finding ASDF systems

I
initialize-output-translations: Output location API
initialize-source-registry: ソースレジストリのAPI
input-files: 新しいオペレーションを定義する

L
load-asd: 単純な例
load-system: 便利な関数
locate-system: コンポーネント

M
make: 便利な関数
make-operation: オペレーション
merge-pathnames*: Some Utility Functions

O
oos: 便利な関数
oos: オペレーション
operate: 便利な関数
operate: オペレーション
operation-done-p: 新しいオペレーションを定義する
output-files: 新しいオペレーションを定義する

P
package-inferred-system: package-inferred-system拡張
parse-unix-namestring: Some Utility Functions
perform: 新しいオペレーションを定義する
primary-system-name: コンポーネント
primary-system-name: コンポーネント

R
register-immutable-system: オペレーション
register-immutable-system: Miscellaneous Functions
register-preloaded-system: Miscellaneous Functions
register-system-packages: package-inferred-system拡張
require-system: 便利な関数
run-program: Some Utility Functions
run-shell-command: Miscellaneous Functions

S
slurp-input-stream: Some Utility Functions
source-file-type: Pitfalls of the transition to ASDF 2
subpathname: Some Utility Functions
subpathname*: Some Utility Functions
system-defsystem-depends-on: システムの依存関係についての情報
system-depends-on: システムの依存関係についての情報
system-relative-pathname: Miscellaneous Functions
system-relative-pathname: LOAD-PATHNAME has a weird value
system-source-directory: Miscellaneous Functions
system-weakly-depends-on: システムの依存関係についての情報

T
test-system: 便利な関数
traverse: オペレーション

U
uiop:define-package: package-inferred-system拡張

V
version-satisfies: コンポーネントの共通の属性
version-satisfies: 関連する関数

Jump to:   A   C   D   E   F   I   L   M   O   P   R   S   T   U   V  

索引(変数)

Jump to:   *  
A  
Index Entry  Section

*
*central-registry*: After upgrading ASDF
*central-registry*: How can I debug problems finding ASDF systems
*compile-file-failure-behaviour*: エラー処理
*compile-file-warnings-behaviour*: エラー処理
*default-source-registry-exclusions*: 探索アルゴリズム
*features*: はじめに
*image-dump-hook*: ASDFの設定をリセットする
*LOAD-PATHNAME*: LOAD-PATHNAME has a weird value
*LOAD-TRUENAME*: LOAD-PATHNAME has a weird value
*nil-pathname*: Some Utility Functions
*oldest-forward-compatible-asdf-version*: Pitfalls of the upgrade to ASDF 3
*source-registry*: How can I debug problems finding ASDF systems
*source-registry-parameter*: *source-registry-parameter*変数
*standard-output*: How can I capture ASDF's output?
*system-definition-search-functions*: コンポーネント

A
asdf:*user-cache*: アウトプットトランスレーションを設定するDSL
ASDF_OUTPUT_TRANSLATIONS: ASDFがコンパイルされたファイルを保持する場所を設定する

Jump to:   *  
A  

索引(クラスと型)

Jump to:   B   C   D   F   H   I   J   L   M   N   O   P   S   T   U  
Index Entry  Section

B
binary-op (obsolete): What happened to the bundle operations

C
c-source-file: コンポーネントの組み込みのサブクラス
cl-source-file: コンポーネントの組み込みのサブクラス
compile-bundle-op: 組み込みのオペレーション
compile-bundle-op: What happened to the bundle operations
compile-concatenated-source-op: 組み込みのオペレーション
compile-op: 組み込みのオペレーション
component: ASDFのオブジェクトモデル
concatenate-source-op: 組み込みのオペレーション

D
deliver-asd-op: 組み込みのオペレーション
deliver-asd-op: What happened to the bundle operations
dll-op: 組み込みのオペレーション
doc-file: コンポーネントの組み込みのサブクラス
downward-operation: 新しいオペレーションを定義する

F
fasl-op (obsolete): What happened to the bundle operations

H
html-file: コンポーネントの組み込みのサブクラス

I
image-op: 組み込みのオペレーション

J
java-source-file: コンポーネントの組み込みのサブクラス

L
lib-op: 組み込みのオペレーション
load-bundle-op: 組み込みのオペレーション
load-bundle-op: What happened to the bundle operations
load-compiled-concatenated-source-op: 組み込みのオペレーション
load-concatenated-source-op: 組み込みのオペレーション
load-fasl-op (obsolete): What happened to the bundle operations
load-op: 組み込みのオペレーション
load-source-op: 組み込みのオペレーション

M
module: コンポーネントの組み込みのサブクラス
monolithic-binary-op (obsolete): What happened to the bundle operations
monolithic-compile-bundle-op: 組み込みのオペレーション
monolithic-compile-bundle-op: What happened to the bundle operations
monolithic-compile-concatenated-source-op: 組み込みのオペレーション
monolithic-concatenate-source-op: 組み込みのオペレーション
monolithic-deliver-asd-op: 組み込みのオペレーション
monolithic-deliver-asd-op: What happened to the bundle operations
monolithic-dll-op: 組み込みのオペレーション
monolithic-fasl-op (obsolete): What happened to the bundle operations
monolithic-lib-op: 組み込みのオペレーション
monolithic-load-bundle-op: 組み込みのオペレーション
monolithic-load-bundle-op: What happened to the bundle operations
monolithic-load-compiled-concatenated-source-op: 組み込みのオペレーション
monolithic-load-concatenated-source-op: 組み込みのオペレーション
monolithic-load-fasl-op (obsolete): What happened to the bundle operations

N
non-propagating-operation: 新しいオペレーションを定義する

O
operation: ASDFのオブジェクトモデル
operation-error: エラー処理

P
prepare-op: 組み込みのオペレーション
prepare-source-op: 組み込みのオペレーション
program-op: 組み込みのオペレーション

S
selfward-operation: 新しいオペレーションを定義する
sideway-operation: 新しいオペレーションを定義する
source-file: コンポーネントの組み込みのサブクラス
static-file: コンポーネントの組み込みのサブクラス
system: コンポーネントの組み込みのサブクラス
system-definition-error: エラー処理

T
test-op: 組み込みのオペレーション

U
upward-operation: 新しいオペレーションを定義する

Jump to:   B   C   D   F   H   I   J   L   M   N   O   P   S   T   U  

Footnotes

(1)

[訳注] Quicklispがインストールされている場合、手動でソフトウェアを置く場所としてはql:*local-project-directories*(~/quicklisp/local-projects/など)も使えます。同名のシステムがある場合、ローカルプロジェクトのものがQuicklisp配布のバージョンに優先します。Quicklispはこれらのパスを(少なくとも、(ql:register-local-projects)を実行すれば)ASDFからも使えるように自動設定しているはずです。

また、すべてをRoswellで管理するという方法もあるでしょう。Roswellは、処理系マネージャとしての機能を中心にした高機能なコマンドラインツールです。Roswellから処理系を起動している場合は~/.roswell/local-projects/が同じように使えます。

clbuild(とその後継のclbuild2)は2018年現在ではメンテナンスされておらず、やや古いツールと思われます。

(2)

(require "ASDF")(require 'asdf)(require :asdf)などもGNU CLISP以外では有効ですが、ポータビリティのためには(require "asdf")を使うべきです。

(3)

[訳注] ~/common-lisp/などのディレクトリに移動してgit clone https://gitlab.common-lisp.net/asdf/asdf.gitを実行します。

(4)

~/common-lisp/がデフォルトのパスに含まれるのはASDF 3.1.2以降です。それ以前のASDFを使っている場合は、後述のように、このパスを使うことを明示的に設定する必要があるかもしれません。

[訳注] また、Windowsの場合、後者は%LOCALAPPDATA%/common-lisp/source/または%APPDATA%/Local/common-lisp/source/に相当することが多いでしょう。詳細についてはASDFがシステムを探す場所を設定するを参照するべきですが、特に必要でなければ~/common-lisp/を使うことを推奨します。

(5)

Windowsの場合、ASDF 3.1.5以降では、%LOCALAPPDATA%以下にconfig/common-lisp/source-registry.conf.d/ディレクトリを作りましょう。%LOCALAPPDATA%は通常なら~/AppData/Local/を指していますが、実際の値はCMD.EXE上でecho %LOCALAPPDATA%を実行することで確かめることができます。

(6)

拡張子が.confでないファイルは無視されるので、これを利用して特定の設定ファイルを無効にすることができます。例えば、エディタのバックアップなどのファイルは同じディレクトリにあっても読み込まれないでしょう。

また、ASDFは.で始まるファイル名も無視します。

慣習では、これらの設定ファイル名は2つの数字から始まります。こうすることで設定ディレクトリ内の.confファイルの並びが決まり、設定が読み込まれる順番をコントロールすることができます。

(7)

.asdファイルの探索をさらにカスタマイズすることも可能です。これは発展的な使い方になるので、後に扱います。*system-definition-search-functions*で検索してください。

(8)

[訳注] 実装を見る限りでは、evalの返す値がuiop:directory-pathname-pを満たせば良いようです(満たさない場合はエラーが通知される)。

(9)

WindowsではPOSIXのシンボリックリンクの代わりにショートカットを使うことができます。MacOSのエイリアスについては、うまく動くかどうかの情報を求めています。

(10)

[訳注] asdf:clear-source-registryasdf:clear-output-translationsが実行されます。

(11)

[訳注] ASDF以前に使われていたシステム定義ツール。

(12)

:force-not (already-loaded-systems)を指定すると実現できます。

(13)

[訳注] 例を挙げて説明すると、システムlibraryとシステムapplicationがあってapplicationlibraryに依存しているとき、libraryのコードを変更して(asdf:load-system :application)を実行すると、libraryapplicationの順で必要な再コンパイル、ロードが行われます。

(14)

ASDF 1、2(バージョン2.26まで)は、各.asdファイルに対してasdfNという一時的なパッケージを作っては削除していました。これは名前の衝突を減らすための仕様でしたが、目的にかなってもいなかったし、ものごとを複雑にしただけでした。ASDF 3は単に共通のパッケージであるasdf-userを使い、シンボル衝突の回避はCommon Lispの慣習に委ねます。asdf-userパッケージの特筆すべき点は、uiop/common-lispuseしていることです。このパッケージはcommon-lispパッケージのすべてのシンボルを再エクスポートしていて、いくつかのCommon Lisp処理系のANSI CL違反の挙動が修正されています。しかし、Corman Lisp、GCL、Genera、MCLを使っているのでなければ、この点を気にするべきではありません。

(15)

[訳注] 現在のバージョンでは、キーワード指定するとasdf/interfaceパッケージからクラスが探されます。別のパッケージのクラスを使いたい場合は"package:my-new-system-subclass"のように文字列で指定しましょう。

(16)

[訳注] "foo/bar.lisp"#p"foo/bar.lisp.lisp"と解釈されるので、拡張子を明示して書くことはできません。

(17)

[訳注] ここはわかりにくいですが、文字列中では".."がUnixのパスと同じように使えるということを知っていれば十分です。ANSI CLのパス名オブジェクトは6つのコンポーネントを含みますが、その内の1つであるディレクトリコンポーネント中に:backが含まれているのと同じ意味になるということです。CLHS: 19.2.2.4.3 Restrictions on Examining a Pathname Directory Componentも参照。

(18)

[訳注] CLのパス名(リテラル#p...あるいは#.(make-pathname ...)による)を与えるか、文字列でUnixスタイルの相対パスを与えるかの2択であり、絶対パスを指定したい場合は(稀と思われますが)前者のパス名しか使えないということです。相対パスは.asdファイルのあるディレクトリを基点に解釈されるので、:pathnameオプションでシステムのトップレベルのディレクトリを変更していても影響しないという点にも注意しましょう。

なお、具体的には(uiop:subpathname (asdf:system-source-file <システム名>) <パス名または文字列>)の返すパス名が使われています。

(19)

[訳注] component-pathnameでアクセスできるパス名です。

(20)

[訳注] ただしこれは、load-systemで読み込むのとloadで読み込むのが同等、という意味ではありません。ASDFがシステム定義を.asdファイルから読み込むときはカレントパッケージが:asdf-userになるなど、loadによる実行と比べるといろいろな違いがあります。

(21)

[訳注] システムfooのディレクトリ構成として、システム定義ファイルをfoo/foo.asdに置き、ソースファイルをすべてfoo/src/以下に置く方式を選んだとしましょう。この時、foo/src/baz.lispに対応するシステムはデフォルトではfoo/src/bazですが、:pathname "src"でトップレベルのパスをfoo/src/に変えるとfoo/bazにできます。:pathnameオプションについてはdefsystemの文法を参照してください。

(22)

古いASDFでは、計画を作る関数はtraverseであり、実行すべきアクションのリストを返していました。しかし、ASDF 3でplanオブジェクトが導入され、make-planがこのオブジェクトを返す関数として代わりに使われるようになったため、traverseは廃止予定です。traverseは後方互換性とデバッグのためだけに残されており、近い将来には削除されるかもしれません。

(23)

アクション(action)という用語は、Kent Pitmanの論文 “The Description of Large Systems”(→ 参考文献)で使われていて、これはUnixのmakeに端を発するのではないかと我々は考えています。この用語がASDFハッカーたちに使われるようになったのはASDF 2からですが、それ以前も単に明示されてはいなかっただけで、概念としてはASDF 1の当初からありました。

(24)

[訳注] 降順。例えば(defsystem "foo" :components ((:module "src" ...)))srcモジュールはリスト("foo" "src")で指定できます。

(25)

[訳注] traverseはオペレーション指示子とコンポーネント指示子を引数に取り、実行されるアクション(オペレーションとコンポーネントの対)のリストを順序付きで返します。ただし、廃止予定

(26)

[訳注] この場合、:build-pathnameの指定があれば出力先はそのパス名になります。指定がない場合はcomponent-pathnameの返すディレクトリが使われるようですが、公式の仕様かどうかは明示されていません。

(27)

バージョン2.27~3.0.3では、uiopの代わりにuiop/packageのみがuseされていました。バージョン3.1.2以前に対応したコードを書くためには、uiopuseしたパッケージを自ら定義するか、あるいはuiop:というパッケージ接頭辞をいちいち付ける必要があります。

(28)

[訳注] 例えば冒頭に(cl:in-package :asdf-user)を追加する、など。

(29)

[訳注] clear-source-registryでキャッシュのリセットができます。

(30)

ASDF 2.26以前はプライマリネームの機能をサポートしていませんでした。従って、それらのバージョンでは、システムfoo/barfoo.asdに定義されている場合は、(asdf:find-system "foo")を実行するなどして、事前にそのファイルを明示的にロードしなければなりません。我々はASDF 2をサポートしていませんので、ASDF 3にアップグレードすることを推奨します。

(31)

[訳注] ASDFでは、:depends-onでつながっているコンポーネントをsibling(兄弟姉妹)と呼びます。

(32)

[訳注] defsystem:depends-onオプションで使われる構文です。(→ defsystemの文法

(33)

[訳注] このリストの3番目以降の処理は、asdf:*default-source-registries*中の7つの関数に順に対応しています。リストの最後の項目の意図は判然としませんが、処理系などが独自にこの変数を編集していると、処理が加わったり(あるいは減ったり)するでしょう。Mezzanoなどにその例があります。

(34)

[訳注] 継承した設定(inherited configuration)とは、ソースレジストリの一連の設定において、で処理される設定を指します。つまり、大ざっぱに言えば、箇条書きの下の処理に相当します。「継承」の語感から逆に考える方もいるかもしれないので、注意しておきます。(append 新しい設定 (次の処理))のように再帰のイメージを持つと間違えにくいかもしれません。:ignore-inherited-configurationの指定があると、ソースレジストリの設定がそこで終わることになります。

(35)

[訳注] asdf::default-user-source-registryasdf::default-system-source-registryによる設定が挿入されます。ソースレジストリの設定のリスト中の6番目と9番目の処理に相当します。

(36)

[訳注] asdf:*user-cache*のパス名が使われます。主にアウトプットトランスレーションに関係します。アウトプットトランスレーションを設定するDSLを参照。

(37)

[訳注] clbuildは2018年現在、メンテナンスされていません。

(38)

[訳注] こちらの例ではユーザー名が明示されていますが、もちろん(:tree (:home "cl"))という記述も有効です。

(39)

[訳注] 文字列の末尾を:(または;)にすると、最後に空のエントリを追加したことになります。

(40)

[訳注] 具体的には(uiop:normalize-pathname-directory-component (pathname-directory パス名))です。uiop:normalize-pathname-directory-componentは、処理系によって異なるディレクトリコンポーネントのフォーマットをCLHS標準のリストに直す関数です。

(41)

[訳注] ASDFの競合だったビルドシステムです。今は使われていません。

(42)

[訳注] 現在の実装もそうなっています。

(43)

[訳注] .cl-source-registry.cacheの読み込みはignore-errorsで行われるため、キャッシュファイルが正しい構文で書かれていない場合、単に無視されるだけでエラーは通知されないことに注意してください。

(44)

“FASL”は“FASt Loading”の略です。

(45)

[訳注] このリストの3番目以降の処理は、asdf/output-translations::*default-output-translations*中の5つの関数に順に対応しています。ソースレジストリの場合と違ってこのシンボルはエクスポートされていないため、処理系などが独自にこの変数を編集する可能性は考えられていないのでしょう。

(46)

[訳注] 順序としては、このディレクトリの設定は上のリストの1番目の処理で追加されます。無効にする方法についてはアウトプットトランスレーションを設定するDSLを参照してください。

(47)

部分的には、clean-opがこの解決策になるかもしれません。

(48)

[訳注] 継承した設定(inherited configuration)とは、アウトプットトランスレーションの一連の設定において、で処理される設定を指します。:ignore-inherited-configurationの指定があると、アウトプットトランスレーションの設定がそこで終わることになります。

(49)

[訳注] 入力指示子の:root(:root :**/ :*.*.*)の略であり、すべてのホスト、デバイスのすべてのファイルにマッチします。このマッピングが適用されると、例えば#P"C:/Users/foo/common-lisp/bar.lisp"#P"C:/Users/foo/common-lisp/sbcl-1.4.2-win-x64/bar.lisp"などに変換されます。

(50)

[訳注] asdf:*user-cache*はデフォルトでは(uiop:xdg-cache-home "common-lisp" :implementation)にセットされます。(→ XDGベースディレクトリ

(51)

[訳注] clbuildは2018年現在、メンテナンスされていません。

(52)

Alternatively, you could touch foo.asd or remove the corresponding fasls from the output file cache.

(53)

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.