Merge pull request #4677 from cstrahan/pleasant-ruby

Pleasant ruby
This commit is contained in:
Charles Strahan
2015-01-21 23:27:38 -05:00
58 changed files with 1577 additions and 2932 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

@@ -0,0 +1,115 @@
# The standard set of gems in nixpkgs including potential fixes.
#
# The gemset is derived from two points of entry:
# - An attrset describing a gem, including version, source and dependencies
# This is just meta data, most probably automatically generated by a tool
# like Bundix (https://github.com/aflatter/bundix).
# {
# name = "bundler";
# version = "1.6.5";
# sha256 = "1s4x0f5by9xs2y24jk6krq5ky7ffkzmxgr4z1nhdykdmpsi2zd0l";
# dependencies = [ "rake" ];
# }
# - An optional derivation that may override how the gem is built. For popular
# gems that don't behave correctly, fixes are already provided in the form of
# derivations.
#
# This seperates "what to build" (the exact gem versions) from "how to build"
# (to make gems behave if necessary).
{ lib, fetchurl, writeScript, ruby, libxml2, libxslt, python, stdenv, which
, libiconv, postgresql, v8, v8_3_16_14, clang, sqlite, zlib, imagemagick, pkgconfig
, ncurses, xapian, gpgme, utillinux, fetchpatch
}:
let
v8 = v8_3_16_14;
in
{
gpgme = attrs: {
buildInputs = [ gpgme ];
};
libv8 = attrs: {
buildInputs = [ which v8 python ];
buildFlags = [
"--with-system-v8=true"
];
patches = [
(fetchpatch {
url = https://github.com/cowboyd/libv8/pull/161.patch;
sha256 = "1l6572cmigc22g249jj8h0xlbig88mj43kdqdbimhw2pmpv3q0rs";
})
];
};
ncursesw = attrs: {
buildInputs = [ ncurses ];
buildFlags = [
"--with-cflags=-I${ncurses}/include"
"--with-ldflags=-L${ncurses}/lib"
];
};
nokogiri = attrs: {
buildFlags = [
"--use-system-libraries"
"--with-zlib-dir=${zlib}"
"--with-xml2-lib=${libxml2}/lib"
"--with-xml2-include=${libxml2}/include/libxml2"
"--with-xslt-lib=${libxslt}/lib"
"--with-xslt-include=${libxslt}/include"
"--with-exslt-lib=${libxslt}/lib"
"--with-exslt-include=${libxslt}/include"
] ++ lib.optional stdenv.isDarwin "--with-iconv-dir=${libiconv}";
};
pg = attrs: {
buildFlags = [
"--with-pg-config=${postgresql}/bin/pg_config"
];
};
rmagick = attrs: {
buildInputs = [ imagemagick pkgconfig ];
};
sqlite3 = attrs: {
buildFlags = [
"--with-sqlite3-include=${sqlite}/include"
"--with-sqlite3-lib=${sqlite}/lib"
];
};
sup = attrs: {
# prevent sup from trying to dynamically install `xapian-ruby`.
postPatch = ''
cp ${./mkrf_conf_xapian.rb} ext/mkrf_conf_xapian.rb
substituteInPlace lib/sup/crypto.rb \
--replace 'which gpg2' \
'${which}/bin/which gpg2'
'';
};
therubyracer = attrs: {
buildFlags = [
"--with-v8-dir=${v8}"
"--with-v8-include=${v8}/include"
"--with-v8-lib=${v8}/lib"
];
};
xapian-ruby = attrs: {
# use the system xapian
buildInputs = [ xapian pkgconfig zlib ];
postPatch = ''
cp ${./xapian-Rakefile} Rakefile
'';
preInstall = ''
export XAPIAN_CONFIG=${xapian}/bin/xapian-config
'';
};
}

View File

@@ -0,0 +1,306 @@
{ stdenv, runCommand, writeText, writeScript, writeScriptBin, ruby, lib
, callPackage, defaultGemConfig, fetchurl, fetchgit, buildRubyGem , bundler_HEAD
, git
}@defs:
# This is a work-in-progress.
# The idea is that his will replace load-ruby-env.nix.
{ name, gemset, gemfile, lockfile, ruby ? defs.ruby, gemConfig ? defaultGemConfig
, enableParallelBuilding ? false # TODO: this might not work, given the env-var shinanigans.
, documentation ? false
, meta ? {}
}@args:
let
shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'";
const = x: y: x;
bundler = bundler_HEAD.override { inherit ruby; };
inherit (builtins) attrValues;
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.source) url rev sha256 fetchSubmodules;
leaveDotGit = true;
};
applySrc = attrs:
attrs // {
src = (fetchers."${attrs.source.type}" attrs);
};
applyGemConfigs = attrs:
if gemConfig ? "${attrs.name}"
then attrs // gemConfig."${attrs.name}" attrs
else attrs;
needsPatch = attrs:
(attrs ? patches) || (attrs ? prePatch) || (attrs ? postPatch);
# 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 !needsPatch attrs
then attrs
else attrs // { src =
stdenv.mkDerivation {
name = gemName attrs;
phases = [ "unpackPhase" "patchPhase" "installPhase" ];
buildInputs = [ ruby ] ++ attrs.buildInputs or [];
patches = attrs.patches or [ ];
prePatch = attrs.prePatch or "true";
postPatch = attrs.postPatch or "true";
unpackPhase = ''
runHook preUnpack
if [[ -f ${attrs.src} ]]; then
isGem=1
# 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
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'
require 'rubygems/package'
require 'fileutils'
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
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
patched_package.build(false)
FileUtils.cp(patched_package.spec.file_name, out)
''}
else
cp -r . out
fi
runHook postInstall
'';
};
};
instantiate = (attrs:
applyPatches (applyGemConfigs (applySrc attrs))
);
instantiated = lib.flip lib.mapAttrs (import gemset) (name: attrs:
instantiate (attrs // { inherit name; })
);
needsPreInstall = attrs:
(attrs ? preInstall) || (attrs ? buildInputs) || (attrs ? nativeBuildInputs);
# TODO: support cross compilation? look at stdenv/generic/default.nix.
runPreInstallers = lib.fold (next: acc:
if !needsPreInstall next
then acc
else acc + ''
${writeScript "${next.name}-pre-install" ''
#!${stdenv.shell}
export nativeBuildInputs="${toString ((next.nativeBuildInputs or []) ++ (next.buildInputs or []))}"
source ${stdenv}/setup
header "running pre-install script for ${next.name}"
${next.preInstall or ""}
${ruby}/bin/ruby -e 'print ENV.inspect' > env/${next.name}
stopNest
''}
''
) "" (attrValues instantiated);
# copy *.gem to ./gems
copyGems = lib.fold (next: acc:
if next.source.type == "gem"
then acc + "cp ${next.src} gems/${gemName next}\n"
else acc
) "" (attrValues instantiated);
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.
json2rb = writeScript "json2rb" ''
#!${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; } ''
printf '%s' "$json" | ${json2rb} > $out
'';
# 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.
purifiedLockfile = runRuby "purifiedLockfile" {} ''
out = ENV['out']
sources = eval(File.read("${sources}"))
paths = sources["path"]
lockfile = File.read("${lockfile}")
paths.each_pair do |impure, pure|
lockfile.gsub!(/^ remote: #{Regexp.escape(impure)}/, " remote: #{pure}")
end
File.open(out, "wb") do |f|
f.print lockfile
end
'';
needsBuildFlags = attrs: attrs ? buildFlags;
mkBuildFlags = spec:
"export BUNDLE_BUILD__${lib.toUpper spec.name}='${lib.concatStringsSep " " (map shellEscape spec.buildFlags)}'";
allBuildFlags =
lib.concatStringsSep "\n"
(map mkBuildFlags
(lib.filter needsBuildFlags (attrValues instantiated)));
in
stdenv.mkDerivation {
inherit name;
buildInputs = [
ruby
bundler
git
];
phases = [ "installPhase" "fixupPhase" ];
outputs = [
"out" # the installed libs/bins
"bundle" # supporting files for bundler
];
installPhase = ''
mkdir -p $bundle
export BUNDLE_GEMFILE=$bundle/Gemfile
cp ${gemfile} $BUNDLE_GEMFILE
cp ${purifiedLockfile} $BUNDLE_GEMFILE.lock
export NIX_GEM_SOURCES=${sources}
export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath}
export GEM_HOME=$out/${ruby.gemPath}
export GEM_PATH=$GEM_HOME
mkdir -p $GEM_HOME
${allBuildFlags}
#export
mkdir gems
${copyGems}
${lib.optionalString (!documentation) ''
mkdir home
HOME="$(pwd -P)/home"
echo "gem: --no-rdoc --no-ri" > $HOME/.gemrc
''}
mkdir env
${runPreInstallers}
mkdir $out/bin
cp ${./monkey_patches.rb} monkey_patches.rb
export RUBYOPT="-rmonkey_patches.rb -I $(pwd -P)"
bundler install --frozen --binstubs ${lib.optionalString enableParallelBuilding "--jobs $NIX_BUILD_CORES"}
'';
passthru = {
inherit ruby;
inherit bundler;
};
inherit meta;
}

View File

@@ -0,0 +1,14 @@
require 'rubygems'
require 'rubygems/command.rb'
require 'rubygems/dependency_installer.rb'
require 'rbconfig'
begin
Gem::Command.build_args = ARGV
rescue NoMethodError
end
# create dummy rakefile to indicate success
f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w")
f.write("task :default\n")
f.close

View File

@@ -0,0 +1,238 @@
require 'bundler'
# Undo the RUBYOPT trickery.
opt = ENV['RUBYOPT'].dup
opt.gsub!(/-rmonkey_patches.rb -I [^ ]*/, '')
ENV['RUBYOPT'] = opt
Bundler.module_eval do
class << self
# mappings from original uris to store paths.
def nix_gem_sources
@nix_gem_sources ||=
begin
src = ENV['NIX_GEM_SOURCES']
eval(Bundler.read_file(src))
end
end
# extract the gemspecs from the gems pulled from Rubygems.
def nix_gemspecs
@nix_gemspecs ||= Dir.glob("gems/*.gem").map do |path|
Bundler.rubygems.spec_from_gem(path)
end
end
# swap out ENV
def nix_with_env(env, &block)
if env
old_env = ENV.to_hash
begin
ENV.replace(env)
block.call
ensure
ENV.replace(old_env)
end
else
block.call
end
end
# map a git uri to a fetchgit store path.
def nix_git(uri)
Pathname.new(nix_gem_sources["git"][uri])
end
end
end
Bundler::Source::Git::GitProxy.class_eval do
def checkout
unless path.exist?
FileUtils.mkdir_p(path.dirname)
FileUtils.cp_r(Bundler.nix_git(@uri).join(".git"), path)
system("chmod -R +w #{path}")
end
end
def copy_to(destination, submodules=false)
unless File.exist?(destination.join(".git"))
FileUtils.mkdir_p(destination.dirname)
FileUtils.cp_r(Bundler.nix_git(@uri), destination)
system("chmod -R +w #{destination}")
end
end
end
Bundler::Fetcher.class_eval do
def use_api
true
end
def fetch_dependency_remote_specs(gem_names)
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
deps_list = []
spec_list = gem_names.map do |name|
spec = Bundler.nix_gemspecs.detect {|spec| spec.name == name }
dependencies = spec.dependencies.
select {|dep| dep.type != :development}.
map do |dep|
deps_list << dep.name
dep
end
[spec.name, spec.version, spec.platform, dependencies]
end
[spec_list, deps_list.uniq]
end
end
Bundler::Source::Rubygems.class_eval do
# We copy all gems into $PWD/gems, and this allows RubyGems to find those
# gems during installation.
def fetchers
@fetchers ||= [
Bundler::Fetcher.new(URI.parse("file://#{File.expand_path(Dir.pwd)}"))
]
end
# Look-up gems that were originally from RubyGems.
def remote_specs
@remote_specs ||=
begin
lockfile = Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile))
gem_names = lockfile.specs.
select {|spec| spec.source.is_a?(Bundler::Source::Rubygems)}.
map {|spec| spec.name}
idx = Bundler::Index.new
api_fetchers.each do |f|
Bundler.ui.info "Fetching source index from #{f.uri}"
idx.use f.specs(gem_names, self)
end
idx
end
end
end
Bundler::Installer.class_eval do
# WHY:
# This allows us to provide a typical Nix experience, where
# `buildInputs` and/or `preInstall` may set up the $PATH and other env-vars
# as needed. By swapping out the environment per install, we can have finer
# grained control than we would have otherwise.
#
# HOW:
# This is a wrapper around the original `install_gem_from_spec`.
# We expect that a "pre-installer" might exist at `pre-installers/<gem-name>`,
# and if it does, we execute it.
# The pre-installer is expected to dump its environment variables as a Ruby
# hash to `env/<gem-name>`.
# We then swap out the environment for the duration of the install,
# and then set it back to what it was originally.
alias original_install_gem_from_spec install_gem_from_spec
def install_gem_from_spec(spec, standalone = false, worker = 0)
env_dump = "env/#{spec.name}"
if File.exist?(env_dump)
env = eval(Bundler.read_file(env_dump))
unless env
Bundler.ui.error "The environment variables for #{spec.name} could not be loaded!"
exit 1
end
Bundler.nix_with_env(env) do
original_install_gem_from_spec(spec, standalone, worker)
end
else
original_install_gem_from_spec(spec, standalone, worker)
end
end
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.
#
# Stage 1.
# Set $BUNDLE_GEMFILE so bundler knows what gems to load.
# Set $GEM_HOME to the installed gems, because bundler looks there for
# non-Rubygems installed gems (e.g. git/svn/path sources).
# Set $GEM_PATH to include both bundler and installed gems.
#
# Stage 2.
# Setup bundler, locking down the gem versions.
#
# Stage 3.
# Reset $BUNDLE_GEMFILE, $GEM_HOME, $GEM_PATH.
#
# Stage 4.
# Run the actual executable.
def app_script_text(bin_file_name)
return <<-TEXT
#{shebang bin_file_name}
#
# This file was generated by Nix's RubyGems.
#
# The application '#{spec.name}' is installed as part of a gem, and
# this file is here to facilitate running it.
#
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}', '#{bin_file_name}')
TEXT
end
end

View File

@@ -0,0 +1,29 @@
require 'rubygems/installer'
require 'rubygems/builder'
# Simulate RubyGems 2.0 behavior.
module Gem::Package
def self.new(gem)
@gem = gem
self
end
def self.extract_files(dir)
installer = Gem::Installer.new @gem
installer.unpack(dir)
end
def self.build(skip_validation=false)
builder = Gem::Builder.new(spec)
builder.build
end
def self.spec=(spec)
@spec = spec
end
def self.spec
@spec ||= Gem::Installer.new(@gem).spec
end
end

View File

@@ -0,0 +1,38 @@
# encoding: utf-8
# Install the xapian binaries into the lib folder of the gem
require 'rbconfig'
c = RbConfig::CONFIG
def system!(cmd)
puts cmd
system(cmd) or raise
end
source_dir = 'xapian_source'
bindings = Dir["#{source_dir}/xapian-bindings-*"].first
bindings = File.basename(bindings, ".tar.xz")
task :default do
system! "tar -xJf #{source_dir}/#{bindings}.tar.xz"
prefix = Dir.pwd
ENV['LDFLAGS'] = "-L#{prefix}/lib"
system! "mkdir -p lib"
Dir.chdir bindings do
ENV['RUBY'] ||= "#{c['bindir']}/#{c['RUBY_INSTALL_NAME']}"
system! "./configure --prefix=#{prefix} --exec-prefix=#{prefix} --with-ruby"
system! "make clean all"
end
system! "cp -r #{bindings}/ruby/.libs/_xapian.* lib"
system! "cp #{bindings}/ruby/xapian.rb lib"
system! "rm lib/*.la"
system! "rm lib/*.lai"
system! "rm -R #{bindings}"
system! "rm -R #{source_dir}"
end

View File

@@ -0,0 +1,18 @@
{ buildRubyGem, coreutils, fetchgit }:
buildRubyGem {
name = "bundler-HEAD";
src = fetchgit {
url = "https://github.com/bundler/bundler.git";
rev = "a2343c9eabf5403d8ffcbca4dea33d18a60fc157";
sha256 = "1l4r55n1wzr817l225l6pm97li1mxg9icd8s51cpfihh91nkdz68";
leaveDotGit = true;
};
dontPatchShebangs = true;
postInstall = ''
find $out -type f -perm +0100 | while read f; do
substituteInPlace $f \
--replace "/usr/bin/env" "${coreutils}/bin/env"
done
'';
}

View File

@@ -0,0 +1,13 @@
{ buildRubyGem, coreutils }:
buildRubyGem {
name = "bundler-1.7.9";
sha256 = "1gd201rh17xykab9pbqp0dkxfm7b9jri02llyvmrc0c5bz2vhycm";
dontPatchShebangs = true;
postInstall = ''
find $out -type f -perm +0100 | while read f; do
substituteInPlace $f \
--replace "/usr/bin/env" "${coreutils}/bin/env"
done
'';
}

View File

@@ -1,35 +0,0 @@
diff --git a/lib/nix/gem-nix-command.rb b/lib/nix/gem-nix-command.rb
index 8d3733e..ba942ff 100644
--- a/lib/nix/gem-nix-command.rb
+++ b/lib/nix/gem-nix-command.rb
@@ -108,11 +108,12 @@ class Gem::Commands::NixCommand < Gem::Command
# args to dep informations
args.each { |arg|
- if arg =~ /(.+)-?(.*)?/ then
+ if arg =~ /(.+)-([0-9][^-]+)/ then
gem_name = $1
- version = $2.empty? ? Gem::Requirement.default : Gem::Version.new($2)
+ version = Gem::Version.new($2)
else
- raise Gem::CommandLineError, "couldn't parse arg. expected: name or name-version"
+ gem_name = arg
+ version = Gem::Requirement.default
end
adddep(Gem::Dependency.new gem_name, version)
@@ -162,7 +163,13 @@ class Gem::Commands::NixCommand < Gem::Command
spec, source_uri = find_gem_with_source(dep)
full_name = spec.full_name
- return if @gems_with_deps.key?(full_name)
+ if @gems_with_deps.key?(full_name)
+ unless @gems_with_deps[full_name].nil?
+ return @gems_with_deps[full_name][0]
+ else
+ return nil
+ end
+ end
@gems_with_deps[full_name] = nil # there maybe circular dependencies. thus mark this gem seen as early as possible
# development deps can't be found. Some are old. Thus only add rutime dependencies

View File

@@ -1,63 +1,136 @@
{stdenv, fetchurl, ruby, rubygems, makeWrapper, patches, overrides}:
{ lib, ruby, rubygemsFun, fetchurl, makeWrapper, git } @ defs:
let
gemDefaults = { name, basename, requiredGems, sha256, meta }:
{
buildInputs = [rubygems makeWrapper];
unpackPhase = ":";
configurePhase=":";
bulidPhase=":";
lib.makeOverridable (
src = fetchurl {
url = "http://rubygems.org/downloads/${name}.gem";
inherit sha256;
{ name
, ruby ? defs.ruby
, rubygems ? (rubygemsFun ruby)
, stdenv ? ruby.stdenv
, namePrefix ? "${ruby.name}" + "-"
, buildInputs ? []
, doCheck ? false
, dontBuild ? true
, meta ? {}
, gemPath ? []
, ...} @ attrs:
stdenv.mkDerivation (attrs // {
inherit ruby rubygems;
inherit doCheck;
buildInputs = [ ruby rubygems makeWrapper git ] ++ buildInputs;
name = namePrefix + name;
src = if attrs ? src
then attrs.src
else fetchurl {
url = "http://rubygems.org/downloads/${attrs.name}.gem";
inherit (attrs) sha256;
};
name = "ruby-${name}";
phases = [ "unpackPhase" "patchPhase" "buildPhase" "checkPhase" "installPhase" "fixupPhase" ];
propagatedBuildInputs = requiredGems ++ [ruby];
# The source is expected to either be a gem package or a directory.
#
# - Gem packages are already built, so they don't even need to be unpacked.
# They will skip the buildPhase.
# - A directory containing the sources will need to go through all of the
# usual phases.
unpackPhase= ''
gemRegex="\.gem"
if [[ $src =~ $gemRegex ]]
then
runHook preUnpack
echo "source is a gem package, won't unpack"
gempkg=$src
dontBuild=1
runHook postUnpack
else
# Fall back to the original thing for everything else.
unpackPhase
fi
'';
inherit meta;
checkPhase = "true";
installPhase = ''
export HOME=$TMP/home; mkdir -pv "$HOME"
buildPhase = ''
runHook preBuild
gem install -V --ignore-dependencies \
-i "$out/${ruby.gemPath}" -n "$out/bin" "$src" $gemFlags -- $buildFlags
rm -frv $out/${ruby.gemPath}/cache # don't keep the .gem file here
# TODO: Investigate. The complete working tree is touched by fetchgit.
if [ -d .git ]; then
git reset
fi
gemspec=$(find . -name '*.gemspec')
echo "found the following gemspecs:"
echo "$gemspec"
gemspec=$(echo "$gemspec" | head -n1)
echo "building $gemspec"
exec 3>&1
output=$(gem build $gemspec | tee >(cat - >&3))
exec 3>&-
gempkg=$(echo "$output" | grep -oP 'File: \K(.*)')
echo "gem package built: $gempkg"
runHook postBuild
'';
installPhase = ''
runHook preInstall
# NOTE: This does NOT build the unpacked gem, but installs $src directly.
# Gems that have not been downloaded from rubygems.org may need a
# separate buildPhase.
# --ignore-dependencies is necessary as rubygems otherwise always
# connects to the repository, thus breaking pure builds.
GEM_HOME=$out/${ruby.gemPath} \
gem install \
--local \
--force \
--http-proxy "http://nodtd.invalid" \
--ignore-dependencies \
--build-root "/" \
--backtrace \
$gempkg $gemFlags -- $buildFlags
# Yes, we really do need the $out/${ruby.gemPath}/cache.
# This is very important in order for many parts of RubyGems/Bundler to not blow up.
# See https://github.com/bundler/bundler/issues/3327
mkdir -p $out/bin
for prog in $out/${ruby.gemPath}/gems/*/bin/*; do
makeWrapper $prog $out/bin/$(basename $prog) \
--prefix GEM_PATH : "$out/${ruby.gemPath}:$GEM_PATH" \
--prefix RUBYLIB : "${rubygems}/lib" \
$extraWrapperFlags ''${extraWrapperFlagsArray[@]}
done
#--prefix RUBYOPT rubygems \
# looks like useless files which break build repeatability and consume space
rm -fv $out/${ruby.gemPath}/doc/*/*/created.rid || true
rm -fv $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true
mkdir -p $out/nix-support
cat > $out/nix-support/setup-hook <<EOF
if [[ "\$GEM_PATH" != *$out* ]]; then
addToSearchPath GEM_PATH $out/${ruby.gemPath}
fi
EOF
for prog in $out/bin/*; do
wrapProgram "$prog" \
--prefix GEM_PATH : "$GEM_PATH" \
--prefix RUBYLIB : "${rubygems}/lib" \
--set RUBYOPT rubygems \
$extraWrapperFlags ''${extraWrapperFlagsArray[@]}
done
runHook postInstall
'';
for prog in $out/gems/*/bin/*; do
[[ -e "$out/bin/$(basename $prog)" ]]
done
propagatedBuildInputs = gemPath;
propagatedUserEnvPkgs = gemPath;
# looks like useless files which break build repeatability and consume space
rm $out/${ruby.gemPath}/doc/*/*/created.rid || true
rm $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true
passthru.isRubyGem = true;
inherit meta;
})
runHook postInstall
'';
propagatedUserEnvPkgs = requiredGems;
passthru.isRubyGem = true;
};
mb = stdenv.lib.maybeAttr;
patchedGem = a: stdenv.mkDerivation (removeAttrs (stdenv.lib.mergeAttrsByFuncDefaults
([ (gemDefaults a) ]
++ (stdenv.lib.concatMap (p: [(mb a.basename {} p) (mb a.name {} p)] )
patches)))
[ "mergeAttrBy" ]);
in
aName: a@{ name, basename, requiredGems, sha256, meta }:
stdenv.lib.foldl (d: o: mb name (mb basename d o) o) (patchedGem a) overrides
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,45 +0,0 @@
{ stdenv, config, fetchurl, callPackage }:
let
inherit (stdenv.lib) fold optional;
gemsMergeableFun = { generatedFuns ? [], patchFuns ? [], overrideFuns ? [] }:
let
generatedAttrs = map (f: f customGems) generatedFuns;
generatedGems = map (a: a.gems) generatedAttrs;
gem = callPackage ./gem.nix {
patches = map (f: callPackage f { inherit gems; }) patchFuns;
overrides = map (f: callPackage f { }) overrideFuns;
};
customGems = stdenv.lib.mapAttrs gem (fold (x: y: x // y) { } generatedGems);
gems = fold (x: y: x // y) customGems (map (a: a.aliases) generatedAttrs);
in
gems // {
merge = { generated ? null, patches ? null, overrides ? null }:
gemsMergeableFun {
generatedFuns = generatedFuns ++ optional (generated != null) generated;
patchFuns = patchFuns ++ optional (patches != null) patches;
overrideFuns = overrideFuns ++ optional (overrides != null) overrides;
};
};
in
((gemsMergeableFun { }).merge {
generated = import ./generated.nix;
patches = import ./patches.nix;
overrides = import ./overrides.nix;
}).merge (
let
localGemDir = (builtins.getEnv "HOME") + "/.nixpkgs/gems/";
getLocalGemFun = name:
let
file = localGemDir + name + ".nix";
fallback =
if builtins.pathExists file then import (builtins.toPath file)
else null;
in
stdenv.lib.attrByPath [ "gems" name ] fallback config;
in
{
generated = getLocalGemFun "generated";
patches = getLocalGemFun "patches";
overrides = getLocalGemFun "overrides";
})

View File

@@ -0,0 +1,69 @@
{ ruby, lib, callPackage, gemFixes, fetchurl, fetchgit, buildRubyGem }@defs:
# This function builds a set of gems. You first convert your Gemfile to an attrset
# called a "gemset", and then use this function to build the gemset.
#
# A gemset looks like the following:
#
# {
# libv8 = {
# version = "3.16.14.7";
# src = {
# type = "gem";
# sha256 = "...";
# };
# };
# therubyracer = {
# version = "0.12.1";
# dependencies = [ "libv8" ];
# src = {
# type = "gem";
# sha256 = "...";
# };
# };
# }
#
# If you use these gems as build inputs, the GEM_PATH will be updated
# appropriately, and command like `bundle exec` should work out of the box.
{ gemset, ruby ? defs.ruby, fixes ? gemFixes }@args:
let
const = x: y: x;
fetchers.path = attrs: attrs.src.path;
fetchers.gem = attrs: fetchurl {
url = "${attrs.src.source or "https://rubygems.org"}/downloads/${attrs.name}-${attrs.version}.gem";
inherit (attrs.src) sha256;
};
fetchers.git = attrs: fetchgit {
inherit (attrs.src) url rev sha256 fetchSubmodules;
leaveDotGit = true;
};
instantiate = (attrs:
let
defaultAttrs = {
name = "${attrs.name}-${attrs.version}";
inherit ruby gemPath;
};
gemPath = map (name: gemset''."${name}") (attrs.dependencies or []);
fixedAttrs = attrs // (fixes."${attrs.name}" or (const {})) attrs;
withSource = fixedAttrs //
(if (lib.isDerivation fixedAttrs.src || builtins.isString fixedAttrs.src)
then {}
else { src = (fetchers."${fixedAttrs.src.type}" fixedAttrs); });
in
buildRubyGem (withSource // defaultAttrs)
);
gemset' = if builtins.isAttrs gemset then gemset else import gemset;
gemset'' = lib.flip lib.mapAttrs gemset' (name: attrs:
if (lib.isDerivation attrs)
then attrs
else instantiate (attrs // { inherit name; })
);
in gemset''

View File

@@ -1,5 +0,0 @@
{ xapianBindings }:
{
xapian_full = xapianBindings.merge { cfg = { rubySupport = true; }; };
}

View File

@@ -88,7 +88,8 @@ stdenv.mkDerivation rec {
minorVersion = "8";
teenyVersion = "7";
patchLevel = "374";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -108,7 +108,8 @@ stdenv.mkDerivation rec {
minorVersion = "9";
teenyVersion = "3";
patchLevel = "547";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -91,7 +91,8 @@ stdenv.mkDerivation rec {
minorVersion = "0";
teenyVersion = "0";
patchLevel = "481";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -106,7 +106,8 @@ stdenv.mkDerivation rec {
minorVersion = "1";
teenyVersion = "0";
patchLevel = "0";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -105,7 +105,8 @@ stdenv.mkDerivation rec {
minorVersion = "1";
teenyVersion = "1";
patchLevel = "0";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -104,7 +104,8 @@ stdenv.mkDerivation rec {
minorVersion = "1";
teenyVersion = "2";
patchLevel = "353";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -108,7 +108,8 @@ stdenv.mkDerivation rec {
minorVersion = "1";
teenyVersion = "3";
patchLevel = "0";
libPath = "lib/ruby/${majorVersion}.${minorVersion}";
gemPath = "lib/ruby/gems/${majorVersion}.${minorVersion}";
rubyEngine = "ruby";
libPath = "lib/${rubyEngine}/${majorVersion}.${minorVersion}.${teenyVersion}";
gemPath = "lib/${rubyEngine}/gems/${majorVersion}.${minorVersion}.${teenyVersion}";
};
}

View File

@@ -1,10 +1,11 @@
args : with args;
rec {
version = "1.8.25";
rec {
name = "rubygems-" + version;
version = "2.4.1";
src = fetchurl {
url = "http://production.cf.rubygems.org/rubygems/${name}.tgz";
sha256 = "1j0wiy829nsfrpdzr9xzs39jf1lga3f5b7773vxqfs3lz3fli4v4";
sha256 = "0cpr6cx3h74ykpb0cp4p4xg7a8j0bhz3sk271jq69l4mm4zy4h4f";
};
buildInputs = [ruby makeWrapper];
@@ -23,7 +24,6 @@ rec {
/* doConfigure should be specified separately */
phaseNames = ["doPatch" "doInstall"];
name = "rubygems-" + version;
meta = {
description = "Ruby gems package collection";
longDescription = ''