Writing Libraries
Ecosystem Overview
In Common Lisp, the build system and the package manager are two separate things.
- ASDF
- ASDF is the build system. It lets you define projects – called systems – along with their metadata, dependencies, their source code files, and the order in which those files are loaded.
- Quicklisp
- Quicklisp is the package manager. It uses ASDF to extract package’s dependencies and downloads them from a central repository. Unlike most package managers, Quicklisp isn’t a command-line application: you run it from the REPL like every other Lisp tool, so you can get up and running very easily.
Defining Systems
A typical system definition looks like this (from this project):
(defsystem common-doc
:author "Fernando Borretti <eudoxiahp@gmail.com>"
:maintainer "Fernando Borretti <eudoxiahp@gmail.com>"
:license "MIT"
:version "0.2"
:homepage "https://github.com/CommonDoc/common-doc"
:bug-tracker "https://github.com/CommonDoc/common-doc/issues"
:source-control (:git "git@github.com:CommonDoc/common-doc.git")
:description "A framework for representing and manipulating documents as CLOS objects."
:depends-on (:trivial-types
:local-time
:quri
:anaphora
:alexandria
:closer-mop)
:components ((:module "src"
:serial t
:components
((:file "packages")
(:file "define")
(:file "error")
(:file "file")
(:file "classes")
(:file "metadata")
(:file "constructors")
(:file "macros")
(:file "format")
(:file "util")
(:module "operations"
:serial t
:components
((:file "traverse")
(:file "figures")
(:file "tables")
(:file "links")
(:file "text")
(:file "unique-ref")
(:file "toc")
(:file "equality")))
(:file "print"))))
:long-description
#.(uiop:read-file-string
(uiop:subpathname *load-pathname* "README.md"))
:in-order-to ((test-op (test-op common-doc-test))))
Breaking it down, the first line defines the name of the system, common-doc
.
Then, we have the metadata:
:author "Fernando Borretti <eudoxiahp@gmail.com>"
:maintainer "Fernando Borretti <eudoxiahp@gmail.com>"
:license "MIT"
:version "0.2"
:homepage "https://github.com/CommonDoc/common-doc"
:bug-tracker "https://github.com/CommonDoc/common-doc/issues"
:source-control (:git "git@github.com:CommonDoc/common-doc.git")
:description "A framework for representing and manipulating documents as CLOS objects."
ASDF allows us to supply author contact information (name and email address), license information, the version string, some useful links, and a one-line description.
Then, we have the list of dependencies:
:depends-on (:trivial-types
:local-time
:quri
:anaphora
:alexandria
:closer-mop)
This is simply a list of systems this system depends on. You can specify versions here, but typically version management is done externally – see below.
This is followed by the components – basically a description of the source tree. Modules are directories, and files are Lisp files.
:components ((:module "src"
:serial t
:components
((:file "packages")
(:file "define")
(:file "error")
(:file "file")
(:file "classes")
(:file "metadata")
(:file "constructors")
(:file "macros")
(:file "format")
(:file "util")
(:module "operations"
:serial t
:components
((:file "traverse")
(:file "figures")
(:file "tables")
(:file "links")
(:file "text")
(:file "unique-ref")
(:file "toc")
(:file "equality")))
(:file "print"))))
The :serial t
option basically says “the files in this directory should be
loaded in the order they appear here. ASDF also allows us to be more
sophisticated, and manually specify which files depend on which, and just let
ASDF figure out a total order in which to load them. But for most cases, just
using :serial t
is good enough.
Then, we use the :long-description
option and read-time execution to embed the
README.md
file into the system definition:
:long-description
#.(uiop:read-file-string
(uiop:subpathname *load-pathname* "README.md"))
This option is used by tools like [Quickdocs][qd] to display information about a system.
Finally, we tell ASDF about the associated test system – this is the system that loads a testing framework and runs the separate tests. We’ll cover that in the guide about unit testing.
:in-order-to ((test-op (test-op common-doc-test)))
Configuring ASDF
Before you can load a system, you have to tell ASDF where to find them. This is
specified in the ~/.config/common-lisp/source-registry.conf
file.
For instance, the following configuration:
(:source-registry
(:tree (:home "code"))
:inherit-configuration)
Tells ASDF to find your system be searching recursively through the ~/code/
directory, and to inherit the configuration from Quicklisp so you can load
Quicklisp’s systems.
Loading
After you have configured ASDF and created your system, you can load it using either Quicklisp or ASDF:
CL-USER> (asdf:load-system :my-system)
CL-USER> (ql:quickload :my-system)