diff options
author | 7c6f434c <7c6f434c@mail.ru> | 2023-04-03 06:00:48 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-03 06:00:48 +0000 |
commit | 8ce248a7345212d0f96a8c6dea536a9c05f0685a (patch) | |
tree | 4f3fa207249647eeb0cdc1660f12adc70429e7c0 /doc | |
parent | 471eeab9ddfb6177d40b3852ae79a059d0cd36a2 (diff) | |
parent | 2b180f796a983d3cd8dbb5d39610175eba60de78 (diff) |
Merge pull request #218870 from Uthar/lisp2
lisp-modules: replace with an implementation based on github:uthar/nix-cl
Diffstat (limited to 'doc')
-rw-r--r-- | doc/languages-frameworks/index.xml | 1 | ||||
-rw-r--r-- | doc/languages-frameworks/lisp.section.md | 304 |
2 files changed, 305 insertions, 0 deletions
diff --git a/doc/languages-frameworks/index.xml b/doc/languages-frameworks/index.xml index f089b99a0451b..e1e35564e321f 100644 --- a/doc/languages-frameworks/index.xml +++ b/doc/languages-frameworks/index.xml @@ -25,6 +25,7 @@ <xi:include href="ios.section.xml" /> <xi:include href="java.section.xml" /> <xi:include href="javascript.section.xml" /> + <xi:include href="lisp.section.xml" /> <xi:include href="lua.section.xml" /> <xi:include href="maven.section.xml" /> <xi:include href="nim.section.xml" /> diff --git a/doc/languages-frameworks/lisp.section.md b/doc/languages-frameworks/lisp.section.md new file mode 100644 index 0000000000000..3c408eaa09dae --- /dev/null +++ b/doc/languages-frameworks/lisp.section.md @@ -0,0 +1,304 @@ +# lisp-modules {#lisp} + +This document describes the Nixpkgs infrastructure for building Common Lisp +libraries that use ASDF (Another System Definition Facility). It lives in +`pkgs/development/lisp-modules`. + +## Overview {#lisp-overview} + +The main entry point of the API are the Common Lisp implementation packages +(e.g. `abcl`, `ccl`, `clasp-common-lisp`, `clisp` `ecl`, `sbcl`) +themselves. They have the `pkgs` and `withPackages` attributes, which can be +used to discover available packages and to build wrappers, respectively. + +The `pkgs` attribute set contains packages that were automatically imported from +Quicklisp, and any other manually defined ones. Not every package works for all +the CL implementations (e.g. `nyxt` only makes sense for `sbcl`). + +The `withPackages` function is of primary utility. It is used to build runnable +wrappers, with a pinned and pre-built ASDF FASL available in the `ASDF` +environment variable, and `CL_SOURCE_REGISTRY`/`ASDF_OUTPUT_TRANSLATIONS` +configured to find the desired systems on runtime. + +With a few exceptions, the primary thing that the infrastructure does is to run +`asdf:load-system` for each system specified in the `systems` argument to +`build-asdf-system`, and save the FASLs to the Nix store. Then, it makes these +FASLs available to wrappers. Any other use-cases, such as producing SBCL +executables with `sb-ext:save-lisp-and-die`, are achieved via overriding the +`buildPhase` etc. + +In addition, Lisps have the `withOverrides` function, which can be used to +substitute any package in the scope of their `pkgs`. This will be useful +together with `overrideLispAttrs` when dealing with slashy ASDF systems, because +they should stay in the main package and be build by specifying the `systems` +argument to `build-asdf-system`. + +## The 90% use case example {#lisp-use-case-example} + +The most common way to use the library is to run ad-hoc wrappers like this: + +`nix-shell -p 'sbcl.withPackages (ps: with ps; [ alexandria ])'` + +Then, in a shell: + +``` +$ result/bin/sbcl +* (load (sb-ext:posix-getenv "ASDF")) +* (asdf:load-system 'alexandria) +``` + +Also one can create a `pkgs.mkShell` environment in `shell.nix`/`flake.nix`: + +``` +let + sbcl' = sbcl.withPackages (ps: [ ps.alexandria ]); +in mkShell { + buildInputs = [ sbcl' ]; +} +``` + +Such a Lisp can be now used e.g. to compile your sources: + +``` +buildPhase = '' + ${sbcl'}/bin/sbcl --load my-build-file.lisp +'' +``` + +## Importing packages from Quicklisp {#lisp-importing-packages-from-quicklisp} + +The library is able to very quickly import all the packages distributed by +Quicklisp by parsing its `releases.txt` and `systems.txt` files. These files are +available from [http://beta.quicklisp.org/dist/quicklisp.txt]. + +The import process is implemented in the `import` directory as Common Lisp +functions in the `org.lispbuilds.nix` ASDF system. To run the script, one can +execute `ql-import.lisp`: + +``` +nix-shell --run 'sbcl --script ql-import.lisp' +``` + +The script will: + +1. Download the latest Quicklisp `systems.txt` and `releases.txt` files +2. Generate an SQLite database of all QL systems in `packages.sqlite` +3. Generate an `imported.nix` file from the database + +The maintainer's job there is to: + +1. Re-run the `ql-import.lisp` script +2. Add missing native dependencies in `ql.nix` +3. For packages that still don't build, package them manually in `packages.nix` + +Also, the `imported.nix` file **must not be edited manually**! It should only be +generated as described in this section. + +### Adding native dependencies {#lisp-quicklisp-adding-native-dependencies} + +The Quicklisp files contain ASDF dependency data, but don't include native +library (CFFI) dependencies, and, in the case of ABCL, Java dependencies. + +The `ql.nix` file contains a long list of overrides, where these dependencies +can be added. + +Packages defined in `packages.nix` contain these dependencies naturally. + +### Trusting `systems.txt` and `releases.txt` {#lisp-quicklisp-trusting} + +The previous implementation of `lisp-modules` didn't fully trust the Quicklisp +data, because there were times where the dependencies specified were not +complete, and caused broken builds. It instead used a `nix-shell` environment to +discover real dependencies by using the ASDF APIs. + +The current implementation has chosen to trust this data, because it's faster to +parse a text file than to build each system to generate its Nix file, and +because that way packages can be mass-imported. Because of that, there may come +a day where some packages will break, due to bugs in Quicklisp. In that case, +the fix could be a manual override in `packages.nix` and `ql.nix`. + +A known fact is that Quicklisp doesn't include dependencies on slashy systems in +its data. This is an example of a situation where such fixes were used, e.g. to +replace the `systems` attribute of the affected packages. (See the definition of +`iolib`). + +### Quirks {#lisp-quicklisp-quirks} + +During Quicklisp import: + +- `+` in names are converted to `_plus{_,}`: `cl+ssl`->`cl_plus_ssl`, `alexandria+`->`alexandria_plus` +- `.` to `_dot_`: `iolib.base`->`iolib_dot_base` +- names starting with a number have a `_` prepended (`3d-vectors`->`_3d-vectors`) +- `_` in names is converted to `__` for reversibility + + +## Defining packages manually inside Nixpkgs {#lisp-defining-packages-inside} + +New packages, that for some reason are not in Quicklisp, and so cannot be +auto-imported, can be written in the `packages.nix` file. + +In that file, use the `build-asdf-system` function, which is a wrapper around +`mkDerivation` for building ASDF systems. Various other hacks are present, such +as `build-with-compile-into-pwd` for systems which create files during +compilation. + +The `build-asdf-system` function is documented with comments in +`nix-cl.nix`. Also, `packages.nix` is full of examples of how to use it. + +## Defining packages manually outside Nixpkgs {#lisp-defining-packages-outside} + +Lisp derivations (`abcl`, `sbcl` etc.) also export the `buildASDFSystem` +function, which is the same as `build-asdf-system`, except for the `lisp` +argument which is set to the given CL implementation. + +It can be used to define packages outside Nixpkgs, and, for example, add them +into the package scope with `withOverrides` which will be discussed later on. + +### Including an external package in scope {#lisp-including-external-pkg-in-scope} + +A package defined outside Nixpkgs using `buildASDFSystem` can be woven into the +Nixpkgs-provided scope like this: + +``` +let + alexandria = sbcl.buildASDFSystem rec { + pname = "alexandria"; + version = "1.4"; + src = fetchFromGitLab { + domain = "gitlab.common-lisp.net"; + owner = "alexandria"; + repo = "alexandria"; + rev = "v${version}"; + hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ="; + }; + }; + sbcl' = sbcl.withOverrides (self: super: { + inherit alexandria; + }); +in sbcl'.pkgs.alexandria +``` + +## Overriding package attributes {#lisp-overriding-package-attributes} + +Packages export the `overrideLispAttrs` function, which can be used to build a +new package with different parameters. + +Example of overriding `alexandria`: + +``` +sbcl.pkgs.alexandria.overrideLispAttrs (oldAttrs: rec { + version = "1.4"; + src = fetchFromGitLab { + domain = "gitlab.common-lisp.net"; + owner = "alexandria"; + repo = "alexandria"; + rev = "v${version}"; + hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ="; + }; +}) +``` + +## Overriding packages in scope {#lisp-overriding-packages-in-scope} + +Packages can be woven into a new scope by using `withOverrides`: + +``` +let + sbcl' = sbcl.withOverrides (self: super: { + alexandria = super.alexandria.overrideLispAttrs (oldAttrs: rec { + pname = "alexandria"; + version = "1.4"; + src = fetchFromGitLab { + domain = "gitlab.common-lisp.net"; + owner = "alexandria"; + repo = "alexandria"; + rev = "v${version}"; + hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ="; + }; + }); + }); +in builtins.elemAt sbcl'.pkgs.bordeaux-threads.lispLibs 0 +``` + +### Dealing with slashy systems {#lisp-dealing-with-slashy-systems} + +Slashy (secondary) systems should not exist in their own packages! Instead, they +should be included in the parent package as an extra entry in the `systems` +argument to the `build-asdf-system`/`buildASDFSystem` functions. + +The reason is that ASDF searches for a secondary system in the `.asd` of the +parent package. Thus, having them separate would cause either one of them not to +load cleanly, because one will contains FASLs of itself but not the other, and +vice versa. + +To package slashy systems, use `overrideLispAttrs`, like so: + +``` +ecl.pkgs.alexandria.overrideLispAttrs (oldAttrs: { + systems = oldAttrs.systems ++ [ "alexandria/tests" ]; + lispLibs = oldAttrs.lispLibs ++ [ ecl.pkgs.rt ]; +}) +``` + +See the respective section on using `withOverrides` for how to weave it back +into `ecl.pkgs`. + +Note that sometimes the slashy systems might not only have more dependencies +than the main one, but create a circular dependency between `.asd` +files. Unfortunately, in this case an adhoc solution becomes necessary. + +## Building Wrappers {#lisp-building-wrappers} + +Wrappers can be built using the `withPackages` function of Common Lisp +implementations (`abcl`, `ecl`, `sbcl` etc.): + +``` +sbcl.withPackages (ps: [ ps.alexandria ps.bordeaux-threads ]) +``` + +Such a wrapper can then be executed like this: + +``` +result/bin/sbcl +``` + +### Loading ASDF {#lisp-loading-asdf} + +For best results, avoid calling `(require 'asdf)` When using the +library-generated wrappers. + +Use `(load (ext:getenv "ASDF"))` instead, supplying your implementation's way of +getting an environment variable for `ext:getenv`. This will load the +(pre-compiled to FASL) Nixpkgs-provided version of ASDF. + +### Loading systems {#lisp-loading-systems} + +There, you can simply use `asdf:load-system`. This works by setting the right +values for the `CL_SOURCE_REGISTRY`/`ASDF_OUTPUT_TRANSLATIONS` environment +variables, so that systems are found in the Nix store and pre-compiled FASLs are +loaded. + +## Adding a new Lisp {#lisp-adding-a-new-lisp} + +The function `wrapLisp` is used to wrap Common Lisp implementations. It adds the +`pkgs`, `withPackages`, `withOverrides` and `buildASDFSystem` attributes to the +derivation. + +`wrapLisp` takes these arguments: + +- `pkg`: the Lisp package +- `faslExt`: Implementation-specific extension for FASL files +- `program`: The name of executable file in `${pkg}/bin/` (Default: `pkg.pname`) +- `flags`: A list of flags to always pass to `program` (Default: `[]`) +- `asdf`: The ASDF version to use (Default: `pkgs.asdf_3_3`) +- `packageOverrides`: Package overrides config (Default: `(self: super: {})`) + +This example wraps CLISP: + +``` +wrapLisp { + pkg = clisp; + faslExt = "fas"; + flags = ["-E" "UTF8"]; +} +``` |