1{ stdenv, runCommand, ruby, lib
2, defaultGemConfig, buildRubyGem, buildEnv
3, makeWrapper
4, bundler
5}@defs:
6
7{
8 name ? null
9, pname ? null
10, mainGemName ? null
11, gemdir ? null
12, gemfile ? null
13, lockfile ? null
14, gemset ? null
15, ruby ? defs.ruby
16, gemConfig ? defaultGemConfig
17, postBuild ? null
18, document ? []
19, meta ? {}
20, groups ? ["default"]
21, ignoreCollisions ? false
22, ...
23}@args:
24
25assert name == null -> pname != null;
26
27with import ./functions.nix { inherit lib gemConfig; };
28
29let
30 gemFiles = bundlerFiles args;
31
32 importedGemset = if builtins.typeOf gemFiles.gemset != "set"
33 then import gemFiles.gemset
34 else gemFiles.gemset;
35
36 filteredGemset = filterGemset { inherit ruby groups; } importedGemset;
37
38 configuredGemset = lib.flip lib.mapAttrs filteredGemset (name: attrs:
39 applyGemConfigs (attrs // { inherit ruby; gemName = name; })
40 );
41
42 hasBundler = builtins.hasAttr "bundler" filteredGemset;
43
44 bundler =
45 if hasBundler then gems.bundler
46 else defs.bundler.override (attrs: { inherit ruby; });
47
48 gems = lib.flip lib.mapAttrs configuredGemset (name: attrs: buildGem name attrs);
49
50 name' = if name != null then
51 name
52 else
53 let
54 gem = gems."${pname}";
55 version = gem.version;
56 in
57 "${pname}-${version}";
58
59 pname' = if pname != null then
60 pname
61 else
62 name;
63
64 copyIfBundledByPath = { bundledByPath ? false, ...}:
65 (if bundledByPath then
66 assert gemFiles.gemdir != null; "cp -a ${gemFiles.gemdir}/* $out/" #*/
67 else ""
68 );
69
70 maybeCopyAll = pkgname: if pkgname == null then "" else
71 let
72 mainGem = gems."${pkgname}" or (throw "bundlerEnv: gem ${pkgname} not found");
73 in
74 copyIfBundledByPath mainGem;
75
76 # We have to normalize the Gemfile.lock, otherwise bundler tries to be
77 # helpful by doing so at run time, causing executables to immediately bail
78 # out. Yes, I'm serious.
79 confFiles = runCommand "gemfile-and-lockfile" {} ''
80 mkdir -p $out
81 ${maybeCopyAll mainGemName}
82 cp ${gemFiles.gemfile} $out/Gemfile || ls -l $out/Gemfile
83 cp ${gemFiles.lockfile} $out/Gemfile.lock || ls -l $out/Gemfile.lock
84 '';
85
86 buildGem = name: attrs: (
87 let
88 gemAttrs = composeGemAttrs ruby gems name attrs;
89 in
90 if gemAttrs.type == "path" then
91 pathDerivation gemAttrs
92 else
93 buildRubyGem gemAttrs
94 );
95
96 envPaths = lib.attrValues gems ++ lib.optional (!hasBundler) bundler;
97
98 basicEnv = buildEnv {
99 inherit ignoreCollisions;
100
101 name = name';
102
103 paths = envPaths;
104 pathsToLink = [ "/lib" ];
105
106 postBuild = genStubsScript (defs // args // {
107 inherit confFiles bundler groups;
108 binPaths = envPaths;
109 }) + lib.optionalString (postBuild != null) postBuild;
110
111 meta = { platforms = ruby.meta.platforms; } // meta;
112
113 passthru = rec {
114 inherit ruby bundler gems confFiles envPaths;
115
116 wrappedRuby = stdenv.mkDerivation {
117 name = "wrapped-ruby-${pname'}";
118 nativeBuildInputs = [ makeWrapper ];
119 buildCommand = ''
120 mkdir -p $out/bin
121 for i in ${ruby}/bin/*; do
122 makeWrapper "$i" $out/bin/$(basename "$i") \
123 --set BUNDLE_GEMFILE ${confFiles}/Gemfile \
124 --set BUNDLE_PATH ${basicEnv}/${ruby.gemPath} \
125 --set BUNDLE_FROZEN 1 \
126 --set GEM_HOME ${basicEnv}/${ruby.gemPath} \
127 --set GEM_PATH ${basicEnv}/${ruby.gemPath}
128 done
129 '';
130 };
131
132 env = let
133 irbrc = builtins.toFile "irbrc" ''
134 if !(ENV["OLD_IRBRC"].nil? || ENV["OLD_IRBRC"].empty?)
135 require ENV["OLD_IRBRC"]
136 end
137 require 'rubygems'
138 require 'bundler/setup'
139 '';
140 in stdenv.mkDerivation {
141 name = "${pname'}-interactive-environment";
142 nativeBuildInputs = [ wrappedRuby basicEnv ];
143 shellHook = ''
144 export OLD_IRBRC=$IRBRC
145 export IRBRC=${irbrc}
146 '';
147 buildCommand = ''
148 echo >&2 ""
149 echo >&2 "*** Ruby 'env' attributes are intended for interactive nix-shell sessions, not for building! ***"
150 echo >&2 ""
151 exit 1
152 '';
153 };
154 };
155 };
156in
157 basicEnv