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