From fbf59bdbea63efd34ccc144e648467d2f52e7345 Mon Sep 17 00:00:00 2001 From: drbrain Date: Sat, 10 Nov 2007 07:48:56 +0000 Subject: Import RubyGems trunk revision 1493. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13862 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/remote_installer.rb | 195 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 lib/rubygems/remote_installer.rb (limited to 'lib/rubygems/remote_installer.rb') diff --git a/lib/rubygems/remote_installer.rb b/lib/rubygems/remote_installer.rb new file mode 100644 index 0000000000..e33fd548f2 --- /dev/null +++ b/lib/rubygems/remote_installer.rb @@ -0,0 +1,195 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' + +require 'rubygems' +require 'rubygems/installer' +require 'rubygems/source_info_cache' + +module Gem + + class RemoteInstaller + + include UserInteraction + + # options[:http_proxy]:: + # * [String]: explicit specification of proxy; overrides any + # environment variable setting + # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, HTTP_PROXY_PASS) + # * :no_proxy: ignore environment variables and _don't_ + # use a proxy + # + # * :cache_dir: override where downloaded gems are cached. + def initialize(options={}) + @options = options + @source_index_hash = nil + end + + # This method will install package_name onto the local system. + # + # gem_name:: + # [String] Name of the Gem to install + # + # version_requirement:: + # [default = ">= 0"] Gem version requirement to install + # + # Returns:: + # an array of Gem::Specification objects, one for each gem installed. + # + def install(gem_name, version_requirement = Gem::Requirement.default, + force = false, install_dir = Gem.dir) + unless version_requirement.respond_to?(:satisfied_by?) + version_requirement = Gem::Requirement.new [version_requirement] + end + installed_gems = [] + begin + spec, source = find_gem_to_install(gem_name, version_requirement) + dependencies = find_dependencies_not_installed(spec.dependencies) + + installed_gems << install_dependencies(dependencies, force, install_dir) + + cache_dir = @options[:cache_dir] || File.join(install_dir, "cache") + destination_file = File.join(cache_dir, spec.full_name + ".gem") + + download_gem(destination_file, source, spec) + + installer = new_installer(destination_file) + installed_gems.unshift installer.install(force, install_dir) + rescue RemoteInstallationSkipped => e + alert_error e.message + end + installed_gems.flatten + end + + # Return a hash mapping the available source names to the source + # index of that source. + def source_index_hash + return @source_index_hash if @source_index_hash + @source_index_hash = {} + Gem::SourceInfoCache.cache_data.each do |source_uri, sic_entry| + @source_index_hash[source_uri] = sic_entry.source_index + end + @source_index_hash + end + + # Finds the Gem::Specification objects and the corresponding source URI + # for gems matching +gem_name+ and +version_requirement+ + def specs_n_sources_matching(gem_name, version_requirement) + specs_n_sources = [] + + source_index_hash.each do |source_uri, source_index| + specs = source_index.search(/^#{Regexp.escape gem_name}$/i, + version_requirement) + # TODO move to SourceIndex#search? + ruby_version = Gem::Version.new RUBY_VERSION + specs = specs.select do |spec| + spec.required_ruby_version.nil? or + spec.required_ruby_version.satisfied_by? ruby_version + end + specs.each { |spec| specs_n_sources << [spec, source_uri] } + end + + if specs_n_sources.empty? then + raise GemNotFoundException, "Could not find #{gem_name} (#{version_requirement}) in any repository" + end + + specs_n_sources = specs_n_sources.sort_by { |gs,| gs.version }.reverse + + specs_n_sources + end + + # Find a gem to be installed by interacting with the user. + def find_gem_to_install(gem_name, version_requirement) + specs_n_sources = specs_n_sources_matching gem_name, version_requirement + + top_3_versions = specs_n_sources.map{|gs| gs.first.version}.uniq[0..3] + specs_n_sources.reject!{|gs| !top_3_versions.include?(gs.first.version)} + + binary_gems = specs_n_sources.reject { |item| + item[0].platform.nil? || item[0].platform==Platform::RUBY + } + + # only non-binary gems...return latest + return specs_n_sources.first if binary_gems.empty? + + list = specs_n_sources.collect { |spec, source_uri| + "#{spec.name} #{spec.version} (#{spec.platform})" + } + + list << "Skip this gem" + list << "Cancel installation" + + string, index = choose_from_list( + "Select which gem to install for your platform (#{RUBY_PLATFORM})", + list) + + if index.nil? or index == (list.size - 1) then + raise RemoteInstallationCancelled, "Installation of #{gem_name} cancelled." + end + + if index == (list.size - 2) then + raise RemoteInstallationSkipped, "Installation of #{gem_name} skipped." + end + + specs_n_sources[index] + end + + def find_dependencies_not_installed(dependencies) + to_install = [] + dependencies.each do |dependency| + srcindex = Gem::SourceIndex.from_installed_gems + matches = srcindex.find_name(dependency.name, dependency.requirement_list) + to_install.push dependency if matches.empty? + end + to_install + end + + # Install all the given dependencies. Returns an array of + # Gem::Specification objects, one for each dependency installed. + # + # TODO: For now, we recursively install, but this is not the right + # way to do things (e.g. if a package fails to download, we + # shouldn't install anything). + def install_dependencies(dependencies, force, install_dir) + return if @options[:ignore_dependencies] + installed_gems = [] + dependencies.each do |dep| + if @options[:include_dependencies] || + ask_yes_no("Install required dependency #{dep.name}?", true) + remote_installer = RemoteInstaller.new @options + installed_gems << remote_installer.install(dep.name, + dep.version_requirements, + force, install_dir) + elsif force then + # ignore + else + raise DependencyError, "Required dependency #{dep.name} not installed" + end + end + installed_gems + end + + def download_gem(destination_file, source, spec) + return if File.exist? destination_file + uri = source + "/gems/#{spec.full_name}.gem" + response = Gem::RemoteFetcher.fetcher.fetch_path uri + write_gem_to_file response, destination_file + end + + def write_gem_to_file(body, destination_file) + FileUtils.mkdir_p(File.dirname(destination_file)) unless File.exist?(destination_file) + File.open(destination_file, 'wb') do |out| + out.write(body) + end + end + + def new_installer(gem) + return Installer.new(gem, @options) + end + end + +end -- cgit v1.2.3