1# lisp-modules {#lisp}
2
3This document describes the Nixpkgs infrastructure for building Common Lisp
4systems that use [ASDF](https://asdf.common-lisp.dev/) (Another System
5Definition Facility). It lives in `pkgs/development/lisp-modules`.
6
7## Overview {#lisp-overview}
8
9The main entry point of the API are the Common Lisp implementation packages
10themselves (e.g. `abcl`, `ccl`, `clasp-common-lisp`, `clisp`, `ecl`,
11`sbcl`). They have the `pkgs` and `withPackages` attributes, which can be used
12to discover available packages and to build wrappers, respectively.
13
14The `pkgs` attribute set contains packages that were automatically
15[imported](#lisp-importing-packages-from-quicklisp) from Quicklisp, and any
16other [manually defined](#lisp-defining-packages-inside) ones. Not every package
17works for all the CL implementations (e.g. `nyxt` only makes sense for `sbcl`).
18
19The `withPackages` function is of primary utility. It is used to build
20[runnable wrappers](#lisp-building-wrappers), with a pinned and pre-built
21[ASDF FASL](#lisp-loading-asdf) available in the `ASDF` environment variable,
22and `CL_SOURCE_REGISTRY`/`ASDF_OUTPUT_TRANSLATIONS` configured to
23[find the desired systems on runtime](#lisp-loading-systems).
24
25In addition, Lisps have the `withOverrides` function, which can be used to
26[substitute](#lisp-including-external-pkg-in-scope) any package in the scope of
27their `pkgs`. This will also be useful together with `overrideLispAttrs` when
28[dealing with slashy systems](#lisp-dealing-with-slashy-systems), because they
29should stay in the main package and be built by specifying the `systems`
30argument to `build-asdf-system`.
31
32## The 90% use case example {#lisp-use-case-example}
33
34The most common way to use the library is to run ad-hoc wrappers like this:
35
36`nix-shell -p 'sbcl.withPackages (ps: with ps; [ alexandria ])'`
37
38Then, in a shell:
39
40```
41$ sbcl
42* (load (sb-ext:posix-getenv "ASDF"))
43* (asdf:load-system 'alexandria)
44```
45
46Also one can create a `pkgs.mkShell` environment in `shell.nix`/`flake.nix`:
47
48```nix
49let
50 sbcl' = sbcl.withPackages (ps: [ ps.alexandria ]);
51in
52mkShell {
53 packages = [ sbcl' ];
54}
55```
56
57Such a Lisp can be now used e.g. to compile your sources:
58
59```nix
60{
61 buildPhase = ''
62 runHook preBuild
63
64 ${sbcl'}/bin/sbcl --load my-build-file.lisp
65
66 runHook postBuild
67 '';
68}
69```
70
71## Importing packages from Quicklisp {#lisp-importing-packages-from-quicklisp}
72
73To save some work of writing Nix expressions, there is a script that imports all
74the packages distributed by Quicklisp into `imported.nix`. This works by parsing
75its `releases.txt` and `systems.txt` files, which are published every couple of
76months on [quicklisp.org](https://beta.quicklisp.org/dist/quicklisp.txt).
77
78The import process is implemented in the `import` directory as Common Lisp
79code in the `org.lispbuilds.nix` ASDF system. To run the script, one can
80execute `ql-import.lisp`:
81
82```
83cd pkgs/development/lisp-modules
84nix-shell --run 'sbcl --script ql-import.lisp'
85```
86
87The script will:
88
891. Download the latest Quicklisp `systems.txt` and `releases.txt` files
902. Generate a temporary SQLite database of all QL systems in `packages.sqlite`
913. Generate an `imported.nix` file from the database
92
93(The `packages.sqlite` file can be deleted at will, because it is regenerated
94each time the script runs.)
95
96The maintainer's job is to:
97
981. Re-run the `ql-import.lisp` script when there is a new Quicklisp release
992. [Add any missing native dependencies](#lisp-quicklisp-adding-native-dependencies) in `ql.nix`
1003. For packages that still don't build, [package them manually](#lisp-defining-packages-inside) in `packages.nix`
101
102Also, the `imported.nix` file **must not be edited manually**! It should only be
103generated as described in this section (by running `ql-import.lisp`).
104
105### Adding native dependencies {#lisp-quicklisp-adding-native-dependencies}
106
107The Quicklisp files contain ASDF dependency data, but don't include native
108library (CFFI) dependencies, and, in the case of ABCL, Java dependencies.
109
110The `ql.nix` file contains a long list of overrides, where these dependencies
111can be added.
112
113Packages defined in `packages.nix` contain these dependencies naturally.
114
115### Trusting `systems.txt` and `releases.txt` {#lisp-quicklisp-trusting}
116
117The previous implementation of `lisp-modules` didn't fully trust the Quicklisp
118data, because there were times where the dependencies specified were not
119complete and caused broken builds. It instead used a `nix-shell` environment to
120discover real dependencies by using the ASDF APIs.
121
122The current implementation has chosen to trust this data, because it's faster to
123parse a text file than to build each system to generate its Nix file, and
124because that way packages can be mass-imported. Because of that, there may come
125a day where some packages will break, due to bugs in Quicklisp. In that case,
126the fix could be a manual override in `packages.nix` and `ql.nix`.
127
128A known fact is that Quicklisp doesn't include dependencies on slashy systems in
129its data. This is an example of a situation where such fixes were used, e.g. to
130replace the `systems` attribute of the affected packages. (See the definition of
131`iolib`).
132
133### Quirks {#lisp-quicklisp-quirks}
134
135During Quicklisp import:
136
137- `+` in names is converted to `_plus{_,}`: `cl+ssl`->`cl_plus_ssl`, `alexandria+`->`alexandria_plus`
138- `.` in names is converted to `_dot_`: `iolib.base`->`iolib_dot_base`
139- names starting with a number have a `_` prepended (`3d-vectors`->`_3d-vectors`)
140- `_` in names is converted to `__` for reversibility
141
142## Defining packages manually inside Nixpkgs {#lisp-defining-packages-inside}
143
144Packages that for some reason are not in Quicklisp, and so cannot be
145auto-imported, or don't work straight from the import, are defined in the
146`packages.nix` file.
147
148In that file, use the `build-asdf-system` function, which is a wrapper around
149`mkDerivation` for building ASDF systems. Various other hacks are present, such
150as `build-with-compile-into-pwd` for systems which create files during
151compilation (such as cl-unicode).
152
153The `build-asdf-system` function is documented
154[here](#lisp-defining-packages-outside). Also, `packages.nix` is full of
155examples of how to use it.
156
157## Defining packages manually outside Nixpkgs {#lisp-defining-packages-outside}
158
159Lisp derivations (`abcl`, `sbcl` etc.) also export the `buildASDFSystem`
160function, which is similar to `build-asdf-system` from `packages.nix`, but is
161part of the public API.
162
163It takes the following arguments:
164
165- `pname`: the package name
166- `version`: the package version
167- `src`: the package source
168- `patches`: patches to apply to the source before build
169- `nativeLibs`: native libraries used by CFFI and grovelling
170- `javaLibs`: Java libraries for ABCL
171- `lispLibs`: dependencies on other packages build with `buildASDFSystem`
172- `systems`: list of systems to build
173
174It can be used to define packages outside Nixpkgs, and, for example, add them
175into the package scope with `withOverrides`.
176
177### Including an external package in scope {#lisp-including-external-pkg-in-scope}
178
179A package defined outside Nixpkgs using `buildASDFSystem` can be woven into the
180Nixpkgs-provided scope like this:
181
182```nix
183let
184 alexandria = sbcl.buildASDFSystem rec {
185 pname = "alexandria";
186 version = "1.4";
187 src = fetchFromGitLab {
188 domain = "gitlab.common-lisp.net";
189 owner = "alexandria";
190 repo = "alexandria";
191 tag = "v${version}";
192 hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ=";
193 };
194 };
195 sbcl' = sbcl.withOverrides (
196 self: super: {
197 inherit alexandria;
198 }
199 );
200in
201sbcl'.pkgs.alexandria
202```
203
204## Overriding package attributes {#lisp-overriding-package-attributes}
205
206Packages export the `overrideLispAttrs` function, which can be used to build a
207new package with different parameters.
208
209Example of overriding `alexandria`:
210
211```nix
212sbcl.pkgs.alexandria.overrideLispAttrs (oldAttrs: rec {
213 version = "1.4";
214 src = fetchFromGitLab {
215 domain = "gitlab.common-lisp.net";
216 owner = "alexandria";
217 repo = "alexandria";
218 tag = "v${version}";
219 hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ=";
220 };
221})
222```
223
224### Dealing with slashy systems {#lisp-dealing-with-slashy-systems}
225
226Slashy (secondary) systems should not exist in their own packages! Instead, they
227should be included in the parent package as an extra entry in the `systems`
228argument to the `build-asdf-system`/`buildASDFSystem` functions.
229
230The reason is that ASDF searches for a secondary system in the `.asd` of the
231parent package. Thus, having them separate would cause either one of them not to
232load cleanly, because one will contains FASLs of itself but not the other, and
233vice versa.
234
235To package slashy systems, use `overrideLispAttrs`, like so:
236
237```nix
238ecl.pkgs.alexandria.overrideLispAttrs (oldAttrs: {
239 systems = oldAttrs.systems ++ [ "alexandria/tests" ];
240 lispLibs = oldAttrs.lispLibs ++ [ ecl.pkgs.rt ];
241})
242```
243
244See the [respective section](#lisp-including-external-pkg-in-scope) on using
245`withOverrides` for how to weave it back into `ecl.pkgs`.
246
247Note that sometimes the slashy systems might not only have more dependencies
248than the main one, but create a circular dependency between `.asd`
249files. Unfortunately, in this case an adhoc solution becomes necessary.
250
251## Building Wrappers {#lisp-building-wrappers}
252
253Wrappers can be built using the `withPackages` function of Common Lisp
254implementations (`abcl`, `ecl`, `sbcl` etc.):
255
256```
257nix-shell -p 'sbcl.withPackages (ps: [ ps.alexandria ps.bordeaux-threads ])'
258```
259
260Such a wrapper can then be used like this:
261
262```
263$ sbcl
264* (load (sb-ext:posix-getenv "ASDF"))
265* (asdf:load-system 'alexandria)
266* (asdf:load-system 'bordeaux-threads)
267```
268
269### Loading ASDF {#lisp-loading-asdf}
270
271For best results, avoid calling `(require 'asdf)` When using the
272library-generated wrappers.
273
274Use `(load (ext:getenv "ASDF"))` instead, supplying your implementation's way of
275getting an environment variable for `ext:getenv`. This will load the
276(pre-compiled to FASL) Nixpkgs-provided version of ASDF.
277
278### Loading systems {#lisp-loading-systems}
279
280There, you can use `asdf:load-system`. This works by setting the right
281values for the `CL_SOURCE_REGISTRY`/`ASDF_OUTPUT_TRANSLATIONS` environment
282variables, so that systems are found in the Nix store and pre-compiled FASLs are
283loaded.
284
285## Adding a new Lisp {#lisp-adding-a-new-lisp}
286
287The function `wrapLisp` is used to wrap Common Lisp implementations. It adds the
288`pkgs`, `withPackages`, `withOverrides` and `buildASDFSystem` attributes to the
289derivation.
290
291`wrapLisp` takes these arguments:
292
293- `pkg`: the Lisp package
294- `faslExt`: Implementation-specific extension for FASL files
295- `program`: The name of executable file in `${pkg}/bin/` (Default: `pkg.pname`)
296- `flags`: A list of flags to always pass to `program` (Default: `[]`)
297- `asdf`: The ASDF version to use (Default: `pkgs.asdf_3_3`)
298- `packageOverrides`: Package overrides config (Default: `(self: super: {})`)
299
300This example wraps CLISP:
301
302```nix
303wrapLisp {
304 pkg = clisp;
305 faslExt = "fas";
306 flags = [
307 "-E"
308 "UTF8"
309 ];
310}
311```