Created
October 9, 2008 20:28
-
-
Save fabien/15872 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems/dependency_installer' | |
require 'rubygems/uninstaller' | |
require 'rubygems/dependency' | |
module GemManagement | |
include ColorfulMessages | |
# Install a gem - looks remotely and local gem cache; | |
# won't process rdoc or ri options. | |
def install_gem(gem, options = {}) | |
refresh = options.delete(:refresh) || [] | |
from_cache = (options.key?(:cache) && options.delete(:cache)) | |
if from_cache | |
install_gem_from_cache(gem, options) | |
else | |
version = options.delete(:version) | |
Gem.configuration.update_sources = false | |
# Limit source index to install dir | |
update_source_index(options[:install_dir]) if options[:install_dir] | |
installer = Gem::DependencyInstaller.new(options.merge(:user_install => false)) | |
# Force-refresh certain gems by excluding them from the current index | |
if refresh.respond_to?(:include?) && !refresh.empty? | |
source_index = installer.instance_variable_get(:@source_index) | |
source_index.gems.each do |name, spec| | |
source_index.gems.delete(name) if refresh.include?(spec.name) | |
end | |
end | |
exception = nil | |
begin | |
installer.install gem, version | |
rescue Gem::InstallError => e | |
exception = e | |
rescue Gem::GemNotFoundException => e | |
if from_cache && gem_file = find_gem_in_cache(gem, version) | |
puts "Located #{gem} in gem cache..." | |
installer.install gem_file | |
else | |
exception = e | |
end | |
rescue => e | |
exception = e | |
end | |
if installer.installed_gems.empty? && exception | |
error "Failed to install gem '#{gem} (#{version})' (#{exception.message})" | |
end | |
installer.installed_gems.each do |spec| | |
success "Successfully installed #{spec.full_name}" | |
end | |
return !installer.installed_gems.empty? | |
end | |
end | |
# Install a gem - looks in the system's gem cache instead of remotely; | |
# won't process rdoc or ri options. | |
def install_gem_from_cache(gem, options = {}) | |
version = options.delete(:version) | |
Gem.configuration.update_sources = false | |
installer = Gem::DependencyInstaller.new(options.merge(:user_install => false)) | |
exception = nil | |
begin | |
if gem_file = find_gem_in_cache(gem, version) | |
puts "Located #{gem} in gem cache..." | |
installer.install gem_file | |
else | |
raise Gem::InstallError, "Unknown gem #{gem}" | |
end | |
rescue Gem::InstallError => e | |
exception = e | |
end | |
if installer.installed_gems.empty? && exception | |
error "Failed to install gem '#{gem}' (#{e.message})" | |
end | |
installer.installed_gems.each do |spec| | |
success "Successfully installed #{spec.full_name}" | |
end | |
end | |
# Install a gem from source - builds and packages it first then installs. | |
def install_gem_from_src(gem_src_dir, options = {}) | |
if !File.directory?(gem_src_dir) | |
raise "Missing rubygem source path: #{gem_src_dir}" | |
end | |
if options[:install_dir] && !File.directory?(options[:install_dir]) | |
raise "Missing rubygems path: #{options[:install_dir]}" | |
end | |
gem_name = File.basename(gem_src_dir) | |
gem_pkg_dir = File.expand_path(File.join(gem_src_dir, 'pkg')) | |
# We need to use local bin executables if available. | |
thor = "#{Gem.ruby} -S #{which('thor')}" | |
rake = "#{Gem.ruby} -S #{which('rake')}" | |
# Handle pure Thor installation instead of Rake | |
if File.exists?(File.join(gem_src_dir, 'Thorfile')) | |
# Remove any existing packages. | |
FileUtils.rm_rf(gem_pkg_dir) if File.directory?(gem_pkg_dir) | |
# Create the package. | |
FileUtils.cd(gem_src_dir) { system("#{thor} :package") } | |
# Install the package using rubygems. | |
if package = Dir[File.join(gem_pkg_dir, "#{gem_name}-*.gem")].last | |
FileUtils.cd(File.dirname(package)) do | |
install_gem(File.basename(package), options.dup) | |
return true | |
end | |
else | |
raise Gem::InstallError, "No package found for #{gem_name}" | |
end | |
# Handle elaborate installation through Rake | |
else | |
# Clean and regenerate any subgems for meta gems. | |
Dir[File.join(gem_src_dir, '*', 'Rakefile')].each do |rakefile| | |
FileUtils.cd(File.dirname(rakefile)) do | |
system("#{rake} clobber_package; #{rake} package") | |
end | |
end | |
# Handle the main gem install. | |
if File.exists?(File.join(gem_src_dir, 'Rakefile')) | |
subgems = [] | |
# Remove any existing packages. | |
FileUtils.cd(gem_src_dir) { system("#{rake} clobber_package") } | |
# Create the main gem pkg dir if it doesn't exist. | |
FileUtils.mkdir_p(gem_pkg_dir) unless File.directory?(gem_pkg_dir) | |
# Copy any subgems to the main gem pkg dir. | |
Dir[File.join(gem_src_dir, '*', 'pkg', '*.gem')].each do |subgem_pkg| | |
if name = File.basename(subgem_pkg, '.gem')[/^(.*?)-([\d\.]+)$/, 1] | |
subgems << name | |
end | |
dest = File.join(gem_pkg_dir, File.basename(subgem_pkg)) | |
FileUtils.copy_entry(subgem_pkg, dest, true, false, true) | |
end | |
# Finally generate the main package and install it; subgems | |
# (dependencies) are local to the main package. | |
FileUtils.cd(gem_src_dir) do | |
system("#{rake} package") | |
FileUtils.cd(gem_pkg_dir) do | |
if package = Dir[File.join(gem_pkg_dir, "#{gem_name}-*.gem")].last | |
# If the (meta) gem has it's own package, install it. | |
install_gem(File.basename(package), options.merge(:refresh => subgems)) | |
else | |
# Otherwise install each package seperately. | |
Dir["*.gem"].each { |gem| install_gem(gem, options.dup) } | |
end | |
end | |
return true | |
end | |
end | |
end | |
raise Gem::InstallError, "No Rakefile found for #{gem_name}" | |
end | |
# Uninstall a gem. | |
def uninstall_gem(gem, options = {}) | |
if options[:version] && !options[:version].is_a?(Gem::Requirement) | |
options[:version] = Gem::Requirement.new ["= #{options[:version]}"] | |
end | |
update_source_index(options[:install_dir]) if options[:install_dir] | |
Gem::Uninstaller.new(gem, options).uninstall | |
end | |
# Use the local bin/* executables if available. | |
def which(executable) | |
if File.executable?(exec = File.join(Dir.pwd, 'bin', executable)) | |
exec | |
else | |
executable | |
end | |
end | |
# Partition gems into system, local and missing gems | |
def partition_dependencies(dependencies, gem_dir) | |
system_specs, local_specs, missing_deps = [], [], [] | |
if gem_dir && File.directory?(gem_dir) | |
gem_dir = File.expand_path(gem_dir) | |
::Gem.clear_paths; ::Gem.path.unshift(gem_dir) | |
::Gem.source_index.refresh! | |
dependencies.each do |dep| | |
if gemspec = ::Gem.source_index.search(dep).last | |
if gemspec.loaded_from.index(gem_dir) == 0 | |
local_specs << gemspec | |
else | |
system_specs << gemspec | |
end | |
else | |
missing_deps << dep | |
end | |
end | |
::Gem.clear_paths | |
end | |
[system_specs, local_specs, missing_deps] | |
end | |
# Create a modified executable wrapper in the specified bin directory. | |
def ensure_bin_wrapper_for(gem_dir, bin_dir, *gems) | |
if bin_dir && File.directory?(bin_dir) | |
gems.each do |gem| | |
if gemspec_path = Dir[File.join(gem_dir, 'specifications', "#{gem}-*.gemspec")].last | |
spec = Gem::Specification.load(gemspec_path) | |
spec.executables.each do |exec| | |
executable = File.join(bin_dir, exec) | |
message "Writing executable wrapper #{executable}" | |
File.open(executable, 'w', 0755) do |f| | |
f.write(executable_wrapper(spec, exec)) | |
end | |
end | |
end | |
end | |
end | |
end | |
private | |
def executable_wrapper(spec, bin_file_name) | |
<<-TEXT | |
#!/usr/bin/env ruby | |
# | |
# This file was generated by Merb's GemManagement | |
# | |
# The application '#{spec.name}' is installed as part of a gem, and | |
# this file is here to facilitate running it. | |
begin | |
require 'minigems' | |
rescue LoadError | |
require 'rubygems' | |
end | |
if File.directory?(gems_dir = File.join(Dir.pwd, 'gems')) || | |
File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')) | |
$BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir) | |
end | |
version = "#{Gem::Requirement.default}" | |
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then | |
version = $1 | |
ARGV.shift | |
end | |
gem '#{spec.name}', version | |
load '#{bin_file_name}' | |
TEXT | |
end | |
def find_gem_in_cache(gem, version) | |
spec = if version | |
version = Gem::Requirement.new ["= #{version}"] unless version.is_a?(Gem::Requirement) | |
Gem.source_index.find_name(gem, version).first | |
else | |
Gem.source_index.find_name(gem).sort_by { |g| g.version }.last | |
end | |
if spec && File.exists?(gem_file = "#{spec.installation_path}/cache/#{spec.full_name}.gem") | |
gem_file | |
end | |
end | |
def update_source_index(dir) | |
Gem.source_index.load_gems_in(File.join(dir, 'specifications')) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment