1
0
Fork 0
cl-sites/lispcookbook.github.io/cl-cookbook/systems.html

329 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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">
<link rel="icon" href=
"assets/cl-logo-blue.png"/>
<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> &ndash; 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> &ndash; 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 style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPY-XMAS2023" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover our contributor's Lisp course with this Christmas coupon.</a>
<strong>
Recently added: 18 videos on MACROS.
</strong>
<a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</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-definition">Loading a system definition</h3>
<p>When you start your Lisp, it knows about its internal modules and, by
default, it has no way to know that your shiny new project is located
under your <code>~/code/foo/bar/new-ideas/</code> directory. So, in order to load
your project in your image, you have one of three ways:</p>
<ul>
<li>use ASDF or Quicklisp defaults</li>
<li>configure where ASDF or Quicklisp look for project definitions</li>
<li>load your project definition explicitely.</li>
</ul>
<p>Please read our section on the <a href="getting-started.html#how-to-load-an-existing-project">getting started#how-to-load-an-existing-project</a> page.</p>
<h3 id="loading-a-system">Loading a system</h3>
<p>Once your Lisp knows what your system is and where it lives, you can load it.</p>
<p>The most trivial use of ASDF is by calling <code>asdf: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>
<p>Quicklisp calls ASDF under the hood, with the advantage that it will download and install any dependency if they are not already installed.</p>
<pre><code class="language-lisp">(ql:quickload "foobar")
;; =&gt;
;; installs all dependencies
;; and loads the system.
</code></pre>
<p>Also, you can use SLIME to load a system, using the <code>M-x slime-load-system</code> Emacs command or the <code>, load-system</code> comma command in the prompt.
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:load-system "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 projects 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">(asdf: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:load-system "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>(asdf:load-asd "path/to/foobar.asd")</code>, or with <code>C-c C-k</code> in Slime to compile and load the whole file.
<ul>
<li>note: avoid using the built-in <code>load</code> for ASDF files, it may work but <code>asdf:load-asd</code> is preferred.</li>
</ul>
</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 dont 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">(asdf:defsystem "foobar"
:depends-on ("alexandria" "trivia")
:components ((:file "foobar"))
:in-order-to ((test-op (test-op "foobar/tests"))))
(asdf: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))
;-&gt; 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
;=&gt; T
</code></pre>
<p>And youre 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/>
&copy; 2002&ndash;2023 the Common Lisp Cookbook Project
<div>
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">vindarel's Lisp course on Udemy</a>
</div>
</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>