ruby: refactoring, and package bundix

This commit is contained in:
Charles Strahan 2015-01-20 01:07:55 -05:00
parent bf16d03075
commit 221509b0a6
7 changed files with 193 additions and 44 deletions

View file

@ -0,0 +1,4 @@
source "http://rubygems.org"
gem "bundix",
:git => "https://github.com/cstrahan/bundix.git",
:ref => "5df25b11b5b86e636754d54c2a8859c7c6ec78c7"

View file

@ -0,0 +1,19 @@
GIT
remote: https://github.com/cstrahan/bundix.git
revision: 5df25b11b5b86e636754d54c2a8859c7c6ec78c7
ref: 5df25b11b5b86e636754d54c2a8859c7c6ec78c7
specs:
bundix (0.1.0)
bundler (~> 1.7.9)
thor (~> 0.19.1)
GEM
remote: http://rubygems.org/
specs:
thor (0.19.1)
PLATFORMS
ruby
DEPENDENCIES
bundix!

View file

@ -0,0 +1,9 @@
{ ruby, bundlerEnv }:
bundlerEnv {
name = "bundix";
inherit ruby;
gemset = ./gemset.nix;
gemfile = ./Gemfile;
lockfile = ./Gemfile.lock;
}

View file

@ -0,0 +1,22 @@
{
"bundix" = {
version = "0.1.0";
source = {
type = "git";
url = "https://github.com/cstrahan/bundix.git";
rev = "5df25b11b5b86e636754d54c2a8859c7c6ec78c7";
fetchSubmodules = false;
sha256 = "0334jsavpzkikcs7wrx7a3r0ilvr5vsnqd34lhc58b8cgvgll47p";
};
dependencies = [
"thor"
];
};
"thor" = {
version = "0.19.1";
source = {
type = "gem";
sha256 = "08p5gx18yrbnwc6xc0mxvsfaxzgy2y9i78xq7ds0qmdm67q39y4z";
};
};
}

View file

@ -12,53 +12,103 @@
let
const = x: y: x;
#bundler = bundler_PATCHED;
bundler = bundler_HEAD.override { inherit ruby; };
inherit (builtins) attrValues;
fetchers.path = attrs: attrs.src.path;
fetchers.gem = attrs:
let fname = "${attrs.name}-${attrs.version}.gem";
in toString (runCommand fname {
gem = fetchurl {
url = "${attrs.src.source or "https://rubygems.org"}/downloads/${fname}";
inherit (attrs.src) sha256;
};
} ''
mkdir $out
cp $gem $out/${fname}
'') + "/${fname}";
gemName = attrs: "${attrs.name}-${attrs.version}.gem";
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;
};
fetchers.git = attrs: fetchgit {
inherit (attrs.src) url rev sha256 fetchSubmodules;
inherit (attrs.source) url rev sha256 fetchSubmodules;
leaveDotGit = true;
};
fixSpec = attrs:
attrs // (fixes."${attrs.name}" or (const {})) attrs;
applySrc = attrs:
attrs // {
src = (fetchers."${attrs.source.type}" attrs);
};
applyFixes = attrs:
if fixes ? "${attrs.name}"
then attrs // fixes."${attrs.name}" attrs
else attrs;
# 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
applyPatches = { attrs }:
if (!(attrs ? patches))
then attrs
else attrs // { src =
stdenv.mkDerivation {
name = gemName attrs;
phases = [ "unpackPhase" "patchPhase" "installPhase" ];
buildInputs = [ ruby ];
inherit (attrs) patches;
unpackPhase = ''
runHook preUnpack
if [[ -f ${attrs.src} ]]; then
isGem=1
gem unpack ${attrs.src} --target=contents
else
cp -r ${attrs.src} contents
chmod -R +w contents
fi
cd contents
runHook postUnpack
'';
installPhase = ''
runHook preInstall
mkdir build
if [[ -n "$isGem" ]]; then
${writeScript "repack.rb" ''
#!${ruby}/bin/ruby
require 'rubygems'
require 'fileutils'
out = ENV['out']
files = Dir['**/{.[^\.]*,*}']
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
# Change dir and build the patched gem
Dir.chdir("../build") do
patched_package.build false
end
FileUtils.cp "../build/#{package.file_name}" out
''}
else
cp -r . out
fi
runHook postInstall
'';
};
};
instantiate = (attrs:
let
withFixes = fixSpec attrs;
withSource = withFixes //
(if (lib.isDerivation withFixes.src || builtins.isString withFixes.src)
then { source = attrs.src; }
else { source = attrs.src; src = (fetchers."${attrs.src.type}" attrs); });
in
withSource
applyFixes (applySrc attrs)
);
instantiated = lib.flip lib.mapAttrs (import gemset) (name: attrs:
instantiate (attrs // { inherit name; })
);
# only the *.gem files.
gems = lib.fold (next: acc:
# copy *.gem to ./gems
copyGems = lib.fold (next: acc:
if next.source.type == "gem"
then acc ++ [next.src]
then acc + "cp ${next.src} gems/${gemName next}\n"
else acc
) [] (attrValues instantiated);
) "" (attrValues instantiated);
runRuby = name: env: command:
runCommand name env ''
@ -68,7 +118,7 @@ let
# 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.
json2rb = writeScriptBin "json2rb" ''
json2rb = writeScript "json2rb" ''
#!${ruby}/bin/ruby
begin
require 'json'
@ -81,7 +131,7 @@ let
# dump the instantiated gemset as a ruby expression.
serializedGemset = runCommand "gemset.rb" { json = builtins.toJSON instantiated; } ''
printf '%s' "$json" | ${json2rb}/bin/json2rb > $out
printf '%s' "$json" | ${json2rb} > $out
'';
# this is a mapping from a source type and identifier (uri/path/etc)
@ -122,20 +172,22 @@ let
'';
# rewrite PATH sources to point into the nix store.
purifyLockfile = writeScript "purifyLockfile" ''
purifiedLockfile = runRuby "purifiedLockfile" {} ''
#!${ruby}/bin/ruby
out = ENV['out']
sources = eval(File.read("${sources}"))
paths = sources["path"]
lockfile = STDIN.read
lockfile = File.read("${lockfile}")
paths.each_pair do |impure, pure|
lockfile.gsub!(/^ remote: #{Regexp.escape(impure)}/, " remote: #{pure}")
end
print lockfile
File.open(out, "wb") do |f|
f.print lockfile
end
'';
in
@ -149,15 +201,15 @@ stdenv.mkDerivation {
];
phases = [ "installPhase" "fixupPhase" ];
outputs = [
"out" # the installed libs/bins
"bundler" # supporting files for bundler
"out" # the installed libs/bins
"bundle" # supporting files for bundler
];
installPhase = ''
# Copy the Gemfile and Gemfile.lock
mkdir -p $bundler
export BUNDLE_GEMFILE=$bundler/Gemfile
mkdir -p $bundle
export BUNDLE_GEMFILE=$bundle/Gemfile
cp ${gemfile} $BUNDLE_GEMFILE
cat ${lockfile} | ${purifyLockfile} > $BUNDLE_GEMFILE.lock
cp ${purifiedLockfile} $BUNDLE_GEMFILE.lock
export NIX_GEM_SOURCES=${sources}
export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath}
@ -167,15 +219,15 @@ stdenv.mkDerivation {
mkdir -p $GEM_HOME
mkdir gems
for gem in ${toString gems}; do
ln -s $gem gems
done
${copyGems}
mkdir $out/bin
cp ${./monkey_patches.rb} monkey_patches.rb
export RUBYOPT="-rmonkey_patches.rb -I $(pwd -P)"
bundler install --frozen
bundler install --frozen --binstubs
'';
passthru = {
inherit ruby;
inherit bundler;
};
}

View file

@ -95,6 +95,46 @@ Bundler::Source::Rubygems.class_eval do
end
end
Bundler::Installer.class_eval do
def generate_bundler_executable_stubs(spec, options = {})
return if spec.executables.empty?
out = ENV['out']
spec.executables.each do |executable|
next if executable == "bundle" || executable == "bundler"
binstub_path = "#{out}/bin/#{executable}"
File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
f.print <<-TEXT
#!/usr/bin/env #{RbConfig::CONFIG['ruby_install_name']}
old_gemfile = ENV["BUNDLE_GEMFILE"]
old_gem_home = ENV["GEM_HOME"]
old_gem_path = ENV["GEM_PATH"]
ENV["BUNDLE_GEMFILE"] =
"#{ENV["BUNDLE_GEMFILE"]}"
ENV["GEM_HOME"] =
"#{ENV["GEM_HOME"]}"
ENV["GEM_PATH"] =
"#{ENV["NIX_BUNDLER_GEMPATH"]}:#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}"
require 'rubygems'
require 'bundler/setup'
ENV["BUNDLE_GEMFILE"] = old_gemfile
ENV["GEM_HOME"] = old_gem_home
ENV["GEM_PATH"] = old_gem_path
load Gem.bin_path('#{spec.name}', '#{executable}')
TEXT
end
end
end
end
Gem::Installer.class_eval do
# Make the wrappers automagically use bundler.
#
@ -122,7 +162,7 @@ Gem::Installer.class_eval do
# this file is here to facilitate running it.
#
old_gemfile = ENV["BUNDLE_GEMFILE"]
old_gemfile = ENV["BUNDLE_GEMFILE"]
old_gem_home = ENV["GEM_HOME"]
old_gem_path = ENV["GEM_PATH"]

View file

@ -4188,6 +4188,9 @@ let
wrapPython = pythonPackages.wrapPython;
};
bundix = callPackage ../development/interpreters/ruby/bundix {
ruby = ruby_2_1_3;
};
bundler = callPackage ../development/interpreters/ruby/bundler.nix { };
bundler_HEAD = import ../development/interpreters/ruby/bundler-head.nix {
inherit buildRubyGem coreutils fetchgit;