lol

ruby: new bundler infrastructure

This improves our Bundler integration (i.e. `bundlerEnv`).

Before describing the implementation differences, I'd like to point a
breaking change: buildRubyGem now expects `gemName` and `version` as
arguments, rather than a `name` attribute in the form of
"<gem-name>-<version>".

Now for the differences in implementation.

The previous implementation installed all gems at once in a single
derivation. This was made possible by using a set of monkey-patches to
prevent Bundler from downloading gems impurely, and to help Bundler
find and activate all required gems prior to installation. This had
several downsides:

* The patches were really hard to understand, and required subtle
interaction with the rest of the build environment.
* A single install failure would cause the entire derivation to fail.

The new implementation takes a different approach: we install gems into
separate derivations, and then present Bundler with a symlink forest
thereof. This has a couple benefits over the existing approach:

* Fewer patches are required, with less interplay with the rest of the
build environment.
* Changes to one gem no longer cause a rebuild of the entire dependency
graph.
* Builds take 20% less time (using gitlab as a reference).

It's unfortunate that we still have to muck with Bundler's internals,
though it's unavoidable with the way that Bundler is currently designed.
There are a number improvements that could be made in Bundler that would
simplify our packaging story:

* Bundler requires all installed gems reside within the same prefix
(GEM_HOME), unlike RubyGems which allows for multiple prefixes to
be specified through GEM_PATH. It would be ideal if Bundler allowed
for packages to be installed and sourced from multiple prefixes.
* Bundler installs git sources very differently from how RubyGems
installs gem packages, and, unlike RubyGems, it doesn't provide a
public interface (CLI or programmatic) to guide the installation of a
single gem. We are presented with the options of either
reimplementing a considerable portion Bundler, or patch and use parts
of its internals; I choose the latter. Ideally, there would be a way
to install gems from git sources in a manner similar to how we drive
`gem` to install gem packages.
* When a bundled program is executed (via `bundle exec` or a
binstub that does `require 'bundler/setup'`), the setup process reads
the Gemfile.lock, activates the dependencies, re-serializes the lock
file it read earlier, and then attempts to overwrite the Gemfile.lock
if the contents aren't bit-identical. I think the reasoning is that
by merely running an application with a newer version of Bundler, you'll
automatically keep the Gemfile.lock up-to-date with any changes in the
format. Unfortunately, that doesn't play well with any form of
packaging, because bundler will immediately cause the application to
abort when it attempts to write to the read-only Gemfile.lock in the
store. We work around this by normalizing the Gemfile.lock with the
version of Bundler that we'll use at runtime before we copy it into
the store. This feels fragile, but it's the best we can do without
changes upstream, or resorting to more delicate hacks.

With all of the challenges in using Bundler, one might wonder why we
can't just cut Bundler out of the picture and use RubyGems. After all,
Nix provides most of the isolation that Bundler is used for anyway.

The problem, however, is that almost every Rails application calls
`Bundler::require` at startup (by way of the default project templates).
Because bundler will then, by default, `require` each gem listed in the
Gemfile, Rails applications are almost always written such that none of
the source files explicitly require their dependencies. That leaves us
with two options: support and use Bundler, or maintain massive patches
for every Rails application that we package.

Closes #8612

+645 -886
-2
pkgs/applications/misc/jekyll/default.nix
··· 8 8 lockfile = ./Gemfile.lock; 9 9 gemset = ./gemset.nix; 10 10 11 - buildInputs = [ curl ]; 12 - 13 11 meta = with lib; { 14 12 description = "Simple, blog aware, static site generator"; 15 13 homepage = http://jekyllrb.com/;
+4 -4
pkgs/applications/networking/cluster/panamax/api/default.nix
··· 1 - { stdenv, buildEnv, fetchgit, fetchurl, makeWrapper, bundlerEnv, bundler_HEAD 1 + { stdenv, buildEnv, fetchgit, fetchurl, makeWrapper, bundlerEnv, bundler 2 2 , ruby, libxslt, libxml2, sqlite, openssl, docker 3 - , dataDir ? "/var/lib/panamax-api" }: 3 + , dataDir ? "/var/lib/panamax-api" }@args: 4 4 5 5 with stdenv.lib; 6 6 ··· 14 14 gemset = ./gemset.nix; 15 15 gemfile = ./Gemfile; 16 16 lockfile = ./Gemfile.lock; 17 - buildInputs = [ openssl ]; 18 17 }; 19 - bundler = bundler_HEAD.override { inherit ruby; }; 18 + 19 + bundler = args.bundler.override { inherit ruby; }; 20 20 21 21 database_yml = builtins.toFile "database.yml" '' 22 22 production:
+3 -4
pkgs/applications/networking/cluster/panamax/ui/default.nix
··· 1 - { stdenv, fetchgit, fetchurl, makeWrapper, bundlerEnv, bundler_HEAD 2 - , ruby, rubygemsFun, openssl, sqlite, dataDir ? "/var/lib/panamax-ui"}: 1 + { stdenv, fetchgit, fetchurl, makeWrapper, bundlerEnv, bundler 2 + , ruby, openssl, sqlite, dataDir ? "/var/lib/panamax-ui"}@args: 3 3 4 4 with stdenv.lib; 5 5 ··· 13 13 gemset = ./gemset.nix; 14 14 gemfile = ./Gemfile; 15 15 lockfile = ./Gemfile.lock; 16 - buildInputs = [ openssl ]; 17 16 }; 18 17 19 - bundler = bundler_HEAD.override { inherit ruby; }; 18 + bundler = args.bundler.override { inherit ruby; }; 20 19 21 20 src = fetchgit { 22 21 rev = "refs/tags/v${version}";
+208
pkgs/development/interpreters/ruby/build-ruby-gem/default.nix
··· 1 + # This builds gems in a way that is compatible with bundler. 2 + # 3 + # Bundler installs gems from git sources _very_ differently from how RubyGems 4 + # installes gem packages, though they both install gem packages similarly. 5 + # 6 + # We monkey-patch Bundler to remove any impurities and then drive its internals 7 + # to install git gems. 8 + # 9 + # For the sake of simplicity, gem packages are installed with the standard `gem` 10 + # program. 11 + # 12 + # Note that bundler does not support multiple prefixes; it assumes that all 13 + # gems are installed in a common prefix, and has no support for specifying 14 + # otherwise. Therefore, if you want to be able to use the resulting derivations 15 + # with bundler, you need to create a symlink forrest first, which is what 16 + # `bundlerEnv` does for you. 17 + # 18 + # Normal gem packages can be used outside of bundler; a binstub is created in 19 + # $out/bin. 20 + 21 + { lib, ruby, rubygems, bundler, fetchurl, fetchgit, makeWrapper, git, buildRubyGem 22 + } @ defs: 23 + 24 + lib.makeOverridable ( 25 + 26 + { name ? null 27 + , gemName 28 + , version ? null 29 + , type ? "gem" 30 + , document ? [] # e.g. [ "ri" "rdoc" ] 31 + , platform ? "ruby" 32 + , ruby ? defs.ruby 33 + , stdenv ? ruby.stdenv 34 + , namePrefix ? "${ruby.name}" + "-" 35 + , buildInputs ? [] 36 + , doCheck ? false 37 + , meta ? {} 38 + , patches ? [] 39 + , gemPath ? [] 40 + , dontStrip ? true 41 + , remotes ? ["https://rubygems.org"] 42 + # Assume we don't have to build unless strictly necessary (e.g. the source is a 43 + # git checkout). 44 + # If you need to apply patches, make sure to set `dontBuild = false`; 45 + , dontBuild ? true 46 + , propagatedBuildInputs ? [] 47 + , propagatedUserEnvPkgs ? [] 48 + , buildFlags ? null 49 + , passthru ? {} 50 + , ...} @ attrs: 51 + 52 + if ! builtins.elem type [ "git" "gem" ] 53 + then throw "buildRubyGem: don't know how to build a gem of type \"${type}\"" 54 + else 55 + 56 + let 57 + shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'"; 58 + rubygems = (attrs.rubygems or defs.rubygems).override { 59 + inherit ruby; 60 + }; 61 + src = attrs.src or ( 62 + if type == "gem" 63 + then fetchurl { 64 + urls = map (remote: "${remote}/gems/${gemName}-${version}.gem") remotes; 65 + inherit (attrs) sha256; 66 + } else fetchgit { 67 + inherit (attrs) url rev sha256 fetchSubmodules; 68 + leaveDotGit = true; 69 + } 70 + ); 71 + documentFlag = 72 + if document == [] 73 + then "-N" 74 + else "--document ${lib.concatStringsSep "," document}"; 75 + 76 + in 77 + 78 + stdenv.mkDerivation (attrs // { 79 + inherit ruby rubygems; 80 + inherit doCheck; 81 + inherit dontBuild; 82 + inherit dontStrip; 83 + inherit type; 84 + 85 + buildInputs = [ 86 + ruby rubygems makeWrapper 87 + ] ++ lib.optionals (type == "git") [ git bundler ] 88 + ++ buildInputs; 89 + 90 + name = attrs.name or (namePrefix + gemName); 91 + 92 + inherit src; 93 + 94 + phases = attrs.phases or [ "unpackPhase" "patchPhase" "buildPhase" "installPhase" "fixupPhase" ]; 95 + 96 + unpackPhase = attrs.unpackPhase or '' 97 + runHook preUnpack 98 + 99 + if [[ -f $src && $src == *.gem ]]; then 100 + if [[ -z "$dontBuild" ]]; then 101 + # we won't know the name of the directory that RubyGems creates, 102 + # so we'll just use a glob to find it and move it over. 103 + gempkg="$src" 104 + sourceRoot=source 105 + gem unpack $gempkg --target=container 106 + cp -r container/* $sourceRoot 107 + rm -r container 108 + 109 + # copy out the original gemspec, for convenience during patching / 110 + # overrides. 111 + gem specification $gempkg --ruby > original.gemspec 112 + gemspec=$(readlink -f .)/original.gemspec 113 + else 114 + gempkg="$src" 115 + fi 116 + else 117 + # Fall back to the original thing for everything else. 118 + dontBuild="" 119 + preUnpack="" postUnpack="" unpackPhase 120 + fi 121 + 122 + runHook postUnpack 123 + ''; 124 + 125 + buildPhase = attrs.buildPhase or '' 126 + runHook preBuild 127 + 128 + if [[ "$type" == "gem" ]]; then 129 + if [[ -z "$gemspec" ]]; then 130 + gemspec="$(find . -name '*.gemspec')" 131 + echo "found the following gemspecs:" 132 + echo "$gemspec" 133 + gemspec="$(echo "$gemspec" | head -n1)" 134 + fi 135 + 136 + exec 3>&1 137 + output="$(gem build $gemspec | tee >(cat - >&3))" 138 + exec 3>&- 139 + 140 + gempkg=$(echo "$output" | grep -oP 'File: \K(.*)') 141 + 142 + echo "gem package built: $gempkg" 143 + fi 144 + 145 + runHook postBuild 146 + ''; 147 + 148 + # Note: 149 + # We really do need to keep the $out/${ruby.gemPath}/cache. 150 + # This is very important in order for many parts of RubyGems/Bundler to not blow up. 151 + # See https://github.com/bundler/bundler/issues/3327 152 + installPhase = attrs.installPhase or '' 153 + runHook preInstall 154 + 155 + export GEM_HOME=$out/${ruby.gemPath} 156 + mkdir -p $GEM_HOME 157 + 158 + echo "buildFlags: $buildFlags" 159 + 160 + ${lib.optionalString (type == "git") '' 161 + ruby ${./nix-bundle-install.rb} \ 162 + ${gemName} \ 163 + ${attrs.url} \ 164 + ${src} \ 165 + ${attrs.rev} \ 166 + ${version} \ 167 + ${shellEscape (toString buildFlags)} 168 + ''} 169 + 170 + ${lib.optionalString (type == "gem") '' 171 + if [[ -z "$gempkg" ]]; then 172 + echo "failure: \$gempkg path unspecified" 1>&2 173 + exit 1 174 + elif [[ ! -f "$gempkg" ]]; then 175 + echo "failure: \$gempkg path invalid" 1>&2 176 + exit 1 177 + fi 178 + 179 + gem install \ 180 + --local \ 181 + --force \ 182 + --http-proxy 'http://nodtd.invalid' \ 183 + --ignore-dependencies \ 184 + --build-root '/' \ 185 + --backtrace \ 186 + ${documentFlag} \ 187 + $gempkg $gemFlags -- $buildFlags 188 + 189 + # looks like useless files which break build repeatability and consume space 190 + rm -fv $out/${ruby.gemPath}/doc/*/*/created.rid || true 191 + rm -fv $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true 192 + 193 + # write out metadata and binstubs 194 + spec=$(echo $out/${ruby.gemPath}/specifications/*.gemspec) 195 + ruby ${./gem-post-build.rb} "$spec" 196 + ''} 197 + 198 + runHook postInstall 199 + ''; 200 + 201 + propagatedBuildInputs = gemPath ++ propagatedBuildInputs; 202 + propagatedUserEnvPkgs = gemPath ++ propagatedUserEnvPkgs; 203 + 204 + passthru = passthru // { isRubyGem = true; }; 205 + inherit meta; 206 + }) 207 + 208 + )
+77
pkgs/development/interpreters/ruby/build-ruby-gem/gem-post-build.rb
··· 1 + require 'rbconfig' 2 + require 'rubygems' 3 + require 'rubygems/specification' 4 + require 'fileutils' 5 + 6 + ruby = File.join(ENV["ruby"], "bin", RbConfig::CONFIG['ruby_install_name']) 7 + out = ENV["out"] 8 + bin_path = File.join(ENV["out"], "bin") 9 + gem_home = ENV["GEM_HOME"] 10 + gem_path = ENV["GEM_PATH"].split(":") 11 + install_path = Dir.glob("#{gem_home}/gems/*").first 12 + gemspec_path = ARGV[0] 13 + 14 + if defined?(Encoding.default_internal) 15 + Encoding.default_internal = Encoding::UTF_8 16 + Encoding.default_external = Encoding::UTF_8 17 + end 18 + 19 + gemspec_content = File.read(gemspec_path) 20 + spec = nil 21 + if gemspec_content[0..2] == "---" # YAML header 22 + spec = Gem::Specification.from_yaml(gemspec_content) 23 + else 24 + spec = Gem::Specification.load(gemspec_path) 25 + end 26 + 27 + FileUtils.mkdir_p("#{out}/nix-support") 28 + 29 + # write meta-data 30 + meta = "#{out}/nix-support/gem-meta" 31 + FileUtils.mkdir_p(meta) 32 + FileUtils.ln_s(gemspec_path, "#{meta}/spec") 33 + File.open("#{meta}/name", "w") do |f| 34 + f.write(spec.name) 35 + end 36 + File.open("#{meta}/install-path", "w") do |f| 37 + f.write(install_path) 38 + end 39 + File.open("#{meta}/require-paths", "w") do |f| 40 + f.write(spec.require_paths.join(" ")) 41 + end 42 + File.open("#{meta}/executables", "w") do |f| 43 + f.write(spec.executables.join(" ")) 44 + end 45 + 46 + # add this gem to the GEM_PATH for dependencies 47 + File.open("#{out}/nix-support/setup-hook", "a") do |f| 48 + f.puts("addToSearchPath GEM_PATH #{gem_home}") 49 + spec.require_paths.each do |dir| 50 + f.puts("addToSearchPath RUBYLIB #{install_path}/#{dir}") 51 + end 52 + end 53 + 54 + # create regular rubygems binstubs 55 + FileUtils.mkdir_p(bin_path) 56 + spec.executables.each do |exe| 57 + File.open("#{bin_path}/#{exe}", "w") do |f| 58 + f.write(<<-EOF) 59 + #!#{ruby} 60 + # 61 + # This file was generated by Nix. 62 + # 63 + # The application '#{exe}' is installed as part of a gem, and 64 + # this file is here to facilitate running it. 65 + # 66 + 67 + gem_path = ENV["GEM_PATH"] 68 + ENV["GEM_PATH"] = "\#{gem_path}\#{":" unless gem_path.nil? || gem_path.empty?}#{(gem_path+[gem_home]).join(":")}" 69 + 70 + require 'rubygems' 71 + 72 + load Gem.bin_path(#{spec.name.inspect}, #{exe.inspect}) 73 + EOF 74 + end 75 + 76 + FileUtils.chmod("+x", "#{bin_path}/#{exe}") 77 + end
+153
pkgs/development/interpreters/ruby/build-ruby-gem/nix-bundle-install.rb
··· 1 + require 'rbconfig' 2 + require 'bundler/vendored_thor' 3 + require 'bundler' 4 + require 'rubygems/command' 5 + require 'fileutils' 6 + require 'pathname' 7 + require 'tmpdir' 8 + 9 + # Options: 10 + # 11 + # name - the gem name 12 + # uri - git repo uri 13 + # repo - path to local checkout 14 + # ref - the commit hash 15 + # version - gem version 16 + # build-flags - build arguments 17 + 18 + ruby = File.join(ENV["ruby"], "bin", RbConfig::CONFIG['ruby_install_name']) 19 + out = ENV["out"] 20 + bin_dir = File.join(ENV["out"], "bin") 21 + 22 + name = ARGV[0] 23 + uri = ARGV[1] 24 + REPO = ARGV[2] 25 + ref = ARGV[3] 26 + version = ARGV[4] 27 + build_flags = ARGV[5] 28 + 29 + # options to pass to bundler 30 + options = { 31 + "name" => name, 32 + "uri" => uri, 33 + "ref" => ref, 34 + "version" => version, 35 + } 36 + 37 + # Monkey-patch Bundler to use our local checkout. 38 + # I wish we didn't have to do this, but bundler does not expose an API to do 39 + # these kinds of things. 40 + Bundler.module_eval do 41 + def self.requires_sudo? 42 + false 43 + end 44 + 45 + def self.root 46 + # we don't have a Gemfile, so it doesn't make sense to try to make paths 47 + # relative to the (non existent) parent directory thereof, so we give a 48 + # nonsense path here. 49 + Pathname.new("/no-root-path") 50 + end 51 + 52 + def self.bundle_path 53 + Pathname.new(ENV["GEM_HOME"]) 54 + end 55 + 56 + def self.locked_gems 57 + nil 58 + end 59 + end 60 + 61 + Bundler::Source::Git.class_eval do 62 + def allow_git_ops? 63 + true 64 + end 65 + end 66 + 67 + Bundler::Source::Git::GitProxy.class_eval do 68 + def checkout 69 + unless path.exist? 70 + FileUtils.mkdir_p(path.dirname) 71 + FileUtils.cp_r(File.join(REPO, ".git"), path) 72 + system("chmod -R +w #{path}") 73 + end 74 + end 75 + 76 + def copy_to(destination, submodules=false) 77 + unless File.exist?(destination.join(".git")) 78 + FileUtils.mkdir_p(destination.dirname) 79 + FileUtils.cp_r(REPO, destination) 80 + system("chmod -R +w #{destination}") 81 + end 82 + end 83 + end 84 + 85 + # UI 86 + verbose = false 87 + no_color = false 88 + Bundler.ui = Bundler::UI::Shell.new({"no-color" => no_color}) 89 + Bundler.ui.level = "debug" if verbose 90 + 91 + # Install 92 + source = Bundler::Source::Git.new(options) 93 + spec = source.specs.search_all(name).first 94 + Bundler.rubygems.with_build_args [build_flags] do 95 + source.install(spec) 96 + end 97 + 98 + msg = spec.post_install_message 99 + if msg 100 + Bundler.ui.confirm "Post-install message from #{name}:" 101 + Bundler.ui.info msg 102 + end 103 + 104 + # Write out the binstubs 105 + if spec.executables.any? 106 + FileUtils.mkdir_p(bin_dir) 107 + spec.executables.each do |exe| 108 + wrapper = File.join(bin_dir, exe) 109 + File.open(wrapper, "w") do |f| 110 + stub = generate_stub(spec.name, exe) 111 + f.write(<<-EOF) 112 + #!#{ruby} 113 + # 114 + # This file was generated by Nix. 115 + # 116 + # The application '#{exe}' is installed as part of a gem, and 117 + # this file is here to facilitate running it. 118 + # 119 + 120 + require 'rubygems' 121 + require 'bundler/setup' 122 + 123 + load Gem.bin_path(#{spec.name.inspect}, #{exe.inspect}) 124 + EOF 125 + end 126 + 127 + FileUtils.chmod("+x", wrapper) 128 + end 129 + end 130 + 131 + # Write out metadata 132 + meta = "#{out}/nix-support/gem-meta" 133 + FileUtils.mkdir_p(meta) 134 + FileUtils.ln_s(spec.loaded_from.to_s, "#{meta}/spec") 135 + File.open("#{meta}/name", "w") do |f| 136 + f.write spec.name 137 + end 138 + File.open("#{meta}/install-path", "w") do |f| 139 + f.write source.install_path.to_s 140 + end 141 + File.open("#{meta}/require-paths", "w") do |f| 142 + f.write spec.require_paths.join(" ") 143 + end 144 + File.open("#{meta}/executables", "w") do |f| 145 + f.write spec.executables.join(" ") 146 + end 147 + 148 + # make the lib available during bundler/git installs 149 + File.open("#{out}/nix-support/setup-hook", "a") do |f| 150 + spec.require_paths.each do |dir| 151 + f.puts("addToSearchPath RUBYLIB #{source.install_path}/#{dir}") 152 + end 153 + end
-4
pkgs/development/interpreters/ruby/bundix/Gemfile
··· 1 - source "http://rubygems.org" 2 - gem "bundix", 3 - :git => "https://github.com/cstrahan/bundix.git", 4 - :ref => "v1.0.3"
-18
pkgs/development/interpreters/ruby/bundix/Gemfile.lock
··· 1 - GIT 2 - remote: https://github.com/cstrahan/bundix.git 3 - revision: c879cf901ff8084b4c97aaacfb5ecbdb0f95cc03 4 - ref: v1.0.3 5 - specs: 6 - bundix (1.0.2) 7 - thor (~> 0.19.1) 8 - 9 - GEM 10 - remote: http://rubygems.org/ 11 - specs: 12 - thor (0.19.1) 13 - 14 - PLATFORMS 15 - ruby 16 - 17 - DEPENDENCIES 18 - bundix!
+18 -7
pkgs/development/interpreters/ruby/bundix/default.nix
··· 1 - { ruby, bundlerEnv }: 1 + { ruby, fetchgit, buildRubyGem, bundler }: 2 + 3 + let 4 + thor = buildRubyGem { 5 + gemName = "thor"; 6 + version = "0.19.1"; 7 + type = "gem"; 8 + sha256 = "08p5gx18yrbnwc6xc0mxvsfaxzgy2y9i78xq7ds0qmdm67q39y4z"; 9 + }; 2 10 3 - bundlerEnv { 4 - name = "bundix"; 5 - inherit ruby; 6 - gemset = ./gemset.nix; 7 - gemfile = ./Gemfile; 8 - lockfile = ./Gemfile.lock; 11 + in buildRubyGem { 12 + gemName = "bundix"; 13 + version = "1.0.4"; 14 + gemPath = [ thor bundler ]; 15 + src = fetchgit { 16 + url = "https://github.com/cstrahan/bundix.git"; 17 + rev = "6dcf1f71c61584f5c9b919ee9df7b0c554862076"; 18 + sha256 = "1w17bvc9srcgr4ry81ispcj35g9kxihbyknmqp8rnd4h5090b7b2"; 19 + }; 9 20 }
-22
pkgs/development/interpreters/ruby/bundix/gemset.nix
··· 1 - { 2 - "bundix" = { 3 - version = "1.0.2"; 4 - source = { 5 - type = "git"; 6 - url = "https://github.com/cstrahan/bundix.git"; 7 - rev = "c879cf901ff8084b4c97aaacfb5ecbdb0f95cc03"; 8 - sha256 = "05kmdnq4qa5h8l3asv05cjpnyplnqqx6hrqybj2cjlzmdxnkkgyj"; 9 - fetchSubmodules = false; 10 - }; 11 - dependencies = [ 12 - "thor" 13 - ]; 14 - }; 15 - "thor" = { 16 - version = "0.19.1"; 17 - source = { 18 - type = "gem"; 19 - sha256 = "08p5gx18yrbnwc6xc0mxvsfaxzgy2y9i78xq7ds0qmdm67q39y4z"; 20 - }; 21 - }; 22 - }
+24 -4
pkgs/development/interpreters/ruby/bundler-env/default-gem-config.nix pkgs/development/interpreters/ruby/gemconfig/default.nix
··· 20 20 { lib, fetchurl, writeScript, ruby, kerberos, libxml2, libxslt, python, stdenv, which 21 21 , libiconv, postgresql, v8_3_16_14, clang, sqlite, zlib, imagemagick 22 22 , pkgconfig , ncurses, xapian, gpgme, utillinux, fetchpatch, tzdata, icu, libffi 23 - , cmake, libssh2, openssl, mysql, darwin 23 + , cmake, libssh2, openssl, mysql, darwin, git, perl, gecode_3, curl 24 24 }: 25 25 26 26 let ··· 32 32 buildInputs = [ which icu zlib ]; 33 33 }; 34 34 35 + dep-selector-libgecode = attrs: { 36 + USE_SYSTEM_GECODE = true; 37 + postInstall = '' 38 + installPath=$(cat $out/nix-support/gem-meta/install-path) 39 + sed -i $installPath/lib/dep-selector-libgecode.rb -e 's@VENDORED_GECODE_DIR =.*@VENDORED_GECODE_DIR = "${gecode_3}"@' 40 + ''; 41 + }; 42 + 35 43 ffi = attrs: { 36 44 buildInputs = [ libffi pkgconfig ]; 37 45 }; ··· 40 48 buildInputs = [ gpgme ]; 41 49 }; 42 50 51 + # note that you need version >= v3.16.14.8, 52 + # otherwise the gem will fail to link to the libv8 binary. 53 + # see: https://github.com/cowboyd/libv8/pull/161 43 54 libv8 = attrs: { 44 55 buildInputs = [ which v8 python ]; 45 - buildFlags = [ 46 - "--with-system-v8=true" 47 - ]; 56 + buildFlags = [ "--with-system-v8=true" ]; 48 57 }; 49 58 50 59 mysql2 = attrs: { ··· 71 80 "--with-exslt-include=${libxslt}/include" 72 81 ] ++ lib.optional stdenv.isDarwin "--with-iconv-dir=${libiconv}"; 73 82 buildInputs = lib.optional stdenv.isDarwin darwin.libobjc; 83 + }; 84 + 85 + patron = attrs: { 86 + buildInputs = [ curl ]; 74 87 }; 75 88 76 89 pg = attrs: { ··· 79 92 ]; 80 93 }; 81 94 95 + puma = attrs: { 96 + buildInputs = [ openssl ]; 97 + }; 98 + 82 99 rmagick = attrs: { 83 100 buildInputs = [ imagemagick pkgconfig ]; 84 101 }; ··· 95 112 }; 96 113 97 114 sup = attrs: { 115 + dontBuild = false; 98 116 # prevent sup from trying to dynamically install `xapian-ruby`. 99 117 postPatch = '' 100 118 cp ${./mkrf_conf_xapian.rb} ext/mkrf_conf_xapian.rb ··· 118 136 }; 119 137 120 138 tzinfo = attrs: { 139 + dontBuild = false; 121 140 postPatch = '' 122 141 substituteInPlace lib/tzinfo/zoneinfo_data_source.rb \ 123 142 --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo" ··· 130 149 131 150 xapian-ruby = attrs: { 132 151 # use the system xapian 152 + dontBuild = false; 133 153 buildInputs = [ xapian pkgconfig zlib ]; 134 154 postPatch = '' 135 155 cp ${./xapian-Rakefile} Rakefile
+60 -297
pkgs/development/interpreters/ruby/bundler-env/default.nix
··· 1 1 { stdenv, runCommand, writeText, writeScript, writeScriptBin, ruby, lib 2 - , callPackage, defaultGemConfig, fetchurl, fetchgit, buildRubyGem , bundler_HEAD 2 + , callPackage, defaultGemConfig, fetchurl, fetchgit, buildRubyGem, buildEnv 3 + , rubygems 3 4 , git 5 + , makeWrapper 6 + , bundler 7 + , tree 4 8 }@defs: 5 9 6 - # This is a work-in-progress. 7 - # The idea is that his will replace load-ruby-env.nix. 8 - 9 10 { name, gemset, gemfile, lockfile, ruby ? defs.ruby, gemConfig ? defaultGemConfig 10 - , enableParallelBuilding ? false # TODO: this might not work, given the env-var shinanigans. 11 - , postInstall ? null 12 - , documentation ? false 11 + , postBuild ? null 12 + , document ? [] 13 13 , meta ? {} 14 + , ignoreCollisions ? false 14 15 , ... 15 16 }@args: 16 17 17 18 let 18 19 19 20 shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'"; 20 - const = x: y: x; 21 - bundler = bundler_HEAD.override { inherit ruby; }; 22 - inherit (builtins) attrValues; 23 - 24 - gemName = attrs: "${attrs.name}-${attrs.version}.gem"; 25 - 26 - fetchers.path = attrs: attrs.source.path; 27 - fetchers.gem = attrs: fetchurl { 28 - url = "${attrs.source.source or "https://rubygems.org"}/downloads/${gemName attrs}"; 29 - inherit (attrs.source) sha256; 30 - }; 31 - fetchers.git = attrs: fetchgit { 32 - inherit (attrs.source) url rev sha256 fetchSubmodules; 33 - leaveDotGit = true; 34 - }; 35 - 36 - applySrc = attrs: 37 - attrs // { 38 - src = (fetchers."${attrs.source.type}" attrs); 39 - }; 40 - 21 + importedGemset = import gemset; 41 22 applyGemConfigs = attrs: 42 - if gemConfig ? "${attrs.name}" 43 - then attrs // gemConfig."${attrs.name}" attrs 44 - else attrs; 45 - 46 - needsPatch = attrs: 47 - (attrs ? patches) || (attrs ? prePatch) || (attrs ? postPatch); 48 - 49 - # patch a gem or source tree. 50 - # for gems, the gem is unpacked, patched, and then repacked. 51 - # see: https://github.com/fedora-ruby/gem-patch/blob/master/lib/rubygems/patcher.rb 52 - applyPatches = attrs: 53 - if !needsPatch attrs 54 - then attrs 55 - else attrs // { src = 56 - stdenv.mkDerivation { 57 - name = gemName attrs; 58 - phases = [ "unpackPhase" "patchPhase" "installPhase" ]; 59 - buildInputs = [ ruby ] ++ attrs.buildInputs or []; 60 - patches = attrs.patches or [ ]; 61 - prePatch = attrs.prePatch or "true"; 62 - postPatch = attrs.postPatch or "true"; 63 - unpackPhase = '' 64 - runHook preUnpack 65 - 66 - if [[ -f ${attrs.src} ]]; then 67 - isGem=1 68 - # we won't know the name of the directory that RubyGems creates, 69 - # so we'll just use a glob to find it and move it over. 70 - gem unpack ${attrs.src} --target=container 71 - cp -r container/* contents 72 - rm -r container 73 - else 74 - cp -r ${attrs.src} contents 75 - chmod -R +w contents 76 - fi 77 - 78 - cd contents 79 - runHook postUnpack 80 - ''; 81 - installPhase = '' 82 - runHook preInstall 83 - 84 - if [[ -n "$isGem" ]]; then 85 - ${writeScript "repack.rb" '' 86 - #!${ruby}/bin/ruby 87 - require 'rubygems' 88 - require 'rubygems/package' 89 - require 'fileutils' 90 - 91 - Encoding.default_internal = Encoding::UTF_8 92 - Encoding.default_external = Encoding::UTF_8 93 - 94 - if Gem::VERSION < '2.0' 95 - load "${./package-1.8.rb}" 96 - end 97 - 98 - out = ENV['out'] 99 - files = Dir['**/{.[^\.]*,*}'] 100 - 101 - package = Gem::Package.new("${attrs.src}") 102 - patched_package = Gem::Package.new(package.spec.file_name) 103 - patched_package.spec = package.spec.clone 104 - patched_package.spec.files = files 105 - 106 - patched_package.build(false) 107 - 108 - FileUtils.cp(patched_package.spec.file_name, out) 109 - ''} 110 - else 111 - cp -r . $out 112 - fi 113 - 114 - runHook postInstall 115 - ''; 116 - }; 117 - }; 118 - 119 - instantiate = (attrs: 120 - applyPatches (applyGemConfigs (applySrc attrs)) 23 + (if gemConfig ? "${attrs.gemName}" 24 + then attrs // gemConfig."${attrs.gemName}" attrs 25 + else attrs); 26 + configuredGemset = lib.flip lib.mapAttrs importedGemset (name: attrs: 27 + applyGemConfigs (attrs // { gemName = name; }) 121 28 ); 122 - 123 - instantiated = lib.flip lib.mapAttrs (import gemset) (name: attrs: 124 - instantiate (attrs // { inherit name; }) 125 - ); 126 - 127 - needsPreInstall = attrs: 128 - (attrs ? preInstall) || (attrs ? buildInputs) || (attrs ? nativeBuildInputs); 129 - 130 - # TODO: support cross compilation? look at stdenv/generic/default.nix. 131 - runPreInstallers = lib.fold (next: acc: 132 - if !needsPreInstall next 133 - then acc 134 - else acc + '' 135 - ${writeScript "${next.name}-pre-install" '' 136 - #!${stdenv.shell} 137 - 138 - export nativeBuildInputs="${toString ((next.nativeBuildInputs or []) ++ (next.buildInputs or []))}" 139 - 140 - source ${stdenv}/setup 141 - 142 - header "running pre-install script for ${next.name}" 143 - 144 - ${next.preInstall or ""} 145 - 146 - ${ruby}/bin/ruby -e 'print ENV.inspect' > env/${next.name} 29 + hasBundler = builtins.hasAttr "bundler" importedGemset; 30 + bundler = if hasBundler then gems.bundler else defs.bundler.override (attrs: { inherit ruby; }); 31 + rubygems = defs.rubygems.override (attrs: { inherit ruby; }); 32 + gems = lib.flip lib.mapAttrs configuredGemset (name: attrs: 33 + buildRubyGem ((removeAttrs attrs ["source"]) // attrs.source // { 34 + inherit ruby rubygems; 35 + gemName = name; 36 + gemPath = map (gemName: gems."${gemName}") (attrs.dependencies or []); 37 + })); 38 + # We have to normalize the Gemfile.lock, otherwise bundler tries to be 39 + # helpful by doing so at run time, causing executables to immediately bail 40 + # out. Yes, I'm serious. 41 + confFiles = runCommand "gemfile-and-lockfile" {} '' 42 + mkdir -p $out 43 + cp ${gemfile} $out/Gemfile 44 + cp ${lockfile} $out/Gemfile.lock 147 45 148 - stopNest 149 - ''} 150 - '' 151 - ) "" (attrValues instantiated); 152 - 153 - # copy *.gem to ./gems 154 - copyGems = lib.fold (next: acc: 155 - if next.source.type == "gem" 156 - then acc + "cp ${next.src} gems/${gemName next}\n" 157 - else acc 158 - ) "" (attrValues instantiated); 159 - 160 - runRuby = name: env: command: 161 - runCommand name env '' 162 - ${ruby}/bin/ruby ${writeText name command} 163 - ''; 164 - 165 - # TODO: include json_pure, so the version of ruby doesn't matter. 166 - # not all rubies have support for JSON built-in, 167 - # so we'll convert JSON to ruby expressions. 168 - json2rb = writeScript "json2rb" '' 169 - #!${ruby}/bin/ruby 170 - begin 171 - require 'json' 172 - rescue LoadError => ex 173 - require 'json_pure' 174 - end 175 - 176 - puts JSON.parse(STDIN.read).inspect 46 + cd $out 47 + chmod +w Gemfile.lock 48 + source ${rubygems}/nix-support/setup-hook 49 + export GEM_PATH=${bundler}/${ruby.gemPath} 50 + ${ruby}/bin/ruby -rubygems -e \ 51 + "require 'bundler'; Bundler.definition.lock('Gemfile.lock')" 177 52 ''; 178 - 179 - # dump the instantiated gemset as a ruby expression. 180 - serializedGemset = runCommand "gemset.rb" { json = builtins.toJSON instantiated; } '' 181 - printf '%s' "$json" | ${json2rb} > $out 182 - ''; 183 - 184 - # this is a mapping from a source type and identifier (uri/path/etc) 185 - # to the pure store path. 186 - # we'll use this from the patched bundler to make fetching sources pure. 187 - sources = runRuby "sources.rb" { gemset = serializedGemset; } '' 188 - out = ENV['out'] 189 - gemset = eval(File.read(ENV['gemset'])) 190 - 191 - sources = { 192 - "git" => { }, 193 - "path" => { }, 194 - "gem" => { }, 195 - "svn" => { } 196 - } 197 - 198 - gemset.each_value do |spec| 199 - type = spec["source"]["type"] 200 - val = spec["src"] 201 - key = 202 - case type 203 - when "gem" 204 - spec["name"] 205 - when "git" 206 - spec["source"]["url"] 207 - when "path" 208 - spec["source"]["originalPath"] 209 - when "svn" 210 - nil # TODO 211 - end 212 - 213 - sources[type][key] = val if key 214 - end 215 - 216 - File.open(out, "wb") do |f| 217 - f.print sources.inspect 218 - end 219 - ''; 220 - 221 - # rewrite PATH sources to point into the nix store. 222 - purifiedLockfile = runRuby "purifiedLockfile" {} '' 223 - out = ENV['out'] 224 - sources = eval(File.read("${sources}")) 225 - paths = sources["path"] 226 - 227 - lockfile = File.read("${lockfile}") 228 - 229 - paths.each_pair do |impure, pure| 230 - lockfile.gsub!(/^ remote: #{Regexp.escape(impure)}/, " remote: #{pure}") 231 - end 232 - 233 - File.open(out, "wb") do |f| 234 - f.print lockfile 235 - end 236 - ''; 237 - 238 - needsBuildFlags = attrs: attrs ? buildFlags; 239 - 240 - mkBuildFlags = spec: 241 - "export BUNDLE_BUILD__${lib.toUpper spec.name}='${lib.concatStringsSep " " (map shellEscape spec.buildFlags)}'"; 242 - 243 - allBuildFlags = 244 - lib.concatStringsSep "\n" 245 - (map mkBuildFlags 246 - (lib.filter needsBuildFlags (attrValues instantiated))); 247 - 248 - derivation = stdenv.mkDerivation { 249 - inherit name; 53 + envPaths = lib.attrValues gems ++ lib.optional (!hasBundler) bundler; 54 + bundlerEnv = buildEnv { 55 + inherit name ignoreCollisions; 56 + paths = envPaths; 57 + pathsToLink = [ "/lib" ]; 58 + postBuild = '' 59 + source ${rubygems}/nix-support/setup-hook 250 60 251 - buildInputs = [ 252 - ruby 253 - bundler 254 - git 255 - ] ++ args.buildInputs or []; 256 - 257 - phases = [ "installPhase" "fixupPhase" ]; 258 - 259 - outputs = [ 260 - "out" # the installed libs/bins 261 - "bundle" # supporting files for bundler 262 - ]; 263 - 264 - installPhase = '' 265 - mkdir -p $bundle 266 - export BUNDLE_GEMFILE=$bundle/Gemfile 267 - cp ${gemfile} $BUNDLE_GEMFILE 268 - cp ${purifiedLockfile} $BUNDLE_GEMFILE.lock 269 - 270 - export NIX_GEM_SOURCES=${sources} 271 - export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath} 272 - 273 - export GEM_HOME=$out/${ruby.gemPath} 274 - export GEM_PATH=$NIX_BUNDLER_GEMPATH:$GEM_HOME 275 - mkdir -p $GEM_HOME 276 - 277 - ${allBuildFlags} 278 - 279 - mkdir gems 280 - cp ${bundler}/${bundler.ruby.gemPath}/cache/bundler-*.gem gems 281 - ${copyGems} 282 - 283 - ${lib.optionalString (!documentation) '' 284 - mkdir home 285 - HOME="$(pwd -P)/home" 286 - echo "gem: --no-rdoc --no-ri" > $HOME/.gemrc 287 - ''} 288 - 289 - mkdir env 290 - ${runPreInstallers} 291 - 292 - mkdir $out/bin 293 - cp ${./monkey_patches.rb} monkey_patches.rb 294 - export RUBYOPT="-rmonkey_patches.rb -I $(pwd -P)" 295 - bundler install --frozen --binstubs ${lib.optionalString enableParallelBuilding "--jobs $NIX_BUILD_CORES"} 296 - RUBYOPT="" 297 - 298 - runHook postInstall 299 - ''; 300 - 301 - inherit postInstall; 302 - 61 + ${ruby}/bin/ruby ${./gen-bin-stubs.rb} \ 62 + "${ruby}/bin/ruby" \ 63 + "${confFiles}/Gemfile" \ 64 + "$out/${ruby.gemPath}" \ 65 + "${bundler}/${ruby.gemPath}" \ 66 + ${shellEscape (toString envPaths)} 67 + '' + lib.optionalString (postBuild != null) postBuild; 303 68 passthru = { 304 - inherit ruby; 305 - inherit bundler; 306 - 69 + inherit ruby bundler meta gems; 307 70 env = let 308 71 irbrc = builtins.toFile "irbrc" '' 309 - if not ENV["OLD_IRBRC"].empty? 72 + if !(ENV["OLD_IRBRC"].nil? || ENV["OLD_IRBRC"].empty?) 310 73 require ENV["OLD_IRBRC"] 311 74 end 312 75 require 'rubygems' ··· 314 77 ''; 315 78 in stdenv.mkDerivation { 316 79 name = "interactive-${name}-environment"; 317 - nativeBuildInputs = [ ruby derivation ]; 80 + nativeBuildInputs = [ ruby bundlerEnv ]; 318 81 shellHook = '' 319 - export BUNDLE_GEMFILE=${derivation.bundle}/Gemfile 320 - export GEM_HOME=${derivation}/${ruby.gemPath} 321 - export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath} 322 - export GEM_PATH=$NIX_BUNDLER_GEMPATH:$GEM_HOME 82 + export BUNDLE_GEMFILE=${confFiles}/Gemfile 83 + export BUNDLE_PATH=${bundlerEnv}/${ruby.gemPath} 84 + export GEM_HOME=${bundlerEnv}/${ruby.gemPath} 85 + export GEM_PATH=${bundlerEnv}/${ruby.gemPath} 323 86 export OLD_IRBRC="$IRBRC" 324 87 export IRBRC=${irbrc} 325 88 ''; ··· 331 94 ''; 332 95 }; 333 96 }; 334 - 335 - inherit meta; 336 97 }; 337 98 338 - in derivation 99 + in 100 + 101 + bundlerEnv
+46
pkgs/development/interpreters/ruby/bundler-env/gen-bin-stubs.rb
··· 1 + require 'rbconfig' 2 + require 'rubygems' 3 + require 'rubygems/specification' 4 + require 'fileutils' 5 + 6 + # args/settings 7 + out = ENV["out"] 8 + ruby = ARGV[0] 9 + gemfile = ARGV[1] 10 + bundle_path = ARGV[2] 11 + bundler_gem_path = ARGV[3] 12 + paths = ARGV[4].split 13 + 14 + # generate binstubs 15 + FileUtils.mkdir_p("#{out}/bin") 16 + paths.each do |path| 17 + next unless File.directory?("#{path}/nix-support/gem-meta") 18 + 19 + name = File.read("#{path}/nix-support/gem-meta/name") 20 + executables = File.read("#{path}/nix-support/gem-meta/executables").split 21 + executables.each do |exe| 22 + File.open("#{out}/bin/#{exe}", "w") do |f| 23 + f.write(<<-EOF) 24 + #!#{ruby} 25 + # 26 + # This file was generated by Nix. 27 + # 28 + # The application '#{exe}' is installed as part of a gem, and 29 + # this file is here to facilitate running it. 30 + # 31 + 32 + ENV["BUNDLE_GEMFILE"] = "#{gemfile}" 33 + ENV["BUNDLE_PATH"] = "#{bundle_path}" 34 + 35 + gem_path = ENV["GEM_PATH"] 36 + ENV["GEM_PATH"] = "\#{gem_path}\#{":" unless gem_path.nil? || gem_path.empty?}#{bundler_gem_path}" 37 + 38 + require 'rubygems' 39 + require 'bundler/setup' 40 + 41 + load Gem.bin_path(#{name.inspect}, #{exe.inspect}) 42 + EOF 43 + FileUtils.chmod("+x", "#{out}/bin/#{exe}") 44 + end 45 + end 46 + end
pkgs/development/interpreters/ruby/bundler-env/mkrf_conf_xapian.rb pkgs/development/interpreters/ruby/gemconfig/mkrf_conf_xapian.rb
-246
pkgs/development/interpreters/ruby/bundler-env/monkey_patches.rb
··· 1 - require 'bundler' 2 - 3 - # Undo the RUBYOPT trickery. 4 - opt = ENV['RUBYOPT'].dup 5 - opt.gsub!(/-rmonkey_patches.rb -I [^ ]*/, '') 6 - ENV['RUBYOPT'] = opt 7 - 8 - Bundler.module_eval do 9 - class << self 10 - # mappings from original uris to store paths. 11 - def nix_gem_sources 12 - @nix_gem_sources ||= 13 - begin 14 - src = ENV['NIX_GEM_SOURCES'] 15 - eval(Bundler.read_file(src)) 16 - end 17 - end 18 - 19 - # extract the gemspecs from the gems pulled from Rubygems. 20 - def nix_gemspecs 21 - @nix_gemspecs ||= Dir.glob("gems/*.gem").map do |path| 22 - Bundler.rubygems.spec_from_gem(path) 23 - end 24 - end 25 - 26 - # swap out ENV 27 - def nix_with_env(env, &block) 28 - if env 29 - old_env = ENV.to_hash 30 - begin 31 - ENV.replace(env) 32 - block.call 33 - ensure 34 - ENV.replace(old_env) 35 - end 36 - else 37 - block.call 38 - end 39 - end 40 - 41 - # map a git uri to a fetchgit store path. 42 - def nix_git(uri) 43 - Pathname.new(nix_gem_sources["git"][uri]) 44 - end 45 - end 46 - end 47 - 48 - Bundler::Source::Git::GitProxy.class_eval do 49 - def checkout 50 - unless path.exist? 51 - FileUtils.mkdir_p(path.dirname) 52 - FileUtils.cp_r(Bundler.nix_git(@uri).join(".git"), path) 53 - system("chmod -R +w #{path}") 54 - end 55 - end 56 - 57 - def copy_to(destination, submodules=false) 58 - unless File.exist?(destination.join(".git")) 59 - FileUtils.mkdir_p(destination.dirname) 60 - FileUtils.cp_r(Bundler.nix_git(@uri), destination) 61 - system("chmod -R +w #{destination}") 62 - end 63 - end 64 - end 65 - 66 - Bundler::Fetcher.class_eval do 67 - def use_api 68 - true 69 - end 70 - 71 - def fetch_dependency_remote_specs(gem_names) 72 - Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}" 73 - deps_list = [] 74 - 75 - spec_list = gem_names.map do |name| 76 - spec = Bundler.nix_gemspecs.detect {|spec| spec.name == name } 77 - if spec.nil? 78 - msg = "WARNING: Could not find gemspec for '#{name}'" 79 - Bundler.ui.warn msg 80 - nil 81 - else 82 - dependencies = spec.dependencies. 83 - select {|dep| dep.type != :development}. 84 - map do |dep| 85 - deps_list << dep.name 86 - dep 87 - end 88 - 89 - [spec.name, spec.version, spec.platform, dependencies] 90 - end 91 - end 92 - 93 - spec_list.compact! 94 - 95 - [spec_list, deps_list.uniq] 96 - end 97 - end 98 - 99 - Bundler::Source::Rubygems.class_eval do 100 - # We copy all gems into $PWD/gems, and this allows RubyGems to find those 101 - # gems during installation. 102 - def fetchers 103 - @fetchers ||= [ 104 - Bundler::Fetcher.new(URI.parse("file://#{File.expand_path(Dir.pwd)}")) 105 - ] 106 - end 107 - 108 - # Look-up gems that were originally from RubyGems. 109 - def remote_specs 110 - @remote_specs ||= 111 - begin 112 - lockfile = Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile)) 113 - gem_names = lockfile.specs. 114 - select {|spec| spec.source.is_a?(Bundler::Source::Rubygems)}. 115 - map {|spec| spec.name} 116 - idx = Bundler::Index.new 117 - api_fetchers.each do |f| 118 - Bundler.ui.info "Fetching source index from #{f.uri}" 119 - idx.use f.specs(gem_names, self) 120 - end 121 - idx 122 - end 123 - end 124 - end 125 - 126 - Bundler::Installer.class_eval do 127 - 128 - # WHY: 129 - # This allows us to provide a typical Nix experience, where 130 - # `buildInputs` and/or `preInstall` may set up the $PATH and other env-vars 131 - # as needed. By swapping out the environment per install, we can have finer 132 - # grained control than we would have otherwise. 133 - # 134 - # HOW: 135 - # This is a wrapper around the original `install_gem_from_spec`. 136 - # We expect that a "pre-installer" might exist at `pre-installers/<gem-name>`, 137 - # and if it does, we execute it. 138 - # The pre-installer is expected to dump its environment variables as a Ruby 139 - # hash to `env/<gem-name>`. 140 - # We then swap out the environment for the duration of the install, 141 - # and then set it back to what it was originally. 142 - alias original_install_gem_from_spec install_gem_from_spec 143 - def install_gem_from_spec(spec, standalone = false, worker = 0) 144 - env_dump = "env/#{spec.name}" 145 - if File.exist?(env_dump) 146 - env = eval(Bundler.read_file(env_dump)) 147 - unless env 148 - Bundler.ui.error "The environment variables for #{spec.name} could not be loaded!" 149 - exit 1 150 - end 151 - Bundler.nix_with_env(env) do 152 - original_install_gem_from_spec(spec, standalone, worker) 153 - end 154 - else 155 - original_install_gem_from_spec(spec, standalone, worker) 156 - end 157 - end 158 - 159 - def generate_bundler_executable_stubs(spec, options = {}) 160 - return if spec.executables.empty? 161 - 162 - out = ENV['out'] 163 - 164 - spec.executables.each do |executable| 165 - next if executable == "bundle" || executable == "bundler" 166 - 167 - binstub_path = "#{out}/bin/#{executable}" 168 - 169 - File.open(binstub_path, 'w', 0777 & ~File.umask) do |f| 170 - f.print <<-TEXT 171 - #!#{RbConfig.ruby} 172 - 173 - old_gemfile = ENV["BUNDLE_GEMFILE"] 174 - old_gem_home = ENV["GEM_HOME"] 175 - old_gem_path = ENV["GEM_PATH"] 176 - 177 - ENV["BUNDLE_GEMFILE"] = 178 - "#{ENV["BUNDLE_GEMFILE"]}" 179 - ENV["GEM_HOME"] = 180 - "#{ENV["GEM_HOME"]}" 181 - ENV["GEM_PATH"] = 182 - "#{ENV["NIX_BUNDLER_GEMPATH"]}:\#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}" 183 - 184 - require 'rubygems' 185 - require 'bundler/setup' 186 - 187 - ENV["BUNDLE_GEMFILE"] = old_gemfile 188 - ENV["GEM_HOME"] = old_gem_home 189 - ENV["GEM_PATH"] = old_gem_path 190 - 191 - load Gem.bin_path('#{spec.name}', '#{executable}') 192 - TEXT 193 - end 194 - end 195 - end 196 - end 197 - 198 - Gem::Installer.class_eval do 199 - # Make the wrappers automagically use bundler. 200 - # 201 - # Stage 1. 202 - # Set $BUNDLE_GEMFILE so bundler knows what gems to load. 203 - # Set $GEM_HOME to the installed gems, because bundler looks there for 204 - # non-Rubygems installed gems (e.g. git/svn/path sources). 205 - # Set $GEM_PATH to include both bundler and installed gems. 206 - # 207 - # Stage 2. 208 - # Setup bundler, locking down the gem versions. 209 - # 210 - # Stage 3. 211 - # Reset $BUNDLE_GEMFILE, $GEM_HOME, $GEM_PATH. 212 - # 213 - # Stage 4. 214 - # Run the actual executable. 215 - def app_script_text(bin_file_name) 216 - return <<-TEXT 217 - #!#{RbConfig.ruby} 218 - # 219 - # This file was generated by Nix's RubyGems. 220 - # 221 - # The application '#{spec.name}' is installed as part of a gem, and 222 - # this file is here to facilitate running it. 223 - # 224 - 225 - old_gemfile = ENV["BUNDLE_GEMFILE"] 226 - old_gem_home = ENV["GEM_HOME"] 227 - old_gem_path = ENV["GEM_PATH"] 228 - 229 - ENV["BUNDLE_GEMFILE"] = 230 - "#{ENV["BUNDLE_GEMFILE"]}" 231 - ENV["GEM_HOME"] = 232 - "#{ENV["GEM_HOME"]}" 233 - ENV["GEM_PATH"] = 234 - "#{ENV["NIX_BUNDLER_GEMPATH"]}:\#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}" 235 - 236 - require 'rubygems' 237 - require 'bundler/setup' 238 - 239 - ENV["BUNDLE_GEMFILE"] = old_gemfile 240 - ENV["GEM_HOME"] = old_gem_home 241 - ENV["GEM_PATH"] = old_gem_path 242 - 243 - load Gem.bin_path('#{spec.name}', '#{bin_file_name}') 244 - TEXT 245 - end 246 - end
-29
pkgs/development/interpreters/ruby/bundler-env/package-1.8.rb
··· 1 - require 'rubygems/installer' 2 - require 'rubygems/builder' 3 - 4 - # Simulate RubyGems 2.0 behavior. 5 - 6 - module Gem::Package 7 - def self.new(gem) 8 - @gem = gem 9 - self 10 - end 11 - 12 - def self.extract_files(dir) 13 - installer = Gem::Installer.new @gem 14 - installer.unpack(dir) 15 - end 16 - 17 - def self.build(skip_validation=false) 18 - builder = Gem::Builder.new(spec) 19 - builder.build 20 - end 21 - 22 - def self.spec=(spec) 23 - @spec = spec 24 - end 25 - 26 - def self.spec 27 - @spec ||= Gem::Installer.new(@gem).spec 28 - end 29 - end
pkgs/development/interpreters/ruby/bundler-env/xapian-Rakefile pkgs/development/interpreters/ruby/gemconfig/xapian-Rakefile
+5 -12
pkgs/development/interpreters/ruby/bundler.nix
··· 1 1 { buildRubyGem, makeWrapper, ruby, coreutils }: 2 2 3 - buildRubyGem { 4 - name = "bundler-1.10.6"; 5 - namePrefix = ""; 3 + buildRubyGem rec { 4 + inherit ruby; 5 + name = "${gemName}-${version}"; 6 + gemName = "bundler"; 7 + version = "1.10.6"; 6 8 sha256 = "1vlzfq0bkkj4jyq6av0y55mh5nj5n0f3mfbmmifwgkh44g8k6agv"; 7 9 dontPatchShebangs = true; 8 - postInstall = '' 9 - find $out -type f -perm -0100 | while read f; do 10 - substituteInPlace $f \ 11 - --replace "/usr/bin/env" "${coreutils}/bin/env" 12 - done 13 - 14 - wrapProgram $out/bin/bundler \ 15 - --prefix PATH ":" ${ruby}/bin 16 - ''; 17 10 }
-136
pkgs/development/interpreters/ruby/gem.nix
··· 1 - { lib, ruby, rubygemsFun, fetchurl, makeWrapper, git } @ defs: 2 - 3 - lib.makeOverridable ( 4 - 5 - { name 6 - , ruby ? defs.ruby 7 - , rubygems ? (rubygemsFun ruby) 8 - , stdenv ? ruby.stdenv 9 - , namePrefix ? "${lib.replaceStrings ["-"] ["_"] ruby.name}" + "-" 10 - , buildInputs ? [] 11 - , doCheck ? false 12 - , dontBuild ? true 13 - , meta ? {} 14 - , gemPath ? [] 15 - , ...} @ attrs: 16 - 17 - stdenv.mkDerivation (attrs // { 18 - inherit ruby rubygems; 19 - inherit doCheck; 20 - 21 - buildInputs = [ ruby rubygems makeWrapper git ] ++ buildInputs; 22 - 23 - name = namePrefix + name; 24 - 25 - src = if attrs ? src 26 - then attrs.src 27 - else fetchurl { 28 - url = "http://rubygems.org/downloads/${attrs.name}.gem"; 29 - inherit (attrs) sha256; 30 - }; 31 - 32 - phases = [ "unpackPhase" "patchPhase" "buildPhase" "checkPhase" "installPhase" "fixupPhase" ]; 33 - 34 - # The source is expected to either be a gem package or a directory. 35 - # 36 - # - Gem packages are already built, so they don't even need to be unpacked. 37 - # They will skip the buildPhase. 38 - # - A directory containing the sources will need to go through all of the 39 - # usual phases. 40 - unpackPhase= '' 41 - gemRegex="\.gem" 42 - if [[ $src =~ $gemRegex ]] 43 - then 44 - runHook preUnpack 45 - echo "source is a gem package, won't unpack" 46 - gempkg=$src 47 - dontBuild=1 48 - runHook postUnpack 49 - else 50 - # Fall back to the original thing for everything else. 51 - unpackPhase 52 - fi 53 - ''; 54 - 55 - checkPhase = "true"; 56 - 57 - buildPhase = '' 58 - runHook preBuild 59 - 60 - # TODO: Investigate. The complete working tree is touched by fetchgit. 61 - if [ -d .git ]; then 62 - git reset 63 - fi 64 - 65 - gemspec=$(find . -name '*.gemspec') 66 - echo "found the following gemspecs:" 67 - echo "$gemspec" 68 - 69 - gemspec=$(echo "$gemspec" | head -n1) 70 - echo "building $gemspec" 71 - 72 - exec 3>&1 73 - output=$(gem build $gemspec | tee >(cat - >&3)) 74 - exec 3>&- 75 - 76 - gempkg=$(echo "$output" | grep -oP 'File: \K(.*)') 77 - 78 - echo "gem package built: $gempkg" 79 - 80 - runHook postBuild 81 - ''; 82 - 83 - installPhase = '' 84 - runHook preInstall 85 - 86 - # NOTE: This does NOT build the unpacked gem, but installs $src directly. 87 - # Gems that have not been downloaded from rubygems.org may need a 88 - # separate buildPhase. 89 - # --ignore-dependencies is necessary as rubygems otherwise always 90 - # connects to the repository, thus breaking pure builds. 91 - GEM_HOME=$out/${ruby.gemPath} \ 92 - gem install \ 93 - --local \ 94 - --force \ 95 - --http-proxy "http://nodtd.invalid" \ 96 - --ignore-dependencies \ 97 - --build-root "/" \ 98 - --backtrace \ 99 - $gempkg $gemFlags -- $buildFlags 100 - 101 - # Yes, we really do need the $out/${ruby.gemPath}/cache. 102 - # This is very important in order for many parts of RubyGems/Bundler to not blow up. 103 - # See https://github.com/bundler/bundler/issues/3327 104 - 105 - mkdir -p $out/bin 106 - for prog in $out/${ruby.gemPath}/gems/*/bin/*; do 107 - makeWrapper $prog $out/bin/$(basename $prog) \ 108 - --prefix GEM_PATH : "$out/${ruby.gemPath}:$GEM_PATH" \ 109 - --prefix RUBYLIB : "${rubygems}/lib" \ 110 - $extraWrapperFlags ''${extraWrapperFlagsArray[@]} 111 - done 112 - #--prefix RUBYOPT rubygems \ 113 - 114 - # looks like useless files which break build repeatability and consume space 115 - rm -fv $out/${ruby.gemPath}/doc/*/*/created.rid || true 116 - rm -fv $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true 117 - 118 - mkdir -p $out/nix-support 119 - 120 - cat > $out/nix-support/setup-hook <<EOF 121 - if [[ "\$GEM_PATH" != *$out* ]]; then 122 - addToSearchPath GEM_PATH $out/${ruby.gemPath} 123 - fi 124 - EOF 125 - 126 - runHook postInstall 127 - ''; 128 - 129 - propagatedBuildInputs = gemPath; 130 - propagatedUserEnvPkgs = gemPath; 131 - 132 - passthru.isRubyGem = true; 133 - inherit meta; 134 - }) 135 - 136 - )
-69
pkgs/development/interpreters/ruby/load-ruby-env.nix
··· 1 - { ruby, lib, callPackage, gemFixes, fetchurl, fetchgit, buildRubyGem }@defs: 2 - 3 - # This function builds a set of gems. You first convert your Gemfile to an attrset 4 - # called a "gemset", and then use this function to build the gemset. 5 - # 6 - # A gemset looks like the following: 7 - # 8 - # { 9 - # libv8 = { 10 - # version = "3.16.14.7"; 11 - # src = { 12 - # type = "gem"; 13 - # sha256 = "..."; 14 - # }; 15 - # }; 16 - # therubyracer = { 17 - # version = "0.12.1"; 18 - # dependencies = [ "libv8" ]; 19 - # src = { 20 - # type = "gem"; 21 - # sha256 = "..."; 22 - # }; 23 - # }; 24 - # } 25 - # 26 - # If you use these gems as build inputs, the GEM_PATH will be updated 27 - # appropriately, and command like `bundle exec` should work out of the box. 28 - 29 - { gemset, ruby ? defs.ruby, fixes ? gemFixes }@args: 30 - 31 - let 32 - const = x: y: x; 33 - 34 - fetchers.path = attrs: attrs.src.path; 35 - fetchers.gem = attrs: fetchurl { 36 - url = "${attrs.src.source or "https://rubygems.org"}/downloads/${attrs.name}-${attrs.version}.gem"; 37 - inherit (attrs.src) sha256; 38 - }; 39 - fetchers.git = attrs: fetchgit { 40 - inherit (attrs.src) url rev sha256 fetchSubmodules; 41 - leaveDotGit = true; 42 - }; 43 - 44 - instantiate = (attrs: 45 - let 46 - defaultAttrs = { 47 - name = "${attrs.name}-${attrs.version}"; 48 - inherit ruby gemPath; 49 - }; 50 - gemPath = map (name: gemset''."${name}") (attrs.dependencies or []); 51 - fixedAttrs = attrs // (fixes."${attrs.name}" or (const {})) attrs; 52 - withSource = fixedAttrs // 53 - (if (lib.isDerivation fixedAttrs.src || builtins.isString fixedAttrs.src) 54 - then {} 55 - else { src = (fetchers."${fixedAttrs.src.type}" fixedAttrs); }); 56 - 57 - in 58 - buildRubyGem (withSource // defaultAttrs) 59 - ); 60 - 61 - gemset' = if builtins.isAttrs gemset then gemset else import gemset; 62 - 63 - gemset'' = lib.flip lib.mapAttrs gemset' (name: attrs: 64 - if (lib.isDerivation attrs) 65 - then attrs 66 - else instantiate (attrs // { inherit name; }) 67 - ); 68 - 69 - in gemset''
+16 -18
pkgs/development/interpreters/ruby/rubygems.nix
··· 1 - args @ { makeWrapper, ruby, ... }: with args; 1 + { stdenv, lib, fetchurl, makeWrapper, ruby }: 2 2 3 - rec { 4 - name = "rubygems-" + version; 3 + stdenv.mkDerivation rec { 4 + name = "rubygems-${version}"; 5 5 version = "2.4.1"; 6 6 src = fetchurl { 7 7 url = "http://production.cf.rubygems.org/rubygems/${name}.tgz"; 8 8 sha256 = "0cpr6cx3h74ykpb0cp4p4xg7a8j0bhz3sk271jq69l4mm4zy4h4f"; 9 9 }; 10 10 11 + patches = [ ./gem_hook.patch ]; 12 + 11 13 buildInputs = [ruby makeWrapper]; 12 - configureFlags = []; 14 + 15 + buildPhase = ":"; 13 16 14 - doInstall = fullDepEntry ('' 17 + installPhase = '' 15 18 ruby setup.rb --prefix=$out/ 19 + 16 20 wrapProgram $out/bin/gem --prefix RUBYLIB : $out/lib 17 - find $out -type f -name "*.rb" | xargs sed -i "s@/usr/bin/env@$(type -p env)@g" 21 + 22 + find $out -type f -name "*.rb" | 23 + xargs sed -i "s@/usr/bin/env@$(type -p env)@g" 24 + 18 25 mkdir -pv $out/nix-support 19 26 cat > $out/nix-support/setup-hook <<EOF 20 27 export RUBYOPT=rubygems 21 28 addToSearchPath RUBYLIB $out/lib 22 - EOF'') ["minInit" "addInputs" "doUnpack" "defEnsureDir"]; 23 - 24 - /* doConfigure should be specified separately */ 25 - phaseNames = ["doPatch" "doInstall"]; 29 + EOF 30 + ''; 26 31 27 32 meta = { 28 - description = "Ruby gems package collection"; 29 - longDescription = '' 30 - Nix can create nix packages from gems. 31 - 32 - To use it by installing gem-nix package. 33 - ''; 33 + description = "A package management framework for Ruby"; 34 34 }; 35 - 36 - patches = [ ./gem_hook.patch ]; 37 35 }
+21
pkgs/development/libraries/gecode/3.nix
··· 1 + { stdenv, fetchurl, perl }: 2 + 3 + stdenv.mkDerivation rec { 4 + name = "gecode-${version}"; 5 + version = "3.7.3"; 6 + 7 + src = fetchurl { 8 + url = "http://www.gecode.org/download/${name}.tar.gz"; 9 + sha256 = "0k45jas6p3cyldgyir1314ja3174sayn2h2ly3z9b4dl3368pk77"; 10 + }; 11 + 12 + buildInputs = [ perl ]; 13 + 14 + meta = with stdenv.lib; { 15 + license = licenses.mit; 16 + homepage = http://www.gecode.org; 17 + description = "Toolkit for developing constraint-based systems"; 18 + platforms = platforms.all; 19 + maintainers = [ maintainers.manveru ]; 20 + }; 21 + }
+2 -1
pkgs/development/tools/vagrant/default.nix
··· 7 7 version = "1.8.0"; 8 8 rake = buildRubyGem { 9 9 inherit ruby; 10 - name = "rake-10.4.2"; 10 + gemName = "rake"; 11 + version = "10.4.2"; 11 12 sha256 = "1rn03rqlf1iv6n87a78hkda2yqparhhaivfjpizblmxvlw2hk5r8"; 12 13 }; 13 14
+1 -1
pkgs/tools/audio/mpdcron/default.nix
··· 1 - { stdenv, fetchgit, autoconf, automake, libtool, pkgconfig, glib, libdaemon, buildRubyGem 1 + { stdenv, fetchgit, autoconf, automake, libtool, pkgconfig, glib, libdaemon 2 2 , mpd_clientlib, curl, sqlite, ruby, bundlerEnv, libnotify, pandoc }: 3 3 4 4 let
-2
pkgs/tools/misc/fluentd/default.nix
··· 8 8 lockfile = ./Gemfile.lock; 9 9 gemset = ./gemset.nix; 10 10 11 - buildInputs = [ curl ]; 12 - 13 11 meta = with lib; { 14 12 description = "A data collector"; 15 13 homepage = http://www.fluentd.org/;
+7 -10
pkgs/top-level/all-packages.nix
··· 5278 5278 ruby = ruby_2_1_3; 5279 5279 }; 5280 5280 bundler = callPackage ../development/interpreters/ruby/bundler.nix { }; 5281 - bundler_HEAD = import ../development/interpreters/ruby/bundler-head.nix { 5282 - inherit buildRubyGem coreutils fetchgit; 5283 - }; 5284 - defaultGemConfig = callPackage ../development/interpreters/ruby/bundler-env/default-gem-config.nix { }; 5285 - buildRubyGem = callPackage ../development/interpreters/ruby/gem.nix { }; 5281 + bundler_HEAD = bundler; 5282 + defaultGemConfig = callPackage ../development/interpreters/ruby/gemconfig/default.nix { }; 5283 + buildRubyGem = callPackage ../development/interpreters/ruby/build-ruby-gem { }; 5286 5284 bundlerEnv = callPackage ../development/interpreters/ruby/bundler-env { }; 5287 5285 5288 5286 inherit (callPackage ../development/interpreters/ruby {}) ··· 5298 5296 ruby_2_1 = ruby_2_1_7; 5299 5297 ruby_2_2 = ruby_2_2_3; 5300 5298 5301 - rubygemsFun = ruby: builderDefsPackage (callPackage ../development/interpreters/ruby/rubygems.nix) { 5302 - inherit ruby; 5303 - }; 5304 - rubygems = hiPrio (rubygemsFun ruby); 5299 + rubygems = hiPrio (callPackage ../development/interpreters/ruby/rubygems.nix {}); 5305 5300 5306 5301 rq = callPackage ../applications/networking/cluster/rq { }; 5307 5302 ··· 6449 6444 6450 6445 gdbm = callPackage ../development/libraries/gdbm { }; 6451 6446 6452 - gecode = callPackage ../development/libraries/gecode { }; 6447 + gecode_3 = callPackage ../development/libraries/gecode/3.nix { }; 6448 + gecode_4 = callPackage ../development/libraries/gecode { }; 6449 + gecode = gecode_4; 6453 6450 6454 6451 gegl = callPackage ../development/libraries/gegl { }; 6455 6452