pytrainer: unpin python 3.10
[NixPkgs.git] / pkgs / development / lisp-modules / nix-cl.nix
bloba1441d2e57fffeb79616d2842251eb41c837d444
1 # TODO:
2 # - faster build by using lisp with preloaded asdf?
3 # - dont include java libs unless abcl?
4 # - dont use build-asdf-system to build lispWithPackages?
5 # - make the lisp packages overridable? (e.g. buildInputs glibc->musl)
6 # - build asdf with nix and use that instead of one shipped with impls
7 #   (e.g. to fix build with clisp - does anyone use clisp?)
8 # - claspPackages ? (gotta package clasp with nix first)
9 # - hard one: remove unrelated sources ( of systems not being built)
10 # - figure out a less awkward way to patch sources
11 #   (have to build from src directly for SLIME to work, so can't just patch sources in place)
13 { pkgs
14 , lib
15 , stdenv
16 , ... }:
18 let
20   inherit (lib)
21     length
22     filter
23     foldl
24     unique
25     id
26     concat
27     concatMap
28     mutuallyExclusive
29     findFirst
30     remove
31     setAttr
32     getAttr
33     hasAttr
34     attrNames
35     attrValues
36     filterAttrs
37     mapAttrs
38     splitString
39     concatStringsSep
40     concatMapStringsSep
41     replaceStrings
42     removeSuffix
43     hasInfix
44     optionalString
45     makeBinPath
46     makeLibraryPath
47     makeSearchPath
48     recurseIntoAttrs
49   ;
51   inherit (builtins)
52     head
53     tail
54     elem
55     split
56     storeDir;
58   inherit (pkgs)
59     substituteAll;
61   # Stolen from python-packages.nix
62   # Actually no idea how this works
63   makeOverridableLispPackage = f: origArgs:
64     let
65       ff = f origArgs;
66       overrideWith = newArgs: origArgs // (if pkgs.lib.isFunction newArgs then newArgs origArgs else newArgs);
67     in
68       if builtins.isAttrs ff then (ff // {
69         overrideLispAttrs = newArgs: makeOverridableLispPackage f (overrideWith newArgs);
70       })
71       else if builtins.isFunction ff then {
72         overrideLispAttrs = newArgs: makeOverridableLispPackage f (overrideWith newArgs);
73         __functor = self: ff;
74       }
75       else ff;
77   buildAsdf = { asdf, pkg, program, flags, faslExt }:
78     stdenv.mkDerivation {
79       inherit (asdf) pname version;
80       dontUnpack = true;
81       buildPhase = ''
82         cp -v ${asdf}/lib/common-lisp/asdf/build/asdf.lisp asdf.lisp
83         ${pkg}/bin/${program} ${toString flags} < <(echo '(compile-file "asdf.lisp")')
84       '';
85       installPhase = ''
86         mkdir -p $out
87         cp -v asdf.${faslExt} $out
88       '';
89     };
92   #
93   # Wrapper around stdenv.mkDerivation for building ASDF systems.
94   #
95   build-asdf-system = makeOverridableLispPackage (
96     { pname,
97       version,
98       src ? null,
99       patches ? [],
101       # Native libraries, will be appended to the library path
102       nativeLibs ? [],
104       # Java libraries for ABCL, will be appended to the class path
105       javaLibs ? [],
107       # Lisp dependencies
108       # these should be packages built with `build-asdf-system`
109       # TODO(kasper): use propagatedBuildInputs
110       lispLibs ? [],
112       # Derivation containing the CL implementation package
113       pkg,
115       # Name of the Lisp executable
116       program ? pkg.meta.mainProgram or pkg.pname,
118       # General flags to the Lisp executable
119       flags ? [],
121       # Extension for implementation-dependent FASL files
122       faslExt,
124       # ASDF amalgamation file to use
125       # Created in build/asdf.lisp by `make` in ASDF source tree
126       asdf,
128       # Some libraries have multiple systems under one project, for
129       # example, cffi has cffi-grovel, cffi-toolchain etc.  By
130       # default, only the `pname` system is build.
131       #
132       # .asd's not listed in `systems` are removed in
133       # installPhase. This prevents asdf from referring to uncompiled
134       # systems on run time.
135       #
136       # Also useful when the pname is different than the system name,
137       # such as when using reverse domain naming.
138       systems ? [ pname ],
140       # The .asd files that this package provides
141       # TODO(kasper): remove
142       asds ? systems,
144       # Other args to mkDerivation
145       ...
146     } @ args:
148     (stdenv.mkDerivation (rec {
149       inherit
150         version nativeLibs javaLibs lispLibs systems asds
151         pkg program flags faslExt
152       ;
154       # When src is null, we are building a lispWithPackages and only
155       # want to make use of the dependency environment variables
156       # generated by build-asdf-system
157       dontUnpack = src == null;
159       # Portable script to build the systems.
160       #
161       # `lisp` must evaluate this file then exit immediately. For
162       # example, SBCL's --script flag does just that.
163       #
164       # NOTE:
165       # Every other library worked fine with asdf:compile-system in
166       # buildScript.
167       #
168       # cl-syslog, for some reason, signals that CL-SYSLOG::VALID-SD-ID-P
169       # is undefined with compile-system, but works perfectly with
170       # load-system. Strange.
172       # TODO(kasper) portable quit
173       asdfFasl = buildAsdf { inherit asdf pkg program flags faslExt; };
175       buildScript = substituteAll {
176         src = ./builder.lisp;
177         asdf = "${asdfFasl}/asdf.${faslExt}";
178       };
180       configurePhase = ''
181         runHook preConfigure
183         source ${./setup-hook.sh}
184         buildAsdfPath
186         runHook postConfigure
187       '';
189       buildPhase = optionalString (src != null) ''
190         runHook preBuild
192         export CL_SOURCE_REGISTRY=$CL_SOURCE_REGISTRY:$src//
193         export ASDF_OUTPUT_TRANSLATIONS="$src:$(pwd):${storeDir}:${storeDir}"
194         ${pkg}/bin/${program} ${toString flags} < $buildScript
196         runHook postBuild
197       '';
199       # Copy compiled files to store
200       #
201       # Make sure to include '$' in regex to prevent skipping
202       # stuff like 'iolib.asdf.asd' for system 'iolib.asd'
203       #
204       # Same with '/': `local-time.asd` for system `cl-postgres+local-time.asd`
205       installPhase =
206         let
207           mkSystemsRegex = systems:
208             concatMapStringsSep "\\|" (replaceStrings ["." "+"] ["[.]" "[+]"]) systems;
209         in
210       ''
211         runHook preInstall
213         mkdir -pv $out
214         cp -r * $out
216         # Remove all .asd files except for those in `systems`.
217         find $out -name "*.asd" \
218         | grep -v "/\(${mkSystemsRegex systems}\)\.asd$" \
219         | xargs rm -fv || true
221         runHook postInstall
222       '';
224       dontPatchShebangs = true;
226       # Not sure if it's needed, but caused problems with SBCL
227       # save-lisp-and-die binaries in the past
228       dontStrip = true;
230     } // (args // (let
231       isJVM = args.pkg.pname == "abcl";
232       javaLibs = lib.optionals isJVM args.javaLibs or [];
233     in {
234       pname = "${args.pkg.pname}-${args.pname}";
235       src = if args?patches || args?postPatch
236             then pkgs.applyPatches {
237               inherit (args) src;
238               patches = args.patches or [];
239               postPatch = args.postPatch or "";
240             }
241             else args.src;
242       patches = [];
243       inherit javaLibs;
244       propagatedBuildInputs = args.propagatedBuildInputs or []
245           ++ lispLibs ++ javaLibs ++ nativeLibs;
246       meta = (args.meta or {}) // {
247         maintainers = args.meta.maintainers or lib.teams.lisp.members;
248       };
249     }))) // {
250       # Useful for overriding
251       # Overriding code would prefer to use pname from the attribute set
252       # However, pname is extended with the implementation name
253       # Moreover, it is used in the default list of systems to load
254       # So we pass the original pname
255       pname = args.pname;
256     }));
258   # Build the set of lisp packages using `lisp`
259   # These packages are defined manually for one reason or another:
260   # - The library is not in quicklisp
261   # - The library that is in quicklisp is broken
262   # - Special build procedure such as cl-unicode, asdf
263   #
264   # They can use the auto-imported quicklisp packages as dependencies,
265   # but some of those don't work out of the box.
266   #
267   # E.g if a QL package depends on cl-unicode it won't build out of
268   # the box.
269   commonLispPackagesFor = spec:
270     let
271       build-asdf-system' = body: build-asdf-system (body // spec);
272     in pkgs.callPackage ./packages.nix {
273       inherit spec quicklispPackagesFor;
274       build-asdf-system = build-asdf-system';
275     };
277   # Build the set of packages imported from quicklisp using `lisp`
278   quicklispPackagesFor = spec:
279     let
280       build-asdf-system' = body: build-asdf-system (body // spec);
281     in pkgs.callPackage ./ql.nix {
282       build-asdf-system = build-asdf-system';
283     };
285   # Creates a lisp wrapper with `packages` installed
286   #
287   # `packages` is a function that takes `clpkgs` - a set of lisp
288   # packages - as argument and returns the list of packages to be
289   # installed
290   # TODO(kasper): assert each package has the same lisp and asdf?
291   lispWithPackages = clpkgs: packages:
292     let first = head (lib.attrValues clpkgs); in
293     (build-asdf-system {
294       inherit (first) pkg program flags faslExt asdf;
295       # See dontUnpack in build-asdf-system
296       src = null;
297       pname = "with";
298       version = "packages";
299       lispLibs = packages clpkgs;
300       systems = [];
301     }).overrideAttrs(o: {
302       nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
303       installPhase = ''
304         mkdir -pv $out/bin
305         makeWrapper \
306           ${o.pkg}/bin/${o.program} \
307           $out/bin/${o.program} \
308           --add-flags "${toString o.flags}" \
309           --set ASDF "${o.asdfFasl}/asdf.${o.faslExt}" \
310           --prefix CL_SOURCE_REGISTRY : "$CL_SOURCE_REGISTRY''${CL_SOURCE_REGISTRY:+:}" \
311           --prefix ASDF_OUTPUT_TRANSLATIONS : "$(echo $CL_SOURCE_REGISTRY | sed s,//:,::,g):" \
312           --prefix LD_LIBRARY_PATH : "$LD_LIBRARY_PATH" \
313           --prefix DYLD_LIBRARY_PATH : "$DYLD_LIBRARY_PATH" \
314           --prefix CLASSPATH : "$CLASSPATH" \
315           --prefix GI_TYPELIB_PATH : "$GI_TYPELIB_PATH" \
316           --prefix PATH : "${makeBinPath (o.propagatedBuildInputs or [])}"
317       '';
318     });
320   wrapLisp = {
321     pkg
322     , faslExt
323     , program ? pkg.meta.mainProgram or pkg.pname
324     , flags ? []
325     , asdf ? pkgs.asdf_3_3
326     , packageOverrides ? (self: super: {})
327   }:
328     let
329       spec = { inherit pkg faslExt program flags asdf; };
330       pkgs = (commonLispPackagesFor spec).overrideScope packageOverrides;
331       withPackages = lispWithPackages pkgs;
332       withOverrides = packageOverrides:
333         wrapLisp {
334           inherit pkg faslExt program flags asdf;
335           inherit packageOverrides;
336         };
337       buildASDFSystem = args: build-asdf-system (args // spec);
338     in pkg // {
339       inherit pkgs withPackages withOverrides buildASDFSystem;
340     };
342 in wrapLisp