ASDF 系统定义保存在.asd后缀的文件中,为了表述清楚,我们假设我们为Cow工程来写一个系统定义文件。
;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
这将保证开启正确的语法支持,它是必须的,因为默认情况下emacs不知道任何.asd文件。
之后,cow.asd 可能书写如下:
(defpackage #:cow-asd
(:use :cl :asdf))
(in-package :cow-asd)
下面是defsystem定义形式,和一些可选的属性。
(defsystem cow :name "cow" :version "0.0.0" :maintainer "T. God" :author "Desmon Table" :licence "BSD sans advertising clause (see file COPYING for details)" :description "Cow" :long-description "Lisp implementation of our favorite ruminant"
system-definition := ( defsystem system-designator system-option* ) system-option := :defsystem-depends-on system-list | :weakly-depends-on system-list | :class class-name (see discussion below) | module-option | option module-option := :components component-list | :serial [ t | nil ] | :if-component-dep-fails component-dep-fail-option option := | :pathname pathname-specifier | :default-component-class class-name | :perform method-form | :explain method-form | :output-files method-form | :operation-done-p method-form | :depends-on ( dependency-def* ) | :in-order-to ( dependency+ ) system-list := ( simple-component-name* ) component-list := ( component-def* ) component-def := ( component-type simple-component-name option* ) component-type := :system | :module | :file | :static-file | other-component-type other-component-type := symbol-by-name (see Component types) dependency-def := simple-component-name | ( :feature name ) | ( :version simple-component-name version-specifier) dependency := (dependent-op requirement+) requirement := (required-op required-component+) | (feature feature-name) dependent-op := operation-name required-op := operation-name | feature simple-component-name := string | symbol pathname-specifier := pathname | string | symbol method-form := (operation-name qual lambda-list &rest body) qual := method qualifier component-dep-fail-option := :fail | :try-next | :ignore
(defsystem cow
;;; (Optional items omitted) :serial t ;; the dependencies are linear. :components ((:file "defpackage") (:file "legs") (:file "tail") (:file "head")))
(defsystem cow
;;; (Optional items omitted) :components ((:file "tail" :depends-on ("package" "legs")) (:file "legs" :depends-on ("package")) (:file "head" :depends-on ("package")) (:file "package")))
In this case you would keep the file cow.asd
in the same directory as the source files. If you want to try out our nice cow
system, you can jump directly to the pertinent section.
(defsystem cow :components ((:file "head" :depends-on ("package")) (:file "tail" :depends-on ("package" circulation)) (:file "package") (:module circulation :components ((:file "water" :depends-on "package") (:file "assorted-solids" :depends-on "package") (:file "package"))) (:module respiratory :pathname "breathing" :components (...))))
circulation
模块中的package.lisp是高层模块的。
(defsystem cow ;;; ... :components (...) :depends-on ("other-system"))
(setf asdf:*central-registry* ;; Default directories, usually just the ``current directory'' '(*default-pathname-defaults* ;; Additional places where ASDF can find ;; system definition files #p"/home/foo/lisp/systems/" #p"/usr/share/common-lisp/systems/"))
构建和加载系统cow的命令如下:
(asdf:operate 'asdf:load-op 'cow)
如果cow.asd碰巧在你当前的工作目录,那么构建和加载过程将会从这个开始。如果没有的话,ASDF将会搜索cetral registry的目录,寻找cow.asd的系统定义文件或者软链接。如果找到的软连接的话,它将跟随它会找到原文件,运行构建过程在相应的目录。如果找到文件的话,它将在此目录运行构建过程。
所以,如果你做了一个符号链接/home/foo/lisp/system到cow的系统定义文件,例如:
$ cd <where-your-system-defs-are>
$ ln -s /home/foo/code/cow/cow.asd
(asdf:operate 'asdf:load-op 'cow)
一些人,使用未进入符号(#:语法),而不是关键字符号,来定义包。如下:
(:use #:common-lisp))
Mario S. Mommer <[email protected]>
Last modified: April 05, 2006.
The aim of this document is to give a brief introduction to the use of ASDF
, Another System Definition Facility. It is not about the arcane tricks and tips, and is not about the design of ASDF
itself nor about system definition tools in general.
A system definition file is nothing else than a description of dependencies between files of source code in such a way that they can be compiled and loaded in the right order. A file A.lisp
depends on a file B.lisp
if the latter contains definitions and/or code that is used by A.lisp
1.
ASDF
. We leave the more advanced features of this tool to the
ASDF
manual
2.
ASDF
solve?When you download the source code of some software from the Internet, or get it from some other source, you usually do not get an amorphous bunch of files, but instead get a system of components that depend on each other in some particular way. The consequence of this is that, if you want to build the software (be it a library, or be it an application), you will probably have to build these components, and the components of these components in order, perhaps giving some special treatment to some of them. You would, of course, be very grateful if the developer had prepared everything, and you could trigger the build process by a single command.
If you are a developer working in a project with a few components, you will probably want some mechanism that keeps track of the dependencies between these components, so that if you change one component, triggering a rebuild only recompiles and reloads the components which are affected.
Finally, you probably want a consistent way of dealing with the dependencies between components and of building and loading software systems, simply because it saves everyone time when installing software and using software.
ASDF
is, roughly speaking, an extensible facility for defining the dependencies between software components, and specifying eventual details of the build process. It is also in fairly wide use, so that you can assume that your system definition will be understood by many others.
mk:defsystem
, which has fans as well as detractors. We will only concentrate here on
ASDF
, since we have to start somewhere. In any case, the actual differences between these two only become apparent for the power user.
This work is licensed under a Creative Commons Attribution 2.5 License.
ASDF
ASDF
system definition is stored in a file with the extension
.asd
. To make the discussion clearer, we are going to pretend that we want to write the system definition facility for our software project, called, unassumingly,
cow
.
The system definition file should be called cow.asd
, and, at least in the usual scenarios, should reside in the same directory as your source code. If you use emacs, you might want to put the following as the first line in cow.asd
.
;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
This makes sure that the proper syntax support is turned on, which is necessary because by default emacs knows nothing about .asd
files.
After that, cow.asd
should start with the following code (preceded or followed perhaps by comments; goes almost without saying)
(defpackage #:cow-asd (:use :cl :asdf)) (in-package :cow-asd)
The next thing is to write a defsystem
form, together with some (optional) extra information.
(defsystem cow :name "cow" :version "0.0.0" :maintainer "T. God" :author "Desmon Table" :licence "BSD sans advertising clause (see file COPYING for details)" :description "Cow" :long-description "Lisp implementation of our favorite ruminant"
system-definition := ( defsystem system-designator system-option* ) system-option := :defsystem-depends-on system-list | :weakly-depends-on system-list | :class class-name (see discussion below) | module-option | option module-option := :components component-list | :serial [ t | nil ] | :if-component-dep-fails component-dep-fail-option option := | :pathname pathname-specifier | :default-component-class class-name | :perform method-form | :explain method-form | :output-files method-form | :operation-done-p method-form | :depends-on ( dependency-def* ) | :in-order-to ( dependency+ ) system-list := ( simple-component-name* ) component-list := ( component-def* ) component-def := ( component-type simple-component-name option* ) component-type := :system | :module | :file | :static-file | other-component-type other-component-type := symbol-by-name (see Component types) dependency-def := simple-component-name | ( :feature name ) | ( :version simple-component-name version-specifier) dependency := (dependent-op requirement+) requirement := (required-op required-component+) | (feature feature-name) dependent-op := operation-name required-op := operation-name | feature simple-component-name := string | symbol pathname-specifier := pathname | string | symbol method-form := (operation-name qual lambda-list &rest body) qual := method qualifier component-dep-fail-option := :fail | :try-next | :ignore
In the simplest case you have a directory with a few files. And the absolutely simplest case is when the dependency of your files is linear. That is, you can make a list of the files such that the first one should be loaded first, the second second, etc.
In such a case, the defsystem
form can look like this.
(defsystem cow ;;; (Optional items omitted) :serial t ;; the dependencies are linear. :components ((:file "defpackage") (:file "legs") (:file "tail") (:file "head")))
Suppose that we have the same files as before, but would like to specify the dependencies more accurately. There is a file called defpackage.lisp
, from which everything else depends. And we have that, for some mysterious reason, tail.lisp
depends on legs.lisp
. The defsystem
form for this project could look as follows.
(defsystem cow ;;; (Optional items omitted) :components ((:file "tail" :depends-on ("package" "legs")) (:file "legs" :depends-on ("package")) (:file "head" :depends-on ("package")) (:file "package")))
In this case you would keep the file cow.asd
in the same directory as the source files. If you want to try out our nice cow
system, you can jump directly to the pertinent section.
Suppose that we have the following, more involved structure. We have a head.lisp
, and legs.lisp
. But we also have a subsystem called respiratory
, which consists of a few more files, and lives in its own subdirectory called breathing
(this is a subdirectory of the directory where cow.asd
lives). Similarly, we have another subsystem called circulation
. The defsystem form
could look more or less like this.
(defsystem cow :components ((:file "head" :depends-on ("package")) (:file "tail" :depends-on ("package" circulation)) (:file "package") (:module circulation :components ((:file "water" :depends-on "package") (:file "assorted-solids" :depends-on "package") (:file "package"))) (:module respiratory :pathname "breathing" :components (...))))
Note that the files in the module circulation
all live in the subdirectory circulation
. Thus the file package.lisp
in the module circulation
is a different one than that a level higher.
A module can have as components both files and other modules, which in turn can have files and modules as components. It is important to note that dependencies can only be defined inside a given set of components. So, the file tail.lisp
cannot depend on the file assorted-solids
, which is a component of a submodule.
A system that depends on other systems will look exactly like a regular one, except for an additional :depends-on
option. It looks like this.
(defsystem cow ;;; ... :components (...) :depends-on ("other-system"))
System definition files live in the directory where the corresponding piece of software lives. However, you do not need to have that directory as your working directory to be able to build and load said software. You only need to put a symbolic link to the system definition file in a directory where ASDF
searches.
The usual setup is as follows. To begin with, you need to have ASDF
loaded. In some implementations ASDF
is already loadad. In others, it is just a matter of writing
(require 'asdf)whereas in others you might need to install it yourself first. You can get it, in a single file, from here, and put it somewhere reasonable. For instance, if your login name was
foo
, in the directory
/home/foo/lisp/utils/
. You may also want to compile that file.
Now, somewhere in the init file of your Common Lisp implementation (for CMUCL it would be .cmucl-init.lisp
in your home directory) a variation of the following passage should appear.
(load "/home/foo/lisp/utils/asdf") (setf asdf:*central-registry* ;; Default directories, usually just the ``current directory'' '(*default-pathname-defaults* ;; Additional places where ASDF can find ;; system definition files #p"/home/foo/lisp/systems/" #p"/usr/share/common-lisp/systems/"))
The command to build and load system cow is
(asdf:operate 'asdf:load-op 'cow)
If the file cow.asd
happens to be in the current working directory, the build and load process will start there. If not, ASDF will search through the directories in the central registry, and look for a system definition file named cow.asd
, or for a symbolic link to one. If it finds the latter, it will follow the link to the original file, and run the build process in the corresponding directory. If it finds the file, it will run the build process in the directory where it finds it.
So, if you make a symbolic link in /home/foo/lisp/systems/
to the cow
system definition file, by executing (for example)
$ cd <where-your-system-defs-are> $ ln -s /home/foo/code/cow/cow.asd
Then you can build and load the cow software without having to be in the directory where this software lives simply by issuing the command
(asdf:operate 'asdf:load-op 'cow)
#:
syntax.
(defpackage #:com.gigamonkeys.email-db (:use #:common-lisp))
DEFPACKAGE
(or the code it expands into) is done with it. However, the difference is so slight that it really boils down to a matter of aesthetics.