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)
12
13{
14 pkgs,
15 lib,
16 stdenv,
17 ...
18}:
19
20let
21
22 inherit (lib)
23 length
24 filter
25 foldl
26 unique
27 id
28 concat
29 concatMap
30 mutuallyExclusive
31 findFirst
32 remove
33 setAttr
34 getAttr
35 hasAttr
36 attrNames
37 attrValues
38 filterAttrs
39 mapAttrs
40 splitString
41 concatStringsSep
42 concatMapStringsSep
43 replaceStrings
44 removeSuffix
45 hasInfix
46 optionalString
47 makeBinPath
48 makeLibraryPath
49 makeSearchPath
50 recurseIntoAttrs
51 ;
52
53 inherit (builtins)
54 head
55 tail
56 elem
57 split
58 storeDir
59 ;
60
61 inherit (pkgs)
62 replaceVars
63 ;
64
65 # Stolen from python-packages.nix
66 # Actually no idea how this works
67 makeOverridableLispPackage =
68 f: origArgs:
69 let
70 ff = f origArgs;
71 overrideWith =
72 newArgs: origArgs // (if pkgs.lib.isFunction newArgs then newArgs origArgs else newArgs);
73 in
74 if builtins.isAttrs ff then
75 (
76 ff
77 // {
78 overrideLispAttrs = newArgs: makeOverridableLispPackage f (overrideWith newArgs);
79 }
80 )
81 else if builtins.isFunction ff then
82 {
83 overrideLispAttrs = newArgs: makeOverridableLispPackage f (overrideWith newArgs);
84 __functor = self: ff;
85 }
86 else
87 ff;
88
89 buildAsdf =
90 {
91 asdf,
92 pkg,
93 program,
94 flags,
95 faslExt,
96 }:
97 stdenv.mkDerivation {
98 inherit (asdf) pname version;
99 dontUnpack = true;
100 buildPhase = ''
101 cp -v ${asdf}/lib/common-lisp/asdf/build/asdf.lisp asdf.lisp
102 ${pkg}/bin/${program} ${toString flags} < <(echo '(compile-file "asdf.lisp")')
103 '';
104 installPhase = ''
105 mkdir -p $out
106 cp -v asdf.${faslExt} $out
107 '';
108 };
109
110 #
111 # Wrapper around stdenv.mkDerivation for building ASDF systems.
112 #
113 build-asdf-system = makeOverridableLispPackage (
114 {
115 pname,
116 version,
117 src ? null,
118 patches ? [ ],
119
120 # Native libraries, will be appended to the library path
121 nativeLibs ? [ ],
122
123 # Java libraries for ABCL, will be appended to the class path
124 javaLibs ? [ ],
125
126 # Lisp dependencies
127 # these should be packages built with `build-asdf-system`
128 # TODO(kasper): use propagatedBuildInputs
129 lispLibs ? [ ],
130
131 # Derivation containing the CL implementation package
132 pkg,
133
134 # Name of the Lisp executable
135 program ? pkg.meta.mainProgram or pkg.pname,
136
137 # General flags to the Lisp executable
138 flags ? [ ],
139
140 # Extension for implementation-dependent FASL files
141 faslExt,
142
143 # ASDF amalgamation file to use
144 # Created in build/asdf.lisp by `make` in ASDF source tree
145 asdf,
146
147 # Some libraries have multiple systems under one project, for
148 # example, cffi has cffi-grovel, cffi-toolchain etc. By
149 # default, only the `pname` system is build.
150 #
151 # .asd's not listed in `systems` are removed in
152 # installPhase. This prevents asdf from referring to uncompiled
153 # systems on run time.
154 #
155 # Also useful when the pname is different than the system name,
156 # such as when using reverse domain naming.
157 systems ? [ pname ],
158
159 # The .asd files that this package provides
160 # TODO(kasper): remove
161 asds ? systems,
162
163 # Other args to mkDerivation
164 ...
165 }@args:
166
167 (
168 stdenv.mkDerivation (
169 rec {
170 inherit
171 version
172 nativeLibs
173 javaLibs
174 lispLibs
175 systems
176 asds
177 pkg
178 program
179 flags
180 faslExt
181 ;
182
183 # When src is null, we are building a lispWithPackages and only
184 # want to make use of the dependency environment variables
185 # generated by build-asdf-system
186 dontUnpack = src == null;
187
188 # Portable script to build the systems.
189 #
190 # `lisp` must evaluate this file then exit immediately. For
191 # example, SBCL's --script flag does just that.
192 #
193 # NOTE:
194 # Every other library worked fine with asdf:compile-system in
195 # buildScript.
196 #
197 # cl-syslog, for some reason, signals that CL-SYSLOG::VALID-SD-ID-P
198 # is undefined with compile-system, but works perfectly with
199 # load-system. Strange.
200
201 # TODO(kasper) portable quit
202 asdfFasl = buildAsdf {
203 inherit
204 asdf
205 pkg
206 program
207 flags
208 faslExt
209 ;
210 };
211
212 buildScript = replaceVars ./builder.lisp {
213 asdf = "${asdfFasl}/asdf.${faslExt}";
214 };
215
216 configurePhase = ''
217 runHook preConfigure
218
219 source ${./setup-hook.sh}
220 buildAsdfPath
221
222 runHook postConfigure
223 '';
224
225 buildPhase = optionalString (src != null) ''
226 runHook preBuild
227
228 export CL_SOURCE_REGISTRY=$CL_SOURCE_REGISTRY:$src//
229 export ASDF_OUTPUT_TRANSLATIONS="$src:$(pwd):${storeDir}:${storeDir}"
230 ${pkg}/bin/${program} ${toString flags} < $buildScript
231
232 runHook postBuild
233 '';
234
235 # Copy compiled files to store
236 #
237 # Make sure to include '$' in regex to prevent skipping
238 # stuff like 'iolib.asdf.asd' for system 'iolib.asd'
239 #
240 # Same with '/': `local-time.asd` for system `cl-postgres+local-time.asd`
241 installPhase =
242 let
243 mkSystemsRegex =
244 systems: concatMapStringsSep "\\|" (replaceStrings [ "." "+" ] [ "[.]" "[+]" ]) systems;
245 in
246 ''
247 runHook preInstall
248
249 mkdir -pv $out
250 cp -r * $out
251
252 # Remove all .asd files except for those in `systems`.
253 find $out -name "*.asd" \
254 | grep -v "/\(${mkSystemsRegex systems}\)\.asd$" \
255 | xargs rm -fv || true
256
257 runHook postInstall
258 '';
259
260 dontPatchShebangs = true;
261
262 # Not sure if it's needed, but caused problems with SBCL
263 # save-lisp-and-die binaries in the past
264 dontStrip = true;
265
266 }
267 // (
268 args
269 // (
270 let
271 isJVM = args.pkg.pname == "abcl";
272 javaLibs = lib.optionals isJVM args.javaLibs or [ ];
273 in
274 {
275 pname = "${args.pkg.pname}-${args.pname}";
276 src =
277 if args ? patches || args ? postPatch then
278 pkgs.applyPatches {
279 inherit (args) src;
280 patches = args.patches or [ ];
281 postPatch = args.postPatch or "";
282 }
283 else
284 args.src;
285 patches = [ ];
286 inherit javaLibs;
287 propagatedBuildInputs = args.propagatedBuildInputs or [ ] ++ lispLibs ++ javaLibs ++ nativeLibs;
288 meta = (args.meta or { }) // {
289 maintainers = args.meta.maintainers or [ ];
290 teams = args.meta.teams or [ lib.teams.lisp ];
291 };
292 }
293 )
294 )
295 )
296 // {
297 # Useful for overriding
298 # Overriding code would prefer to use pname from the attribute set
299 # However, pname is extended with the implementation name
300 # Moreover, it is used in the default list of systems to load
301 # So we pass the original pname
302 pname = args.pname;
303 }
304 )
305 );
306
307 # Build the set of lisp packages using `lisp`
308 # These packages are defined manually for one reason or another:
309 # - The library is not in quicklisp
310 # - The library that is in quicklisp is broken
311 # - Special build procedure such as cl-unicode, asdf
312 #
313 # They can use the auto-imported quicklisp packages as dependencies,
314 # but some of those don't work out of the box.
315 #
316 # E.g if a QL package depends on cl-unicode it won't build out of
317 # the box.
318 commonLispPackagesFor =
319 spec:
320 let
321 build-asdf-system' = body: build-asdf-system (body // spec);
322 in
323 pkgs.callPackage ./packages.nix {
324 inherit spec quicklispPackagesFor;
325 build-asdf-system = build-asdf-system';
326 };
327
328 # Build the set of packages imported from quicklisp using `lisp`
329 quicklispPackagesFor =
330 spec:
331 let
332 build-asdf-system' = body: build-asdf-system (body // spec);
333 in
334 pkgs.callPackage ./ql.nix {
335 build-asdf-system = build-asdf-system';
336 };
337
338 # Creates a lisp wrapper with `packages` installed
339 #
340 # `packages` is a function that takes `clpkgs` - a set of lisp
341 # packages - as argument and returns the list of packages to be
342 # installed
343 # TODO(kasper): assert each package has the same lisp and asdf?
344 lispWithPackages =
345 clpkgs: packages:
346 let
347 first = head (lib.attrValues clpkgs);
348 in
349 (build-asdf-system {
350 inherit (first)
351 pkg
352 program
353 flags
354 faslExt
355 asdf
356 ;
357 # See dontUnpack in build-asdf-system
358 src = null;
359 pname = "with";
360 version = "packages";
361 lispLibs = packages clpkgs;
362 systems = [ ];
363 }).overrideAttrs
364 (o: {
365 nativeBuildInputs = [ pkgs.makeBinaryWrapper ];
366 installPhase = ''
367 mkdir -pv $out/bin
368 makeWrapper \
369 ${o.pkg}/bin/${o.program} \
370 $out/bin/${o.program} \
371 --add-flags "${toString o.flags}" \
372 --set ASDF "${o.asdfFasl}/asdf.${o.faslExt}" \
373 --prefix CL_SOURCE_REGISTRY : "$CL_SOURCE_REGISTRY''${CL_SOURCE_REGISTRY:+:}" \
374 --prefix ASDF_OUTPUT_TRANSLATIONS : "$(echo $CL_SOURCE_REGISTRY | sed s,//:,::,g):" \
375 --prefix LD_LIBRARY_PATH : "$LD_LIBRARY_PATH" \
376 --prefix DYLD_LIBRARY_PATH : "$DYLD_LIBRARY_PATH" \
377 --prefix CLASSPATH : "$CLASSPATH" \
378 --prefix GI_TYPELIB_PATH : "$GI_TYPELIB_PATH" \
379 --prefix PATH : "${makeBinPath (o.propagatedBuildInputs or [ ])}"
380 '';
381 });
382
383 wrapLisp =
384 {
385 pkg,
386 faslExt,
387 program ? pkg.meta.mainProgram or pkg.pname,
388 flags ? [ ],
389 asdf ? pkgs.asdf_3_3,
390 packageOverrides ? (self: super: { }),
391 }:
392 let
393 spec = {
394 inherit
395 pkg
396 faslExt
397 program
398 flags
399 asdf
400 ;
401 };
402 pkgs = (commonLispPackagesFor spec).overrideScope packageOverrides;
403 withPackages = lispWithPackages pkgs;
404 withOverrides =
405 packageOverrides:
406 wrapLisp {
407 inherit
408 pkg
409 faslExt
410 program
411 flags
412 asdf
413 ;
414 inherit packageOverrides;
415 };
416 buildASDFSystem = args: build-asdf-system (args // spec);
417 in
418 pkg
419 // {
420 inherit
421 pkgs
422 withPackages
423 withOverrides
424 buildASDFSystem
425 ;
426 };
427
428in
429wrapLisp