* lib/rubygems*: Updated to RubyGems 2.0

* test/rubygems*:  ditto.

* common.mk (prelude):  Updated for RubyGems 2.0 source rearrangement.

* tool/change_maker.rb:  Allow invalid UTF-8 characters in source
  files.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37976 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2012-11-29 06:52:18 +00:00
Родитель 3f606b7063
Коммит 9694bb8cac
214 изменённых файлов: 14049 добавлений и 7085 удалений

Просмотреть файл

@ -1,3 +1,13 @@
Thu Nov 29 15:51:54 2012 Eric Hodel <drbrain@segment7.net>
* lib/rubygems*: Updated to RubyGems 2.0
* test/rubygems*: ditto.
* common.mk (prelude): Updated for RubyGems 2.0 source rearrangement.
* tool/change_maker.rb: Allow invalid UTF-8 characters in source
files.
Thu Nov 29 15:38:14 2012 Koichi Sasada <ko1@atdot.net> Thu Nov 29 15:38:14 2012 Koichi Sasada <ko1@atdot.net>
* include/ruby/debug.h: provide rb_tracearg_*() APIs, * include/ruby/debug.h: provide rb_tracearg_*() APIs,

Просмотреть файл

@ -887,7 +887,8 @@ $(MINIPRELUDE_C): $(srcdir)/tool/compile_prelude.rb $(srcdir)/prelude.rb
$(Q) $(BASERUBY) -I$(srcdir) $(srcdir)/tool/compile_prelude.rb $(srcdir)/prelude.rb $@ $(Q) $(BASERUBY) -I$(srcdir) $(srcdir)/tool/compile_prelude.rb $(srcdir)/prelude.rb $@
prelude.c: $(srcdir)/tool/compile_prelude.rb $(RBCONFIG) \ prelude.c: $(srcdir)/tool/compile_prelude.rb $(RBCONFIG) \
$(srcdir)/lib/rubygems/defaults.rb $(srcdir)/lib/rubygems/custom_require.rb \ $(srcdir)/lib/rubygems/defaults.rb \
$(srcdir)/lib/rubygems/core_ext/kernel_gem.rb \
$(PRELUDE_SCRIPTS) $(PREP) $(PRELUDE_SCRIPTS) $(PREP)
$(ECHO) generating $@ $(ECHO) generating $@
$(Q) $(COMPILE_PRELUDE) $(PRELUDE_SCRIPTS) $@ $(Q) $(COMPILE_PRELUDE) $(PRELUDE_SCRIPTS) $@

Просмотреть файл

@ -5,32 +5,6 @@
# See LICENSE.txt for permissions. # See LICENSE.txt for permissions.
#++ #++
module Gem
QUICKLOADER_SUCKAGE = RUBY_VERSION =~ /^1\.9\.1/
# Only MRI 1.9.2 has the custom prelude.
GEM_PRELUDE_SUCKAGE = RUBY_VERSION =~ /^1\.9\.2/ && RUBY_ENGINE == "ruby"
end
if Gem::GEM_PRELUDE_SUCKAGE and defined?(Gem::QuickLoader) then
Gem::QuickLoader.remove
$LOADED_FEATURES.delete Gem::QuickLoader.path_to_full_rubygems_library
if $LOADED_FEATURES.any? do |path| path.end_with? '/rubygems.rb' end then
# TODO path does not exist here
raise LoadError, "another rubygems is already loaded from #{path}"
end
class << Gem
remove_method :try_activate if Gem.respond_to?(:try_activate, true)
end
end
require 'rubygems/defaults'
require 'rbconfig'
require "rubygems/deprecate"
## ##
# RubyGems is the Ruby standard for publishing and managing third party # RubyGems is the Ruby standard for publishing and managing third party
# libraries. # libraries.
@ -72,8 +46,8 @@ require "rubygems/deprecate"
# For RubyGems packagers, provide lib/rubygems/operating_system.rb and # For RubyGems packagers, provide lib/rubygems/operating_system.rb and
# override any defaults from lib/rubygems/defaults.rb. # override any defaults from lib/rubygems/defaults.rb.
# #
# For Ruby implementers, provide lib/rubygems/#{RUBY_ENGINE}.rb and override # For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and
# any defaults from lib/rubygems/defaults.rb. # override any defaults from lib/rubygems/defaults.rb.
# #
# If you need RubyGems to perform extra work on install or uninstall, your # If you need RubyGems to perform extra work on install or uninstall, your
# defaults override file can set pre and post install and uninstall hooks. # defaults override file can set pre and post install and uninstall hooks.
@ -83,8 +57,8 @@ require "rubygems/deprecate"
# == Bugs # == Bugs
# #
# You can submit bugs to the # You can submit bugs to the
# {RubyGems bug tracker}[http://rubyforge.org/tracker/?atid=575&group_id=126] # {RubyGems bug tracker}[https://github.com/rubygems/rubygems/issues]
# on RubyForge # on GitHub
# #
# == Credits # == Credits
# #
@ -112,7 +86,8 @@ require "rubygems/deprecate"
# * Daniel Berger -- djberg96(at)gmail.com # * Daniel Berger -- djberg96(at)gmail.com
# * Phil Hagelberg -- technomancy(at)gmail.com # * Phil Hagelberg -- technomancy(at)gmail.com
# * Ryan Davis -- ryand-ruby(at)zenspider.com # * Ryan Davis -- ryand-ruby(at)zenspider.com
# * Evan Phoenix -- evan@phx.io # * Evan Phoenix -- evan(at)fallingsnow.net
# * Steve Klabnik -- steve(at)steveklabnik.com
# #
# (If your name is missing, PLEASE let us know!) # (If your name is missing, PLEASE let us know!)
# #
@ -120,49 +95,22 @@ require "rubygems/deprecate"
# #
# -The RubyGems Team # -The RubyGems Team
require 'rbconfig'
module Gem module Gem
VERSION = '1.8.24' VERSION = '2.0.a'
end
## # Must be first since it unloads the prelude from 1.9.2
# Raised when RubyGems is unable to load or activate a gem. Contains the require 'rubygems/compatibility'
# name and version requirements of the gem that either conflicts with
# already activated gems or that RubyGems is otherwise unable to activate.
class LoadError < ::LoadError require 'rubygems/defaults'
# Name of gem require 'rubygems/deprecate'
attr_accessor :name require 'rubygems/errors'
# Version requirement of gem
attr_accessor :requirement
end
# :stopdoc:
RubyGemsVersion = VERSION
RbConfigPriorities = %w[
EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name
ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir
]
unless defined?(ConfigMap)
##
# Configuration settings from ::RbConfig
ConfigMap = Hash.new do |cm, key|
cm[key] = RbConfig::CONFIG[key.to_s]
end
else
RbConfigPriorities.each do |key|
ConfigMap[key.to_sym] = RbConfig::CONFIG[key]
end
end
RubyGemsPackageVersion = VERSION
module Gem
RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__) RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__)
# :startdoc:
## ##
# An Array of Regexps that match windows ruby platforms. # An Array of Regexps that match windows ruby platforms.
@ -175,11 +123,13 @@ module Gem
/wince/i, /wince/i,
] ]
@@source_index = nil GEM_DEP_FILES = %w!gem.deps.rb Gemfile Isolate!
@@win_platform = nil @@win_platform = nil
@configuration = nil @configuration = nil
@loaded_specs = {} @loaded_specs = {}
@path_to_default_spec_map = {}
@platforms = [] @platforms = []
@ruby = nil @ruby = nil
@sources = nil @sources = nil
@ -198,12 +148,13 @@ module Gem
# activated. Returns false if it can't find the path in a gem. # activated. Returns false if it can't find the path in a gem.
def self.try_activate path def self.try_activate path
# TODO: deprecate when 1.9.3 comes out.
# finds the _latest_ version... regardless of loaded specs and their deps # finds the _latest_ version... regardless of loaded specs and their deps
# if another gem had a requirement that would mean we shouldn't
# activate the latest version, then either it would alreaby be activated
# or if it was ambigious (and thus unresolved) the code in our custom
# require will try to activate the more specific version.
# TODO: use find_all and bork if ambiguous spec = Gem::Specification.find_inactive_by_path path
spec = Gem::Specification.find_by_path path
return false unless spec return false unless spec
begin begin
@ -215,77 +166,43 @@ module Gem
return true return true
end end
## def self.needs
# Activates an installed gem matching +dep+. The gem must satisfy rs = Gem::RequestSet.new
# +requirements+.
#
# Returns true if the gem is activated, false if it is already
# loaded, or an exception otherwise.
#
# Gem#activate adds the library paths in +dep+ to $LOAD_PATH. Before a Gem
# is activated its required Gems are activated. If the version information
# is omitted, the highest version Gem of the supplied name is loaded. If a
# Gem is not found that meets the version requirements or a required Gem is
# not found, a Gem::LoadError is raised.
#
# More information on version requirements can be found in the
# Gem::Requirement and Gem::Version documentation.
def self.activate(dep, *requirements) yield rs
raise ArgumentError, "Deprecated use of Gem.activate(dep)" if
Gem::Dependency === dep
Gem::Specification.find_by_name(dep, *requirements).activate finish_resolve rs
end end
def self.activate_dep dep, *requirements # :nodoc: def self.finish_resolve(request_set=Gem::RequestSet.new)
dep.to_spec.activate request_set.import Gem::Specification.unresolved_deps.values
request_set.resolve_current.each do |s|
s.full_spec.activate
end
end end
def self.activate_spec spec # :nodoc: def self.detect_gemdeps
spec.activate if path = ENV['RUBYGEMS_GEMDEPS']
end path = path.dup.untaint
def self.unresolved_deps if path == "-"
@unresolved_deps ||= Hash.new { |h, n| h[n] = Gem::Dependency.new n } path = GEM_DEP_FILES.find { |f| File.exists?(f) }
end
## return unless path
# An Array of all possible load paths for all versions of all gems in the end
# Gem installation.
def self.all_load_paths return unless File.exists? path
result = []
Gem.path.each do |gemdir| rs = Gem::RequestSet.new
each_load_path all_partials(gemdir) do |load_path| rs.load_gemdeps path
result << load_path
rs.resolve_current.map do |s|
sp = s.full_spec
sp.activate
sp
end end
end end
result
end
##
# Return all the partial paths in +gemdir+.
def self.all_partials(gemdir)
Dir[File.join(gemdir, "gems/*")]
end
private_class_method :all_partials
##
# See if a given gem is available.
def self.available?(dep, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
unless dep.respond_to?(:name) and dep.respond_to?(:requirement) then
dep = Gem::Dependency.new dep, requirements
end
not dep.matching_specs(true).empty?
end end
## ##
@ -343,11 +260,10 @@ module Gem
# mainly used by the unit tests to provide test isolation. # mainly used by the unit tests to provide test isolation.
def self.clear_paths def self.clear_paths
@@source_index = nil
@paths = nil @paths = nil
@user_home = nil @user_home = nil
@searcher = nil
Gem::Specification.reset Gem::Specification.reset
Gem::Security.reset if const_defined? :Security
end end
## ##
@ -377,7 +293,8 @@ module Gem
# package is not available as a gem, return nil. # package is not available as a gem, return nil.
def self.datadir(gem_name) def self.datadir(gem_name)
# TODO: deprecate # TODO: deprecate and move to Gem::Specification
# and drop the extra ", gem_name" which is uselessly redundant
spec = @loaded_specs[gem_name] spec = @loaded_specs[gem_name]
return nil if spec.nil? return nil if spec.nil?
File.join spec.full_gem_path, "data", gem_name File.join spec.full_gem_path, "data", gem_name
@ -391,10 +308,12 @@ module Gem
Zlib::Deflate.deflate data Zlib::Deflate.deflate data
end end
# DOC: needs doc'd or :nodoc'd
def self.paths def self.paths
@paths ||= Gem::PathSupport.new @paths ||= Gem::PathSupport.new
end end
# DOC: needs doc'd or :nodoc'd
def self.paths=(env) def self.paths=(env)
clear_paths clear_paths
@paths = Gem::PathSupport.new env @paths = Gem::PathSupport.new env
@ -450,7 +369,7 @@ module Gem
require 'fileutils' require 'fileutils'
%w[cache doc gems specifications].each do |name| %w[cache build_info doc gems specifications].each do |name|
subdir = File.join dir, name subdir = File.join dir, name
next if File.exist? subdir next if File.exist? subdir
FileUtils.mkdir_p subdir rescue nil # in case of perms issues -- lame FileUtils.mkdir_p subdir rescue nil # in case of perms issues -- lame
@ -566,13 +485,28 @@ module Gem
Zlib::Inflate.inflate data Zlib::Inflate.inflate data
end end
##
# Top level install helper method. Allows you to install gems interactively:
#
# % irb
# >> Gem.install "minitest"
# Fetching: minitest-3.0.1.gem (100%)
# => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>]
def self.install name, version = Gem::Requirement.default
require "rubygems/dependency_installer"
inst = Gem::DependencyInstaller.new
inst.install name, version
inst.installed_gems
end
## ##
# Get the default RubyGems API host. This is normally # Get the default RubyGems API host. This is normally
# <tt>https://rubygems.org</tt>. # <tt>https://rubygems.org</tt>.
def self.host def self.host
# TODO: move to utils # TODO: move to utils
@host ||= "https://rubygems.org" @host ||= Gem::DEFAULT_HOST
end end
## Set the default RubyGems API host. ## Set the default RubyGems API host.
@ -582,43 +516,6 @@ module Gem
@host = host @host = host
end end
##
# Return a list of all possible load paths for the latest version for all
# gems in the Gem installation.
def self.latest_load_paths
result = []
Gem.path.each do |gemdir|
each_load_path(latest_partials(gemdir)) do |load_path|
result << load_path
end
end
result
end
##
# Return only the latest partial paths in the given +gemdir+.
def self.latest_partials(gemdir)
latest = {}
all_partials(gemdir).each do |gp|
base = File.basename gp
if base.to_s =~ /(.*)-((\d+\.)*\d+)/ then
name, version = $1, $2
ver = Gem::Version.new(version)
if latest[name].nil? || ver > latest[name][0]
latest[name] = [ver, gp]
end
end
end
latest.collect { |k,v| v[1] }
end
private_class_method :latest_partials
## ##
# The index to insert activated gem paths into the $LOAD_PATH. # The index to insert activated gem paths into the $LOAD_PATH.
# #
@ -629,16 +526,6 @@ module Gem
def self.load_path_insert_index def self.load_path_insert_index
index = $LOAD_PATH.index ConfigMap[:sitelibdir] index = $LOAD_PATH.index ConfigMap[:sitelibdir]
if QUICKLOADER_SUCKAGE then
$LOAD_PATH.each_with_index do |path, i|
if path.instance_variables.include?(:@gem_prelude_index) or
path.instance_variables.include?('@gem_prelude_index') then
index = i
break
end
end
end
index index
end end
@ -711,27 +598,6 @@ module Gem
"#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
end end
##
# Get the appropriate cache path.
#
# Pass a string to use a different base path, or nil/false (default) for
# Gem.dir.
#
def self.cache_dir(custom_dir=false)
File.join(custom_dir || Gem.dir, "cache")
end
##
# Given a gem path, find the gem in cache.
#
# Pass a string as the second argument to use a different base path, or
# nil/false (default) for Gem.dir.
def self.cache_gem(filename, user_dir=false)
cache_dir(user_dir).add(filename)
end
## ##
# Set array of platforms this RubyGems supports (primarily for testing). # Set array of platforms this RubyGems supports (primarily for testing).
@ -769,6 +635,15 @@ module Gem
@post_install_hooks << hook @post_install_hooks << hook
end end
##
# Adds a post-installs hook that will be passed a Gem::DependencyInstaller
# and a list of installed specifications when
# Gem::DependencyInstaller#install is complete
def self.done_installing(&hook)
@done_installing_hooks << hook
end
## ##
# Adds a hook that will get run after Gem::Specification.reset is # Adds a hook that will get run after Gem::Specification.reset is
# run. # run.
@ -828,39 +703,10 @@ module Gem
end end
## ##
# Promotes the load paths of the +gem_name+ over the load paths of # Refresh available gems from disk.
# +over_name+. Useful for allowing one gem to override features in another
# using #find_files.
def self.promote_load_path(gem_name, over_name)
gem = Gem.loaded_specs[gem_name]
over = Gem.loaded_specs[over_name]
raise ArgumentError, "gem #{gem_name} is not activated" if gem.nil?
raise ArgumentError, "gem #{over_name} is not activated" if over.nil?
last_gem_path = Gem::Path.path(gem.full_gem_path).add(gem.require_paths.last)
over_paths = over.require_paths.map do |path|
Gem::Path.path(over.full_gem_path).add(path).to_s
end
over_paths.each do |path|
$LOAD_PATH.delete path
end
gem = $LOAD_PATH.index(last_gem_path) + 1
$LOAD_PATH.insert(gem, *over_paths)
end
##
# Refresh source_index from disk and clear searcher.
def self.refresh def self.refresh
Gem::Specification.reset Gem::Specification.reset
@source_index = nil
@searcher = nil
end end
## ##
@ -870,50 +716,6 @@ module Gem
File.open path, binary_mode do |f| f.read end File.open path, binary_mode do |f| f.read end
end end
##
# Report a load error during activation. The message of load error
# depends on whether it was a version mismatch or if there are not gems of
# any version by the requested name.
def self.report_activate_error(gem)
matches = Gem::Specification.find_by_name(gem.name)
if matches.empty? then
error = Gem::LoadError.new(
"Could not find RubyGem #{gem.name} (#{gem.requirement})\n")
else
error = Gem::LoadError.new(
"RubyGem version error: " +
"#{gem.name}(#{matches.first.version} not #{gem.requirement})\n")
end
error.name = gem.name
error.requirement = gem.requirement
raise error
end
private_class_method :report_activate_error
##
# Full path to +libfile+ in +gemname+. Searches for the latest gem unless
# +requirements+ is given.
def self.required_location(gemname, libfile, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
matches = Gem::Specification.find_all_by_name gemname, *requirements
return nil if matches.empty?
spec = matches.last
spec.require_paths.each do |path|
result = Gem::Path.path(spec.full_gem_path).add(path, libfile)
return result if result.exist?
end
nil
end
## ##
# The path to the running Ruby interpreter. # The path to the running Ruby interpreter.
@ -928,6 +730,7 @@ module Gem
@ruby @ruby
end end
# DOC: needs doc'd or :nodoc'd
def self.latest_spec_for name def self.latest_spec_for name
dependency = Gem::Dependency.new name dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher fetcher = Gem::SpecFetcher.fetcher
@ -942,13 +745,16 @@ module Gem
match and fetcher.fetch_spec(*match) match and fetcher.fetch_spec(*match)
end end
# DOC: needs doc'd or :nodoc'd
def self.latest_version_for name def self.latest_version_for name
spec = latest_spec_for name spec = latest_spec_for name
spec and spec.version spec and spec.version
end end
# DOC: needs doc'd or :nodoc'd
def self.latest_rubygems_version def self.latest_rubygems_version
latest_version_for "rubygems-update" latest_version_for("rubygems-update") or
raise "Can't find 'rubygems-update' in any repo. Check `gem source list`."
end end
## ##
@ -968,36 +774,34 @@ module Gem
end end
## ##
# The GemPathSearcher object used to search for matching installed gems. # A Gem::Version for the currently running RubyGems
def self.searcher def self.rubygems_version
@searcher ||= Gem::GemPathSearcher.new return @rubygems_version if defined? @rubygems_version
@rubygems_version = Gem::Version.new Gem::VERSION
end end
## ##
# Returns the Gem::SourceIndex of specifications that are in the Gem.path # Returns an Array of sources to fetch remote gems from. Uses
# default_sources if the sources list is empty.
def self.source_index
@@source_index ||= Gem::Deprecate.skip_during do
SourceIndex.new Gem::Specification.dirs
end
end
##
# Returns an Array of sources to fetch remote gems from. If the sources
# list is empty, attempts to load the "sources" gem, then uses
# default_sources if it is not installed.
def self.sources def self.sources
@sources ||= default_sources @sources ||= Gem::SourceList.from(default_sources)
end end
## ##
# Need to be able to set the sources without calling # Need to be able to set the sources without calling
# Gem.sources.replace since that would cause an infinite loop. # Gem.sources.replace since that would cause an infinite loop.
#
# DOC: This comment is not documentation about the method itself, it's
# more of a code comment about the implementation.
def self.sources= new_sources def self.sources= new_sources
@sources = new_sources if !new_sources
@sources = nil
else
@sources = Gem::SourceList.from(new_sources)
end
end end
## ##
@ -1007,12 +811,6 @@ module Gem
@suffix_pattern ||= "{#{suffixes.join(',')}}" @suffix_pattern ||= "{#{suffixes.join(',')}}"
end end
def self.loaded_path? path
# TODO: ruby needs a feature to let us query what's loaded in 1.8 and 1.9
re = /(^|\/)#{Regexp.escape path}#{Regexp.union(*Gem.suffixes)}$/
$LOADED_FEATURES.any? { |s| s =~ re }
end
## ##
# Suffixes for require-able paths. # Suffixes for require-able paths.
@ -1067,7 +865,7 @@ module Gem
# The home directory for the user. # The home directory for the user.
def self.user_home def self.user_home
@user_home ||= find_home @user_home ||= find_home.untaint
end end
## ##
@ -1126,6 +924,9 @@ module Gem
load_plugin_files files load_plugin_files files
end end
# FIX: Almost everywhere else we use the `def self.` way of defining class
# methods, and then we switch over to `class << self` here. Pick one or the
# other.
class << self class << self
## ##
@ -1134,29 +935,73 @@ module Gem
attr_reader :loaded_specs attr_reader :loaded_specs
## ##
# The list of hooks to be run before Gem::Install#install finishes # Register a Gem::Specification for default gem
# installation
def register_default_spec(spec)
spec.files.each do |file|
@path_to_default_spec_map[file] = spec
end
end
##
# Find a Gem::Specification of default gem from +path+
def find_unresolved_default_spec(path)
Gem.suffixes.each do |suffix|
spec = @path_to_default_spec_map["#{path}#{suffix}"]
return spec if spec
end
nil
end
##
# Remove needless Gem::Specification of default gem from
# unresolved default gem list
def remove_unresolved_default_spec(spec)
spec.files.each do |file|
@path_to_default_spec_map.delete(file)
end
end
##
# Clear default gem related varibles. It is for test
def clear_default_specs
@path_to_default_spec_map.clear
end
##
# The list of hooks to be run after Gem::Installer#install extracts files
# and builds extensions
attr_reader :post_build_hooks attr_reader :post_build_hooks
## ##
# The list of hooks to be run before Gem::Install#install does any work # The list of hooks to be run after Gem::Installer#install completes
# installation
attr_reader :post_install_hooks attr_reader :post_install_hooks
##
# The list of hooks to be run after Gem::DependencyInstaller installs a
# set of gems
attr_reader :done_installing_hooks
## ##
# The list of hooks to be run after Gem::Specification.reset is run. # The list of hooks to be run after Gem::Specification.reset is run.
attr_reader :post_reset_hooks attr_reader :post_reset_hooks
## ##
# The list of hooks to be run before Gem::Uninstall#uninstall does any # The list of hooks to be run after Gem::Uninstaller#uninstall completes
# work # installation
attr_reader :post_uninstall_hooks attr_reader :post_uninstall_hooks
## ##
# The list of hooks to be run after Gem::Install#install is finished # The list of hooks to be run before Gem::Installer#install does any work
attr_reader :pre_install_hooks attr_reader :pre_install_hooks
@ -1166,15 +1011,12 @@ module Gem
attr_reader :pre_reset_hooks attr_reader :pre_reset_hooks
## ##
# The list of hooks to be run after Gem::Uninstall#uninstall is finished # The list of hooks to be run before Gem::Uninstaller#uninstall does any
# work
attr_reader :pre_uninstall_hooks attr_reader :pre_uninstall_hooks
end end
def self.cache # :nodoc:
source_index
end
## ##
# Location of Marshal quick gemspecs on remote repositories # Location of Marshal quick gemspecs on remote repositories
@ -1184,77 +1026,21 @@ module Gem
autoload :Requirement, 'rubygems/requirement' autoload :Requirement, 'rubygems/requirement'
autoload :Dependency, 'rubygems/dependency' autoload :Dependency, 'rubygems/dependency'
autoload :DependencyList, 'rubygems/dependency_list' autoload :DependencyList, 'rubygems/dependency_list'
autoload :GemPathSearcher, 'rubygems/gem_path_searcher' autoload :SourceList, 'rubygems/source_list'
autoload :SpecFetcher, 'rubygems/spec_fetcher' autoload :SpecFetcher, 'rubygems/spec_fetcher'
autoload :Specification, 'rubygems/specification' autoload :Specification, 'rubygems/specification'
autoload :Cache, 'rubygems/source_index'
autoload :SourceIndex, 'rubygems/source_index'
autoload :PathSupport, 'rubygems/path_support' autoload :PathSupport, 'rubygems/path_support'
autoload :Platform, 'rubygems/platform' autoload :Platform, 'rubygems/platform'
autoload :Builder, 'rubygems/builder'
autoload :ConfigFile, 'rubygems/config_file' autoload :ConfigFile, 'rubygems/config_file'
end autoload :DependencyResolver, 'rubygems/dependency_resolver'
autoload :RequestSet, 'rubygems/request_set'
module Kernel require "rubygems/specification"
remove_method :gem if 'method' == defined? gem # from gem_prelude.rb on 1.9
##
# Use Kernel#gem to activate a specific version of +gem_name+.
#
# +requirements+ is a list of version requirements that the
# specified gem must match, most commonly "= example.version.number". See
# Gem::Requirement for how to specify a version requirement.
#
# If you will be activating the latest version of a gem, there is no need to
# call Kernel#gem, Kernel#require will do the right thing for you.
#
# Kernel#gem returns true if the gem was activated, otherwise false. If the
# gem could not be found, didn't match the version requirements, or a
# different version was already activated, an exception will be raised.
#
# Kernel#gem should be called *before* any require statements (otherwise
# RubyGems may load a conflicting library version).
#
# In older RubyGems versions, the environment variable GEM_SKIP could be
# used to skip activation of specified gems, for example to test out changes
# that haven't been installed yet. Now RubyGems defers to -I and the
# RUBYLIB environment variable to skip activation of a gem.
#
# Example:
#
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
def gem(gem_name, *requirements) # :doc:
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
spec = Gem::Dependency.new(gem_name, *requirements).to_spec
spec.activate if spec
end
private :gem
end
##
# Return the path to the data directory associated with the named package. If
# the package is loaded as a gem, return the gem specific data directory.
# Otherwise return a path to the share area as define by
# "#{ConfigMap[:datadir]}/#{package_name}".
def RbConfig.datadir(package_name) # :nodoc:
warn "#{Gem.location_of_caller.join ':'}:Warning: " \
"RbConfig.datadir is deprecated and will be removed on or after " \
"August 2011. " \
"Use Gem::datadir."
require 'rbconfig/datadir'
Gem.datadir(package_name) || File.join(Gem::ConfigMap[:datadir], package_name)
end end
require 'rubygems/exceptions' require 'rubygems/exceptions'
# REFACTOR: This should be pulled out into some kind of hacks file.
gem_preluded = Gem::GEM_PRELUDE_SUCKAGE and defined? Gem gem_preluded = Gem::GEM_PRELUDE_SUCKAGE and defined? Gem
unless gem_preluded then # TODO: remove guard after 1.9.2 dropped unless gem_preluded then # TODO: remove guard after 1.9.2 dropped
begin begin
@ -1277,29 +1063,10 @@ unless gem_preluded then # TODO: remove guard after 1.9.2 dropped
end end
## ##
# Enables the require hook for RubyGems. # Loads the default specs.
Gem::Specification.load_defaults
require 'rubygems/custom_require' require 'rubygems/core_ext/kernel_gem'
require 'rubygems/core_ext/kernel_require'
module Gem Gem.detect_gemdeps
class << self
extend Gem::Deprecate
deprecate :activate_dep, "Specification#activate", 2011, 6
deprecate :activate_spec, "Specification#activate", 2011, 6
deprecate :cache, "Gem::source_index", 2011, 8
deprecate :activate, "Specification#activate", 2011, 10
deprecate :all_load_paths, :none, 2011, 10
deprecate :all_partials, :none, 2011, 10
deprecate :latest_load_paths, :none, 2011, 10
deprecate :promote_load_path, :none, 2011, 10
deprecate :available?, "Specification::find_by_name", 2011, 11
deprecate :cache_dir, "Specification#cache_dir", 2011, 11
deprecate :cache_gem, "Specification#cache_file", 2011, 11
deprecate :default_system_source_cache_dir, :none, 2011, 11
deprecate :default_user_source_cache_dir, :none, 2011, 11
deprecate :report_activate_error, :none, 2011, 11
deprecate :required_location, :none, 2011, 11
deprecate :searcher, "Specification", 2011, 11
deprecate :source_index, "Specification", 2011, 11
end
end

Просмотреть файл

@ -0,0 +1,95 @@
module Gem
class AvailableSet
Tuple = Struct.new(:spec, :source)
def initialize
@set = []
@sorted = nil
end
attr_reader :set
def add(spec, source)
@set << Tuple.new(spec, source)
@sorted = nil
self
end
def <<(o)
case o
when AvailableSet
s = o.set
when Array
s = o.map do |sp,so|
if !sp.kind_of?(Specification) or !so.kind_of?(Source)
raise TypeError, "Array must be in [[spec, source], ...] form"
end
Tuple.new(sp,so)
end
else
raise TypeError, "Must be an AvailableSet"
end
@set += s
@sorted = nil
self
end
def empty?
@set.empty?
end
def all_specs
@set.map { |t| t.spec }
end
def match_platform!
@set.reject! { |t| !Gem::Platform.match(t.spec.platform) }
@sorted = nil
self
end
def sorted
@sorted ||= @set.sort do |a,b|
i = b.spec <=> a.spec
i != 0 ? i : (a.source <=> b.source)
end
end
def size
@set.size
end
def source_for(spec)
f = @set.find { |t| t.spec == spec }
f.source
end
def pick_best!
return self if empty?
@set = [sorted.first]
@sorted = nil
self
end
def remove_installed!(dep)
@set.reject! do |t|
# already locally installed
Gem::Specification.any? do |installed_spec|
dep.name == installed_spec.name and
dep.requirement.satisfied_by? installed_spec.version
end
end
@sorted = nil
self
end
def inject_into_list(dep_list)
@set.each { |t| dep_list.add t.spec }
end
end
end

Просмотреть файл

@ -1,99 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems'
require 'rubygems/user_interaction'
Gem.load_yaml
require 'rubygems/package'
##
# The Builder class processes RubyGem specification files
# to produce a .gem file.
class Gem::Builder
include Gem::UserInteraction
##
# Constructs a builder instance for the provided specification
#
# spec:: [Gem::Specification] The specification instance
def initialize(spec)
@spec = spec
end
##
# Builds the gem from the specification. Returns the name of the file
# written.
def build(skip_validation=false)
@spec.mark_version
@spec.validate unless skip_validation
@signer = sign
write_package
say success if Gem.configuration.verbose
File.basename @spec.cache_file
end
def success
<<-EOM
Successfully built RubyGem
Name: #{@spec.name}
Version: #{@spec.version}
File: #{File.basename @spec.cache_file}
EOM
end
private
##
# If the signing key was specified, then load the file, and swap to the
# public key (TODO: we should probably just omit the signing key in favor of
# the signing certificate, but that's for the future, also the signature
# algorithm should be configurable)
def sign
signer = nil
if @spec.respond_to?(:signing_key) and @spec.signing_key then
require 'rubygems/security'
signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain
@spec.signing_key = nil
@spec.cert_chain = signer.cert_chain.map { |cert| cert.to_s }
end
signer
end
def write_package
file_name = File.basename @spec.cache_file
open file_name, 'wb' do |gem_io|
Gem::Package.open gem_io, 'w', @signer do |pkg|
yaml = @spec.to_yaml
pkg.metadata = yaml
@spec.files.each do |file|
next if File.directory?(file)
next if file == file_name # Don't add gem onto itself
stat = File.stat(file)
mode = stat.mode & 0777
size = stat.size
pkg.add_file_simple file, mode, size do |tar_io|
tar_io.write open(file, "rb") { |f| f.read }
end
end
end
end
end
end

Просмотреть файл

@ -9,7 +9,7 @@ require 'rubygems/user_interaction'
## ##
# Base class for all Gem commands. When creating a new gem command, define # Base class for all Gem commands. When creating a new gem command, define
# #new, #execute, #arguments, #defaults_str, #description and #usage # #initialize, #execute, #arguments, #defaults_str, #description and #usage
# (as appropriate). See the above mentioned methods for details. # (as appropriate). See the above mentioned methods for details.
# #
# A very good example to look at is Gem::Commands::ContentsCommand # A very good example to look at is Gem::Commands::ContentsCommand
@ -150,8 +150,9 @@ class Gem::Command
def show_lookup_failure(gem_name, version, errors, domain) def show_lookup_failure(gem_name, version, errors, domain)
if errors and !errors.empty? if errors and !errors.empty?
alert_error "Could not find a valid gem '#{gem_name}' (#{version}), here is why:" msg = "Could not find a valid gem '#{gem_name}' (#{version}), here is why:\n"
errors.each { |x| say " #{x.wordy}" } errors.each { |x| msg << " #{x.wordy}\n" }
alert_error msg
else else
alert_error "Could not find a valid gem '#{gem_name}' (#{version}) in any repository" alert_error "Could not find a valid gem '#{gem_name}' (#{version}) in any repository"
end end
@ -179,6 +180,15 @@ class Gem::Command
args.select { |arg| arg !~ /^-/ } args.select { |arg| arg !~ /^-/ }
end end
##
# Get all [gem, version] from the command line.
#
# An argument in the form gem:ver is pull apart into the gen name and version,
# respectively.
def get_all_gem_names_and_versions
get_all_gem_names.map { |name| name.split(":", 2) }
end
## ##
# Get a single gem name from the command line. Fail if there is no gem name # Get a single gem name from the command line. Fail if there is no gem name
# or if there is more than one gem name given. # or if there is more than one gem name given.
@ -268,8 +278,18 @@ class Gem::Command
# Invoke the command with the given list of arguments. # Invoke the command with the given list of arguments.
def invoke(*args) def invoke(*args)
invoke_with_build_args args, nil
end
##
# Invoke the command with the given list of normal arguments
# and additional build arguments.
def invoke_with_build_args(args, build_args)
handle_options args handle_options args
options[:build_args] = build_args
if options[:help] then if options[:help] then
show_help show_help
elsif @when_invoked then elsif @when_invoked then
@ -344,7 +364,7 @@ class Gem::Command
def handle_options(args) def handle_options(args)
args = add_extra_args(args) args = add_extra_args(args)
@options = @defaults.clone @options = Marshal.load Marshal.dump @defaults # deep copy
parser.parse!(args) parser.parse!(args)
@options[:args] = args @options[:args] = args
end end
@ -372,18 +392,23 @@ class Gem::Command
private private
## def add_parser_description # :nodoc:
# Create on demand parser. return unless description
def parser formatted = description.split("\n\n").map do |chunk|
create_option_parser if @parser.nil? wrap chunk, 80 - 4
@parser end.join "\n"
end
def create_option_parser
@parser = OptionParser.new
@parser.separator nil @parser.separator nil
@parser.separator " Description:"
formatted.split("\n").each do |line|
@parser.separator " #{line.rstrip}"
end
end
def add_parser_options # :nodoc:
@parser.separator nil
regular_options = @option_groups.delete :options regular_options = @option_groups.delete :options
configure_options "", regular_options configure_options "", regular_options
@ -392,45 +417,56 @@ class Gem::Command
@parser.separator nil @parser.separator nil
configure_options group_name, option_list configure_options group_name, option_list
end end
end
##
# Adds a section with +title+ and +content+ to the parser help view. Used
# for adding command arguments and default arguments.
def add_parser_run_info title, content
return if content.empty?
@parser.separator nil
@parser.separator " #{title}:"
content.split(/\n/).each do |line|
@parser.separator " #{line}"
end
end
def add_parser_summary # :nodoc:
return unless @summary
@parser.separator nil
@parser.separator " Summary:"
wrap(@summary, 80 - 4).split("\n").each do |line|
@parser.separator " #{line.strip}"
end
end
##
# Create on demand parser.
def parser
create_option_parser if @parser.nil?
@parser
end
##
# Creates an option parser and fills it in with the help info for the
# command.
def create_option_parser
@parser = OptionParser.new
add_parser_options
@parser.separator nil @parser.separator nil
configure_options "Common", Gem::Command.common_options configure_options "Common", Gem::Command.common_options
unless arguments.empty? add_parser_run_info "Arguments", arguments
@parser.separator nil add_parser_summary
@parser.separator " Arguments:" add_parser_description
arguments.split(/\n/).each do |arg_desc| add_parser_run_info "Defaults", defaults_str
@parser.separator " #{arg_desc}"
end
end
if @summary then
@parser.separator nil
@parser.separator " Summary:"
wrap(@summary, 80 - 4).split("\n").each do |line|
@parser.separator " #{line.strip}"
end
end
if description then
formatted = description.split("\n\n").map do |chunk|
wrap chunk, 80 - 4
end.join "\n"
@parser.separator nil
@parser.separator " Description:"
formatted.split("\n").each do |line|
@parser.separator " #{line.rstrip}"
end
end
unless defaults_str.empty?
@parser.separator nil
@parser.separator " Defaults:"
defaults_str.split(/\n/).each do |line|
@parser.separator " #{line}"
end
end
end end
def configure_options(header, option_list) def configure_options(header, option_list)
@ -521,7 +557,7 @@ basic help message containing pointers to more information.
http://localhost:8808/ http://localhost:8808/
with info about installed gems with info about installed gems
Further information: Further information:
http://rubygems.rubyforge.org http://guides.rubygems.org
HELP HELP
# :startdoc: # :startdoc:
@ -529,7 +565,7 @@ basic help message containing pointers to more information.
end end
## ##
# This is where Commands will be placed in the namespace # \Commands will be placed in this namespace
module Gem::Commands module Gem::Commands
end end

Просмотреть файл

@ -18,12 +18,15 @@ require 'rubygems/user_interaction'
# # file rubygems_plugin.rb # # file rubygems_plugin.rb
# require 'rubygems/command_manager' # require 'rubygems/command_manager'
# #
# Gem::CommandManager.instance.register_command :edit
#
# You should put the implementation of your command in rubygems/commands.
#
# # file rubygems/commands/edit_command.rb
# class Gem::Commands::EditCommand < Gem::Command # class Gem::Commands::EditCommand < Gem::Command
# # ... # # ...
# end # end
# #
# Gem::CommandManager.instance.register_command :edit
#
# See Gem::Command for instructions on writing gem commands. # See Gem::Command for instructions on writing gem commands.
class Gem::CommandManager class Gem::CommandManager
@ -37,6 +40,14 @@ class Gem::CommandManager
@command_manager ||= new @command_manager ||= new
end end
##
# Returns self. Allows a CommandManager instance to stand
# in for the class itself.
def instance
self
end
## ##
# Reset the authoritative instance of the command manager. # Reset the authoritative instance of the command manager.
@ -63,6 +74,7 @@ class Gem::CommandManager
register_command :install register_command :install
register_command :list register_command :list
register_command :lock register_command :lock
register_command :mirror
register_command :outdated register_command :outdated
register_command :owner register_command :owner
register_command :pristine register_command :pristine
@ -78,13 +90,14 @@ class Gem::CommandManager
register_command :unpack register_command :unpack
register_command :update register_command :update
register_command :which register_command :which
register_command :yank
end end
## ##
# Register the Symbol +command+ as a gem command. # Register the Symbol +command+ as a gem command.
def register_command(command) def register_command(command, obj=false)
@commands[command] = false @commands[command] = obj
end end
## ##
@ -95,7 +108,7 @@ class Gem::CommandManager
end end
## ##
# Return the registered command from the command name. # Returns a Command instance for +command_name+
def [](command_name) def [](command_name)
command_name = command_name.intern command_name = command_name.intern
@ -104,56 +117,69 @@ class Gem::CommandManager
end end
## ##
# Return a sorted list of all command names (as strings). # Return a sorted list of all command names as strings.
def command_names def command_names
@commands.keys.collect {|key| key.to_s}.sort @commands.keys.collect {|key| key.to_s}.sort
end end
## ##
# Run the config specified by +args+. # Run the command specified by +args+.
def run(args) def run(args, build_args=nil)
process_args(args) process_args(args, build_args)
rescue StandardError, Timeout::Error => ex rescue StandardError, Timeout::Error => ex
alert_error "While executing gem ... (#{ex.class})\n #{ex.to_s}" alert_error "While executing gem ... (#{ex.class})\n #{ex.to_s}"
ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if ui.backtrace ex
Gem.configuration.backtrace
if Gem.configuration.really_verbose and \
ex.kind_of?(Gem::Exception) and ex.source_exception
e = ex.source_exception
ui.errs.puts "Because of: (#{e.class})\n #{e.to_s}"
ui.backtrace e
end
terminate_interaction(1) terminate_interaction(1)
rescue Interrupt rescue Interrupt
alert_error "Interrupted" alert_error "Interrupted"
terminate_interaction(1) terminate_interaction(1)
end end
def process_args(args) def process_args(args, build_args=nil)
args = args.to_str.split(/\s+/) if args.respond_to?(:to_str) args = args.to_str.split(/\s+/) if args.respond_to?(:to_str)
if args.size == 0
if args.empty? then
say Gem::Command::HELP say Gem::Command::HELP
terminate_interaction(1) terminate_interaction 1
end end
case args[0]
when '-h', '--help' case args.first
when '-h', '--help' then
say Gem::Command::HELP say Gem::Command::HELP
terminate_interaction(0) terminate_interaction 0
when '-v', '--version' when '-v', '--version' then
say Gem::VERSION say Gem::VERSION
terminate_interaction(0) terminate_interaction 0
when /^-/ when /^-/ then
alert_error "Invalid option: #{args[0]}. See 'gem --help'." alert_error "Invalid option: #{args.first}. See 'gem --help'."
terminate_interaction(1) terminate_interaction 1
else else
cmd_name = args.shift.downcase cmd_name = args.shift.downcase
cmd = find_command(cmd_name) cmd = find_command cmd_name
cmd.invoke(*args) cmd.invoke_with_build_args args, build_args
end end
end end
def find_command(cmd_name) def find_command(cmd_name)
possibilities = find_command_possibilities cmd_name possibilities = find_command_possibilities cmd_name
if possibilities.size > 1 then if possibilities.size > 1 then
raise "Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]" raise Gem::CommandLineError,
elsif possibilities.size < 1 then "Ambiguous command #{cmd_name} " \
raise "Unknown command #{cmd_name}" "matches [#{possibilities.join(', ')}]"
elsif possibilities.empty? then
raise Gem::CommandLineError, "Unknown command #{cmd_name}"
end end
self[possibilities.first] self[possibilities.first]
@ -162,7 +188,11 @@ class Gem::CommandManager
def find_command_possibilities(cmd_name) def find_command_possibilities(cmd_name)
len = cmd_name.length len = cmd_name.length
command_names.select { |n| cmd_name == n[0, len] } found = command_names.select { |name| cmd_name == name[0, len] }
exact = found.find { |name| name == cmd_name }
exact ? [exact] : found
end end
private private
@ -170,23 +200,20 @@ class Gem::CommandManager
def load_and_instantiate(command_name) def load_and_instantiate(command_name)
command_name = command_name.to_s command_name = command_name.to_s
const_name = command_name.capitalize.gsub(/_(.)/) { $1.upcase } << "Command" const_name = command_name.capitalize.gsub(/_(.)/) { $1.upcase } << "Command"
commands = Gem::Commands load_error = nil
retried = false
begin begin
commands.const_get(const_name).new
rescue NameError
raise if retried
retried = true
begin begin
require "rubygems/commands/#{command_name}_command" require "rubygems/commands/#{command_name}_command"
rescue Exception => e rescue LoadError => e
alert_error "Loading command: #{command_name} (#{e.class})\n #{e}" load_error = e
ui.errs.puts "\t#{e.backtrace.join "\n\t"}" if
Gem.configuration.backtrace
end end
retry Gem::Commands.const_get(const_name).new
rescue Exception => e
e = load_error if load_error
alert_error "Loading command: #{command_name} (#{e.class})\n\t#{e}"
ui.backtrace e
end end
end end

Просмотреть файл

@ -1,5 +1,5 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/builder' require 'rubygems/package'
class Gem::Commands::BuildCommand < Gem::Command class Gem::Commands::BuildCommand < Gem::Command
@ -22,11 +22,11 @@ class Gem::Commands::BuildCommand < Gem::Command
def execute def execute
gemspec = get_one_gem_name gemspec = get_one_gem_name
if File.exist? gemspec if File.exist? gemspec then
spec = load_gemspec gemspec spec = Gem::Specification.load gemspec
if spec then if spec then
Gem::Builder.new(spec).build options[:force] Gem::Package.build spec, options[:force]
else else
alert_error "Error loading gemspec. Aborting." alert_error "Error loading gemspec. Aborting."
terminate_interaction 1 terminate_interaction 1
@ -37,23 +37,5 @@ class Gem::Commands::BuildCommand < Gem::Command
end end
end end
def load_gemspec filename
if yaml?(filename)
open(filename) do |f|
begin
Gem::Specification.from_yaml(f)
rescue Gem::EndOfYAMLException
nil
end
end
else
Gem::Specification.load(filename) # can return nil
end
end
def yaml?(filename)
line = open(filename) { |f| line = f.gets }
result = line =~ %r{!ruby/object:Gem::Specification}
result
end
end end

Просмотреть файл

@ -4,82 +4,224 @@ require 'rubygems/security'
class Gem::Commands::CertCommand < Gem::Command class Gem::Commands::CertCommand < Gem::Command
def initialize def initialize
super 'cert', 'Manage RubyGems certificates and signing settings' super 'cert', 'Manage RubyGems certificates and signing settings',
:add => [], :remove => [], :list => [], :build => [], :sign => []
add_option('-a', '--add CERT', OptionParser.accept OpenSSL::X509::Certificate do |certificate|
'Add a trusted certificate.') do |value, options| begin
cert = OpenSSL::X509::Certificate.new(File.read(value)) OpenSSL::X509::Certificate.new File.read certificate
Gem::Security.add_trusted_cert(cert) rescue Errno::ENOENT
say "Added '#{cert.subject.to_s}'" raise OptionParser::InvalidArgument, "#{certificate}: does not exist"
end rescue OpenSSL::X509::CertificateError
raise OptionParser::InvalidArgument,
add_option('-l', '--list', "#{certificate}: invalid X509 certificate"
'List trusted certificates.') do |value, options|
glob_str = File::join(Gem::Security::OPT[:trust_dir], '*.pem')
Dir::glob(glob_str) do |path|
begin
cert = OpenSSL::X509::Certificate.new(File.read(path))
# this could probably be formatted more gracefully
say cert.subject.to_s
rescue OpenSSL::X509::CertificateError
next
end
end end
end end
add_option('-r', '--remove STRING', OptionParser.accept OpenSSL::PKey::RSA do |key_file|
'Remove trusted certificates containing', begin
'STRING.') do |value, options| key = OpenSSL::PKey::RSA.new File.read key_file
trust_dir = Gem::Security::OPT[:trust_dir] rescue Errno::ENOENT
glob_str = File::join(trust_dir, '*.pem') raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
rescue OpenSSL::PKey::RSAError
Dir::glob(glob_str) do |path| raise OptionParser::InvalidArgument, "#{key_file}: invalid RSA key"
begin
cert = OpenSSL::X509::Certificate.new(File.read(path))
if cert.subject.to_s.downcase.index(value)
say "Removed '#{cert.subject.to_s}'"
File.unlink(path)
end
rescue OpenSSL::X509::CertificateError
next
end
end end
raise OptionParser::InvalidArgument,
"#{key_file}: private key not found" unless key.private?
key
end
add_option('-a', '--add CERT', OpenSSL::X509::Certificate,
'Add a trusted certificate.') do |cert, options|
options[:add] << cert
end
add_option('-l', '--list [FILTER]',
'List trusted certificates where the',
'subject contains FILTER') do |filter, options|
filter ||= ''
options[:list] << filter
end
add_option('-r', '--remove FILTER',
'Remove trusted certificates where the',
'subject contains FILTER') do |filter, options|
options[:remove] << filter
end end
add_option('-b', '--build EMAIL_ADDR', add_option('-b', '--build EMAIL_ADDR',
'Build private key and self-signed', 'Build private key and self-signed',
'certificate for EMAIL_ADDR.') do |value, options| 'certificate for EMAIL_ADDR') do |email_address, options|
vals = Gem::Security.build_self_signed_cert(value) options[:build] << email_address
FileUtils.chmod 0600, vals[:key_path]
say "Public Cert: #{vals[:cert_path]}"
say "Private Key: #{vals[:key_path]}"
say "Don't forget to move the key file to somewhere private..."
end end
add_option('-C', '--certificate CERT', add_option('-C', '--certificate CERT', OpenSSL::X509::Certificate,
'Certificate for --sign command.') do |value, options| 'Signing certificate for --sign') do |cert, options|
cert = OpenSSL::X509::Certificate.new(File.read(value))
options[:issuer_cert] = cert options[:issuer_cert] = cert
end end
add_option('-K', '--private-key KEY', add_option('-K', '--private-key KEY', OpenSSL::PKey::RSA,
'Private key for --sign command.') do |value, options| 'Key for --sign or --build') do |key, options|
key = OpenSSL::PKey::RSA.new(File.read(value)) options[:key] = key
options[:issuer_key] = key
end end
add_option('-s', '--sign NEWCERT', add_option('-s', '--sign CERT',
'Sign a certificate with my key and', 'Signs CERT with the key from -K',
'certificate.') do |value, options| 'and the certificate from -C') do |cert_file, options|
cert = OpenSSL::X509::Certificate.new(File.read(value)) raise OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless
my_cert = options[:issuer_cert] File.file? cert_file
my_key = options[:issuer_key]
cert = Gem::Security.sign_cert(cert, my_key, my_cert) options[:sign] << cert_file
File.open(value, 'wb') { |file| file.write(cert.to_pem) }
end end
end end
def execute def execute
options[:add].each do |certificate|
Gem::Security.trust_dir.trust_cert certificate
say "Added '#{certificate.subject}'"
end
options[:remove].each do |filter|
certificates_matching filter do |certificate, path|
FileUtils.rm path
say "Removed '#{certificate.subject}'"
end
end
options[:list].each do |filter|
certificates_matching filter do |certificate, _|
# this could probably be formatted more gracefully
say certificate.subject.to_s
end
end
options[:build].each do |name|
build name
end
unless options[:sign].empty? then
load_default_cert unless options[:issuer_cert]
load_default_key unless options[:key]
end
options[:sign].each do |cert_file|
sign cert_file
end
end
def build name
key = options[:key] || Gem::Security.create_key
cert = Gem::Security.create_cert_email name, key
key_path = Gem::Security.write key, "gem-private_key.pem"
cert_path = Gem::Security.write cert, "gem-public_cert.pem"
say "Certificate: #{cert_path}"
say "Private Key: #{key_path}"
say "Don't forget to move the key file to somewhere private!"
end
def certificates_matching filter
return enum_for __method__, filter unless block_given?
Gem::Security.trusted_certificates.select do |certificate, _|
subject = certificate.subject.to_s
subject.downcase.index filter
end.sort_by do |certificate, _|
certificate.subject.to_a.map { |name, data,| [name, data] }
end.each do |certificate, path|
yield certificate, path
end
end
def description # :nodoc:
<<-EOF
The cert command manages signing keys and certificates for creating signed
gems. Your signing certificate and private key are typically stored in
~/.gem/gem-public_cert.pem and ~/.gem/gem-private_key.pem respectively.
To build a certificate for signing gems:
gem cert --build you@example
If you already have an RSA key, or are creating a new certificate for an
existing key:
gem cert --build you@example --private-key /path/to/key.pem
If you wish to trust a certificate you can add it to the trust list with:
gem cert --add /path/to/cert.pem
You can list trusted certificates with:
gem cert --list
or:
gem cert --list cert_subject_substring
If you wish to remove a previously trusted certificate:
gem cert --remove cert_subject_substring
To sign another gem author's certificate:
gem cert --sign /path/to/other_cert.pem
For further reading on signing gems see `ri Gem::Security`.
EOF
end
def load_default_cert
cert_file = File.join Gem.user_home, 'gem-public_cert.pem'
cert = File.read cert_file
options[:issuer_cert] = OpenSSL::X509::Certificate.new cert
rescue Errno::ENOENT
alert_error \
"--certificate not specified and ~/.gem/gem-public_cert.pem does not exist"
terminate_interaction 1
rescue OpenSSL::X509::CertificateError
alert_error \
"--certificate not specified and ~/.gem/gem-public_cert.pem is not valid"
terminate_interaction 1
end
def load_default_key
key_file = File.join Gem.user_home, 'gem-private_key.pem'
key = File.read key_file
options[:key] = OpenSSL::PKey::RSA.new key
rescue Errno::ENOENT
alert_error \
"--private-key not specified and ~/.gem/gem-private_key.pem does not exist"
terminate_interaction 1
rescue OpenSSL::PKey::RSAError
alert_error \
"--private-key not specified and ~/.gem/gem-private_key.pem is not valid"
terminate_interaction 1
end
def sign cert_file
cert = File.read cert_file
cert = OpenSSL::X509::Certificate.new cert
permissions = File.stat(cert_file).mode & 0777
issuer_cert = options[:issuer_cert]
issuer_key = options[:key]
cert = Gem::Security.sign cert, issuer_key, issuer_cert
Gem::Security.write cert, cert_file, permissions
end end
end end

Просмотреть файл

@ -8,13 +8,7 @@ class Gem::Commands::CheckCommand < Gem::Command
def initialize def initialize
super 'check', 'Check installed gems', super 'check', 'Check installed gems',
:verify => false, :alien => false :alien => true
add_option( '--verify FILE',
'Verify gem file against its internal',
'checksum') do |value, options|
options[:verify] = value
end
add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the", add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the",
"gem repository") do |value, options| "gem repository") do |value, options|
@ -25,40 +19,21 @@ class Gem::Commands::CheckCommand < Gem::Command
end end
def execute def execute
if options[:alien] say "Checking gems..."
say "Performing the 'alien' operation" say
say gems = get_all_gem_names rescue []
gems = get_all_gem_names rescue []
Gem::Validator.new.alien(gems).sort.each do |key, val|
unless val.empty? then
say "#{key} has #{val.size} problems"
val.each do |error_entry|
say " #{error_entry.path}:"
say " #{error_entry.problem}"
end
else
say "#{key} is error-free" if Gem.configuration.verbose
end
say
end
end
if options[:verify] Gem::Validator.new.alien(gems).sort.each do |key, val|
gem_name = options[:verify] unless val.empty? then
unless gem_name say "#{key} has #{val.size} problems"
alert_error "Must specify a .gem file with --verify NAME" val.each do |error_entry|
return say " #{error_entry.path}:"
end say " #{error_entry.problem}"
unless File.exist?(gem_name) end
alert_error "Unknown file: #{gem_name}." else
return say "#{key} is error-free" if Gem.configuration.verbose
end
say "Verifying gem: '#{gem_name}'"
begin
Gem::Validator.new.verify_gem_file(gem_name)
rescue Exception
alert_error "#{gem_name} is invalid."
end end
say
end end
end end

Просмотреть файл

@ -26,6 +26,9 @@ class Gem::Commands::CleanupCommand < Gem::Command
<<-EOF <<-EOF
The cleanup command removes old gems from GEM_HOME. If an older version is The cleanup command removes old gems from GEM_HOME. If an older version is
installed elsewhere in GEM_PATH the cleanup command won't touch it. installed elsewhere in GEM_PATH the cleanup command won't touch it.
Older gems that are required to satisify the dependencies of gems
are not removed.
EOF EOF
end end
@ -56,6 +59,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it.
primary_gems[spec.name].version != spec.version primary_gems[spec.name].version != spec.version
} }
full = Gem::DependencyList.from_specs
deplist = Gem::DependencyList.new deplist = Gem::DependencyList.new
gems_to_cleanup.uniq.each do |spec| deplist.add spec end gems_to_cleanup.uniq.each do |spec| deplist.add spec end
@ -64,6 +69,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it.
original_path = Gem.path original_path = Gem.path
deps.each do |spec| deps.each do |spec|
next unless full.ok_to_remove?(spec.full_name)
if options[:dryrun] then if options[:dryrun] then
say "Dry Run Mode: Would uninstall #{spec.full_name}" say "Dry Run Mode: Would uninstall #{spec.full_name}"
else else

Просмотреть файл

@ -1,3 +1,4 @@
require 'English'
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/version_option' require 'rubygems/version_option'
@ -80,19 +81,36 @@ class Gem::Commands::ContentsCommand < Gem::Command
terminate_interaction 1 if gem_names.length == 1 terminate_interaction 1 if gem_names.length == 1
end end
gem_path = spec.full_gem_path if spec.default_gem?
extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only] files = spec.files.map do |file|
glob = "#{gem_path}#{extra}/**/*" case file
files = Dir[glob] when /\A#{spec.bindir}\//
[Gem::ConfigMap[:bindir], $POSTMATCH]
when /\.so\z/
[Gem::ConfigMap[:archdir], file]
else
[Gem::ConfigMap[:rubylibdir], file]
end
end
else
gem_path = spec.full_gem_path
extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only]
glob = "#{gem_path}#{extra}/**/*"
prefix_re = /#{Regexp.escape(gem_path)}\//
files = Dir[glob].map do |file|
[gem_path, file.sub(prefix_re, "")]
end
end
gem_path = File.join gem_path, '' # add trailing / if missing files.sort.each do |prefix, basename|
absolute_path = File.join(prefix, basename)
next if File.directory? absolute_path
files.sort.each do |file| if options[:prefix]
next if File.directory? file say absolute_path
else
file = file.sub gem_path, '' unless options[:prefix] say basename
end
say file
end end
end end
end end

Просмотреть файл

@ -71,14 +71,9 @@ class Gem::Commands::DependencyCommand < Gem::Command
if remote? and not options[:reverse_dependencies] then if remote? and not options[:reverse_dependencies] then
fetcher = Gem::SpecFetcher.fetcher fetcher = Gem::SpecFetcher.fetcher
# REFACTOR: fetcher.find_specs_matching => specs ss, _ = fetcher.spec_for_dependency dependency
specs_and_sources = fetcher.find_matching(dependency,
dependency.specific?, true,
dependency.prerelease?)
specs.concat specs_and_sources.map { |spec_tuple, source_uri| ss.each { |s,o| specs << s }
fetcher.fetch_spec spec_tuple, URI.parse(source_uri)
}
end end
if specs.empty? then if specs.empty? then

Просмотреть файл

@ -24,33 +24,38 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
The RubyGems environment can be controlled through command line arguments, The RubyGems environment can be controlled through command line arguments,
gemrc files, environment variables and built-in defaults. gemrc files, environment variables and built-in defaults.
Command line argument defaults and some RubyGems defaults can be set in Command line argument defaults and some RubyGems defaults can be set in a
~/.gemrc file for individual users and a /etc/gemrc for all users. A gemrc ~/.gemrc file for individual users and a /etc/gemrc for all users. These
is a YAML file with the following YAML keys: files are YAML files with the following YAML keys:
:sources: A YAML array of remote gem repositories to install gems from :sources: A YAML array of remote gem repositories to install gems from
:verbose: Verbosity of the gem command. false, true, and :really are the :verbose: Verbosity of the gem command. false, true, and :really are the
levels levels
:update_sources: Enable/disable automatic updating of repository metadata :update_sources: Enable/disable automatic updating of repository metadata
:backtrace: Print backtrace when RubyGems encounters an error :backtrace: Print backtrace when RubyGems encounters an error
:gempath: The paths in which to look for gems :gempath: The paths in which to look for gems
gem_command: A string containing arguments for the specified gem command :disable_default_gem_server: Force specification of gem server host on push
<gem_command>: A string containing arguments for the specified gem command
Example: Example:
:verbose: false :verbose: false
install: --no-wrappers install: --no-wrappers
update: --no-wrappers update: --no-wrappers
:disable_default_gem_server: true
RubyGems' default local repository can be overridden with the GEM_PATH and RubyGems' default local repository can be overridden with the GEM_PATH and
GEM_HOME environment variables. GEM_HOME sets the default repository to GEM_HOME environment variables. GEM_HOME sets the default repository to
install into. GEM_PATH allows multiple local repositories to be searched for install into. GEM_PATH allows multiple local repositories to be searched for
gems. gems.
If you are behind a proxy server, RubyGems uses the HTTP_PROXY, If you are behind a proxy server, RubyGems uses the HTTP_PROXY,
HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the
proxy server. proxy server.
If you would like to push gems to a private gem server the RUBYGEMS_HOST
environment variable can be set to the URI for that server.
If you are packaging RubyGems all of RubyGems' defaults are in If you are packaging RubyGems all of RubyGems' defaults are in
lib/rubygems/defaults.rb. You may override these in lib/rubygems/defaults.rb. You may override these in
lib/rubygems/defaults/operating_system.rb lib/rubygems/defaults/operating_system.rb
@ -74,7 +79,7 @@ lib/rubygems/defaults/operating_system.rb
when /^gempath/, /^path/, /^GEM_PATH/ then when /^gempath/, /^path/, /^GEM_PATH/ then
out << Gem.path.join(File::PATH_SEPARATOR) out << Gem.path.join(File::PATH_SEPARATOR)
when /^remotesources/ then when /^remotesources/ then
out << Gem.sources.join("\n") out << Gem.sources.to_a.join("\n")
when /^platform/ then when /^platform/ then
out << Gem.platforms.join(File::PATH_SEPARATOR) out << Gem.platforms.join(File::PATH_SEPARATOR)
when nil then when nil then

Просмотреть файл

@ -34,7 +34,6 @@ class Gem::Commands::FetchCommand < Gem::Command
def execute def execute
version = options[:version] || Gem::Requirement.default version = options[:version] || Gem::Requirement.default
all = Gem::Requirement.default != version
platform = Gem.platforms.last platform = Gem.platforms.last
gem_names = get_all_gem_names gem_names = get_all_gem_names
@ -43,32 +42,20 @@ class Gem::Commands::FetchCommand < Gem::Command
dep = Gem::Dependency.new gem_name, version dep = Gem::Dependency.new gem_name, version
dep.prerelease = options[:prerelease] dep.prerelease = options[:prerelease]
specs_and_sources, errors = specs_and_sources, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
Gem::SpecFetcher.fetcher.fetch_with_errors(dep, all, true,
dep.prerelease?)
if platform then if platform then
filtered = specs_and_sources.select { |s,| s.platform == platform } filtered = specs_and_sources.select { |s,| s.platform == platform }
specs_and_sources = filtered unless filtered.empty? specs_and_sources = filtered unless filtered.empty?
end end
spec, source_uri = specs_and_sources.sort_by { |s,| s.version }.last spec, source = specs_and_sources.sort_by { |s,| s.version }.first
if spec.nil? then if spec.nil? then
show_lookup_failure gem_name, version, errors, options[:domain] show_lookup_failure gem_name, version, errors, options[:domain]
next next
end end
file = "#{spec.full_name}.gem" source.download spec
remote_path = URI.parse(source_uri) + "gems/#{file}"
fetch = Gem::RemoteFetcher.fetcher
gem = fetch.fetch_path remote_path.to_s
File.open file, "wb" do |f|
f.write gem
end
say "Downloaded #{spec.full_name}" say "Downloaded #{spec.full_name}"
end end

Просмотреть файл

@ -11,29 +11,16 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command
def initialize def initialize
super 'generate_index', super 'generate_index',
'Generates the index files for a gem server directory', 'Generates the index files for a gem server directory',
:directory => '.', :build_legacy => true, :build_modern => true :directory => '.', :build_modern => true
add_option '-d', '--directory=DIRNAME', add_option '-d', '--directory=DIRNAME',
'repository base dir containing gems subdir' do |dir, options| 'repository base dir containing gems subdir' do |dir, options|
options[:directory] = File.expand_path dir options[:directory] = File.expand_path dir
end end
add_option '--[no-]legacy',
'Generate Marshal.4.8' do |value, options|
unless options[:build_modern] or value then
raise OptionParser::InvalidOption, 'no indicies will be built'
end
options[:build_legacy] = value
end
add_option '--[no-]modern', add_option '--[no-]modern',
'Generate indexes for RubyGems newer', 'Generate indexes for RubyGems',
'than 1.2.0' do |value, options| '(always true)' do |value, options|
unless options[:build_legacy] or value then
raise OptionParser::InvalidOption, 'no indicies will be built'
end
options[:build_modern] = value options[:build_modern] = value
end end
@ -42,27 +29,10 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command
'since the last update' do |value, options| 'since the last update' do |value, options|
options[:update] = value options[:update] = value
end end
add_option :RSS, '--rss-gems-host=GEM_HOST',
'Host name where gems are served from,',
'used for GUID and enclosure values' do |value, options|
options[:rss_gems_host] = value
end
add_option :RSS, '--rss-host=HOST',
'Host name for more gems information,',
'used for RSS feed link' do |value, options|
options[:rss_host] = value
end
add_option :RSS, '--rss-title=TITLE',
'Set title for RSS feed' do |value, options|
options[:rss_title] = value
end
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--directory . --legacy --modern" "--directory . --modern"
end end
def description # :nodoc: def description # :nodoc:
@ -85,25 +55,15 @@ When done, it will generate a set of files like this:
prerelease_specs.<version>.gz # prerelease specs index prerelease_specs.<version>.gz # prerelease specs index
quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file
# these files support legacy RubyGems The .rz extension files are compressed with the inflate algorithm.
Marshal.<version>
Marshal.<version>.Z # Marshal full index
The .Z and .rz extension files are compressed with the inflate algorithm.
The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and
Marshal::MINOR_VERSION constants. It is used to ensure compatibility. Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
If --rss-host and --rss-gem-host are given an RSS feed will be generated at
index.rss containing gems released in the last two days.
EOF EOF
end end
def execute def execute
if options[:update] and # This is always true becasue it's the only way now.
(options[:rss_host] or options[:rss_gems_host]) then options[:build_modern] = true
alert_error '--update not compatible with RSS generation'
terminate_interaction 1
end
if not File.exist?(options[:directory]) or if not File.exist?(options[:directory]) or
not File.directory?(options[:directory]) then not File.directory?(options[:directory]) then

Просмотреть файл

@ -37,7 +37,7 @@ Some examples of 'gem' usage.
* Create a gem: * Create a gem:
See http://rubygems.rubyforge.org/wiki/wiki.pl?CreateAGemInTenMinutes See http://guides.rubygems.org/make-your-own-gem/
* See information about RubyGems: * See information about RubyGems:

Просмотреть файл

@ -1,10 +1,11 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/doc_manager'
require 'rubygems/install_update_options' require 'rubygems/install_update_options'
require 'rubygems/dependency_installer' require 'rubygems/dependency_installer'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/validator' require 'rubygems/validator'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/install_message' # must come before rdoc for messaging
require 'rubygems/rdoc'
## ##
# Gem installer command line tool # Gem installer command line tool
@ -13,14 +14,14 @@ require 'rubygems/version_option'
class Gem::Commands::InstallCommand < Gem::Command class Gem::Commands::InstallCommand < Gem::Command
attr_reader :installed_specs # :nodoc:
include Gem::VersionOption include Gem::VersionOption
include Gem::LocalRemoteOptions include Gem::LocalRemoteOptions
include Gem::InstallUpdateOptions include Gem::InstallUpdateOptions
def initialize def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:generate_rdoc => true,
:generate_ri => true,
:format_executable => false, :format_executable => false,
:version => Gem::Requirement.default, :version => Gem::Requirement.default,
}) })
@ -32,6 +33,14 @@ class Gem::Commands::InstallCommand < Gem::Command
add_platform_option add_platform_option
add_version_option add_version_option
add_prerelease_option "to be installed. (Only for listed gems)" add_prerelease_option "to be installed. (Only for listed gems)"
add_option(:"Install/Update", '-g', '--file FILE',
'Read from a gem dependencies API file and',
'install the listed gems') do |v,o|
o[:gemdeps] = v
end
@installed_specs = nil
end end
def arguments # :nodoc: def arguments # :nodoc:
@ -39,7 +48,7 @@ class Gem::Commands::InstallCommand < Gem::Command
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--both --version '#{Gem::Requirement.default}' --rdoc --ri --no-force\n" \ "--both --version '#{Gem::Requirement.default}' --document --no-force\n" \
"--install-dir #{Gem.dir}" "--install-dir #{Gem.dir}"
end end
@ -100,31 +109,73 @@ to write the specification by hand. For example:
"#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags" "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
end end
def execute def install_from_gemdeps(gf)
if options[:include_dependencies] then require 'rubygems/request_set'
alert "`gem install -y` is now default and will be removed" rs = Gem::RequestSet.new
alert "use --ignore-dependencies to install only the gems you list" rs.load_gemdeps gf
rs.resolve
specs = rs.install options do |req, inst|
s = req.full_spec
if inst
say "Installing #{s.name} (#{s.version})"
else
say "Using #{s.name} (#{s.version})"
end
end end
installed_gems = [] @installed_specs = specs
raise Gem::SystemExitException, 0
end
def execute
if gf = options[:gemdeps] then
install_from_gemdeps gf
return
end
@installed_specs = []
ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9' ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9'
if options[:install_dir] and options[:user_install]
alert_error "Use --install-dir or --user-install but not both"
terminate_interaction 1
end
exit_code = 0 exit_code = 0
get_all_gem_names.each do |gem_name| if options[:version] != Gem::Requirement.default &&
get_all_gem_names.size > 1 then
alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
terminate_interaction 1
end
get_all_gem_names_and_versions.each do |gem_name, gem_version|
gem_version ||= options[:version]
begin begin
next if options[:conservative] and next if options[:conservative] and
not Gem::Dependency.new(gem_name, options[:version]).matching_specs.empty? not Gem::Dependency.new(gem_name, gem_version).matching_specs.empty?
inst = Gem::DependencyInstaller.new options inst = Gem::DependencyInstaller.new options
inst.install gem_name, options[:version] inst.install gem_name, Gem::Requirement.create(gem_version)
inst.installed_gems.each do |spec| @installed_specs.push(*inst.installed_gems)
say "Successfully installed #{spec.full_name}"
next unless errs = inst.errors
errs.each do |x|
next unless Gem::SourceFetchProblem === x
msg = "Unable to pull data from '#{x.source.uri}': #{x.error.message}"
alert_warning msg
end end
installed_gems.push(*inst.installed_gems)
rescue Gem::InstallError => e rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}" alert_error "Error installing #{gem_name}:\n\t#{e.message}"
exit_code |= 1 exit_code |= 1
@ -135,27 +186,9 @@ to write the specification by hand. For example:
end end
end end
unless installed_gems.empty? then unless @installed_specs.empty? then
gems = installed_gems.length == 1 ? 'gem' : 'gems' gems = @installed_specs.length == 1 ? 'gem' : 'gems'
say "#{installed_gems.length} #{gems} installed" say "#{@installed_specs.length} #{gems} installed"
# NOTE: *All* of the RI documents must be generated first. For some
# reason, RI docs cannot be generated after any RDoc documents are
# generated.
if options[:generate_ri] then
installed_gems.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
end
Gem::DocManager.update_ri_cache
end
if options[:generate_rdoc] then
installed_gems.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc
end
end
end end
raise Gem::SystemExitException, exit_code raise Gem::SystemExitException, exit_code

Просмотреть файл

@ -7,8 +7,9 @@ require 'rubygems/commands/query_command'
class Gem::Commands::ListCommand < Gem::Commands::QueryCommand class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
def initialize def initialize(name = 'list',
super 'list', 'Display gems whose name starts with STRING' summary = 'Display gems whose name starts with STRING')
super name, summary
remove_option('--name-matches') remove_option('--name-matches')
end end
@ -26,8 +27,9 @@ class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
end end
def execute def execute
string = get_one_optional_argument || '' name = get_one_optional_argument || ''
options[:name] = /^#{string}/i options[:name] = /^#{name}/i
super super
end end

Просмотреть файл

@ -30,7 +30,7 @@ generated.
Example: Example:
gemlock rails-1.0.0 > lockdown.rb gem lock rails-1.0.0 > lockdown.rb
will produce in lockdown.rb: will produce in lockdown.rb:

Просмотреть файл

@ -0,0 +1,17 @@
require 'rubygems/command'
class Gem::Commands::MirrorCommand < Gem::Command
def initialize
super('mirror', 'Mirror all gem files (requires rubygems-mirror)')
begin
Gem::Specification.find_by_name('rubygems-mirror').activate
rescue Gem::LoadError
# no-op
end
end
def execute
alert_error "Install the rubygems-mirror gem for the mirror command"
end
end

Просмотреть файл

@ -19,12 +19,15 @@ class Gem::Commands::OutdatedCommand < Gem::Command
Gem::Specification.outdated.sort.each do |name| Gem::Specification.outdated.sort.each do |name|
local = Gem::Specification.find_all_by_name(name).max local = Gem::Specification.find_all_by_name(name).max
dep = Gem::Dependency.new local.name, ">= #{local.version}" dep = Gem::Dependency.new local.name, ">= #{local.version}"
remotes = Gem::SpecFetcher.fetcher.fetch dep remotes, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
next if remotes.empty? next if remotes.empty?
remote = remotes.last.first remotes.sort! { |a,b| a[0].version <=> b[0].version }
say "#{local.name} (#{local.version} < #{remote.version})"
highest = remotes.last.first
say "#{local.name} (#{local.version} < #{highest.version})"
end end
end end
end end

Просмотреть файл

@ -14,6 +14,10 @@ class Gem::Commands::OwnerCommand < Gem::Command
"GEM gem to manage owners for" "GEM gem to manage owners for"
end end
def usage # :nodoc:
"#{program_name} GEM"
end
def initialize def initialize
super 'owner', description super 'owner', description
add_proxy_option add_proxy_option
@ -63,12 +67,16 @@ class Gem::Commands::OwnerCommand < Gem::Command
def manage_owners method, name, owners def manage_owners method, name, owners
owners.each do |owner| owners.each do |owner|
response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request| begin
request.set_form_data 'email' => owner response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
request.add_field "Authorization", api_key request.set_form_data 'email' => owner
end request.add_field "Authorization", api_key
end
with_response response with_response response
rescue
# ignore
end
end end
end end

Просмотреть файл

@ -1,5 +1,5 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/format' require 'rubygems/package'
require 'rubygems/installer' require 'rubygems/installer'
require 'rubygems/version_option' require 'rubygems/version_option'
@ -24,6 +24,11 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:extensions] = value options[:extensions] = value
end end
add_option('--only-executables',
'Only restore executables') do |value, options|
options[:only_executables] = value
end
add_version_option('restore to', 'pristine condition') add_version_option('restore to', 'pristine condition')
end end
@ -78,6 +83,11 @@ extensions.
say "Restoring gems to pristine condition..." say "Restoring gems to pristine condition..."
specs.each do |spec| specs.each do |spec|
if spec.default_gem?
say "Skipped #{spec.full_name}, it is a default gem"
next
end
unless spec.extensions.empty? or options[:extensions] then unless spec.extensions.empty? or options[:extensions] then
say "Skipped #{spec.full_name}, it needs to compile an extension" say "Skipped #{spec.full_name}, it needs to compile an extension"
next next
@ -101,8 +111,13 @@ extensions.
:wrappers => true, :wrappers => true,
:force => true, :force => true,
:install_dir => spec.base_dir, :install_dir => spec.base_dir,
:env_shebang => installer_env_shebang) :env_shebang => installer_env_shebang,
installer.install :build_args => spec.build_args)
if options[:only_executables] then
installer.generate_bin
else
installer.install
end
say "Restored #{spec.full_name}" say "Restored #{spec.full_name}"
end end

Просмотреть файл

@ -1,6 +1,7 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/gemcutter_utilities' require 'rubygems/gemcutter_utilities'
require 'rubygems/package'
class Gem::Commands::PushCommand < Gem::Command class Gem::Commands::PushCommand < Gem::Command
include Gem::LocalRemoteOptions include Gem::LocalRemoteOptions
@ -39,13 +40,23 @@ class Gem::Commands::PushCommand < Gem::Command
def send_gem name def send_gem name
args = [:post, "api/v1/gems"] args = [:post, "api/v1/gems"]
args << options[:host] if options[:host]
if Gem.latest_rubygems_version < Gem::Version.new(Gem::VERSION) then if Gem.latest_rubygems_version < Gem::Version.new(Gem::VERSION) then
alert_error "Using beta/unreleased version of rubygems. Not pushing." alert_error "Using beta/unreleased version of rubygems. Not pushing."
terminate_interaction 1 terminate_interaction 1
end end
host = options[:host]
unless host
if gem_data = Gem::Package.new(name) then
host = gem_data.spec.metadata['default_gem_server']
end
end
args << host if host
say "Pushing gem to #{host || Gem.host}..."
response = rubygems_api_request(*args) do |request| response = rubygems_api_request(*args) do |request|
request.body = Gem.read_binary name request.body = Gem.read_binary name
request.add_field "Content-Length", request.body.size request.add_field "Content-Length", request.body.size

Просмотреть файл

@ -21,6 +21,10 @@ class Gem::Commands::QueryCommand < Gem::Command
options[:installed] = value options[:installed] = value
end end
add_option('-I', 'Equivalent to --no-installed') do |value, options|
options[:installed] = false
end
add_version_option command, "for use with --installed" add_version_option command, "for use with --installed"
add_option('-n', '--name-matches REGEXP', add_option('-n', '--name-matches REGEXP',
@ -80,6 +84,7 @@ class Gem::Commands::QueryCommand < Gem::Command
req = Gem::Requirement.default req = Gem::Requirement.default
# TODO: deprecate for real # TODO: deprecate for real
dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req } dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req }
dep.prerelease = prerelease
if local? then if local? then
if prerelease and not both? then if prerelease and not both? then
@ -97,7 +102,7 @@ class Gem::Commands::QueryCommand < Gem::Command
} }
spec_tuples = specs.map do |spec| spec_tuples = specs.map do |spec|
[[spec.name, spec.version, spec.original_platform, spec], :local] [spec.name_tuple, spec]
end end
output_query_results spec_tuples output_query_results spec_tuples
@ -110,13 +115,27 @@ class Gem::Commands::QueryCommand < Gem::Command
say say
end end
all = options[:all]
fetcher = Gem::SpecFetcher.fetcher fetcher = Gem::SpecFetcher.fetcher
spec_tuples = fetcher.find_matching dep, all, false, prerelease
spec_tuples += fetcher.find_matching dep, false, false, true if type = if options[:all]
prerelease and all if options[:prerelease]
:complete
else
:released
end
elsif options[:prerelease]
:prerelease
else
:latest
end
if options[:name].source.empty?
spec_tuples = fetcher.detect(type) { true }
else
spec_tuples = fetcher.detect(type) do |gem_name, ver, plat|
options[:name] === gem_name
end
end
output_query_results spec_tuples output_query_results spec_tuples
end end
@ -135,32 +154,30 @@ class Gem::Commands::QueryCommand < Gem::Command
output = [] output = []
versions = Hash.new { |h,name| h[name] = [] } versions = Hash.new { |h,name| h[name] = [] }
spec_tuples.each do |spec_tuple, source_uri| spec_tuples.each do |spec_tuple, source|
versions[spec_tuple.first] << [spec_tuple, source_uri] versions[spec_tuple.name] << [spec_tuple, source]
end end
versions = versions.sort_by do |(name,_),_| versions = versions.sort_by do |(n,_),_|
name.downcase n.downcase
end end
versions.each do |gem_name, matching_tuples| versions.each do |gem_name, matching_tuples|
matching_tuples = matching_tuples.sort_by do |(_, version,_),_| matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse
version
end.reverse
platforms = Hash.new { |h,version| h[version] = [] } platforms = Hash.new { |h,version| h[version] = [] }
matching_tuples.map do |(_, version, platform,_),_| matching_tuples.map do |n,_|
platforms[version] << platform if platform platforms[n.version] << n.platform if n.platform
end end
seen = {} seen = {}
matching_tuples.delete_if do |(_, version,_),_| matching_tuples.delete_if do |n,_|
if seen[version] then if seen[n.version] then
true true
else else
seen[version] = true seen[n.version] = true
false false
end end
end end
@ -169,7 +186,7 @@ class Gem::Commands::QueryCommand < Gem::Command
if options[:versions] then if options[:versions] then
list = if platforms.empty? or options[:details] then list = if platforms.empty? or options[:details] then
matching_tuples.map { |(_, version,_),_| version }.uniq matching_tuples.map { |n,_| n.version }.uniq
else else
platforms.sort.reverse.map do |version, pls| platforms.sort.reverse.map do |version, pls|
if pls == [Gem::Platform::RUBY] then if pls == [Gem::Platform::RUBY] then
@ -188,12 +205,11 @@ class Gem::Commands::QueryCommand < Gem::Command
if options[:details] then if options[:details] then
detail_tuple = matching_tuples.first detail_tuple = matching_tuples.first
spec = if detail_tuple.first.length == 4 then spec = detail_tuple.last
detail_tuple.first.last
else unless spec.kind_of? Gem::Specification
uri = URI.parse detail_tuple.last spec = spec.fetch_spec detail_tuple.first
Gem::SpecFetcher.fetcher.fetch_spec detail_tuple.first, uri end
end
entry << "\n" entry << "\n"
@ -243,9 +259,9 @@ class Gem::Commands::QueryCommand < Gem::Command
entry << "\n" << " Installed at: #{loaded_from}" entry << "\n" << " Installed at: #{loaded_from}"
else else
label = 'Installed at' label = 'Installed at'
matching_tuples.each do |(_,version,_,s),| matching_tuples.each do |n,s|
loaded_from = File.dirname File.dirname(s.loaded_from) loaded_from = File.dirname File.dirname(s.loaded_from)
entry << "\n" << " #{label} (#{version}): #{loaded_from}" entry << "\n" << " #{label} (#{n.version}): #{loaded_from}"
label = ' ' * label.length label = ' ' * label.length
end end
end end

Просмотреть файл

@ -1,6 +1,6 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/doc_manager' require 'rubygems/rdoc'
class Gem::Commands::RdocCommand < Gem::Command class Gem::Commands::RdocCommand < Gem::Command
include Gem::VersionOption include Gem::VersionOption
@ -8,7 +8,7 @@ class Gem::Commands::RdocCommand < Gem::Command
def initialize def initialize
super 'rdoc', 'Generates RDoc for pre-installed gems', super 'rdoc', 'Generates RDoc for pre-installed gems',
:version => Gem::Requirement.default, :version => Gem::Requirement.default,
:include_rdoc => true, :include_ri => true, :overwrite => false :include_rdoc => false, :include_ri => true, :overwrite => false
add_option('--all', add_option('--all',
'Generate RDoc/RI documentation for all', 'Generate RDoc/RI documentation for all',
@ -39,7 +39,7 @@ class Gem::Commands::RdocCommand < Gem::Command
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--version '#{Gem::Requirement.default}' --rdoc --ri --no-overwrite" "--version '#{Gem::Requirement.default}' --ri --no-overwrite"
end end
def description # :nodoc: def description # :nodoc:
@ -54,37 +54,32 @@ The rdoc command builds RDoc and RI documentation for installed gems. Use
end end
def execute def execute
if options[:all] then specs = if options[:all] then
specs = Gem::SourceIndex.from_installed_gems.collect { |name, spec| Gem::Specification.to_a
spec else
} get_all_gem_names.map do |name|
else Gem::Specification.find_by_name name, options[:version]
gem_name = get_one_gem_name end.flatten.uniq
dep = Gem::Dependency.new gem_name, options[:version] end
specs = Gem::SourceIndex.from_installed_gems.search dep
if specs.empty? then
alert_error 'No matching gems found'
terminate_interaction 1
end end
if specs.empty? specs.each do |spec|
raise "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}" doc = Gem::RDoc.new spec, options[:include_rdoc], options[:include_ri]
end
if options[:include_ri] doc.force = options[:overwrite]
specs.sort.each do |spec|
doc = Gem::DocManager.new(spec)
doc.generate_ri if options[:overwrite] || !doc.ri_installed?
end
Gem::DocManager.update_ri_cache begin
end doc.generate
rescue Errno::ENOENT => e
if options[:include_rdoc] e.message =~ / - /
specs.sort.each do |spec| alert_error "Unable to document #{spec.full_name}, #{$'} is missing, skipping"
doc = Gem::DocManager.new(spec) terminate_interaction 1 if specs.length == 1
doc.generate_rdoc if options[:overwrite] || !doc.rdoc_installed?
end end
end end
true
end end
end end

Просмотреть файл

@ -1,30 +1,16 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/commands/query_command' require 'rubygems/commands/list_command'
class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand class Gem::Commands::SearchCommand < Gem::Commands::ListCommand
def initialize def initialize
super 'search', 'Display all gems whose name contains STRING' super 'search', 'Display all gems whose name contains STRING'
remove_option '--name-matches' @defaults[:domain] = :remote
end
def arguments # :nodoc:
"STRING fragment of gem name to search for"
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--local --no-details" "--remote --no-details"
end
def usage # :nodoc:
"#{program_name} [STRING]"
end
def execute
string = get_one_optional_argument
options[:name] = /#{string}/i
super
end end
end end

Просмотреть файл

@ -78,7 +78,7 @@ You can set up a shortcut to gem server documentation using the URL:
end end
def execute def execute
options[:gemdir] << Gem.dir if options[:gemdir].empty? options[:gemdir] = Gem.path if options[:gemdir].empty?
Gem::Server.run options Gem::Server.run options
end end

Просмотреть файл

@ -5,14 +5,22 @@ require 'rubygems/command'
# RubyGems checkout or tarball. # RubyGems checkout or tarball.
class Gem::Commands::SetupCommand < Gem::Command class Gem::Commands::SetupCommand < Gem::Command
HISTORY_HEADER = /^===\s*[\d.]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
VERSION_MATCHER = /^===\s*([\d.]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
def initialize def initialize
require 'tmpdir' require 'tmpdir'
super 'setup', 'Install RubyGems', super 'setup', 'Install RubyGems',
:format_executable => true, :rdoc => true, :ri => true, :format_executable => true, :document => %w[ri],
:site_or_vendor => :sitelibdir, :site_or_vendor => :sitelibdir,
:destdir => '', :prefix => '' :destdir => '', :prefix => '', :previous_version => ''
add_option '--previous-version=VERSION',
'Previous version of rubygems',
'Used for changelog processing' do |version, options|
options[:previous_version] = version
end
add_option '--prefix=PREFIX', add_option '--prefix=PREFIX',
'Prefix path for installing RubyGems', 'Prefix path for installing RubyGems',
@ -37,14 +45,37 @@ class Gem::Commands::SetupCommand < Gem::Command
options[:format_executable] = value options[:format_executable] = value
end end
add_option '--[no-]document [TYPES]', Array,
'Generate documentation for RubyGems.',
'List the documentation types you wish to',
'generate. For example: rdoc,ri' do |value, options|
options[:document] = case value
when nil then %w[rdoc ri]
when false then []
else value
end
end
add_option '--[no-]rdoc', add_option '--[no-]rdoc',
'Generate RDoc documentation for RubyGems' do |value, options| 'Generate RDoc documentation for RubyGems' do |value, options|
options[:rdoc] = value if value then
options[:document] << 'rdoc'
else
options[:document].delete 'rdoc'
end
options[:document].uniq!
end end
add_option '--[no-]ri', add_option '--[no-]ri',
'Generate RI documentation for RubyGems' do |value, options| 'Generate RI documentation for RubyGems' do |value, options|
options[:ri] = value if value then
options[:document] << 'ri'
else
options[:document].delete 'ri'
end
options[:document].uniq!
end end
end end
@ -58,7 +89,7 @@ class Gem::Commands::SetupCommand < Gem::Command
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--format-executable --rdoc --ri" "--format-executable --document ri"
end end
def description # :nodoc: def description # :nodoc:
@ -110,7 +141,7 @@ By default, this RubyGems will install gem as:
uninstall_old_gemcutter uninstall_old_gemcutter
install_rdoc documentation_success = install_rdoc
say say
if @verbose then if @verbose then
@ -118,14 +149,30 @@ By default, this RubyGems will install gem as:
say say
end end
if options[:previous_version].empty?
options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, '0')
end
options[:previous_version] = Gem::Version.new(options[:previous_version])
release_notes = File.join Dir.pwd, 'History.txt' release_notes = File.join Dir.pwd, 'History.txt'
release_notes = if File.exist? release_notes then release_notes = if File.exist? release_notes then
open release_notes do |io| history = File.read release_notes
text = io.gets '===' history = history.sub(/^# coding:.*?^=/m, '')
text << io.gets('===')
text[0...-3].sub(/^# coding:.*?^=/m, '') text = history.split(HISTORY_HEADER)
text.shift # correct an off-by-one generated by split
version_lines = history.scan(HISTORY_HEADER)
versions = history.scan(VERSION_MATCHER).flatten.map { |x| Gem::Version.new(x) }
history_string = ""
until versions.length == 0 or versions.shift < options[:previous_version]
history_string += version_lines.shift + text.shift
end end
history_string
else else
"Oh-no! Unable to find release notes!" "Oh-no! Unable to find release notes!"
end end
@ -145,6 +192,31 @@ By default, this RubyGems will install gem as:
say "to remove it by hand." say "to remove it by hand."
say say
end end
if documentation_success
if options[:document].include? 'rdoc' then
say "Rdoc documentation was installed. You may now invoke:"
say " gem server"
say "and then peruse beautifully formatted documentation for your gems"
say "with your web browser."
say "If you do not wish to install this documentation in the future, use the"
say "--no-document flag, or set it as the default in your ~/.gemrc file. See"
say "'gem help env' for details."
say
end
if options[:document].include? 'ri' then
say "Ruby Interactive (ri) documentation was installed. ri is kind of like man "
say "pages for ruby libraries. You may access it like this:"
say " ri Classname"
say " ri Classname.class_method"
say " ri Classname#instance_method"
say "If you do not wish to install this documentation in the future, use the"
say "--no-document flag, or set it as the default in your ~/.gemrc file. See"
say "'gem help env' for details."
say
end
end
end end
def install_executables(bin_dir) def install_executables(bin_dir)
@ -165,7 +237,7 @@ By default, this RubyGems will install gem as:
end end
dest_file = File.join bin_dir, bin_file_formatted dest_file = File.join bin_dir, bin_file_formatted
bin_tmp_file = File.join Dir.tmpdir, bin_file bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
begin begin
bin = File.readlines bin_file bin = File.readlines bin_file
@ -209,10 +281,7 @@ TEXT
say "Installing RubyGems" if @verbose say "Installing RubyGems" if @verbose
Dir.chdir 'lib' do Dir.chdir 'lib' do
lib_files = Dir[File.join('**', '*rb')] lib_files = Dir[File.join('**', '*rb')]
# Be sure to include our SSL ca bundles
lib_files += Dir[File.join('**', '*pem')]
lib_files.each do |lib_file| lib_files.each do |lib_file|
dest_file = File.join lib_dir, lib_file dest_file = File.join lib_dir, lib_file
@ -229,6 +298,12 @@ TEXT
rubygems_name = "rubygems-#{Gem::VERSION}" rubygems_name = "rubygems-#{Gem::VERSION}"
rubygems_doc_dir = File.join gem_doc_dir, rubygems_name rubygems_doc_dir = File.join gem_doc_dir, rubygems_name
begin
Gem.ensure_gem_subdirectories Gem.dir
rescue SystemCallError
# ignore
end
if File.writable? gem_doc_dir and if File.writable? gem_doc_dir and
(not File.exist? rubygems_doc_dir or (not File.exist? rubygems_doc_dir or
File.writable? rubygems_doc_dir) then File.writable? rubygems_doc_dir) then
@ -237,21 +312,26 @@ TEXT
rm_rf dir rm_rf dir
end end
if options[:ri] then require 'rubygems/rdoc'
ri_dir = File.join rubygems_doc_dir, 'ri'
say "Installing #{rubygems_name} ri into #{ri_dir}" if @verbose fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION
run_rdoc '--ri', '--op', ri_dir def fake_spec.full_gem_path
File.expand_path '../../../..', __FILE__
end end
if options[:rdoc] then generate_ri = options[:document].include? 'ri'
rdoc_dir = File.join rubygems_doc_dir, 'rdoc' generate_rdoc = options[:document].include? 'rdoc'
say "Installing #{rubygems_name} rdoc into #{rdoc_dir}" if @verbose
run_rdoc '--op', rdoc_dir rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri
end rdoc.generate
return true
elsif @verbose then elsif @verbose then
say "Skipping RDoc generation, #{gem_doc_dir} not writable" say "Skipping RDoc generation, #{gem_doc_dir} not writable"
say "Set the GEM_HOME environment variable if you want RDoc generated" say "Set the GEM_HOME environment variable if you want RDoc generated"
end end
return false
end end
def make_destination_dirs(install_destdir) def make_destination_dirs(install_destdir)
@ -331,23 +411,6 @@ abort "#{deprecation_message}"
end end
end end
def run_rdoc(*args)
begin
gem 'rdoc'
rescue Gem::LoadError
end
require 'rdoc/rdoc'
args << '--main' << 'README.rdoc' << '--quiet'
args << '.'
args << 'README.rdoc' << 'UPGRADING.rdoc'
args << 'LICENSE.txt' << 'MIT.txt' << 'History.txt'
r = RDoc::RDoc.new
r.document args
end
def uninstall_old_gemcutter def uninstall_old_gemcutter
require 'rubygems/uninstaller' require 'rubygems/uninstaller'

Просмотреть файл

@ -48,7 +48,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:update]) options[:update])
if options[:clear_all] then if options[:clear_all] then
path = Gem::SpecFetcher.fetcher.dir path = File.join Gem.user_home, '.gem', 'specs'
FileUtils.rm_rf path FileUtils.rm_rf path
unless File.exist? path then unless File.exist? path then
@ -64,16 +64,19 @@ class Gem::Commands::SourcesCommand < Gem::Command
end end
end end
if options[:add] then if source_uri = options[:add] then
source_uri = options[:add] source = Gem::Source.new source_uri
uri = URI.parse source_uri
begin begin
Gem::SpecFetcher.fetcher.load_specs uri, 'specs' if Gem.sources.include? source_uri then
Gem.sources << source_uri say "source #{source_uri} already present in the cache"
Gem.configuration.write else
source.load_specs :released
Gem.sources << source
Gem.configuration.write
say "#{source_uri} added to sources" say "#{source_uri} added to sources"
end
rescue URI::Error, ArgumentError rescue URI::Error, ArgumentError
say "#{source_uri} is not a URI" say "#{source_uri} is not a URI"
terminate_interaction 1 terminate_interaction 1
@ -97,12 +100,9 @@ class Gem::Commands::SourcesCommand < Gem::Command
end end
if options[:update] then if options[:update] then
fetcher = Gem::SpecFetcher.fetcher Gem.sources.each_source do |src|
src.load_specs :released
Gem.sources.each do |update_uri| src.load_specs :latest
update_uri = URI.parse update_uri
fetcher.load_specs update_uri, 'specs'
fetcher.load_specs update_uri, 'latest_specs'
end end
say "source cache successfully updated" say "source cache successfully updated"
@ -112,8 +112,8 @@ class Gem::Commands::SourcesCommand < Gem::Command
say "*** CURRENT SOURCES ***" say "*** CURRENT SOURCES ***"
say say
Gem.sources.each do |source| Gem.sources.each do |src|
say source say src
end end
end end
end end

Просмотреть файл

@ -1,7 +1,7 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/format' require 'rubygems/package'
class Gem::Commands::SpecificationCommand < Gem::Command class Gem::Commands::SpecificationCommand < Gem::Command
@ -17,6 +17,7 @@ class Gem::Commands::SpecificationCommand < Gem::Command
add_version_option('examine') add_version_option('examine')
add_platform_option add_platform_option
add_prerelease_option
add_option('--all', 'Output specifications for all versions of', add_option('--all', 'Output specifications for all versions of',
'the gem') do |value, options| 'the gem') do |value, options|
@ -62,13 +63,13 @@ FIELD name of gemspec field to show
"Please specify a gem name or file on the command line" "Please specify a gem name or file on the command line"
end end
case options[:version] case v = options[:version]
when String when String
req = Gem::Requirement.parse options[:version] req = Gem::Requirement.create v
when Gem::Requirement when Gem::Requirement
req = options[:version] req = v
else else
raise Gem::CommandLineError, "Unsupported version type: #{options[:version]}" raise Gem::CommandLineError, "Unsupported version type: '#{v}'"
end end
if !req.none? and options[:all] if !req.none? and options[:all]
@ -79,7 +80,7 @@ FIELD name of gemspec field to show
if options[:all] if options[:all]
dep = Gem::Dependency.new gem dep = Gem::Dependency.new gem
else else
dep = Gem::Dependency.new gem, options[:version] dep = Gem::Dependency.new gem, req
end end
field = get_one_optional_argument field = get_one_optional_argument
@ -89,7 +90,7 @@ FIELD name of gemspec field to show
if local? then if local? then
if File.exist? gem then if File.exist? gem then
specs << Gem::Format.from_file_by_path(gem).spec rescue nil specs << Gem::Package.new(gem).spec rescue nil
end end
if specs.empty? then if specs.empty? then
@ -98,17 +99,14 @@ FIELD name of gemspec field to show
end end
if remote? then if remote? then
found = Gem::SpecFetcher.fetcher.fetch dep, true dep.prerelease = options[:prerelease]
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
if dep.prerelease? or options[:prerelease]
found += Gem::SpecFetcher.fetcher.fetch dep, false, true, true
end
specs.push(*found.map { |spec,| spec }) specs.push(*found.map { |spec,| spec })
end end
if specs.empty? then if specs.empty? then
alert_error "Unknown gem '#{gem}'" alert_error "No gem matching '#{dep}' found"
terminate_interaction 1 terminate_interaction 1
end end

Просмотреть файл

@ -13,7 +13,8 @@ class Gem::Commands::UninstallCommand < Gem::Command
def initialize def initialize
super 'uninstall', 'Uninstall gems from the local repository', super 'uninstall', 'Uninstall gems from the local repository',
:version => Gem::Requirement.default, :user_install => true :version => Gem::Requirement.default, :user_install => true,
:check_dev => false
add_option('-a', '--[no-]all', add_option('-a', '--[no-]all',
'Uninstall all matching versions' 'Uninstall all matching versions'
@ -27,6 +28,12 @@ class Gem::Commands::UninstallCommand < Gem::Command
options[:ignore] = value options[:ignore] = value
end end
add_option('-D', '--[no-]-check-development',
'Check development dependencies while uninstalling',
'(default: false)') do |value, options|
options[:check_dev] = value
end
add_option('-x', '--[no-]executables', add_option('-x', '--[no-]executables',
'Uninstall applicable executables without', 'Uninstall applicable executables without',
'confirmation') do |value, options| 'confirmation') do |value, options|
@ -54,6 +61,12 @@ class Gem::Commands::UninstallCommand < Gem::Command
options[:format_executable] = value options[:format_executable] = value
end end
add_option('--[no-]force',
'Uninstall all versions of the named gems',
'ignoring dependencies') do |value, options|
options[:force] = value
end
add_version_option add_version_option
add_platform_option add_platform_option
end end
@ -73,19 +86,23 @@ class Gem::Commands::UninstallCommand < Gem::Command
end end
def execute def execute
original_path = Gem.path # REFACTOR: stolen from cleanup_command
deplist = Gem::DependencyList.new
get_all_gem_names.uniq.each do |name|
Gem::Specification.find_all_by_name(name).each do |spec|
deplist.add spec
end
end
get_all_gem_names.each do |gem_name| deps = deplist.strongly_connected_components.flatten.reverse
deps.map(&:name).uniq.each do |gem_name|
begin begin
Gem::Uninstaller.new(gem_name, options).uninstall Gem::Uninstaller.new(gem_name, options).uninstall
rescue Gem::InstallError => e
alert e.message
rescue Gem::GemNotInHomeException => e rescue Gem::GemNotInHomeException => e
spec = e.spec spec = e.spec
alert("In order to remove #{spec.name}, please execute:\n" \ alert("In order to remove #{spec.name}, please execute:\n" \
"\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}") "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
ensure
Gem.use_paths(*original_path)
end end
end end
end end

Просмотреть файл

@ -69,8 +69,10 @@ class Gem::Commands::UnpackCommand < Gem::Command
else else
basename = File.basename path, '.gem' basename = File.basename path, '.gem'
target_dir = File.expand_path basename, options[:target] target_dir = File.expand_path basename, options[:target]
FileUtils.mkdir_p target_dir
Gem::Installer.new(path, :unpack => true).unpack target_dir package = Gem::Package.new path
package.extract_files target_dir
say "Unpacked gem: '#{target_dir}'" say "Unpacked gem: '#{target_dir}'"
end end
end end
@ -134,9 +136,11 @@ class Gem::Commands::UnpackCommand < Gem::Command
## ##
# Extracts the Gem::Specification and raw metadata from the .gem file at # Extracts the Gem::Specification and raw metadata from the .gem file at
# +path+. # +path+.
#--
# TODO move to Gem::Package as #raw_spec or something
def get_metadata path def get_metadata path
format = Gem::Format.from_file_by_path path format = Gem::Package.new path
spec = format.spec spec = format.spec
metadata = nil metadata = nil

Просмотреть файл

@ -1,10 +1,12 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/command_manager' require 'rubygems/command_manager'
require 'rubygems/dependency_installer'
require 'rubygems/install_update_options' require 'rubygems/install_update_options'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/spec_fetcher' require 'rubygems/spec_fetcher'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/commands/install_command' require 'rubygems/install_message' # must come before rdoc for messaging
require 'rubygems/rdoc'
class Gem::Commands::UpdateCommand < Gem::Command class Gem::Commands::UpdateCommand < Gem::Command
@ -13,11 +15,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
include Gem::VersionOption include Gem::VersionOption
def initialize def initialize
super 'update', super 'update', 'Update installed gems to the latest version',
'Update the named gems (or all installed gems) in the local repository', :document => %w[rdoc ri],
:generate_rdoc => true, :force => false
:generate_ri => true,
:force => false
add_install_update_options add_install_update_options
@ -37,6 +37,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
add_local_remote_options add_local_remote_options
add_platform_option add_platform_option
add_prerelease_option "as update targets" add_prerelease_option "as update targets"
@updated = []
@installer = Gem::DependencyInstaller.new options
end end
def arguments # :nodoc: def arguments # :nodoc:
@ -44,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--rdoc --ri --no-force --install-dir #{Gem.dir}" "--document --no-force --install-dir #{Gem.dir}"
end end
def usage # :nodoc: def usage # :nodoc:
@ -52,9 +55,6 @@ class Gem::Commands::UpdateCommand < Gem::Command
end end
def execute def execute
@installer = Gem::DependencyInstaller.new options
@updated = []
hig = {} hig = {}
if options[:system] then if options[:system] then
@ -79,21 +79,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
if updated.empty? then if updated.empty? then
say "Nothing to update" say "Nothing to update"
else else
say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}" say "Gems updated: #{updated.map { |spec| spec.name }.join ' '}"
if options[:generate_ri] then
updated.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
end
Gem::DocManager.update_ri_cache
end
if options[:generate_rdoc] then
updated.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc
end
end
end end
end end
@ -112,7 +98,6 @@ class Gem::Commands::UpdateCommand < Gem::Command
@installer.installed_gems.each do |spec| @installer.installed_gems.each do |spec|
@updated << spec @updated << spec
say "Successfully installed #{spec.full_name}" if success
end end
end end
@ -178,8 +163,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
args = [] args = []
args << '--prefix' << Gem.prefix if Gem.prefix args << '--prefix' << Gem.prefix if Gem.prefix
args << '--no-rdoc' unless options[:generate_rdoc] # TODO use --document for >= 1.9 , --no-rdoc --no-ri < 1.9
args << '--no-ri' unless options[:generate_ri] args << '--no-rdoc' unless options[:document].include? 'rdoc'
args << '--no-ri' unless options[:document].include? 'ri'
args << '--no-format-executable' if options[:no_format_executable] args << '--no-format-executable' if options[:no_format_executable]
update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}" update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}"
@ -205,20 +191,20 @@ class Gem::Commands::UpdateCommand < Gem::Command
gem_names.all? { |name| /#{name}/ !~ l_spec.name } gem_names.all? { |name| /#{name}/ !~ l_spec.name }
dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}" dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}"
dependency.prerelease = options[:prerelease]
fetcher = Gem::SpecFetcher.fetcher fetcher = Gem::SpecFetcher.fetcher
spec_tuples = fetcher.find_matching dependency
matching_gems = spec_tuples.select do |(name, _, platform),| spec_tuples, _ = fetcher.search_for_dependency dependency
name == l_name and Gem::Platform.match platform
matching_gems = spec_tuples.select do |g,_|
g.name == l_name and g.match_platform?
end end
highest_remote_gem = matching_gems.sort_by do |(_, version),| highest_remote_gem = matching_gems.sort_by { |g,_| g.version }.last
version
end.last
highest_remote_gem ||= [[nil, Gem::Version.new(0), nil]] # "null" object highest_remote_gem ||= [Gem::NameTuple.null]
highest_remote_ver = highest_remote_gem.first[1] highest_remote_ver = highest_remote_gem.first.version
if system or (l_spec.version < highest_remote_ver) then if system or (l_spec.version < highest_remote_ver) then
result << [l_spec.name, [l_spec.version, highest_remote_ver].max] result << [l_spec.name, [l_spec.version, highest_remote_ver].max]

Просмотреть файл

@ -0,0 +1,98 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
require 'rubygems/version_option'
require 'rubygems/gemcutter_utilities'
class Gem::Commands::YankCommand < Gem::Command
include Gem::LocalRemoteOptions
include Gem::VersionOption
include Gem::GemcutterUtilities
def description # :nodoc:
'Remove a specific gem version release from RubyGems.org'
end
def arguments # :nodoc:
"GEM name of gem"
end
def usage # :nodoc:
"#{program_name} GEM -v VERSION [-p PLATFORM] [--undo] [--key KEY_NAME]"
end
def initialize
super 'yank', description
add_version_option("remove")
add_platform_option("remove")
add_option('--undo') do |value, options|
options[:undo] = true
end
add_option('-k', '--key KEY_NAME',
'Use API key from your gem credentials file') do |value, options|
options[:key] = value
end
end
def execute
sign_in
version = get_version_from_requirements(options[:version])
platform = get_platform_from_requirements(options)
api_key = Gem.configuration.rubygems_api_key
api_key = Gem.configuration.api_keys[options[:key].to_sym] if options[:key]
if version then
if options[:undo] then
unyank_gem(version, platform, api_key)
else
yank_gem(version, platform, api_key)
end
else
say "A version argument is required: #{usage}"
terminate_interaction
end
end
def yank_gem(version, platform, api_key)
say "Yanking gem from #{self.host}..."
yank_api_request(:delete, version, platform, "api/v1/gems/yank", api_key)
end
def unyank_gem(version, platform, api_key)
say "Unyanking gem from #{host}..."
yank_api_request(:put, version, platform, "api/v1/gems/unyank", api_key)
end
private
def yank_api_request(method, version, platform, api, api_key)
name = get_one_gem_name
response = rubygems_api_request(method, api) do |request|
request.add_field("Authorization", api_key)
data = {
'gem_name' => name,
'version' => version,
}
data['platform'] = platform if platform
request.set_form_data data
end
say response.body
end
def get_version_from_requirements(requirements)
requirements.requirements.first[1].version
rescue
nil
end
def get_platform_from_requirements(requirements)
Gem.platforms[1].to_s if requirements.key? :added_platform
end
end

Просмотреть файл

@ -0,0 +1,51 @@
# This file contains all sorts of little compatibility hacks that we've
# had to introduce over the years. Quarantining them into one file helps
# us know when we can get rid of them.
# Ruby 1.9.x has introduced some things that are awkward, and we need to
# support them, so we define some constants to use later.
module Gem
# Only MRI 1.9.2 has the custom prelude.
GEM_PRELUDE_SUCKAGE = RUBY_VERSION =~ /^1\.9\.2/ and RUBY_ENGINE == "ruby"
end
# Gem::QuickLoader exists in the gem prelude code in ruby 1.9.2 itself.
# We gotta get rid of it if it's there, before we do anything else.
if Gem::GEM_PRELUDE_SUCKAGE and defined?(Gem::QuickLoader) then
Gem::QuickLoader.remove
$LOADED_FEATURES.delete Gem::QuickLoader.path_to_full_rubygems_library
if $LOADED_FEATURES.any? do |path| path.end_with? '/rubygems.rb' end then
# TODO path does not exist here
raise LoadError, "another rubygems is already loaded from #{path}"
end
class << Gem
remove_method :try_activate if Gem.respond_to?(:try_activate, true)
end
end
module Gem
RubyGemsVersion = VERSION
RbConfigPriorities = %w[
EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name
ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir
rubylibdir
]
unless defined?(ConfigMap)
##
# Configuration settings from ::RbConfig
ConfigMap = Hash.new do |cm, key|
cm[key] = RbConfig::CONFIG[key.to_s]
end
else
RbConfigPriorities.each do |key|
ConfigMap[key.to_sym] = RbConfig::CONFIG[key]
end
end
RubyGemsPackageVersion = VERSION
end

Просмотреть файл

@ -5,9 +5,9 @@
#++ #++
## ##
# Gem::ConfigFile RubyGems options and gem command options from ~/.gemrc. # Gem::ConfigFile RubyGems options and gem command options from gemrc.
# #
# ~/.gemrc is a YAML file that uses strings to match gem command arguments and # gemrc is a YAML file that uses strings to match gem command arguments and
# symbols to match RubyGems options. # symbols to match RubyGems options.
# #
# Gem command arguments use a String key that matches the command name and # Gem command arguments use a String key that matches the command name and
@ -21,16 +21,19 @@
# RubyGems options use symbol keys. Valid options are: # RubyGems options use symbol keys. Valid options are:
# #
# +:backtrace+:: See #backtrace # +:backtrace+:: See #backtrace
# +:benchmark+:: See #benchmark
# +:sources+:: Sets Gem::sources # +:sources+:: Sets Gem::sources
# +:verbose+:: See #verbose # +:verbose+:: See #verbose
#
require 'rbconfig' # gemrc files may exist in various locations and are read and merged in
# the following order:
#
# - system wide (/etc/gemrc)
# - per user (~/.gemrc)
# - per environment (gemrc files listed in the GEMRC environment variable)
class Gem::ConfigFile class Gem::ConfigFile
DEFAULT_BACKTRACE = false DEFAULT_BACKTRACE = false
DEFAULT_BENCHMARK = false
DEFAULT_BULK_THRESHOLD = 1000 DEFAULT_BULK_THRESHOLD = 1000
DEFAULT_VERBOSITY = true DEFAULT_VERBOSITY = true
DEFAULT_UPDATE_SOURCES = true DEFAULT_UPDATE_SOURCES = true
@ -96,11 +99,6 @@ class Gem::ConfigFile
attr_writer :backtrace attr_writer :backtrace
##
# True if we are benchmarking this run.
attr_accessor :benchmark
## ##
# Bulk threshold value. If the number of missing gems are above this # Bulk threshold value. If the number of missing gems are above this
# threshold value, then a bulk download technique is used. (deprecated) # threshold value, then a bulk download technique is used. (deprecated)
@ -131,6 +129,10 @@ class Gem::ConfigFile
attr_reader :api_keys attr_reader :api_keys
## ##
# True if we want to force specification of gem server when pushing a gem
attr_accessor :disable_default_gem_server
# openssl verify mode value, used for remote https connection # openssl verify mode value, used for remote https connection
attr_reader :ssl_verify_mode attr_reader :ssl_verify_mode
@ -158,29 +160,29 @@ class Gem::ConfigFile
# <tt>--debug</tt>:: # <tt>--debug</tt>::
# Enable Ruby level debug messages. Handled early for the same reason as # Enable Ruby level debug messages. Handled early for the same reason as
# --backtrace. # --backtrace.
#--
# TODO: parse options upstream, pass in options directly
def initialize(arg_list) def initialize(args)
@config_file_name = nil @config_file_name = nil
need_config_file_name = false need_config_file_name = false
arg_list = arg_list.map do |arg| arg_list = []
args.each do |arg|
if need_config_file_name then if need_config_file_name then
@config_file_name = arg @config_file_name = arg
need_config_file_name = false need_config_file_name = false
nil
elsif arg =~ /^--config-file=(.*)/ then elsif arg =~ /^--config-file=(.*)/ then
@config_file_name = $1 @config_file_name = $1
nil
elsif arg =~ /^--config-file$/ then elsif arg =~ /^--config-file$/ then
need_config_file_name = true need_config_file_name = true
nil
else else
arg arg_list << arg
end end
end.compact end
@backtrace = DEFAULT_BACKTRACE @backtrace = DEFAULT_BACKTRACE
@benchmark = DEFAULT_BENCHMARK
@bulk_threshold = DEFAULT_BULK_THRESHOLD @bulk_threshold = DEFAULT_BULK_THRESHOLD
@verbose = DEFAULT_VERBOSITY @verbose = DEFAULT_VERBOSITY
@update_sources = DEFAULT_UPDATE_SOURCES @update_sources = DEFAULT_UPDATE_SOURCES
@ -189,19 +191,25 @@ class Gem::ConfigFile
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS) platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
system_config = load_file SYSTEM_WIDE_CONFIG_FILE system_config = load_file SYSTEM_WIDE_CONFIG_FILE
user_config = load_file config_file_name.dup.untaint user_config = load_file config_file_name.dup.untaint
environment_config = (ENV['GEMRC'] || '').split(/[:;]/).inject({}) do |result, file|
result.merge load_file file
end
@hash = operating_system_config.merge platform_config @hash = operating_system_config.merge platform_config
@hash = @hash.merge system_config @hash = @hash.merge system_config
@hash = @hash.merge user_config @hash = @hash.merge user_config
@hash = @hash.merge environment_config
# HACK these override command-line args, which is bad # HACK these override command-line args, which is bad
@backtrace = @hash[:backtrace] if @hash.key? :backtrace @backtrace = @hash[:backtrace] if @hash.key? :backtrace
@benchmark = @hash[:benchmark] if @hash.key? :benchmark @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
@bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold @home = @hash[:gemhome] if @hash.key? :gemhome
@home = @hash[:gemhome] if @hash.key? :gemhome @path = @hash[:gempath] if @hash.key? :gempath
@path = @hash[:gempath] if @hash.key? :gempath @update_sources = @hash[:update_sources] if @hash.key? :update_sources
@update_sources = @hash[:update_sources] if @hash.key? :update_sources @verbose = @hash[:verbose] if @hash.key? :verbose
@verbose = @hash[:verbose] if @hash.key? :verbose @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
@ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
@ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
@ -224,6 +232,7 @@ class Gem::ConfigFile
else else
@hash @hash
end end
if @api_keys.key? :rubygems_api_key then if @api_keys.key? :rubygems_api_key then
@rubygems_api_key = @api_keys[:rubygems_api_key] @rubygems_api_key = @api_keys[:rubygems_api_key]
@api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless @api_keys.key? :rubygems @api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless @api_keys.key? :rubygems
@ -238,7 +247,8 @@ class Gem::ConfigFile
Gem.load_yaml Gem.load_yaml
File.open(credentials_path, 'w') do |f| permissions = 0600 & (~File.umask)
File.open(credentials_path, 'w', permissions) do |f|
f.write config.to_yaml f.write config.to_yaml
end end
@ -249,13 +259,21 @@ class Gem::ConfigFile
Gem.load_yaml Gem.load_yaml
return {} unless filename and File.exist? filename return {} unless filename and File.exist? filename
begin begin
YAML.load(File.read(filename)) content = YAML.load(File.read(filename))
unless content.kind_of? Hash
warn "Failed to load #{config_file_name} because it doesn't contain valid YAML hash"
return {}
end
return content
rescue ArgumentError rescue ArgumentError
warn "Failed to load #{config_file_name}" warn "Failed to load #{config_file_name}"
rescue Errno::EACCES rescue Errno::EACCES
warn "Failed to load #{config_file_name} due to permissions problem." warn "Failed to load #{config_file_name} due to permissions problem."
end or {} end
{}
end end
# True if the backtrace option has been specified, or debug is on. # True if the backtrace option has been specified, or debug is on.
@ -273,13 +291,11 @@ class Gem::ConfigFile
hash = @hash.dup hash = @hash.dup
hash.delete :update_sources hash.delete :update_sources
hash.delete :verbose hash.delete :verbose
hash.delete :benchmark
hash.delete :backtrace hash.delete :backtrace
hash.delete :bulk_threshold hash.delete :bulk_threshold
yield :update_sources, @update_sources yield :update_sources, @update_sources
yield :verbose, @verbose yield :verbose, @verbose
yield :benchmark, @benchmark
yield :backtrace, @backtrace yield :backtrace, @backtrace
yield :bulk_threshold, @bulk_threshold yield :bulk_threshold, @bulk_threshold
@ -296,8 +312,6 @@ class Gem::ConfigFile
case arg case arg
when /^--(backtrace|traceback)$/ then when /^--(backtrace|traceback)$/ then
@backtrace = true @backtrace = true
when /^--bench(mark)?$/ then
@benchmark = true
when /^--debug$/ then when /^--debug$/ then
$DEBUG = true $DEBUG = true
else else
@ -309,25 +323,41 @@ class Gem::ConfigFile
# Really verbose mode gives you extra output. # Really verbose mode gives you extra output.
def really_verbose def really_verbose
case verbose case verbose
when true, false, nil then false when true, false, nil then
else true false
else
true
end end
end end
# to_yaml only overwrites things you can't override on the command line. # to_yaml only overwrites things you can't override on the command line.
def to_yaml # :nodoc: def to_yaml # :nodoc:
yaml_hash = {} yaml_hash = {}
yaml_hash[:backtrace] = @hash.key?(:backtrace) ? @hash[:backtrace] : yaml_hash[:backtrace] = if @hash.key?(:backtrace)
DEFAULT_BACKTRACE @hash[:backtrace]
yaml_hash[:benchmark] = @hash.key?(:benchmark) ? @hash[:benchmark] : else
DEFAULT_BENCHMARK DEFAULT_BACKTRACE
yaml_hash[:bulk_threshold] = @hash.key?(:bulk_threshold) ? end
@hash[:bulk_threshold] : DEFAULT_BULK_THRESHOLD
yaml_hash[:sources] = Gem.sources yaml_hash[:bulk_threshold] = if @hash.key?(:bulk_threshold)
yaml_hash[:update_sources] = @hash.key?(:update_sources) ? @hash[:bulk_threshold]
@hash[:update_sources] : DEFAULT_UPDATE_SOURCES else
yaml_hash[:verbose] = @hash.key?(:verbose) ? @hash[:verbose] : DEFAULT_BULK_THRESHOLD
DEFAULT_VERBOSITY end
yaml_hash[:sources] = Gem.sources.to_a
yaml_hash[:update_sources] = if @hash.key?(:update_sources)
@hash[:update_sources]
else
DEFAULT_UPDATE_SOURCES
end
yaml_hash[:verbose] = if @hash.key?(:verbose)
@hash[:verbose]
else
DEFAULT_VERBOSITY
end
keys = yaml_hash.keys.map { |key| key.to_s } keys = yaml_hash.keys.map { |key| key.to_s }
keys << 'debug' keys << 'debug'
@ -361,15 +391,13 @@ class Gem::ConfigFile
def ==(other) # :nodoc: def ==(other) # :nodoc:
self.class === other and self.class === other and
@backtrace == other.backtrace and @backtrace == other.backtrace and
@benchmark == other.benchmark and @bulk_threshold == other.bulk_threshold and
@bulk_threshold == other.bulk_threshold and @verbose == other.verbose and
@verbose == other.verbose and @update_sources == other.update_sources and
@update_sources == other.update_sources and @hash == other.hash
@hash == other.hash
end end
protected
attr_reader :hash attr_reader :hash
protected :hash
end end

Просмотреть файл

@ -0,0 +1,53 @@
module Kernel
# REFACTOR: This should be pulled out into some kind of hacks file.
remove_method :gem if 'method' == defined? gem # from gem_prelude.rb on 1.9
##
# Use Kernel#gem to activate a specific version of +gem_name+.
#
# +requirements+ is a list of version requirements that the
# specified gem must match, most commonly "= example.version.number". See
# Gem::Requirement for how to specify a version requirement.
#
# If you will be activating the latest version of a gem, there is no need to
# call Kernel#gem, Kernel#require will do the right thing for you.
#
# Kernel#gem returns true if the gem was activated, otherwise false. If the
# gem could not be found, didn't match the version requirements, or a
# different version was already activated, an exception will be raised.
#
# Kernel#gem should be called *before* any require statements (otherwise
# RubyGems may load a conflicting library version).
#
# In older RubyGems versions, the environment variable GEM_SKIP could be
# used to skip activation of specified gems, for example to test out changes
# that haven't been installed yet. Now RubyGems defers to -I and the
# RUBYLIB environment variable to skip activation of a gem.
#
# Example:
#
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
def gem(gem_name, *requirements) # :doc:
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
if gem_name.kind_of? Gem::Dependency
unless Gem::Deprecate.skip
warn "#{Gem.location_of_caller.join ':'}:Warning: Kernel.gem no longer "\
"accepts a Gem::Dependency object, please pass the name "\
"and requirements directly"
end
requirements = gem_name.requirement
gem_name = gem_name.name
end
spec = Gem::Dependency.new(gem_name, *requirements).to_spec
spec.activate if spec
end
private :gem
end

Просмотреть файл

@ -0,0 +1,119 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
module Kernel
if defined?(gem_original_require) then
# Ruby ships with a custom_require, override its require
remove_method :require
else
##
# The Kernel#require from before RubyGems was loaded.
alias gem_original_require require
private :gem_original_require
end
##
# When RubyGems is required, Kernel#require is replaced with our own which
# is capable of loading gems on demand.
#
# When you call <tt>require 'x'</tt>, this is what happens:
# * If the file can be loaded from the existing Ruby loadpath, it
# is.
# * Otherwise, installed gems are searched for a file that matches.
# If it's found in gem 'y', that gem is activated (added to the
# loadpath).
#
# The normal <tt>require</tt> functionality of returning false if
# that file has already been loaded is preserved.
def require path
spec = Gem.find_unresolved_default_spec(path)
if spec
Gem.remove_unresolved_default_spec(spec)
gem(spec.name)
end
# If there are no unresolved deps, then we can use just try
# normal require handle loading a gem from the rescue below.
if Gem::Specification.unresolved_deps.empty? then
return gem_original_require(path)
end
# If +path+ is for a gem that has already been loaded, don't
# bother trying to find it in an unresolved gem, just go straight
# to normal require.
#--
# TODO request access to the C implementation of this to speed up RubyGems
spec = Gem::Specification.find { |s|
s.activated? and s.contains_requirable_file? path
}
return gem_original_require(path) if spec
# Attempt to find +path+ in any unresolved gems...
found_specs = Gem::Specification.find_in_unresolved path
# If there are no directly unresolved gems, then try and find +path+
# in any gems that are available via the currently unresolved gems.
# For example, given:
#
# a => b => c => d
#
# If a and b are currently active with c being unresolved and d.rb is
# requested, then find_in_unresolved_tree will find d.rb in d because
# it's a dependency of c.
#
if found_specs.empty? then
found_specs = Gem::Specification.find_in_unresolved_tree path
found_specs.each do |found_spec|
found_spec.activate
end
# We found +path+ directly in an unresolved gem. Now we figure out, of
# the possible found specs, which one we should activate.
else
# Check that all the found specs are just different
# versions of the same gem
names = found_specs.map(&:name).uniq
if names.size > 1 then
raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
end
# Ok, now find a gem that has no conflicts, starting
# at the highest version.
valid = found_specs.select { |s| s.conflicts.empty? }.last
unless valid then
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
le.name = names.first
raise le
end
valid.activate
end
gem_original_require path
rescue LoadError => load_error
if load_error.message.start_with?("Could not find") or
(load_error.message.end_with?(path) and Gem.try_activate(path)) then
return gem_original_require(path)
end
raise load_error
end
private :require
end

Просмотреть файл

@ -1,69 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
module Kernel
if defined?(gem_original_require) then
# Ruby ships with a custom_require, override its require
remove_method :require
else
##
# The Kernel#require from before RubyGems was loaded.
alias gem_original_require require
private :gem_original_require
end
##
# When RubyGems is required, Kernel#require is replaced with our own which
# is capable of loading gems on demand.
#
# When you call <tt>require 'x'</tt>, this is what happens:
# * If the file can be loaded from the existing Ruby loadpath, it
# is.
# * Otherwise, installed gems are searched for a file that matches.
# If it's found in gem 'y', that gem is activated (added to the
# loadpath).
#
# The normal <tt>require</tt> functionality of returning false if
# that file has already been loaded is preserved.
def require path
if Gem.unresolved_deps.empty? then
gem_original_require path
else
spec = Gem::Specification.find { |s|
s.activated? and s.contains_requirable_file? path
}
unless spec then
found_specs = Gem::Specification.find_in_unresolved path
unless found_specs.empty? then
found_specs = [found_specs.last]
else
found_specs = Gem::Specification.find_in_unresolved_tree path
end
found_specs.each do |found_spec|
found_spec.activate
end
end
return gem_original_require path
end
rescue LoadError => load_error
if load_error.message.start_with?("Could not find") or
(load_error.message.end_with?(path) and Gem.try_activate(path)) then
return gem_original_require(path)
end
raise load_error
end
private :require
end

Просмотреть файл

@ -1,8 +1,8 @@
module Gem module Gem
DEFAULT_HOST = "https://rubygems.org"
# TODO: move this whole file back into rubygems.rb
@post_install_hooks ||= [] @post_install_hooks ||= []
@done_installing_hooks ||= []
@post_uninstall_hooks ||= [] @post_uninstall_hooks ||= []
@pre_uninstall_hooks ||= [] @pre_uninstall_hooks ||= []
@pre_install_hooks ||= [] @pre_install_hooks ||= []
@ -61,7 +61,7 @@ module Gem
# Default gem load path # Default gem load path
def self.default_path def self.default_path
if File.exist? Gem.user_home then if Gem.user_home && File.exist?(Gem.user_home) then
[user_dir, default_dir] [user_dir, default_dir]
else else
[default_dir] [default_dir]
@ -93,24 +93,6 @@ module Gem
end end
end end
##
# The default system-wide source info cache directory
def self.default_system_source_cache_dir
File.join(Gem.dir, 'source_cache')
end
##
# The default user-specific source info cache directory
def self.default_user_source_cache_dir
#
# NOTE Probably an argument for moving this to per-ruby supported dirs like
# user_dir
#
File.join(Gem.user_home, '.gem', 'source_cache')
end
## ##
# A wrapper around RUBY_ENGINE const that may not be defined # A wrapper around RUBY_ENGINE const that may not be defined

Просмотреть файл

@ -1,8 +1,8 @@
require "rubygems/requirement"
## ##
# The Dependency class holds a Gem name and a Gem::Requirement. # The Dependency class holds a Gem name and a Gem::Requirement.
require "rubygems/requirement"
class Gem::Dependency class Gem::Dependency
## ##
@ -11,6 +11,9 @@ class Gem::Dependency
# When this list is updated, be sure to change # When this list is updated, be sure to change
# Gem::Specification::CURRENT_SPECIFICATION_VERSION as well. # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well.
# REFACTOR: This type of constant, TYPES, indicates we might want
# two classes, used via inheretance or duck typing.
TYPES = [ TYPES = [
:development, :development,
:runtime, :runtime,
@ -32,18 +35,23 @@ class Gem::Dependency
# <tt>:runtime</tt>. # <tt>:runtime</tt>.
def initialize name, *requirements def initialize name, *requirements
if Regexp === name then case name
when String then # ok
when Regexp then
msg = ["NOTE: Dependency.new w/ a regexp is deprecated.", msg = ["NOTE: Dependency.new w/ a regexp is deprecated.",
"Dependency.new called from #{Gem.location_of_caller.join(":")}"] "Dependency.new called from #{Gem.location_of_caller.join(":")}"]
warn msg.join("\n") unless Gem::Deprecate.skip warn msg.join("\n") unless Gem::Deprecate.skip
else
raise ArgumentError,
"dependency name must be a String, was #{name.inspect}"
end end
type = Symbol === requirements.last ? requirements.pop : :runtime type = Symbol === requirements.last ? requirements.pop : :runtime
requirements = requirements.first if 1 == requirements.length # unpack requirements = requirements.first if 1 == requirements.length # unpack
unless TYPES.include? type unless TYPES.include? type
raise ArgumentError, "Valid types are #{TYPES.inspect}, " raise ArgumentError, "Valid types are #{TYPES.inspect}, " +
+ "not #{type.inspect}" "not #{type.inspect}"
end end
@name = name @name = name
@ -66,8 +74,13 @@ class Gem::Dependency
end end
def inspect # :nodoc: def inspect # :nodoc:
"<%s type=%p name=%p requirements=%p>" % if @prerelease
[self.class, self.type, self.name, requirement.to_s] "<%s type=%p name=%p requirements=%p prerelease=ok>" %
[self.class, self.type, self.name, requirement.to_s]
else
"<%s type=%p name=%p requirements=%p>" %
[self.class, self.type, self.name, requirement.to_s]
end
end end
## ##
@ -77,6 +90,14 @@ class Gem::Dependency
@prerelease || requirement.prerelease? @prerelease || requirement.prerelease?
end end
##
# Is this dependency simply asking for the latest version
# of a gem?
def latest_version?
@requirement.none?
end
def pretty_print q # :nodoc: def pretty_print q # :nodoc:
q.group 1, 'Gem::Dependency.new(', ')' do q.group 1, 'Gem::Dependency.new(', ')' do
q.pp name q.pp name
@ -113,6 +134,8 @@ class Gem::Dependency
# Children, define explicit marshal and unmarshal behavior for # Children, define explicit marshal and unmarshal behavior for
# public classes. Marshal formats are part of your public API. # public classes. Marshal formats are part of your public API.
# REFACTOR: See above
if defined?(@version_requirement) && @version_requirement if defined?(@version_requirement) && @version_requirement
version = @version_requirement.instance_variable_get :@version version = @version_requirement.instance_variable_get :@version
@version_requirement = nil @version_requirement = nil
@ -122,6 +145,7 @@ class Gem::Dependency
@requirement = @version_requirements if defined?(@version_requirements) @requirement = @version_requirements if defined?(@version_requirements)
end end
# DOC: this method needs documentation or :nodoc''d
def requirements_list def requirements_list
requirement.as_list requirement.as_list
end end
@ -179,13 +203,24 @@ class Gem::Dependency
requirement.satisfied_by? version requirement.satisfied_by? version
end end
def match? name, version # DOC: this method needs either documented or :nodoc'd
def match? obj, version=nil
if !version
name = obj.name
version = obj.version
else
name = obj
end
return false unless self.name === name return false unless self.name === name
return true if requirement.none? return true if requirement.none?
requirement.satisfied_by? Gem::Version.new(version) requirement.satisfied_by? Gem::Version.new(version)
end end
# DOC: this method needs either documented or :nodoc'd
def matches_spec? spec def matches_spec? spec
return false unless name === spec.name return false unless name === spec.name
return true if requirement.none? return true if requirement.none?
@ -212,6 +247,8 @@ class Gem::Dependency
self.class.new name, self_req.as_list.concat(other_req.as_list) self.class.new name, self_req.as_list.concat(other_req.as_list)
end end
# DOC: this method needs either documented or :nodoc'd
def matching_specs platform_only = false def matching_specs platform_only = false
matches = Gem::Specification.find_all { |spec| matches = Gem::Specification.find_all { |spec|
self.name === spec.name and # TODO: == instead of === self.name === spec.name and # TODO: == instead of ===
@ -234,14 +271,26 @@ class Gem::Dependency
@requirement.specific? @requirement.specific?
end end
# DOC: this method needs either documented or :nodoc'd
def to_specs def to_specs
matches = matching_specs true matches = matching_specs true
# TODO: check Gem.activated_spec[self.name] in case matches falls outside # TODO: check Gem.activated_spec[self.name] in case matches falls outside
if matches.empty? then if matches.empty? then
specs = Gem::Specification.all_names.join ", " specs = Gem::Specification.find_all { |s|
error = Gem::LoadError.new "Could not find #{name} (#{requirement}) amongst [#{specs}]" s.name == name
}.map { |x| x.full_name }
if specs.empty?
total = Gem::Specification.to_a.size
error = Gem::LoadError.new \
"Could not find '#{name}' (#{requirement}) among #{total} total gem(s)"
else
error = Gem::LoadError.new \
"Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]"
end
error.name = self.name error.name = self.name
error.requirement = self.requirement error.requirement = self.requirement
raise error raise error
@ -252,6 +301,8 @@ class Gem::Dependency
matches matches
end end
# DOC: this method needs either documented or :nodoc'd
def to_spec def to_spec
matches = self.to_specs matches = self.to_specs

Просмотреть файл

@ -1,8 +1,12 @@
require 'rubygems' require 'rubygems'
require 'rubygems/dependency_list' require 'rubygems/dependency_list'
require 'rubygems/package'
require 'rubygems/installer' require 'rubygems/installer'
require 'rubygems/spec_fetcher' require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'rubygems/source_local'
require 'rubygems/source_specific_file'
require 'rubygems/available_set'
## ##
# Installs a gem along with all its dependencies from local and remote gems. # Installs a gem along with all its dependencies from local and remote gems.
@ -14,8 +18,14 @@ class Gem::DependencyInstaller
attr_reader :gems_to_install attr_reader :gems_to_install
attr_reader :installed_gems attr_reader :installed_gems
##
# Documentation types. For use by the Gem.done_installing hook
attr_reader :document
DEFAULT_OPTIONS = { DEFAULT_OPTIONS = {
:env_shebang => false, :env_shebang => false,
:document => %w[ri],
:domain => :both, # HACK dup :domain => :both, # HACK dup
:force => false, :force => false,
:format_executable => false, # HACK dup :format_executable => false, # HACK dup
@ -23,7 +33,8 @@ class Gem::DependencyInstaller
:prerelease => false, :prerelease => false,
:security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
:wrappers => true, :wrappers => true,
} :build_docs_in_background => false,
}.freeze
## ##
# Creates a new installer instance. # Creates a new installer instance.
@ -47,6 +58,7 @@ class Gem::DependencyInstaller
if options[:install_dir] then if options[:install_dir] then
@gem_home = options[:install_dir] @gem_home = options[:install_dir]
# HACK shouldn't change the global settings
Gem::Specification.dirs = @gem_home Gem::Specification.dirs = @gem_home
Gem.ensure_gem_subdirectories @gem_home Gem.ensure_gem_subdirectories @gem_home
options[:install_dir] = @gem_home # FIX: because we suck and reuse below options[:install_dir] = @gem_home # FIX: because we suck and reuse below
@ -55,7 +67,9 @@ class Gem::DependencyInstaller
options = DEFAULT_OPTIONS.merge options options = DEFAULT_OPTIONS.merge options
@bin_dir = options[:bin_dir] @bin_dir = options[:bin_dir]
@dev_shallow = options[:dev_shallow]
@development = options[:development] @development = options[:development]
@document = options[:document]
@domain = options[:domain] @domain = options[:domain]
@env_shebang = options[:env_shebang] @env_shebang = options[:env_shebang]
@force = options[:force] @force = options[:force]
@ -65,8 +79,14 @@ class Gem::DependencyInstaller
@security_policy = options[:security_policy] @security_policy = options[:security_policy]
@user_install = options[:user_install] @user_install = options[:user_install]
@wrappers = options[:wrappers] @wrappers = options[:wrappers]
@build_docs_in_background = options[:build_docs_in_background]
# Indicates that we should not try to update any deps unless
# we absolutely must.
@minimal_deps = options[:minimal_deps]
@installed_gems = [] @installed_gems = []
@toplevel_specs = nil
@install_dir = options[:install_dir] || Gem.dir @install_dir = options[:install_dir] || Gem.dir
@cache_dir = options[:cache_dir] || @install_dir @cache_dir = options[:cache_dir] || @install_dir
@ -76,6 +96,24 @@ class Gem::DependencyInstaller
@errors = nil @errors = nil
end end
attr_reader :errors
##
# Indicated, based on the requested domain, if local
# gems should be considered.
def consider_local?
@domain == :both or @domain == :local
end
##
# Indicated, based on the requested domain, if remote
# gems should be considered.
def consider_remote?
@domain == :both or @domain == :remote
end
## ##
# Returns a list of pairs of gemspecs and source_uris that match # Returns a list of pairs of gemspecs and source_uris that match
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources) # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
@ -83,35 +121,34 @@ class Gem::DependencyInstaller
# local gems preferred over remote gems. # local gems preferred over remote gems.
def find_gems_with_sources(dep) def find_gems_with_sources(dep)
# Reset the errors set = Gem::AvailableSet.new
@errors = nil
gems_and_sources = []
if @domain == :both or @domain == :local then if consider_local?
Dir[File.join(Dir.pwd, "#{dep.name}-[0-9]*.gem")].each do |gem_file| sl = Gem::Source::Local.new
spec = Gem::Format.from_file_by_path(gem_file).spec
gems_and_sources << [spec, gem_file] if spec.name == dep.name if spec = sl.find_gem(dep.name)
if dep.matches_spec? spec
set.add spec, sl
end
end end
end end
if @domain == :both or @domain == :remote then if consider_remote?
begin begin
# REFACTOR: all = dep.requirement.needs_all? found, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
requirements = dep.requirement.requirements.map do |req, ver|
req if @errors
@errors += errors
else
@errors = errors
end end
all = !dep.prerelease? && set << found
# we only need latest if there's one requirement and it is
# guaranteed to match the newest specs
(requirements.length > 1 or
(requirements.first != ">=" and requirements.first != ">"))
found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?
gems_and_sources.push(*found)
rescue Gem::RemoteFetcher::FetchError => e rescue Gem::RemoteFetcher::FetchError => e
# FIX if there is a problem talking to the network, we either need to always tell
# the user (no really_verbose) or fail hard, not silently tell them that we just
# couldn't find their requested gem.
if Gem.configuration.really_verbose then if Gem.configuration.really_verbose then
say "Error fetching remote data:\t\t#{e.message}" say "Error fetching remote data:\t\t#{e.message}"
say "Falling back to local-only install" say "Falling back to local-only install"
@ -120,9 +157,7 @@ class Gem::DependencyInstaller
end end
end end
gems_and_sources.sort_by do |gem, source| set
[gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
end
end end
## ##
@ -130,17 +165,22 @@ class Gem::DependencyInstaller
# remote sources unless the ignore_dependencies was given. # remote sources unless the ignore_dependencies was given.
def gather_dependencies def gather_dependencies
specs = @specs_and_sources.map { |spec,_| spec } specs = @available.all_specs
# these gems were listed by the user, always install them # these gems were listed by the user, always install them
keep_names = specs.map { |spec| spec.full_name } keep_names = specs.map { |spec| spec.full_name }
if @dev_shallow
@toplevel_specs = keep_names
end
dependency_list = Gem::DependencyList.new @development dependency_list = Gem::DependencyList.new @development
dependency_list.add(*specs) dependency_list.add(*specs)
to_do = specs.dup to_do = specs.dup
add_found_dependencies to_do, dependency_list unless @ignore_dependencies add_found_dependencies to_do, dependency_list unless @ignore_dependencies
# REFACTOR maybe abstract away using Gem::Specification.include? so
# that this isn't dependent only on the currently installed gems
dependency_list.specs.reject! { |spec| dependency_list.specs.reject! { |spec|
not keep_names.include?(spec.full_name) and not keep_names.include?(spec.full_name) and
Gem::Specification.include?(spec) Gem::Specification.include?(spec)
@ -162,32 +202,43 @@ class Gem::DependencyInstaller
until to_do.empty? do until to_do.empty? do
spec = to_do.shift spec = to_do.shift
# HACK why is spec nil?
next if spec.nil? or seen[spec.name] next if spec.nil? or seen[spec.name]
seen[spec.name] = true seen[spec.name] = true
deps = spec.runtime_dependencies deps = spec.runtime_dependencies
deps |= spec.development_dependencies if @development
if @development
if @dev_shallow
if @toplevel_specs.include? spec.full_name
deps |= spec.development_dependencies
end
else
deps |= spec.development_dependencies
end
end
deps.each do |dep| deps.each do |dep|
dependencies[dep.name] = dependencies[dep.name].merge dep dependencies[dep.name] = dependencies[dep.name].merge dep
results = find_gems_with_sources(dep).reverse if @minimal_deps
next if Gem::Specification.any? do |installed_spec|
results.reject! do |dep_spec,| dep.name == installed_spec.name and
to_do.push dep_spec dep.requirement.satisfied_by? installed_spec.version
end
# already locally installed
Gem::Specification.any? do |installed_spec|
dep.name == installed_spec.name and
dep.requirement.satisfied_by? installed_spec.version
end
end end
results.each do |dep_spec, source_uri| results = find_gems_with_sources(dep)
@specs_and_sources << [dep_spec, source_uri]
dependency_list.add dep_spec results.sorted.each do |t|
to_do.push t.spec
end end
results.remove_installed! dep
@available << results
results.inject_into_list dependency_list
end end
end end
@ -202,42 +253,36 @@ class Gem::DependencyInstaller
def find_spec_by_name_and_version(gem_name, def find_spec_by_name_and_version(gem_name,
version = Gem::Requirement.default, version = Gem::Requirement.default,
prerelease = false) prerelease = false)
spec_and_source = nil
glob = if File::ALT_SEPARATOR then set = Gem::AvailableSet.new
gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
else
gem_name
end
local_gems = Dir["#{glob}*"].sort.reverse if consider_local?
if File.exists? gem_name
src = Gem::Source::SpecificFile.new(gem_name)
set.add src.spec, src
else
local = Gem::Source::Local.new
local_gems.each do |gem_file| if s = local.find_gem(gem_name, version)
next unless gem_file =~ /gem$/ set.add s, local
begin end
spec = Gem::Format.from_file_by_path(gem_file).spec
spec_and_source = [spec, gem_file]
break
rescue SystemCallError, Gem::Package::FormatError
end end
end end
unless spec_and_source then if set.empty?
dep = Gem::Dependency.new gem_name, version dep = Gem::Dependency.new gem_name, version
# HACK Dependency objects should be immutable
dep.prerelease = true if prerelease dep.prerelease = true if prerelease
spec_and_sources = find_gems_with_sources(dep).reverse
spec_and_source = spec_and_sources.find { |spec, source| set = find_gems_with_sources(dep)
Gem::Platform.match spec.platform set.match_platform!
}
end end
if spec_and_source.nil? then if set.empty?
raise Gem::GemNotFoundException.new( raise Gem::SpecificGemNotFoundException.new(gem_name, version, @errors)
"Could not find a valid gem '#{gem_name}' (#{version}) locally or in a repository",
gem_name, version, @errors)
end end
@specs_and_sources = [spec_and_source] @available = set
end end
## ##
@ -258,33 +303,49 @@ class Gem::DependencyInstaller
if String === dep_or_name then if String === dep_or_name then
find_spec_by_name_and_version dep_or_name, version, @prerelease find_spec_by_name_and_version dep_or_name, version, @prerelease
else else
dep_or_name.prerelease = @prerelease dep = dep_or_name.dup
@specs_and_sources = [find_gems_with_sources(dep_or_name).last] dep.prerelease = @prerelease
@available = find_gems_with_sources(dep).pick_best!
end end
@installed_gems = [] @installed_gems = []
gather_dependencies gather_dependencies
# REFACTOR is the last gem always the one that the user requested?
# This code assumes that but is that actually validated by the code?
last = @gems_to_install.size - 1 last = @gems_to_install.size - 1
@gems_to_install.each_with_index do |spec, index| @gems_to_install.each_with_index do |spec, index|
# REFACTOR more current spec set hardcoding, should be abstracted?
next if Gem::Specification.include?(spec) and index != last next if Gem::Specification.include?(spec) and index != last
# TODO: make this sorta_verbose so other users can benefit from it # TODO: make this sorta_verbose so other users can benefit from it
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
_, source_uri = @specs_and_sources.assoc spec source = @available.source_for spec
begin begin
local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri, # REFACTOR make the fetcher to use configurable
@cache_dir local_gem_path = source.download spec, @cache_dir
rescue Gem::RemoteFetcher::FetchError rescue Gem::RemoteFetcher::FetchError
# TODO I doubt all fetch errors are recoverable, we should at least
# report the errors probably.
next if @force next if @force
raise raise
end end
if @development
if @dev_shallow
is_dev = @toplevel_specs.include? spec.full_name
else
is_dev = true
end
end
inst = Gem::Installer.new local_gem_path, inst = Gem::Installer.new local_gem_path,
:bin_dir => @bin_dir, :bin_dir => @bin_dir,
:development => @development, :development => is_dev,
:env_shebang => @env_shebang, :env_shebang => @env_shebang,
:force => @force, :force => @force,
:format_executable => @format_executable, :format_executable => @format_executable,
@ -299,6 +360,33 @@ class Gem::DependencyInstaller
@installed_gems << spec @installed_gems << spec
end end
# Since this is currently only called for docs, we can be lazy and just say
# it's documentation. Ideally the hook adder could decide whether to be in
# the background or not, and what to call it.
in_background "Installing documentation" do
start = Time.now
Gem.done_installing_hooks.each do |hook|
hook.call self, @installed_gems
end
finish = Time.now
say "Done installing documentation for #{@installed_gems.map(&:name).join(', ')} (#{(finish-start).to_i} sec)."
end unless Gem.done_installing_hooks.empty?
@installed_gems @installed_gems
end end
def in_background what
fork_happened = false
if @build_docs_in_background and Process.respond_to?(:fork)
begin
Process.fork do
yield
end
fork_happened = true
say "#{what} in a background process."
rescue NotImplementedError
end
end
yield unless fork_happened
end
end end

Просмотреть файл

@ -10,6 +10,10 @@ require 'rubygems/deprecate'
## ##
# Gem::DependencyList is used for installing and uninstalling gems in the # Gem::DependencyList is used for installing and uninstalling gems in the
# correct order to avoid conflicts. # correct order to avoid conflicts.
#--
# TODO: It appears that all but topo-sort functionality is being duplicated
# (or is planned to be duplicated) elsewhere in rubygems. Is the majority of
# this class necessary anymore? Especially #ok?, #why_not_ok?
class Gem::DependencyList class Gem::DependencyList
attr_reader :specs attr_reader :specs
@ -27,19 +31,10 @@ class Gem::DependencyList
def self.from_specs def self.from_specs
list = new list = new
list.add(*Gem::Specification.map) list.add(*Gem::Specification.to_a)
list list
end end
##
# Creates a DependencyList from a Gem::SourceIndex +source_index+
def self.from_source_index(ignored=nil)
warn "NOTE: DependencyList.from_source_index ignores it's arg" if ignored
from_specs
end
## ##
# Creates a new DependencyList. If +development+ is true, development # Creates a new DependencyList. If +development+ is true, development
# dependencies will be included. # dependencies will be included.
@ -143,7 +138,7 @@ class Gem::DependencyList
# If removing the gemspec creates breaks a currently ok dependency, then it # If removing the gemspec creates breaks a currently ok dependency, then it
# is NOT ok to remove the gemspec. # is NOT ok to remove the gemspec.
def ok_to_remove?(full_name) def ok_to_remove?(full_name, check_dev=true)
gem_to_remove = find_name full_name gem_to_remove = find_name full_name
siblings = @specs.find_all { |s| siblings = @specs.find_all { |s|
@ -154,7 +149,9 @@ class Gem::DependencyList
deps = [] deps = []
@specs.each do |spec| @specs.each do |spec|
spec.dependencies.each do |dep| check = check_dev ? spec.dependencies : spec.runtime_dependencies
check.each do |dep|
deps << dep if gem_to_remove.satisfies_requirement?(dep) deps << dep if gem_to_remove.satisfies_requirement?(dep)
end end
end end
@ -213,7 +210,7 @@ class Gem::DependencyList
@specs.each(&block) @specs.each(&block)
end end
def tsort_each_child(node, &block) def tsort_each_child(node)
specs = @specs.sort.reverse specs = @specs.sort.reverse
dependencies = node.runtime_dependencies dependencies = node.runtime_dependencies
@ -242,11 +239,6 @@ class Gem::DependencyList
def active_count(specs, ignored) def active_count(specs, ignored)
specs.count { |spec| ignored[spec.full_name].nil? } specs.count { |spec| ignored[spec.full_name].nil? }
end end
end end
class Gem::DependencyList
class << self
extend Gem::Deprecate
deprecate :from_source_index, "from_specs", 2011, 11
end
end

Просмотреть файл

@ -0,0 +1,562 @@
require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/exceptions'
require 'uri'
require 'net/http'
module Gem
# Raised when a DependencyConflict reaches the toplevel.
# Indicates which dependencies were incompatible.
#
class DependencyResolutionError < Gem::Exception
def initialize(conflict)
@conflict = conflict
a, b = conflicting_dependencies
super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
end
attr_reader :conflict
def conflicting_dependencies
@conflict.conflicting_dependencies
end
end
# Raised when a dependency requests a gem for which there is
# no spec.
#
class UnsatisfiableDepedencyError < Gem::Exception
def initialize(dep)
super "unable to find any gem matching dependency '#{dep}'"
@dependency = dep
end
attr_reader :dependency
end
# Raised when dependencies conflict and create the inability to
# find a valid possible spec for a request.
#
class ImpossibleDependenciesError < Gem::Exception
def initialize(request, conflicts)
s = conflicts.size == 1 ? "" : "s"
super "detected #{conflicts.size} conflict#{s} with dependency '#{request.dependency}'"
@request = request
@conflicts = conflicts
end
def dependency
@request.dependency
end
attr_reader :conflicts
end
# Given a set of Gem::Dependency objects as +needed+ and a way
# to query the set of available specs via +set+, calculates
# a set of ActivationRequest objects which indicate all the specs
# that should be activated to meet the all the requirements.
#
class DependencyResolver
# Represents a specification retrieved via the rubygems.org
# API. This is used to avoid having to load the full
# Specification object when all we need is the name, version,
# and dependencies.
#
class APISpecification
def initialize(set, api_data)
@set = set
@name = api_data[:name]
@version = Gem::Version.new api_data[:number]
@dependencies = api_data[:dependencies].map do |name, ver|
Gem::Dependency.new name, ver.split(/\s*,\s*/)
end
end
attr_reader :name, :version, :dependencies
def full_name
"#{@name}-#{@version}"
end
end
# The global rubygems pool, available via the rubygems.org API.
# Returns instances of APISpecification.
#
class APISet
def initialize
@data = Hash.new { |h,k| h[k] = [] }
end
# Return data for all versions of the gem +name+.
#
def versions(name)
if @data.key?(name)
return @data[name]
end
u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{name}"
str = Net::HTTP.get(u)
Marshal.load(str).each do |ver|
@data[ver[:name]] << ver
end
@data[name]
end
# Return an array of APISpecification objects matching
# DependencyRequest +req+.
#
def find_all(req)
res = []
versions(req.name).each do |ver|
if req.dependency.match? req.name, ver[:number]
res << APISpecification.new(self, ver)
end
end
res
end
# A hint run by the resolver to allow the Set to fetch
# data for DependencyRequests +reqs+.
#
def prefetch(reqs)
names = reqs.map { |r| r.dependency.name }
needed = names.find_all { |d| !@data.key?(d) }
return if needed.empty?
u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{needed.join ','}"
str = Net::HTTP.get(u)
Marshal.load(str).each do |ver|
@data[ver[:name]] << ver
end
end
end
# Represents a possible Specification object returned
# from IndexSet. Used to delay needed to download full
# Specification objects when only the +name+ and +version+
# are needed.
#
class IndexSpecification
def initialize(set, name, version, source, plat)
@set = set
@name = name
@version = version
@source = source
@platform = plat
@spec = nil
end
attr_reader :name, :version, :source
def full_name
"#{@name}-#{@version}"
end
def spec
@spec ||= @set.load_spec(@name, @version, @source)
end
def dependencies
spec.dependencies
end
end
# The global rubygems pool represented via the traditional
# source index.
#
class IndexSet
def initialize
@f = Gem::SpecFetcher.fetcher
@all = Hash.new { |h,k| h[k] = [] }
list, _ = @f.available_specs(:released)
list.each do |uri, specs|
specs.each do |n|
@all[n.name] << [uri, n]
end
end
@specs = {}
end
# Return an array of IndexSpecification objects matching
# DependencyRequest +req+.
#
def find_all(req)
res = []
name = req.dependency.name
@all[name].each do |uri, n|
if req.dependency.match? n
res << IndexSpecification.new(self, n.name, n.version,
uri, n.platform)
end
end
res
end
# No prefetching needed since we load the whole index in
# initially.
#
def prefetch(gems)
end
# Called from IndexSpecification to get a true Specification
# object.
#
def load_spec(name, ver, source)
key = "#{name}-#{ver}"
@specs[key] ||= source.fetch_spec(Gem::NameTuple.new(name, ver))
end
end
# A set which represents the installed gems. Respects
# all the normal settings that control where to look
# for installed gems.
#
class CurrentSet
def find_all(req)
req.dependency.matching_specs
end
def prefetch(gems)
end
end
# Create DependencyResolver object which will resolve
# the tree starting with +needed+ Depedency objects.
#
# +set+ is an object that provides where to look for
# specifications to satisify the Dependencies. This
# defaults to IndexSet, which will query rubygems.org.
#
def initialize(needed, set=IndexSet.new)
@set = set || IndexSet.new # Allow nil to mean IndexSet
@needed = needed
@conflicts = nil
end
# Provide a DependencyResolver that queries only against
# the already installed gems.
#
def self.for_current_gems(needed)
new needed, CurrentSet.new
end
# Contains all the conflicts encountered while doing resolution
#
attr_reader :conflicts
# Proceed with resolution! Returns an array of ActivationRequest
# objects.
#
def resolve
@conflicts = []
needed = @needed.map { |n| DependencyRequest.new(n, nil) }
res = resolve_for needed, []
if res.kind_of? DependencyConflict
raise DependencyResolutionError.new(res)
end
res
end
# Used internally to indicate that a dependency conflicted
# with a spec that would be activated.
#
class DependencyConflict
def initialize(dependency, activated, failed_dep=dependency)
@dependency = dependency
@activated = activated
@failed_dep = failed_dep
end
attr_reader :dependency, :activated
# Return the Specification that listed the dependency
#
def requester
@failed_dep.requester
end
def for_spec?(spec)
@dependency.name == spec.name
end
# Return the 2 dependency objects that conflicted
#
def conflicting_dependencies
[@failed_dep.dependency, @activated.request.dependency]
end
end
# Used Internally. Wraps a Depedency object to also track
# which spec contained the Dependency.
#
class DependencyRequest
def initialize(dep, act)
@dependency = dep
@requester = act
end
attr_reader :dependency, :requester
def name
@dependency.name
end
def matches_spec?(spec)
@dependency.matches_spec? spec
end
def to_s
@dependency.to_s
end
def ==(other)
case other
when Dependency
@dependency == other
when DependencyRequest
@dependency == other.dep && @requester == other.requester
else
false
end
end
end
# Specifies a Specification object that should be activated.
# Also contains a dependency that was used to introduce this
# activation.
#
class ActivationRequest
def initialize(spec, req, others_possible=true)
@spec = spec
@request = req
@others_possible = others_possible
end
attr_reader :spec, :request
# Indicate if this activation is one of a set of possible
# requests for the same Dependency request.
#
def others_possible?
@others_possible
end
# Return the ActivationRequest that contained the dependency
# that we were activated for.
#
def parent
@request.requester
end
def name
@spec.name
end
def full_name
@spec.full_name
end
def version
@spec.version
end
def full_spec
Gem::Specification === @spec ? @spec : @spec.spec
end
def download(path)
if @spec.respond_to? :source
source = @spec.source
else
source = Gem.sources.first
end
source.download full_spec, path
end
def ==(other)
case other
when Gem::Specification
@spec == other
when ActivationRequest
@spec == other.spec && @request == other.request
else
false
end
end
##
# Indicates if the requested gem has already been installed.
def installed?
this_spec = full_spec
Gem::Specification.any? do |s|
s == this_spec
end
end
end
def requests(s, act)
reqs = []
s.dependencies.each do |d|
next unless d.type == :runtime
reqs << DependencyRequest.new(d, act)
end
@set.prefetch(reqs)
reqs
end
# The meat of the algorithm. Given +needed+ DependencyRequest objects
# and +specs+ being a list to ActivationRequest, calculate a new list
# of ActivationRequest objects.
#
def resolve_for(needed, specs)
until needed.empty?
dep = needed.shift
# If there is already a spec activated for the requested name...
if existing = specs.find { |s| dep.name == s.name }
# then we're done since this new dep matches the
# existing spec.
next if dep.matches_spec? existing
# There is a conflict! We return the conflict
# object which will be seen by the caller and be
# handled at the right level.
# If the existing activation indicates that there
# are other possibles for it, then issue the conflict
# on the dep for the activation itself. Otherwise, issue
# it on the requester's request itself.
#
if existing.others_possible?
conflict = DependencyConflict.new(dep, existing)
else
depreq = existing.request.requester.request
conflict = DependencyConflict.new(depreq, existing, dep)
end
@conflicts << conflict
return conflict
end
# Get a list of all specs that satisfy dep
possible = @set.find_all(dep)
case possible.size
when 0
# If there are none, then our work here is done.
raise UnsatisfiableDepedencyError.new(dep)
when 1
# If there is one, then we just add it to specs
# and process the specs dependencies by adding
# them to needed.
spec = possible.first
act = ActivationRequest.new(spec, dep, false)
specs << act
# Put the deps for at the beginning of needed
# rather than the end to match the depth first
# searching done by the multiple case code below.
#
# This keeps the error messages consistent.
needed = requests(spec, act) + needed
else
# There are multiple specs for this dep. This is
# the case that this class is built to handle.
# Sort them so that we try the highest versions
# first.
possible = possible.sort_by { |s| s.version }
# We track the conflicts seen so that we can report them
# to help the user figure out how to fix the situation.
conflicts = []
# To figure out which to pick, we keep resolving
# given each one being activated and if there isn't
# a conflict, we know we've found a full set.
#
# We use an until loop rather than #reverse_each
# to keep the stack short since we're using a recursive
# algorithm.
#
until possible.empty?
s = possible.pop
# Recursively call #resolve_for with this spec
# and add it's dependencies into the picture...
act = ActivationRequest.new(s, dep)
try = requests(s, act) + needed
res = resolve_for(try, specs + [act])
# While trying to resolve these dependencies, there may
# be a conflict!
if res.kind_of? DependencyConflict
# The conflict might be created not by this invocation
# but rather one up the stack, so if we can't attempt
# to resolve this conflict (conflict isn't with the spec +s+)
# then just return it so the caller can try to sort it out.
return res unless res.for_spec? s
# Otherwise, this is a conflict that we can attempt to fix
conflicts << [s, res]
# Optimization:
#
# Because the conflict indicates the dependency that trigger
# it, we can prune possible based on this new information.
#
# This cuts down on the number of iterations needed.
possible.delete_if { |x| !res.dependency.matches_spec? x }
else
# No conflict, return the specs
return res
end
end
# We tried all possibles and nothing worked, so we let the user
# know and include as much information about the problem since
# the user is going to have to take action to fix this.
raise ImpossibleDependenciesError.new(dep, conflicts)
end
end
specs
end
end
end

Просмотреть файл

@ -20,51 +20,51 @@
# end # end
# end # end
module Gem module Gem::Deprecate
module Deprecate
def self.skip # :nodoc: def self.skip # :nodoc:
@skip ||= false @skip ||= false
end
def self.skip= v # :nodoc:
@skip = v
end
##
# Temporarily turn off warnings. Intended for tests only.
def skip_during
Gem::Deprecate.skip, original = true, Gem::Deprecate.skip
yield
ensure
Gem::Deprecate.skip = original
end
##
# Simple deprecation method that deprecates +name+ by wrapping it up
# in a dummy method. It warns on each call to the dummy method
# telling the user of +repl+ (unless +repl+ is :none) and the
# year/month that it is planned to go away.
def deprecate name, repl, year, month
class_eval {
old = "_deprecated_#{name}"
alias_method old, name
define_method name do |*args, &block| # TODO: really works on 1.8.7?
klass = self.kind_of? Module
target = klass ? "#{self}." : "#{self.class}#"
msg = [ "NOTE: #{target}#{name} is deprecated",
repl == :none ? " with no replacement" : ", use #{repl}",
". It will be removed on or after %4d-%02d-01." % [year, month],
"\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
]
warn "#{msg.join}." unless Gem::Deprecate.skip
send old, *args, &block
end
}
end
module_function :deprecate, :skip_during
end end
def self.skip= v # :nodoc:
@skip = v
end
##
# Temporarily turn off warnings. Intended for tests only.
def skip_during
Gem::Deprecate.skip, original = true, Gem::Deprecate.skip
yield
ensure
Gem::Deprecate.skip = original
end
##
# Simple deprecation method that deprecates +name+ by wrapping it up
# in a dummy method. It warns on each call to the dummy method
# telling the user of +repl+ (unless +repl+ is :none) and the
# year/month that it is planned to go away.
def deprecate name, repl, year, month
class_eval {
old = "_deprecated_#{name}"
alias_method old, name
define_method name do |*args, &block| # TODO: really works on 1.8.7?
klass = self.kind_of? Module
target = klass ? "#{self}." : "#{self.class}#"
msg = [ "NOTE: #{target}#{name} is deprecated",
repl == :none ? " with no replacement" : "; use #{repl} instead",
". It will be removed on or after %4d-%02d-01." % [year, month],
"\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
]
warn "#{msg.join}." unless Gem::Deprecate.skip
send old, *args, &block
end
}
end
module_function :deprecate, :skip_during
end end

Просмотреть файл

@ -1,243 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems'
##
# The documentation manager generates RDoc and RI for RubyGems.
class Gem::DocManager
include Gem::UserInteraction
@configured_args = []
def self.configured_args
@configured_args ||= []
end
def self.configured_args=(args)
case args
when Array
@configured_args = args
when String
@configured_args = args.split
end
end
##
# Load RDoc from a gem if it is available, otherwise from Ruby's stdlib
def self.load_rdoc
begin
gem 'rdoc'
rescue Gem::LoadError
# use built-in RDoc
end
begin
require 'rdoc/rdoc'
@rdoc_version = if defined? RDoc::VERSION then
Gem::Version.new RDoc::VERSION
else
Gem::Version.new '1.0.1' # HACK parsing is hard
end
rescue LoadError => e
raise Gem::DocumentError,
"ERROR: RDoc documentation generator not installed: #{e}"
end
end
def self.rdoc_version
@rdoc_version
end
##
# Updates the RI cache for RDoc 2 if it is installed
def self.update_ri_cache
load_rdoc rescue return
return unless defined? RDoc::VERSION # RDoc 1 does not have VERSION
require 'rdoc/ri/driver'
options = {
:use_cache => true,
:use_system => true,
:use_site => true,
:use_home => true,
:use_gems => true,
:formatter => RDoc::RI::Formatter,
}
RDoc::RI::Driver.new(options).class_cache
end
##
# Create a document manager for +spec+. +rdoc_args+ contains arguments for
# RDoc (template etc.) as a String.
def initialize(spec, rdoc_args="")
require 'fileutils'
@spec = spec
@doc_dir = spec.doc_dir
@rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
end
##
# Is the RDoc documentation installed?
def rdoc_installed?
File.exist?(File.join(@doc_dir, "rdoc"))
end
##
# Is the RI documentation installed?
def ri_installed?
File.exist?(File.join(@doc_dir, "ri"))
end
##
# Generate the RI documents for this gem spec.
#
# Note that if both RI and RDoc documents are generated from the same
# process, the RI docs should be done first (a likely bug in RDoc will cause
# RI docs generation to fail if run after RDoc).
def generate_ri
setup_rdoc
install_ri # RDoc bug, ri goes first
FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
end
##
# Generate the RDoc documents for this gem spec.
#
# Note that if both RI and RDoc documents are generated from the same
# process, the RI docs should be done first (a likely bug in RDoc will cause
# RI docs generation to fail if run after RDoc).
def generate_rdoc
setup_rdoc
install_rdoc
FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
end
##
# Generate and install RDoc into the documentation directory
def install_rdoc
rdoc_dir = File.join @doc_dir, 'rdoc'
FileUtils.rm_rf rdoc_dir
say "Installing RDoc documentation for #{@spec.full_name}..."
run_rdoc '--op', rdoc_dir
end
##
# Generate and install RI into the documentation directory
def install_ri
ri_dir = File.join @doc_dir, 'ri'
FileUtils.rm_rf ri_dir
say "Installing ri documentation for #{@spec.full_name}..."
run_rdoc '--ri', '--op', ri_dir
end
##
# Run RDoc with +args+, which is an ARGV style argument list
def run_rdoc(*args)
args << @spec.rdoc_options
args << self.class.configured_args
args << @spec.require_paths.clone
args << @spec.extra_rdoc_files
args << '--title' << "#{@spec.full_name} Documentation"
args << '--quiet'
args = args.flatten.map do |arg| arg.to_s end
if self.class.rdoc_version >= Gem::Version.new('2.4.0') then
args.delete '--inline-source'
args.delete '--promiscuous'
args.delete '-p'
args.delete '--one-file'
# HACK more
end
debug_args = args.dup
r = RDoc::RDoc.new
old_pwd = Dir.pwd
Dir.chdir @spec.full_gem_path
say "rdoc #{args.join ' '}" if Gem.configuration.really_verbose
begin
r.document args
rescue Errno::EACCES => e
dirname = File.dirname e.message.split("-")[1].strip
raise Gem::FilePermissionError.new(dirname)
rescue Interrupt => e
raise e
rescue Exception => ex
alert_error "While generating documentation for #{@spec.full_name}"
ui.errs.puts "... MESSAGE: #{ex}"
ui.errs.puts "... RDOC args: #{debug_args.join(' ')}"
ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if
Gem.configuration.backtrace
terminate_interaction 1
ensure
Dir.chdir old_pwd
end
end
def setup_rdoc
if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then
raise Gem::FilePermissionError.new(@doc_dir)
end
FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
self.class.load_rdoc
end
##
# Remove RDoc and RI documentation
def uninstall_doc
base_dir = @spec.base_dir
raise Gem::FilePermissionError.new base_dir unless File.writable? base_dir
# TODO: ok... that's twice... ugh
old_name = [
@spec.name, @spec.version, @spec.original_platform].join '-'
doc_dir = @spec.doc_dir
unless File.directory? doc_dir then
doc_dir = File.join File.dirname(doc_dir), old_name
end
ri_dir = @spec.ri_dir
unless File.directory? ri_dir then
ri_dir = File.join File.dirname(ri_dir), old_name
end
FileUtils.rm_rf doc_dir
FileUtils.rm_rf ri_dir
end
end

Просмотреть файл

@ -1,35 +1,88 @@
class Gem::ErrorReason; end ##
# This file contains all the various exceptions and other errors that are used
# Generated when trying to lookup a gem to indicate that the gem # inside of RubyGems.
# was found, but that it isn't usable on the current platform.
# #
# fetch and install read these and report them to the user to aid # DOC: Confirm _all_
# in figuring out why a gem couldn't be installed.
#
class Gem::PlatformMismatch < Gem::ErrorReason
attr_reader :name module Gem
attr_reader :version ##
attr_reader :platforms # Raised when RubyGems is unable to load or activate a gem. Contains the
# name and version requirements of the gem that either conflicts with
# already activated gems or that RubyGems is otherwise unable to activate.
def initialize(name, version) class LoadError < ::LoadError
@name = name # Name of gem
@version = version attr_accessor :name
@platforms = []
# Version requirement of gem
attr_accessor :requirement
end end
def add_platform(platform) # FIX: does this need to exist? The subclass is the only other reference
@platforms << platform # I can find.
end class ErrorReason; end
def wordy # Generated when trying to lookup a gem to indicate that the gem
prefix = "Found #{@name} (#{@version})" # was found, but that it isn't usable on the current platform.
#
# fetch and install read these and report them to the user to aid
# in figuring out why a gem couldn't be installed.
#
class PlatformMismatch < ErrorReason
if @platforms.size == 1 ##
"#{prefix}, but was for platform #{@platforms[0]}" # the name of the gem
else attr_reader :name
"#{prefix}, but was for platforms #{@platforms.join(' ,')}"
##
# the version
attr_reader :version
##
# The platforms that are mismatched
attr_reader :platforms
def initialize(name, version)
@name = name
@version = version
@platforms = []
end
##
# append a platform to the list of mismatched platforms.
#
# Platforms are added via this instead of injected via the constructor
# so that we can loop over a list of mismatches and just add them rather
# than perform some kind of calculation mismatch summary before creation.
def add_platform(platform)
@platforms << platform
end
##
# A wordy description of the error.
def wordy
"Found %s (%), but was for platform%s %s" %
[@name,
@version,
@platforms.size == 1 ? 's' : '',
@platforms.join(' ,')]
end end
end end
##
# An error that indicates we weren't able to fetch some
# data from a source
class SourceFetchProblem < ErrorReason
def initialize(source, error)
@source = source
@error = error
end
attr_reader :source, :error
def wordy
"Unable to download data from #{@source.uri} - #{@error.message}"
end
end
end end

Просмотреть файл

@ -1,7 +1,14 @@
# TODO: the documentation in here is terrible.
#
# Each exception needs a brief description and the scenarios where it is
# likely to be raised
## ##
# Base exception class for RubyGems. All exception raised by RubyGems are a # Base exception class for RubyGems. All exception raised by RubyGems are a
# subclass of this one. # subclass of this one.
class Gem::Exception < RuntimeError; end class Gem::Exception < RuntimeError
attr_accessor :source_exception
end
class Gem::CommandLineError < Gem::Exception; end class Gem::CommandLineError < Gem::Exception; end
@ -24,11 +31,18 @@ class Gem::EndOfYAMLException < Gem::Exception; end
## ##
# Signals that a file permission error is preventing the user from # Signals that a file permission error is preventing the user from
# installing in the requested directories. # operating on the given directory.
class Gem::FilePermissionError < Gem::Exception class Gem::FilePermissionError < Gem::Exception
def initialize(path)
super("You don't have write permissions into the #{path} directory.") attr_reader :directory
def initialize directory
@directory = directory
super "You don't have write permissions for the #{directory} directory."
end end
end end
## ##
@ -37,9 +51,12 @@ class Gem::FormatException < Gem::Exception
attr_accessor :file_path attr_accessor :file_path
end end
class Gem::GemNotFoundException < Gem::Exception class Gem::GemNotFoundException < Gem::Exception; end
def initialize(msg, name=nil, version=nil, errors=nil)
super msg class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException
def initialize(name, version, errors=nil)
super "Could not find a valid gem '#{name}' (#{version}) locally or in a repository"
@name = name @name = name
@version = version @version = version
@errors = errors @errors = errors
@ -89,3 +106,4 @@ class Gem::SystemExitException < SystemExit
end end
end end

Просмотреть файл

@ -16,7 +16,7 @@ class Gem::Ext::Builder
raise Gem::InstallError, "Makefile not found:\n\n#{results.join "\n"}" raise Gem::InstallError, "Makefile not found:\n\n#{results.join "\n"}"
end end
mf = File.read('Makefile') mf = Gem.read_binary 'Makefile'
mf = mf.gsub(/^RUBYARCHDIR\s*=\s*\$[^$]*/, "RUBYARCHDIR = #{dest_path}") mf = mf.gsub(/^RUBYARCHDIR\s*=\s*\$[^$]*/, "RUBYARCHDIR = #{dest_path}")
mf = mf.gsub(/^RUBYLIBDIR\s*=\s*\$[^$]*/, "RUBYLIBDIR = #{dest_path}") mf = mf.gsub(/^RUBYLIBDIR\s*=\s*\$[^$]*/, "RUBYLIBDIR = #{dest_path}")
@ -24,18 +24,14 @@ class Gem::Ext::Builder
# try to find make program from Ruby configure arguments first # try to find make program from Ruby configure arguments first
RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/ RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
make_program = $1 || ENV['make'] make_program = $1 || ENV['MAKE'] || ENV['make']
unless make_program then unless make_program then
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
end end
['', ' install'].each do |target| ['', ' install'].each do |target|
cmd = "#{make_program}#{target}" cmd = "#{make_program}#{target}"
results << cmd run(cmd, results, "make#{target}")
results << `#{cmd} #{redirector}`
raise Gem::InstallError, "make#{target} failed:\n\n#{results}" unless
$?.success?
end end
end end
@ -43,12 +39,20 @@ class Gem::Ext::Builder
'2>&1' '2>&1'
end end
def self.run(command, results) def self.run(command, results, command_name = nil)
results << command verbose = Gem.configuration.really_verbose
results << `#{command} #{redirector}`
if verbose
puts(command)
system(command)
else
results << command
results << `#{command} #{redirector}`
end
unless $?.success? then unless $?.success? then
raise Gem::InstallError, "#{class_name} failed:\n\n#{results.join "\n"}" results << "Building has failed. See above output for more information on the failure." if verbose
raise Gem::InstallError, "#{command_name || class_name} failed:\n\n#{results.join "\n"}"
end end
end end

Просмотреть файл

@ -8,10 +8,10 @@ require 'rubygems/ext/builder'
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
def self.build(extension, directory, dest_path, results) def self.build(extension, directory, dest_path, results, args=[])
unless File.exist?('Makefile') then unless File.exist?('Makefile') then
cmd = "sh ./configure --prefix=#{dest_path}" cmd = "sh ./configure --prefix=#{dest_path}"
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty? cmd << " #{args.join ' '}" unless args.empty?
run cmd, results run cmd, results
end end

Просмотреть файл

@ -9,9 +9,9 @@ require 'rubygems/command'
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
def self.build(extension, directory, dest_path, results) def self.build(extension, directory, dest_path, results, args=[])
cmd = "#{Gem.ruby} #{File.basename extension}" cmd = "#{Gem.ruby} #{File.basename extension}"
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty? cmd << " #{args.join ' '}" unless args.empty?
run cmd, results run cmd, results

Просмотреть файл

@ -9,10 +9,10 @@ require 'rubygems/command'
class Gem::Ext::RakeBuilder < Gem::Ext::Builder class Gem::Ext::RakeBuilder < Gem::Ext::Builder
def self.build(extension, directory, dest_path, results) def self.build(extension, directory, dest_path, results, args=[])
if File.basename(extension) =~ /mkrf_conf/i then if File.basename(extension) =~ /mkrf_conf/i then
cmd = "#{Gem.ruby} #{File.basename extension}" cmd = "#{Gem.ruby} #{File.basename extension}"
cmd << " #{Gem::Command.build_args.join " "}" unless Gem::Command.build_args.empty? cmd << " #{args.join " "}" unless args.empty?
run cmd, results run cmd, results
end end

Просмотреть файл

@ -1,82 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems/package'
##
# Gem::Format knows the guts of the RubyGem .gem file format and provides the
# capability to read gem files
class Gem::Format
attr_accessor :spec
attr_accessor :file_entries
attr_accessor :gem_path
##
# Constructs a Format representing the gem's data which came from +gem_path+
def initialize(gem_path)
@gem_path = gem_path
end
##
# Reads the gem +file_path+ using +security_policy+ and returns a Format
# representing the data in the gem
def self.from_file_by_path(file_path, security_policy = nil)
unless File.file?(file_path)
raise Gem::Exception, "Cannot load gem at [#{file_path}] in #{Dir.pwd}"
end
start = File.read file_path, 20
if start.nil? or start.length < 20 then
nil
elsif start.include?("MD5SUM =") # old version gems
require 'rubygems/old_format'
Gem::OldFormat.from_file_by_path file_path
else
begin
open file_path, Gem.binary_mode do |io|
from_io io, file_path, security_policy
end
rescue Gem::Package::TarInvalidError => e
message = "corrupt gem (#{e.class}: #{e.message})"
raise Gem::Package::FormatError.new(message, file_path)
end
end
end
##
# Reads a gem from +io+ at +gem_path+ using +security_policy+ and returns a
# Format representing the data from the gem
def self.from_io(io, gem_path="(io)", security_policy = nil)
format = new gem_path
Gem::Package.open io, 'r', security_policy do |pkg|
format.spec = pkg.metadata
format.file_entries = []
pkg.each do |entry|
size = entry.header.size
mode = entry.header.mode
format.file_entries << [{
"size" => size, "mode" => mode, "path" => entry.full_name,
},
entry.read
]
end
end
format
end
end

Просмотреть файл

@ -1,90 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
#--
# Some system might not have OpenSSL installed, therefore the core
# library file openssl might not be available. We localize testing
# for the presence of OpenSSL in this file.
#++
module Gem
class << self
##
# Is SSL (used by the signing commands) available on this
# platform?
def ssl_available?
@ssl_available
end
##
# Is SSL available?
attr_writer :ssl_available
##
# Ensure that SSL is available. Throw an exception if it is not.
def ensure_ssl_available
unless ssl_available?
raise Gem::Exception, "SSL is not installed on this system"
end
end
end
end
# :stopdoc:
begin
require 'openssl'
# Reference a constant defined in the .rb portion of ssl (just to
# make sure that part is loaded too).
Gem.ssl_available = !!OpenSSL::Digest::SHA1
class OpenSSL::X509::Certificate
# Check the validity of this certificate.
def check_validity(issuer_cert = nil, time = Time.now)
ret = if @not_before && @not_before > time
[false, :expired, "not valid before '#@not_before'"]
elsif @not_after && @not_after < time
[false, :expired, "not valid after '#@not_after'"]
elsif issuer_cert && !verify(issuer_cert.public_key)
[false, :issuer, "#{issuer_cert.subject} is not issuer"]
else
[true, :ok, 'Valid certificate']
end
# return hash
{ :is_valid => ret[0], :error => ret[1], :desc => ret[2] }
end
end
rescue LoadError, StandardError
Gem.ssl_available = false
end
module Gem::SSL
# We make our own versions of the constants here. This allows us
# to reference the constants, even though some systems might not
# have SSL installed in the Ruby core package.
#
# These constants are only used during load time. At runtime, any
# method that makes a direct reference to SSL software must be
# protected with a Gem.ensure_ssl_available call.
if Gem.ssl_available? then
PKEY_RSA = OpenSSL::PKey::RSA
DIGEST_SHA1 = OpenSSL::Digest::SHA1
else
PKEY_RSA = :rsa
DIGEST_SHA1 = :sha1
end
end

Просмотреть файл

@ -1,172 +0,0 @@
require "rubygems"
require "rubygems/deprecate"
##
# GemPathSearcher has the capability to find loadable files inside
# gems. It generates data up front to speed up searches later.
class Gem::GemPathSearcher
##
# Initialise the data we need to make searches later.
def initialize
# We want a record of all the installed gemspecs, in the order we wish to
# examine them.
# TODO: remove this stupid method
@gemspecs = init_gemspecs
# Map gem spec to glob of full require_path directories. Preparing this
# information may speed up searches later.
@lib_dirs = {}
@gemspecs.each do |spec|
@lib_dirs[spec.object_id] = lib_dirs_for spec
end
end
##
# Look in all the installed gems until a matching +glob+ is found.
# Return the _gemspec_ of the gem where it was found. If no match
# is found, return nil.
#
# The gems are searched in alphabetical order, and in reverse
# version order.
#
# For example:
#
# find('log4r') # -> (log4r-1.1 spec)
# find('log4r.rb') # -> (log4r-1.1 spec)
# find('rake/rdoctask') # -> (rake-0.4.12 spec)
# find('foobarbaz') # -> nil
#
# Matching paths can have various suffixes ('.rb', '.so', and
# others), which may or may not already be attached to _file_.
# This method doesn't care about the full filename that matches;
# only that there is a match.
def find(glob)
# HACK violation of encapsulation
@gemspecs.find do |spec|
# TODO: inverted responsibility
matching_file? spec, glob
end
end
# Looks through the available gemspecs and finds the first
# one that contains +file+ as a requirable file.
def find_spec_for_file(file)
@gemspecs.find do |spec|
return spec if spec.contains_requirable_file?(file)
end
end
def find_active(glob)
# HACK violation of encapsulation
@gemspecs.find do |spec|
# TODO: inverted responsibility
spec.loaded? and matching_file? spec, glob
end
end
##
# Works like #find, but finds all gemspecs matching +glob+.
def find_all(glob)
# HACK violation of encapsulation
@gemspecs.select do |spec|
# TODO: inverted responsibility
matching_file? spec, glob
end || []
end
def find_in_unresolved(glob)
# HACK violation
specs = Gem.unresolved_deps.values.map { |dep|
Gem.source_index.search dep, true
}.flatten
specs.select do |spec|
# TODO: inverted responsibility
matching_file? spec, glob
end || []
end
def find_in_unresolved_tree glob
# HACK violation
# TODO: inverted responsibility
specs = Gem.unresolved_deps.values.map { |dep|
Gem.source_index.search dep, true
}.flatten
specs.reverse_each do |spec|
trails = matching_paths(spec, glob)
next if trails.empty?
return trails.map(&:reverse).sort.first.reverse
end
[]
end
##
# Attempts to find a matching path using the require_paths of the given
# +spec+.
def matching_file?(spec, path)
not matching_files(spec, path).empty?
end
def matching_paths(spec, path)
trails = []
spec.traverse do |from_spec, dep, to_spec, trail|
next unless to_spec.conflicts.empty?
trails << trail unless matching_files(to_spec, path).empty?
end
trails
end
##
# Returns files matching +path+ in +spec+.
#--
# Some of the intermediate results are cached in @lib_dirs for speed.
def matching_files(spec, path)
return [] unless @lib_dirs[spec.object_id] # case no paths
glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}"
Dir[glob].select { |f| File.file? f.untaint }
end
##
# Return a list of all installed gemspecs, sorted by alphabetical order and
# in reverse version order. (bar-2, bar-1, foo-2)
def init_gemspecs
Gem::Specification.sort { |a, b|
names = a.name <=> b.name
next names if names.nonzero?
b.version <=> a.version
}
end
##
# Returns library directories glob for a gemspec. For example,
# '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}'
def lib_dirs_for(spec)
"#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" if
spec.require_paths
end
extend Gem::Deprecate
deprecate :initialize, :none, 2011, 10
deprecate :find, :none, 2011, 10
deprecate :find_active, :none, 2011, 10
deprecate :find_all, :none, 2011, 10
deprecate :find_in_unresolved, :none, 2011, 10
deprecate :find_in_unresolved_tree, :none, 2011, 10
deprecate :find_spec_for_file, :none, 2011, 10
end

Просмотреть файл

@ -4,10 +4,9 @@
# See LICENSE.txt for permissions. # See LICENSE.txt for permissions.
#++ #++
require "rubygems" require 'rubygems'
require 'rubygems/command_manager' require 'rubygems/command_manager'
require 'rubygems/config_file' require 'rubygems/config_file'
require 'rubygems/doc_manager'
## ##
# Load additional plugins from $LOAD_PATH # Load additional plugins from $LOAD_PATH
@ -29,25 +28,21 @@ class Gem::GemRunner
# TODO: nuke these options # TODO: nuke these options
@command_manager_class = options[:command_manager] || Gem::CommandManager @command_manager_class = options[:command_manager] || Gem::CommandManager
@config_file_class = options[:config_file] || Gem::ConfigFile @config_file_class = options[:config_file] || Gem::ConfigFile
@doc_manager_class = options[:doc_manager] || Gem::DocManager
end end
## ##
# Run the gem command with the following arguments. # Run the gem command with the following arguments.
def run(args) def run(args)
start_time = Time.now
if args.include?('--') if args.include?('--')
# We need to preserve the original ARGV to use for passing gem options # We need to preserve the original ARGV to use for passing gem options
# to source gems. If there is a -- in the line, strip all options after # to source gems. If there is a -- in the line, strip all options after
# it...its for the source building process. # it...its for the source building process.
# TODO use slice!
build_args = args[args.index("--") + 1...args.length] build_args = args[args.index("--") + 1...args.length]
args = args[0...args.index("--")] args = args[0...args.index("--")]
end end
Gem::Command.build_args = build_args if build_args
do_configuration args do_configuration args
cmd = @command_manager_class.instance cmd = @command_manager_class.instance
@ -62,14 +57,7 @@ class Gem::GemRunner
Gem::Command.add_specific_extra_args command_name, config_args Gem::Command.add_specific_extra_args command_name, config_args
end end
cmd.run Gem.configuration.args cmd.run Gem.configuration.args, build_args
end_time = Time.now
if Gem.configuration.benchmark then
printf "\nExecution time: %0.2f seconds.\n", end_time - start_time
puts "Press Enter to finish"
STDIN.gets
end
end end
private private
@ -78,7 +66,6 @@ class Gem::GemRunner
Gem.configuration = @config_file_class.new(args) Gem.configuration = @config_file_class.new(args)
Gem.use_paths Gem.configuration[:gemhome], Gem.configuration[:gempath] Gem.use_paths Gem.configuration[:gemhome], Gem.configuration[:gempath]
Gem::Command.extra_args = Gem.configuration[:gem] Gem::Command.extra_args = Gem.configuration[:gem]
@doc_manager_class.configured_args = Gem.configuration[:rdoc]
end end
end end

Просмотреть файл

@ -1,6 +1,7 @@
require 'rubygems/remote_fetcher' require 'rubygems/remote_fetcher'
module Gem::GemcutterUtilities module Gem::GemcutterUtilities
# TODO: move to Gem::Command
OptionParser.accept Symbol do |value| OptionParser.accept Symbol do |value|
value.to_sym value.to_sym
end end
@ -19,6 +20,8 @@ module Gem::GemcutterUtilities
def api_key def api_key
if options[:key] then if options[:key] then
verify_api_key options[:key] verify_api_key options[:key]
elsif Gem.configuration.api_keys.key?(host)
Gem.configuration.api_keys[host]
else else
Gem.configuration.rubygems_api_key Gem.configuration.rubygems_api_key
end end
@ -44,12 +47,24 @@ module Gem::GemcutterUtilities
end end
end end
def rubygems_api_request(method, path, host = Gem.host, &block) attr_writer :host
require 'net/http' def host
host = ENV['RUBYGEMS_HOST'] if ENV['RUBYGEMS_HOST'] configured_host = Gem.host unless
uri = URI.parse "#{host}/#{path}" Gem.configuration.disable_default_gem_server
say "Pushing gem to #{host}..." @host ||= ENV['RUBYGEMS_HOST'] || configured_host
end
def rubygems_api_request(method, path, host = nil, &block)
require 'net/http'
self.host = host if host
unless self.host
alert_error "You must specify a gem server"
terminate_interaction 1 # TODO: question this
end
uri = URI.parse "#{self.host}/#{path}"
request_method = Net::HTTP.const_get method.to_s.capitalize request_method = Net::HTTP.const_get method.to_s.capitalize
@ -66,7 +81,7 @@ module Gem::GemcutterUtilities
end end
else else
say resp.body say resp.body
terminate_interaction 1 terminate_interaction 1 # TODO: question this
end end
end end
@ -74,8 +89,8 @@ module Gem::GemcutterUtilities
if Gem.configuration.api_keys.key? key then if Gem.configuration.api_keys.key? key then
Gem.configuration.api_keys[key] Gem.configuration.api_keys[key]
else else
alert_error "No such API key. You can add it with gem keys --add #{key}" alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)."
terminate_interaction 1 terminate_interaction 1 # TODO: question this
end end
end end

Просмотреть файл

@ -1,5 +1,5 @@
require 'rubygems' require 'rubygems'
require 'rubygems/format' require 'rubygems/package'
require 'time' require 'time'
begin begin
@ -15,11 +15,6 @@ class Gem::Indexer
include Gem::UserInteraction include Gem::UserInteraction
##
# Build indexes for RubyGems older than 1.2.0 when true
attr_accessor :build_legacy
## ##
# Build indexes for RubyGems 1.2.0 and newer when true # Build indexes for RubyGems 1.2.0 and newer when true
@ -63,15 +58,10 @@ class Gem::Indexer
"\n\tgem install builder" "\n\tgem install builder"
end end
options = { :build_legacy => true, :build_modern => true }.merge options options = { :build_modern => true }.merge options
@build_legacy = options[:build_legacy]
@build_modern = options[:build_modern] @build_modern = options[:build_modern]
@rss_title = options[:rss_title]
@rss_host = options[:rss_host]
@rss_gems_host = options[:rss_gems_host]
@dest_directory = directory @dest_directory = directory
@directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}") @directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}")
@ -99,8 +89,6 @@ class Gem::Indexer
@dest_prerelease_specs_index = @dest_prerelease_specs_index =
File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}") File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}")
@rss_index = File.join @directory, 'index.rss'
@files = [] @files = []
end end
@ -109,6 +97,8 @@ class Gem::Indexer
# searching, downloading and related activities and do not need deployment # searching, downloading and related activities and do not need deployment
# specific information (e.g. list of files). So we abbreviate the spec, # specific information (e.g. list of files). So we abbreviate the spec,
# making it much smaller for quicker downloads. # making it much smaller for quicker downloads.
#--
# TODO move to Gem::Specification
def abbreviate(spec) def abbreviate(spec)
spec.files = [] spec.files = []
@ -123,37 +113,15 @@ class Gem::Indexer
# Build various indicies # Build various indicies
def build_indicies def build_indicies
# Marshal gemspecs are used by both modern and legacy RubyGems
Gem::Specification.dirs = [] Gem::Specification.dirs = []
Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list)) Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list))
build_marshal_gemspecs build_marshal_gemspecs
build_legacy_indicies if @build_legacy
build_modern_indicies if @build_modern build_modern_indicies if @build_modern
build_rss
compress_indicies compress_indicies
end end
##
# Builds indicies for RubyGems older than 1.2.x
def build_legacy_indicies
index = collect_specs
say "Generating Marshal master index"
Gem.time 'Generated Marshal master index' do
open @marshal_index, 'wb' do |io|
io.write index.dump
end
end
@files << @marshal_index
@files << "#{@marshal_index}.Z"
end
## ##
# Builds Marshal quick index gemspecs. # Builds Marshal quick index gemspecs.
@ -238,104 +206,6 @@ class Gem::Indexer
"#{@prerelease_specs_index}.gz"] "#{@prerelease_specs_index}.gz"]
end end
##
# Builds an RSS feed for past two days gem releases according to the gem's
# date.
def build_rss
if @rss_host.nil? or @rss_gems_host.nil? then
if Gem.configuration.really_verbose then
alert_warning "no --rss-host or --rss-gems-host, RSS generation disabled"
end
return
end
require 'cgi'
require 'rubygems/text'
extend Gem::Text
Gem.time 'Generated rss' do
open @rss_index, 'wb' do |io|
rss_host = CGI.escapeHTML @rss_host
rss_title = CGI.escapeHTML(@rss_title || 'gems')
io.puts <<-HEADER
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>#{rss_title}</title>
<link>http://#{rss_host}</link>
<description>Recently released gems from http://#{rss_host}</description>
<generator>RubyGems v#{Gem::VERSION}</generator>
<docs>http://cyber.law.harvard.edu/rss/rss.html</docs>
HEADER
today = Gem::Specification::TODAY
yesterday = today - 86400
index = Gem::Specification.select do |spec|
spec_date = spec.date
# TODO: remove this and make YAML based specs properly normalized
spec_date = Time.parse(spec_date.to_s) if Date === spec_date
spec_date >= yesterday && spec_date <= today
end
index.sort_by { |spec| [-spec.date.to_i, spec] }.each do |spec|
file_name = File.basename spec.cache_file
gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{file_name}"
size = File.stat(spec.loaded_from).size # rescue next
description = spec.description || spec.summary || ''
authors = Array spec.authors
emails = Array spec.email
authors = emails.zip(authors).map do |email, author|
email += " (#{author})" if author and not author.empty?
end.join ', '
description = description.split(/\n\n+/).map do |chunk|
format_text chunk, 78
end
description = description.join "\n\n"
item = ''
item << <<-ITEM
<item>
<title>#{CGI.escapeHTML spec.full_name}</title>
<description>
&lt;pre&gt;#{CGI.escapeHTML description.chomp}&lt;/pre&gt;
</description>
<author>#{CGI.escapeHTML authors}</author>
<guid>#{CGI.escapeHTML spec.full_name}</guid>
<enclosure url=\"#{gem_path}\"
length=\"#{size}\" type=\"application/octet-stream\" />
<pubDate>#{spec.date.rfc2822}</pubDate>
ITEM
item << <<-ITEM if spec.homepage
<link>#{CGI.escapeHTML spec.homepage}</link>
ITEM
item << <<-ITEM
</item>
ITEM
io.puts item
end
io.puts <<-FOOTER
</channel>
</rss>
FOOTER
end
end
@files << @rss_index
end
def map_gems_to_specs gems def map_gems_to_specs gems
gems.map { |gemfile| gems.map { |gemfile|
if File.size(gemfile) == 0 then if File.size(gemfile) == 0 then
@ -344,7 +214,7 @@ class Gem::Indexer
end end
begin begin
spec = Gem::Format.from_file_by_path(gemfile).spec spec = Gem::Package.new(gemfile).spec
spec.loaded_from = gemfile spec.loaded_from = gemfile
# HACK: fuck this shit - borks all tests that use pl1 # HACK: fuck this shit - borks all tests that use pl1
@ -373,21 +243,6 @@ class Gem::Indexer
}.compact }.compact
end end
##
# Collect specifications from .gem files from the gem directory.
def collect_specs(gems = gem_file_list)
Gem::Deprecate.skip_during do
index = Gem::SourceIndex.new
map_gems_to_specs(gems).each do |spec|
index.add_spec spec, spec.original_name
end
index
end
end
## ##
# Compresses indicies on disk # Compresses indicies on disk
#-- #--
@ -397,11 +252,6 @@ class Gem::Indexer
say "Compressing indicies" say "Compressing indicies"
Gem.time 'Compressed indicies' do Gem.time 'Compressed indicies' do
if @build_legacy then
compress @marshal_index, 'Z'
paranoid @marshal_index, 'Z'
end
if @build_modern then if @build_modern then
gzip @specs_index gzip @specs_index
gzip @latest_specs_index gzip @latest_specs_index
@ -559,12 +409,9 @@ class Gem::Indexer
end end
## ##
# Perform an in-place update of the repository from newly added gems. Only # Perform an in-place update of the repository from newly added gems.
# works for modern indicies, and sets #build_legacy to false when run.
def update_index def update_index
@build_legacy = false
make_temp_directories make_temp_directories
specs_mtime = File.stat(@dest_specs_index).mtime specs_mtime = File.stat(@dest_specs_index).mtime
@ -584,6 +431,9 @@ class Gem::Indexer
specs = map_gems_to_specs updated_gems specs = map_gems_to_specs updated_gems
prerelease, released = specs.partition { |s| s.version.prerelease? } prerelease, released = specs.partition { |s| s.version.prerelease? }
Gem::Specification.dirs = []
Gem::Specification.add_specs(*specs)
files = build_marshal_gemspecs files = build_marshal_gemspecs
Gem.time 'Updated indexes' do Gem.time 'Updated indexes' do

Просмотреть файл

@ -0,0 +1,12 @@
require 'rubygems'
require 'rubygems/user_interaction'
##
# A default post-install hook that displays "Successfully installed
# some_gem-1.0"
Gem.post_install do |installer|
ui = Gem::DefaultUserInteraction.ui
ui.say "Successfully installed #{installer.spec.full_name}"
end

Просмотреть файл

@ -22,6 +22,7 @@ module Gem::InstallUpdateOptions
# Add the install/update options to the option parser. # Add the install/update options to the option parser.
def add_install_update_options def add_install_update_options
# TODO: use @parser.accept
OptionParser.accept Gem::Security::Policy do |value| OptionParser.accept Gem::Security::Policy do |value|
require 'rubygems/security' require 'rubygems/security'
@ -39,21 +40,49 @@ module Gem::InstallUpdateOptions
end end
add_option(:"Install/Update", '-n', '--bindir DIR', add_option(:"Install/Update", '-n', '--bindir DIR',
'Directory where binary files are', 'Directory where binary files are',
'located') do |value, options| 'located') do |value, options|
options[:bin_dir] = File.expand_path(value) options[:bin_dir] = File.expand_path(value)
end end
add_option(:"Install/Update", '-d', '--[no-]rdoc', add_option(:"Install/Update", '--[no-]document [TYPES]', Array,
'Generate RDoc documentation for the gem on', 'Generate documentation for installed gems',
'install') do |value, options| 'List the documentation types you wish to',
options[:generate_rdoc] = value 'generate. For example: rdoc,ri') do |value, options|
options[:document] = case value
when nil then %w[ri]
when false then []
else value
end
end end
add_option(:"Install/Update", '--[no-]ri', add_option(:"Install/Update", '-N', '--no-document',
'Generate RI documentation for the gem on', 'Disable documentation generation') do |value, options|
'install') do |value, options| options[:document] = []
options[:generate_ri] = value end
add_option(:Deprecated, '--[no-]rdoc',
'Generate RDoc for installed gems',
'Use --document instead') do |value, options|
if value then
options[:document] << 'rdoc'
else
options[:document].delete 'rdoc'
end
options[:document].uniq!
end
add_option(:Deprecated, '--[no-]ri',
'Generate ri data for installed gems.',
'Use --document instead') do |value, options|
if value then
options[:document] << 'ri'
else
options[:document].delete 'ri'
end
options[:document].uniq!
end end
add_option(:"Install/Update", '-E', '--[no-]env-shebang', add_option(:"Install/Update", '-E', '--[no-]env-shebang',
@ -85,12 +114,6 @@ module Gem::InstallUpdateOptions
options[:ignore_dependencies] = value options[:ignore_dependencies] = value
end end
add_option(:"Install/Update", '-y', '--include-dependencies',
'Unconditionally install the required',
'dependent gems') do |value, options|
options[:include_dependencies] = value
end
add_option(:"Install/Update", '--[no-]format-executable', add_option(:"Install/Update", '--[no-]format-executable',
'Make installed executable names match ruby.', 'Make installed executable names match ruby.',
'If ruby is ruby18, foo_exec will be', 'If ruby is ruby18, foo_exec will be',
@ -105,15 +128,30 @@ module Gem::InstallUpdateOptions
end end
add_option(:"Install/Update", "--development", add_option(:"Install/Update", "--development",
"Install any additional development", "Install additional development",
"dependencies") do |value, options| "dependencies") do |value, options|
options[:development] = true options[:development] = true
options[:dev_shallow] = true
end
add_option(:"Install/Update", "--development-all",
"Install development dependencies for all",
"gems (including dev deps themselves)") do |value, options|
options[:development] = true
options[:dev_shallow] = false
end end
add_option(:"Install/Update", "--conservative", add_option(:"Install/Update", "--conservative",
"Don't attempt to upgrade gems already", "Don't attempt to upgrade gems already",
"meeting version requirement") do |value, options| "meeting version requirement") do |value, options|
options[:conservative] = true options[:conservative] = true
options[:minimal_deps] = true
end
add_option(:"Install/Update", "--minimal-deps",
"Don't upgrade any dependencies that already",
"meet version requirements") do |value, options|
options[:minimal_deps] = true
end end
end end
@ -121,7 +159,7 @@ module Gem::InstallUpdateOptions
# Default options for the gem install command. # Default options for the gem install command.
def install_update_defaults_str def install_update_defaults_str
'--rdoc --no-force --wrappers' '--document=rdoc,ri --wrappers'
end end
end end

Просмотреть файл

@ -4,15 +4,13 @@
# See LICENSE.txt for permissions. # See LICENSE.txt for permissions.
#++ #++
require 'rubygems/format'
require 'rubygems/exceptions' require 'rubygems/exceptions'
require 'rubygems/package'
require 'rubygems/ext' require 'rubygems/ext'
require 'rubygems/require_paths_builder'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
## ##
# The installer class processes RubyGem .gem files and installs the files # The installer installs the files contained in the .gem into the Gem.home.
# contained in the .gem into the Gem.path.
# #
# Gem::Installer does the work of putting files in all the right places on the # Gem::Installer does the work of putting files in all the right places on the
# filesystem including unpacking the gem into its gem dir, installing the # filesystem including unpacking the gem into its gem dir, installing the
@ -39,8 +37,7 @@ class Gem::Installer
include Gem::UserInteraction include Gem::UserInteraction
include Gem::RequirePathsBuilder if Gem::QUICKLOADER_SUCKAGE # DOC: Missing docs or :nodoc:.
attr_reader :gem attr_reader :gem
## ##
@ -67,6 +64,7 @@ class Gem::Installer
attr_accessor :path_warning attr_accessor :path_warning
# DOC: Missing docs or :nodoc:.
attr_writer :exec_format attr_writer :exec_format
# Defaults to use Ruby's program prefix and suffix. # Defaults to use Ruby's program prefix and suffix.
@ -80,24 +78,36 @@ class Gem::Installer
# Constructs an Installer instance that will install the gem located at # Constructs an Installer instance that will install the gem located at
# +gem+. +options+ is a Hash with the following keys: # +gem+. +options+ is a Hash with the following keys:
# #
# :bin_dir:: Where to put a bin wrapper if needed.
# :development:: Whether or not development dependencies should be installed.
# :env_shebang:: Use /usr/bin/env in bin wrappers. # :env_shebang:: Use /usr/bin/env in bin wrappers.
# :force:: Overrides all version checks and security policy checks, except # :force:: Overrides all version checks and security policy checks, except
# for a signed-gems-only policy. # for a signed-gems-only policy.
# :ignore_dependencies:: Don't raise if a dependency is missing.
# :install_dir:: The directory to install the gem into.
# :format_executable:: Format the executable the same as the ruby executable. # :format_executable:: Format the executable the same as the ruby executable.
# If your ruby is ruby18, foo_exec will be installed as # If your ruby is ruby18, foo_exec will be installed as
# foo_exec18. # foo_exec18.
# :ignore_dependencies:: Don't raise if a dependency is missing.
# :install_dir:: The directory to install the gem into.
# :security_policy:: Use the specified security policy. See Gem::Security # :security_policy:: Use the specified security policy. See Gem::Security
# :user_install:: Indicate that the gem should be unpacked into the users
# personal gem directory.
# :only_install_dir:: Only validate dependencies against what is in the
# install_dir
# :wrappers:: Install wrappers if true, symlinks if false. # :wrappers:: Install wrappers if true, symlinks if false.
# :build_args:: An Array of arguments to pass to the extension builder
# process. If not set, then Gem::Command.build_args is used
def initialize(gem, options={}) def initialize(gem, options={})
require 'fileutils' require 'fileutils'
@gem = gem @gem = gem
@options = options @options = options
@package = Gem::Package.new @gem
process_options process_options
@package.security_policy = @security_policy
if options[:user_install] and not options[:unpack] then if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir @gem_home = Gem.user_dir
check_that_user_bin_dir_is_in_path check_that_user_bin_dir_is_in_path
@ -105,28 +115,79 @@ class Gem::Installer
end end
## ##
# Lazy accessor for the spec's gem directory. # Checks if +filename+ exists in +@bin_dir+.
#
# If +@force+ is set +filename+ is overwritten.
#
# If +filename+ exists and is a RubyGems wrapper for different gem the user
# is consulted.
#
# If +filename+ exists and +@bin_dir+ is Gem.default_bindir (/usr/local) the
# user is consulted.
#
# Otherwise +filename+ is overwritten.
def gem_dir def check_executable_overwrite filename # :nodoc:
@gem_dir ||= spec.gem_dir.dup.untaint return if @force
generated_bin = File.join @bin_dir, filename
return unless File.exist? generated_bin
ruby_executable = false
existing = nil
open generated_bin, 'rb' do |io|
next unless io.gets =~ /^#!/ # shebang
io.gets # blankline
# TODO detect a specially formatted comment instead of trying
# to run a regexp against ruby code.
next unless io.gets =~ /This file was generated by RubyGems/
ruby_executable = true
existing = io.read.slice(/^gem (['"])(.*?)(\1),/, 2)
end
return if spec.name == existing
# somebody has written to RubyGems' directory, overwrite, too bad
return if Gem.default_bindir != @bin_dir and not ruby_executable
question = "#{spec.name}'s executable \"#{filename}\" conflicts with "
if ruby_executable then
question << existing
return if ask_yes_no "#{question}\nOverwrite the executable?", false
conflict = "installed executable from #{existing}"
else
question << generated_bin
return if ask_yes_no "#{question}\nOverwrite the executable?", false
conflict = generated_bin
end
raise Gem::InstallError,
"\"#{filename}\" from #{spec.name} conflicts with #{conflict}"
end end
## ##
# Lazy accessor for the installer's Gem::Format instance. # Lazy accessor for the spec's gem directory.
def format def gem_dir
begin @gem_dir ||= File.join(gem_home, "gems", spec.full_name)
@format ||= Gem::Format.from_file_by_path gem, @security_policy
rescue Gem::Package::FormatError
raise Gem::InstallError, "invalid gem format for #{gem}"
end
end end
## ##
# Lazy accessor for the installer's spec. # Lazy accessor for the installer's spec.
def spec def spec
@spec ||= format.spec @spec ||= @package.spec
rescue Gem::Package::Error => e
raise Gem::InstallError, "invalid gem: #{e.message}"
end end
## ##
@ -141,11 +202,7 @@ class Gem::Installer
# specifications/<gem-version>.gemspec #=> the Gem::Specification # specifications/<gem-version>.gemspec #=> the Gem::Specification
def install def install
current_home = Gem.dir
current_path = Gem.paths.path
verify_gem_home(options[:unpack]) verify_gem_home(options[:unpack])
Gem.use_paths gem_home, current_path # HACK: shouldn't need Gem.paths.path
# If we're forcing the install then disable security unless the security # If we're forcing the install then disable security unless the security
# policy says that we only install signed gems. # policy says that we only install signed gems.
@ -158,31 +215,65 @@ class Gem::Installer
ensure_dependencies_met unless @ignore_dependencies ensure_dependencies_met unless @ignore_dependencies
end end
Gem.pre_install_hooks.each do |hook| run_pre_install_hooks
result = hook.call self
if result == false then
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
message = "pre-install hook#{location} failed for #{spec.full_name}"
raise Gem::InstallError, message
end
end
Gem.ensure_gem_subdirectories gem_home Gem.ensure_gem_subdirectories gem_home
# Completely remove any previous gem files # Completely remove any previous gem files
FileUtils.rm_rf(gem_dir) if File.exist? gem_dir FileUtils.rm_rf(gem_dir)
FileUtils.mkdir_p gem_dir FileUtils.mkdir_p gem_dir
extract_files extract_files
build_extensions build_extensions
Gem.post_build_hooks.each do |hook| run_post_build_hooks
result = hook.call self
if result == false then generate_bin
write_spec
unless @build_args.empty?
File.open spec.build_info_file, "w" do |f|
@build_args.each { |a| f.puts a }
end
end
# TODO should be always cache the file? Other classes have options
# to controls if caching is done.
cache_file = File.join(gem_home, "cache", "#{spec.full_name}.gem")
FileUtils.cp gem, cache_file unless File.exist? cache_file
say spec.post_install_message unless spec.post_install_message.nil?
spec.loaded_from = spec_file
Gem::Specification.add_spec spec unless Gem::Specification.include? spec
run_post_install_hooks
spec
# TODO This rescue is in the wrong place. What is raising this exception?
# move this rescue to arround the code that actually might raise it.
rescue Zlib::GzipFile::Error
raise Gem::InstallError, "gzip error installing #{gem}"
end
def run_pre_install_hooks # :nodoc:
Gem.pre_install_hooks.each do |hook|
if hook.call(self) == false then
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
message = "pre-install hook#{location} failed for #{spec.full_name}"
raise Gem::InstallError, message
end
end
end
def run_post_build_hooks # :nodoc:
Gem.post_build_hooks.each do |hook|
if hook.call(self) == false then
FileUtils.rm_rf gem_dir FileUtils.rm_rf gem_dir
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
@ -191,32 +282,29 @@ class Gem::Installer
raise Gem::InstallError, message raise Gem::InstallError, message
end end
end end
end
generate_bin def run_post_install_hooks # :nodoc:
write_spec
write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE
cache_file = spec.cache_file
FileUtils.cp gem, cache_file unless File.exist? cache_file
say spec.post_install_message unless spec.post_install_message.nil?
spec.loaded_from = spec.spec_file
Gem::Specification.add_spec spec unless Gem::Specification.include? spec
Gem.post_install_hooks.each do |hook| Gem.post_install_hooks.each do |hook|
hook.call self hook.call self
end end
end
return spec ##
rescue Zlib::GzipFile::Error #
raise Gem::InstallError, "gzip error installing #{gem}" # Return an Array of Specifications contained within the gem_home
ensure # we'll be installing into.
# conditional since we might be here because we're erroring out early.
if current_path def installed_specs
Gem.use_paths current_home, current_path @specs ||= begin
specs = []
Dir[File.join(gem_home, "specifications", "*.gemspec")].each do |path|
spec = Gem::Specification.load path.untaint
specs << spec if spec
end
specs
end end
end end
@ -235,9 +323,11 @@ class Gem::Installer
end end
## ##
# True if the gems in the source_index satisfy +dependency+. # True if the gems in the system satisfy +dependency+.
def installation_satisfies_dependency?(dependency) def installation_satisfies_dependency?(dependency)
return true if installed_specs.detect { |s| dependency.matches_spec? s }
return false if @only_install_dir
not dependency.matching_specs.empty? not dependency.matching_specs.empty?
end end
@ -246,18 +336,23 @@ class Gem::Installer
def unpack(directory) def unpack(directory)
@gem_dir = directory @gem_dir = directory
@format = Gem::Format.from_file_by_path gem, @security_policy
extract_files extract_files
end end
##
# The location of of the spec file that is installed.
#
def spec_file
File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
end
## ##
# Writes the .gemspec specification (in Ruby) to the gem home's # Writes the .gemspec specification (in Ruby) to the gem home's
# specifications directory. # specifications directory.
def write_spec def write_spec
file_name = spec.spec_file.untaint File.open(spec_file, "w") do |file|
File.open(file_name, "w") do |file|
file.puts spec.to_ruby_for_cache file.puts spec.to_ruby_for_cache
end end
end end
@ -277,34 +372,34 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:.
def generate_bin def generate_bin
return if spec.executables.nil? or spec.executables.empty? return if spec.executables.nil? or spec.executables.empty?
# If the user has asked for the gem to be installed in a directory that is Dir.mkdir @bin_dir unless File.exist? @bin_dir
# the system gem directory, then use the system bin directory, else create raise Gem::FilePermissionError.new(@bin_dir) unless File.writable? @bin_dir
# (or use) a new bin dir under the gem_home.
bindir = @bin_dir || Gem.bindir(gem_home)
Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
spec.executables.each do |filename| spec.executables.each do |filename|
filename.untaint filename.untaint
bin_path = File.expand_path File.join(gem_dir, spec.bindir, filename) bin_path = File.join gem_dir, spec.bindir, filename
unless File.exist? bin_path unless File.exist? bin_path then
warn "Hey?!?! Where did #{bin_path} go??" # TODO change this to a more useful warning
warn "#{bin_path} maybe `gem pristine #{spec.name}` will fix it?"
next next
end end
mode = File.stat(bin_path).mode | 0111 mode = File.stat(bin_path).mode | 0111
FileUtils.chmod mode, bin_path FileUtils.chmod mode, bin_path
check_executable_overwrite filename
if @wrappers then if @wrappers then
generate_bin_script filename, bindir generate_bin_script filename, @bin_dir
else else
generate_bin_symlink filename, bindir generate_bin_symlink filename, @bin_dir
end end
end end
end end
@ -358,10 +453,21 @@ class Gem::Installer
## ##
# Generates a #! line for +bin_file_name+'s wrapper copying arguments if # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
# necessary. # necessary.
#
# If the :custom_shebang config is set, then it is used as a template
# for how to create the shebang used for to run a gem's executables.
#
# The template supports 4 expansions:
#
# $env the path to the unix env utility
# $ruby the path to the currently running ruby interpreter
# $exec the path to the gem's executable
# $name the name of the gem the executable is for
#
def shebang(bin_file_name) def shebang(bin_file_name)
ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang
path = spec.bin_file bin_file_name path = File.join gem_dir, spec.bindir, bin_file_name
first_line = File.open(path, "rb") {|file| file.gets} first_line = File.open(path, "rb") {|file| file.gets}
if /\A#!/ =~ first_line then if /\A#!/ =~ first_line then
@ -371,7 +477,25 @@ class Gem::Installer
shebang.strip! # Avoid nasty ^M issues. shebang.strip! # Avoid nasty ^M issues.
end end
if not ruby_name then if which = Gem.configuration[:custom_shebang]
# replace bin_file_name with "ruby" to avoid endless loops
which = which.gsub(/ #{bin_file_name}$/," #{Gem::ConfigMap[:ruby_install_name]}")
which = which.gsub(/\$(\w+)/) do
case $1
when "env"
@env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
when "ruby"
"#{Gem.ruby}#{opts}"
when "exec"
bin_file_name
when "name"
spec.name
end
end
"#!#{which}"
elsif not ruby_name then
"#!#{Gem.ruby}#{opts}" "#!#{Gem.ruby}#{opts}"
elsif opts then elsif opts then
"#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
@ -382,6 +506,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:.
def ensure_required_ruby_version_met def ensure_required_ruby_version_met
if rrv = spec.required_ruby_version then if rrv = spec.required_ruby_version then
unless rrv.satisfied_by? Gem.ruby_version then unless rrv.satisfied_by? Gem.ruby_version then
@ -390,9 +515,10 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:.
def ensure_required_rubygems_version_met def ensure_required_rubygems_version_met
if rrgv = spec.required_rubygems_version then if rrgv = spec.required_rubygems_version then
unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then unless rrgv.satisfied_by? Gem.rubygems_version then
raise Gem::InstallError, raise Gem::InstallError,
"#{spec.name} requires RubyGems version #{rrgv}. " + "#{spec.name} requires RubyGems version #{rrgv}. " +
"Try 'gem update --system' to update RubyGems itself." "Try 'gem update --system' to update RubyGems itself."
@ -400,6 +526,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:.
def ensure_dependencies_met def ensure_dependencies_met
deps = spec.runtime_dependencies deps = spec.runtime_dependencies
deps |= spec.development_dependencies if @development deps |= spec.development_dependencies if @development
@ -409,13 +536,14 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:.
def process_options def process_options
@options = { @options = {
:bin_dir => nil, :bin_dir => nil,
:env_shebang => false, :env_shebang => false,
:exec_format => false,
:force => false, :force => false,
:install_dir => Gem.dir, :install_dir => Gem.dir,
:only_install_dir => false
}.merge options }.merge options
@env_shebang = options[:env_shebang] @env_shebang = options[:env_shebang]
@ -425,13 +553,18 @@ class Gem::Installer
@format_executable = options[:format_executable] @format_executable = options[:format_executable]
@security_policy = options[:security_policy] @security_policy = options[:security_policy]
@wrappers = options[:wrappers] @wrappers = options[:wrappers]
@bin_dir = options[:bin_dir] @only_install_dir = options[:only_install_dir]
# If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home.
@bin_dir = options[:bin_dir] || Gem.bindir(gem_home)
@development = options[:development] @development = options[:development]
raise "NOTE: Installer option :source_index is dead" if @build_args = options[:build_args] || Gem::Command.build_args
options[:source_index]
end end
# DOC: Missing docs or :nodoc:.
def check_that_user_bin_dir_is_in_path def check_that_user_bin_dir_is_in_path
user_bin_dir = @bin_dir || Gem.bindir(gem_home) user_bin_dir = @bin_dir || Gem.bindir(gem_home)
user_bin_dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR user_bin_dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
@ -449,6 +582,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:.
def verify_gem_home(unpack = false) def verify_gem_home(unpack = false)
FileUtils.mkdir_p gem_home FileUtils.mkdir_p gem_home
raise Gem::FilePermissionError, gem_home unless raise Gem::FilePermissionError, gem_home unless
@ -499,7 +633,6 @@ GOTO :EOF
:WinNT :WinNT
@"#{ruby}" "%~dpn0" %* @"#{ruby}" "%~dpn0" %*
TEXT TEXT
end end
## ##
@ -508,7 +641,14 @@ TEXT
def build_extensions def build_extensions
return if spec.extensions.empty? return if spec.extensions.empty?
say "Building native extensions. This could take a while..."
if @build_args.empty?
say "Building native extensions. This could take a while..."
else
say "Building native extensions with: '#{@build_args.join(' ')}'"
say "This could take a while..."
end
dest_path = File.join gem_dir, spec.require_paths.first dest_path = File.join gem_dir, spec.require_paths.first
ran_rake = false # only run rake once ran_rake = false # only run rake once
@ -516,6 +656,9 @@ TEXT
break if ran_rake break if ran_rake
results = [] results = []
extension ||= ""
extension_dir = File.join gem_dir, File.dirname(extension)
builder = case extension builder = case extension
when /extconf/ then when /extconf/ then
Gem::Ext::ExtConfBuilder Gem::Ext::ExtConfBuilder
@ -525,43 +668,41 @@ TEXT
ran_rake = true ran_rake = true
Gem::Ext::RakeBuilder Gem::Ext::RakeBuilder
else else
results = ["No builder for extension '#{extension}'"] message = "No builder for extension '#{extension}'"
nil extension_build_error extension_dir, message
end end
extension_dir = begin
File.join gem_dir, File.dirname(extension)
rescue TypeError # extension == nil
gem_dir
end
begin begin
Dir.chdir extension_dir do Dir.chdir extension_dir do
results = builder.build(extension, gem_dir, dest_path, results) results = builder.build(extension, gem_dir, dest_path,
results, @build_args)
say results.join("\n") if Gem.configuration.really_verbose say results.join("\n") if Gem.configuration.really_verbose
end end
rescue rescue
results = results.join "\n" extension_build_error(extension_dir, results.join("\n"))
end
end
end
gem_make_out = File.join extension_dir, 'gem_make.out' ##
# Logs the build +output+ in +build_dir+, then raises ExtensionBuildError.
open gem_make_out, 'wb' do |io| io.puts results end def extension_build_error(build_dir, output)
gem_make_out = File.join build_dir, 'gem_make.out'
message = <<-EOF open gem_make_out, 'wb' do |io| io.puts output end
message = <<-EOF
ERROR: Failed to build gem native extension. ERROR: Failed to build gem native extension.
#{results} #{output}
Gem files will remain installed in #{gem_dir} for inspection. Gem files will remain installed in #{gem_dir} for inspection.
Results logged to #{gem_make_out} Results logged to #{gem_make_out}
EOF EOF
raise ExtensionBuildError, message raise ExtensionBuildError, message
end
end
end end
## ##
@ -570,36 +711,7 @@ EOF
# Ensures that files can't be installed outside the gem directory. # Ensures that files can't be installed outside the gem directory.
def extract_files def extract_files
raise ArgumentError, "format required to extract from" if @format.nil? @package.extract_files gem_dir
@format.file_entries.each do |entry, file_data|
path = entry['path'].untaint
if path.start_with? "/" then # for extra sanity
raise Gem::InstallError, "attempt to install file into #{entry['path']}"
end
path = File.expand_path File.join(gem_dir, path)
unless path.start_with? gem_dir then
msg = "attempt to install file into %p under %s" %
[entry['path'], gem_dir]
raise Gem::InstallError, msg
end
FileUtils.rm_rf(path) if File.exist? path
dir = File.dirname path
FileUtils.mkdir_p dir unless File.exist? dir
File.open(path, "wb") do |out|
out.write file_data
end
FileUtils.chmod entry['mode'], path
say path if Gem.configuration.really_verbose
end
end end
## ##

Просмотреть файл

@ -6,11 +6,26 @@ class Gem::Installer
## ##
# Available through requiring rubygems/installer_test_case # Available through requiring rubygems/installer_test_case
attr_writer :bin_dir
##
# Available through requiring rubygems/installer_test_case
attr_writer :build_args
##
# Available through requiring rubygems/installer_test_case
attr_writer :gem_dir attr_writer :gem_dir
## ##
# Available through requiring rubygems/installer_test_case # Available through requiring rubygems/installer_test_case
attr_writer :force
##
# Available through requiring rubygems/installer_test_case
attr_writer :format attr_writer :format
## ##
@ -54,47 +69,68 @@ end
class Gem::InstallerTestCase < Gem::TestCase class Gem::InstallerTestCase < Gem::TestCase
##
# Creates the following instance variables:
#
# @spec::
# a spec named 'a', intended for regular installs
# @user_spec::
# a spec named 'b', intended for user installs
# @gem::
# the path to a built gem from @spec
# @user_spec::
# the path to a built gem from @user_spec
#
# @installer::
# a Gem::Installer for the @spec that installs into @gemhome
# @user_installer::
# a Gem::Installer for the @user_spec that installs into Gem.user_dir
def setup def setup
super super
@installer_tmp = File.join @tempdir, 'installer' @spec = quick_gem 'a' do |spec|
FileUtils.mkdir_p @installer_tmp util_make_exec spec
end
Gem.use_paths @installer_tmp @user_spec = quick_gem 'b' do |spec|
Gem.ensure_gem_subdirectories @installer_tmp util_make_exec spec
end
@spec = quick_gem 'a'
util_make_exec @spec
util_build_gem @spec util_build_gem @spec
@gem = @spec.cache_file
@user_spec = quick_gem 'b'
util_make_exec @user_spec
util_build_gem @user_spec util_build_gem @user_spec
@user_gem = @user_spec.cache_file
Gem.use_paths @gemhome @gem = @spec.cache_file
@user_gem = @user_spec.cache_file
@installer = util_installer @spec, @gemhome @installer = util_installer @spec, @gemhome
@user_installer = util_installer @user_spec, Gem.user_dir, :user @user_installer = util_installer @user_spec, Gem.user_dir, :user
Gem.use_paths @gemhome
end end
def util_gem_bindir spec = @spec def util_gem_bindir spec = @spec # :nodoc:
# TODO: deprecate # TODO: deprecate
spec.bin_dir spec.bin_dir
end end
def util_gem_dir spec = @spec def util_gem_dir spec = @spec # :nodoc:
# TODO: deprecate # TODO: deprecate
spec.gem_dir spec.gem_dir
end end
##
# The path where installed executables live
def util_inst_bindir def util_inst_bindir
File.join @gemhome, "bin" File.join @gemhome, "bin"
end end
##
# Adds an executable named "executable" to +spec+ with the given +shebang+.
#
# The executable is also written to the bin dir in @tmpdir and the installed
# gem directory for +spec+.
def util_make_exec(spec = @spec, shebang = "#!/usr/bin/ruby") def util_make_exec(spec = @spec, shebang = "#!/usr/bin/ruby")
spec.executables = %w[executable] spec.executables = %w[executable]
spec.files << 'bin/executable' spec.files << 'bin/executable'
@ -110,6 +146,14 @@ class Gem::InstallerTestCase < Gem::TestCase
end end
end end
##
# Builds the @spec gem and returns an installer for it. The built gem
# includes:
#
# bin/executable
# lib/code.rb
# ext/a/mkrf_conf.rb
def util_setup_gem(ui = @ui) # HACK fix use_ui to make this automatic def util_setup_gem(ui = @ui) # HACK fix use_ui to make this automatic
@spec.files << File.join('lib', 'code.rb') @spec.files << File.join('lib', 'code.rb')
@spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb') @spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
@ -129,16 +173,24 @@ class Gem::InstallerTestCase < Gem::TestCase
end end
use_ui ui do use_ui ui do
FileUtils.rm @gem FileUtils.rm_f @gem
@gem = Gem::Builder.new(@spec).build @gem = Gem::Package.build @spec
end end
end end
@installer = Gem::Installer.new @gem @installer = Gem::Installer.new @gem
end end
##
# Creates an installer for +spec+ that will install into +gem_home+. If
# +user+ is true a user-install will be performed.
def util_installer(spec, gem_home, user=false) def util_installer(spec, gem_home, user=false)
Gem::Installer.new spec.cache_file, :user_install => user Gem::Installer.new(spec.cache_file,
:install_dir => gem_home,
:user_install => user)
end end
end end

Просмотреть файл

@ -6,6 +6,17 @@ require 'rubygems/user_interaction'
# retrieval during tests. # retrieval during tests.
class Gem::MockGemUi < Gem::StreamUI class Gem::MockGemUi < Gem::StreamUI
##
# Raised when you haven't provided enough input to your MockGemUi
class InputEOFError < RuntimeError
def initialize question
super "Out of input for MockGemUi on #{question.inspect}"
end
end
class TermError < RuntimeError class TermError < RuntimeError
attr_reader :exit_code attr_reader :exit_code
@ -44,6 +55,12 @@ class Gem::MockGemUi < Gem::StreamUI
@terminated = false @terminated = false
end end
def ask question
raise InputEOFError, question if @ins.eof?
super
end
def input def input
@ins.string @ins.string
end end

110
lib/rubygems/name_tuple.rb Normal file
Просмотреть файл

@ -0,0 +1,110 @@
##
#
# Represents a gem of name +name+ at +version+ of +platform+. These
# wrap the data returned from the indexes.
require 'rubygems/platform'
class Gem::NameTuple
def initialize(name, version, platform="ruby")
@name = name
@version = version
unless platform.kind_of? Gem::Platform
platform = "ruby" if !platform or platform.empty?
end
@platform = platform
end
attr_reader :name, :version, :platform
##
# Turn an array of [name, version, platform] into an array of
# NameTuple objects.
def self.from_list list
list.map { |t| new(*t) }
end
##
# Turn an array of NameTuple objects back into an array of
# [name, version, platform] tuples.
def self.to_basic list
list.map { |t| t.to_a }
end
##
# A null NameTuple, ie name=nil, version=0
def self.null
new nil, Gem::Version.new(0), nil
end
##
# Indicate if this NameTuple matches the current platform.
def match_platform?
Gem::Platform.match @platform
end
##
# Indicate if this NameTuple is for a prerelease version.
def prerelease?
@version.prerelease?
end
##
# Return the name that the gemspec file would be
def spec_name
case @platform
when nil, 'ruby', ''
"#{@name}-#{@version}.gemspec"
else
"#{@name}-#{@version}-#{@platform}.gemspec"
end
end
##
# Convert back to the [name, version, platform] tuple
def to_a
[@name, @version, @platform]
end
def to_s
"#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>"
end
def <=> other
to_a <=> other.to_a
end
include Comparable
##
# Compare with +other+. Supports another NameTuple or an Array
# in the [name, version, platform] format.
def == other
case other
when self.class
@name == other.name and
@version == other.version and
@platform == other.platform
when Array
to_a == other
else
false
end
end
alias_method :eql?, :==
def hash
to_a.hash
end
end

Просмотреть файл

@ -1,153 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems'
##
# The format class knows the guts of the RubyGem .gem file format and provides
# the capability to read gem files
class Gem::OldFormat
attr_accessor :spec, :file_entries, :gem_path
##
# Constructs an instance of a Format object, representing the gem's data
# structure.
#
# gem:: [String] The file name of the gem
def initialize(gem_path)
require 'fileutils'
require 'zlib'
Gem.load_yaml
@gem_path = gem_path
end
##
# Reads the named gem file and returns a Format object, representing the
# data from the gem file
#
# file_path:: [String] Path to the gem file
def self.from_file_by_path(file_path)
unless File.exist?(file_path)
raise Gem::Exception, "Cannot load gem file [#{file_path}]"
end
File.open(file_path, 'rb') do |file|
from_io(file, file_path)
end
end
##
# Reads a gem from an io stream and returns a Format object, representing
# the data from the gem file
#
# io:: [IO] Stream from which to read the gem
def self.from_io(io, gem_path="(io)")
format = self.new(gem_path)
skip_ruby(io)
format.spec = read_spec(io)
format.file_entries = []
read_files_from_gem(io) do |entry, file_data|
format.file_entries << [entry, file_data]
end
format
end
private
##
# Skips the Ruby self-install header. After calling this method, the
# IO index will be set after the Ruby code.
#
# file:: [IO] The IO to process (skip the Ruby code)
def self.skip_ruby(file)
end_seen = false
loop {
line = file.gets
if(line == nil || line.chomp == "__END__") then
end_seen = true
break
end
}
if end_seen == false then
raise Gem::Exception.new("Failed to find end of ruby script while reading gem")
end
end
##
# Reads the specification YAML from the supplied IO and constructs
# a Gem::Specification from it. After calling this method, the
# IO index will be set after the specification header.
#
# file:: [IO] The IO to process
def self.read_spec(file)
yaml = ''
read_until_dashes file do |line|
yaml << line
end
Gem::Specification.from_yaml yaml
rescue YAML::Error => e
raise Gem::Exception, "Failed to parse gem specification out of gem file"
rescue ArgumentError => e
raise Gem::Exception, "Failed to parse gem specification out of gem file"
end
##
# Reads lines from the supplied IO until a end-of-yaml (---) is
# reached
#
# file:: [IO] The IO to process
# block:: [String] The read line
def self.read_until_dashes(file)
while((line = file.gets) && line.chomp.strip != "---") do
yield line
end
end
##
# Reads the embedded file data from a gem file, yielding an entry
# containing metadata about the file and the file contents themselves
# for each file that's archived in the gem.
# NOTE: Many of these methods should be extracted into some kind of
# Gem file read/writer
#
# gem_file:: [IO] The IO to process
def self.read_files_from_gem(gem_file)
errstr = "Error reading files from gem"
header_yaml = ''
begin
self.read_until_dashes(gem_file) do |line|
header_yaml << line
end
header = YAML.load(header_yaml)
raise Gem::Exception, errstr unless header
header.each do |entry|
file_data = ''
self.read_until_dashes(gem_file) do |line|
file_data << line
end
yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])]
end
rescue Zlib::DataError
raise Gem::Exception, errstr
end
end
end

Просмотреть файл

@ -3,16 +3,54 @@
# Copyright (C) 2004 Mauricio Julio Fernández Pradier # Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information. # See LICENSE.txt for additional licensing information.
#++ #++
#
# Example using a Gem::Package
#
# Builds a .gem file given a Gem::Specification. A .gem file is a tarball
# which contains a data.tar.gz and metadata.gz, and possibly signatures.
#
# require 'rubygems'
# require 'rubygems/package'
#
# spec = Gem::Specification.new do |s|
# s.summary = "Ruby based make-like utility."
# s.name = 'rake'
# s.version = PKG_VERSION
# s.requirements << 'none'
# s.files = PKG_FILES
# s.description = <<-EOF
# Rake is a Make-like program implemented in Ruby. Tasks
# and dependencies are specified in standard Ruby syntax.
# EOF
# end
#
# Gem::Package.build spec
#
# Reads a .gem file.
#
# require 'rubygems'
# require 'rubygems/package'
#
# the_gem = Gem::Package.new(path_to_dot_gem)
# the_gem.contents # get the files in the gem
# the_gem.extract_files destination_directory # extract the gem into a directory
# the_gem.spec # get the spec out of the gem
# the_gem.verify # check the gem is OK (contains valid gem specification, contains a not corrupt contents archive)
#
# #files are the files in the .gem tar file, not the ruby files in the gem
# #extract_files and #contents automatically call #verify
require 'rubygems/security'
require 'rubygems/specification' require 'rubygems/specification'
require 'rubygems/user_interaction'
require 'zlib'
module Gem::Package class Gem::Package
include Gem::UserInteraction
class Error < Gem::Exception; end
class Error < StandardError; end
class NonSeekableIO < Error; end
class ClosedIO < Error; end
class BadCheckSum < Error; end
class TooLongFileName < Error; end
class FormatError < Error class FormatError < Error
attr_reader :path attr_reader :path
@ -26,57 +64,489 @@ module Gem::Package
end end
class PathError < Error
def initialize destination, destination_dir
super "installing into parent path %s of %s is not allowed" %
[destination, destination_dir]
end
end
class NonSeekableIO < Error; end
class TooLongFileName < Error; end
## ##
# Raised when a tar file is corrupt # Raised when a tar file is corrupt
class TarInvalidError < Error; end class TarInvalidError < Error; end
# FIX: zenspider said: does it really take an IO? attr_accessor :build_time # :nodoc:
# passed to a method called open?!? that seems stupid.
def self.open(io, mode = "r", signer = nil, &block)
tar_type = case mode
when 'r' then TarInput
when 'w' then TarOutput
else
raise "Unknown Package open mode"
end
tar_type.open(io, signer, &block) ##
# Checksums for the contents of the package
attr_reader :checksums
##
# The files in this package. This is not the contents of the gem, just the
# files in the top-level container.
attr_reader :files
##
# The security policy used for verifying the contents of this package.
attr_accessor :security_policy
##
# Sets the Gem::Specification to use to build this package.
attr_writer :spec
def self.build spec, skip_validation=false
gem_file = spec.file_name
package = new gem_file
package.spec = spec
package.build skip_validation
gem_file
end end
def self.pack(src, destname, signer = nil) ##
TarOutput.open(destname, signer) do |outp| # Creates a new Gem::Package for the file at +gem+.
dir_class.chdir(src) do #
outp.metadata = (file_class.read("RPA/metadata") rescue nil) # If +gem+ is an existing file in the old format a Gem::Package::Old will be
find_class.find('.') do |entry| # returned.
case
when file_class.file?(entry) def self.new gem
entry.sub!(%r{\./}, "") return super unless Gem::Package == self
next if entry =~ /\ARPA\// return super unless File.exist? gem
stat = File.stat(entry)
outp.add_file_simple(entry, stat.mode, stat.size) do |os| start = File.read gem, 20
file_class.open(entry, "rb") do |f|
os.write(f.read(4096)) until f.eof? return super unless start
end return super unless start.include? 'MD5SUM ='
end
when file_class.dir?(entry) Gem::Package::Old.new gem
entry.sub!(%r{\./}, "") end
next if entry == "RPA"
outp.mkdir(entry, file_class.stat(entry).mode) ##
else # Creates a new package that will read or write to the file +gem+.
raise "Don't know how to pack this yet!"
end def initialize gem # :notnew:
@gem = gem
@build_time = Time.now
@checksums = {}
@contents = nil
@digests = Hash.new { |h, algorithm| h[algorithm] = {} }
@files = nil
@security_policy = nil
@signatures = {}
@signer = nil
@spec = nil
end
##
# Adds a checksum for each entry in the gem to checksums.yaml.gz.
def add_checksums tar
Gem.load_yaml
checksums_by_algorithm = Hash.new { |h, algorithm| h[algorithm] = {} }
@checksums.each do |name, digests|
digests.each do |algorithm, digest|
checksums_by_algorithm[algorithm][name] = digest.hexdigest
end
end
tar.add_file_signed 'checksums.yaml.gz', 0444, @signer do |io|
gzip_to io do |gz_io|
YAML.dump checksums_by_algorithm, gz_io
end
end
end
##
# Adds the files listed in the packages's Gem::Specification to data.tar.gz
# and adds this file to the +tar+.
def add_contents tar # :nodoc:
digests = tar.add_file_signed 'data.tar.gz', 0444, @signer do |io|
gzip_to io do |gz_io|
Gem::Package::TarWriter.new gz_io do |data_tar|
add_files data_tar
end
end
end
@checksums['data.tar.gz'] = digests
end
##
# Adds files included the package's Gem::Specification to the +tar+ file
def add_files tar # :nodoc:
@spec.files.each do |file|
stat = File.stat file
tar.add_file_simple file, stat.mode, stat.size do |dst_io|
open file, 'rb' do |src_io|
dst_io.write src_io.read 16384 until src_io.eof?
end end
end end
end end
end end
##
# Adds the package's Gem::Specification to the +tar+ file
def add_metadata tar # :nodoc:
digests = tar.add_file_signed 'metadata.gz', 0444, @signer do |io|
gzip_to io do |gz_io|
gz_io.write @spec.to_yaml
end
end
@checksums['metadata.gz'] = digests
end
##
# Builds this package based on the specification set by #spec=
def build skip_validation = false
require 'rubygems/security'
@spec.validate unless skip_validation
@spec.mark_version
setup_signer
open @gem, 'wb' do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
add_metadata gem
add_contents gem
add_checksums gem
end
end
say <<-EOM
Successfully built RubyGem
Name: #{@spec.name}
Version: #{@spec.version}
File: #{File.basename @spec.cache_file}
EOM
ensure
@signer = nil
end
##
# A list of file names contained in this gem
def contents
return @contents if @contents
verify unless @spec
@contents = []
open @gem, 'rb' do |io|
gem_tar = Gem::Package::TarReader.new io
gem_tar.each do |entry|
next unless entry.full_name == 'data.tar.gz'
open_tar_gz entry do |pkg_tar|
pkg_tar.each do |contents_entry|
@contents << contents_entry.full_name
end
end
return @contents
end
end
end
##
# Creates a digest of the TarEntry +entry+ from the digest algorithm set by
# the security policy.
def digest entry # :nodoc:
return unless @checksums
@checksums.each_key do |algorithm|
digester = OpenSSL::Digest.new algorithm
digester << entry.read(16384) until entry.eof?
entry.rewind
@digests[algorithm][entry.full_name] = digester
end
@digests
end
##
# Extracts the files in this package into +destination_dir+
def extract_files destination_dir
verify unless @spec
FileUtils.mkdir_p destination_dir
open @gem, 'rb' do |io|
reader = Gem::Package::TarReader.new io
reader.each do |entry|
next unless entry.full_name == 'data.tar.gz'
extract_tar_gz entry, destination_dir
return # ignore further entries
end
end
end
##
# Extracts all the files in the gzipped tar archive +io+ into
# +destination_dir+.
#
# If an entry in the archive contains a relative path above
# +destination_dir+ or an absolute path is encountered an exception is
# raised.
def extract_tar_gz io, destination_dir # :nodoc:
open_tar_gz io do |tar|
tar.each do |entry|
destination = install_location entry.full_name, destination_dir
FileUtils.rm_rf destination
FileUtils.mkdir_p File.dirname destination
open destination, 'wb', entry.header.mode do |out|
out.write entry.read
out.fsync rescue nil # for filesystems without fsync(2)
end
say destination if Gem.configuration.really_verbose
end
end
end
##
# Gzips content written to +gz_io+ to +io+.
#--
# Also sets the gzip modification time to the package build time to ease
# testing.
def gzip_to io # :yields: gz_io
gz_io = Zlib::GzipWriter.new io, Zlib::BEST_COMPRESSION
gz_io.mtime = @build_time
yield gz_io
ensure
gz_io.close
end
##
# Returns the full path for installing +filename+.
#
# If +filename+ is not inside +destination_dir+ an exception is raised.
def install_location filename, destination_dir # :nodoc:
raise Gem::Package::PathError.new(filename, destination_dir) if
filename.start_with? '/'
destination = File.join destination_dir, filename
destination = File.expand_path destination
raise Gem::Package::PathError.new(destination, destination_dir) unless
destination.start_with? destination_dir
destination.untaint
destination
end
##
# Loads a Gem::Specification from the TarEntry +entry+
def load_spec entry # :nodoc:
case entry.full_name
when 'metadata' then
@spec = Gem::Specification.from_yaml entry.read
when 'metadata.gz' then
args = [entry]
args << { :external_encoding => Encoding::UTF_8 } if
Object.const_defined? :Encoding
Zlib::GzipReader.wrap(*args) do |gzio|
@spec = Gem::Specification.from_yaml gzio.read
end
end
end
##
# Opens +io+ as a gzipped tar archive
def open_tar_gz io # :nodoc:
Zlib::GzipReader.wrap io do |gzio|
tar = Gem::Package::TarReader.new gzio
yield tar
end
end
##
# Reads and loads checksums.yaml.gz from the tar file +gem+
def read_checksums gem
Gem.load_yaml
@checksums = gem.seek 'checksums.yaml.gz' do |entry|
Zlib::GzipReader.wrap entry do |gz_io|
YAML.load gz_io.read
end
end
end
##
# Prepares the gem for signing and checksum generation. If a signing
# certificate and key are not present only checksum generation is set up.
def setup_signer
if @spec.signing_key then
@signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain
@spec.signing_key = nil
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
else
@signer = Gem::Security::Signer.new nil, nil
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_pem } if
@signer.cert_chain
end
end
##
# The spec for this gem.
#
# If this is a package for a built gem the spec is loaded from the
# gem and returned. If this is a package for a gem being built the provided
# spec is returned.
def spec
verify unless @spec
@spec
end
##
# Verifies that this gem:
#
# * Contains a valid gem specification
# * Contains a contents archive
# * The contents archive is not corrupt
#
# After verification the gem specification from the gem is available from
# #spec
def verify
@files = []
@spec = nil
open @gem, 'rb' do |io|
Gem::Package::TarReader.new io do |reader|
read_checksums reader
verify_files reader
end
end
verify_checksums @digests, @checksums
@security_policy.verify_signatures @spec, @digests, @signatures if
@security_policy
true
rescue Errno::ENOENT => e
raise Gem::Package::FormatError.new e.message
rescue Gem::Package::TarInvalidError => e
raise Gem::Package::FormatError.new e.message, @gem
end
##
# Verifies the +checksums+ against the +digests+. This check is not
# cryptographically secure. Missing checksums are ignored.
def verify_checksums digests, checksums # :nodoc:
return unless checksums
checksums.sort.each do |algorithm, gem_digests|
gem_digests.sort.each do |file_name, gem_hexdigest|
computed_digest = digests[algorithm][file_name]
unless computed_digest.hexdigest == gem_hexdigest then
raise Gem::Package::FormatError.new \
"#{algorithm} checksum mismatch for #{file_name}", @gem
end
end
end
end
##
# Verifies the files of the +gem+
def verify_files gem
gem.each do |entry|
file_name = entry.full_name
@files << file_name
case file_name
when /\.sig$/ then
@signatures[$`] = entry.read if @security_policy
next
when 'checksums.yaml.gz' then
next # already handled
else
digest entry
end
case file_name
when /^metadata(.gz)?$/ then
load_spec entry
when 'data.tar.gz' then
verify_gz entry
end
end
unless @spec then
raise Gem::Package::FormatError.new 'package metadata is missing', @gem
end
unless @files.include? 'data.tar.gz' then
raise Gem::Package::FormatError.new \
'package content (data.tar.gz) is missing', @gem
end
end
##
# Verifies that +entry+ is a valid gzipped file.
def verify_gz entry # :nodoc:
Zlib::GzipReader.wrap entry do |gzio|
gzio.read 16384 until gzio.eof? # gzip checksum verification
end
rescue Zlib::GzipFile::Error => e
raise Gem::Package::FormatError.new(e.message, entry.full_name)
end
end end
require 'rubygems/package/f_sync_dir' require 'rubygems/package/digest_io'
require 'rubygems/package/old'
require 'rubygems/package/tar_header' require 'rubygems/package/tar_header'
require 'rubygems/package/tar_input'
require 'rubygems/package/tar_output'
require 'rubygems/package/tar_reader' require 'rubygems/package/tar_reader'
require 'rubygems/package/tar_reader/entry' require 'rubygems/package/tar_reader/entry'
require 'rubygems/package/tar_writer' require 'rubygems/package/tar_writer'

Просмотреть файл

@ -0,0 +1,64 @@
##
# IO wrapper that creates digests of contents written to the IO it wraps.
class Gem::Package::DigestIO
##
# Collected digests for wrapped writes.
#
# {
# 'SHA1' => #<OpenSSL::Digest: [...]>,
# 'SHA512' => #<OpenSSL::Digest: [...]>,
# }
attr_reader :digests
##
# Wraps +io+ and updates digest for each of the digest algorithms in
# the +digests+ Hash. Returns the digests hash. Example:
#
# io = StringIO.new
# digests = {
# 'SHA1' => OpenSSL::Digest.new('SHA1'),
# 'SHA512' => OpenSSL::Digest.new('SHA512'),
# }
#
# Gem::Package::DigestIO.wrap io, digests do |digest_io|
# digest_io.write "hello"
# end
#
# digests['SHA1'].hexdigest #=> "aaf4c61d[...]"
# digests['SHA512'].hexdigest #=> "9b71d224[...]"
def self.wrap io, digests
digest_io = new io, digests
yield digest_io
return digests
end
##
# Creates a new DigestIO instance. Using ::wrap is recommended, see the
# ::wrap documentation for documentation of +io+ and +digests+.
def initialize io, digests
@io = io
@digests = digests
end
##
# Writes +data+ to the underlying IO and updates the digests
def write data
result = @io.write data
@digests.each do |_, digest|
digest << data
end
result
end
end

Просмотреть файл

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
#++
module Gem::Package::FSyncDir
private
##
# make sure this hits the disc
def fsync_dir(dirname)
dir = open dirname, 'r'
dir.fsync
rescue # ignore IOError if it's an unpatched (old) Ruby
ensure
dir.close if dir rescue nil
end
end

147
lib/rubygems/package/old.rb Normal file
Просмотреть файл

@ -0,0 +1,147 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
##
# The format class knows the guts of the ancient .gem file format and provides
# the capability to read such ancient gems.
#
# Please pretend this doesn't exist.
class Gem::Package::Old < Gem::Package
undef_method :spec=
##
# Creates a new old-format package reader for +gem+. Old-format packages
# cannot be written.
def initialize gem
require 'fileutils'
require 'zlib'
Gem.load_yaml
@gem = gem
@contents = nil
@spec = nil
end
##
# A list of file names contained in this gem
def contents
return @contents if @contents
open @gem, 'rb' do |io|
read_until_dashes io # spec
header = file_list io
@contents = header.map { |file| file['path'] }
end
end
##
# Extracts the files in this package into +destination_dir+
def extract_files destination_dir
errstr = "Error reading files from gem"
open @gem, 'rb' do |io|
read_until_dashes io # spec
header = file_list io
raise Gem::Exception, errstr unless header
header.each do |entry|
full_name = entry['path']
destination = install_location full_name, destination_dir
file_data = ''
read_until_dashes io do |line|
file_data << line
end
file_data = file_data.strip.unpack("m")[0]
file_data = Zlib::Inflate.inflate file_data
raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if
file_data.length != entry['size'].to_i
FileUtils.rm_rf destination
FileUtils.mkdir_p File.dirname destination
open destination, 'wb', entry['mode'] do |out|
out.write file_data
end
say destination if Gem.configuration.really_verbose
end
end
rescue Zlib::DataError
raise Gem::Exception, errstr
end
##
# Reads the file list section from the old-format gem +io+
def file_list io # :nodoc:
header = ''
read_until_dashes io do |line|
header << line
end
YAML.load header
end
##
# Reads lines until a "---" separator is found
def read_until_dashes io # :nodoc:
while (line = io.gets) && line.chomp.strip != "---" do
yield line if block_given?
end
end
##
# Skips the Ruby self-install header in +io+.
def skip_ruby io # :nodoc:
loop do
line = io.gets
return if line.chomp == '__END__'
break unless line
end
raise Gem::Exception, "Failed to find end of ruby script while reading gem"
end
##
# The specification for this gem
def spec
return @spec if @spec
yaml = ''
open @gem, 'rb' do |io|
skip_ruby io
read_until_dashes io do |line|
yaml << line
end
end
@spec = Gem::Specification.from_yaml yaml
rescue YAML::SyntaxError => e
raise Gem::Exception, "Failed to parse gem specification out of gem file"
rescue ArgumentError => e
raise Gem::Exception, "Failed to parse gem specification out of gem file"
end
end

Просмотреть файл

@ -102,61 +102,24 @@ class Gem::Package::TarHeader
fields = header.unpack UNPACK_FORMAT fields = header.unpack UNPACK_FORMAT
name = fields.shift new :name => fields.shift,
mode = fields.shift.oct :mode => fields.shift.oct,
uid = fields.shift.oct :uid => fields.shift.oct,
gid = fields.shift.oct :gid => fields.shift.oct,
size = fields.shift.oct :size => fields.shift.oct,
mtime = fields.shift.oct :mtime => fields.shift.oct,
checksum = fields.shift.oct :checksum => fields.shift.oct,
typeflag = fields.shift :typeflag => fields.shift,
linkname = fields.shift :linkname => fields.shift,
magic = fields.shift :magic => fields.shift,
version = fields.shift.oct :version => fields.shift.oct,
uname = fields.shift :uname => fields.shift,
gname = fields.shift :gname => fields.shift,
devmajor = fields.shift.oct :devmajor => fields.shift.oct,
devminor = fields.shift.oct :devminor => fields.shift.oct,
prefix = fields.shift :prefix => fields.shift,
new :name => name, :empty => empty
:mode => mode,
:uid => uid,
:gid => gid,
:size => size,
:mtime => mtime,
:checksum => checksum,
:typeflag => typeflag,
:linkname => linkname,
:magic => magic,
:version => version,
:uname => uname,
:gname => gname,
:devmajor => devmajor,
:devminor => devminor,
:prefix => prefix,
:empty => empty
# HACK unfactor for Rubinius
#new :name => fields.shift,
# :mode => fields.shift.oct,
# :uid => fields.shift.oct,
# :gid => fields.shift.oct,
# :size => fields.shift.oct,
# :mtime => fields.shift.oct,
# :checksum => fields.shift.oct,
# :typeflag => fields.shift,
# :linkname => fields.shift,
# :magic => fields.shift,
# :version => fields.shift.oct,
# :uname => fields.shift,
# :gname => fields.shift,
# :devmajor => fields.shift.oct,
# :devminor => fields.shift.oct,
# :prefix => fields.shift,
# :empty => empty
end end
## ##

Просмотреть файл

@ -1,235 +0,0 @@
# -*- coding: iso-8859-1 -*-
#++
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
#--
require 'zlib'
Gem.load_yaml
class Gem::Package::TarInput
include Gem::Package::FSyncDir
include Enumerable
attr_reader :metadata
private_class_method :new
def self.open(io, security_policy = nil, &block)
is = new io, security_policy
yield is
ensure
is.close if is
end
def initialize(io, security_policy = nil)
@io = io
@tarreader = Gem::Package::TarReader.new @io
has_meta = false
data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
@tarreader.each do |entry|
case entry.full_name
when "metadata"
@metadata = load_gemspec entry.read
has_meta = true
when "metadata.gz"
begin
# if we have a security_policy, then pre-read the metadata file
# and calculate it's digest
sio = nil
if security_policy
Gem.ensure_ssl_available
sio = StringIO.new(entry.read)
meta_dgst = dgst_algo.digest(sio.string)
sio.rewind
end
# Ruby 1.8 doesn't have encoding and YAML is UTF-8
args = [sio || entry]
args << { :external_encoding => Encoding::UTF_8 } if
Object.const_defined?(:Encoding)
gzis = Zlib::GzipReader.new(*args)
# YAML wants an instance of IO
@metadata = load_gemspec(gzis)
has_meta = true
ensure
gzis.close unless gzis.nil?
end
when 'metadata.gz.sig'
meta_sig = entry.read
when 'data.tar.gz.sig'
data_sig = entry.read
when 'data.tar.gz'
if security_policy
Gem.ensure_ssl_available
data_dgst = dgst_algo.digest(entry.read)
end
end
end
if security_policy then
Gem.ensure_ssl_available
# map trust policy from string to actual class (or a serialized YAML
# file, if that exists)
if String === security_policy then
if Gem::Security::Policies.key? security_policy then
# load one of the pre-defined security policies
security_policy = Gem::Security::Policies[security_policy]
elsif File.exist? security_policy then
# FIXME: this doesn't work yet
security_policy = YAML.load File.read(security_policy)
else
raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
end
end
if data_sig && data_dgst && meta_sig && meta_dgst then
# the user has a trust policy, and we have a signed gem
# file, so use the trust policy to verify the gem signature
begin
security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
rescue Exception => e
raise "Couldn't verify data signature: #{e}"
end
begin
security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
rescue Exception => e
raise "Couldn't verify metadata signature: #{e}"
end
elsif security_policy.only_signed
raise Gem::Exception, "Unsigned gem"
else
# FIXME: should display warning here (trust policy, but
# either unsigned or badly signed gem file)
end
end
@tarreader.rewind
unless has_meta then
path = io.path if io.respond_to? :path
error = Gem::Package::FormatError.new 'no metadata found', path
raise error
end
end
def close
@io.close
@tarreader.close
end
def each(&block)
@tarreader.each do |entry|
next unless entry.full_name == "data.tar.gz"
is = zipped_stream entry
begin
Gem::Package::TarReader.new is do |inner|
inner.each(&block)
end
ensure
is.close if is
end
end
@tarreader.rewind
end
def extract_entry(destdir, entry, expected_md5sum = nil)
if entry.directory? then
dest = File.join destdir, entry.full_name
if File.directory? dest then
FileUtils.chmod entry.header.mode, dest, :verbose => false
else
FileUtils.mkdir_p dest, :mode => entry.header.mode, :verbose => false
end
fsync_dir dest
fsync_dir File.join(dest, "..")
return
end
# it's a file
md5 = Digest::MD5.new if expected_md5sum
destdir = File.join destdir, File.dirname(entry.full_name)
FileUtils.mkdir_p destdir, :mode => 0755, :verbose => false
destfile = File.join destdir, File.basename(entry.full_name)
FileUtils.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
open destfile, "wb", entry.header.mode do |os|
loop do
data = entry.read 4096
break unless data
# HACK shouldn't we check the MD5 before writing to disk?
md5 << data if expected_md5sum
os.write(data)
end
os.fsync
end
FileUtils.chmod entry.header.mode, destfile, :verbose => false
fsync_dir File.dirname(destfile)
fsync_dir File.join(File.dirname(destfile), "..")
if expected_md5sum && expected_md5sum != md5.hexdigest then
raise Gem::Package::BadCheckSum
end
end
# Attempt to YAML-load a gemspec from the given _io_ parameter. Return
# nil if it fails.
def load_gemspec(io)
Gem::Specification.from_yaml io
rescue Gem::Exception
nil
end
##
# Return an IO stream for the zipped entry.
#
# NOTE: Originally this method used two approaches, Return a GZipReader
# directly, or read the GZipReader into a string and return a StringIO on
# the string. The string IO approach was used for versions of ZLib before
# 1.2.1 to avoid buffer errors on windows machines. Then we found that
# errors happened with 1.2.1 as well, so we changed the condition. Then
# we discovered errors occurred with versions as late as 1.2.3. At this
# point (after some benchmarking to show we weren't seriously crippling
# the unpacking speed) we threw our hands in the air and declared that
# this method would use the String IO approach on all platforms at all
# times. And that's the way it is.
#
# Revisited. Here's the beginning of the long story.
# http://osdir.com/ml/lang.ruby.gems.devel/2007-06/msg00045.html
#
# StringIO wraping has never worked as a workaround by definition. Skipping
# initial 10 bytes and passing -MAX_WBITS to Zlib::Inflate luckily works as
# gzip reader, but it only works if the GZip header is 10 bytes long (see
# below) and it does not check inflated stream consistency (CRC value in the
# Gzip trailer.)
#
# RubyGems generated Gzip Header: 10 bytes
# magic(2) + method(1) + flag(1) + mtime(4) + exflag(1) + os(1) +
# orig_name(0) + comment(0)
#
# Ideally, it must return a GZipReader without meaningless buffering. We
# have lots of CRuby committers around so let's fix windows build when we
# received an error.
def zipped_stream(entry)
Zlib::GzipReader.new entry
end
end

Просмотреть файл

@ -1,146 +0,0 @@
# -*- coding: utf-8 -*-
#--
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
# See LICENSE.txt for additional licensing information.
#++
##
# TarOutput is a wrapper to TarWriter that builds gem-format tar file.
#
# Gem-format tar files contain the following files:
# [data.tar.gz] A gzipped tar file containing the files that compose the gem
# which will be extracted into the gem/ dir on installation.
# [metadata.gz] A YAML format Gem::Specification.
# [data.tar.gz.sig] A signature for the gem's data.tar.gz.
# [metadata.gz.sig] A signature for the gem's metadata.gz.
#
# See TarOutput::open for usage details.
class Gem::Package::TarOutput
##
# Creates a new TarOutput which will yield a TarWriter object for the
# data.tar.gz portion of a gem-format tar file.
#
# See #initialize for details on +io+ and +signer+.
#
# See #add_gem_contents for details on adding metadata to the tar file.
def self.open(io, signer = nil, &block) # :yield: data_tar_writer
tar_outputter = new io, signer
tar_outputter.add_gem_contents(&block)
tar_outputter.add_metadata
tar_outputter.add_signatures
ensure
tar_outputter.close
end
##
# Creates a new TarOutput that will write a gem-format tar file to +io+. If
# +signer+ is given, the data.tar.gz and metadata.gz will be signed and
# the signatures will be added to the tar file.
def initialize(io, signer)
@io = io
@signer = signer
@tar_writer = Gem::Package::TarWriter.new @io
@metadata = nil
@data_signature = nil
@meta_signature = nil
end
##
# Yields a TarWriter for the data.tar.gz inside a gem-format tar file.
# The yielded TarWriter has been extended with a #metadata= method for
# attaching a YAML format Gem::Specification which will be written by
# add_metadata.
def add_gem_contents
@tar_writer.add_file "data.tar.gz", 0644 do |inner|
sio = @signer ? StringIO.new : nil
Zlib::GzipWriter.wrap(sio || inner) do |os|
Gem::Package::TarWriter.new os do |data_tar_writer|
# :stopdoc:
def data_tar_writer.metadata() @metadata end
def data_tar_writer.metadata=(metadata) @metadata = metadata end
# :startdoc:
yield data_tar_writer
@metadata = data_tar_writer.metadata
end
end
# if we have a signing key, then sign the data
# digest and return the signature
if @signer then
require 'rubygems/security'
digest = Gem::Security::OPT[:dgst_algo].digest sio.string
@data_signature = @signer.sign digest
inner.write sio.string
end
end
self
end
##
# Adds metadata.gz to the gem-format tar file which was saved from a
# previous #add_gem_contents call.
def add_metadata
return if @metadata.nil?
@tar_writer.add_file "metadata.gz", 0644 do |io|
begin
sio = @signer ? StringIO.new : nil
gzos = Zlib::GzipWriter.new(sio || io)
gzos.write @metadata
ensure
gzos.flush
gzos.finish
# if we have a signing key, then sign the metadata digest and return
# the signature
if @signer then
require 'rubygems/security'
digest = Gem::Security::OPT[:dgst_algo].digest sio.string
@meta_signature = @signer.sign digest
io.write sio.string
end
end
end
end
##
# Adds data.tar.gz.sig and metadata.gz.sig to the gem-format tar files if
# a Gem::Security::Signer was sent to initialize.
def add_signatures
if @data_signature then
@tar_writer.add_file 'data.tar.gz.sig', 0644 do |io|
io.write @data_signature
end
end
if @meta_signature then
@tar_writer.add_file 'metadata.gz.sig', 0644 do |io|
io.write @meta_signature
end
end
end
##
# Closes the TarOutput.
def close
@tar_writer.close
end
end

Просмотреть файл

@ -9,7 +9,7 @@
class Gem::Package::TarReader class Gem::Package::TarReader
include Gem::Package include Enumerable
## ##
# Raised if the tar IO is not seekable # Raised if the tar IO is not seekable
@ -52,9 +52,9 @@ class Gem::Package::TarReader
# Iterates over files in the tarball yielding each entry # Iterates over files in the tarball yielding each entry
def each def each
loop do return enum_for __method__ unless block_given?
return if @io.eof?
until @io.eof? do
header = Gem::Package::TarHeader.from @io header = Gem::Package::TarHeader.from @io
return if header.empty? return if header.empty?
@ -100,6 +100,23 @@ class Gem::Package::TarReader
end end
end end
##
# Seeks through the tar file until it finds the +entry+ with +name+ and
# yields it. Rewinds the tar file to the beginning when the block
# terminates.
def seek name # :yields: entry
found = find do |entry|
entry.full_name == name
end
return unless found
return yield found
ensure
rewind
end
end end
require 'rubygems/package/tar_reader/entry' require 'rubygems/package/tar_reader/entry'

Просмотреть файл

@ -40,12 +40,12 @@ class Gem::Package::TarWriter
# number of bytes will be more than #limit # number of bytes will be more than #limit
def write(data) def write(data)
if data.size + @written > @limit if data.bytesize + @written > @limit
raise FileOverflow, "You tried to feed more data than fits in the file." raise FileOverflow, "You tried to feed more data than fits in the file."
end end
@io.write data @io.write data
@written += data.size @written += data.bytesize
data.size data.bytesize
end end
end end
@ -129,6 +129,62 @@ class Gem::Package::TarWriter
self self
end end
##
# Adds +name+ with permissions +mode+ to the tar, yielding +io+ for writing
# the file. The +digest_algorithm+ is written to a read-only +name+.sum
# file following the given file contents containing the digest name and
# hexdigest separated by a tab.
#
# The created digest object is returned.
def add_file_digest name, mode, digest_algorithms # :yields: io
digests = digest_algorithms.map do |digest_algorithm|
digest = digest_algorithm.new
[digest.name, digest]
end
digests = Hash[*digests.flatten]
add_file name, mode do |io|
Gem::Package::DigestIO.wrap io, digests do |digest_io|
yield digest_io
end
end
digests
end
##
# Adds +name+ with permissions +mode+ to the tar, yielding +io+ for writing
# the file. The +signer+ is used to add a digest file using its
# digest_algorithm per add_file_digest and a cryptographic signature in
# +name+.sig. If the signer has no key only the checksum file is added.
#
# Returns the digest.
def add_file_signed name, mode, signer
digest_algorithms = [
signer.digest_algorithm,
OpenSSL::Digest::SHA512,
].uniq
digests = add_file_digest name, mode, digest_algorithms do |io|
yield io
end
signature_digest = digests.values.find do |digest|
digest.name == signer.digest_name
end
signature = signer.sign signature_digest.digest
add_file_simple "#{name}.sig", 0444, signature.length do |io|
io.write signature
end if signature
digests
end
## ##
# Add file +name+ with permissions +mode+ +size+ bytes long. Yields an IO # Add file +name+ with permissions +mode+ +size+ bytes long. Yields an IO
# to write the file to. # to write the file to.
@ -211,9 +267,9 @@ class Gem::Package::TarWriter
# Splits +name+ into a name and prefix that can fit in the TarHeader # Splits +name+ into a name and prefix that can fit in the TarHeader
def split_name(name) # :nodoc: def split_name(name) # :nodoc:
raise Gem::Package::TooLongFileName if name.size > 256 raise Gem::Package::TooLongFileName if name.bytesize > 256
if name.size <= 100 then if name.bytesize <= 100 then
prefix = "" prefix = ""
else else
parts = name.split(/\//) parts = name.split(/\//)
@ -222,14 +278,14 @@ class Gem::Package::TarWriter
loop do loop do
nxt = parts.pop nxt = parts.pop
break if newname.size + 1 + nxt.size > 100 break if newname.bytesize + 1 + nxt.bytesize > 100
newname = nxt + "/" + newname newname = nxt + "/" + newname
end end
prefix = (parts + [nxt]).join "/" prefix = (parts + [nxt]).join "/"
name = newname name = newname
if name.size > 100 or prefix.size > 155 then if name.bytesize > 100 or prefix.bytesize > 155 then
raise Gem::Package::TooLongFileName raise Gem::Package::TooLongFileName
end end
end end

Просмотреть файл

@ -20,6 +20,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require 'rubygems' require 'rubygems'
require 'rubygems/package'
begin begin
gem 'rake' gem 'rake'
rescue Gem::LoadError rescue Gem::LoadError
@ -43,13 +44,10 @@ require 'rake/packagetask'
# require 'rubygems/package_task' # require 'rubygems/package_task'
# #
# spec = Gem::Specification.new do |s| # spec = Gem::Specification.new do |s|
# s.platform = Gem::Platform::RUBY
# s.summary = "Ruby based make-like utility." # s.summary = "Ruby based make-like utility."
# s.name = 'rake' # s.name = 'rake'
# s.version = PKG_VERSION # s.version = PKG_VERSION
# s.requirements << 'none' # s.requirements << 'none'
# s.require_path = 'lib'
# s.autorequire = 'rake'
# s.files = PKG_FILES # s.files = PKG_FILES
# s.description = <<-EOF # s.description = <<-EOF
# Rake is a Make-like program implemented in Ruby. Tasks # Rake is a Make-like program implemented in Ruby. Tasks
@ -113,7 +111,8 @@ class Gem::PackageTask < Rake::PackageTask
file gem_path => [package_dir, gem_dir] + @gem_spec.files do file gem_path => [package_dir, gem_dir] + @gem_spec.files do
chdir(gem_dir) do chdir(gem_dir) do
when_writing "Creating #{gem_spec.file_name}" do when_writing "Creating #{gem_spec.file_name}" do
Gem::Builder.new(gem_spec).build Gem::Package.build gem_spec
verbose trace do verbose trace do
mv gem_file, '..' mv gem_file, '..'
end end

Просмотреть файл

@ -1,4 +1,5 @@
## ##
#
# Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings # Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings
# to the rest of RubyGems. # to the rest of RubyGems.
# #
@ -42,16 +43,18 @@ class Gem::PathSupport
# Set the Gem search path (as reported by Gem.path). # Set the Gem search path (as reported by Gem.path).
def path=(gpaths) def path=(gpaths)
gem_path = [@home] # FIX: it should be [home, *path], not [*path, home]
gem_path = []
# FIX: I can't tell wtf this is doing. # FIX: I can't tell wtf this is doing.
gpaths ||= (ENV['GEM_PATH'] || "").empty? ? nil : ENV["GEM_PATH"] gpaths ||= (ENV['GEM_PATH'] || "").empty? ? nil : ENV["GEM_PATH"]
if gpaths then if gpaths
if gpaths.kind_of?(Array) then if gpaths.kind_of?(Array)
gem_path.push(*gpaths) gem_path = gpaths.dup
else else
gem_path.push(*gpaths.split(File::PATH_SEPARATOR)) gem_path = gpaths.split(File::PATH_SEPARATOR)
end end
if File::ALT_SEPARATOR then if File::ALT_SEPARATOR then
@ -59,10 +62,14 @@ class Gem::PathSupport
this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR
end end
end end
else
gem_path.push(*Gem.default_path)
gem_path << APPLE_GEM_HOME if defined?(APPLE_GEM_HOME) gem_path << @home
else
gem_path = Gem.default_path + [@home]
if defined?(APPLE_GEM_HOME)
gem_path << APPLE_GEM_HOME
end
end end
@path = gem_path.uniq @path = gem_path.uniq

Просмотреть файл

@ -21,7 +21,8 @@ class Gem::Platform
def self.match(platform) def self.match(platform)
Gem.platforms.any? do |local_platform| Gem.platforms.any? do |local_platform|
platform.nil? or local_platform == platform or platform.nil? or
local_platform == platform or
(local_platform != Gem::Platform::RUBY and local_platform =~ platform) (local_platform != Gem::Platform::RUBY and local_platform =~ platform)
end end
end end
@ -65,28 +66,28 @@ class Gem::Platform
@cpu, os = nil, cpu if os.nil? # legacy jruby @cpu, os = nil, cpu if os.nil? # legacy jruby
@os, @version = case os @os, @version = case os
when /aix(\d+)/ then [ 'aix', $1 ] when /aix(\d+)?/ then [ 'aix', $1 ]
when /cygwin/ then [ 'cygwin', nil ] when /cygwin/ then [ 'cygwin', nil ]
when /darwin(\d+)?/ then [ 'darwin', $1 ] when /darwin(\d+)?/ then [ 'darwin', $1 ]
when /^macruby$/ then [ 'macruby', nil ] when /^macruby$/ then [ 'macruby', nil ]
when /freebsd(\d+)/ then [ 'freebsd', $1 ] when /freebsd(\d+)?/ then [ 'freebsd', $1 ]
when /hpux(\d+)/ then [ 'hpux', $1 ] when /hpux(\d+)?/ then [ 'hpux', $1 ]
when /^java$/, /^jruby$/ then [ 'java', nil ] when /^java$/, /^jruby$/ then [ 'java', nil ]
when /^java([\d.]*)/ then [ 'java', $1 ] when /^java([\d.]*)/ then [ 'java', $1 ]
when /^dotnet$/ then [ 'dotnet', nil ] when /^dotnet$/ then [ 'dotnet', nil ]
when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ] when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
when /linux/ then [ 'linux', $1 ] when /linux/ then [ 'linux', $1 ]
when /mingw32/ then [ 'mingw32', nil ] when /mingw32/ then [ 'mingw32', nil ]
when /(mswin\d+)(\_(\d+))?/ then when /(mswin\d+)(\_(\d+))?/ then
os, version = $1, $3 os, version = $1, $3
@cpu = 'x86' if @cpu.nil? and os =~ /32$/ @cpu = 'x86' if @cpu.nil? and os =~ /32$/
[os, version] [os, version]
when /(netbsd[a-z]*)(\d+)/ then [ $1, $2 ] when /netbsdelf/ then [ 'netbsdelf', nil ]
when /openbsd(\d+\.\d+)/ then [ 'openbsd', $1 ] when /openbsd(\d+\.\d+)?/ then [ 'openbsd', $1 ]
when /solaris(\d+\.\d+)/ then [ 'solaris', $1 ] when /solaris(\d+\.\d+)?/ then [ 'solaris', $1 ]
# test # test
when /^(\w+_platform)(\d+)/ then [ $1, $2 ] when /^(\w+_platform)(\d+)?/ then [ $1, $2 ]
else [ 'unknown', nil ] else [ 'unknown', nil ]
end end
when Gem::Platform then when Gem::Platform then
@cpu = arch.cpu @cpu = arch.cpu
@ -109,10 +110,6 @@ class Gem::Platform
to_a.compact.join '-' to_a.compact.join '-'
end end
def empty?
to_s.empty?
end
## ##
# Is +other+ equal to this platform? Two platforms are equal if they have # Is +other+ equal to this platform? Two platforms are equal if they have
# the same CPU, OS and version. # the same CPU, OS and version.
@ -186,9 +183,5 @@ class Gem::Platform
# This will be replaced with Gem::Platform::local. # This will be replaced with Gem::Platform::local.
CURRENT = 'current' CURRENT = 'current'
extend Gem::Deprecate
deprecate :empty?, :none, 2011, 11
end end

316
lib/rubygems/rdoc.rb Normal file
Просмотреть файл

@ -0,0 +1,316 @@
require 'rubygems'
require 'rubygems/user_interaction'
require 'fileutils'
begin
gem 'rdoc'
rescue Gem::LoadError
# swallow
else
# This will force any deps that 'rdoc' might have
# (such as json) that are ambigious to be activated, which
# is important because we end up using Specification.reset
# and we don't want the warning it pops out.
Gem.finish_resolve
end
loaded_hook = false
begin
require 'rdoc/rubygems_hook'
loaded_hook = true
module Gem
RDoc = RDoc::RubygemsHook
end
rescue LoadError
end
##
# Gem::RDoc provides methods to generate RDoc and ri data for installed gems.
# It works for RDoc 1.0.1 (in Ruby 1.8) up to RDoc 3.6.
#
# This implementation is considered obsolete. The RDoc project is the
# appropriate location to find this functionality. This file provides the
# hooks to load RDoc generation code from the "rdoc" gem and a fallback in
# case the installed version of RDoc does not have them.
class Gem::RDoc # :nodoc: all
include Gem::UserInteraction
@rdoc_version = nil
@specs = []
##
# Force installation of documentation?
attr_accessor :force
##
# Generate rdoc?
attr_accessor :generate_rdoc
##
# Generate ri data?
attr_accessor :generate_ri
class << self
##
# Loaded version of RDoc. Set by ::load_rdoc
attr_reader :rdoc_version
end
##
# Post installs hook that generates documentation for each specification in
# +specs+
def self.generation_hook installer, specs
types = installer.document
generate_rdoc = types.include? 'rdoc'
generate_ri = types.include? 'ri'
specs.each do |spec|
new(spec, generate_rdoc, generate_ri).generate
end
end
##
# Loads the RDoc generator
def self.load_rdoc
return if @rdoc_version
begin
require 'rdoc/rdoc'
@rdoc_version = if ::RDoc.const_defined? :VERSION then
Gem::Version.new ::RDoc::VERSION
else
Gem::Version.new '1.0.1'
end
rescue LoadError => e
raise Gem::DocumentError, "RDoc is not installed: #{e}"
end
end
##
# Creates a new documentation generator for +spec+. RDoc and ri data
# generation can be enabled or disabled through +generate_rdoc+ and
# +generate_ri+ respectively.
#
# Only +generate_ri+ is enabled by default.
def initialize spec, generate_rdoc = false, generate_ri = true
@doc_dir = spec.doc_dir
@file_info = nil
@force = false
@rdoc = nil
@spec = spec
@generate_rdoc = generate_rdoc
@generate_ri = generate_ri
@rdoc_dir = spec.doc_dir 'rdoc'
@ri_dir = spec.doc_dir 'ri'
end
##
# Removes legacy rdoc arguments from +args+
def delete_legacy_args args
args.delete '--inline-source'
args.delete '--promiscuous'
args.delete '-p'
args.delete '--one-file'
end
##
# Generates documentation using the named +generator+ ("darkfish" or "ri")
# and following the given +options+.
#
# Documentation will be generated into +destination+
def document generator, options, destination
options = options.dup
options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
options.setup_generator generator
options.op_dir = destination
options.finish
@rdoc.options = options
@rdoc.generator = options.generator.new options
say "Installing #{generator} documentation for #{@spec.full_name}"
FileUtils.mkdir_p options.op_dir
Dir.chdir options.op_dir do
begin
@rdoc.class.current = @rdoc
@rdoc.generator.generate @file_info
ensure
@rdoc.class.current = nil
end
end
end
##
# Generates RDoc and ri data
def generate
return unless @generate_ri or @generate_rdoc
setup
if Gem::Requirement.new('< 3').satisfied_by? self.class.rdoc_version then
generate_legacy
else
::RDoc::TopLevel.reset # TODO ::RDoc::RDoc.reset
::RDoc::Parser::C.reset
options = ::RDoc::Options.new
options.default_title = "#{@spec.full_name} Documentation"
options.files = []
options.files.push(*@spec.require_paths)
options.files.push(*@spec.extra_rdoc_files)
args = @spec.rdoc_options
case config_args = Gem.configuration[:rdoc]
when String then
args = args.concat config_args.split
when Array then
args = args.concat config_args
end
delete_legacy_args args
options.parse args
options.quiet = !Gem.configuration.really_verbose
@rdoc = new_rdoc
@rdoc.options = options
Dir.chdir @spec.full_gem_path do
@file_info = @rdoc.parse_files options.files
end
document 'ri', options, @ri_dir if
@generate_ri and (@force or not File.exist? @ri_dir)
document 'darkfish', options, @rdoc_dir if
@generate_rdoc and (@force or not File.exist? @rdoc_dir)
end
end
##
# Generates RDoc and ri data for legacy RDoc versions. This method will not
# exist in future versions.
def generate_legacy
if @generate_rdoc then
FileUtils.rm_rf @rdoc_dir
say "Installing RDoc documentation for #{@spec.full_name}"
legacy_rdoc '--op', @rdoc_dir
end
if @generate_ri then
FileUtils.rm_rf @ri_dir
say "Installing ri documentation for #{@spec.full_name}"
legacy_rdoc '--ri', '--op', @ri_dir
end
end
##
# Generates RDoc using a legacy version of RDoc from the ARGV-like +args+.
# This method will not exist in future versions.
def legacy_rdoc *args
args << @spec.rdoc_options
args << '--quiet'
args << @spec.require_paths.clone
args << @spec.extra_rdoc_files
args << '--title' << "#{@spec.full_name} Documentation"
args = args.flatten.map do |arg| arg.to_s end
delete_legacy_args args if
Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version
r = new_rdoc
say "rdoc #{args.join ' '}" if Gem.configuration.really_verbose
Dir.chdir @spec.full_gem_path do
begin
r.document args
rescue Errno::EACCES => e
dirname = File.dirname e.message.split("-")[1].strip
raise Gem::FilePermissionError, dirname
rescue Interrupt => e
raise e
rescue Exception => ex
alert_error "While generating documentation for #{@spec.full_name}"
ui.errs.puts "... MESSAGE: #{ex}"
ui.errs.puts "... RDOC args: #{args.join(' ')}"
ui.backtrace ex
ui.errs.puts "(continuing with the rest of the installation)"
ensure
end
end
end
##
# #new_rdoc creates a new RDoc instance. This method is provided only to
# make testing easier.
def new_rdoc
::RDoc::RDoc.new
end
##
# Is rdoc documentation installed?
def rdoc_installed?
File.exist? @rdoc_dir
end
##
# Removes generated RDoc and ri data
def remove
base_dir = @spec.base_dir
raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
FileUtils.rm_rf @rdoc_dir
FileUtils.rm_rf @ri_dir
end
##
# Is ri data installed?
def ri_installed?
File.exist? @ri_dir
end
##
# Prepares the spec for documentation generation
def setup
self.class.load_rdoc
raise Gem::FilePermissionError, @doc_dir if
File.exist?(@doc_dir) and not File.writable?(@doc_dir)
FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
end
end unless loaded_hook
Gem.done_installing(&Gem::RDoc.method(:generation_hook))

Просмотреть файл

@ -1,6 +1,7 @@
require 'rubygems' require 'rubygems'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'uri' require 'uri'
require 'resolv'
## ##
# RemoteFetcher handles the details of fetching gems and gem information from # RemoteFetcher handles the details of fetching gems and gem information from
@ -8,8 +9,6 @@ require 'uri'
class Gem::RemoteFetcher class Gem::RemoteFetcher
BuiltinSSLCerts = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
include Gem::UserInteraction include Gem::UserInteraction
## ##
@ -34,6 +33,13 @@ class Gem::RemoteFetcher
end end
##
# A FetchError that indicates that the reason for not being
# able to fetch data was that the host could not be contacted
class UnknownHostError < FetchError
end
@fetcher = nil @fetcher = nil
## ##
@ -53,8 +59,11 @@ class Gem::RemoteFetcher
# * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
# HTTP_PROXY_PASS) # HTTP_PROXY_PASS)
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
#
# +dns+: An object to use for DNS resolution of the API endpoint.
# By default, use Resolv::DNS.
def initialize(proxy = nil) def initialize(proxy=nil, dns=Resolv::DNS.new)
require 'net/http' require 'net/http'
require 'stringio' require 'stringio'
require 'time' require 'time'
@ -72,6 +81,27 @@ class Gem::RemoteFetcher
else URI.parse(proxy) else URI.parse(proxy)
end end
@user_agent = user_agent @user_agent = user_agent
@env_no_proxy = get_no_proxy_from_env
@dns = dns
end
##
#
# Given a source at +uri+, calculate what hostname to actually
# connect to query the data for it.
def api_endpoint(uri)
host = uri.host
begin
res = @dns.getresource "_rubygems._tcp.#{host}",
Resolv::DNS::Resource::IN::SRV
rescue Resolv::ResolvError
uri
else
URI.parse "#{res.target}#{uri.path}"
end
end end
## ##
@ -82,14 +112,13 @@ class Gem::RemoteFetcher
# larger, more emcompassing effort. -erikh # larger, more emcompassing effort. -erikh
def download_to_cache dependency def download_to_cache dependency
found = Gem::SpecFetcher.fetcher.fetch dependency, true, true, found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
dependency.prerelease?
return if found.empty? return if found.empty?
spec, source_uri = found.sort_by { |(s,_)| s.version }.last spec, source = found.sort_by { |(s,_)| s.version }.last
download spec, source_uri download spec, source.uri.to_s
end end
## ##
@ -100,11 +129,14 @@ class Gem::RemoteFetcher
def download(spec, source_uri, install_dir = Gem.dir) def download(spec, source_uri, install_dir = Gem.dir)
Gem.ensure_gem_subdirectories(install_dir) rescue nil Gem.ensure_gem_subdirectories(install_dir) rescue nil
if File.writable?(install_dir) cache_dir =
cache_dir = File.join install_dir, "cache" if Dir.pwd == install_dir then # see fetch_command
else install_dir
cache_dir = File.join Gem.user_dir, "cache" elsif File.writable? install_dir then
end File.join install_dir, "cache"
else
File.join Gem.user_dir, "cache"
end
gem_file_name = File.basename spec.cache_file gem_file_name = File.basename spec.cache_file
local_gem_path = File.join cache_dir, gem_file_name local_gem_path = File.join cache_dir, gem_file_name
@ -123,6 +155,8 @@ class Gem::RemoteFetcher
# URI.parse gets confused by MS Windows paths with forward slashes. # URI.parse gets confused by MS Windows paths with forward slashes.
scheme = nil if scheme =~ /^[a-z]$/i scheme = nil if scheme =~ /^[a-z]$/i
# REFACTOR: split this up and dispatch on scheme (eg download_http)
# REFACTOR: be sure to clean up fake fetcher when you do this... cleaner
case scheme case scheme
when 'http', 'https' then when 'http', 'https' then
unless File.exist? local_gem_path then unless File.exist? local_gem_path then
@ -132,7 +166,7 @@ class Gem::RemoteFetcher
remote_gem_path = source_uri + "gems/#{gem_file_name}" remote_gem_path = source_uri + "gems/#{gem_file_name}"
gem = self.fetch_path remote_gem_path self.cache_update_path remote_gem_path, local_gem_path
rescue Gem::RemoteFetcher::FetchError rescue Gem::RemoteFetcher::FetchError
raise if spec.original_platform == spec.platform raise if spec.original_platform == spec.platform
@ -143,11 +177,7 @@ class Gem::RemoteFetcher
remote_gem_path = source_uri + "gems/#{alternate_name}" remote_gem_path = source_uri + "gems/#{alternate_name}"
gem = self.fetch_path remote_gem_path self.cache_update_path remote_gem_path, local_gem_path
end
File.open local_gem_path, 'wb' do |fp|
fp.write gem
end end
end end
when 'file' then when 'file' then
@ -184,7 +214,7 @@ class Gem::RemoteFetcher
say "Using local gem #{local_gem_path}" if say "Using local gem #{local_gem_path}" if
Gem.configuration.really_verbose Gem.configuration.really_verbose
else else
raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}" raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}"
end end
local_gem_path local_gem_path
@ -232,18 +262,54 @@ class Gem::RemoteFetcher
uri = URI.parse uri unless URI::Generic === uri uri = URI.parse uri unless URI::Generic === uri
raise ArgumentError, "bad uri: #{uri}" unless uri raise ArgumentError, "bad uri: #{uri}" unless uri
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" unless
uri.scheme unless uri.scheme
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
end
data = send "fetch_#{uri.scheme}", uri, mtime, head data = send "fetch_#{uri.scheme}", uri, mtime, head
data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/
if data and !head and uri.to_s =~ /gz$/
begin
data = Gem.gunzip data
rescue Zlib::GzipFile::Error
raise FetchError.new("server did not return a valid file", uri.to_s)
end
end
data data
rescue FetchError rescue FetchError
raise raise
rescue Timeout::Error rescue Timeout::Error
raise FetchError.new('timed out', uri.to_s) raise UnknownHostError.new('timed out', uri.to_s)
rescue IOError, SocketError, SystemCallError => e rescue IOError, SocketError, SystemCallError => e
raise FetchError.new("#{e.class}: #{e}", uri.to_s) if e.message =~ /getaddrinfo/
raise UnknownHostError.new('no such name', uri.to_s)
else
raise FetchError.new("#{e.class}: #{e}", uri.to_s)
end
end
##
# Downloads +uri+ to +path+ if necessary. If no path is given, it just
# passes the data.
def cache_update_path(uri, path = nil)
mtime = path && File.stat(path).mtime rescue nil
if mtime && Net::HTTPNotModified === fetch_path(uri, mtime, true)
Gem.read_binary(path)
else
data = fetch_path(uri)
if path
open(path, 'wb') do |io|
io.write data
end
end
data
end
end end
## ##
@ -273,6 +339,17 @@ class Gem::RemoteFetcher
URI URI
end end
##
# Returns list of no_proxy entries (if any) from the environment
def get_no_proxy_from_env
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
return [] if env_no_proxy.nil? or env_no_proxy.empty?
env_no_proxy.split(/\s*,\s*/)
end
## ##
# Returns an HTTP proxy URI if one is set in the environment variables. # Returns an HTTP proxy URI if one is set in the environment variables.
@ -296,7 +373,7 @@ class Gem::RemoteFetcher
# Normalize the URI by adding "http://" if it is missing. # Normalize the URI by adding "http://" if it is missing.
def normalize_uri(uri) def normalize_uri(uri)
(uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" (uri =~ /^(https?|ftp|file):/i) ? uri : "http://#{uri}"
end end
## ##
@ -306,7 +383,7 @@ class Gem::RemoteFetcher
def connection_for(uri) def connection_for(uri)
net_http_args = [uri.host, uri.port] net_http_args = [uri.host, uri.port]
if @proxy_uri then if @proxy_uri and not no_proxy?(uri.host) then
net_http_args += [ net_http_args += [
@proxy_uri.host, @proxy_uri.host,
@proxy_uri.port, @proxy_uri.port,
@ -319,37 +396,23 @@ class Gem::RemoteFetcher
@connections[connection_id] ||= Net::HTTP.new(*net_http_args) @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
connection = @connections[connection_id] connection = @connections[connection_id]
if https?(uri) and !connection.started? then if https?(uri) and not connection.started? then
configure_connection_for_https(connection) configure_connection_for_https(connection)
# Don't refactor this with the else branch. We don't want the
# http-only code path to not depend on anything in OpenSSL.
#
begin
connection.start
rescue OpenSSL::SSL::SSLError, Errno::EHOSTDOWN => e
raise FetchError.new(e.message, uri)
end
else
begin
connection.start unless connection.started?
rescue Errno::EHOSTDOWN => e
raise FetchError.new(e.message, uri)
end
end end
connection.start unless connection.started?
connection connection
rescue OpenSSL::SSL::SSLError, Errno::EHOSTDOWN => e
raise FetchError.new(e.message, uri)
end end
def configure_connection_for_https(connection) def configure_connection_for_https(connection)
require 'net/https' require 'net/https'
connection.use_ssl = true connection.use_ssl = true
connection.verify_mode = connection.verify_mode =
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
store = OpenSSL::X509::Store.new store = OpenSSL::X509::Store.new
if Gem.configuration.ssl_ca_cert if Gem.configuration.ssl_ca_cert
if File.directory? Gem.configuration.ssl_ca_cert if File.directory? Gem.configuration.ssl_ca_cert
store.add_path Gem.configuration.ssl_ca_cert store.add_path Gem.configuration.ssl_ca_cert
@ -360,12 +423,12 @@ class Gem::RemoteFetcher
store.set_default_paths store.set_default_paths
add_rubygems_trusted_certs(store) add_rubygems_trusted_certs(store)
end end
connection.cert_store = store connection.cert_store = store
end end
def add_rubygems_trusted_certs(store) def add_rubygems_trusted_certs(store)
Dir.glob(BuiltinSSLCerts).each do |ssl_cert_file| pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
Dir.glob(pattern).each do |ssl_cert_file|
store.add_file ssl_cert_file store.add_file ssl_cert_file
end end
end end
@ -378,13 +441,13 @@ class Gem::RemoteFetcher
end end
end end
## def no_proxy? host
# Read the data from the (source based) URI, but if it is a file:// URI, host = host.downcase
# read from the filesystem instead. @env_no_proxy.each do |pattern|
pattern = pattern.downcase
def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0) return true if host[-pattern.length, pattern.length ] == pattern
raise "NO: Use fetch_path instead" end
# TODO: deprecate for fetch_path return false
end end
## ##

182
lib/rubygems/request_set.rb Normal file
Просмотреть файл

@ -0,0 +1,182 @@
require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/dependency_resolver'
require 'rubygems/dependency_list'
require 'rubygems/installer'
require 'tsort'
module Gem
class RequestSet
include TSort
def initialize(*deps)
@dependencies = deps
yield self if block_given?
end
attr_reader :dependencies
# Declare that a gem of name +name+ with +reqs+ requirements
# is needed.
#
def gem(name, *reqs)
@dependencies << Gem::Dependency.new(name, reqs)
end
# Add +deps+ Gem::Depedency objects to the set.
#
def import(deps)
@dependencies += deps
end
# Resolve the requested dependencies and return an Array of
# Specification objects to be activated.
#
def resolve(set=nil)
r = Gem::DependencyResolver.new(@dependencies, set)
@requests = r.resolve
end
# Resolve the requested dependencies against the gems
# available via Gem.path and return an Array of Specification
# objects to be activated.
#
def resolve_current
resolve DependencyResolver::CurrentSet.new
end
# Load a dependency management file.
#
def load_gemdeps(path)
gf = GemDepedencyAPI.new(self, path)
gf.load
end
def specs
@specs ||= @requests.map { |r| r.full_spec }
end
def tsort_each_node(&block)
@requests.each(&block)
end
def tsort_each_child(node)
node.spec.dependencies.each do |dep|
next if dep.type == :development
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
if match
begin
yield match
rescue TSort::Cyclic
end
else
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
end
end
end
def sorted_requests
@sorted ||= strongly_connected_components.flatten
end
def specs_in(dir)
Dir["#{dir}/specifications/*.gemspec"].map do |g|
Gem::Specification.load g
end
end
def install_into(dir, force=true, &b)
existing = force ? [] : specs_in(dir)
dir = File.expand_path dir
installed = []
sorted_requests.each do |req|
if existing.find { |s| s.full_name == req.spec.full_name }
b.call req, nil if b
next
end
path = req.download(dir)
inst = Gem::Installer.new path, :install_dir => dir,
:only_install_dir => true
b.call req, inst if b
inst.install
installed << req
end
installed
end
def install(options, &b)
if dir = options[:install_dir]
return install_into(dir, false, &b)
end
cache_dir = options[:cache_dir] || Gem.dir
specs = []
sorted_requests.each do |req|
if req.installed?
b.call req, nil if b
next
end
path = req.download cache_dir
inst = Gem::Installer.new path, options
b.call req, inst if b
specs << inst.install
end
specs
end
# A semi-compatible DSL for Bundler's Gemfile format
#
class GemDepedencyAPI
def initialize(set, path)
@set = set
@path = path
end
def load
instance_eval File.read(@path).untaint, @path, 1
end
# DSL
def source(url)
end
def gem(name, *reqs)
# Ignore the opts for now.
reqs.pop if reqs.last.kind_of?(Hash)
@set.gem name, *reqs
end
def platform(what)
if what == :ruby
yield
end
end
alias_method :platforms, :platform
def group(*what)
end
end
end
end

Просмотреть файл

@ -1,18 +0,0 @@
require 'rubygems'
# TODO: remove after 1.9.1 dropped
module Gem::RequirePathsBuilder
def write_require_paths_file_if_needed(spec = @spec, gem_home = @gem_home)
return if spec.require_paths == ["lib"] &&
(spec.bindir.nil? || spec.bindir == "bin")
file_name = File.join(gem_home, 'gems', "#{@spec.full_name}", ".require_paths")
file_name.untaint
File.open(file_name, "w") do |file|
spec.require_paths.each do |path|
file.puts path
end
file.puts spec.bindir if spec.bindir
end
end
end if Gem::QUICKLOADER_SUCKAGE

Просмотреть файл

@ -1,5 +1,3 @@
require "rubygems/version"
## ##
# A Requirement is a set of one or more version restrictions. It supports a # A Requirement is a set of one or more version restrictions. It supports a
# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators. # few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators.
@ -13,14 +11,16 @@ require "rubygems/version"
require "rubygems/version" require "rubygems/version"
require "rubygems/deprecate" require "rubygems/deprecate"
class Gem::Requirement # If we're being loaded after yaml was already required, then
include Comparable # load our yaml + workarounds now.
Gem.load_yaml if defined? ::YAML
class Gem::Requirement
OPS = { #:nodoc: OPS = { #:nodoc:
"=" => lambda { |v, r| v == r }, "=" => lambda { |v, r| v == r },
"!=" => lambda { |v, r| v != r }, "!=" => lambda { |v, r| v != r },
">" => lambda { |v, r| v > r }, ">" => lambda { |v, r| v > r },
"<" => lambda { |v, r| v < r }, "<" => lambda { |v, r| v < r },
">=" => lambda { |v, r| v >= r }, ">=" => lambda { |v, r| v >= r },
"<=" => lambda { |v, r| v <= r }, "<=" => lambda { |v, r| v <= r },
"~>" => lambda { |v, r| v >= r && v.release < r.bump } "~>" => lambda { |v, r| v >= r && v.release < r.bump }
@ -29,6 +29,10 @@ class Gem::Requirement
quoted = OPS.keys.map { |k| Regexp.quote k }.join "|" quoted = OPS.keys.map { |k| Regexp.quote k }.join "|"
PATTERN = /\A\s*(#{quoted})?\s*(#{Gem::Version::VERSION_PATTERN})\s*\z/ PATTERN = /\A\s*(#{quoted})?\s*(#{Gem::Version::VERSION_PATTERN})\s*\z/
DefaultRequirement = [">=", Gem::Version.new(0)]
class BadRequirementError < ArgumentError; end
## ##
# Factory method to create a Gem::Requirement object. Input may be # Factory method to create a Gem::Requirement object. Input may be
# a Version, a String, or nil. Intended to simplify client code. # a Version, a String, or nil. Intended to simplify client code.
@ -36,6 +40,9 @@ class Gem::Requirement
# If the input is "weird", the default version requirement is # If the input is "weird", the default version requirement is
# returned. # returned.
# REFACTOR: There's no reason that this can't be unified with .new.
# .new is the standard Ruby factory method.
def self.create input def self.create input
case input case input
when Gem::Requirement then when Gem::Requirement then
@ -53,10 +60,6 @@ class Gem::Requirement
## ##
# A default "version requirement" can surely _only_ be '>= 0'. # A default "version requirement" can surely _only_ be '>= 0'.
#--
# This comment once said:
#
# "A default "version requirement" can surely _only_ be '> 0'."
def self.default def self.default
new '>= 0' new '>= 0'
@ -74,14 +77,23 @@ class Gem::Requirement
# parse("1.0") # => ["=", "1.0"] # parse("1.0") # => ["=", "1.0"]
# parse(Gem::Version.new("1.0")) # => ["=, "1.0"] # parse(Gem::Version.new("1.0")) # => ["=, "1.0"]
# REFACTOR: Little two element arrays like this have no real semantic
# value. I'd love to see something like this:
# Constraint = Struct.new(:operator, :version); (or similar)
# and have a Requirement be a list of Constraints.
def self.parse obj def self.parse obj
return ["=", obj] if Gem::Version === obj return ["=", obj] if Gem::Version === obj
unless PATTERN =~ obj.to_s unless PATTERN =~ obj.to_s
raise ArgumentError, "Illformed requirement [#{obj.inspect}]" raise BadRequirementError, "Illformed requirement [#{obj.inspect}]"
end end
[$1 || "=", Gem::Version.new($2)] if $1 == ">=" && $2 == "0"
DefaultRequirement
else
[$1 || "=", Gem::Version.new($2)]
end
end end
## ##
@ -101,13 +113,23 @@ class Gem::Requirement
requirements.compact! requirements.compact!
requirements.uniq! requirements.uniq!
requirements << ">= 0" if requirements.empty? if requirements.empty?
@none = (requirements == ">= 0") @requirements = [DefaultRequirement]
@requirements = requirements.map! { |r| self.class.parse r } else
@requirements = requirements.map! { |r| self.class.parse r }
end
end end
##
# true if this gem has no requirements.
# FIX: maybe this should be using #default ?
def none? def none?
@none ||= (to_s == ">= 0") if @requirements.size == 1
@requirements[0] == DefaultRequirement
else
false
end
end end
def as_list # :nodoc: def as_list # :nodoc:
@ -135,6 +157,7 @@ class Gem::Requirement
instance_variable_set "@#{ivar}", val instance_variable_set "@#{ivar}", val
end end
Gem.load_yaml
fix_syck_default_key_in_requirements fix_syck_default_key_in_requirements
end end
@ -142,6 +165,18 @@ class Gem::Requirement
yaml_initialize coder.tag, coder.map yaml_initialize coder.tag, coder.map
end end
def to_yaml_properties
["@requirements"]
end
def encode_with(coder)
coder.add 'requirements', @requirements
end
##
# A requirement is a prerelease if any of the versions inside of it
# are prereleases
def prerelease? def prerelease?
requirements.any? { |r| r.last.prerelease? } requirements.any? { |r| r.last.prerelease? }
end end
@ -156,6 +191,8 @@ class Gem::Requirement
# True if +version+ satisfies this Requirement. # True if +version+ satisfies this Requirement.
def satisfied_by? version def satisfied_by? version
raise ArgumentError, "Need a Gem::Version: #{version.inspect}" unless
Gem::Version === version
# #28965: syck has a bug with unquoted '=' YAML.loading as YAML::DefaultKey # #28965: syck has a bug with unquoted '=' YAML.loading as YAML::DefaultKey
requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv } requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv }
end end
@ -176,12 +213,14 @@ class Gem::Requirement
as_list.join ", " as_list.join ", "
end end
def <=> other # :nodoc: # DOC: this should probably be :nodoc'd
to_s <=> other.to_s def == other
Gem::Requirement === other and to_s == other.to_s
end end
private private
# DOC: this should probably be :nodoc'd
def fix_syck_default_key_in_requirements def fix_syck_default_key_in_requirements
Gem.load_yaml Gem.load_yaml
@ -194,11 +233,9 @@ class Gem::Requirement
end end
end end
# :stopdoc: # This is needed for compatibility with older yaml
# Gem::Version::Requirement is used in a lot of old YAML specs. It's aliased # gemspecs.
# here for backwards compatibility. I'd like to remove this, maybe in RubyGems
# 2.0.
::Gem::Version::Requirement = ::Gem::Requirement
# :startdoc:
class Gem::Version
Requirement = Gem::Requirement
end

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,115 @@
module Gem::Security
##
# No security policy: all package signature checks are disabled.
NoSecurity = Policy.new(
'No Security',
:verify_data => false,
:verify_signer => false,
:verify_chain => false,
:verify_root => false,
:only_trusted => false,
:only_signed => false
)
##
# AlmostNo security policy: only verify that the signing certificate is the
# one that actually signed the data. Make no attempt to verify the signing
# certificate chain.
#
# This policy is basically useless. better than nothing, but can still be
# easily spoofed, and is not recommended.
AlmostNoSecurity = Policy.new(
'Almost No Security',
:verify_data => true,
:verify_signer => false,
:verify_chain => false,
:verify_root => false,
:only_trusted => false,
:only_signed => false
)
##
# Low security policy: only verify that the signing certificate is actually
# the gem signer, and that the signing certificate is valid.
#
# This policy is better than nothing, but can still be easily spoofed, and
# is not recommended.
LowSecurity = Policy.new(
'Low Security',
:verify_data => true,
:verify_signer => true,
:verify_chain => false,
:verify_root => false,
:only_trusted => false,
:only_signed => false
)
##
# Medium security policy: verify the signing certificate, verify the signing
# certificate chain all the way to the root certificate, and only trust root
# certificates that we have explicitly allowed trust for.
#
# This security policy is reasonable, but it allows unsigned packages, so a
# malicious person could simply delete the package signature and pass the
# gem off as unsigned.
MediumSecurity = Policy.new(
'Medium Security',
:verify_data => true,
:verify_signer => true,
:verify_chain => true,
:verify_root => true,
:only_trusted => true,
:only_signed => false
)
##
# High security policy: only allow signed gems to be installed, verify the
# signing certificate, verify the signing certificate chain all the way to
# the root certificate, and only trust root certificates that we have
# explicitly allowed trust for.
#
# This security policy is significantly more difficult to bypass, and offers
# a reasonable guarantee that the contents of the gem have not been altered.
HighSecurity = Policy.new(
'High Security',
:verify_data => true,
:verify_signer => true,
:verify_chain => true,
:verify_root => true,
:only_trusted => true,
:only_signed => true
)
##
# Policy used to verify a certificate and key when signing a gem
SigningPolicy = Policy.new(
'Signing Policy',
:verify_data => false,
:verify_signer => true,
:verify_chain => true,
:verify_root => true,
:only_trusted => false,
:only_signed => false
)
##
# Hash of configured security policies
Policies = {
'NoSecurity' => NoSecurity,
'AlmostNoSecurity' => AlmostNoSecurity,
'LowSecurity' => LowSecurity,
'MediumSecurity' => MediumSecurity,
'HighSecurity' => HighSecurity,
# SigningPolicy is not intended for use by `gem -P` so do not list it
}
end

Просмотреть файл

@ -0,0 +1,227 @@
##
# A Gem::Security::Policy object encapsulates the settings for verifying
# signed gem files. This is the base class. You can either declare an
# instance of this or use one of the preset security policies in
# Gem::Security::Policies.
class Gem::Security::Policy
attr_reader :name
attr_accessor :only_signed
attr_accessor :only_trusted
attr_accessor :verify_chain
attr_accessor :verify_data
attr_accessor :verify_root
attr_accessor :verify_signer
##
# Create a new Gem::Security::Policy object with the given mode and
# options.
def initialize name, policy = {}, opt = {}
@name = name
@opt = opt
# Default to security
@only_signed = true
@only_trusted = true
@verify_chain = true
@verify_data = true
@verify_root = true
@verify_signer = true
policy.each_pair do |key, val|
case key
when :verify_data then @verify_data = val
when :verify_signer then @verify_signer = val
when :verify_chain then @verify_chain = val
when :verify_root then @verify_root = val
when :only_trusted then @only_trusted = val
when :only_signed then @only_signed = val
end
end
end
##
# Verifies each certificate in +chain+ has signed the following certificate
# and is valid for the given +time+.
def check_chain chain, time
chain.each_cons 2 do |issuer, cert|
check_cert cert, issuer, time
end
true
rescue Gem::Security::Exception => e
raise Gem::Security::Exception, "invalid signing chain: #{e.message}"
end
##
# Verifies that +data+ matches the +signature+ created by +public_key+ and
# the +digest+ algorithm.
def check_data public_key, digest, signature, data
raise Gem::Security::Exception, "invalid signature" unless
public_key.verify digest.new, signature, data.digest
true
end
##
# Ensures that +signer+ is valid for +time+ and was signed by the +issuer+.
# If the +issuer+ is +nil+ no verification is performed.
def check_cert signer, issuer, time
message = "certificate #{signer.subject}"
if not_before = signer.not_before and not_before > time then
raise Gem::Security::Exception,
"#{message} not valid before #{not_before}"
end
if not_after = signer.not_after and not_after < time then
raise Gem::Security::Exception, "#{message} not valid after #{not_after}"
end
if issuer and not signer.verify issuer.public_key then
raise Gem::Security::Exception,
"#{message} was not issued by #{issuer.subject}"
end
true
end
##
# Ensures the public key of +key+ matches the public key in +signer+
def check_key signer, key
raise Gem::Security::Exception,
"certificate #{signer.subject} does not match the signing key" unless
signer.public_key.to_pem == key.public_key.to_pem
true
end
##
# Ensures the root certificate in +chain+ is self-signed and valid for
# +time+.
def check_root chain, time
root = chain.first
raise Gem::Security::Exception,
"root certificate #{root.subject} is not self-signed " \
"(issuer #{root.issuer})" if
root.issuer.to_s != root.subject.to_s # HACK to_s is for ruby 1.8
check_cert root, root, time
end
##
# Ensures the root of +chain+ has a trusted certificate in +trust_dir+ and
# the digests of the two certificates match according to +digester+
def check_trust chain, digester, trust_dir
root = chain.first
path = Gem::Security.trust_dir.cert_path root
unless File.exist? path then
message = "root cert #{root.subject} is not trusted"
message << " (root of signing cert #{chain.last.subject})" if
chain.length > 1
raise Gem::Security::Exception, message
end
save_cert = OpenSSL::X509::Certificate.new File.read path
save_dgst = digester.digest save_cert.public_key.to_s
pkey_str = root.public_key.to_s
cert_dgst = digester.digest pkey_str
raise Gem::Security::Exception,
"trusted root certificate #{root.subject} checksum " \
"does not match signing root certificate checksum" unless
save_dgst == cert_dgst
true
end
def inspect # :nodoc:
"[Policy: %s - data: %p signer: %p chain: %p root: %p " \
"signed-only: %p trusted-only: %p]" % [
@name, @verify_chain, @verify_data, @verify_root, @verify_signer,
@only_signed, @only_trusted,
]
end
##
# Verifies the certificate +chain+ is valid, the +digests+ match the
# signatures +signatures+ created by the signer depending on the +policy+
# settings.
#
# If +key+ is given it is used to validate the signing certificate.
def verify chain, key = nil, digests = {}, signatures = {}
if @only_signed and signatures.empty? then
raise Gem::Security::Exception,
"unsigned gems are not allowed by the #{name} policy"
end
opt = @opt
digester = Gem::Security::DIGEST_ALGORITHM
trust_dir = opt[:trust_dir]
time = Time.now
signer_digests = digests.find do |algorithm, file_digests|
file_digests.values.first.name == Gem::Security::DIGEST_NAME
end
signer_digests = digests.values.first || {}
signer = chain.last
check_key signer, key if key
check_cert signer, nil, time if @verify_signer
check_chain chain, time if @verify_chain
check_root chain, time if @verify_root
check_trust chain, digester, trust_dir if @only_trusted
signer_digests.each do |file, digest|
signature = signatures[file]
raise Gem::Security::Exception, "missing signature for #{file}" unless
signature
check_data signer.public_key, digester, signature, digest if @verify_data
end
true
end
##
# Extracts the certificate chain from the +spec+ and calls #verify to ensure
# the signatures and certificate chain is valid according to the policy..
def verify_signatures spec, digests, signatures
chain = spec.cert_chain.map do |cert_pem|
OpenSSL::X509::Certificate.new cert_pem
end
verify chain, nil, digests, signatures
true
end
alias to_s name # :nodoc:
end

Просмотреть файл

@ -0,0 +1,136 @@
##
# Basic OpenSSL-based package signing class.
class Gem::Security::Signer
##
# The chain of certificates for signing including the signing certificate
attr_accessor :cert_chain
##
# The private key for the signing certificate
attr_accessor :key
##
# The digest algorithm used to create the signature
attr_reader :digest_algorithm
##
# The name of the digest algorithm, used to pull digests out of the hash by
# name.
attr_reader :digest_name # :nodoc:
##
# Creates a new signer with an RSA +key+ or path to a key, and a certificate
# +chain+ containing X509 certificates, encoding certificates or paths to
# certificates.
def initialize key, cert_chain
@cert_chain = cert_chain
@key = key
unless @key then
default_key = File.join Gem.user_home, 'gem-private_key.pem'
@key = default_key if File.exist? default_key
end
unless @cert_chain then
default_cert = File.join Gem.user_home, 'gem-public_cert.pem'
@cert_chain = [default_cert] if File.exist? default_cert
end
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM
@digest_name = Gem::Security::DIGEST_NAME
@key = OpenSSL::PKey::RSA.new File.read @key if
@key and not OpenSSL::PKey::RSA === @key
if @cert_chain then
@cert_chain = @cert_chain.compact.map do |cert|
next cert if OpenSSL::X509::Certificate === cert
cert = File.read cert if File.exist? cert
OpenSSL::X509::Certificate.new cert
end
load_cert_chain
end
end
##
# Loads any missing issuers in the cert chain from the trusted certificates.
#
# If the issuer does not exist it is ignored as it will be checked later.
def load_cert_chain # :nodoc:
return if @cert_chain.empty?
while @cert_chain.first.issuer.to_s != @cert_chain.first.subject.to_s do
issuer = Gem::Security.trust_dir.issuer_of @cert_chain.first
break unless issuer # cert chain is verified later
@cert_chain.unshift issuer
end
end
##
# Sign data with given digest algorithm
def sign data
return unless @key
if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
re_sign_key
end
Gem::Security::SigningPolicy.verify @cert_chain, @key
@key.sign @digest_algorithm.new, data
end
##
# Attempts to re-sign the private key if the signing certificate is expired.
#
# The key will be re-signed if:
# * The expired certificate is self-signed
# * The expired certificate is saved at ~/.gem/gem-public_cert.pem
# * There is no file matching the expiry date at
# ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S
#
# If the signing certificate can be re-signed the expired certificate will
# be saved as ~/.gem/gem-pubilc_cert.pem.expired.%Y%m%d%H%M%S where the
# expiry time (not after) is used for the timestamp.
def re_sign_key # :nodoc:
old_cert = @cert_chain.last
disk_cert_path = File.join Gem.user_home, 'gem-public_cert.pem'
disk_cert = File.read disk_cert_path rescue nil
disk_key =
File.read File.join(Gem.user_home, 'gem-private_key.pem') rescue nil
if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then
expiry = old_cert.not_after.strftime '%Y%m%d%H%M%S'
old_cert_file = "gem-public_cert.pem.expired.#{expiry}"
old_cert_path = File.join Gem.user_home, old_cert_file
unless File.exist? old_cert_path then
Gem::Security.write old_cert, old_cert_path
cert = Gem::Security.re_sign old_cert, @key
Gem::Security.write cert, disk_cert_path
@cert_chain = [cert]
end
end
end
end

Просмотреть файл

@ -0,0 +1,104 @@
class Gem::Security::TrustDir
DEFAULT_PERMISSIONS = {
:trust_dir => 0700,
:trusted_cert => 0600,
}
def initialize dir, permissions = DEFAULT_PERMISSIONS
@dir = dir
@permissions = permissions
@digester = Gem::Security::DIGEST_ALGORITHM
end
attr_reader :dir
##
# Returns the path to the trusted +certificate+
def cert_path certificate
name_path certificate.subject
end
##
# Enumerates trusted certificates.
def each_certificate
return enum_for __method__ unless block_given?
glob = File.join @dir, '*.pem'
Dir[glob].each do |certificate_file|
begin
certificate = load_certificate certificate_file
yield certificate, certificate_file
rescue OpenSSL::X509::CertificateError
next # HACK warn
end
end
end
##
# Returns the issuer certificate of the given +certificate+ if it exists in
# the trust directory.
def issuer_of certificate
path = name_path certificate.issuer
return unless File.exist? path
load_certificate path
end
##
# Returns the path to the trusted certificate with the given ASN.1 +name+
def name_path name
digest = @digester.hexdigest name.to_s
File.join @dir, "cert-#{digest}.pem"
end
##
# Loads the given +certificate_file+
def load_certificate certificate_file
pem = File.read certificate_file
OpenSSL::X509::Certificate.new pem
end
##
# Add a certificate to trusted certificate list.
def trust_cert certificate
verify
destination = cert_path certificate
open destination, 'wb', @permissions[:trusted_cert] do |io|
io.write certificate.to_pem
end
end
##
# Make sure the trust directory exists. If it does exist, make sure it's
# actually a directory. If not, then create it with the appropriate
# permissions.
def verify
if File.exist? @dir then
raise Gem::Security::Exception,
"trust directory #{@dir} is not a directory" unless
File.directory? @dir
FileUtils.chmod 0700, @dir
else
FileUtils.mkdir_p @dir, :mode => @permissions[:trust_dir]
end
end
end

Просмотреть файл

@ -3,7 +3,7 @@ require 'zlib'
require 'erb' require 'erb'
require 'rubygems' require 'rubygems'
require 'rubygems/doc_manager' require 'rubygems/rdoc'
## ##
# Gem::Server and allows users to serve gems for consumption by # Gem::Server and allows users to serve gems for consumption by
@ -17,9 +17,6 @@ require 'rubygems/doc_manager'
# * "/quick/" - Individual gemspecs # * "/quick/" - Individual gemspecs
# * "/gems" - Direct access to download the installable gems # * "/gems" - Direct access to download the installable gems
# * "/rdoc?q=" - Search for installed rdoc documentation # * "/rdoc?q=" - Search for installed rdoc documentation
# * legacy indexes:
# * "/Marshal.#{Gem.marshal_version}" - Full SourceIndex dump of metadata
# for installed gems
# #
# == Usage # == Usage
# #
@ -430,53 +427,25 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
options[:launch], options[:addresses]).run options[:launch], options[:addresses]).run
end end
##
# Only the first directory in gem_dirs is used for serving gems
def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil) def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)
Gem::RDoc.load_rdoc
Socket.do_not_reverse_lookup = true Socket.do_not_reverse_lookup = true
@gem_dirs = Array gem_dirs @gem_dirs = Array gem_dirs
@port = port @port = port
@daemon = daemon @daemon = daemon
@launch = launch @launch = launch
@addresses = addresses @addresses = addresses
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
@server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
@spec_dirs = @gem_dirs.map do |gem_dir| @spec_dirs = @gem_dirs.map { |gem_dir| File.join gem_dir, 'specifications' }
spec_dir = File.join gem_dir, 'specifications' @spec_dirs.reject! { |spec_dir| !File.directory? spec_dir }
unless File.directory? spec_dir then
raise ArgumentError, "#{gem_dir} does not appear to be a gem repository"
end
spec_dir
end
Gem::Specification.dirs = @gem_dirs Gem::Specification.dirs = @gem_dirs
end
def Marshal(req, res) @have_rdoc_4_plus = nil
Gem::Specification.reset
add_date res
index = Gem::Deprecate.skip_during { Marshal.dump Gem.source_index }
if req.request_method == 'HEAD' then
res['content-length'] = index.length
return
end
if req.path =~ /Z$/ then
res['content-type'] = 'application/x-deflate'
index = Gem.deflate index
else
res['content-type'] = 'application/octet-stream'
end
res.body << index
end end
def add_date res def add_date res
@ -485,6 +454,19 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end.max end.max
end end
def doc_root gem_name
if have_rdoc_4_plus? then
"/doc_root/#{gem_name}/"
else
"/doc_root/#{gem_name}/rdoc/index.html"
end
end
def have_rdoc_4_plus?
@have_rdoc_4_plus ||=
Gem::Requirement.new('>= 4').satisfied_by? Gem::RDoc.rdoc_version
end
def latest_specs(req, res) def latest_specs(req, res)
Gem::Specification.reset Gem::Specification.reset
@ -614,14 +596,14 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
"authors" => spec.authors.sort.join(", "), "authors" => spec.authors.sort.join(", "),
"date" => spec.date.to_s, "date" => spec.date.to_s,
"dependencies" => deps, "dependencies" => deps,
"doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html", "doc_path" => doc_root(spec.full_name),
"executables" => executables, "executables" => executables,
"only_one_executable" => (executables && executables.size == 1), "only_one_executable" => (executables && executables.size == 1),
"full_name" => spec.full_name, "full_name" => spec.full_name,
"has_deps" => !deps.empty?, "has_deps" => !deps.empty?,
"homepage" => spec.homepage, "homepage" => spec.homepage,
"name" => spec.name, "name" => spec.name,
"rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, "rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?,
"summary" => spec.summary, "summary" => spec.summary,
"version" => spec.version.to_s, "version" => spec.version.to_s,
} }
@ -630,7 +612,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
specs << { specs << {
"authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
"dependencies" => [], "dependencies" => [],
"doc_path" => "/doc_root/rubygems-#{Gem::VERSION}/rdoc/index.html", "doc_path" => doc_root("rubygems-#{Gem::VERSION}"),
"executables" => [{"executable" => 'gem', "is_last" => true}], "executables" => [{"executable" => 'gem', "is_last" => true}],
"only_one_executable" => true, "only_one_executable" => true,
"full_name" => "rubygems-#{Gem::VERSION}", "full_name" => "rubygems-#{Gem::VERSION}",
@ -730,15 +712,15 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
when 1 when 1
new_path = File.basename(found_gems[0]) new_path = File.basename(found_gems[0])
res.status = 302 res.status = 302
res['Location'] = "/doc_root/#{new_path}/rdoc/index.html" res['Location'] = doc_root new_path
return true return true
else else
doc_items = [] doc_items = []
found_gems.each do |file_name| found_gems.each do |file_name|
base_name = File.basename(file_name) base_name = File.basename(file_name)
doc_items << { doc_items << {
:name => base_name, :name => base_name,
:url => "/doc_root/#{base_name}/rdoc/index.html", :url => doc_root(new_path),
:summary => '' :summary => ''
} }
end end
@ -756,9 +738,6 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
WEBrick::Daemon.start if @daemon WEBrick::Daemon.start if @daemon
@server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal)
@server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
@server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs) @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
@server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs) @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
@ -779,10 +758,21 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
@server.mount_proc "/rdoc", method(:rdoc) @server.mount_proc "/rdoc", method(:rdoc)
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } file_handlers = {
paths.each do |mount_point, mount_dir| '/gems' => '/cache/',
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, }
File.join(@gem_dirs.first, mount_dir), true)
if have_rdoc_4_plus? then
@server.mount '/doc_root', RDoc::Servlet, '/doc_root'
else
file_handlers['/doc_root'] = '/doc/'
end
@gem_dirs.each do |gem_dir|
file_handlers.each do |mount_point, mount_dir|
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
File.join(gem_dir, mount_dir), true)
end
end end
trap("INT") { @server.shutdown; exit! } trap("INT") { @server.shutdown; exit! }

144
lib/rubygems/source.rb Normal file
Просмотреть файл

@ -0,0 +1,144 @@
require 'uri'
require 'fileutils'
class Gem::Source
FILES = {
:released => 'specs',
:latest => 'latest_specs',
:prerelease => 'prerelease_specs',
}
def initialize(uri)
unless uri.kind_of? URI
uri = URI.parse(uri.to_s)
end
@uri = uri
@api_uri = nil
end
attr_reader :uri
def api_uri
require 'rubygems/remote_fetcher'
@api_uri ||= Gem::RemoteFetcher.fetcher.api_endpoint uri
end
def <=>(other)
if !@uri
return 0 unless other.uri
return -1
end
return 1 if !other.uri
@uri.to_s <=> other.uri.to_s
end
include Comparable
def ==(other)
case other
when self.class
@uri == other.uri
else
false
end
end
alias_method :eql?, :==
def hash
@uri.hash
end
##
# Returns the local directory to write +uri+ to.
def cache_dir(uri)
# Correct for windows paths
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
root = File.join Gem.user_home, '.gem', 'specs'
File.join root, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
def update_cache?
@update_cache ||= File.stat(Gem.user_home).uid == Process.uid
end
def fetch_spec(name)
fetcher = Gem::RemoteFetcher.fetcher
spec_file_name = name.spec_name
uri = @uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
cache_dir = cache_dir uri
local_spec = File.join cache_dir, spec_file_name
if File.exist? local_spec then
spec = Gem.read_binary local_spec
spec = Marshal.load(spec) rescue nil
return spec if spec
end
uri.path << '.rz'
spec = fetcher.fetch_path uri
spec = Gem.inflate spec
if update_cache? then
FileUtils.mkdir_p cache_dir
open local_spec, 'wb' do |io|
io.write spec
end
end
# TODO: Investigate setting Gem::Specification#loaded_from to a URI
Marshal.load spec
end
##
# Loads +type+ kind of specs fetching from +@uri+ if the on-disk cache is
# out of date.
#
# +type+ is one of the following:
#
# :released => Return the list of all released specs
# :latest => Return the list of only the highest version of each gem
# :prerelease => Return the list of all prerelease only specs
#
def load_specs(type)
file = FILES[type]
fetcher = Gem::RemoteFetcher.fetcher
file_name = "#{file}.#{Gem.marshal_version}"
spec_path = @uri + "#{file_name}.gz"
cache_dir = cache_dir spec_path
local_file = File.join(cache_dir, file_name)
retried = false
FileUtils.mkdir_p cache_dir if update_cache?
spec_dump = fetcher.cache_update_path(spec_path, local_file)
begin
Gem::NameTuple.from_list Marshal.load(spec_dump)
rescue ArgumentError
if update_cache? && !retried
FileUtils.rm local_file
retried = true
retry
else
raise Gem::Exception.new("Invalid spec cache file in #{local_file}")
end
end
end
def download(spec, dir=Dir.pwd)
fetcher = Gem::RemoteFetcher.fetcher
fetcher.download spec, @uri.to_s, dir
end
end

Просмотреть файл

@ -1,406 +0,0 @@
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require 'rubygems/specification'
require 'rubygems/deprecate'
##
# The SourceIndex object indexes all the gems available from a
# particular source (e.g. a list of gem directories, or a remote
# source). A SourceIndex maps a gem full name to a gem
# specification.
#
# NOTE:: The class used to be named Cache, but that became
# confusing when cached source fetchers where introduced. The
# constant Gem::Cache is an alias for this class to allow old
# YAMLized source index objects to load properly.
class Gem::SourceIndex
include Enumerable
attr_reader :gems # :nodoc:
##
# Directories to use to refresh this SourceIndex when calling refresh!
attr_accessor :spec_dirs
##
# Factory method to construct a source index instance for a given
# path.
#
# deprecated::
# If supplied, from_installed_gems will act just like
# +from_gems_in+. This argument is deprecated and is provided
# just for backwards compatibility, and should not generally
# be used.
#
# return::
# SourceIndex instance
def self.from_installed_gems(*deprecated)
if deprecated.empty?
from_gems_in(*installed_spec_directories)
else
warn "NOTE: from_installed_gems(arg) is deprecated. From #{caller.first}"
from_gems_in(*deprecated) # HACK warn
end
end
##
# Returns a list of directories from Gem.path that contain specifications.
def self.installed_spec_directories
# TODO: move to Gem::Utils
Gem.path.collect { |dir| File.join(dir, "specifications") }
end
##
# Creates a new SourceIndex from the ruby format gem specifications in
# +spec_dirs+.
def self.from_gems_in(*spec_dirs)
new spec_dirs
end
##
# Loads a ruby-format specification from +file_name+ and returns the
# loaded spec.
def self.load_specification(file_name)
Gem::Deprecate.skip_during do
Gem::Specification.load Gem::Path.new(file_name)
end
end
##
# Constructs a source index instance from the provided specifications, which
# is a Hash of gem full names and Gem::Specifications.
def initialize specs_or_dirs = []
@gems = {}
@spec_dirs = nil
case specs_or_dirs
when Hash then
specs_or_dirs.each do |full_name, spec|
add_spec spec
end
when Array, String then
self.spec_dirs = Array(specs_or_dirs)
refresh!
else
arg = specs_or_dirs.inspect
warn "NOTE: SourceIndex.new(#{arg}) is deprecated; From #{caller.first}."
end
end
def all_gems
gems
end
def prerelease_gems
@gems.reject { |name, gem| !gem.version.prerelease? }
end
def released_gems
@gems.reject { |name, gem| gem.version.prerelease? }
end
##
# Reconstruct the source index from the specifications in +spec_dirs+.
def load_gems_in(*spec_dirs)
@gems.clear
spec_dirs.reverse_each do |spec_dir|
spec_files = Dir[File.join(spec_dir, "*.gemspec")]
spec_files.each do |spec_file|
gemspec = Gem::Deprecate.skip_during do
Gem::Specification.load spec_file
end
add_spec gemspec if gemspec
end
end
self
end
##
# Returns an Array specifications for the latest released versions
# of each gem in this index.
def latest_specs(include_prerelease=false)
result = Hash.new { |h,k| h[k] = [] }
latest = {}
sort.each do |_, spec|
name = spec.name
curr_ver = spec.version
prev_ver = latest.key?(name) ? latest[name].version : nil
next if !include_prerelease && curr_ver.prerelease?
next unless prev_ver.nil? or curr_ver >= prev_ver or
latest[name].platform != Gem::Platform::RUBY
if prev_ver.nil? or
(curr_ver > prev_ver and spec.platform == Gem::Platform::RUBY) then
result[name].clear
latest[name] = spec
end
if spec.platform != Gem::Platform::RUBY then
result[name].delete_if do |result_spec|
result_spec.platform == spec.platform
end
end
result[name] << spec
end
result.values.flatten
end
##
# An array including only the prerelease gemspecs
def prerelease_specs
prerelease_gems.values
end
##
# An array including only the released gemspecs
def released_specs
released_gems.values
end
##
# Add a gem specification to the source index.
def add_spec(gem_spec, name = gem_spec.full_name)
# No idea why, but the Indexer wants to insert them using original_name
# instead of full_name. So we make it an optional arg.
@gems[name] = gem_spec
end
##
# Add gem specifications to the source index.
def add_specs(*gem_specs)
Gem::Deprecate.skip_during do
gem_specs.each do |spec|
add_spec spec
end
end
end
##
# Remove a gem specification named +full_name+.
def remove_spec(full_name)
@gems.delete full_name
end
##
# Iterate over the specifications in the source index.
def each(&block) # :yields: gem.full_name, gem
@gems.each(&block)
end
##
# The gem specification given a full gem spec name.
def specification(full_name)
@gems[full_name]
end
##
# The signature for the source index. Changes in the signature indicate a
# change in the index.
def index_signature
require 'digest'
Digest::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
end
##
# The signature for the given gem specification.
def gem_signature(gem_full_name)
require 'digest'
Digest::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
end
def size
@gems.size
end
alias length size
##
# Find a gem by an exact match on the short name.
def find_name(gem_name, requirement = Gem::Requirement.default)
dep = Gem::Dependency.new gem_name, requirement
Gem::Deprecate.skip_during do
search dep
end
end
##
# Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
# is true, only gems matching Gem::Platform.local will be returned. An
# Array of matching Gem::Specification objects is returned.
#
# For backwards compatibility, a String or Regexp pattern may be passed as
# +gem_pattern+, and a Gem::Requirement for +platform_only+. This
# behavior is deprecated and will be removed.
def search(gem_pattern, platform_or_requirement = false)
requirement = nil
only_platform = false # FIX: WTF is this?!?
# TODO - Remove support and warning for legacy arguments after 2008/11
unless Gem::Dependency === gem_pattern
warn "#{Gem.location_of_caller.join ':'}:Warning: Gem::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated, use #find_name"
end
case gem_pattern
when Regexp then
requirement = platform_or_requirement || Gem::Requirement.default
when Gem::Dependency then
only_platform = platform_or_requirement
requirement = gem_pattern.requirement
gem_pattern = if Regexp === gem_pattern.name then
gem_pattern.name
elsif gem_pattern.name.empty? then
//
else
/^#{Regexp.escape gem_pattern.name}$/
end
else
requirement = platform_or_requirement || Gem::Requirement.default
gem_pattern = /#{gem_pattern}/i
end
unless Gem::Requirement === requirement then
requirement = Gem::Requirement.create requirement
end
specs = @gems.values.select do |spec|
spec.name =~ gem_pattern and
requirement.satisfied_by? spec.version
end
if only_platform then
specs = specs.select do |spec|
Gem::Platform.match spec.platform
end
end
specs.sort_by { |s| s.sort_obj }
end
##
# Replaces the gems in the source index from specifications in the
# directories this source index was created from. Raises an exception if
# this source index wasn't created from a directory (via from_gems_in or
# from_installed_gems, or having spec_dirs set).
def refresh!
raise 'source index not created from disk' if @spec_dirs.nil?
load_gems_in(*@spec_dirs)
end
##
# Returns an Array of Gem::Specifications that are not up to date.
def outdated
outdateds = []
latest_specs.each do |local|
dependency = Gem::Dependency.new local.name, ">= #{local.version}"
fetcher = Gem::SpecFetcher.fetcher
remotes = fetcher.find_matching dependency
remotes = remotes.map { |(_, version, _), _| version }
latest = remotes.sort.last
outdateds << local.name if latest and local.version < latest
end
outdateds
end
def ==(other) # :nodoc:
self.class === other and @gems == other.gems
end
def dump
Marshal.dump(self)
end
end
# :stopdoc:
module Gem
##
# Cache is an alias for SourceIndex to allow older YAMLized source index
# objects to load properly.
Cache = SourceIndex
end
class Gem::SourceIndex
extend Gem::Deprecate
deprecate :all_gems, :none, 2011, 10
deprecate :==, :none, 2011, 11 # noisy
deprecate :add_specs, :none, 2011, 11 # noisy
deprecate :each, :none, 2011, 11
deprecate :gems, :none, 2011, 11
deprecate :load_gems_in, :none, 2011, 11
deprecate :refresh!, :none, 2011, 11
deprecate :spec_dirs=, "Specification.dirs=", 2011, 11 # noisy
deprecate :add_spec, "Specification.add_spec", 2011, 11
deprecate :find_name, "Specification.find_by_name", 2011, 11
deprecate :gem_signature, :none, 2011, 11
deprecate :index_signature, :none, 2011, 11
deprecate :initialize, :none, 2011, 11
deprecate :latest_specs, "Specification.latest_specs", 2011, 11
deprecate :length, "Specification.all.length", 2011, 11
deprecate :outdated, :none, 2011, 11
deprecate :prerelease_gems, :none, 2011, 11
deprecate :prerelease_specs, :none, 2011, 11
deprecate :released_gems, :none, 2011, 11
deprecate :released_specs, :none, 2011, 11
deprecate :remove_spec, "Specification.remove_spec", 2011, 11
deprecate :search, :none, 2011, 11
deprecate :size, "Specification.all.size", 2011, 11
deprecate :spec_dirs, "Specification.dirs", 2011, 11
deprecate :specification, "Specification.find", 2011, 11
class << self
extend Gem::Deprecate
deprecate :from_gems_in, :none, 2011, 10
deprecate :from_installed_gems, :none, 2011, 10
deprecate :installed_spec_directories, "Specification.dirs", 2011, 11
deprecate :load_specification, :none, 2011, 10
end
end
# :startdoc:

Просмотреть файл

@ -0,0 +1,87 @@
require 'rubygems/source'
class Gem::SourceList
def initialize
@sources = []
end
attr_reader :sources
def self.from(ary)
list = new
if ary
ary.each do |x|
list << x
end
end
return list
end
def initialize_copy(other)
@sources = @sources.dup
end
def <<(obj)
src = case obj
when URI
Gem::Source.new(obj)
when Gem::Source
obj
else
Gem::Source.new(URI.parse(obj))
end
@sources << src
src
end
def replace(other)
@sources.clear
other.each do |x|
self << x
end
self
end
def each
@sources.each { |s| yield s.uri.to_s }
end
def each_source(&b)
@sources.each(&b)
end
def ==(other)
to_a == other
end
def to_a
@sources.map { |x| x.uri.to_s }
end
alias_method :to_ary, :to_a
def first
@sources.first
end
def include?(other)
if other.kind_of? Gem::Source
@sources.include? other
else
@sources.find { |x| x.uri.to_s == other.to_s }
end
end
def delete(uri)
if uri.kind_of? Gem::Source
@sources.delete uri
else
@sources.delete_if { |x| x.uri.to_s == uri.to_s }
end
end
end

Просмотреть файл

@ -0,0 +1,92 @@
require 'rubygems/source'
class Gem::Source::Local < Gem::Source
def initialize
@uri = nil
end
def load_specs(type)
names = []
@specs = {}
Dir["*.gem"].each do |file|
begin
pkg = Gem::Package.new(file)
rescue SystemCallError, Gem::Package::FormatError
# ignore
else
tup = pkg.spec.name_tuple
@specs[tup] = [File.expand_path(file), pkg]
case type
when :released
unless pkg.spec.version.prerelease?
names << pkg.spec.name_tuple
end
when :prerelease
if pkg.spec.version.prerelease?
names << pkg.spec.name_tuple
end
when :latest
tup = pkg.spec.name_tuple
cur = names.find { |x| x.name == tup.name }
if !cur
names << tup
elsif cur.version < tup.version
names.delete cur
names << tup
end
else
names << pkg.spec.name_tuple
end
end
end
names
end
def find_gem(gem_name, version=Gem::Requirement.default,
prerelease=false)
load_specs :complete
found = []
@specs.each do |n, data|
if n.name == gem_name
s = data[1].spec
if version.satisfied_by?(s.version)
if prerelease
found << s
elsif !s.version.prerelease?
found << s
end
end
end
end
found.sort_by { |s| s.version }.last
end
def fetch_spec(name)
load_specs :complete
if data = @specs[name]
data.last.spec
else
raise Gem::Exception, "Unable to find spec for '#{name}'"
end
end
def download(spec, cache_dir=nil)
load_specs :complete
@specs.each do |name, data|
return data[0] if data[1].spec == spec
end
raise Gem::Exception, "Unable to find file for '#{spec.full_name}'"
end
end

Просмотреть файл

@ -0,0 +1,28 @@
class Gem::Source::SpecificFile < Gem::Source
def initialize(file)
@uri = nil
@path = ::File.expand_path(file)
@package = Gem::Package.new @path
@spec = @package.spec
@name = @spec.name_tuple
end
attr_reader :spec
def load_specs(*a)
[@name]
end
def fetch_spec(name)
return @spec if name == @name
raise Gem::Exception, "Unable to find '#{name}'"
@spec
end
def download(spec, dir=nil)
return @path if spec == @spec
raise Gem::Exception, "Unable to download '#{spec.full_name}'"
end
end

Просмотреть файл

@ -2,6 +2,7 @@ require 'rubygems/remote_fetcher'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'rubygems/errors' require 'rubygems/errors'
require 'rubygems/text' require 'rubygems/text'
require 'rubygems/name_tuple'
## ##
# SpecFetcher handles metadata updates from remote gem repositories. # SpecFetcher handles metadata updates from remote gem repositories.
@ -11,17 +12,6 @@ class Gem::SpecFetcher
include Gem::UserInteraction include Gem::UserInteraction
include Gem::Text include Gem::Text
FILES = {
:all => 'specs',
:latest => 'latest_specs',
:prerelease => 'prerelease_specs',
}
##
# The SpecFetcher cache dir.
attr_reader :dir # :nodoc:
## ##
# Cache of latest specs # Cache of latest specs
@ -48,8 +38,6 @@ class Gem::SpecFetcher
end end
def initialize def initialize
require 'fileutils'
@dir = File.join Gem.user_home, '.gem', 'specs' @dir = File.join Gem.user_home, '.gem', 'specs'
@update_cache = File.stat(Gem.user_home).uid == Process.uid @update_cache = File.stat(Gem.user_home).uid == Process.uid
@ -60,99 +48,40 @@ class Gem::SpecFetcher
@caches = { @caches = {
:latest => @latest_specs, :latest => @latest_specs,
:prerelease => @prerelease_specs, :prerelease => @prerelease_specs,
:all => @specs :released => @specs,
} }
@fetcher = Gem::RemoteFetcher.fetcher @fetcher = Gem::RemoteFetcher.fetcher
end end
## ##
# Returns the local directory to write +uri+ to. #
# Find and fetch gem name tuples that match +dependency+.
#
# If +matching_platform+ is false, gems for all platforms are returned.
def cache_dir(uri) def search_for_dependency(dependency, matching_platform=true)
# Correct for windows paths
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
##
# Fetch specs matching +dependency+. If +all+ is true, all matching
# (released) versions are returned. If +matching_platform+ is
# false, all platforms are returned. If +prerelease+ is true,
# prerelease versions are included.
def fetch_with_errors(dependency,
all = false,
matching_platform = true,
prerelease = false)
specs_and_sources, errors = find_matching_with_errors(dependency,
all,
matching_platform,
prerelease)
ss = specs_and_sources.map do |spec_tuple, source_uri|
[fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
end
return [ss, errors]
end
def fetch(*args)
fetch_with_errors(*args).first
end
def fetch_spec(spec, source_uri)
source_uri = URI.parse source_uri if String === source_uri
spec = spec - [nil, 'ruby', '']
spec_file_name = "#{spec.join '-'}.gemspec"
uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
cache_dir = cache_dir uri
local_spec = File.join cache_dir, spec_file_name
if File.exist? local_spec then
spec = Gem.read_binary local_spec
else
uri.path << '.rz'
spec = @fetcher.fetch_path uri
spec = Gem.inflate spec
if @update_cache then
FileUtils.mkdir_p cache_dir
open local_spec, 'wb' do |io|
io.write spec
end
end
end
# TODO: Investigate setting Gem::Specification#loaded_from to a URI
Marshal.load spec
end
##
# Find spec names that match +dependency+. If +all+ is true, all
# matching released versions are returned. If +matching_platform+
# is false, gems for all platforms are returned.
def find_matching_with_errors(dependency,
all = false,
matching_platform = true,
prerelease = false)
found = {} found = {}
rejected_specs = {} rejected_specs = {}
list(all, prerelease).each do |source_uri, specs| if dependency.prerelease?
found[source_uri] = specs.select do |spec_name, version, spec_platform| type = :complete
if dependency.match?(spec_name, version) elsif dependency.latest_version?
if matching_platform and !Gem::Platform.match(spec_platform) type = :latest
pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version)) else
pm.add_platform spec_platform type = :released
end
list, errors = available_specs(type)
list.each do |source, specs|
found[source] = specs.select do |tup|
if dependency.match?(tup)
if matching_platform and !Gem::Platform.match(tup.platform)
pm = (
rejected_specs[dependency] ||= \
Gem::PlatformMismatch.new(tup.name, tup.version))
pm.add_platform tup.platform
false false
else else
true true
@ -161,43 +90,82 @@ class Gem::SpecFetcher
end end
end end
errors = rejected_specs.values errors += rejected_specs.values
specs_and_sources = [] tuples = []
found.each do |source_uri, specs| found.each do |source, specs|
uri_str = source_uri.to_s specs.each do |s|
specs_and_sources.concat(specs.map { |spec| [spec, uri_str] }) tuples << [s, source]
end
end end
[specs_and_sources, errors] tuples = tuples.sort_by { |x| x[0] }
return [tuples, errors]
end end
def find_matching(*args)
find_matching_with_errors(*args).first ##
# Return all gem name tuples who's names match +obj+
def detect(type=:complete)
tuples = []
list, _ = available_specs(type)
list.each do |source, specs|
specs.each do |tup|
if yield(tup)
tuples << [tup, source]
end
end
end
tuples
end
##
# Find and fetch specs that match +dependency+.
#
# If +matching_platform+ is false, gems for all platforms are returned.
def spec_for_dependency(dependency, matching_platform=true)
tuples, errors = search_for_dependency(dependency, matching_platform)
specs = []
tuples.each do |tup, source|
begin
spec = source.fetch_spec(tup)
rescue Gem::RemoteFetcher::FetchError => e
errors << Gem::SourceFetchProblem.new(source, e)
else
specs << [spec, source]
end
end
return [specs, errors]
end end
## ##
# Suggests a gem based on the supplied +gem_name+. Returns a string # Suggests gems based on the supplied +gem_name+. Returns an array of
# of the gem name if an approximate match can be found or nil # alternative gem names.
# otherwise. NOTE: for performance reasons only gems which exactly
# match the first character of +gem_name+ are considered.
def suggest_gems_from_name gem_name def suggest_gems_from_name gem_name
gem_name = gem_name.downcase gem_name = gem_name.downcase.tr('_-', '')
max = gem_name.size / 2 max = gem_name.size / 2
specs = list.values.flatten 1 names = available_specs(:complete).first.values.flatten(1)
matches = specs.map { |name, version, platform| matches = names.map { |n|
next unless Gem::Platform.match platform next unless n.match_platform?
distance = levenshtein_distance gem_name, name.downcase distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '')
next if distance >= max next if distance >= max
return [name] if distance == 0 return [n.name] if distance == 0
[name, distance] [n.name, distance]
}.compact }.compact
matches = matches.uniq.sort_by { |name, dist| dist } matches = matches.uniq.sort_by { |name, dist| dist }
@ -206,92 +174,46 @@ class Gem::SpecFetcher
end end
## ##
# Returns a list of gems available for each source in Gem::sources. If # Returns a list of gems available for each source in Gem::sources.
# +all+ is true, all released versions are returned instead of only latest #
# versions. If +prerelease+ is true, include prerelease versions. # +type+ can be one of 3 values:
# :released => Return the list of all released specs
# :complete => Return the list of all specs
# :latest => Return the list of only the highest version of each gem
# :prerelease => Return the list of all prerelease only specs
#
def list(all = false, prerelease = false) def available_specs(type)
# TODO: make type the only argument errors = []
type = if all list = {}
:all
elsif prerelease
:prerelease
else
:latest
end
list = {} Gem.sources.each_source do |source|
file = FILES[type]
cache = @caches[type]
Gem.sources.each do |source_uri|
source_uri = URI.parse source_uri
unless cache.include? source_uri
cache[source_uri] = load_specs source_uri, file
end
list[source_uri] = cache[source_uri]
end
if type == :all
list.values.map do |gems|
gems.reject! { |g| !g[1] || g[1].prerelease? }
end
end
list
end
##
# Loads specs in +file+, fetching from +source_uri+ if the on-disk cache is
# out of date.
def load_specs(source_uri, file)
file_name = "#{file}.#{Gem.marshal_version}"
spec_path = source_uri + "#{file_name}.gz"
cache_dir = cache_dir spec_path
local_file = File.join(cache_dir, file_name)
loaded = false
if File.exist? local_file then
begin begin
spec_dump = names = case type
@fetcher.fetch_path(spec_path, File.mtime(local_file)) when :latest
tuples_for source, :latest
when :released
tuples_for source, :released
when :complete
tuples_for(source, :prerelease) + tuples_for(source, :released)
when :prerelease
tuples_for(source, :prerelease)
else
raise Gem::Exception, "Unknown type - :#{type}"
end
rescue Gem::RemoteFetcher::FetchError => e rescue Gem::RemoteFetcher::FetchError => e
alert_warning "Error fetching data: #{e.message}" errors << Gem::SourceFetchProblem.new(source, e)
end else
list[source] = names
loaded = true if spec_dump
spec_dump ||= Gem.read_binary local_file
else
spec_dump = @fetcher.fetch_path spec_path
loaded = true
end
specs = begin
Marshal.load spec_dump
rescue ArgumentError
spec_dump = @fetcher.fetch_path spec_path
loaded = true
Marshal.load spec_dump
end
if loaded and @update_cache then
begin
FileUtils.mkdir_p cache_dir
open local_file, 'wb' do |io|
io << spec_dump
end
rescue
end end
end end
specs [list, errors]
end end
def tuples_for(source, type)
cache = @caches[type]
cache[source.uri] ||= source.load_specs(type)
end
end end

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,90 @@
This CA certificate is for verifying HTTPS connection to;
- https://rubygems.org/ (obtained by RubyGems team)
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
Validity
Not Before: May 30 10:48:38 2000 GMT
Not After : May 30 10:48:38 2020 GMT
Subject: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b7:f7:1a:33:e6:f2:00:04:2d:39:e0:4e:5b:ed:
1f:bc:6c:0f:cd:b5:fa:23:b6:ce:de:9b:11:33:97:
a4:29:4c:7d:93:9f:bd:4a:bc:93:ed:03:1a:e3:8f:
cf:e5:6d:50:5a:d6:97:29:94:5a:80:b0:49:7a:db:
2e:95:fd:b8:ca:bf:37:38:2d:1e:3e:91:41:ad:70:
56:c7:f0:4f:3f:e8:32:9e:74:ca:c8:90:54:e9:c6:
5f:0f:78:9d:9a:40:3c:0e:ac:61:aa:5e:14:8f:9e:
87:a1:6a:50:dc:d7:9a:4e:af:05:b3:a6:71:94:9c:
71:b3:50:60:0a:c7:13:9d:38:07:86:02:a8:e9:a8:
69:26:18:90:ab:4c:b0:4f:23:ab:3a:4f:84:d8:df:
ce:9f:e1:69:6f:bb:d7:42:d7:6b:44:e4:c7:ad:ee:
6d:41:5f:72:5a:71:08:37:b3:79:65:a4:59:a0:94:
37:f7:00:2f:0d:c2:92:72:da:d0:38:72:db:14:a8:
45:c4:5d:2a:7d:b7:b4:d6:c4:ee:ac:cd:13:44:b7:
c9:2b:dd:43:00:25:fa:61:b9:69:6a:58:23:11:b7:
a7:33:8f:56:75:59:f5:cd:29:d7:46:b7:0a:2b:65:
b6:d3:42:6f:15:b2:b8:7b:fb:ef:e9:5d:53:d5:34:
5a:27
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
X509v3 Key Usage:
Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Authority Key Identifier:
keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
DirName:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
serial:01
Signature Algorithm: sha1WithRSAEncryption
b0:9b:e0:85:25:c2:d6:23:e2:0f:96:06:92:9d:41:98:9c:d9:
84:79:81:d9:1e:5b:14:07:23:36:65:8f:b0:d8:77:bb:ac:41:
6c:47:60:83:51:b0:f9:32:3d:e7:fc:f6:26:13:c7:80:16:a5:
bf:5a:fc:87:cf:78:79:89:21:9a:e2:4c:07:0a:86:35:bc:f2:
de:51:c4:d2:96:b7:dc:7e:4e:ee:70:fd:1c:39:eb:0c:02:51:
14:2d:8e:bd:16:e0:c1:df:46:75:e7:24:ad:ec:f4:42:b4:85:
93:70:10:67:ba:9d:06:35:4a:18:d3:2b:7a:cc:51:42:a1:7a:
63:d1:e6:bb:a1:c5:2b:c2:36:be:13:0d:e6:bd:63:7e:79:7b:
a7:09:0d:40:ab:6a:dd:8f:8a:c3:f6:f6:8c:1a:42:05:51:d4:
45:f5:9f:a7:62:21:68:15:20:43:3c:99:e7:7c:bd:24:d8:a9:
91:17:73:88:3f:56:1b:31:38:18:b4:71:0f:9a:cd:c8:0e:9e:
8e:2e:1b:e1:8c:98:83:cb:1f:31:f1:44:4c:c6:04:73:49:76:
60:0f:c7:f8:bd:17:80:6b:2e:e9:cc:4c:0e:5a:9a:79:0f:20:
0a:2e:d5:9e:63:26:1e:55:92:94:d8:82:17:5a:7b:d0:bc:c7:
8f:4e:86:04
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----

Просмотреть файл

@ -0,0 +1,90 @@
This CA certificate is for verifying HTTPS connection to;
- https://d2chzxaqi4y7f8.cloudfront.net/ (prepared by AWS)
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 927650371 (0x374ad243)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
Validity
Not Before: May 25 16:09:40 1999 GMT
Not After : May 25 16:39:40 2019 GMT
Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:cd:28:83:34:54:1b:89:f3:0f:af:37:91:31:ff:
af:31:60:c9:a8:e8:b2:10:68:ed:9f:e7:93:36:f1:
0a:64:bb:47:f5:04:17:3f:23:47:4d:c5:27:19:81:
26:0c:54:72:0d:88:2d:d9:1f:9a:12:9f:bc:b3:71:
d3:80:19:3f:47:66:7b:8c:35:28:d2:b9:0a:df:24:
da:9c:d6:50:79:81:7a:5a:d3:37:f7:c2:4a:d8:29:
92:26:64:d1:e4:98:6c:3a:00:8a:f5:34:9b:65:f8:
ed:e3:10:ff:fd:b8:49:58:dc:a0:de:82:39:6b:81:
b1:16:19:61:b9:54:b6:e6:43
Exponent: 3 (0x3)
X509v3 extensions:
Netscape Cert Type:
SSL CA, S/MIME CA, Object Signing CA
X509v3 CRL Distribution Points:
Full Name:
DirName: C = US, O = Entrust.net, OU = www.entrust.net/CPS incorp. by ref. (limits liab.), OU = (c) 1999 Entrust.net Limited, CN = Entrust.net Secure Server Certification Authority, CN = CRL1
Full Name:
URI:http://www.entrust.net/CRL/net1.crl
X509v3 Private Key Usage Period:
Not Before: May 25 16:09:40 1999 GMT, Not After: May 25 16:09:40 2019 GMT
X509v3 Key Usage:
Certificate Sign, CRL Sign
X509v3 Authority Key Identifier:
keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
X509v3 Subject Key Identifier:
F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
X509v3 Basic Constraints:
CA:TRUE
1.2.840.113533.7.65.0:
0
..V4.0....
Signature Algorithm: sha1WithRSAEncryption
90:dc:30:02:fa:64:74:c2:a7:0a:a5:7c:21:8d:34:17:a8:fb:
47:0e:ff:25:7c:8d:13:0a:fb:e4:98:b5:ef:8c:f8:c5:10:0d:
f7:92:be:f1:c3:d5:d5:95:6a:04:bb:2c:ce:26:36:65:c8:31:
c6:e7:ee:3f:e3:57:75:84:7a:11:ef:46:4f:18:f4:d3:98:bb:
a8:87:32:ba:72:f6:3c:e2:3d:9f:d7:1d:d9:c3:60:43:8c:58:
0e:22:96:2f:62:a3:2c:1f:ba:ad:05:ef:ab:32:78:87:a0:54:
73:19:b5:5c:05:f9:52:3e:6d:2d:45:0b:f7:0a:93:ea:ed:06:
f9:b2
-----BEGIN CERTIFICATE-----
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
-----END CERTIFICATE-----

Просмотреть файл

@ -0,0 +1,57 @@
This CA certificate is for verifying HTTPS connection to;
- https://s3.amazon.com/ (prepared by AWS)
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
7d:d9:fe:07:cf:a8:1e:b7:10:79:67:fb:a7:89:34:c6
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority - G2, OU=(c) 1998 VeriSign, Inc. - For authorized use only, OU=VeriSign Trust Network
Validity
Not Before: May 18 00:00:00 1998 GMT
Not After : Aug 1 23:59:59 2028 GMT
Subject: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority - G2, OU=(c) 1998 VeriSign, Inc. - For authorized use only, OU=VeriSign Trust Network
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:cc:5e:d1:11:5d:5c:69:d0:ab:d3:b9:6a:4c:99:
1f:59:98:30:8e:16:85:20:46:6d:47:3f:d4:85:20:
84:e1:6d:b3:f8:a4:ed:0c:f1:17:0f:3b:f9:a7:f9:
25:d7:c1:cf:84:63:f2:7c:63:cf:a2:47:f2:c6:5b:
33:8e:64:40:04:68:c1:80:b9:64:1c:45:77:c7:d8:
6e:f5:95:29:3c:50:e8:34:d7:78:1f:a8:ba:6d:43:
91:95:8f:45:57:5e:7e:c5:fb:ca:a4:04:eb:ea:97:
37:54:30:6f:bb:01:47:32:33:cd:dc:57:9b:64:69:
61:f8:9b:1d:1c:89:4f:5c:67
Exponent: 65537 (0x10001)
Signature Algorithm: sha1WithRSAEncryption
51:4d:cd:be:5c:cb:98:19:9c:15:b2:01:39:78:2e:4d:0f:67:
70:70:99:c6:10:5a:94:a4:53:4d:54:6d:2b:af:0d:5d:40:8b:
64:d3:d7:ee:de:56:61:92:5f:a6:c4:1d:10:61:36:d3:2c:27:
3c:e8:29:09:b9:11:64:74:cc:b5:73:9f:1c:48:a9:bc:61:01:
ee:e2:17:a6:0c:e3:40:08:3b:0e:e7:eb:44:73:2a:9a:f1:69:
92:ef:71:14:c3:39:ac:71:a7:91:09:6f:e4:71:06:b3:ba:59:
57:26:79:00:f6:f8:0d:a2:33:30:28:d4:aa:58:a0:9d:9d:69:
91:fd
-----BEGIN CERTIFICATE-----
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
oJ2daZH9
-----END CERTIFICATE-----

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше