システム (asdf)
asdf パッケージは、Common Lisp のビルド機構である ASDF の限定的な API 互換サブセットを提供します:
複数ファイルのプロジェクトを NAME.asd ファイルに
asdf:defsystem で一度だけ記述すれば、
asdf:load-system がファイルを依存順に —
すべてのバックエンドで — ロードします。本物の ASDF は移植されていません
(CLOS、コンディションシステム、パスネーム API に依存しており、いずれもここには存在しません)。
代わりに .asd ファイルはプレーンなデータとして解析され、サポートされる defsystem
サブセットが load/require と同じ機構を駆動します。サブセット内に収まる .asd
はそのまま動作します。
| オペレータ | 用途 |
|---|---|
asdf:defsystem | システムの定義: :depends-on、:serial、:components |
asdf:load-system | システムのロード (依存が先、ファイルは順序どおり、冪等) |
プロジェクトの全体像
app/
my-app.asd
package.lisp
main.lisp
run.lisp
registry/base/
base.asd
base.lisp
;; app/my-app.asd
(defsystem :my-app
:version "0.1.0"
:depends-on (:base)
:serial t
:components ((:file "package")
(:file "main")))
;; app/package.lisp
(defpackage :my-app (:use :cl) (:export :run))
;; app/main.lisp
(in-package :my-app)
(defun run () (print (base:double 21)))
;; app/run.lisp
(asdf:load-system :my-app)
(my-app:run)
エントリファイルを実行またはコンパイルします。同じディレクティブが 4 つのバックエンドすべてで動作します:
rontolisp app/run.lisp --system-path registry/base # interpret
rontolisp app/run.lisp --system-path registry/base -o Prog.class # JVM
rontolisp app/run.lisp --system-path registry/base -o app.wasm # WASM
my-app.asd は run.lisp の隣で見つかり、依存システム :base は --system-path
経由で見つかります。コンパイルパスではシステム全体 (依存が先) がコンパイル時にプログラムへ
継ぎ足されます。コンパイル時 load インクルードとまったく同じ仕組みなので、JVM と WASM
のコンパイラはすべての defun をネイティブに認識します。
システム探索パス
asdf:load-system は NAME.asd を次の順で探します:
load-systemを実行しているファイルのディレクトリ (loadと同様)、--system-pathで指定したディレクトリ (PATHのようにプラットフォームのパス区切り文字で複数連結可)、- 環境変数
RONTOLISP_SOURCE_REGISTRYのディレクトリ (同じ形式)。
依存システムの .asd は、依存する側のシステムのディレクトリから探索が始まるため、
1 つのレジストリディレクトリに並んだ兄弟システムは互いを見つけられます。
quickload でダウンロードする
手動ダウンロードを省くには、ql:quickload
を使います。システム (とその依存) を本物の
Quicklisp ディストリビューションから取得し、上記の
仕組みでそのままロードします:
$ rontolisp
> (ql:quickload "split-sequence")
(split-sequence)
> (split-sequence:split-sequence #\, "a,b,c")
("a" "b" "c")
ダウンロードは Quicklisp の dist メタデータ (依存解決の systems.txt、tarball URL の
releases.txt) に基づきます。各リリースは展開され ~/.rontolisp/quicklisp/ 以下に
キャッシュされる (RONTOLISP_QUICKLISP_HOME で変更可能) ため、2 回目以降の
quickload はネットワーク I/O を行いません。ダウンロードはインタプリタ実行時または
コンパイル時に (Java 側で) 行われ、コンパイル済みプログラムはソースを内包していて
実行時にはフェッチしないので、ql:quickload は 4 バックエンドすべてで動作します。
ロード自体は asdf サブセットを経由するため、同じ制約が当てはまります — ダウンロード
できたライブラリでも、そのソースが下記のサポート範囲に収まっている場合にのみロード
できます。
サポート範囲 (と非サポート)
.asdファイルはデータとして解析されます:defsystem(裸またはasdf:修飾)、in-package/defpackageフォーム (スキップ)、そして純粋なリテラル/条件値を持つ トップレベルdefparameter(解析時環境に評価されます) を書けます。#+/#-フィーチャ条件は動作し (ターゲットバックエンドのフィーチャーに対して評価されます。 データ型を参照)、#.リード時評価フォームはそのdefparameterに対して解決されます ((:file #.*string-file*)の慣用形)。解決できないもの — ASDF バージョンガードなど — は警告付きでスキップされます。また:depends-onのエントリには(:feature EXPR DEP)を書け、フィーチャ式が成立するときだけ依存が追加されます。defsystemはメタデータオプション (無視)、:depends-on、:serial、:file/:module/:static-fileエントリを持つ:componentsをサポートします。 コンポーネントには:if-feature exprを付けられます。フィーチャー式が成立しない 場合、そのコンポーネントのファイルは除外されますが (ライブラリが CLOS 専用 ファイルを(:or :sbcl ...)の後ろにゲートする方法)、依存順序内の位置は 維持されます。test-op 配線用のオプション:in-order-toと:performは許容され 無視されます (test-op/operateの機構はありません)。:versionの値は ASDF の(:read-file-form ...)間接参照を含む任意のリテラルフォームで構いません (検査されません)。それ以外 (:defsystem-depends-onなど) は句を名指しする エラーです。- 同じシステムの 2 回目のロードは no-op です。循環する
:depends-onは検出して報告されます。 - コンパイルパスはリテラルなトップレベルの
(asdf:load-system NAME)を要求します。 インタプリタは実行時に計算された名前も受け付けます。
実際に何がロードできるか
現在、実世界の 4 つのライブラリが無改変でロードでき、4 つ全てのバックエンド
(インタプリタ、JVM、WASM Preview 1、--component) で検証済みです:
- split-sequence v2.0.1:
split-sequence/split-sequence-if/split-sequence-if-notが文字列と リストに対して動作します — 関数境界を多値チャネル経由で越える第 2 戻り値 (再開インデックス) を含めて。CLOS 専用のextended-sequence.lispは:if-feature (:or :sbcl :abcl)でゲートされており自動的に除外されます。 - parse-number v1.8:
parse-number/parse-real-number/parse-positive-real-numberが整数、 有理数、浮動小数点数、基数プレフィクス付きリテラル (#xFF、#3r12)、 指数マーカーを扱います。(error 'invalid-number :value ... :reason ...)イディオムは簡易コンディション代替を通じて意図した診断情報付きで シグナルされます。 - cl-utilities v1.2.4:
公開 API 全体が動作します — 独自の
split-sequence、extremumファミリー (extremum/extremum-fastkey/extrema/n-most-extreme)、read-delimited、expt-mod、collecting/with-collectors、自作マクロ から使えるwith-unique-names/with-gensyms/once-only(3 段のネスト バッククォート)、rotate-byte、copy-array、compose。 - cl-who v1.1.5: Edi Weitz による
(X)HTML 生成マクロ。
with-html-output-to-string(およびwith-html-output) が S 式の HTML を、属性・ネストしたタグ・ローカルなstr/esc/fmt/htm演算子とともにレンダリングします。エスケープと 数値文字参照も動作します。マクロ展開は通常の defun 群と総称関数 (convert-tag-to-string-list) をマクロ展開時に実行します — CLOS 静的 サブセットと setf 関数定義 ((defun (setf html-mode) ...)) によりロード できます。2 つの簡易版の制限があります::indent(整形出力) は未対応 で、既定のコンパクトなレンダリングになります。また出力モードの切り替えは(setf (html-mode) :html5)を使います — cl-who はモードをマクロ展開 (コンパイル) 時に読み取るため、*html-mode*の実行時let再束縛は既に 展開済みのマクロには反映されません (スペシャル変数束縛自体は動作します)。 既定の:xmlモードと:html5はどちらも正しくレンダリングされます。
最初の 3 ライブラリの実行可能なデモ — バックエンド別の実行コマンドと期待
出力付き — は
examples/asdf/
にあります。
現時点でロードできるライブラリの目安は、おおよそ次の範囲に収まるものです:
素の defun/defmacro/defpackage コード、loop、values を末尾に持つ
関数への multiple-value-bind、サポート済みの型指定子による
check-type/etypecase、宣言 (パース済み no-op、deftype を含む)、CLOS
静的サブセット (単一ディスパッチの
defclass/defgeneric/defmethod/make-instance/slot-value、および
(defun (setf name) ...) setf 関数)、そして
簡易版 define-condition/make-condition/warn/restart-case/
return-from のイディオム、そして動的 (スペシャル) 変数束縛 (defvar の
スペシャル変数に対する let/let*)。完全なメタオブジェクトプロトコル、
コンディション/リスタートシステム、パス名の上に
構築されたライブラリはまだロードできません
(未対応のCL機能を参照)。それ以外の場合の実用は、
自分自身の複数ファイル rontolisp プロジェクトの構成です — その .asd は
本物の ASDF でも読めるものになります。