2015-01-20 01:02:47 +00:00
|
|
|
{ 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.
|
2015-01-20 01:02:47 +00:00
|
|
|
# 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;
|
2015-01-20 01:02:47 +00:00
|
|
|
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 01:02:47 +00:00
|
|
|
|
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; })
|
|
|
|
);
|
|
|
|
|
2015-01-21 01:21:03 +00:00
|
|
|
needsPreInstall = attrs:
|
|
|
|
(attrs ? preInstall) || (attrs ? buildInputs);
|
|
|
|
|
|
|
|
createPreInstallers = lib.fold (next: acc:
|
|
|
|
if !needsPreInstall next
|
|
|
|
then acc
|
|
|
|
else acc + ''
|
|
|
|
cp ${writeScript "${next.name}-pre-install" ''
|
|
|
|
#!/bin/sh
|
|
|
|
buildInputs="${toString (next.buildInputs or [])}"
|
|
|
|
source ${stdenv}/setup
|
|
|
|
|
|
|
|
${next.preInstall or ""}
|
|
|
|
|
|
|
|
${ruby}/bin/ruby -e '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:
|
2015-01-20 01:02:47 +00:00
|
|
|
if next.source.type == "gem"
|
2015-01-20 06:07:55 +00:00
|
|
|
then acc + "cp ${next.src} gems/${gemName next}\n"
|
2015-01-20 01:02:47 +00:00
|
|
|
else acc
|
2015-01-20 06:07:55 +00:00
|
|
|
) "" (attrValues instantiated);
|
2015-01-20 01:02:47 +00:00
|
|
|
|
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" {} ''
|
2015-01-20 01:02:47 +00:00
|
|
|
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;
|
2015-01-20 01:02:47 +00:00
|
|
|
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}
|
2015-01-20 01:02:47 +00:00
|
|
|
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
|
|
|
|
|
2015-01-20 01:02:47 +00:00
|
|
|
mkdir gems
|
2015-01-20 06:07:55 +00:00
|
|
|
${copyGems}
|
2015-01-20 01:02:47 +00:00
|
|
|
|
2015-01-21 01:21:03 +00:00
|
|
|
mkdir env
|
|
|
|
mkdir pre-installers
|
|
|
|
${createPreInstallers}
|
|
|
|
|
2015-01-20 06:07:55 +00:00
|
|
|
mkdir $out/bin
|
2015-01-20 01:02:47 +00:00
|
|
|
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
|
|
|
};
|
|
|
|
}
|