在这里,我们将一个 Lisp 应用或库的完整项目文件的集合称为系统,因为项目文件需要当成一个整体。系统定义将指定哪些源文件是用来构建系统的、源文件的的依赖以及以及这些依赖文件编译和加载的顺序。
ASDF
ASDF 是 Common Lisp 的标准构建系统。可以在绝大部分解释器上使用。ASDF 包含了 UIOP, “the Utilities for Implementation- and OS- Portability”。详细知识参考 ASDF 手册 或是 ASDF 教程和最佳实践.
简单示例
加载项目
ASDF最简单的用法就是用(asdf:make :foobar)
(或是 load-system
)来加载库。比如说,在 foobar
这个库中有个 some-fun
的外部函数,那么调用 some-fun
函数就可以这么写 (foobar:some-fun ...)
或是这样:
(in-package :foobar)
(some-fun ...)
当然,也可以用 Quicklisp:
(ql:quickload :foobar)
测试项目
当需要测试一个系统时,可以这样:
(asdf:test-system :foobar)
通常来说如果测试不通过的话是会抛出异常的。
指定项目
在程序中指定系统的正确方法是使用小写字符串,而不是符号,如下:
(asdf:make "foobar")
(asdf:test-system "foobar")
简单的项目定义
通常一个项目中会包含个单个的 Lisp 文件如 foobar.lisp
,这个文件会依赖一些现有的库,比如通用库 alexandria
和模式匹配库 trivia
。为了让项目能用 ASDF 构建,还需要在项目中创建一个 .asd
后缀的文件,如foobar.asd
。
(defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar")))
注意,上面中的 foobar.lisp
文件类型默认是 lisp
,文件的内容大致如下:
(defpackage :foobar
(:use :common-lisp :alexandria :trivia)
(:export
#:some-function
#:another-function
#:call-with-foobar
#:with-foobar))
(in-package :foobar)
(defun some-function (...)
...)
...
与其使用多个完整的包,有时只需要导入其中的一部分:
(defpackage :foobar
(:use #:common-lisp)
(:import-from #:alexandria
#:some-function
#:another-function))
(:import-from #:trivia
#:some-function
#:another-function))
...)
自定义的项目
假设项目是在 ~/common-lisp/
、~/quicklisp/local-projects/
或者是已经在 ASDF 中配置了的路径,就可以直接使用 (asdf:make "foobar")
将项目加载进来。如果已经在创建文件之前启动了 Lisp,就需要重新加载下 ASDF 的配置了:(asdf:clear-configuration)
简单的测试定义
即便是最简单的项目,也需要一些测试单元,因为项目的修改是不可避免的,同时有希望这些修改不会破坏现有的代码。测试同时也是记录预期行为的好方法。
创建测试单元最简单的方法就是创建一个 foobar-tests.lisp
文件,然后在修改下 foobar.asd
文件:
(defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar"))
:in-order-to ((test-op (test-op "foobar/tests"))))
(defsystem "foobar/tests"
:depends-on ("foobar" "fiveam")
:components ((:file "foobar-tests"))
:perform (test-op (o c) (symbol-call :fiveam '#:run! :foobar)))
在第一个 defsystem
中,:in-order-to
从句中标明了可以使用 (asdf:test-system :foobar)
,这个将链接到 foobar/tests
。第二个 defsystem
中的 :perform
是执行自己的测试语句。
在测试单元中,fiveam
这个词比较通用,perform
方法中的内容是运行 :foobar
的测试单元的。当然,具体还是要视情况而定。
创建项目框架
可以使用 cl-project 来创建一个项目框架。这个库会生成一个默认的 ASDF 的定义,一个测试单元等。
通过 quicklisp 安装:
(ql:quickload :cl-project)
创建一个项目:
(cl-project:make-project #p"lib/cl-sample/"
:author "Eitaro Fukamachi"
:email "e.arrows@gmail.com"
:license "LLGPL"
:depends-on '(:clack :cl-annot))
;-> writing /Users/fukamachi/Programs/lib/cl-sample/.gitignore
; writing /Users/fukamachi/Programs/lib/cl-sample/README.markdown
; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample-test.asd
; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample.asd
; writing /Users/fukamachi/Programs/lib/cl-sample/src/hogehoge.lisp
; writing /Users/fukamachi/Programs/lib/cl-sample/t/hogehoge.lisp
;=> T