So you’ve got a Common Lisp project that you want to build in Nix?
ql2nix can help! ql2nix will help you produce a nix expression
that builds a Quicklisp bundle that contains the Common Lisp
dependencies of your project.
The preferred way to build ql2nix is with Nix, of course! ql2nix
includes a default.nix that builds ql2nix. Just run nix-build!
nix-build
ql2nix is also a valid ASDF system and it is set up to output an
executable. So, all you need to do is get ASDF to perform the
program-op on "ql2nix". Something like this!
sbcl --eval '(require :asdf)' \
--eval '(let ((asdf:*central-registry* (cons (truename ".") asdf:*central-registry*)))
(asdf:oos (quote asdf:program-op) "ql2nix"))'
ASDF will save the executable in the usual output location – usually
somewhere in $HOME/.cache/common-lisp/.
ql2nix [--quicklisp-setup path/to/quicklisp/setup.lisp] [--project-dir PATH] [--] system...
ql2nix works by loading the named systems with ql:quickload. Any
system that ASDF touches gets marked. If that system is provided by
your Quicklisp installation then ql2nix will include it in the
closure.
If ql2nix wasn’t built with Quicklisp already loaded then you must
provide the path to Quicklisp’s setup.lisp via the
--quicklisp-setup command line argument.
Any paths you specify with --project-dir will be included in ASDF’s
source registry. Systems contained within those paths are not
included in the closure that ql2nix produces.
There are two ways you can use ql2nix. You can either have ql2nix
load your ASDF system and discover required dependencies automatically
or you can pass in dependencies explicitly. Either way, you must run
ql2nix in an environment where it can ql:quickload the systems
that are named. So, if a package depends on a native library then
that native library must be available in the environment when you run
ql2nix. For example…
nix-shell -p someNativeDependency # if you have any! # Automatic dependency discovery ql2nix --quicklisp-setup ~/quicklisp/setup.lisp --project-dir path/to/your/source/ your-system-name # Explicit dependencies ql2nix --quicklisp-setup ~/quicklisp/setup.lisp systems-you depend-on-go here
Either way, ql2nix will output a file named qlDist.nix. This file
describes the transitive closure of Quicklisp systems that were
touched while loading the systems specified on the command line.
qlDist.nix can be combined with the provided mkNixlispBundle.nix
to produce a Nix derivation that contains the transitive closure of
systems described by qlDist.nix.
If you’re already using Nixpkgs’s clwrapper, then using
mkNixlispBundle is trivial. Simply include the derivation it
returns in your buildInputs. clwrapper will ensure that all of
the closure’s systems are included in ASDF’s source registry. For example,
{ pkgs ? import <nixpkgs> {} }:
with (import (fetchFromGitHub {
owner = "SquircleSpace"
repo = "ql2nix";
rev = "...";
sha256 = "...";
}) { inherit pkgs; });
let
nixlispBundle = mkNixlispBundle ./qlDist.nix;
in
pkgs.stdenv.mkDerivation rec {
name = "example";
buildInputs = [ ql2nix nixlispBundle ];
# ...
}
If you’re not using clwrapper, then you’ll need to LOAD the file
at lib/common-lisp/bundle/bundle.lisp. This file will configure
ASDF so that the closure’s systems are included in ASDF’s source
registry.
As mentioned above, you need to use mkNixlispBundle to leverage the
qlDist.nix file produced by ql2nix. Until Nixpkgs includes
mkNixlispBundle, you’ll need to embed it within your project.
ql2nix will helpfully write out the supporting Nix expressions
alongside qlDist.nix if you pass in the --nixlisp-lib flag.