nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 stdenv,
3 lib,
4 buildPackages,
5 runCommand,
6 ruby,
7 defaultGemConfig,
8 buildRubyGem,
9 buildEnv,
10 makeBinaryWrapper,
11 bundler,
12}@defs:
13
14{
15 name ? null,
16 pname ? null,
17 version ? null,
18 mainGemName ? null,
19 gemdir ? null,
20 gemfile ? null,
21 lockfile ? null,
22 gemset ? null,
23 ruby ? defs.ruby,
24 copyGemFiles ? false, # Copy gem files instead of symlinking
25 gemConfig ? defaultGemConfig,
26 postBuild ? null,
27 document ? [ ],
28 meta ? { },
29 groups ? null,
30 ignoreCollisions ? false,
31 nativeBuildInputs ? [ ],
32 buildInputs ? [ ],
33 extraConfigPaths ? [ ],
34 passthru ? { },
35 ...
36}@args:
37
38assert name == null -> pname != null;
39
40let
41 functions = import ./functions.nix { inherit lib gemConfig; };
42
43 inherit (functions)
44 applyGemConfigs
45 bundlerFiles
46 composeGemAttrs
47 filterGemset
48 genStubsScript
49 pathDerivation
50 ;
51
52 gemFiles = bundlerFiles args;
53
54 importedGemset =
55 if builtins.typeOf gemFiles.gemset != "set" then import gemFiles.gemset else gemFiles.gemset;
56
57 filteredGemset = filterGemset { inherit ruby groups; } importedGemset;
58
59 configuredGemset = lib.flip lib.mapAttrs filteredGemset (
60 name: attrs:
61 applyGemConfigs (
62 attrs
63 // {
64 inherit ruby document;
65 gemName = name;
66 }
67 )
68 );
69
70 hasBundler = builtins.hasAttr "bundler" filteredGemset;
71
72 bundler =
73 if hasBundler then
74 gems.bundler
75 else
76 defs.bundler.override (attrs: {
77 inherit ruby;
78 });
79
80 gems = lib.flip lib.mapAttrs configuredGemset (name: attrs: buildGem name attrs);
81
82 version' =
83 if version != null then
84 version
85 else if pname != null then
86 gems.${pname}.suffix
87 else
88 null;
89
90 name' = if name != null then name else "${pname}-${version'}";
91
92 pname' = if pname != null then pname else name;
93
94 copyIfBundledByPath =
95 {
96 bundledByPath ? false,
97 ...
98 }:
99 (lib.optionalString bundledByPath (
100 assert gemFiles.gemdir != null;
101 "cp -a ${gemFiles.gemdir}/* $out/"
102 ) # */
103 );
104
105 maybeCopyAll =
106 pkgname:
107 lib.optionalString (pkgname != null) (
108 let
109 mainGem = gems.${pkgname} or (throw "bundlerEnv: gem ${pkgname} not found");
110 in
111 copyIfBundledByPath mainGem
112 );
113
114 # We have to normalize the Gemfile.lock, otherwise bundler tries to be
115 # helpful by doing so at run time, causing executables to immediately bail
116 # out. Yes, I'm serious.
117 confFiles = runCommand "gemfile-and-lockfile" { } ''
118 mkdir -p $out
119 ${maybeCopyAll mainGemName}
120 cp ${gemFiles.gemfile} $out/Gemfile || ls -l $out/Gemfile
121 cp ${gemFiles.lockfile} $out/Gemfile.lock || ls -l $out/Gemfile.lock
122
123 ${lib.concatMapStringsSep "\n" (path: "cp -r ${path} $out/") extraConfigPaths}
124 '';
125
126 buildGem =
127 name: attrs:
128 (
129 let
130 gemAttrs = composeGemAttrs ruby gems name attrs;
131 in
132 if gemAttrs.type == "path" then
133 pathDerivation (gemAttrs.source // gemAttrs)
134 else
135 buildRubyGem gemAttrs
136 );
137
138 envPaths = lib.attrValues gems ++ lib.optional (!hasBundler) bundler;
139
140 basicEnvArgs = {
141 inherit
142 nativeBuildInputs
143 buildInputs
144 ignoreCollisions
145 pname
146 ;
147
148 name = name';
149 version = version';
150
151 paths = envPaths;
152 pathsToLink = [ "/lib" ];
153
154 postBuild =
155 genStubsScript (
156 defs
157 // args
158 // {
159 inherit confFiles bundler groups;
160 binPaths = envPaths;
161 }
162 )
163 + lib.optionalString (postBuild != null) postBuild;
164
165 meta = {
166 platforms = ruby.meta.platforms;
167 } // meta;
168
169 passthru = (
170 lib.optionalAttrs (pname != null) {
171 inherit (gems.${pname}) gemType;
172 }
173 // rec {
174 inherit
175 ruby
176 bundler
177 gems
178 confFiles
179 envPaths
180 ;
181
182 wrappedRuby = stdenv.mkDerivation {
183 name = "wrapped-ruby-${pname'}";
184
185 nativeBuildInputs = [ makeBinaryWrapper ];
186
187 dontUnpack = true;
188
189 buildPhase = ''
190 mkdir -p $out/bin
191 for i in ${ruby}/bin/*; do
192 makeWrapper "$i" $out/bin/$(basename "$i") \
193 --set BUNDLE_GEMFILE ${confFiles}/Gemfile \
194 --unset BUNDLE_PATH \
195 --set BUNDLE_FROZEN 1 \
196 --set GEM_HOME ${basicEnv}/${ruby.gemPath} \
197 --set GEM_PATH ${basicEnv}/${ruby.gemPath}
198 done
199 '';
200
201 dontInstall = true;
202
203 doCheck = true;
204 checkPhase = ''
205 $out/bin/ruby --help > /dev/null
206 '';
207
208 inherit (ruby) meta;
209 };
210
211 env =
212 let
213 irbrc = builtins.toFile "irbrc" ''
214 if !(ENV["OLD_IRBRC"].nil? || ENV["OLD_IRBRC"].empty?)
215 require ENV["OLD_IRBRC"]
216 end
217 require 'rubygems'
218 require 'bundler/setup'
219 '';
220 in
221 stdenv.mkDerivation {
222 name = "${pname'}-interactive-environment";
223 nativeBuildInputs = [
224 wrappedRuby
225 basicEnv
226 ];
227 shellHook = ''
228 export OLD_IRBRC=$IRBRC
229 export IRBRC=${irbrc}
230 '';
231 buildCommand = ''
232 echo >&2 ""
233 echo >&2 "*** Ruby 'env' attributes are intended for interactive nix-shell sessions, not for building! ***"
234 echo >&2 ""
235 exit 1
236 '';
237 };
238 }
239 // passthru
240 );
241 };
242
243 basicEnv =
244 if copyGemFiles then
245 runCommand name' basicEnvArgs ''
246 mkdir -p $out
247 for i in $paths; do
248 ${buildPackages.rsync}/bin/rsync -a $i/lib $out/
249 done
250 eval "$postBuild"
251 ''
252 else
253 buildEnv basicEnvArgs;
254in
255basicEnv