在这里,我们将一个 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 ...) 或是这样:

  1. (in-package :foobar)
  2. (some-fun ...)

当然,也可以用 Quicklisp:

  1. (ql:quickload :foobar)

测试项目

当需要测试一个系统时,可以这样:

  1. (asdf:test-system :foobar)

通常来说如果测试不通过的话是会抛出异常的。

指定项目

在程序中指定系统的正确方法是使用小写字符串,而不是符号,如下:

  1. (asdf:make "foobar")
  2. (asdf:test-system "foobar")

简单的项目定义

通常一个项目中会包含个单个的 Lisp 文件如 foobar.lisp,这个文件会依赖一些现有的库,比如通用库 alexandria 和模式匹配库 trivia。为了让项目能用 ASDF 构建,还需要在项目中创建一个 .asd 后缀的文件,如foobar.asd

  1. (defsystem "foobar"
  2. :depends-on ("alexandria" "trivia")
  3. :components ((:file "foobar")))

注意,上面中的 foobar.lisp 文件类型默认是 lisp,文件的内容大致如下:

  1. (defpackage :foobar
  2. (:use :common-lisp :alexandria :trivia)
  3. (:export
  4. #:some-function
  5. #:another-function
  6. #:call-with-foobar
  7. #:with-foobar))
  8. (in-package :foobar)
  9. (defun some-function (...)
  10. ...)
  11. ...

与其使用多个完整的包,有时只需要导入其中的一部分:

  1. (defpackage :foobar
  2. (:use #:common-lisp)
  3. (:import-from #:alexandria
  4. #:some-function
  5. #:another-function))
  6. (:import-from #:trivia
  7. #:some-function
  8. #:another-function))
  9. ...)

自定义的项目

假设项目是在 ~/common-lisp/~/quicklisp/local-projects/ 或者是已经在 ASDF 中配置了的路径,就可以直接使用 (asdf:make "foobar") 将项目加载进来。如果已经在创建文件之前启动了 Lisp,就需要重新加载下 ASDF 的配置了:(asdf:clear-configuration)

简单的测试定义

即便是最简单的项目,也需要一些测试单元,因为项目的修改是不可避免的,同时有希望这些修改不会破坏现有的代码。测试同时也是记录预期行为的好方法。

创建测试单元最简单的方法就是创建一个 foobar-tests.lisp 文件,然后在修改下 foobar.asd 文件:

  1. (defsystem "foobar"
  2. :depends-on ("alexandria" "trivia")
  3. :components ((:file "foobar"))
  4. :in-order-to ((test-op (test-op "foobar/tests"))))
  5. (defsystem "foobar/tests"
  6. :depends-on ("foobar" "fiveam")
  7. :components ((:file "foobar-tests"))
  8. :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 安装:

  1. (ql:quickload :cl-project)

创建一个项目:

  1. (cl-project:make-project #p"lib/cl-sample/"
  2. :author "Eitaro Fukamachi"
  3. :email "e.arrows@gmail.com"
  4. :license "LLGPL"
  5. :depends-on '(:clack :cl-annot))
  6. ;-> writing /Users/fukamachi/Programs/lib/cl-sample/.gitignore
  7. ; writing /Users/fukamachi/Programs/lib/cl-sample/README.markdown
  8. ; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample-test.asd
  9. ; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample.asd
  10. ; writing /Users/fukamachi/Programs/lib/cl-sample/src/hogehoge.lisp
  11. ; writing /Users/fukamachi/Programs/lib/cl-sample/t/hogehoge.lisp
  12. ;=> T