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