A system is a collection of Lisp files that together constitute an application or a library, and that should therefore be managed as a whole. A system definition describes which source files make up the system, what the dependencies among them are, and the order they should be compiled and loaded in. You can think of a system definition as an enhanced version of a Unix makefile, one that uses Lisp objects to define the systems instead of text files. Unfortunately, system definition is one of the areas that the CL ANSI standard does not address, and therefore almost every lisp version has its own version of a defsystem tool; they all have approximately the same functionality, but with slightly different syntax.
This example shows how to use the defsystem facility available in Allegro Common Lisp 6.x. Suppose you are working on an HTML generation library, that is composed of three files: "top.cl", that defines a package for this library, "html.cl" that implements the basic HTML-generating operations, and "library.cl" that defines high-level functions and macros to generate HTML pages. A system definition for your library might look like this:
(defsystem :html (:pretty-name "HTML generation library" :default-pathname #P"/code/lisp/html/") (:serial "top" "html" "library"))
The above form defines a system whose name is the symbol
:html. After the system name comes a list
containing optional information: in this case, we specified a
human-readable name for the package, and the directory where the
source files are located. Finally, the three files that compose
the system are listed, with the
indicating that they should be compiled and loaded in the
specified order (possibly because each one depends on
definitions in the previous ones).
Having defined a system like the above, you can then operate on it. For example, calling:
(load-system :html :compile t)will cause the three files in the system to be compiled (if needed) and loaded.
Note that a system can include other systems as components, and
the included systems will be recursively compiled and
loaded. For example, if we wanted the definitions in the
:util system to be available to the
:html system, we would change the above definition
(defsystem :html (:pretty-name "HTML generation library" :default-pathname #P"/code/lisp/html/") (:serial :util "top" "html" "library"))
The most advanced attempt at building a cross-platform system definition tool is probably MK-DEFSYSTEM, available from CLOCC. Please refer to that site for more details on how to install it and use it.
Organizing a large Lisp system is not trivial: managing the
interdependencies among a large number of source files and the
interactions between multiple packages can easily turn into a
complex undertaking that requires careful planning. Combine this
with the fact that ANSI CL provides an alternative, although much
more limited, way of managing code (
and the whole issue can quickly become a nightmare for
beginners. So here is a suggestion on a possible way to organize
your code, one that works best for small, self-contained packages
with a well-defined API, and that provide functions that might be
useful to other parts of a larger program. Just follow these three
(eval-when (:load-toplevel :execute) (shadow 'cl:require)) (defun require (system) (excl:load-system system :compile t) ; *** ACL specific (unless (member system *modules*) (push system *modules*) (when (find-package system) (use-package system))))The
LOAD-SYSTEMcall is specific to ACL's defsystem, so you should replace it with the equivalent call for the defsystem you are using.
:htmlsystem defined above as an example:
(defpackage :html (:use :lisp) (:export "ANCHOR" "HEADER" ...))
:htmlat the beginning of this chapter).
Now you can simply call
* (require :html)and the system will be compiled (if needed) and loaded; moreover, all of its external symbols will be imported into the current package. If you later modify your code, you just have to call
REQUIREagain, and the system will be recompiled and reloaded. This behaves approximately like the
usecommand in Perl or the
importstatement in Java: it allows you to easily access the functionality provided by a Lisp package with a single command.