1# Generic builder for lua packages
2{
3 lib,
4 lua,
5 wrapLua,
6 luarocks_bootstrap,
7 writeTextFile,
8
9 # Whether the derivation provides a lua module or not.
10 luarocksCheckHook,
11 luaLib,
12}:
13
14{
15 pname,
16 version,
17 # we need rockspecVersion to find the .rockspec even when version changes
18 rockspecVersion ? version,
19
20 # by default prefix `name` e.g. "lua5.2-${name}"
21 namePrefix ? "${lua.pname}${lib.versions.majorMinor lua.version}-",
22
23 # Dependencies for building the package
24 buildInputs ? [ ],
25
26 # Dependencies needed for running the checkPhase.
27 # These are added to nativeBuildInputs when doCheck = true.
28 nativeCheckInputs ? [ ],
29
30 # propagate build dependencies so in case we have A -> B -> C,
31 # C can import package A propagated by B
32 propagatedBuildInputs ? [ ],
33
34 # used to disable derivation, useful for specific lua versions
35 # TODO move from this setting meta.broken to a 'disabled' attribute on the
36 # package, then use that to skip/include in each lua${ver}Packages set?
37 disabled ? false,
38
39 # Additional arguments to pass to the makeWrapper function, which wraps
40 # generated binaries.
41 makeWrapperArgs ? [ ],
42
43 # Skip wrapping of lua programs altogether
44 dontWrapLuaPrograms ? false,
45 doCheck ? false,
46 # Non-Lua / system (e.g. C library) dependencies. Is a list of deps, where
47 # each dep is either a derivation, or an attribute set like
48 # { name = "rockspec external_dependencies key"; dep = derivation; }
49 # The latter is used to work-around luarocks having a problem with
50 # multiple-output derivations as external deps:
51 # https://github.com/luarocks/luarocks/issues/766<Paste>
52 externalDeps ? [ ],
53
54 # Appended to the generated luarocks config
55 extraConfig ? "",
56
57 # transparent mapping nix <-> lua used as LUAROCKS_CONFIG
58 # Refer to https://github.com/luarocks/luarocks/wiki/Config-file-format for specs
59 luarocksConfig ? { },
60
61 # relative to srcRoot, path to the rockspec to use when using rocks
62 rockspecFilename ? null,
63
64 # must be set for packages that don't have a rock
65 knownRockspec ? null,
66
67 ...
68}@attrs:
69
70# Keep extra attributes from `attrs`, e.g., `patchPhase', etc.
71
72let
73
74 # TODO fix warnings "Couldn't load rockspec for ..." during manifest
75 # construction -- from initial investigation, appears it will require
76 # upstream luarocks changes to fix cleanly (during manifest construction,
77 # luarocks only looks for rockspecs in the default/system tree instead of all
78 # configured trees)
79 luarocks_config = "luarocks-config.lua";
80
81 luarocksDrv = luaLib.toLuaModule (
82 lua.stdenv.mkDerivation (
83 self:
84 attrs
85 // {
86
87 name = namePrefix + self.pname + "-" + self.version;
88 inherit rockspecVersion;
89
90 __structuredAttrs = true;
91 env = {
92 LUAROCKS_CONFIG = self.configFile;
93 }
94 // attrs.env or { };
95
96 generatedRockspecFilename = "./${self.pname}-${self.rockspecVersion}.rockspec";
97
98 nativeBuildInputs = [
99 lua # for lua.h
100 wrapLua
101 luarocks_bootstrap
102 ];
103
104 inherit
105 doCheck
106 extraConfig
107 rockspecFilename
108 knownRockspec
109 externalDeps
110 nativeCheckInputs
111 ;
112
113 buildInputs =
114 let
115 # example externalDeps': [ { name = "CRYPTO"; dep = pkgs.openssl; } ]
116 externalDeps' = lib.filter (dep: !lib.isDerivation dep) self.externalDeps;
117 in
118 [ luarocks_bootstrap ]
119 ++ buildInputs
120 ++ lib.optionals self.doCheck ([ luarocksCheckHook ] ++ self.nativeCheckInputs)
121 ++ (map (d: d.dep) externalDeps');
122
123 # propagate lua to active setup-hook in nix-shell
124 propagatedBuildInputs = propagatedBuildInputs ++ [ lua ];
125
126 # @-patterns do not capture formal argument default values, so we need to
127 # explicitly inherit this for it to be available as a shell variable in the
128 # builder
129 rocksSubdir = "${self.pname}-${self.version}-rocks";
130
131 configFile = writeTextFile {
132 name = self.pname + "-luarocks-config.lua";
133 text = self.luarocks_content;
134 };
135
136 luarocks_content = (lib.generators.toLua { asBindings = true; } self.luarocksConfig) + ''
137
138 ${self.extraConfig}
139 '';
140
141 # TODO make it the default variable
142 luarocksConfig =
143 let
144 externalDepsGenerated = lib.filter (drv: !drv ? luaModule) (
145 self.nativeBuildInputs ++ self.propagatedBuildInputs ++ self.buildInputs
146 );
147
148 generatedConfig = luaLib.generateLuarocksConfig {
149 externalDeps = lib.unique (self.externalDeps ++ externalDepsGenerated);
150 local_cache = "";
151
152 # To prevent collisions when creating environments, we install the rock
153 # files into per-package subdirectories
154 rocks_subdir = self.rocksSubdir;
155
156 # Filter out the lua derivation itself from the Lua module dependency
157 # closure, as it doesn't have a rock tree :)
158 # luaLib.hasLuaModule
159 requiredLuaRocks = lib.filter luaLib.hasLuaModule (
160 lua.pkgs.requiredLuaModules (self.nativeBuildInputs ++ self.propagatedBuildInputs)
161 );
162 };
163
164 luarocksConfig' = lib.recursiveUpdate luarocksConfig (
165 lib.optionalAttrs (attrs ? extraVariables) (
166 lib.warn "extraVariables in buildLuarocksPackage is deprecated, use luarocksConfig instead" {
167 variables = attrs.extraVariables;
168 }
169 )
170 );
171 in
172 lib.recursiveUpdate generatedConfig luarocksConfig';
173
174 configurePhase = ''
175 runHook preConfigure
176 ''
177 + lib.optionalString (self.rockspecFilename == null) ''
178 rockspecFilename="${self.generatedRockspecFilename}"
179 ''
180 + lib.optionalString (self.knownRockspec != null) ''
181 # prevents the following type of error:
182 # Inconsistency between rockspec filename (42fm1b3d7iv6fcbhgm9674as3jh6y2sh-luv-1.22.0-1.rockspec) and its contents (luv-1.22.0-1.rockspec)
183 rockspecFilename="$TMP/$(stripHash ${self.knownRockspec})"
184 cp ${self.knownRockspec} "$rockspecFilename"
185 ''
186 + ''
187 runHook postConfigure
188 '';
189
190 buildPhase = ''
191 runHook preBuild
192
193 source ${lua}/nix-support/utils.sh
194 nix_debug "Using LUAROCKS_CONFIG=$LUAROCKS_CONFIG"
195
196 LUAROCKS_EXTRA_ARGS=""
197 if (( ''${NIX_DEBUG:-0} >= 1 )); then
198 LUAROCKS_EXTRA_ARGS=" --verbose"
199 fi
200
201 runHook postBuild
202 '';
203
204 postFixup =
205 lib.optionalString (!dontWrapLuaPrograms) ''
206 wrapLuaPrograms
207 ''
208 + attrs.postFixup or "";
209
210 installPhase = ''
211 runHook preInstall
212
213 # work around failing luarocks test for Write access
214 mkdir -p $out
215
216 # luarocks make assumes sources are available in cwd
217 # After the build is complete, it also installs the rock.
218 # If no argument is given, it looks for a rockspec in the current directory
219 # but some packages have several rockspecs in their source directory so
220 # we force the use of the upper level since it is
221 # the sole rockspec in that folder
222 # maybe we could reestablish dependency checking via passing --rock-trees
223
224 nix_debug "ROCKSPEC $rockspecFilename"
225 luarocks $LUAROCKS_EXTRA_ARGS make --deps-mode=all --tree=$out ''${rockspecFilename}
226
227 runHook postInstall
228 '';
229
230 checkPhase = ''
231 runHook preCheck
232 luarocks test
233 runHook postCheck
234 '';
235
236 shellHook = ''
237 runHook preShell
238 export LUAROCKS_CONFIG="$PWD/${luarocks_config}";
239 runHook postShell
240 '';
241
242 passthru = {
243 inherit lua;
244 }
245 // attrs.passthru or { };
246
247 meta = {
248 platforms = lua.meta.platforms;
249 # add extra maintainer(s) to every package
250 maintainers = (attrs.meta.maintainers or [ ]) ++ [ ];
251 broken = disabled;
252 }
253 // attrs.meta or { };
254 }
255 )
256 );
257in
258luarocksDrv