相比于 Python 项目简单清晰的目录结构,Common Lisp 的项目结构要复杂不少,尤其是大型项目。但当你知道每个文件/目录的作用时,会发现这目录结构也不复杂。

Postmodern 项目为例,看看实际项目是怎样组织代码的。

该项目目录结构如下:

├── cl-postgres/...
├── cl-postgres.asd
├── postmodern/
│   ├── connect.lisp
│   ├── ...
│   ├── package.lisp
│   └── tests
│       ├── ...
│       ├── test-package.lisp
│       └── tests.lisp
├── postmodern.asd
├── simple-date/...
├── simple-date.asd
├── s-sql/...
├── s-sql.asd
└── ...

这里省略了部分目录和文件,但不影响我们解读该目录结构。可以点击链接了解完整的目录结构。

首先看最外层的文件,这里列出的都是以 .asd 为后缀的文件,这些文件描述了源代码间的依赖关系,使它们能按正确的顺序进行编译和加载。而这依靠的便是 ASDF 自动编译系统。

ASDF,全称 Another System Definition Facility,该构建系统指定了 Common Lisp 程序中各系统的组成及控制各组件能按正确的顺序进行编译、加载和测试等等。

ASDF, or Another System Definition Facility, is a build system: a tool for specifying how systems of Common Lisp software are made up of components (sub-systems and files), and how to operate on these components in the right order so that they can be compiled, loaded, tested, etc.

这里不讨论 ASDF 的具体用法。直接看看 postmodern.asd 文件的内容:

;;;; -*- Mode: LISP; Syntax: Ansi-Common-Lisp; Base: 10; -*-

(defpackage :postmodern-system
  (:use :common-lisp :asdf)
  (:export :*threads*))
(in-package :postmodern-system)

...

(defsystem "postmodern"
  ...
  :components
  ((:module "postmodern"
            ...
  )))

(defsystem "postmodern/tests"
  ...
  )

第一行的作用是让 Emacs 为该文件添加正确的语法支持。

代码首先定义了一个名为 postmodern-system 的包(package),之后 defsystem 定义的系统(system)描述了对应项目的代码结构。

defsystem 宏的两个主要的选项中,

  • :depends-on 指定该项目依赖的其他 ASDF 格式的项目,
  • :components 定义了有依赖关系的源代码文件。

要注意的是,系统定义文件(system definition file)名与系统名(由 defsystem 定义)必须保持一致。如要同时定义其他系统,系统名需要使用前缀 <system-name>/

在该项目中,postmodern.asd 文件对应的系统是 postmodern,额外的系统 postmodern/tests 使用的前缀是 postmodern/

看下 postmodern/ 目录中 package.lisp 的内容,可以发现该文件专门定义包,并且这个包存放的是实际的逻辑代码。

现在再看其他的文件或目录,代码结构都是类似的,理解起来并不难。

至此,我们已经了解了 Common Lisp 项目的代码结构是怎样组织的。相比 Python 来说,多了一些东西,但学习之后,并没有什么难以理解的地方。

想要了解 ASDF 的具体的用法,可以查阅 ASDF Manual