summary refs log tree commit diff
path: root/pkgs/development/interpreters/lua-5/build-lua-package.nix
blob: be7aa5fc09d789b24507da488baefaf01aa5a2f4 (plain) (blame)
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
# Generic builder for lua packages
{ lib
, lua
, wrapLua
# Whether the derivation provides a lua module or not.
, toLuaModule
}:

{
name ? "${attrs.pname}-${attrs.version}"

, version

# by default prefix `name` e.g. "lua5.2-${name}"
, namePrefix ? if lua.pkgs.isLuaJIT
               then lua.name + "-"
               else "lua" + lua.luaversion + "-"

# Dependencies for building the package
, buildInputs ? []

# Dependencies needed for running the checkPhase.
# These are added to buildInputs when doCheck = true.
, checkInputs ? []

# propagate build dependencies so in case we have A -> B -> C,
# C can import package A propagated by B
, propagatedBuildInputs ? []
, propagatedNativeBuildInputs ? []

# used to disable derivation, useful for specific lua versions
# TODO move from this setting meta.broken to a 'disabled' attribute on the
# package, then use that to skip/include in each lua${ver}Packages set?
, disabled ? false

# Additional arguments to pass to the makeWrapper function, which wraps
# generated binaries.
, makeWrapperArgs ? []

# Skip wrapping of lua programs altogether
, dontWrapLuaPrograms ? false

, meta ? {}

, passthru ? {}
, doCheck ? false

# Non-Lua / system (e.g. C library) dependencies. Is a list of deps, where
# each dep is either a derivation, or an attribute set like
# { name = "rockspec external_dependencies key"; dep = derivation; }
# The latter is used to work-around luarocks having a problem with
# multiple-output derivations as external deps:
# https://github.com/luarocks/luarocks/issues/766<Paste>
, externalDeps ? lib.unique (lib.filter (drv: !drv ? luaModule) (propagatedBuildInputs ++ buildInputs))

# Appended to the generated luarocks config
, extraConfig ? ""
# Inserted into the generated luarocks config in the "variables" table
, extraVariables ? ""
# The two above arguments have access to builder variables -- e.g. to $out

# relative to srcRoot, path to the rockspec to use when using rocks
, rockspecFilename ?  "../*.rockspec"

# must be set for packages that don't have a rock
, knownRockspec ? null

, ... } @ attrs:


# Keep extra attributes from `attrs`, e.g., `patchPhase', etc.

let
  # TODO fix warnings "Couldn't load rockspec for ..." during manifest
  # construction -- from initial investigation, appears it will require
  # upstream luarocks changes to fix cleanly (during manifest construction,
  # luarocks only looks for rockspecs in the default/system tree instead of all
  # configured trees)
  luarocks_config = "luarocks-config.lua";
  luarocks_content = ''
    local_cache = ""
    -- To prevent collisions when creating environments, we install the rock
    -- files into per-package subdirectories
    rocks_subdir = '${rocksSubdir}'
    -- Then we need to tell luarocks where to find the rock files per
    -- dependency
    rocks_trees = {
      ${lib.concatStringsSep "\n, " rocksTrees}
    }
  '' + lib.optionalString lua.pkgs.isLuaJIT ''
    -- Luajit provides some additional functionality built-in; this exposes
    -- that to luarock's dependency system
    rocks_provided = {
      jit='${lua.luaversion}-1';
      ffi='${lua.luaversion}-1';
      luaffi='${lua.luaversion}-1';
      bit='${lua.luaversion}-1';
    }
  '' + ''
    -- For single-output external dependencies
    external_deps_dirs = {
      ${lib.concatStringsSep "\n, " externalDepsDirs}
    }
    variables = {
      -- Some needed machinery to handle multiple-output external dependencies,
      -- as per https://github.com/luarocks/luarocks/issues/766
      ${lib.optionalString (lib.length depVariables > 0) ''
      ${lib.concatStringsSep "\n  " depVariables}''}
      ${extraVariables}
    }
    ${extraConfig}
  '';

  rocksSubdir = "${attrs.pname}-${version}-rocks";

  externalDepsDirs = map
    (x: "'${builtins.toString x}'")
    (lib.filter (lib.isDerivation) externalDeps);

  rocksTrees = lib.imap0
    (i: dep: "{ name = [[dep-${toString i}]], root = '${dep}', rocks_dir = '${dep}/${dep.rocksSubdir}' }")
    requiredLuaRocks;

  # Filter out the lua derivation itself from the Lua module dependency
  # closure, as it doesn't have a rock tree :)
  requiredLuaRocks = lib.filter (d: d ? luaModule)
    (lua.pkgs.requiredLuaModules propagatedBuildInputs);

  # Explicitly point luarocks to the relevant locations for multiple-output
  # derivations that are external dependencies, to work around an issue it has
  # (https://github.com/luarocks/luarocks/issues/766)
  depVariables = lib.concatMap ({name, dep}: [
    "${name}_INCDIR='${lib.getDev dep}/include';"
    "${name}_LIBDIR='${lib.getLib dep}/lib';"
    "${name}_BINDIR='${lib.getBin dep}/bin';"
  ]) externalDeps';

  # example externalDeps': [ { name = "CRYPTO"; dep = pkgs.openssl; } ]
  externalDeps' = lib.filter (dep: !lib.isDerivation dep) externalDeps;
in
toLuaModule ( lua.stdenv.mkDerivation (
builtins.removeAttrs attrs ["disabled" "checkInputs" "externalDeps"] // {

  name = namePrefix + name;

  buildInputs = [ wrapLua lua.pkgs.luarocks ]
    ++ buildInputs
    ++ lib.optionals doCheck checkInputs
    ++ (map (d: d.dep) externalDeps')
    ;

  # propagate lua to active setup-hook in nix-shell
  propagatedBuildInputs = propagatedBuildInputs ++ [ lua ];
  inherit doCheck;

  # @-patterns do not capture formal argument default values, so we need to
  # explicitly inherit this for it to be available as a shell variable in the
  # builder
  inherit rockspecFilename;
  inherit rocksSubdir;

  # enabled only for src.rock
  setSourceRoot= let
    name_only= lib.getName name;
  in
    lib.optionalString (knownRockspec == null) ''
    # format is rockspec_basename/source_basename
    # rockspec can set it via spec.source.dir
    folder=$(find . -mindepth 2 -maxdepth 2 -type d -path '*${name_only}*/*'|head -n1)
    sourceRoot="$folder"
  '';

  configurePhase = ''
    runHook preConfigure

    cat > ${luarocks_config} <<EOF
    ${luarocks_content}
    EOF
    export LUAROCKS_CONFIG="$PWD/${luarocks_config}";
  ''
  + lib.optionalString (knownRockspec != null) ''

    # prevents the following type of error:
    # Inconsistency between rockspec filename (42fm1b3d7iv6fcbhgm9674as3jh6y2sh-luv-1.22.0-1.rockspec) and its contents (luv-1.22.0-1.rockspec)
    rockspecFilename="$TMP/$(stripHash ''${knownRockspec})"
    cp ''${knownRockspec} "$rockspecFilename"
  ''
  + ''
    runHook postConfigure
  '';

  buildPhase = ''
    runHook preBuild

    nix_debug "Using LUAROCKS_CONFIG=$LUAROCKS_CONFIG"

    LUAROCKS=luarocks
    if (( ''${NIX_DEBUG:-0} >= 1 )); then
        LUAROCKS="$LUAROCKS --verbose"
    fi

    patchShebangs .

    runHook postBuild
  '';

  postFixup = lib.optionalString (!dontWrapLuaPrograms) ''
    wrapLuaPrograms
  '' + attrs.postFixup or '''';

  installPhase = attrs.installPhase or ''
    runHook preInstall

    # work around failing luarocks test for Write access
    mkdir -p $out

    # luarocks make assumes sources are available in cwd
    # After the build is complete, it also installs the rock.
    # If no argument is given, it looks for a rockspec in the current directory
    # but some packages have several rockspecs in their source directory so
    # we force the use of the upper level since it is
    # the sole rockspec in that folder
    # maybe we could reestablish dependency checking via passing --rock-trees

    nix_debug "ROCKSPEC $rockspecFilename"
    nix_debug "cwd: $PWD"
    $LUAROCKS make --deps-mode=all --tree=$out ''${rockspecFilename}

    runHook postInstall
  '';

  passthru = {
    inherit lua; # The lua interpreter
    inherit externalDeps;
  } // passthru;

  meta = with lib.maintainers; {
    platforms = lua.meta.platforms;
    # add extra maintainer(s) to every package
    maintainers = (meta.maintainers or []) ++ [ ];
    broken = disabled;
  } // meta;
}))