2020-01-08 03:00:54 +01:00
|
|
|
# buildLisp provides Nix functions to build Common Lisp packages,
|
|
|
|
# targeting SBCL.
|
|
|
|
#
|
|
|
|
# buildLisp is designed to enforce conventions and do away with the
|
|
|
|
# free-for-all of existing Lisp build systems.
|
|
|
|
|
|
|
|
{ pkgs ? { third_party = import <nixpkgs> {}; }
|
|
|
|
, ... }:
|
|
|
|
|
|
|
|
let
|
|
|
|
inherit (builtins) map elemAt match;
|
2020-01-08 20:38:29 +01:00
|
|
|
inherit (pkgs.third_party) lib runCommandNoCC writeText writeShellScriptBin sbcl;
|
2020-01-08 03:00:54 +01:00
|
|
|
|
|
|
|
#
|
|
|
|
# Internal helper definitions
|
|
|
|
#
|
|
|
|
|
2020-01-08 22:39:06 +01:00
|
|
|
# 'genLoadLisp' generates Lisp code that instructs SBCL to load all
|
|
|
|
# the provided Lisp libraries.
|
|
|
|
genLoadLisp = deps: lib.concatStringsSep "\n"
|
|
|
|
(map (lib: "(load \"${lib}/${lib.lispName}.fasl\")") (allDeps deps));
|
|
|
|
|
2020-01-08 03:00:54 +01:00
|
|
|
# 'genCompileLisp' generates a Lisp file that instructs SBCL to
|
|
|
|
# compile the provided list of Lisp source files to $out.
|
2020-01-08 22:39:06 +01:00
|
|
|
genCompileLisp = srcs: deps: writeText "compile.lisp" ''
|
|
|
|
;; This file compiles the specified sources into the Nix build
|
|
|
|
;; directory, creating one FASL file for each source.
|
|
|
|
(require 'sb-posix)
|
|
|
|
|
|
|
|
${genLoadLisp deps}
|
|
|
|
|
|
|
|
(defun nix-compile-lisp (srcfile)
|
|
|
|
(let ((outfile (make-pathname :type "fasl"
|
|
|
|
:directory (or (sb-posix:getenv "NIX_BUILD_TOP")
|
|
|
|
(error "not running in a Nix build"))
|
|
|
|
:defaults srcfile)))
|
|
|
|
(multiple-value-bind (_outfile _warnings-p failure-p)
|
|
|
|
(compile-file srcfile :output-file outfile)
|
|
|
|
(when failure-p
|
|
|
|
(sb-posix:exit 1)))))
|
|
|
|
|
|
|
|
(let ((*compile-verbose* t)
|
|
|
|
;; FASL files are compiled into the working directory of the
|
|
|
|
;; build and *then* moved to the correct out location.
|
|
|
|
(pwd (sb-posix:getcwd)))
|
|
|
|
|
|
|
|
;; These forms were inserted by the Nix build:
|
|
|
|
${
|
|
|
|
lib.concatStringsSep "\n" (map (src: "(nix-compile-lisp \"${src}\")") srcs)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
'';
|
2020-01-08 03:00:54 +01:00
|
|
|
|
2020-01-08 19:40:53 +01:00
|
|
|
# 'allDeps' flattens the list of dependencies (and their
|
|
|
|
# dependencies) into one list of unique deps.
|
2020-01-08 22:39:06 +01:00
|
|
|
#
|
|
|
|
# TODO(tazjin): Ordering needs to be stable (first occurences from
|
|
|
|
# innermost to outer), I don't know if this works accidentally or is
|
|
|
|
# guaranteed by these lib functions.
|
|
|
|
allDeps = deps: lib.reverseList (
|
|
|
|
lib.unique (lib.flatten (deps ++ (map (d: d.lispDeps) deps)))
|
2020-01-08 19:40:53 +01:00
|
|
|
);
|
|
|
|
|
2020-01-08 22:39:26 +01:00
|
|
|
# 'genDumpLisp' generates a Lisp file that instructs SBCL to dump
|
|
|
|
# the currently loaded image as an executable to $out/bin/$name.
|
2020-01-08 03:00:54 +01:00
|
|
|
#
|
2020-01-08 22:39:26 +01:00
|
|
|
# TODO(tazjin): Compression is currently unsupported because the
|
|
|
|
# SBCL in nixpkgs is, by default, not compiled with zlib support.
|
|
|
|
genDumpLisp = name: main: deps: writeText "dump.lisp" ''
|
|
|
|
(require 'sb-posix)
|
|
|
|
|
|
|
|
${genLoadLisp deps}
|
|
|
|
|
|
|
|
(let* ((bindir (concatenate 'string (sb-posix:getenv "out") "/bin"))
|
|
|
|
(outpath (make-pathname :name "${name}"
|
|
|
|
:directory bindir)))
|
|
|
|
(save-lisp-and-die outpath
|
|
|
|
:executable t
|
|
|
|
:toplevel (function ${main})
|
|
|
|
:purify t))
|
|
|
|
;;
|
|
|
|
'';
|
2020-01-08 03:00:54 +01:00
|
|
|
|
|
|
|
# Add an `overrideLisp` attribute to a function result that works
|
|
|
|
# similar to `overrideAttrs`, but is used specifically for the
|
|
|
|
# arguments passed to Lisp builders.
|
|
|
|
makeOverridable = f: orig: (f orig) // {
|
|
|
|
overrideLisp = new: makeOverridable f (orig // (new orig));
|
|
|
|
};
|
|
|
|
|
2020-01-08 22:39:26 +01:00
|
|
|
#
|
|
|
|
# Public API functions
|
|
|
|
#
|
|
|
|
|
2020-01-08 03:00:54 +01:00
|
|
|
# 'library' builds a list of Common Lisp files into a single FASL
|
|
|
|
# which can then be loaded into SBCL.
|
2020-01-08 19:40:53 +01:00
|
|
|
library = { name, srcs, deps ? [] }: runCommandNoCC "${name}-cllib" {} ''
|
2020-01-08 22:39:06 +01:00
|
|
|
${sbcl}/bin/sbcl --script ${genCompileLisp srcs deps}
|
2020-01-08 03:00:54 +01:00
|
|
|
|
|
|
|
# FASL files can be combined by simply concatenating them together:
|
|
|
|
mkdir $out
|
|
|
|
cat ./*.fasl > $out/${name}.fasl
|
2020-01-08 19:40:53 +01:00
|
|
|
'' // { lispName = name; lispDeps = deps; };
|
2020-01-08 03:00:54 +01:00
|
|
|
|
|
|
|
# 'program' creates an executable containing a dumped image of the
|
|
|
|
# specified sources and dependencies.
|
2020-01-08 22:39:26 +01:00
|
|
|
program = { name, main ? "${name}:main", srcs, deps ? [] }: runCommandNoCC "${name}" {} ''
|
|
|
|
mkdir -p $out/bin
|
|
|
|
${sbcl}/bin/sbcl --script ${
|
|
|
|
genDumpLisp name main (lib.singleton (library {
|
|
|
|
inherit name srcs deps;
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
'';
|
2020-01-08 03:00:54 +01:00
|
|
|
|
|
|
|
# 'sbclWith' creates an image with the specified libraries /
|
|
|
|
# programs loaded.
|
2020-01-08 20:38:29 +01:00
|
|
|
sbclWith = deps: writeShellScriptBin "sbcl" ''
|
2020-01-08 22:39:06 +01:00
|
|
|
exec ${sbcl}/bin/sbcl ${
|
|
|
|
if deps == [] then ""
|
|
|
|
else "--load ${writeText "load.lisp" (genLoadLisp deps)}"
|
|
|
|
} $@
|
2020-01-08 20:38:29 +01:00
|
|
|
'';
|
2020-01-08 03:00:54 +01:00
|
|
|
in {
|
|
|
|
library = makeOverridable library;
|
|
|
|
program = makeOverridable program;
|
|
|
|
sbclWith = makeOverridable sbclWith;
|
|
|
|
}
|