1{ stdenv, lib, elixir, erlang, findutils, hex, rebar, rebar3, fetchMixDeps, makeWrapper, git, ripgrep }@inputs:
2
3{ pname
4, version
5, src
6, nativeBuildInputs ? [ ]
7, buildInputs ? [ ]
8, meta ? { }
9, enableDebugInfo ? false
10, mixEnv ? "prod"
11, compileFlags ? [ ]
12 # mix fixed output derivation dependencies
13, mixFodDeps ? null
14 # mix dependencies generated by mix2nix
15 # this assumes each dependency is built by buildMix or buildRebar3
16 # each dependency needs to have a setup hook to add the lib path to $ERL_LIBS
17 # this is how mix will find dependencies
18, mixNixDeps ? { }
19, elixir ? inputs.elixir
20, hex ? inputs.hex.override { inherit elixir; }
21, ...
22}@attrs:
23let
24 # remove non standard attributes that cannot be coerced to strings
25 overridable = builtins.removeAttrs attrs [ "compileFlags" "mixNixDeps" ];
26in
27assert mixNixDeps != { } -> mixFodDeps == null;
28stdenv.mkDerivation (overridable // {
29 # rg is used as a better grep to search for erlang references in the final release
30 nativeBuildInputs = nativeBuildInputs ++ [ erlang hex elixir makeWrapper git ripgrep ];
31 buildInputs = buildInputs ++ builtins.attrValues mixNixDeps;
32
33 MIX_ENV = mixEnv;
34 MIX_DEBUG = if enableDebugInfo then 1 else 0;
35 HEX_OFFLINE = 1;
36 DEBUG = if enableDebugInfo then 1 else 0; # for Rebar3 compilation
37 # the api with `mix local.rebar rebar path` makes a copy of the binary
38 # some older dependencies still use rebar
39 MIX_REBAR = "${rebar}/bin/rebar";
40 MIX_REBAR3 = "${rebar3}/bin/rebar3";
41
42 postUnpack = ''
43 export HEX_HOME="$TEMPDIR/hex"
44 export MIX_HOME="$TEMPDIR/mix"
45
46 # Rebar
47 export REBAR_GLOBAL_CONFIG_DIR="$TEMPDIR/rebar3"
48 export REBAR_CACHE_DIR="$TEMPDIR/rebar3.cache"
49
50 ${lib.optionalString (mixFodDeps != null) ''
51 # compilation of the dependencies will require
52 # that the dependency path is writable
53 # thus a copy to the TEMPDIR is inevitable here
54 export MIX_DEPS_PATH="$TEMPDIR/deps"
55 cp --no-preserve=mode -R "${mixFodDeps}" "$MIX_DEPS_PATH"
56 ''
57 }
58
59 '' + (attrs.postUnpack or "");
60
61 configurePhase = attrs.configurePhase or ''
62 runHook preConfigure
63
64 ${./mix-configure-hook.sh}
65 # this is needed for projects that have a specific compile step
66 # the dependency needs to be compiled in order for the task
67 # to be available
68 # Phoenix projects for example will need compile.phoenix
69 mix deps.compile --no-deps-check --skip-umbrella-children
70
71 runHook postConfigure
72 '';
73
74 buildPhase = attrs.buildPhase or ''
75 runHook preBuild
76
77 mix compile --no-deps-check ${lib.concatStringsSep " " compileFlags}
78
79 runHook postBuild
80 '';
81
82
83 installPhase = attrs.installPhase or ''
84 runHook preInstall
85
86 mix release --no-deps-check --path "$out"
87
88 runHook postInstall
89 '';
90
91 # Stripping of the binary is intentional
92 # even though it does not affect beam files
93 # it is necessary for NIFs binaries
94 postFixup = ''
95 if [ -e "$out/bin/${pname}.bat" ]; then # absent in special cases, i.e. elixir-ls
96 rm "$out/bin/${pname}.bat" # windows file
97 fi
98 # contains secrets and should not be in the nix store
99 # TODO document how to handle RELEASE_COOKIE
100 # secrets should not be in the nix store.
101 # This is only used for connecting multiple nodes
102 if [ -e $out/releases/COOKIE ]; then # absent in special cases, i.e. elixir-ls
103 rm $out/releases/COOKIE
104 fi
105 # removing unused erlang reference from resulting derivation to reduce
106 # closure size
107 if [ -e $out/erts-* ]; then
108 echo "ERTS found in $out - removing references to erlang to reduce closure size"
109 # there is a link in $out/erts-*/bin/start always
110 # TODO:
111 # sometimes there are links in dependencies like bcrypt compiled binaries
112 # at the moment those are not removed since substituteInPlace will
113 # error on binaries
114 for file in $(rg "${erlang}/lib/erlang" "$out" --files-with-matches); do
115 echo "removing reference to erlang in $file"
116 substituteInPlace "$file" --replace "${erlang}/lib/erlang" "$out"
117 done
118 fi
119 '';
120
121 # TODO investigate why the resulting closure still has
122 # a reference to erlang.
123 # uncommenting the following will fail the build
124 # disallowedReferences = [ erlang ];
125})