nixpkgs/pkgs/development/interpreters/ruby/bundler-env.nix

275 lines
7.2 KiB
Nix
Raw Normal View History

{ stdenv, runCommand, writeText, writeScript, writeScriptBin, ruby, lib
, callPackage , gemFixes, fetchurl, fetchgit, buildRubyGem
#, bundler_PATCHED
, bundler_HEAD
, git
2015-01-18 23:47:28 +00:00
}@defs:
# This is a work-in-progress.
# The idea is that his will replace load-ruby-env.nix.
2015-01-18 23:47:28 +00:00
{ name, gemset, gemfile, lockfile, ruby ? defs.ruby, fixes ? gemFixes }@args:
let
const = x: y: x;
bundler = bundler_HEAD.override { inherit ruby; };
inherit (builtins) attrValues;
2015-01-18 23:47:28 +00:00
2015-01-20 06:07:55 +00:00
gemName = attrs: "${attrs.name}-${attrs.version}.gem";
2015-01-20 06:07:55 +00:00
fetchers.path = attrs: attrs.source.path;
fetchers.gem = attrs: fetchurl {
url = "${attrs.source.source or "https://rubygems.org"}/downloads/${gemName attrs}";
inherit (attrs.source) sha256;
};
2015-01-18 23:47:28 +00:00
fetchers.git = attrs: fetchgit {
2015-01-20 06:07:55 +00:00
inherit (attrs.source) url rev sha256 fetchSubmodules;
2015-01-18 23:47:28 +00:00
leaveDotGit = true;
};
2015-01-20 06:07:55 +00:00
applySrc = attrs:
attrs // {
src = (fetchers."${attrs.source.type}" attrs);
};
applyFixes = attrs:
if fixes ? "${attrs.name}"
then attrs // fixes."${attrs.name}" attrs
else attrs;
2015-01-21 00:09:02 +00:00
needsPatch = attrs:
(attrs ? patches) || (attrs ? prePatch) || (attrs ? postPatch);
2015-01-20 06:07:55 +00:00
# patch a gem or source tree.
# for gems, the gem is unpacked, patched, and then repacked.
# see: https://github.com/fedora-ruby/gem-patch/blob/master/lib/rubygems/patcher.rb
2015-01-21 00:09:02 +00:00
applyPatches = attrs:
if !needsPatch attrs
2015-01-20 06:07:55 +00:00
then attrs
else attrs // { src =
stdenv.mkDerivation {
name = gemName attrs;
phases = [ "unpackPhase" "patchPhase" "installPhase" ];
2015-01-21 00:09:02 +00:00
buildInputs = [ ruby ] ++ attrs.buildInputs or [];
patches = attrs.patches or [ ];
prePatch = attrs.prePatch or "true";
postPatch = attrs.postPatch or "true";
2015-01-20 06:07:55 +00:00
unpackPhase = ''
runHook preUnpack
if [[ -f ${attrs.src} ]]; then
isGem=1
2015-01-21 00:09:02 +00:00
# we won't know the name of the directory that RubyGems creates,
# so we'll just use a glob to find it and move it over.
gem unpack ${attrs.src} --target=container
cp -r container/* contents
rm -r container
2015-01-20 06:07:55 +00:00
else
cp -r ${attrs.src} contents
chmod -R +w contents
fi
cd contents
runHook postUnpack
'';
installPhase = ''
runHook preInstall
if [[ -n "$isGem" ]]; then
${writeScript "repack.rb" ''
#!${ruby}/bin/ruby
require 'rubygems'
2015-01-21 00:09:02 +00:00
require 'rubygems/package'
2015-01-20 06:07:55 +00:00
require 'fileutils'
2015-01-21 00:09:02 +00:00
if defined?(Encoding.default_internal)
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
end
if Gem::VERSION < '2.0'
load "${./package-1.8.rb}"
end
2015-01-20 06:07:55 +00:00
out = ENV['out']
files = Dir['**/{.[^\.]*,*}']
2015-01-21 00:09:02 +00:00
2015-01-20 06:07:55 +00:00
package = Gem::Package.new("${attrs.src}")
patched_package = Gem::Package.new(package.spec.file_name)
patched_package.spec = package.spec.clone
patched_package.spec.files = files
2015-01-21 00:09:02 +00:00
patched_package.build(false)
FileUtils.cp(patched_package.spec.file_name, out)
2015-01-20 06:07:55 +00:00
''}
else
cp -r . out
fi
runHook postInstall
'';
};
};
2015-01-18 23:47:28 +00:00
instantiate = (attrs:
2015-01-21 00:09:02 +00:00
applyPatches (applyFixes (applySrc attrs))
2015-01-18 23:47:28 +00:00
);
instantiated = lib.flip lib.mapAttrs (import gemset) (name: attrs:
instantiate (attrs // { inherit name; })
);
needsPreInstall = attrs:
2015-01-21 03:20:28 +00:00
(attrs ? preInstall) || (attrs ? buildInputs) || (attrs ? nativeBuildInputs);
createPreInstallers = lib.fold (next: acc:
if !needsPreInstall next
then acc
else acc + ''
cp ${writeScript "${next.name}-pre-install" ''
#!/bin/sh
2015-01-21 03:20:28 +00:00
buildInputs="${toString (next.buildInputs or [])}"
2015-01-21 03:20:28 +00:00
nativeBuildInputs="${toString (next.nativeBuildInputs or [])}"
source ${stdenv}/setup
${next.preInstall or ""}
2015-01-21 03:20:28 +00:00
${ruby}/bin/ruby -e 'print ENV.inspect' > env/${next.name}
''} pre-installers/${next.name}
''
) "" (attrValues instantiated);
2015-01-20 06:07:55 +00:00
# copy *.gem to ./gems
copyGems = lib.fold (next: acc:
if next.source.type == "gem"
2015-01-20 06:07:55 +00:00
then acc + "cp ${next.src} gems/${gemName next}\n"
else acc
2015-01-20 06:07:55 +00:00
) "" (attrValues instantiated);
2015-01-18 23:47:28 +00:00
runRuby = name: env: command:
runCommand name env ''
${ruby}/bin/ruby ${writeText name command}
'';
# TODO: include json_pure, so the version of ruby doesn't matter.
# not all rubies have support for JSON built-in,
# so we'll convert JSON to ruby expressions.
2015-01-20 06:07:55 +00:00
json2rb = writeScript "json2rb" ''
2015-01-18 23:47:28 +00:00
#!${ruby}/bin/ruby
begin
require 'json'
rescue LoadError => ex
require 'json_pure'
end
puts JSON.parse(STDIN.read).inspect
'';
# dump the instantiated gemset as a ruby expression.
serializedGemset = runCommand "gemset.rb" { json = builtins.toJSON instantiated; } ''
2015-01-20 06:07:55 +00:00
printf '%s' "$json" | ${json2rb} > $out
2015-01-18 23:47:28 +00:00
'';
# this is a mapping from a source type and identifier (uri/path/etc)
# to the pure store path.
# we'll use this from the patched bundler to make fetching sources pure.
sources = runRuby "sources.rb" { gemset = serializedGemset; } ''
out = ENV['out']
gemset = eval(File.read(ENV['gemset']))
sources = {
"git" => { },
"path" => { },
"gem" => { },
"svn" => { }
}
gemset.each_value do |spec|
type = spec["source"]["type"]
val = spec["src"]
key =
case type
when "gem"
spec["name"]
when "git"
spec["source"]["url"]
when "path"
spec["source"]["originalPath"]
when "svn"
nil # TODO
end
sources[type][key] = val if key
end
File.open(out, "wb") do |f|
f.print sources.inspect
end
'';
# rewrite PATH sources to point into the nix store.
2015-01-20 06:07:55 +00:00
purifiedLockfile = runRuby "purifiedLockfile" {} ''
out = ENV['out']
sources = eval(File.read("${sources}"))
paths = sources["path"]
2015-01-18 23:47:28 +00:00
2015-01-20 06:07:55 +00:00
lockfile = File.read("${lockfile}")
2015-01-18 23:47:28 +00:00
paths.each_pair do |impure, pure|
lockfile.gsub!(/^ remote: #{Regexp.escape(impure)}/, " remote: #{pure}")
end
2015-01-20 06:07:55 +00:00
File.open(out, "wb") do |f|
f.print lockfile
end
2015-01-18 23:47:28 +00:00
'';
in
stdenv.mkDerivation {
inherit name;
buildInputs = [
ruby
bundler
git
];
phases = [ "installPhase" "fixupPhase" ];
2015-01-18 23:47:28 +00:00
outputs = [
2015-01-20 06:07:55 +00:00
"out" # the installed libs/bins
"bundle" # supporting files for bundler
2015-01-18 23:47:28 +00:00
];
installPhase = ''
# Copy the Gemfile and Gemfile.lock
2015-01-20 06:07:55 +00:00
mkdir -p $bundle
export BUNDLE_GEMFILE=$bundle/Gemfile
2015-01-18 23:47:28 +00:00
cp ${gemfile} $BUNDLE_GEMFILE
2015-01-20 06:07:55 +00:00
cp ${purifiedLockfile} $BUNDLE_GEMFILE.lock
2015-01-18 23:47:28 +00:00
export NIX_GEM_SOURCES=${sources}
export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath}
2015-01-18 23:47:28 +00:00
export GEM_HOME=$out/${ruby.gemPath}
export GEM_PATH=$GEM_HOME
mkdir -p $GEM_HOME
mkdir gems
2015-01-20 06:07:55 +00:00
${copyGems}
mkdir env
mkdir pre-installers
${createPreInstallers}
2015-01-20 06:07:55 +00:00
mkdir $out/bin
cp ${./monkey_patches.rb} monkey_patches.rb
export RUBYOPT="-rmonkey_patches.rb -I $(pwd -P)"
2015-01-20 06:07:55 +00:00
bundler install --frozen --binstubs
2015-01-18 23:47:28 +00:00
'';
passthru = {
inherit ruby;
2015-01-20 06:07:55 +00:00
inherit bundler;
2015-01-18 23:47:28 +00:00
};
}