nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at flake-libs 255 lines 5.8 kB view raw
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