public
Description: Virtualize Ruby installations
Homepage:
Clone URL: git://github.com/chneukirchen/virtualrb.git
virtualrb / virtualrb
100755 232 lines (191 sloc) 6.075 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/env ruby
# virtualrb - Virtualize Ruby installations
#
# Copyright (C) 2008 Christian Neukirchen <chneukirchen@gmail.com>
# Licensed under the same terms as Ruby itself.
#
# Idea inspired by Python's virtualenv: http://pypi.python.org/pypi/virtualenv
 
VIRTUALRB_VERSION = "0.1"
 
require 'rbconfig'
if defined? RUBY_VIRTUALRB
  abort "* Don't run virtual.rb from a virtualized ruby, or hell will break lose."
end
 
require 'optparse'
require 'fileutils'
 
def find_ruby
  File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
end
 
def find_rbconfig(ruby=find_ruby)
  eval(`#{ruby} -e 'p $:'`).
    map { |dir| dir + "/rbconfig.rb" }.
    find { |file| File.exist?(file) }
end
 
gems = false
gemversion = ">=0"
ruby = find_ruby
inherit = true
 
opts = OptionParser.new("", 30, ' ') { |opts|
  opts.banner = "Usage: virtualrb [options] DIRECTORY"
  opts.separator("")
  opts.separator("Options:")
  opts.on("--with-rubygems=[VERSION]", "set up virtualized Rubygems") { |version|
    gems = true
    gemversion = version if version
  }
  opts.on("--with-ruby PATH", "use the Ruby binary in PATH", String) { |bin|
    ruby = bin
  }
  opts.on("--no-inherit", "don't inherit the site path from Ruby", String) { |bin|
    inherit = false
  }
 
  opts.on("-h", "--help", "Show this message") do
    puts opts
    exit
  end
 
  opts.on("-v", "--version", "Show version") do
    puts "virtualrb #{VIRTUALRB_VERSION}"
    exit
  end
 
  opts.separator("")
  opts.separator("Default ruby: #{find_ruby}")
 
  opts.parse! ARGV
}
 
root = ARGV[0] or abort opts.to_s
 
root = File.expand_path(root)
ruby = File.expand_path(ruby)
 
version = `#{ruby} -v`
cfg = Marshal.load `#{ruby} -r rbconfig -e 'Marshal.dump Config::CONFIG, STDOUT'`
 
puts "* Setting up a virtual Ruby for"
puts" #{version}"
 
FileUtils.mkdir_p(root)
FileUtils.mkdir_p("#{root}/bin")
FileUtils.mkdir_p("#{root}/lib/site_ruby/1.8")
FileUtils.mkdir_p("#{root}/lib/gemhome") if gems
 
File.open("#{root}/bin/ruby", "wb") { |script|
  script.write(<<EOF)
#!/bin/sh
 
if [ "`#{ruby} -v`" = #{version.chomp.dump} ]; then
exec #{ruby} -r "#{root}/lib/site_ruby/1.8/site.rb" "$@";
else
echo "Version mismatch. Got '`#{ruby} -v`', required '#{version}'" >/dev/stderr
echo "If this is intentional, refresh the virtualized root by running virtualrb again." >/dev/stderr
exit -1
fi
EOF
}
File.chmod(0755, "#{root}/bin/ruby")
 
File.open("#{root}/bin/irb", "wb") { |script|
  script.puts "#!#{root}/bin/ruby"
  script.puts "# was:"
  script.puts File.read(File.dirname(ruby) + "/irb")
}
File.chmod(0755, "#{root}/bin/irb")
 
begin
  naive = File.read(File.join(File.dirname(__FILE__), "naive-install"))
  File.open("#{root}/bin/naive-install", "wb") { |script|
    script.puts "#!#{root}/bin/ruby -s"
    script.puts "# was:"
    script.puts naive
  }
  File.chmod(0755, "#{root}/bin/naive-install")
rescue Errno::ENOENT
end
 
File.open("#{root}/bin/activate", "wb") { |script|
  script.write(<<EOF)
#!/bin/sh
if [ -z "$PS1" ]; then
echo "activate must be run by sourcing." >/dev/stderr
exit -1
fi
 
if [ "$_VRB_ROOT" ]; then
echo "Don't activate without deactivating first." >/dev/stderr
return
fi
 
_VRB_ROOT="#{root}"
_OLD_VRB_PATH="$PATH"
_OLD_VRB_PS1="$PS1"
 
deactivate() {
export PATH="$_OLD_VRB_PATH"
export PS1="$_OLD_VRB_PS1"
hash -r
unset _VRB_ROOT
unset -f deactivate
}
 
export PS1="(`basename "$_VRB_ROOT"`) $PS1"
export PATH="$_VRB_ROOT/bin:$PATH"
hash -r
EOF
}
File.chmod(0755, "#{root}/bin/activate")
 
File.open("#{root}/lib/site_ruby/1.8/site.rb", "wb") { |script|
  script.puts "# This file has been created by virtualrb on #{Time.now}"
  script.puts "# for #{ruby}"
  script.puts "# relocated to #{root}"
  script.puts "# #{version}"
  script.puts
  script.puts "RUBY_VIRTUALRB = true"
 
  unless inherit
    %w{sitedir sitelibdir sitearchdir}.each { |dir|
      script.puts "$LOAD_PATH.delete(#{cfg[dir].dump})"
    }
  end
 
  script.puts "$LOAD_PATH.unshift(#{"#{root}/lib/site_ruby/1.8".dump})"
  script.puts "$LOAD_PATH.unshift(#{"#{root}/lib/site_ruby/1.8/#{cfg["sitearch"]}".dump})"
 
  if gems
    script.write <<EOF
 
require 'rubygems'
 
# Virtual Rubygems should not fiddle with ~/.gemrc
def Gem.find_home
"#{root}/lib/gemhome/"
end
EOF
  end
}
 
def patch_settings(config, values)
  values.inject(config) { |cfg, (key, value)|
    cfg.gsub(/(.*#{Regexp.escape key} = ).*/i, "\\1#{value.dump}")
  }
end
 
File.open("#{root}/lib/site_ruby/1.8/rbconfig.rb", "wb") { |script|
  script.puts "# This file has been patched by virtualrb on #{Time.now}"
  script.puts "# for #{ruby}"
  script.puts "# relocated to #{root}"
  script.puts "# #{version}"
 
  config = patch_settings(File.read(find_rbconfig(ruby)),
                          'TOPDIR' => root,
                          'CONFIG["ruby_install_name"]' => "ruby",
                          'CONFIG["sitedir"]' => root + "/lib/site_ruby",
                          'CONFIG["archdir"]' => cfg["archdir"])
 
  script.write(config.
               gsub(/.*RUBY_FRAMEWORK.*/, ''). # no special deal on OS X
               gsub(/.*APPLE_GEM_HOME.*/, '') # ----- " -----
               )
}
 
if gems
  begin
    require 'rubygems'
  rescue LoadError
    abort "* No Rubygems found, please install manually in the activated env."
  end
 
  begin
    gem 'rubygems-update', gemversion
  rescue LoadError
    p $!
    abort "* No appropriate rubygems-update found to install Rubygems into\n" +
      " the virtual env, please gem install rubygems-update."
  end
 
  update_dir = $LOAD_PATH.find { |fn| fn =~ /rubygems-update/ }
 
  if update_dir.nil?
    abort "* Internal error in virtualrb, where has the rubygems-update gone? Please file a bug."
  else
    update_dir = File.dirname(update_dir)
    Dir.chdir update_dir
    update_dir =~ /([0-9.]*)$/
    puts "* Installing RubyGems #{$1}"
    system "#{root}/bin/ruby setup.rb"
    puts "* Consider running"
    puts " #{root}/bin/gem sources -a http://gems.github.com"
  end
end
 
puts "* virtual Ruby set up, run to enter:"
puts " source #{root}/bin/activate"