Merge pull request #268619 from tweag/lib-descriptions
[NixPkgs.git] / pkgs / development / ruby-modules / bundled-common / default.nix
blob86c885b52dcb4ca1da5bec2125bc781567e34f6a
1 { stdenv, runCommand, ruby, lib, rsync
2 , defaultGemConfig, buildRubyGem, buildEnv
3 , makeBinaryWrapper
4 , bundler
5 }@defs:
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 , copyGemFiles ? false # Copy gem files instead of symlinking
17 , gemConfig ? defaultGemConfig
18 , postBuild ? null
19 , document ? []
20 , meta ? {}
21 , groups ? null
22 , ignoreCollisions ? false
23 , nativeBuildInputs ? []
24 , buildInputs ? []
25 , extraConfigPaths ? []
26 , ...
27 }@args:
29 assert name == null -> pname != null;
31 with  import ./functions.nix { inherit lib gemConfig; };
33 let
34   gemFiles = bundlerFiles args;
36   importedGemset = if builtins.typeOf gemFiles.gemset != "set"
37     then import gemFiles.gemset
38     else gemFiles.gemset;
40   filteredGemset = filterGemset { inherit ruby groups; } importedGemset;
42   configuredGemset = lib.flip lib.mapAttrs filteredGemset (name: attrs:
43     applyGemConfigs (attrs // { inherit ruby document; gemName = name; })
44   );
46   hasBundler = builtins.hasAttr "bundler" filteredGemset;
48   bundler =
49     if hasBundler then gems.bundler
50     else defs.bundler.override (attrs: { inherit ruby; });
52   gems = lib.flip lib.mapAttrs configuredGemset (name: attrs: buildGem name attrs);
54   name' = if name != null then
55     name
56   else
57     let
58       gem = gems.${pname};
59       suffix = gem.suffix;
60     in
61       "${pname}-${suffix}";
63   pname' = if pname != null then
64     pname
65   else
66     name;
68   copyIfBundledByPath = { bundledByPath ? false, ...}:
69   (lib.optionalString bundledByPath (
70       assert gemFiles.gemdir != null; "cp -a ${gemFiles.gemdir}/* $out/") #*/
71   );
73   maybeCopyAll = pkgname: lib.optionalString (pkgname != null) (
74     let
75       mainGem = gems.${pkgname} or (throw "bundlerEnv: gem ${pkgname} not found");
76     in
77       copyIfBundledByPath mainGem
78   );
80   # We have to normalize the Gemfile.lock, otherwise bundler tries to be
81   # helpful by doing so at run time, causing executables to immediately bail
82   # out. Yes, I'm serious.
83   confFiles = runCommand "gemfile-and-lockfile" {} ''
84     mkdir -p $out
85     ${maybeCopyAll mainGemName}
86     cp ${gemFiles.gemfile} $out/Gemfile || ls -l $out/Gemfile
87     cp ${gemFiles.lockfile} $out/Gemfile.lock || ls -l $out/Gemfile.lock
89     ${lib.concatMapStringsSep "\n" (path: "cp -r ${path} $out/") extraConfigPaths}
90   '';
92   buildGem = name: attrs: (
93     let
94       gemAttrs = composeGemAttrs ruby gems name attrs;
95     in
96     if gemAttrs.type == "path" then
97       pathDerivation (gemAttrs.source // gemAttrs)
98     else
99       buildRubyGem gemAttrs
100   );
102   envPaths = lib.attrValues gems ++ lib.optional (!hasBundler) bundler;
105   basicEnvArgs = {
106     inherit nativeBuildInputs buildInputs ignoreCollisions;
108     name = name';
110     paths = envPaths;
111     pathsToLink = [ "/lib" ];
113     postBuild = genStubsScript (defs // args // {
114       inherit confFiles bundler groups;
115       binPaths = envPaths;
116     }) + lib.optionalString (postBuild != null) postBuild;
118     meta = { platforms = ruby.meta.platforms; } // meta;
120     passthru = rec {
121       inherit ruby bundler gems confFiles envPaths;
122       inherit (gems.${pname}) gemType;
124       wrappedRuby = stdenv.mkDerivation {
125         name = "wrapped-ruby-${pname'}";
127         nativeBuildInputs = [ makeBinaryWrapper ];
129         dontUnpack = true;
131         buildPhase = ''
132           mkdir -p $out/bin
133           for i in ${ruby}/bin/*; do
134             makeWrapper "$i" $out/bin/$(basename "$i") \
135               --set BUNDLE_GEMFILE ${confFiles}/Gemfile \
136               --unset BUNDLE_PATH \
137               --set BUNDLE_FROZEN 1 \
138               --set GEM_HOME ${basicEnv}/${ruby.gemPath} \
139               --set GEM_PATH ${basicEnv}/${ruby.gemPath}
140           done
141         '';
143         dontInstall = true;
145         doCheck = true;
146         checkPhase = ''
147           $out/bin/ruby --help > /dev/null
148         '';
150         inherit (ruby) meta;
151       };
153       env = let
154         irbrc = builtins.toFile "irbrc" ''
155           if !(ENV["OLD_IRBRC"].nil? || ENV["OLD_IRBRC"].empty?)
156             require ENV["OLD_IRBRC"]
157           end
158           require 'rubygems'
159           require 'bundler/setup'
160         '';
161         in stdenv.mkDerivation {
162           name = "${pname'}-interactive-environment";
163           nativeBuildInputs = [ wrappedRuby basicEnv ];
164           shellHook = ''
165             export OLD_IRBRC=$IRBRC
166             export IRBRC=${irbrc}
167           '';
168           buildCommand = ''
169             echo >&2 ""
170             echo >&2 "*** Ruby 'env' attributes are intended for interactive nix-shell sessions, not for building! ***"
171             echo >&2 ""
172             exit 1
173           '';
174         };
175     };
176   };
178   basicEnv =
179     if copyGemFiles then
180       runCommand name' basicEnvArgs ''
181         mkdir -p $out
182         for i in $paths; do
183           ${rsync}/bin/rsync -a $i/lib $out/
184         done
185         eval "$postBuild"
186       ''
187     else
188       buildEnv basicEnvArgs;
190   basicEnv