2022-08-02 12:34:59 +02:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta name = "generator" content =
"HTML Tidy for HTML5 for Linux version 5.2.0">
< title > Defining Systems< / title >
< meta charset = "utf-8" >
< meta name = "description" content = "A collection of examples of using Common Lisp" >
< meta name = "viewport" content =
"width=device-width, initial-scale=1">
2022-08-04 11:37:48 +02:00
< link rel = "icon" href =
"assets/cl-logo-blue.png"/>
2022-08-02 12:34:59 +02:00
< link rel = "stylesheet" href =
"assets/style.css">
< script type = "text/javascript" src =
"assets/highlight-lisp.js">
< / script >
< script type = "text/javascript" src =
"assets/jquery-3.2.1.min.js">
< / script >
< script type = "text/javascript" src =
"assets/jquery.toc/jquery.toc.min.js">
< / script >
< script type = "text/javascript" src =
"assets/toggle-toc.js">
< / script >
< link rel = "stylesheet" href =
"assets/github.css">
< link rel = "stylesheet" href = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity = "sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin = "anonymous" >
< / head >
< body >
< h1 id = "title-xs" > < a href = "index.html" > The Common Lisp Cookbook< / a > – Defining Systems< / h1 >
< div id = "logo-container" >
< a href = "index.html" >
< img id = "logo" src = "assets/cl-logo-blue.png" / >
< / a >
< div id = "searchform-container" >
< form onsubmit = "duckSearch()" action = "javascript:void(0)" >
< input id = "searchField" type = "text" value = "" placeholder = "Search..." >
< / form >
< / div >
< div id = "toc-container" class = "toc-close" >
< div id = "toc-title" > Table of Contents< / div >
< ul id = "toc" class = "list-unstyled" > < / ul >
< / div >
< / div >
< div id = "content-container" >
< h1 id = "title-non-xs" > < a href = "index.html" > The Common Lisp Cookbook< / a > – Defining Systems< / h1 >
<!-- Announcement we can keep for 1 month or more. I remove it and re - add it from time to time. -->
< p class = "announce" >
📹 < a href = "https://www.udemy.com/course/common-lisp-programming/?couponCode=6926D599AA-LISP4ALL" > NEW! Learn Lisp in videos and support our contributors with this 40% discount.< / a >
< / p >
< p class = "announce-neutral" >
📕 < a href = "index.html#download-in-epub" > Get the EPUB and PDF< / a >
< / p >
< div id = "content"
< p > A < strong > system< / strong > is a collection of Lisp files that together constitute an application or a library, and that should therefore be managed as a whole. A < strong > system definition< / strong > describes which source files make up the system, what the dependencies among them are, and the order they should be compiled and loaded in.< / p >
< h2 id = "asdf" > ASDF< / h2 >
< p > < a href = "https://gitlab.common-lisp.net/asdf/asdf" > ASDF< / a > is the standard build
system for Common Lisp. It is shipped in most Common Lisp
implementations. It includes
< a href = "https://gitlab.common-lisp.net/asdf/asdf/blob/master/uiop/README.md" > UIOP< / a > ,
< em > “the Utilities for Implementation- and OS- Portability”< / em > . You can read
< a href = "https://common-lisp.net/project/asdf/asdf.html" > its manual< / a > and the
< a href = "https://gitlab.common-lisp.net/asdf/asdf/blob/master/doc/best_practices.md" > tutorial and best practices< / a > .< / p >
< p > < a name = "example" > < / a > < / p >
< h2 id = "simple-examples" > Simple examples< / h2 >
< h3 id = "loading-a-system" > Loading a system< / h3 >
< p > The most trivial use of ASDF is by calling < code > (asdf:make "foobar")< / code > (or < code > load-system< / code > )
to load your library.
Then you can use it.
For instance, if it exports a function < code > some-fun< / code > in its package < code > foobar< / code > ,
then you will be able to call it with < code > (foobar:some-fun ...)< / code > or with:< / p >
< pre > < code class = "language-lisp" > (in-package :foobar)
(some-fun ...)
< / code > < / pre >
< p > You can also use Quicklisp:< / p >
< pre > < code class = "language-lisp" > (ql:quickload "foobar")
< / code > < / pre >
< p > Also, you can use SLIME to load a system, using the < code > M-x slime-load-system< / code > Emacs command.
The interesting thing about this way of doing it is that SLIME collects all the system warnings and errors in the process,
and puts them in the < code > *slime-compilation*< / code > buffer, from which you can interactively inspect them after the loading finishes.< / p >
< h3 id = "testing-a-system" > Testing a system< / h3 >
< p > To run the tests for a system, you may use:< / p >
< pre > < code class = "language-lisp" > (asdf:test-system :foobar)
< / code > < / pre >
< p > The convention is that an error SHOULD be signalled if tests are unsuccessful.< / p >
< h3 id = "designating-a-system" > Designating a system< / h3 >
< p > The proper way to designate a system in a program is with lower-case
strings, not symbols, as in:< / p >
< pre > < code class = "language-lisp" > (asdf:make "foobar")
(asdf:test-system "foobar")
< / code > < / pre >
< h3 id = "how-to-write-a-trivial-system-definition" > How to write a trivial system definition< / h3 >
< p > A trivial system would have a single Lisp file called < code > foobar.lisp< / code > , located at the project’ s root.
That file would depend on some existing libraries,
say < code > alexandria< / code > for general purpose utilities,
and < code > trivia< / code > for pattern-matching.
To make this system buildable using ASDF,
you create a system definition file called < code > foobar.asd< / code > ,
with the following contents:< / p >
< pre > < code class = "language-lisp" > (defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar")))
< / code > < / pre >
< p > Note how the type < code > lisp< / code > of < code > foobar.lisp< / code >
is implicit in the name of the file above.
As for contents of that file, they would look like this:< / p >
< pre > < code class = "language-lisp" > (defpackage :foobar
(:use :common-lisp :alexandria :trivia)
(:export
#:some-function
#:another-function
#:call-with-foobar
#:with-foobar))
(in-package :foobar)
(defun some-function (...)
...)
...
< / code > < / pre >
< p > Instead of < code > using< / code > multiple complete packages, you might want to just import parts of them:< / p >
< pre > < code class = "language-lisp" > (defpackage :foobar
(:use #:common-lisp)
(:import-from #:alexandria
#:some-function
#:another-function))
(:import-from #:trivia
#:some-function
#:another-function))
...)
< / code > < / pre >
< h4 id = "using-the-system-you-defined" > Using the system you defined< / h4 >
< p > Assuming your system is installed under < code > ~/common-lisp/< / code > ,
< code > ~/quicklisp/local-projects/< / code > or some other filesystem hierarchy
already configured for ASDF, you can load it with: < code > (asdf:make "foobar")< / code > .< / p >
< p > If your Lisp was already started when you created that file,
you may have to, either:< / p >
< ul >
< li > load the new .asd file: < code > (load "path/to/foobar.asd")< / code > , or with < code > C-c C-k< / code > in Slime to compile and load the whole file.< / li >
< li > < code > (asdf:clear-configuration)< / code > to re-process the configuration.< / li >
< / ul >
< h3 id = "how-to-write-a-trivial-testing-definition" > How to write a trivial testing definition< / h3 >
< p > Even the most trivial of systems needs some tests,
if only because it will have to be modified eventually,
and you want to make sure those modifications don’ t break client code.
Tests are also a good way to document expected behavior.< / p >
< p > The simplest way to write tests is to have a file < code > foobar-tests.lisp< / code >
and modify the above < code > foobar.asd< / code > as follows:< / p >
< pre > < code class = "language-lisp" > (defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar"))
:in-order-to ((test-op (test-op "foobar/tests"))))
(defsystem "foobar/tests"
:depends-on ("foobar" "fiveam")
:components ((:file "foobar-tests"))
:perform (test-op (o c) (symbol-call :fiveam '#:run! :foobar)))
< / code > < / pre >
< p > The < code > :in-order-to< / code > clause in the first system
allows you to use < code > (asdf:test-system :foobar)< / code >
which will chain into < code > foobar/tests< / code > .
The < code > :perform< / code > clause in the second system does the testing itself.< / p >
< p > In the test system, < code > fiveam< / code > is the name of a popular test library,
and the content of the < code > perform< / code > method is how to invoke this library
to run the test suite < code > :foobar< / code > .
Obvious YMMV if you use a different library.< / p >
< h2 id = "create-a-project-skeleton" > Create a project skeleton< / h2 >
< p > < a href = "https://github.com/fukamachi/cl-project" > cl-project< / a > can be used to
generate a project skeleton. It will create a default ASDF definition,
generate a system for unit testing, etc.< / p >
< p > Install with< / p >
< pre > < code > (ql:quickload "cl-project")
< / code > < / pre >
< p > Create a project:< / p >
< pre > < code class = "language-lisp" > (cl-project:make-project #p"lib/cl-sample/"
:author "Eitaro Fukamachi"
:email "e.arrows@gmail.com"
:license "LLGPL"
:depends-on '(:clack :cl-annot))
;-> writing /Users/fukamachi/Programs/lib/cl-sample/.gitignore
; writing /Users/fukamachi/Programs/lib/cl-sample/README.markdown
; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample-test.asd
; writing /Users/fukamachi/Programs/lib/cl-sample/cl-sample.asd
; writing /Users/fukamachi/Programs/lib/cl-sample/src/hogehoge.lisp
; writing /Users/fukamachi/Programs/lib/cl-sample/t/hogehoge.lisp
;=> T
< / code > < / pre >
< p > And you’ re done.< / p >
< p class = "page-source" >
Page source: < a href = "https://github.com/LispCookbook/cl-cookbook/blob/master/systems.md" > systems.md< / a >
< / p >
< / div >
< script type = "text/javascript" >
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
< / script >
< div >
< footer class = "footer" >
< hr / >
© 2002– 2021 the Common Lisp Cookbook Project
< / footer >
< / div >
< div id = "toc-btn" > T< br > O< br > C< / div >
< / div >
< script text = "javascript" >
HighlightLisp.highlight_auto({className: null});
< / script >
< script type = "text/javascript" >
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField & & searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2& kf=-1& ko=1& q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
< / script >
< script async defer data-domain = "lispcookbook.github.io/cl-cookbook" src = "https://plausible.io/js/plausible.js" > < / script >
< / body >
< / html >