nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 stdenv,
3 lib,
4 elixir,
5 erlang,
6 hex,
7 git,
8 rebar,
9 rebar3,
10 fetchMixDeps,
11 findutils,
12 ripgrep,
13 bbe,
14 makeWrapper,
15 coreutils,
16 gnused,
17 gnugrep,
18 gawk,
19}@inputs:
20
21{
22 pname,
23 version,
24 src,
25 nativeBuildInputs ? [ ],
26 buildInputs ? [ ],
27 meta ? { },
28 enableDebugInfo ? false,
29 mixEnv ? "prod",
30 mixTarget ? "host",
31 compileFlags ? [ ],
32 # Build a particular named release.
33 # see https://hexdocs.pm/mix/1.12/Mix.Tasks.Release.html#content
34 mixReleaseName ? "",
35 # If set, the given escript binary will be copied to the output
36 # instead of the release
37 escriptBinName ? null,
38
39 # Options to be passed to the Erlang compiler. As documented in the reference
40 # manual, these must be valid Erlang terms. They will be turned into an
41 # erlang list and set as the ERL_COMPILER_OPTIONS environment variable.
42 # See https://www.erlang.org/doc/man/compile
43 erlangCompilerOptions ? [ ],
44
45 # Deterministic Erlang builds remove full system paths from debug information
46 # among other things to keep builds more reproducible. See their docs for more:
47 # https://www.erlang.org/doc/man/compile
48 erlangDeterministicBuilds ? true,
49
50 # Mix dependencies provided as a fixed output derivation
51 mixFodDeps ? null,
52
53 # Mix dependencies generated by mix2nix
54 #
55 # This assumes each dependency is built by buildMix or buildRebar3. Each
56 # dependency needs to have a setup hook to add the lib path to $ERL_LIBS.
57 # This is how Mix finds dependencies.
58 mixNixDeps ? { },
59
60 elixir ? inputs.elixir,
61 erlang ? inputs.erlang,
62 hex ? inputs.hex.override { inherit elixir; },
63
64 # Remove releases/COOKIE
65 #
66 # People have different views on the nature of cookies. Some believe that they are
67 # secrets, while others believe they are just ids for clustering nodes instead of
68 # secrets.
69 #
70 # If you think cookie is secret, you can set this attr to true, then it will be
71 # removed from nix store. If not, you can set it to false.
72 #
73 # For backward compatibility, it is set to true by default.
74 #
75 # You can always specify a custom cookie by using RELEASE_COOKIE environment
76 # variable, regardless of the value of this attr.
77 removeCookie ? true,
78
79 # This reduces closure size, but can lead to some hard to understand runtime
80 # errors, so use with caution. See e.g.
81 # https://github.com/whitfin/cachex/issues/205
82 # https://framagit.org/framasoft/mobilizon/-/issues/1169
83 stripDebug ? false,
84
85 ...
86}@attrs:
87let
88 # Remove non standard attributes that cannot be coerced to strings
89 overridable = removeAttrs attrs [
90 "compileFlags"
91 "erlangCompilerOptions"
92 "mixNixDeps"
93 ];
94in
95assert mixNixDeps != { } -> mixFodDeps == null;
96assert stripDebug -> !enableDebugInfo;
97assert escriptBinName != null -> mixReleaseName == "";
98
99stdenv.mkDerivation (
100 overridable
101 // {
102 nativeBuildInputs =
103 nativeBuildInputs
104 ++
105 # Erlang/Elixir deps
106 [
107 erlang
108 elixir
109 hex
110 git
111 ]
112 ++
113 # Mix deps
114 (builtins.attrValues mixNixDeps)
115 ++
116 # other compile-time deps
117 [
118 findutils
119 ripgrep
120 bbe
121 makeWrapper
122 ];
123
124 buildInputs = buildInputs ++ lib.optionals (escriptBinName != null) [ erlang ];
125
126 MIX_ENV = mixEnv;
127 MIX_TARGET = mixTarget;
128 MIX_BUILD_PREFIX = (if mixTarget == "host" then "" else "${mixTarget}_") + "${mixEnv}";
129 MIX_DEBUG = if enableDebugInfo then 1 else 0;
130 HEX_OFFLINE = 1;
131
132 __darwinAllowLocalNetworking = true;
133
134 DEBUG = if enableDebugInfo then 1 else 0; # for Rebar3 compilation
135 # The API with `mix local.rebar rebar path` makes a copy of the binary
136 # some older dependencies still use rebar.
137 MIX_REBAR = "${rebar}/bin/rebar";
138 MIX_REBAR3 = "${rebar3}/bin/rebar3";
139
140 ERL_COMPILER_OPTIONS =
141 let
142 options = erlangCompilerOptions ++ lib.optionals erlangDeterministicBuilds [ "deterministic" ];
143 in
144 "[${lib.concatStringsSep "," options}]";
145
146 LANG = if stdenv.hostPlatform.isLinux then "C.UTF-8" else "C";
147 LC_CTYPE = if stdenv.hostPlatform.isLinux then "C.UTF-8" else "UTF-8";
148
149 postUnpack = ''
150 # Mix and Hex
151 export MIX_HOME="$TEMPDIR/mix"
152 export HEX_HOME="$TEMPDIR/hex"
153
154 # Rebar
155 export REBAR_GLOBAL_CONFIG_DIR="$TEMPDIR/rebar3"
156 export REBAR_CACHE_DIR="$TEMPDIR/rebar3.cache"
157
158 ${lib.optionalString (mixFodDeps != null) ''
159 # Compilation of the dependencies will require that the dependency path is
160 # writable, thus a copy to the $TEMPDIR is inevitable here.
161 export MIX_DEPS_PATH="$TEMPDIR/deps"
162 cp --no-preserve=mode -R "${mixFodDeps}" "$MIX_DEPS_PATH"
163 ''}
164 ''
165 + (attrs.postUnpack or "");
166
167 configurePhase =
168 attrs.configurePhase or ''
169 runHook preConfigure
170
171 ${./mix-configure-hook.sh}
172
173 # This is needed for projects that have a specific compile step
174 # the dependency needs to be compiled in order for the task
175 # to be available.
176 #
177 # Phoenix projects for example will need compile.phoenix.
178 mix deps.compile --no-deps-check --skip-umbrella-children
179
180 # Symlink dependency sources. This is needed for projects that require
181 # access to the source of their dependencies. For example, Phoenix
182 # projects need javascript assets to build asset bundles.
183 ${lib.optionalString (mixNixDeps != { }) ''
184 mkdir -p deps
185
186 ${lib.concatMapStringsSep "\n" (dep: ''
187 dep_name=$(basename ${dep} | cut -d '-' -f2)
188 dep_path="deps/$dep_name"
189 if [ -d "${dep}/src" ]; then
190 ln -s ${dep}/src $dep_path
191 fi
192 '') (builtins.attrValues mixNixDeps)}
193 ''}
194
195 # Symlink deps to build root. Similar to above, but allows for mixFodDeps
196 # Phoenix projects to find javascript assets.
197 ${lib.optionalString (mixFodDeps != null) ''
198 ln -s "$MIX_DEPS_PATH" ./deps
199 ''}
200
201 runHook postConfigure
202 '';
203
204 buildPhase =
205 attrs.buildPhase or ''
206 runHook preBuild
207
208 mix compile --no-deps-check ${lib.concatStringsSep " " compileFlags}
209
210 ${lib.optionalString (escriptBinName != null) ''
211 mix escript.build --no-deps-check
212 ''}
213
214 runHook postBuild
215 '';
216
217 installPhase =
218 attrs.installPhase or ''
219 runHook preInstall
220
221 ${
222 if (escriptBinName != null) then
223 ''
224 mkdir -p $out/bin
225 cp ${escriptBinName} $out/bin
226 ''
227 else
228 ''
229 mix release ${mixReleaseName} --no-deps-check --path "$out"
230 ''
231 }
232
233 runHook postInstall
234 '';
235
236 postFixup = ''
237 echo "removing files for Microsoft Windows"
238 rm -f "$out"/bin/*.bat
239
240 echo "wrapping programs in $out/bin with their runtime deps"
241 for f in $(find $out/bin/ -type f -executable); do
242 wrapProgram "$f" \
243 --prefix PATH : ${
244 lib.makeBinPath [
245 coreutils
246 gnused
247 gnugrep
248 gawk
249 ]
250 }
251 done
252 ''
253 + lib.optionalString removeCookie ''
254 if [ -e $out/releases/COOKIE ]; then
255 echo "removing $out/releases/COOKIE"
256 rm $out/releases/COOKIE
257 fi
258 ''
259 + ''
260 if [ -e $out/erts-* ]; then
261 # ERTS is included in the release, then erlang is not required as a runtime dependency.
262 # But, erlang is still referenced in some places. To removed references to erlang,
263 # following steps are required.
264
265 # 1. remove references to erlang from plain text files
266 for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches); do
267 echo "removing references to erlang in $file"
268 substituteInPlace "$file" --replace "${erlang}/lib/erlang" "$out"
269 done
270
271 # 2. remove references to erlang from .beam files
272 #
273 # No need to do anything, because it has been handled by "deterministic" option specified
274 # by ERL_COMPILER_OPTIONS.
275
276 # 3. remove references to erlang from normal binary files
277 for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches --binary --iglob '!*.beam'); do
278 echo "removing references to erlang in $file"
279 # use bbe to substitute strings in binary files, because using substituteInPlace
280 # on binaries will raise errors
281 bbe -e "s|${erlang}/lib/erlang|$out|" -o "$file".tmp "$file"
282 rm -f "$file"
283 mv "$file".tmp "$file"
284 done
285
286 # References to erlang should be removed from output after above processing.
287 fi
288 ''
289 + lib.optionalString stripDebug ''
290 # Strip debug symbols to avoid hardreferences to "foreign" closures actually
291 # not needed at runtime, while at the same time reduce size of BEAM files.
292 erl -noinput -eval 'lists:foreach(fun(F) -> io:format("Stripping ~p.~n", [F]), beam_lib:strip(F) end, filelib:wildcard("'"$out"'/**/*.beam"))' -s init stop
293 '';
294 }
295)